create-ailk 0.18.0 → 0.19.1

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 (218) hide show
  1. package/LICENSE +6 -5
  2. package/NOTICE +2 -0
  3. package/dist/add-module.js +11 -3
  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/index.d.ts +1 -1
  9. package/dist/index.js +1 -1
  10. package/dist/lib/license-key-files.d.ts +6 -3
  11. package/dist/lib/license-key-files.js +8 -2
  12. package/dist/lib/scaffold-render-sweep.d.ts +32 -0
  13. package/dist/lib/scaffold-render-sweep.js +76 -3
  14. package/dist/license-set.d.ts +1 -1
  15. package/dist/license-set.js +13 -8
  16. package/dist/module-architecture.js +128 -3
  17. package/dist/programmatic.js +11 -1
  18. package/dist/site-roster.js +21 -4
  19. package/dist/spoke-checkout.d.ts +11 -3
  20. package/dist/spoke-checkout.js +62 -12
  21. package/dist/starter-grid.d.ts +1 -1
  22. package/dist/surfaces.d.ts +14 -0
  23. package/dist/surfaces.js +170 -1
  24. package/dist/template-manifest.d.ts +4 -3
  25. package/dist/topology.d.ts +7 -2
  26. package/dist/topology.js +88 -3
  27. package/package.json +5 -4
  28. package/templates/.env.example +3 -3
  29. package/templates/.github/workflows/ci.yml +5 -0
  30. package/templates/apps/api/.env.example +17 -1
  31. package/templates/apps/api/CLAUDE.md +31 -7
  32. package/templates/apps/api/LICENSE +6 -5
  33. package/templates/apps/api/NOTICE +2 -0
  34. package/templates/apps/api/jest.config.cjs +4 -29
  35. package/templates/apps/api/package.json +1 -0
  36. package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +19 -0
  37. package/templates/apps/api/src/__tests__/stripe-boot.test.ts +132 -0
  38. package/templates/apps/api/src/lib/__mocks__/prisma.ts +17 -0
  39. package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +237 -0
  40. package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +334 -0
  41. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +38 -0
  42. package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +217 -0
  43. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +7 -0
  44. package/templates/apps/api/src/lib/consent-store.ts +335 -0
  45. package/templates/apps/api/src/lib/deliverable-asset-store.ts +5 -4
  46. package/templates/apps/api/src/lib/stripe.ts +176 -3
  47. package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +272 -39
  48. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +51 -0
  49. package/templates/apps/api/src/middleware/auth.ts +214 -46
  50. package/templates/apps/api/src/middleware/rate-limit.ts +5 -0
  51. package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +167 -0
  52. package/templates/apps/api/src/routes/checkout/session.ts +3 -3
  53. package/templates/apps/api/src/routes/checkout/sessions.ts +6 -4
  54. package/templates/apps/api/src/routes/consent/cookie.ts +90 -0
  55. package/templates/apps/api/src/routes/consent/index.ts +18 -0
  56. package/templates/apps/api/src/routes/consent/unsubscribe.ts +127 -0
  57. package/templates/apps/api/src/routes/leads/index.ts +19 -0
  58. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +5 -0
  59. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +17 -4
  60. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +2 -0
  61. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +159 -0
  62. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +158 -0
  63. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +2 -0
  64. package/templates/apps/api/src/routes/webhooks/stripe.ts +17 -0
  65. package/templates/apps/api/src/server.ts +218 -16
  66. package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +503 -0
  67. package/templates/apps/api/src/services/__tests__/consent.test.ts +18 -3
  68. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +173 -4
  69. package/templates/apps/api/src/services/__tests__/person.test.ts +392 -0
  70. package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +87 -0
  71. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +57 -17
  72. package/templates/apps/api/src/services/consent.ts +488 -3
  73. package/templates/apps/api/src/services/lead-routing.ts +135 -14
  74. package/templates/apps/api/src/services/person.ts +501 -1
  75. package/templates/apps/api/src/services/project-listings.ts +7 -2
  76. package/templates/apps/api/src/services/request-confirm.ts +52 -0
  77. package/templates/apps/api/src/services/revalidation.ts +11 -1
  78. package/templates/apps/api/vercel.json +2 -2
  79. package/templates/apps/mcp/.env.example +10 -0
  80. package/templates/apps/mcp/CLAUDE.md +9 -4
  81. package/templates/apps/mcp/LICENSE +6 -5
  82. package/templates/apps/mcp/NOTICE +2 -0
  83. package/templates/apps/mcp/__tests__/abuse/guard.test.ts +198 -0
  84. package/templates/apps/mcp/__tests__/audit-single-page.test.ts +15 -3
  85. package/templates/apps/mcp/__tests__/boot.test.ts +78 -1
  86. package/templates/apps/mcp/__tests__/http-transport.test.ts +291 -2
  87. package/templates/apps/mcp/__tests__/request_callback.test.ts +69 -7
  88. package/templates/apps/mcp/__tests__/submit_lead.test.ts +69 -8
  89. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +69 -7
  90. package/templates/apps/mcp/jest.config.cjs +3 -44
  91. package/templates/apps/mcp/src/abuse/index.ts +117 -21
  92. package/templates/apps/mcp/src/boot.ts +5 -23
  93. package/templates/apps/mcp/src/http-transport.ts +93 -73
  94. package/templates/apps/mcp/src/index.ts +2 -0
  95. package/templates/apps/mcp/src/request-context.ts +52 -0
  96. package/templates/apps/mcp/src/tools/capture_lead.ts +9 -1
  97. package/templates/apps/mcp/src/tools/request_callback.ts +9 -1
  98. package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +9 -1
  99. package/templates/apps/web/LICENSE +6 -5
  100. package/templates/apps/web/NOTICE +2 -0
  101. package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +3 -3
  102. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/account-frame-sign-out.test.tsx +175 -0
  103. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/billing.test.tsx +302 -0
  104. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/general.test.tsx +724 -0
  105. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/hub-mode.test.tsx +346 -41
  106. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/item.test.tsx +11 -13
  107. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout-sign-out.test.tsx +146 -0
  108. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +232 -29
  109. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +305 -0
  110. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-popups.test.tsx +82 -0
  111. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/notifications.test.tsx +140 -0
  112. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +163 -72
  113. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/popups.test.tsx +451 -0
  114. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/security.test.tsx +409 -0
  115. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/settings.test.tsx +65 -380
  116. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/support.test.tsx +16 -11
  117. package/templates/apps/web/app/[locale]/(authed)/account/account-data.ts +1 -1
  118. package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +188 -54
  119. package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +24 -45
  120. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/page.tsx +253 -0
  121. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/review-form.tsx +145 -0
  122. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/versions/[version]/file/route.ts +100 -0
  123. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/file-route.test.ts +179 -0
  124. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/page.test.tsx +514 -0
  125. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/review.test.tsx +288 -0
  126. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-actions.ts +123 -0
  127. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-data.ts +82 -0
  128. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/page.tsx +174 -0
  129. package/templates/apps/web/app/[locale]/(authed)/account/billing/page.tsx +172 -0
  130. package/templates/apps/web/app/[locale]/(authed)/account/billing-source.ts +147 -0
  131. package/templates/apps/web/app/[locale]/(authed)/account/components/request-quote.tsx +229 -0
  132. package/templates/apps/web/app/[locale]/(authed)/account/components/schedule-service.tsx +276 -0
  133. package/templates/apps/web/app/[locale]/(authed)/account/components/send-message.tsx +128 -0
  134. package/templates/apps/web/app/[locale]/(authed)/account/general/billing-anchor.tsx +27 -0
  135. package/templates/apps/web/app/[locale]/(authed)/account/general/contact-actions.ts +81 -0
  136. package/templates/apps/web/app/[locale]/(authed)/account/general/deletion-request.tsx +132 -0
  137. package/templates/apps/web/app/[locale]/(authed)/account/general/page.tsx +193 -0
  138. package/templates/apps/web/app/[locale]/(authed)/account/general/profile-form.tsx +104 -0
  139. package/templates/apps/web/app/[locale]/(authed)/account/general/profile-source.ts +73 -0
  140. package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/__tests__/route.test.ts +18 -8
  141. package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/route.ts +4 -6
  142. package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +30 -4
  143. package/templates/apps/web/app/[locale]/(authed)/account/new-actions.ts +219 -0
  144. package/templates/apps/web/app/[locale]/(authed)/account/new-menu.tsx +340 -0
  145. package/templates/apps/web/app/[locale]/(authed)/account/new-popups.tsx +107 -0
  146. package/templates/apps/web/app/[locale]/(authed)/account/notifications/page.tsx +57 -0
  147. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +85 -26
  148. package/templates/apps/web/app/[locale]/(authed)/account/profile-actions.ts +116 -0
  149. package/templates/apps/web/app/[locale]/(authed)/account/security/page.tsx +132 -0
  150. package/templates/apps/web/app/[locale]/(authed)/account/security/security-panel.tsx +102 -0
  151. package/templates/apps/web/app/[locale]/(authed)/account/security/security-source.ts +62 -0
  152. package/templates/apps/web/app/[locale]/(authed)/account/settings/page.tsx +13 -354
  153. package/templates/apps/web/app/[locale]/(authed)/account/settings-frame.tsx +116 -0
  154. package/templates/apps/web/app/[locale]/(authed)/account/sign-in-method.ts +37 -0
  155. package/templates/apps/web/app/[locale]/(authed)/account/support-actions.ts +9 -10
  156. package/templates/apps/web/app/[locale]/layout.tsx +23 -4
  157. package/templates/apps/web/app/api/auth/hub/__tests__/continuation.test.ts +143 -0
  158. package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +62 -10
  159. package/templates/apps/web/app/api/auth/hub/authorize/route.ts +22 -7
  160. package/templates/apps/web/app/api/auth/hub/continuation.ts +73 -0
  161. package/templates/apps/web/app/api/auth/hub/continue/[ref]/__tests__/route.test.ts +188 -0
  162. package/templates/apps/web/app/api/auth/hub/continue/[ref]/route.ts +53 -0
  163. package/templates/apps/web/app/robots.txt/route.ts +10 -4
  164. package/templates/apps/web/auth.ts +25 -2
  165. package/templates/apps/web/lib/__tests__/account-config.test.ts +258 -46
  166. package/templates/apps/web/lib/__tests__/auth-email.test.ts +84 -0
  167. package/templates/apps/web/lib/account-config.ts +188 -52
  168. package/templates/apps/web/lib/auth-email.ts +45 -10
  169. package/templates/apps/web/lib/hub-session/__tests__/hub-session.test.ts +143 -0
  170. package/templates/apps/web/lib/hub-session/index.ts +1 -0
  171. package/templates/apps/web/lib/hub-session/reseal.ts +90 -0
  172. package/templates/apps/web/lib/hub-session/seal.ts +13 -9
  173. package/templates/apps/web/lib/topology.generated.ts +33 -0
  174. package/templates/apps/web/messages/en.json +194 -33
  175. package/templates/database/CHANGELOG.md +37 -0
  176. package/templates/database/LICENSE +6 -5
  177. package/templates/database/NOTICE +2 -0
  178. package/templates/database/README.md +1 -1
  179. package/templates/database/__tests__/consent-ledger.db.test.ts +345 -0
  180. package/templates/database/__tests__/fence-fan-out.db.test.ts +6 -2
  181. package/templates/database/__tests__/fixtures/person-contact-backfill.json +182 -0
  182. package/templates/database/__tests__/member-area.db.test.ts +15 -3
  183. package/templates/database/__tests__/member-contract-migration.test.ts +85 -0
  184. package/templates/database/__tests__/member-contract.db.test.ts +413 -0
  185. package/templates/database/__tests__/person-contact.db.test.ts +563 -0
  186. package/templates/database/__tests__/person.db.test.ts +3 -1
  187. package/templates/database/__tests__/seed.test.ts +54 -2
  188. package/templates/database/inbox/schema.prisma +166 -25
  189. package/templates/database/migrations/20261012120000_member_contract/migration.sql +192 -0
  190. package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +20 -0
  191. package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +122 -0
  192. package/templates/database/migrations/20261014120000_person_contact/migration.sql +231 -0
  193. package/templates/database/package.json +3 -2
  194. package/templates/database/seed.ts +34 -2
  195. package/versions.json +36 -36
  196. package/templates/apps/api/src/__tests__/content-write-production-boot.test.ts +0 -104
  197. package/templates/apps/api/src/__tests__/cors.test.ts +0 -276
  198. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +0 -279
  199. package/templates/apps/api/src/__tests__/openapi-keyless-hub.test.ts +0 -122
  200. package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +0 -167
  201. package/templates/apps/api/src/__tests__/root-modules.test.ts +0 -175
  202. package/templates/apps/api/src/__tests__/schedule-forwarded-rate-limit.test.ts +0 -117
  203. package/templates/apps/api/src/__tests__/server.test.ts +0 -591
  204. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +0 -175
  205. package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +0 -293
  206. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +0 -1620
  207. package/templates/apps/api/src/routes/project-listings/__tests__/directory.test.ts +0 -528
  208. package/templates/apps/api/src/routes/workspaces/__tests__/inbox-wire-schemas.test.ts +0 -198
  209. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +0 -652
  210. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -408
  211. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +0 -476
  212. package/templates/apps/mcp/__tests__/audit.test.ts +0 -346
  213. package/templates/apps/mcp/__tests__/parity.test.ts +0 -325
  214. package/templates/apps/mcp/__tests__/workspace-surface.test.ts +0 -364
  215. package/templates/apps/web/app/[locale]/(authed)/account/settings/color-mode.tsx +0 -68
  216. package/templates/apps/web/app/[locale]/(authed)/account/settings/deletion-request.tsx +0 -55
  217. package/templates/apps/web/app/[locale]/(authed)/account/settings/name-form.tsx +0 -118
  218. package/templates/database/__tests__/build.test.ts +0 -131
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The spoke's session re-seal (#8232, spec
3
+ * docs/specs/8232-member-settings-account-general-and-spec.md D11; roadmap
4
+ * D9).
5
+ *
6
+ * After a member saves their name through the hub (`PATCH /v1/member/profile`),
7
+ * the sealed session cookie still names the person as they were at sign-in.
8
+ * `resealHubSession()` re-reads the person from the hub's own record,
9
+ * `GET /v1/hub-sign-in/me`, with the session's credential, and writes the
10
+ * cookie again so the next render shows the new name:
11
+ *
12
+ * - no hub-session configuration (a single site) or no session cookie ⇒
13
+ * `"skipped"`, with no call;
14
+ * - `/me` refusing, throwing, or answering a person of the wrong shape ⇒
15
+ * `"failed"`, and the cookie is left as it is (sign-out is the sign-out
16
+ * route's job — a transient hub error never signs the member out);
17
+ * - otherwise only `user` changes. `v`, `accessToken` and `expiresAt` are
18
+ * kept exactly and `Max-Age` is the remaining lifetime, so a re-seal
19
+ * never extends a session (SEC-8232-1). The cookie's name, key and
20
+ * attributes are the ones the callback route writes at sign-in
21
+ * (`cookieHeader`: `HttpOnly`, `SameSite=Lax`, `Path=/`, `Secure` on an
22
+ * https site — SEC-8232-2).
23
+ *
24
+ * It tests no topology: a single site has no configuration and no cookie.
25
+ * Nothing here logs a credential, a cookie value, a name or an email.
26
+ */
27
+
28
+ import { hubSignIn } from "@working-theory/api-client";
29
+ import { cookies, headers } from "next/headers";
30
+
31
+ import { hubSessionConfig } from "./config";
32
+ import { seal } from "./seal";
33
+ import { type HubSessionUser, readHubSessionPayload, sessionCookieName } from "./session";
34
+
35
+ /**
36
+ * The person `/me` names, when every field is the type the session holds.
37
+ * It repeats `session.ts`'s private shape check on purpose: `sync-routes`
38
+ * brings this file to a scaffold whose own `session.ts` it never updates,
39
+ * so it cannot rely on a new export there.
40
+ */
41
+ function sessionUser(value: unknown): HubSessionUser | null {
42
+ if (typeof value !== "object" || value === null) return null;
43
+ const user = value as Record<string, unknown>;
44
+ const { name, email, emailVerified, createdAt } = user;
45
+ if (
46
+ typeof email !== "string" ||
47
+ typeof emailVerified !== "boolean" ||
48
+ typeof createdAt !== "string" ||
49
+ (name !== null && typeof name !== "string")
50
+ ) {
51
+ return null;
52
+ }
53
+ return { name, email, emailVerified, createdAt };
54
+ }
55
+
56
+ /** Re-seals the hub session cookie from the hub's `/me`. Never throws. */
57
+ export async function resealHubSession(): Promise<"resealed" | "skipped" | "failed"> {
58
+ const config = hubSessionConfig();
59
+ if (config === null) return "skipped";
60
+ const payload = readHubSessionPayload(await headers());
61
+ if (payload === null) return "skipped";
62
+ const maxAge = Math.floor((payload.expiresAt - Date.now()) / 1000);
63
+ if (maxAge <= 0) return "skipped";
64
+
65
+ let user: HubSessionUser | null;
66
+ try {
67
+ const res = await hubSignIn.me({
68
+ headers: { authorization: `Bearer ${payload.accessToken}` },
69
+ });
70
+ user = res.ok ? sessionUser(res.body.user) : null;
71
+ } catch {
72
+ user = null;
73
+ }
74
+ if (user === null) return "failed";
75
+
76
+ const name = sessionCookieName(config);
77
+ try {
78
+ (await cookies()).set(name, seal({ ...payload, user }, config.sessionSecret, "session", name), {
79
+ httpOnly: true,
80
+ sameSite: "lax",
81
+ path: "/",
82
+ maxAge,
83
+ secure: config.secure,
84
+ });
85
+ } catch {
86
+ // Outside a Server Action or route handler Next refuses a cookie write.
87
+ return "failed";
88
+ }
89
+ return "resealed";
90
+ }
@@ -1,12 +1,15 @@
1
1
  /**
2
- * Sealed cookie values for the spoke's hub session (#7894 D7).
2
+ * Sealed values for the hub sign-in (#7894 D7, #8146 D3).
3
3
  *
4
- * AES-256-GCM with a fresh 12-byte IV per seal. The key is HKDF-SHA256 of
5
- * `AILK_HUB_SESSION_SECRET` with one info string per cookie purpose, so the
6
- * session cookie and the flow cookie never share a key, and the cookie's NAME
7
- * is the additional authenticated data, so a value sealed for one cookie never
8
- * opens as the other. The key never derives from the client secret: a leaked
9
- * client secret forges no spoke session.
4
+ * AES-256-GCM with a fresh 12-byte IV per seal. The key is HKDF-SHA256 of the
5
+ * caller's secret with one info string per purpose, so the three purposes
6
+ * never share a key, and the caller-supplied name is the additional
7
+ * authenticated data, so a value sealed for one name never opens as another.
8
+ * Two secrets feed this module: `AILK_HUB_SESSION_SECRET` for the spoke's
9
+ * `session` and `flow` cookies, and `BETTER_AUTH_SECRET` for the hub's
10
+ * `continue` ref (`app/api/auth/hub/continuation.ts`, hub and standalone
11
+ * sites only; the entry is unused on a spoke). The key never derives from the
12
+ * client secret: a leaked client secret forges no spoke session.
10
13
  *
11
14
  * Wire form: base64url(iv ‖ tag ‖ ciphertext). Anything that does not open —
12
15
  * a wrong key, a wrong name, a truncated or altered value — reads as `null`,
@@ -20,12 +23,13 @@ import {
20
23
  randomBytes,
21
24
  } from "node:crypto";
22
25
 
23
- /** The two things this module seals, each under its own derived key. */
24
- export type SealPurpose = "session" | "flow";
26
+ /** The things this module seals, each under its own derived key. */
27
+ export type SealPurpose = "session" | "flow" | "continue";
25
28
 
26
29
  const INFO: Record<SealPurpose, string> = {
27
30
  session: "ailk-hub-session/v1",
28
31
  flow: "ailk-hub-flow/v1",
32
+ continue: "ailk-hub-continue/v1",
29
33
  };
30
34
 
31
35
  const IV_BYTES = 12;
@@ -7,6 +7,10 @@
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.
12
+ // readOwnAccounts lists the signed-in person's own sign-in accounts through
13
+ // Better Auth on a hub (#8431); a spoke has none here, so it answers null.
10
14
 
11
15
  export const TOPOLOGY: "hub" | "spoke" = "hub";
12
16
  export const UNSERVED_MCP_TOOLS: readonly string[] = [];
@@ -30,7 +34,36 @@ export async function readSession(headers: Headers): Promise<SiteSession> {
30
34
  return auth.api.getSession({ headers });
31
35
  }
32
36
 
37
+ export async function readOwnAccounts(
38
+ headers: Headers,
39
+ ): Promise<ReadonlyArray<{ providerId: string }> | null> {
40
+ const { auth } = await import("../auth");
41
+ return auth.api.listUserAccounts({ headers });
42
+ }
43
+
33
44
  export async function registerSiteStores(): Promise<void> {
34
45
  const { registerLicenseKeyStore } = await import("./license-key-store");
35
46
  await registerLicenseKeyStore();
36
47
  }
48
+
49
+ export type SpokeCard = { brand: string; last4: string; expMonth: number; expYear: number };
50
+
51
+ export type SpokeBillingRead =
52
+ | {
53
+ kind: "subscription";
54
+ status: "trialing" | "active" | "past_due" | "canceled";
55
+ planName: string | null;
56
+ paymentMethod: SpokeCard | null;
57
+ unreadable: boolean;
58
+ }
59
+ | { kind: "none"; unreadable: boolean }
60
+ | { kind: "failed" };
61
+
62
+ export type SpokeBilling = {
63
+ read(): Promise<SpokeBillingRead>;
64
+ portalUrl(): Promise<string | null>;
65
+ };
66
+
67
+ export function loadSpokeBilling(): Promise<SpokeBilling | null> {
68
+ return Promise.resolve(null);
69
+ }
@@ -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,12 +573,64 @@
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",
579
630
  "forYou": "For you",
580
631
  "settings": "Settings",
581
632
  "signOut": "Log out",
633
+ "signOutFailed": "We couldn't log you out. You're still signed in; try again.",
582
634
  "help": "Help",
583
635
  "contactSupport": "Contact support",
584
636
  "helpCenter": "Help center",
@@ -590,7 +642,9 @@
590
642
  "collapseAside": "Collapse panel",
591
643
  "expandAside": "Expand panel",
592
644
  "openAside": "Open For you",
593
- "closeAside": "Close For you"
645
+ "closeAside": "Close For you",
646
+ "library": "Library",
647
+ "artifacts": "Artifacts"
594
648
  },
595
649
  "forYou": {
596
650
  "title": "For you",
@@ -617,10 +671,9 @@
617
671
  "title": "Inbox",
618
672
  "lead": "What changed, and what's coming.",
619
673
  "tabs": {
620
- "recents": "Recents",
621
- "suggested": "Suggested",
622
- "scheduled": "Scheduled",
623
- "all": "All"
674
+ "all": "All",
675
+ "purchases": "Purchases",
676
+ "scheduled": "Scheduled"
624
677
  },
625
678
  "empty": "Nothing here yet.",
626
679
  "error": "We couldn't load everything in your Inbox.",
@@ -642,60 +695,112 @@
642
695
  },
643
696
  "settings": {
644
697
  "title": "Settings",
645
- "lead": "Your profile, sign-in, notifications and privacy.",
698
+ "back": "Back",
699
+ "account": "Account",
646
700
  "general": {
647
701
  "title": "General",
648
- "lead": "Your name, email and appearance.",
702
+ "lead": "Your profile and appearance.",
649
703
  "colorMode": "Color mode",
650
704
  "light": "Light",
651
705
  "auto": "Auto",
652
706
  "dark": "Dark",
653
707
  "name": "Name",
654
- "namePlaceholder": "Your name",
708
+ "photo": "Photo",
709
+ "photoPlaceholder": "https://",
710
+ "photoHint": "A link to a square image, starting with https://. Leave it empty to show your initials.",
655
711
  "email": "Email",
656
712
  "save": "Save changes",
657
713
  "saving": "Saving…",
658
714
  "saved": "Saved.",
659
- "unsaved": "Unsaved changes",
660
- "clean": "No unsaved changes",
661
715
  "nameRequired": "Enter your name.",
662
- "saveError": "We couldn't save your name. Try again.",
663
- "manageOnHub": "Manage your account on our main site"
716
+ "saveError": "We couldn't save your changes. Try again.",
717
+ "photoInvalid": "Enter a link that starts with https://.",
718
+ "unverified": "Confirm your email address first, then try again.",
719
+ "unavailable": "Profile changes aren't available on this site yet.",
720
+ "contact": {
721
+ "title": "Contact details",
722
+ "lead": "Kept on this site, for the people you work with here.",
723
+ "givenName": "First name",
724
+ "familyName": "Last name",
725
+ "telephone": "Phone",
726
+ "address": "Address",
727
+ "companyName": "Company",
728
+ "jobTitle": "Job title",
729
+ "save": "Save contact details",
730
+ "saving": "Saving…",
731
+ "saved": "Saved.",
732
+ "addressIncomplete": "Fill in the address line, city, state or region, postal code and country, or clear the whole address.",
733
+ "unavailable": "Contact details aren't available on this site right now.",
734
+ "errors": {
735
+ "telephone": "Enter the phone number with its country code.",
736
+ "invalid": "Some of these details couldn't be saved. Check them and try again.",
737
+ "unverified": "Confirm your email address first, then try again.",
738
+ "unavailable": "Contact details can't be saved on this site yet.",
739
+ "failed": "We couldn't save your contact details. Try again."
740
+ }
741
+ }
664
742
  },
665
743
  "security": {
666
744
  "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."
745
+ "methodsHeading": "Sign-in methods",
746
+ "passwordLabel": "Password",
747
+ "passwordSet": "Set",
748
+ "passwordNotSet": "Not set",
749
+ "setHint": "Add a password so you can also sign in with your email and password.",
750
+ "changeHint": "Change the password you sign in with.",
751
+ "setNote": "We'll email {email} a secure link to create your password.",
752
+ "changeNote": "We'll email {email} a secure link to change your password.",
753
+ "setButton": "Set password",
754
+ "changeButton": "Change password",
755
+ "sending": "Sending…",
756
+ "sent": "Link sent. It works once, for an hour. Saving a password signs you out everywhere.",
757
+ "sendFailed": "We couldn't send the email. Try again in a few minutes.",
758
+ "logOut": "Log out",
759
+ "logOutFailed": "Log out didn't finish. Try again.",
760
+ "signsInWith": "You sign in with {methods}.",
761
+ "signedInAs": "You're signed in as {email}.",
762
+ "methods": {
763
+ "google": "Google",
764
+ "github": "GitHub",
765
+ "magicLink": "a link sent to {email}",
766
+ "password": "your email and password"
767
+ },
768
+ "managedAt": "Your sign-in is managed at {hub}.",
769
+ "managedUnnamed": "Your sign-in is managed by our main site.",
770
+ "manageOnHub": "Manage sign-in on {hub}",
771
+ "passwordUnavailable": "We couldn't check your password settings."
676
772
  },
677
773
  "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."
774
+ "title": "Notifications"
775
+ },
776
+ "signIn": {
777
+ "lead": "How you sign in to this site."
681
778
  },
682
779
  "privacy": {
683
- "title": "Privacy",
684
780
  "lead": "Cookies and your data.",
685
781
  "cookies": "Cookie settings",
686
782
  "deletion": "Request account deletion",
687
783
  "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.",
784
+ "deletionSentSite": "Sent to the site operator. They'll confirm by email.",
785
+ "deletionSentHub": "Sent to the hub operator. They'll confirm by email.",
786
+ "deletionUnverified": "Confirm your email address first, then ask again.",
787
+ "deletionUnavailable": "This site can't take deletion requests right now.",
689
788
  "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."
789
+ "deletionViaSupport": "To ask for your account to be deleted, contact support.",
790
+ "deletionSupportLink": "Contact support"
692
791
  },
693
792
  "billing": {
694
793
  "title": "Billing",
695
- "lead": "Your subscription.",
696
- "status": "Status",
697
- "accessRevokesAt": "Access ends",
794
+ "lead": "Your plan and payment method.",
795
+ "currentPlan": "Current plan",
796
+ "unnamedPlan": "Your subscription",
797
+ "accessEnds": "Access ends {date}",
798
+ "card": "{brand} ending in {last4}, expires {month}/{year}",
799
+ "noCard": "No card on file",
698
800
  "manage": "Manage billing",
801
+ "none": "You don't have a subscription.",
802
+ "unavailable": "We couldn't load your billing details.",
803
+ "unreadable": "Some of your billing is with another Stripe account, so it isn't shown here.",
699
804
  "statuses": {
700
805
  "trialing": "Trial",
701
806
  "active": "Active",
@@ -731,7 +836,19 @@
731
836
  "offer": "Offer",
732
837
  "amount": "Amount",
733
838
  "period": "Period",
734
- "date": "Date"
839
+ "date": "Date",
840
+ "unavailable": "Not recorded"
841
+ },
842
+ "appointments": {
843
+ "heading": "My appointments",
844
+ "untitled": "Appointment",
845
+ "starts": "Starts",
846
+ "ends": "Ends",
847
+ "statuses": {
848
+ "pending": "Pending",
849
+ "confirmed": "Confirmed",
850
+ "cancelled": "Cancelled"
851
+ }
735
852
  },
736
853
  "pieces": {
737
854
  "heading": "My pieces",
@@ -750,6 +867,50 @@
750
867
  "subscription": {
751
868
  "headline": "Your payment didn't go through",
752
869
  "action": "Update payment method"
870
+ },
871
+ "new": {
872
+ "trigger": "New",
873
+ "close": "Close",
874
+ "loading": "Loading…",
875
+ "loadFailed": "This could not load.",
876
+ "retry": "Try again",
877
+ "submitting": "Sending…",
878
+ "optional": "Optional",
879
+ "actions": {
880
+ "scheduleService": "Schedule service",
881
+ "requestQuote": "Request quote",
882
+ "sendMessage": "Send message"
883
+ },
884
+ "schedule": {
885
+ "service": "Service",
886
+ "duration": "{minutes} min",
887
+ "time": "Time",
888
+ "notes": "Notes",
889
+ "submit": "Book",
890
+ "none": "No times are offered right now. You can send a message instead.",
891
+ "suggest": "Send a message",
892
+ "noSlots": "No open times in the next 14 days."
893
+ },
894
+ "quote": {
895
+ "submit": "Send request"
896
+ },
897
+ "message": {
898
+ "subject": "Subject",
899
+ "body": "Message",
900
+ "link": "Link",
901
+ "linkHelp": "Must start with https://.",
902
+ "submit": "Send"
903
+ },
904
+ "errors": {
905
+ "linkNotHttps": "A link must start with https://.",
906
+ "answersInvalid": "Some answers do not fit this form. Check them and try again.",
907
+ "requestFormOff": "Requests are switched off right now.",
908
+ "meetingTypeNotOffered": "This service is no longer offered.",
909
+ "slotUnavailable": "That time was just taken. Pick another.",
910
+ "tooManyRequests": "Too many requests. Try again later.",
911
+ "invalidInput": "Check what you entered and try again.",
912
+ "failed": "This could not be sent. Try again."
913
+ }
753
914
  }
754
915
  },
755
916
  "admin": {
@@ -1,5 +1,42 @@
1
1
  # @working-theory/database
2
2
 
3
+ ## 0.40.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#8497](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/pull/8497) [`52df359`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/52df359fb2a4d1aff488136126dba16d13e49a26) Thanks [@samuelhenry](https://github.com/samuelhenry)! - Contact details on the client record ([#8435](https://github.com/working-theory-labs/ai-launch-kit-internal/issues/8435), spec docs/specs/8435-contact-details-on-the-client-spec.md).
8
+
9
+ A Person now carries a given name, a family name, an E.164 telephone, a six-part address, a company name and a job title, with the two columns that say who last saved them by hand. `callbacks` and `subscribers` gain a `person_id` foreign key. A capture fills only a blank field of an uncurated Person; the operator edits the contact from the Person page and a client edits their own on the site, from the account General tab. A magnet field's `mapsTo` tag names the contact field it fills: `email`, `name`, `givenName`, `familyName`, `phone`, `address`, `company`, `jobTitle`. Additive; the migration (`20261014120000_person_contact`, its second half the idempotent backfill) deploys before the code.
10
+
11
+ - [#8490](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/pull/8490) [`64adcf9`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/64adcf9b58a3f3be3b42782e14018234b263b9bc) Thanks [@samuelhenry](https://github.com/samuelhenry)! - The consent ledger ([#8438](https://github.com/working-theory-labs/ai-launch-kit-internal/issues/8438), the API half). The per-site consent registry gains a kind (`agreement` · `consent` · `preference`), a plain description, a policy link and a policy version per purpose, and its platform default now declares `service_terms`, `account_terms` and `analytics_cookies`; a registry stored before this change still parses and means what it meant. `consent_grants` becomes the one ledger: each row names its source (`cookie_banner` · `sign_up` · `magnet` · `account_settings` · `email_unsubscribe` · `api`), its policy version and, when known, its Person, and the database refuses to change or truncate a stored row. A new `consent_visitor_links` table joins a banner visitor to the Person a later form identifies. `@working-theory/api-client` gains `consent.recordCookieChoice`, `consent.unsubscribe`, `member.consents` and `member.setConsent`; `@working-theory/validation` gains their wire schemas and the `platform-consents` permission. Run the new migration before deploying code that writes the ledger.
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [[`52df359`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/52df359fb2a4d1aff488136126dba16d13e49a26), [`64adcf9`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/64adcf9b58a3f3be3b42782e14018234b263b9bc)]:
16
+ - @working-theory/validation@0.40.0
17
+ - @working-theory/ids@0.13.0
18
+
19
+ ## 0.39.0
20
+
21
+ ### Minor Changes
22
+
23
+ - [#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.
24
+
25
+ - `@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.
26
+ - `@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.
27
+ - `@working-theory/site-config` — `MeetingsSchema` gains optional `customerSlugs` (no default, duplicates refused).
28
+ - `@working-theory/ids` — the `pver_` prefix (`pieceVersion`).
29
+
30
+ - [#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.
31
+
32
+ ### Patch Changes
33
+
34
+ - [#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.
35
+
36
+ - 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)]:
37
+ - @working-theory/validation@0.39.0
38
+ - @working-theory/ids@0.12.0
39
+
3
40
  ## 0.38.0
4
41
 
5
42
  ### 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