create-ailk 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -0
- package/component-catalog.json +3 -3
- package/dist/add-module.d.ts +7 -0
- package/dist/add-module.js +80 -3
- package/dist/add-route.js +26 -2
- package/dist/adopt.d.ts +74 -0
- package/dist/adopt.js +314 -0
- package/dist/apply-design.d.ts +12 -1
- package/dist/apply-design.js +36 -4
- package/dist/cli.js +103 -3
- package/dist/derive-architecture.d.ts +37 -2
- package/dist/derive-architecture.js +92 -7
- package/dist/design-package.js +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/chrome-readers.d.ts +21 -0
- package/dist/lib/chrome-readers.js +100 -0
- package/dist/lib/declared-env.d.ts +142 -0
- package/dist/lib/declared-env.js +985 -0
- package/dist/lib/env-catalog.d.ts +17 -0
- package/dist/lib/env-catalog.js +234 -0
- package/dist/lib/extract-module-bundle.d.ts +20 -0
- package/dist/lib/extract-module-bundle.js +53 -6
- package/dist/lib/license-key-files.d.ts +6 -1
- package/dist/lib/license-key-files.js +9 -4
- package/dist/lib/module-tier.d.ts +2 -0
- package/dist/lib/module-tier.js +4 -0
- package/dist/module-architecture.d.ts +9 -0
- package/dist/module-architecture.js +82 -0
- package/dist/parse-args.d.ts +27 -0
- package/dist/parse-args.js +89 -0
- package/dist/programmatic.d.ts +28 -0
- package/dist/programmatic.js +181 -11
- package/dist/seed-flows.d.ts +65 -0
- package/dist/seed-flows.js +350 -0
- package/dist/site-chrome.d.ts +4 -0
- package/dist/site-chrome.js +34 -3
- package/dist/site-manifest.d.ts +55 -5
- package/dist/site-manifest.js +121 -12
- package/dist/site-roster.d.ts +13 -0
- package/dist/site-roster.js +22 -8
- package/dist/spoke-checkout.d.ts +42 -0
- package/dist/spoke-checkout.js +359 -0
- package/dist/surfaces.js +7 -0
- package/dist/sync-routes.js +29 -5
- package/dist/topology.d.ts +246 -0
- package/dist/topology.js +942 -0
- package/package.json +3 -3
- package/site-templates/designs/minimalism/DESIGN.md +3 -3
- package/templates/.env.example +10 -0
- package/templates/apps/api/CLAUDE.md +21 -4
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/factories/agent-caller.ts +5 -1
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +95 -16
- package/templates/apps/api/src/__tests__/openapi-keyless-hub.test.ts +122 -0
- package/templates/apps/api/src/__tests__/support/hub-sign-in-fake-db.ts +362 -0
- package/templates/apps/api/src/bin/spokes.ts +62 -1
- package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +75 -0
- package/templates/apps/api/src/config/index.ts +35 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +99 -0
- package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +114 -1
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +66 -5
- package/templates/apps/api/src/lib/__tests__/hub-sign-in-store.test.ts +209 -0
- package/templates/apps/api/src/lib/__tests__/site.test.ts +54 -0
- package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +20 -0
- package/templates/apps/api/src/lib/agent-store.ts +95 -3
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +24 -5
- package/templates/apps/api/src/lib/hub-sign-in-store.ts +419 -0
- package/templates/apps/api/src/lib/site.ts +37 -0
- package/templates/apps/api/src/lib/spoke-store.ts +41 -3
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +4 -0
- package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +310 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +22 -0
- package/templates/apps/api/src/middleware/auth.ts +129 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +23 -0
- package/templates/apps/api/src/openapi/spec.ts +38 -2
- package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +483 -0
- package/templates/apps/api/src/routes/auth/hub-sign-in.ts +201 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +50 -2
- package/templates/apps/api/src/routes/checkout/sessions.ts +7 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +282 -0
- package/templates/apps/api/src/routes/leads/index.ts +69 -6
- package/templates/apps/api/src/routes/webhooks/README.md +39 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +105 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +366 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +133 -2
- package/templates/apps/api/src/server.ts +105 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +4 -4
- package/templates/apps/api/src/services/__tests__/hub-sign-in.test.ts +854 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +492 -0
- package/templates/apps/api/src/services/__tests__/person.test.ts +553 -0
- package/templates/apps/api/src/services/default-tenant.ts +41 -7
- package/templates/apps/api/src/services/hub-sign-in.ts +471 -0
- package/templates/apps/api/src/services/lead-routing.ts +278 -30
- package/templates/apps/api/src/services/person.ts +541 -0
- package/templates/apps/api/src/services/public-site-resolver.ts +48 -0
- package/templates/apps/mcp/__tests__/index.test.ts +5 -5
- package/templates/apps/mcp/src/index.ts +6 -4
- package/templates/apps/mcp/src/lib/content-adapter.ts +2 -2
- package/templates/apps/mcp/src/lib/topology.generated.ts +24 -0
- package/templates/apps/web/.env.example +58 -5
- package/templates/apps/web/CLAUDE.md +1 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-in/next-path.ts +0 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/hub-mode.test.tsx +206 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +256 -3
- package/templates/apps/web/app/[locale]/(authed)/account/account-data.ts +5 -8
- package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/__tests__/route.test.ts +205 -0
- package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/route.ts +82 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/page.tsx +98 -29
- package/templates/apps/web/app/[locale]/(authed)/account/support-actions.ts +6 -3
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +19 -101
- package/templates/apps/web/app/[locale]/(authed)/signed-out-gate.generated.tsx +90 -0
- package/templates/apps/web/app/[locale]/[slug]/[detail]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +66 -14
- package/templates/apps/web/app/[locale]/contact/__tests__/site-contact-lines.test.ts +105 -11
- package/templates/apps/web/app/[locale]/contact/site-contact-lines.ts +12 -8
- package/templates/apps/web/app/[locale]/layout.tsx +83 -18
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +5 -2
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +295 -0
- package/templates/apps/web/app/api/auth/hub/authorize/route.ts +156 -0
- package/templates/apps/web/app/api/hub-session/__tests__/routes.test.ts +341 -0
- package/templates/apps/web/app/api/hub-session/callback/route.ts +168 -0
- package/templates/apps/web/app/api/hub-session/sign-in/route.ts +76 -0
- package/templates/apps/web/app/api/hub-session/sign-out/route.ts +63 -0
- package/templates/apps/web/app/openapi.json/__tests__/route.test.ts +104 -1
- package/templates/apps/web/app/openapi.json/route.ts +72 -7
- package/templates/apps/web/auth.ts +29 -2
- package/templates/apps/web/components/sign-out-trigger.tsx +20 -1
- package/templates/apps/web/components/signed-in-nav.tsx +17 -3
- package/templates/apps/web/instrumentation.ts +7 -4
- package/templates/apps/web/lib/__tests__/account-config.test.ts +5 -2
- package/templates/apps/web/lib/__tests__/closing-footer-path.test.ts +96 -0
- package/templates/apps/web/lib/__tests__/listings-config.test.ts +83 -0
- package/templates/apps/web/lib/__tests__/listings.test.tsx +49 -0
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +49 -1
- package/templates/apps/web/lib/__tests__/relay.test.ts +317 -0
- package/templates/apps/web/lib/__tests__/site-brand.test.ts +52 -0
- package/templates/apps/web/lib/account-config.ts +130 -1
- package/templates/apps/web/lib/api-origin.generated.ts +11 -0
- package/templates/apps/web/lib/closing-footer-path.ts +50 -0
- package/templates/apps/web/lib/hub-session/__tests__/hub-session.test.ts +262 -0
- package/templates/apps/web/lib/hub-session/config.ts +104 -0
- package/templates/apps/web/lib/hub-session/index.ts +23 -0
- package/templates/apps/web/lib/hub-session/seal.ts +82 -0
- package/templates/apps/web/lib/hub-session/session.ts +180 -0
- package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +79 -0
- package/templates/apps/web/lib/listings-config.ts +31 -0
- package/templates/apps/web/lib/listings.tsx +7 -3
- package/templates/apps/web/lib/meeting-slugs.ts +11 -1
- package/templates/apps/web/lib/next-path.ts +35 -0
- package/templates/apps/web/lib/relay.ts +209 -3
- package/templates/apps/web/lib/server-api.ts +11 -1
- package/templates/apps/web/lib/site-brand.tsx +22 -1
- package/templates/apps/web/lib/site-chrome.ts +3 -0
- package/templates/apps/web/lib/topology.generated.ts +33 -0
- package/templates/apps/web/messages/en.json +32 -3
- package/templates/apps/web/package.json +1 -1
- package/templates/database/CHANGELOG.md +51 -0
- package/templates/database/__tests__/agents.db.test.ts +85 -0
- package/templates/database/__tests__/fixtures/person-backfill.json +264 -0
- package/templates/database/__tests__/lead-idempotency-key.db.test.ts +92 -0
- package/templates/database/__tests__/member-area.db.test.ts +245 -0
- package/templates/database/__tests__/person.db.test.ts +584 -0
- package/templates/database/auth/schema.prisma +24 -0
- package/templates/database/config/schema.prisma +13 -0
- package/templates/database/inbox/schema.prisma +111 -0
- package/templates/database/jest.config.cjs +7 -11
- package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +35 -0
- package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +20 -0
- package/templates/database/migrations/20261009120000_agent_grants/migration.sql +33 -0
- package/templates/database/migrations/20261010120000_person/migration.sql +106 -0
- package/templates/database/migrations/20261010121000_person_backfill/migration.sql +132 -0
- package/templates/database/migrations/20261010130000_agent_grants_people_read/migration.sql +20 -0
- package/templates/database/migrations/20261011120000_member_area/migration.sql +158 -0
- package/templates/database/migrations/20261011130000_agent_grants_member_pieces_manage/migration.sql +21 -0
- package/templates/database/package.json +1 -1
- package/templates/database/scripts/person-backfill.sql +132 -0
- package/templates/database/tenancy.ts +53 -0
- package/templates/tools/check-declared-env.ts +37 -0
- package/templates/tools/declared-env.ts +1163 -0
- package/versions.json +35 -34
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The described row create-ailk writes for each env name a spoke-carried app
|
|
3
|
+
* reads (#7874 D10). `planEnvRows` (`declared-env.ts`) takes a name's
|
|
4
|
+
* `description` and `secret` from here; a name missing from it gets a
|
|
5
|
+
* placeholder row a reviewer sees.
|
|
6
|
+
*
|
|
7
|
+
* One plain sentence per name, drawn from the `.env.example` comments
|
|
8
|
+
* (`apps/web`, `apps/mcp`, the repo root) where they exist. The keys are
|
|
9
|
+
* exactly the non-platform names this monorepo's `apps/web` and `apps/mcp`
|
|
10
|
+
* read: `__tests__/check-declared-env.test.ts` scans both with the same
|
|
11
|
+
* scanner and fails on a name with no entry or an entry nothing reads, so a
|
|
12
|
+
* new env read in a spoke-carried app cannot ship undescribed. A
|
|
13
|
+
* `NEXT_PUBLIC_*` name is inlined into the browser bundle, so it is never a
|
|
14
|
+
* secret.
|
|
15
|
+
*/
|
|
16
|
+
import type { EnvCatalogEntry } from "./declared-env.js";
|
|
17
|
+
export declare const ENV_CATALOG: Readonly<Record<string, EnvCatalogEntry>>;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
export const ENV_CATALOG = {
|
|
2
|
+
AILK_API_URL: {
|
|
3
|
+
description: "The hub API origin this site's server calls (forms, flows, listings). Server-only; never NEXT_PUBLIC_.",
|
|
4
|
+
secret: false,
|
|
5
|
+
},
|
|
6
|
+
AILK_AUTH_PASSWORD_RESET: {
|
|
7
|
+
description: 'Set to "1" or "true" to offer password reset once the server-side reset flow is wired.',
|
|
8
|
+
secret: false,
|
|
9
|
+
},
|
|
10
|
+
AILK_AUTH_REQUIRED: {
|
|
11
|
+
description: 'Set to "1" or "true" to require sign-in; unset leaves every route public.',
|
|
12
|
+
secret: false,
|
|
13
|
+
},
|
|
14
|
+
AILK_AUTH_SIGNUP_OPEN: {
|
|
15
|
+
description: 'Set to "1" or "true" to open public sign-up on /sign-up.',
|
|
16
|
+
secret: false,
|
|
17
|
+
},
|
|
18
|
+
AILK_AUTH_TRUSTED_PROXIES: {
|
|
19
|
+
description: "Comma-separated CIDRs of this site's edge or proxy, used to key the sign-in rate limit on the real client address.",
|
|
20
|
+
secret: false,
|
|
21
|
+
},
|
|
22
|
+
AILK_BASE_URL: {
|
|
23
|
+
description: "This site's public origin as the MCP server sees it: the same-origin check for audits and the purchase return URLs.",
|
|
24
|
+
secret: false,
|
|
25
|
+
},
|
|
26
|
+
AILK_CONTENT_ROOT: {
|
|
27
|
+
description: "Overrides the on-disk content root; defaults to <repo-root>/content.",
|
|
28
|
+
secret: false,
|
|
29
|
+
},
|
|
30
|
+
AILK_ENABLED_MODULES: {
|
|
31
|
+
description: "Comma-separated paid modules the MCP server enables; must match the API's value. Unset means none.",
|
|
32
|
+
secret: false,
|
|
33
|
+
},
|
|
34
|
+
AILK_HELP_CENTER_URL: {
|
|
35
|
+
description: "When set, the account area's help menu links to this help center.",
|
|
36
|
+
secret: false,
|
|
37
|
+
},
|
|
38
|
+
AILK_HUB_CLIENT_ID: {
|
|
39
|
+
description: "The spoke's client id at its hub (spokes register --redirect-uri <origin>/api/hub-session/callback). Server-only; set with --hub-sign-in.",
|
|
40
|
+
secret: false,
|
|
41
|
+
},
|
|
42
|
+
AILK_HUB_CLIENT_SECRET: {
|
|
43
|
+
description: "The spoke's client secret at its hub, shown once at registration. Server-only; never NEXT_PUBLIC_, never committed.",
|
|
44
|
+
secret: true,
|
|
45
|
+
},
|
|
46
|
+
AILK_HUB_SESSION_SECRET: {
|
|
47
|
+
description: "Seals this spoke's own hub-session cookie (create-ailk writes one; `openssl rand -base64 32` makes another). Never the client secret; rotating it signs every visitor out.",
|
|
48
|
+
secret: true,
|
|
49
|
+
},
|
|
50
|
+
AILK_HUB_SIGN_IN_URL: {
|
|
51
|
+
description: "The hub's WEB origin where its /sign-in lives (https:, or http: on localhost). Server-only; create-ailk writes it to .env.local.",
|
|
52
|
+
secret: false,
|
|
53
|
+
},
|
|
54
|
+
AILK_LEAD_SITE: {
|
|
55
|
+
description: "Marks which site of a hub and its spokes captured an MCP lead; unset leaves the lead unmarked.",
|
|
56
|
+
secret: false,
|
|
57
|
+
},
|
|
58
|
+
AILK_LICENSE_STORAGE_PATH: {
|
|
59
|
+
description: "The file the daily-refreshed licence key is written to; defaults to .ailk-license.jwt in the working directory.",
|
|
60
|
+
secret: false,
|
|
61
|
+
},
|
|
62
|
+
AILK_LISTING_PUBLIC_KEY: {
|
|
63
|
+
description: "The site's public listing key, read on the server when NEXT_PUBLIC_AILK_LISTING_SITE_KEY is unset. Public by design: it names a site and grants nothing.",
|
|
64
|
+
secret: false,
|
|
65
|
+
},
|
|
66
|
+
AILK_ROBOTS_DISALLOW: {
|
|
67
|
+
description: 'Comma-separated paths robots.txt disallows, e.g. "/admin/,/private/".',
|
|
68
|
+
secret: false,
|
|
69
|
+
},
|
|
70
|
+
AILK_SITE_ID: {
|
|
71
|
+
description: "The scope captured leads are stamped with; on a single-tenant install, the default workspace id.",
|
|
72
|
+
secret: false,
|
|
73
|
+
},
|
|
74
|
+
AILK_VISITOR_FORWARD_KEY: {
|
|
75
|
+
description: "This spoke's key for forwarding each visitor's IP to the hub, so the hub rate-limits per visitor. Server-only.",
|
|
76
|
+
secret: true,
|
|
77
|
+
},
|
|
78
|
+
ANALYZE: {
|
|
79
|
+
description: 'Set to "true" to run the bundle analyzer during next build.',
|
|
80
|
+
secret: false,
|
|
81
|
+
},
|
|
82
|
+
AUTH_EMAIL_FROM: {
|
|
83
|
+
description: "The From address on sign-in and password-reset mail.",
|
|
84
|
+
secret: false,
|
|
85
|
+
},
|
|
86
|
+
AUTH_EMAIL_SERVER: {
|
|
87
|
+
description: "The mail server connection that enables the email sign-in method.",
|
|
88
|
+
secret: true,
|
|
89
|
+
},
|
|
90
|
+
AUTH_EMAIL_WEBHOOK_SECRET: {
|
|
91
|
+
description: "Signs the hop that asks the API to send auth mail; must match the API's value. Deployment-only.",
|
|
92
|
+
secret: true,
|
|
93
|
+
},
|
|
94
|
+
BETTER_AUTH_SECRET: {
|
|
95
|
+
description: "Signs session cookies and email-verification tokens. Required in production.",
|
|
96
|
+
secret: true,
|
|
97
|
+
},
|
|
98
|
+
BETTER_AUTH_URL: {
|
|
99
|
+
description: "The origin Better Auth builds OAuth callbacks and password-reset links on.",
|
|
100
|
+
secret: false,
|
|
101
|
+
},
|
|
102
|
+
BETTER_STACK_HEARTBEAT_URL: {
|
|
103
|
+
description: "Better Stack heartbeat URL pinged on cron and startup; unset turns the heartbeat off.",
|
|
104
|
+
secret: true,
|
|
105
|
+
},
|
|
106
|
+
CONTENT_REVALIDATE_SECRET: {
|
|
107
|
+
description: "The shared secret /api/revalidate checks the API's publish webhook against; the same value as the API's.",
|
|
108
|
+
secret: true,
|
|
109
|
+
},
|
|
110
|
+
DATABASE_URL: {
|
|
111
|
+
description: "The database connection string, for the database content adapter and the licence-key store.",
|
|
112
|
+
secret: true,
|
|
113
|
+
},
|
|
114
|
+
GITHUB_CLIENT_ID: {
|
|
115
|
+
description: "GitHub OAuth app client id; set with GITHUB_CLIENT_SECRET to turn on GitHub sign-in.",
|
|
116
|
+
secret: false,
|
|
117
|
+
},
|
|
118
|
+
GITHUB_CLIENT_SECRET: {
|
|
119
|
+
description: "GitHub OAuth app client secret; set with GITHUB_CLIENT_ID to turn on GitHub sign-in.",
|
|
120
|
+
secret: true,
|
|
121
|
+
},
|
|
122
|
+
GOOGLE_CLIENT_ID: {
|
|
123
|
+
description: "Google OAuth client id; set with GOOGLE_CLIENT_SECRET to turn on Google sign-in.",
|
|
124
|
+
secret: false,
|
|
125
|
+
},
|
|
126
|
+
GOOGLE_CLIENT_SECRET: {
|
|
127
|
+
description: "Google OAuth client secret; set with GOOGLE_CLIENT_ID to turn on Google sign-in.",
|
|
128
|
+
secret: true,
|
|
129
|
+
},
|
|
130
|
+
MCP_ABUSE_BLOCKED_USER_AGENTS: {
|
|
131
|
+
description: "Comma-separated User-Agent substrings the MCP server blocks, beyond its built-in list.",
|
|
132
|
+
secret: false,
|
|
133
|
+
},
|
|
134
|
+
MCP_CORS_WRITE_ALLOWED_ORIGINS: {
|
|
135
|
+
description: "Comma-separated origins allowed to call the MCP write tools; empty allows no cross-origin writes.",
|
|
136
|
+
secret: false,
|
|
137
|
+
},
|
|
138
|
+
MCP_HTTP_PATH: {
|
|
139
|
+
description: "The URL path of the MCP HTTP endpoint; defaults to /mcp.",
|
|
140
|
+
secret: false,
|
|
141
|
+
},
|
|
142
|
+
MCP_HTTP_PORT: {
|
|
143
|
+
description: "The port the MCP HTTP transport binds; defaults to 8080.",
|
|
144
|
+
secret: false,
|
|
145
|
+
},
|
|
146
|
+
MCP_PUBLIC_HTTP_URL: {
|
|
147
|
+
description: "The public URL of the MCP HTTP transport, advertised by /.well-known/mcp.json.",
|
|
148
|
+
secret: false,
|
|
149
|
+
},
|
|
150
|
+
MCP_RATE_LIMIT_READ_PER_MIN: {
|
|
151
|
+
description: "Maximum MCP read-tool requests per minute per IP; defaults to 60.",
|
|
152
|
+
secret: false,
|
|
153
|
+
},
|
|
154
|
+
MCP_RATE_LIMIT_WRITE_PER_MIN: {
|
|
155
|
+
description: "Maximum MCP write-tool requests per minute per IP; defaults to 10.",
|
|
156
|
+
secret: false,
|
|
157
|
+
},
|
|
158
|
+
MCP_TRANSPORT: {
|
|
159
|
+
description: 'Which MCP transport to boot: "stdio" (the default) or "http".',
|
|
160
|
+
secret: false,
|
|
161
|
+
},
|
|
162
|
+
NEXT_PUBLIC_AILK_API_URL: {
|
|
163
|
+
description: "The API origin the browser reads on a hub. A spoke never sets it: its hub origin is server-only (AILK_API_URL).",
|
|
164
|
+
secret: false,
|
|
165
|
+
},
|
|
166
|
+
NEXT_PUBLIC_AILK_LISTING_SITE_KEY: {
|
|
167
|
+
description: "The site's public listing key, inlined at build; it switches the listicle directory on. Public by design.",
|
|
168
|
+
secret: false,
|
|
169
|
+
},
|
|
170
|
+
NEXT_PUBLIC_API_URL: {
|
|
171
|
+
description: "Deprecated fallback for the API origin; do not set it in a new deployment.",
|
|
172
|
+
secret: false,
|
|
173
|
+
},
|
|
174
|
+
NEXT_PUBLIC_BASE_URL: {
|
|
175
|
+
description: "This site's public origin, scheme and host only: canonical URLs and Stripe return URLs are built on it.",
|
|
176
|
+
secret: false,
|
|
177
|
+
},
|
|
178
|
+
NEXT_PUBLIC_DEFAULT_LOCALE: {
|
|
179
|
+
description: "The site's default locale; defaults to en.",
|
|
180
|
+
secret: false,
|
|
181
|
+
},
|
|
182
|
+
NEXT_PUBLIC_ENABLED_LOCALES: {
|
|
183
|
+
description: "The site's locales, baked at build from project.yaml; do not set by hand.",
|
|
184
|
+
secret: false,
|
|
185
|
+
},
|
|
186
|
+
NEXT_PUBLIC_GOOGLE_TAG_ID: {
|
|
187
|
+
description: "Google Analytics 4 measurement ID (G-XXXXXXXXXX); unset sends no analytics.",
|
|
188
|
+
secret: false,
|
|
189
|
+
},
|
|
190
|
+
NEXT_PUBLIC_SENTRY_DSN: {
|
|
191
|
+
description: "Sentry DSN for browser error capture; unset turns it off. Public by design.",
|
|
192
|
+
secret: false,
|
|
193
|
+
},
|
|
194
|
+
NEXT_PUBLIC_SITE_NAME: {
|
|
195
|
+
description: "The site name shown where the site's content files cannot be read (the OG image, the admin pages).",
|
|
196
|
+
secret: false,
|
|
197
|
+
},
|
|
198
|
+
NEXT_PUBLIC_SITE_WORDMARK: {
|
|
199
|
+
description: "The wordmark the generated OG image shows.",
|
|
200
|
+
secret: false,
|
|
201
|
+
},
|
|
202
|
+
NEXT_PUBLIC_THEME_COOKIE_NAME: {
|
|
203
|
+
description: "The cookie the colour-mode choice is stored in; defaults to ailk-theme.",
|
|
204
|
+
secret: false,
|
|
205
|
+
},
|
|
206
|
+
OSS_API_INTERNAL_SECRET: {
|
|
207
|
+
description: "Shared secret the /dev/purchase action sends to the API's checkout route; must equal the API's value. Server-only.",
|
|
208
|
+
secret: true,
|
|
209
|
+
},
|
|
210
|
+
SCHEDULING_CAL_LINK: {
|
|
211
|
+
description: 'Which event /meet books, as "<owner>/<event-slug>"; unset makes /meet 404.',
|
|
212
|
+
secret: false,
|
|
213
|
+
},
|
|
214
|
+
SENTRY_AUTH_TOKEN: {
|
|
215
|
+
description: "Build-time Sentry token that uploads source maps; unset skips the upload.",
|
|
216
|
+
secret: true,
|
|
217
|
+
},
|
|
218
|
+
SENTRY_DSN: {
|
|
219
|
+
description: "Sentry DSN for server-side error capture; unset turns it off.",
|
|
220
|
+
secret: false,
|
|
221
|
+
},
|
|
222
|
+
SENTRY_ORG: {
|
|
223
|
+
description: "The Sentry organisation slug used for the source-map upload.",
|
|
224
|
+
secret: false,
|
|
225
|
+
},
|
|
226
|
+
SENTRY_PROJECT: {
|
|
227
|
+
description: "The Sentry project slug used for the source-map upload.",
|
|
228
|
+
secret: false,
|
|
229
|
+
},
|
|
230
|
+
STEALTH: {
|
|
231
|
+
description: 'Set to "true" to keep the site out of search indexes (noindex headers) until launch.',
|
|
232
|
+
secret: false,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
@@ -49,6 +49,10 @@ export interface ExtractModuleBundleOptions {
|
|
|
49
49
|
existingFilePolicy?: ExistingFilePolicy;
|
|
50
50
|
/** A caller check on the manifest's `patches` (#6653 D3). See `InstallBundleTreeOptions`. */
|
|
51
51
|
checkPatches?: (patches: readonly ModulePatch[]) => void;
|
|
52
|
+
/** Destinations this scaffold does not carry (#7426 D4). See `InstallBundleTreeOptions`. */
|
|
53
|
+
skip?: (destination: string) => boolean;
|
|
54
|
+
/** Where an installMap destination lands instead (#7876 D10). See `InstallBundleTreeOptions`. */
|
|
55
|
+
remapDestination?: (destination: string) => string;
|
|
52
56
|
}
|
|
53
57
|
/**
|
|
54
58
|
* Extract a fetched module bundle into `targetDir`, merging its files onto
|
|
@@ -131,6 +135,22 @@ export interface InstallBundleTreeOptions {
|
|
|
131
135
|
* bundle's patches to the plus-config allowlist.
|
|
132
136
|
*/
|
|
133
137
|
checkPatches?: (patches: readonly ModulePatch[]) => void;
|
|
138
|
+
/**
|
|
139
|
+
* Scaffold-relative destinations this scaffold does not carry (#7426 D4) —
|
|
140
|
+
* a spoke passes `topology.ts` `isSpokeExcluded`. A skipped destination is
|
|
141
|
+
* never written: a plain file or an `installMap` destination it names is
|
|
142
|
+
* dropped, and so is a `patches` entry whose target it names. It is NOT an
|
|
143
|
+
* ownership claim, and a skipped patch target is not a missing one. Every
|
|
144
|
+
* entry is still validated as before. Absent ⟹ nothing is skipped.
|
|
145
|
+
*/
|
|
146
|
+
skip?: (destination: string) => boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Where an `installMap` destination lands instead (#7876 D10) — a spoke
|
|
149
|
+
* passes `topology.ts` `spokeDestination`. Applied after the installMap is
|
|
150
|
+
* validated and before `skip`; a remapped destination that leaves the
|
|
151
|
+
* target refuses the tree. Absent ⟹ every destination is unchanged.
|
|
152
|
+
*/
|
|
153
|
+
remapDestination?: (destination: string) => string;
|
|
134
154
|
}
|
|
135
155
|
/** See `InstallBundleTreeOptions.existingFilePolicy`. */
|
|
136
156
|
export type ExistingFilePolicy = "overwrite" | "skip-existing";
|
|
@@ -385,6 +385,24 @@ function validateLayerOwnership(destinations, ownership, subject, isTemplate, pa
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
|
+
/** Every regular file under `srcRoot`, as a `/`-separated relative path. */
|
|
389
|
+
function stagedFiles(srcRoot) {
|
|
390
|
+
const out = [];
|
|
391
|
+
const stack = [srcRoot];
|
|
392
|
+
while (stack.length > 0) {
|
|
393
|
+
const dir = stack.pop();
|
|
394
|
+
if (dir === undefined)
|
|
395
|
+
break;
|
|
396
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
397
|
+
const srcPath = join(dir, entry.name);
|
|
398
|
+
if (entry.isDirectory())
|
|
399
|
+
stack.push(srcPath);
|
|
400
|
+
else if (entry.isFile())
|
|
401
|
+
out.push(relative(srcRoot, srcPath).split(sep).join("/"));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return out;
|
|
405
|
+
}
|
|
388
406
|
/**
|
|
389
407
|
* Every scaffold-relative destination this tree will write: each unmapped
|
|
390
408
|
* staged file at its own path (exactly the set `copyTree` lands, same walk,
|
|
@@ -457,6 +475,10 @@ export async function extractModuleBundle(options) {
|
|
|
457
475
|
...(options.checkPatches !== undefined
|
|
458
476
|
? { checkPatches: options.checkPatches }
|
|
459
477
|
: {}),
|
|
478
|
+
...(options.skip !== undefined ? { skip: options.skip } : {}),
|
|
479
|
+
...(options.remapDestination !== undefined
|
|
480
|
+
? { remapDestination: options.remapDestination }
|
|
481
|
+
: {}),
|
|
460
482
|
});
|
|
461
483
|
}
|
|
462
484
|
finally {
|
|
@@ -484,13 +506,32 @@ export function installBundleTree(options) {
|
|
|
484
506
|
const manifest = isTemplate
|
|
485
507
|
? readAilkTemplateManifest(sourceRoot, name, templateRequires, templateExtends)
|
|
486
508
|
: readAilkModuleManifest(sourceRoot);
|
|
487
|
-
const
|
|
488
|
-
validateInstallMap(
|
|
509
|
+
const declaredInstallMap = manifest?.installMap ?? {};
|
|
510
|
+
validateInstallMap(declaredInstallMap, sourceRoot, targetAbs, subject, isTemplate);
|
|
511
|
+
// #7876 D10 — a spoke lands some destinations elsewhere (its module flows
|
|
512
|
+
// become declared data). The remapped destination passes the same
|
|
513
|
+
// containment rule `validateInstallMap` applies, or the tree is refused.
|
|
514
|
+
const remap = options.remapDestination;
|
|
515
|
+
const remappedInstallMap = {};
|
|
516
|
+
for (const [source, declared] of Object.entries(declaredInstallMap)) {
|
|
517
|
+
const destination = remap !== undefined ? remap(declared) : declared;
|
|
518
|
+
if (destination !== declared &&
|
|
519
|
+
(hasUnsafeSegment(destination) ||
|
|
520
|
+
!isPathInside(targetAbs, resolve(targetAbs, destination)))) {
|
|
521
|
+
throw new Error(`${subject}: remapped installMap destination "${destination}" resolves outside target`);
|
|
522
|
+
}
|
|
523
|
+
remappedInstallMap[source] = destination;
|
|
524
|
+
}
|
|
525
|
+
// #7426 D4 — a destination the scaffold does not carry is dropped here,
|
|
526
|
+
// after validation, so a spoke never receives it and never records it.
|
|
527
|
+
const skip = options.skip;
|
|
528
|
+
const isSkipped = (destination) => skip !== undefined && skip(normalize(destination).split(sep).join("/"));
|
|
529
|
+
const installMap = Object.fromEntries(Object.entries(remappedInstallMap).filter(([, destination]) => !isSkipped(destination)));
|
|
489
530
|
// patches validation (spec 5418 § 3 D6) runs at the same point, before
|
|
490
531
|
// any copy — a bad target/kind/op rejects the whole tree with nothing
|
|
491
532
|
// written to targetDir, same posture as installMap and the tar-entry
|
|
492
533
|
// validation above.
|
|
493
|
-
const
|
|
534
|
+
const declaredPatches = manifest?.patches ?? [];
|
|
494
535
|
// #5514 (Medium) — content, never code (spec 5462 D9/D16): every `patches`
|
|
495
536
|
// kind either edits a code file directly (`next-config`, `jest-config`)
|
|
496
537
|
// or a manifest a template has no legitimate reason to touch
|
|
@@ -498,10 +539,13 @@ export function installBundleTree(options) {
|
|
|
498
539
|
// content template) — no shipped template uses it, so the simplest
|
|
499
540
|
// fail-closed reading refuses it outright rather than enumerating an
|
|
500
541
|
// allowed subset that has no current user.
|
|
501
|
-
if (isTemplate &&
|
|
542
|
+
if (isTemplate && declaredPatches.length > 0) {
|
|
502
543
|
throw new Error(`${subject}: a template must not carry patches — content, never code (spec 5462 D9)`);
|
|
503
544
|
}
|
|
504
|
-
|
|
545
|
+
// Every declared patch is validated, skipped or not (#7426 D4), exactly as
|
|
546
|
+
// the installMap is above; only then are the skipped ones dropped.
|
|
547
|
+
validatePatches(declaredPatches, targetAbs, subject);
|
|
548
|
+
const patches = declaredPatches.filter((patch) => !isSkipped(patch.target));
|
|
505
549
|
options.checkPatches?.(patches);
|
|
506
550
|
// The exclusion set copyTree/the content-only guard both use — built from
|
|
507
551
|
// NORMALIZED installMap keys (#5514) so a "./x" key excludes the same
|
|
@@ -512,7 +556,10 @@ export function installBundleTree(options) {
|
|
|
512
556
|
const excluded = new Set([
|
|
513
557
|
AILK_TEMPLATE_JSON,
|
|
514
558
|
AILK_MODULE_JSON,
|
|
515
|
-
...Object.keys(
|
|
559
|
+
...Object.keys(declaredInstallMap).map((key) => normalize(key).split(sep).join("/")),
|
|
560
|
+
// A plain file lands at its own staged path, so that path IS its
|
|
561
|
+
// destination: a skipped one is excluded from the copy like a mapped source.
|
|
562
|
+
...(skip !== undefined ? stagedFiles(sourceRoot).filter(isSkipped) : []),
|
|
516
563
|
]);
|
|
517
564
|
if (isTemplate) {
|
|
518
565
|
// #6658 D7 — a template's roster of record lands as JSON, through its
|
|
@@ -20,5 +20,10 @@ export declare function appendLine(text: string, line: string): string;
|
|
|
20
20
|
* `apps/api/.env` line (0600, other lines kept), and a `.gitignore` entry for
|
|
21
21
|
* `ailk.key` when no existing line covers it. Idempotent — a second call with
|
|
22
22
|
* the same key changes no byte.
|
|
23
|
+
*
|
|
24
|
+
* `apiEnv: false` skips the `apps/api/.env` line — a spoke (#7426 D6) has no
|
|
25
|
+
* `apps/api`, so it keeps the key in `ailk.key` alone.
|
|
23
26
|
*/
|
|
24
|
-
export declare function writeLicenseKeyFiles(dir: string, key: string
|
|
27
|
+
export declare function writeLicenseKeyFiles(dir: string, key: string, opts?: {
|
|
28
|
+
apiEnv?: boolean;
|
|
29
|
+
}): void;
|
|
@@ -85,13 +85,18 @@ function upsertEnvLine(existing, key) {
|
|
|
85
85
|
* `apps/api/.env` line (0600, other lines kept), and a `.gitignore` entry for
|
|
86
86
|
* `ailk.key` when no existing line covers it. Idempotent — a second call with
|
|
87
87
|
* the same key changes no byte.
|
|
88
|
+
*
|
|
89
|
+
* `apiEnv: false` skips the `apps/api/.env` line — a spoke (#7426 D6) has no
|
|
90
|
+
* `apps/api`, so it keeps the key in `ailk.key` alone.
|
|
88
91
|
*/
|
|
89
|
-
export function writeLicenseKeyFiles(dir, key) {
|
|
92
|
+
export function writeLicenseKeyFiles(dir, key, opts = {}) {
|
|
90
93
|
assertLicenseKeyShape(key);
|
|
91
94
|
writeOwnerOnly(join(dir, LICENSE_KEY_FILE), `${key}\n`);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
if (opts.apiEnv !== false) {
|
|
96
|
+
const envPath = join(dir, LICENSE_ENV_FILE);
|
|
97
|
+
const env = existsSync(envPath) ? readFileSync(envPath, "utf8") : "";
|
|
98
|
+
writeOwnerOnly(envPath, upsertEnvLine(env, key));
|
|
99
|
+
}
|
|
95
100
|
const ignorePath = join(dir, ".gitignore");
|
|
96
101
|
const ignore = existsSync(ignorePath) ? readFileSync(ignorePath, "utf8") : "";
|
|
97
102
|
const covered = ignore
|
|
@@ -30,6 +30,8 @@ export declare const ADD_ONS: readonly ModuleName[];
|
|
|
30
30
|
* adds it without trimming, exactly like an add-on.
|
|
31
31
|
*/
|
|
32
32
|
export declare const OPT_IN_MODULES: readonly ModuleName[];
|
|
33
|
+
/** Whether naming `m` in `--modules` adds it without trimming the level default. */
|
|
34
|
+
export declare function isAddition(m: ModuleName): boolean;
|
|
33
35
|
/**
|
|
34
36
|
* What a key at each level switches on with no `--modules` (D2). Derived —
|
|
35
37
|
* every list is `RECOGNIZED_MODULES.filter(...)`, so declaration order is
|
package/dist/lib/module-tier.js
CHANGED
|
@@ -54,6 +54,10 @@ export const OPT_IN_MODULES = ["workspace"];
|
|
|
54
54
|
const PRO_PLUS_MODULES = ["multi-tenant"];
|
|
55
55
|
/** Named modules that never trim the level default when composing a set (D2). */
|
|
56
56
|
const ADDITIONS = [...ADD_ONS, ...OPT_IN_MODULES];
|
|
57
|
+
/** Whether naming `m` in `--modules` adds it without trimming the level default. */
|
|
58
|
+
export function isAddition(m) {
|
|
59
|
+
return ADDITIONS.includes(m);
|
|
60
|
+
}
|
|
57
61
|
const PRO_EXCLUDED = new Set([...ADDITIONS, ...PRO_PLUS_MODULES]);
|
|
58
62
|
/**
|
|
59
63
|
* What a key at each level switches on with no `--modules` (D2). Derived —
|
|
@@ -83,6 +83,15 @@ export interface PageRosterRow {
|
|
|
83
83
|
* scaffold carries (#6940).
|
|
84
84
|
*/
|
|
85
85
|
export interface ModuleSlice {
|
|
86
|
+
/**
|
|
87
|
+
* Admits the module on a spoke (`create-ailk --topology spoke`, #7426 D7).
|
|
88
|
+
* `true` → a spoke may carry it, and its bundle installs through the spoke
|
|
89
|
+
* filter (`topology.ts` `isSpokeExcluded`), so any `apps/api` or
|
|
90
|
+
* `database` part it carries is dropped. Unset → refused on a spoke.
|
|
91
|
+
* `topology.ts` `spokeModules()` reads it; a sibling issue admits its
|
|
92
|
+
* module by setting it here, never in a second list.
|
|
93
|
+
*/
|
|
94
|
+
spoke?: true;
|
|
86
95
|
/**
|
|
87
96
|
* The paid UI package base-tree code loads through
|
|
88
97
|
* `apps/web/lib/paid-sections.generated.ts` (#7536). Present → one row in the
|
|
@@ -150,6 +150,23 @@ export const ARCHITECTURE_BASE = {
|
|
|
150
150
|
},
|
|
151
151
|
],
|
|
152
152
|
};
|
|
153
|
+
/**
|
|
154
|
+
* The member area's apps/api files (#7877 D9, D11), co-owned by `workspace`
|
|
155
|
+
* and `multi-tenant`: server.ts registers all five routes under the
|
|
156
|
+
* `workspace || multi-tenant` disjunction, so both bundles must deliver every
|
|
157
|
+
* file behind it — the three operator route files, the two member route
|
|
158
|
+
* files, the member resolution (`services/member-area.ts`) and the member
|
|
159
|
+
* data boundary (`lib/member-store.ts`).
|
|
160
|
+
*/
|
|
161
|
+
const MEMBER_AREA_FILES = [
|
|
162
|
+
"apps/api/src/routes/workspaces/pieces.ts",
|
|
163
|
+
"apps/api/src/routes/workspaces/plan.ts",
|
|
164
|
+
"apps/api/src/routes/workspaces/messages-out.ts",
|
|
165
|
+
"apps/api/src/routes/member/pieces.ts",
|
|
166
|
+
"apps/api/src/routes/member/messages.ts",
|
|
167
|
+
"apps/api/src/services/member-area.ts",
|
|
168
|
+
"apps/api/src/lib/member-store.ts",
|
|
169
|
+
];
|
|
153
170
|
/**
|
|
154
171
|
* The workspace asset store (#5854), CO-OWNED by the `workspace` and
|
|
155
172
|
* `multi-tenant` slices.
|
|
@@ -206,6 +223,7 @@ function payloadPaths(kind, name, destRoot) {
|
|
|
206
223
|
export const MODULE_SLICES = {
|
|
207
224
|
// ── marketing — thin presentation module (the default) ───────────────────
|
|
208
225
|
marketing: {
|
|
226
|
+
spoke: true,
|
|
209
227
|
webImports: [
|
|
210
228
|
WEB_AUTH_ADAPTER_IMPORT,
|
|
211
229
|
"packages/content-adapters",
|
|
@@ -287,6 +305,7 @@ export const MODULE_SLICES = {
|
|
|
287
305
|
},
|
|
288
306
|
// ── docs-blog — Pro module: both fumadocs surfaces (D-46) ────────────────
|
|
289
307
|
"docs-blog": {
|
|
308
|
+
spoke: true,
|
|
290
309
|
webImports: [WEB_AUTH_ADAPTER_IMPORT, "packages/fumadocs-schema"],
|
|
291
310
|
packages: {
|
|
292
311
|
"fumadocs-schema": {
|
|
@@ -797,6 +816,15 @@ export const MODULE_SLICES = {
|
|
|
797
816
|
"apps/api/src/routes/workspaces/submissions.ts",
|
|
798
817
|
"apps/api/src/routes/workspaces/subscribers.ts",
|
|
799
818
|
"apps/api/src/services/inbox-submissions.ts",
|
|
819
|
+
// #852 D8 — the People reads: the same workspace||multi-tenant gate and
|
|
820
|
+
// the same tenant-db.ts import, co-owned with `workspace` for the same
|
|
821
|
+
// reason.
|
|
822
|
+
"apps/api/src/routes/workspaces/people.ts",
|
|
823
|
+
// #7877 D9, D11 — the member area: the operator's routes and the member
|
|
824
|
+
// reads register under the same workspace||multi-tenant gate, so each
|
|
825
|
+
// file behind it — the routes, the member resolution and the member
|
|
826
|
+
// data boundary — is co-owned with `workspace`, as people.ts is.
|
|
827
|
+
...MEMBER_AREA_FILES,
|
|
800
828
|
// #7399 — the two siblings #6216 left `workspace`-only. They register
|
|
801
829
|
// under that same `workspace || multi-tenant` gate, so a scaffold with
|
|
802
830
|
// `multi-tenant` and without `workspace` loaded them with no file behind
|
|
@@ -945,6 +973,10 @@ export const MODULE_SLICES = {
|
|
|
945
973
|
"apps/api/src/routes/workspaces/submissions.ts",
|
|
946
974
|
"apps/api/src/routes/workspaces/subscribers.ts",
|
|
947
975
|
"apps/api/src/services/inbox-submissions.ts",
|
|
976
|
+
// Co-owned with `multi-tenant` (#852 D8), as the reads above.
|
|
977
|
+
"apps/api/src/routes/workspaces/people.ts",
|
|
978
|
+
// Co-owned with `multi-tenant` (#7877 D9, D11) — see that slice's entry.
|
|
979
|
+
...MEMBER_AREA_FILES,
|
|
948
980
|
// Co-owned with `multi-tenant` (#5853) — see that slice's entry.
|
|
949
981
|
"apps/api/src/routes/workspaces/content.ts",
|
|
950
982
|
...ASSET_STORE_FILES,
|
|
@@ -1003,6 +1035,15 @@ export const MODULE_SLICES = {
|
|
|
1003
1035
|
// scheduling-bearing scaffold's `apps/api` really does contain the billing,
|
|
1004
1036
|
// checkout and email routes those imports are for.
|
|
1005
1037
|
scheduling: {
|
|
1038
|
+
// #7872 — admitted on a spoke. Its bundle installs through the spoke
|
|
1039
|
+
// filter (`isSpokeExcluded`), which drops every destination under
|
|
1040
|
+
// apps/api or database — the routes, services/scheduling (the provider
|
|
1041
|
+
// client and the only reader of the credential), the Cal.com webhook —
|
|
1042
|
+
// and the (authed)/appointments page. What lands is the web tier: the
|
|
1043
|
+
// three apps/web/app/[locale]/meet pages and the @working-theory/ui-scheduling
|
|
1044
|
+
// dependency (`patches` below), plus the three MCP tools when apps/mcp is
|
|
1045
|
+
// kept. Every booking call then reaches the hub with the spoke key.
|
|
1046
|
+
spoke: true,
|
|
1006
1047
|
apps: {
|
|
1007
1048
|
api: {
|
|
1008
1049
|
path: "apps/api",
|
|
@@ -1339,6 +1380,33 @@ export const MODULE_SLICES = {
|
|
|
1339
1380
|
// cannot place an unreached file either) is unchanged and still governs
|
|
1340
1381
|
// where all eleven live.
|
|
1341
1382
|
"signal-collectors": {
|
|
1383
|
+
// #7876 D8 — admitted on a spoke, so a Pro spoke carries the flow
|
|
1384
|
+
// stepper and its flows reach the hub with the spoke key. Its bundle
|
|
1385
|
+
// installs through the spoke filter (`isSpokeExcluded`) and the D10
|
|
1386
|
+
// remap (`spokeDestination`); every part of the slice lands as follows:
|
|
1387
|
+
//
|
|
1388
|
+
// apps/web/app/[locale]/flows/** (its `flows` surface) lands
|
|
1389
|
+
// apps/web/app/[locale]/reports/**, the ReportPage + ReportsList seeds
|
|
1390
|
+
// dropped (`SPOKE_EXCLUDED_MODULE_PATHS`,
|
|
1391
|
+
// 2026-10-09): the reader links a report's
|
|
1392
|
+
// bytes on the hub through
|
|
1393
|
+
// NEXT_PUBLIC_AILK_API_URL, refused on a
|
|
1394
|
+
// spoke (#7874); deliverables are hub-side
|
|
1395
|
+
// ownedFiles under apps/api/** dropped (removed root)
|
|
1396
|
+
// ownedFiles under (authed)/waitlist/** dropped (removal seed)
|
|
1397
|
+
// ownedFiles projects/apply/ListingApplicationClient lands; no route mounts it
|
|
1398
|
+
// ownedFiles under apps/mcp/src/tools/** lands when apps/mcp is kept
|
|
1399
|
+
// installMap content/en/** (12 seeds) lands, but for the five report seeds
|
|
1400
|
+
// installMap database/flows/{referral,fit-check}.flow.json
|
|
1401
|
+
// remapped to flows/<name>.json — declared
|
|
1402
|
+
// data `create-ailk seed-flows` sends
|
|
1403
|
+
// installMap database/waitlist/**.experiment.json dropped (removed root):
|
|
1404
|
+
// a register is hub-side state
|
|
1405
|
+
// installMap apps/web/public/reports/**, reports/** lands
|
|
1406
|
+
// patches — @working-theory/ui-signal-collectors lands
|
|
1407
|
+
//
|
|
1408
|
+
// It stays a Pro module: a keyless spoke carries no flows.
|
|
1409
|
+
spoke: true,
|
|
1342
1410
|
paidSectionsPackage: "@working-theory/ui-signal-collectors",
|
|
1343
1411
|
ownedFiles: [
|
|
1344
1412
|
"apps/api/src/routes/flows/index.ts",
|
|
@@ -1388,6 +1456,10 @@ export const MODULE_SLICES = {
|
|
|
1388
1456
|
"apps/api/src/bin/deliverable-upload.ts",
|
|
1389
1457
|
"apps/api/src/bin/session-retention-sweep.ts",
|
|
1390
1458
|
"apps/api/src/bin/seed-presets.ts",
|
|
1459
|
+
// #7844 D7 — the operator flow-copy CLI and its service: flows code by
|
|
1460
|
+
// subject, and a Free scaffold carries no flows.
|
|
1461
|
+
"apps/api/src/bin/flows-copy.ts",
|
|
1462
|
+
"apps/api/src/services/flow-copy.ts",
|
|
1391
1463
|
"apps/api/src/bin/listing-csv-sweep.ts",
|
|
1392
1464
|
"apps/api/src/bin/followup-sweep.ts",
|
|
1393
1465
|
"apps/api/src/bin/seed-waitlist-experiments.ts",
|
|
@@ -1649,6 +1721,16 @@ export const MODULE_SLICES = {
|
|
|
1649
1721
|
// `lib/stripe.ts` and the webhook receiver this module owns — declared
|
|
1650
1722
|
// here, not engineered around.
|
|
1651
1723
|
commerce: {
|
|
1724
|
+
// #7838 — admitted on a spoke. Its bundle installs through the spoke
|
|
1725
|
+
// filter (`isSpokeExcluded`), which drops every destination under
|
|
1726
|
+
// apps/api or database and the web files that call the hub's own
|
|
1727
|
+
// checkout (`SPOKE_HUB_CHECKOUT_SEEDS`: flows/[slug]/checkout.actions.ts,
|
|
1728
|
+
// the flow results pages, dev/purchase — spoke-intake Rule 3). What lands
|
|
1729
|
+
// is the scaffold-commerce skill, the commerce-boundary rule and the
|
|
1730
|
+
// @working-theory/ui-commerce dependency (`patches` below); the spoke's own
|
|
1731
|
+
// checkout — two routes, checkout-config.ts and two return pages — is
|
|
1732
|
+
// rendered by create-ailk (`spoke-checkout.ts`), not carried here.
|
|
1733
|
+
spoke: true,
|
|
1652
1734
|
paidSectionsPackage: "@working-theory/ui-commerce",
|
|
1653
1735
|
apps: {
|
|
1654
1736
|
api: {
|
package/dist/parse-args.d.ts
CHANGED
|
@@ -26,6 +26,15 @@ export declare const RESERVED_SHAPES: readonly ["local-business-product"];
|
|
|
26
26
|
/** The `--design` vocabulary (#6754 D1) — `STARTER_DESIGNS`, pinned by test. */
|
|
27
27
|
export declare const RECOGNIZED_DESIGNS: readonly ["standard", "minimalism"];
|
|
28
28
|
export type DesignName = (typeof RECOGNIZED_DESIGNS)[number];
|
|
29
|
+
/**
|
|
30
|
+
* The `--topology` vocabulary (#7426 D1) — orthogonal to `--shape`: any shape
|
|
31
|
+
* can be scaffolded as either. Declared here rather than imported from
|
|
32
|
+
* `topology.ts` for the reason `RECOGNIZED_MODULES` is: that module reads
|
|
33
|
+
* `MODULE_SLICES`, and importing it here closes a runtime cycle through
|
|
34
|
+
* `lib/module-tier.ts`. `spoke-topology.test.ts` pins the two lists equal.
|
|
35
|
+
*/
|
|
36
|
+
export declare const RECOGNIZED_TOPOLOGIES: readonly ["hub", "spoke"];
|
|
37
|
+
export type TopologyName = (typeof RECOGNIZED_TOPOLOGIES)[number];
|
|
29
38
|
export interface CliOptions {
|
|
30
39
|
/** Positional target directory (the dir to scaffold into). Optional only
|
|
31
40
|
* when `help` or `version` is set. */
|
|
@@ -94,6 +103,24 @@ export interface CliOptions {
|
|
|
94
103
|
* flag is.
|
|
95
104
|
*/
|
|
96
105
|
license?: string;
|
|
106
|
+
/**
|
|
107
|
+
* The scaffold's topology (`--topology`, #7426 D1). `hub` — the default,
|
|
108
|
+
* and the only value before #7426 — unless `--topology spoke`.
|
|
109
|
+
*/
|
|
110
|
+
topology: TopologyName;
|
|
111
|
+
/**
|
|
112
|
+
* The raw `--hub-url` value (#7426 D2), present only on a spoke run that
|
|
113
|
+
* passed it. `scaffold()` validates and normalizes it, falling back to
|
|
114
|
+
* `AILK_API_URL`.
|
|
115
|
+
*/
|
|
116
|
+
hubUrl?: string;
|
|
117
|
+
/** Spoke only: `false` with `--without-mcp` (#7426 D3), else `true`. */
|
|
118
|
+
mcp?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Spoke only: the raw `--hub-sign-in` value (#7894 D1) — the hub's web
|
|
121
|
+
* origin this spoke signs visitors in at. `scaffold()` validates it.
|
|
122
|
+
*/
|
|
123
|
+
hubSignIn?: string;
|
|
97
124
|
}
|
|
98
125
|
export declare class ParseError extends Error {
|
|
99
126
|
constructor(message: string);
|