@intelligo-dev/cli 1.0.0-beta.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 (65) hide show
  1. package/LICENSE +201 -0
  2. package/dist/bin.d.ts +14 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +149 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/commands/add.d.ts +48 -0
  7. package/dist/commands/add.d.ts.map +1 -0
  8. package/dist/commands/add.js +83 -0
  9. package/dist/commands/add.js.map +1 -0
  10. package/dist/commands/create.d.ts +33 -0
  11. package/dist/commands/create.d.ts.map +1 -0
  12. package/dist/commands/create.js +61 -0
  13. package/dist/commands/create.js.map +1 -0
  14. package/dist/commands/doctor.d.ts +23 -0
  15. package/dist/commands/doctor.d.ts.map +1 -0
  16. package/dist/commands/doctor.js +129 -0
  17. package/dist/commands/doctor.js.map +1 -0
  18. package/dist/commands/migrate-check.d.ts +44 -0
  19. package/dist/commands/migrate-check.d.ts.map +1 -0
  20. package/dist/commands/migrate-check.js +92 -0
  21. package/dist/commands/migrate-check.js.map +1 -0
  22. package/dist/commands/upgrade-check.d.ts +40 -0
  23. package/dist/commands/upgrade-check.d.ts.map +1 -0
  24. package/dist/commands/upgrade-check.js +91 -0
  25. package/dist/commands/upgrade-check.js.map +1 -0
  26. package/dist/index.d.ts +10 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +10 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/manifest.d.ts +58 -0
  31. package/dist/manifest.d.ts.map +1 -0
  32. package/dist/manifest.js +65 -0
  33. package/dist/manifest.js.map +1 -0
  34. package/dist/migrations-dir.d.ts +13 -0
  35. package/dist/migrations-dir.d.ts.map +1 -0
  36. package/dist/migrations-dir.js +25 -0
  37. package/dist/migrations-dir.js.map +1 -0
  38. package/dist/migrations.d.ts +41 -0
  39. package/dist/migrations.d.ts.map +1 -0
  40. package/dist/migrations.js +99 -0
  41. package/dist/migrations.js.map +1 -0
  42. package/package.json +44 -0
  43. package/templates/admin-page/admin-page.tsx.tpl +56 -0
  44. package/templates/app-scaffold/assistant-route.ts.tpl +66 -0
  45. package/templates/app-scaffold/components.json.tpl +23 -0
  46. package/templates/app-scaffold/env.example.tpl +13 -0
  47. package/templates/app-scaffold/globals.css.tpl +329 -0
  48. package/templates/app-scaffold/i18n-navigation.ts.tpl +10 -0
  49. package/templates/app-scaffold/i18n-request.ts.tpl +51 -0
  50. package/templates/app-scaffold/i18n-routing.ts.tpl +13 -0
  51. package/templates/app-scaffold/intelligo.ts.tpl +84 -0
  52. package/templates/app-scaffold/layout.tsx.tpl +53 -0
  53. package/templates/app-scaffold/lib-utils.ts.tpl +6 -0
  54. package/templates/app-scaffold/messages-en-app.json.tpl +4 -0
  55. package/templates/app-scaffold/middleware.ts.tpl +11 -0
  56. package/templates/app-scaffold/next.config.mjs.tpl +19 -0
  57. package/templates/app-scaffold/package.json.tpl +42 -0
  58. package/templates/app-scaffold/plans.ts.tpl +43 -0
  59. package/templates/app-scaffold/postcss.config.mjs.tpl +7 -0
  60. package/templates/app-scaffold/sonner.tsx.tpl +50 -0
  61. package/templates/app-scaffold/theme-provider.tsx.tpl +28 -0
  62. package/templates/app-scaffold/tsconfig.json.tpl +12 -0
  63. package/templates/billing-page/billing-page.tsx.tpl +72 -0
  64. package/templates/manifest.json +116 -0
  65. package/templates/usage-page/usage-page.tsx.tpl +60 -0
@@ -0,0 +1,329 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ /* custom variant for setting dark mode programmatically */
5
+ @custom-variant dark (&:is(.dark, .dark *));
6
+
7
+ /* ============================================================
8
+ Design tokens.
9
+ This file is yours — Intelligo generated it once and will not
10
+ overwrite it. These tokens are a starting point, not a dependency:
11
+ nothing in your app imports them from an Intelligo package, so
12
+ edit them freely. Registry items you install with `shadcn add`
13
+ assume the shadcn new-york theme below; the ones under "Design
14
+ tokens" are optional extras used by some Intelligo-authored
15
+ registry items (status colors, agent identity colors) — delete
16
+ what you don't use.
17
+ ============================================================ */
18
+
19
+ /* ---- Light mode tokens ---- */
20
+ :root {
21
+ /* Status: success */
22
+ --status-success: hsl(142 76% 36%);
23
+ --status-success-light: hsl(143 85% 96%);
24
+ --status-success-dark: hsl(144 61% 20%);
25
+
26
+ /* Status: error */
27
+ --status-error: hsl(0 84% 60%);
28
+ --status-error-light: hsl(0 86% 97%);
29
+ --status-error-dark: hsl(0 63% 31%);
30
+
31
+ /* Status: warning */
32
+ --status-warning: hsl(38 92% 50%);
33
+ --status-warning-light: hsl(48 96% 89%);
34
+ --status-warning-dark: hsl(32 95% 44%);
35
+
36
+ /* Status: info */
37
+ --status-info: hsl(217 91% 60%);
38
+ --status-info-light: hsl(214 95% 93%);
39
+ --status-info-dark: hsl(224 76% 48%);
40
+
41
+ /* Brand — Monochrome */
42
+ --brand-primary: #18181B;
43
+ --brand-primary-foreground: #ffffff;
44
+ --brand-accent: #27272A;
45
+ --brand-accent-end: #3F3F46;
46
+
47
+ /* Spacing (semantic aliases) */
48
+ --space-xs: 0.25rem;
49
+ --space-sm: 0.5rem;
50
+ --space-md: 1rem;
51
+ --space-lg: 1.5rem;
52
+ --space-xl: 2rem;
53
+ --space-2xl: 3rem;
54
+
55
+ /* Shadows */
56
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
57
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
58
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
59
+ --shadow-glow-primary: 0 0 40px rgba(255, 255, 255, 0.1);
60
+
61
+ /* Z-index scale */
62
+ --z-dropdown: 50;
63
+ --z-sticky: 100;
64
+ --z-modal: 200;
65
+ --z-popover: 250;
66
+ --z-toast: 300;
67
+
68
+ /* Typography */
69
+ --font-size-xs: 0.75rem; /* 12px */
70
+ --font-size-sm: 0.875rem; /* 14px */
71
+ --font-size-base: 1rem; /* 16px */
72
+ --font-size-lg: 1.125rem; /* 18px */
73
+ --font-size-xl: 1.25rem; /* 20px */
74
+ --font-size-2xl: 1.5rem; /* 24px */
75
+ --font-size-3xl: 1.875rem; /* 30px */
76
+
77
+ --line-height-tight: 1.25;
78
+ --line-height-snug: 1.375;
79
+ --line-height-normal: 1.5;
80
+ --line-height-relaxed: 1.625;
81
+
82
+ --letter-spacing-tight: -0.025em;
83
+ --letter-spacing-normal: 0em;
84
+ --letter-spacing-wide: 0.025em;
85
+
86
+ /* Agent identity colors */
87
+ --agent-career: #7c3aed;
88
+ --agent-university: #2563eb;
89
+ --agent-scholarship: #d97706;
90
+ --agent-student-life: #059669;
91
+ --agent-ai-futures: #e11d48;
92
+ --agent-parent: #92400e;
93
+
94
+ /* Weather (decorative/thematic) */
95
+ --weather-day-start: hsl(200 95% 65%); /* sky-400 equivalent */
96
+ --weather-day-mid: hsl(217 91% 60%); /* blue-500 equivalent */
97
+ --weather-day-end: hsl(221 83% 53%); /* blue-600 equivalent */
98
+ --weather-night-start: hsl(244 47% 20%); /* indigo-900 equivalent */
99
+ --weather-night-mid: hsl(269 67% 18%); /* purple-900 equivalent */
100
+ --weather-night-end: hsl(222 47% 11%); /* slate-900 equivalent */
101
+ }
102
+
103
+ /* ---- Dark mode overrides ---- */
104
+ .dark {
105
+ /* Status: success — lighter text on dark backgrounds */
106
+ --status-success: hsl(142 69% 58%);
107
+ --status-success-light: hsl(143 50% 15%);
108
+ --status-success-dark: hsl(144 76% 80%);
109
+
110
+ /* Status: error */
111
+ --status-error: hsl(0 91% 71%);
112
+ --status-error-light: hsl(0 50% 15%);
113
+ --status-error-dark: hsl(0 84% 85%);
114
+
115
+ /* Status: warning */
116
+ --status-warning: hsl(38 95% 64%);
117
+ --status-warning-light: hsl(38 60% 15%);
118
+ --status-warning-dark: hsl(38 92% 80%);
119
+
120
+ /* Status: info */
121
+ --status-info: hsl(217 91% 70%);
122
+ --status-info-light: hsl(217 60% 15%);
123
+ --status-info-dark: hsl(217 91% 85%);
124
+
125
+ /* Brand (dark) — Monochrome */
126
+ --brand-primary: #E8E8E8;
127
+ --brand-primary-foreground: #121212;
128
+ --brand-accent: #888888;
129
+ --brand-accent-end: #555555;
130
+
131
+ /* Weather — same in dark mode (self-contained card) */
132
+ --weather-day-start: hsl(200 95% 65%);
133
+ --weather-day-mid: hsl(217 91% 60%);
134
+ --weather-day-end: hsl(221 83% 53%);
135
+ --weather-night-start: hsl(244 47% 20%);
136
+ --weather-night-mid: hsl(269 67% 18%);
137
+ --weather-night-end: hsl(222 47% 11%);
138
+ }
139
+
140
+ /* ============================================================
141
+ shadcn new-york theme — style: new-york, baseColor: neutral,
142
+ cssVariables: true (components.json). Standard shadcn palette;
143
+ registry items you install assume this default theme, so change
144
+ it after things are working, not before.
145
+ ============================================================ */
146
+
147
+ :root {
148
+ --radius: 0.625rem;
149
+ --background: oklch(1 0 0);
150
+ --foreground: oklch(0.145 0 0);
151
+ --card: oklch(1 0 0);
152
+ --card-foreground: oklch(0.145 0 0);
153
+ --popover: oklch(1 0 0);
154
+ --popover-foreground: oklch(0.145 0 0);
155
+ --primary: oklch(0.205 0 0);
156
+ --primary-foreground: oklch(0.985 0 0);
157
+ --secondary: oklch(0.97 0 0);
158
+ --secondary-foreground: oklch(0.205 0 0);
159
+ --muted: oklch(0.97 0 0);
160
+ --muted-foreground: oklch(0.556 0 0);
161
+ --accent: oklch(0.97 0 0);
162
+ --accent-foreground: oklch(0.205 0 0);
163
+ --destructive: oklch(0.577 0.245 27.325);
164
+ --destructive-foreground: oklch(0.985 0 0);
165
+ --border: oklch(0.922 0 0);
166
+ --input: oklch(0.922 0 0);
167
+ --ring: oklch(0.708 0 0);
168
+ --chart-1: oklch(0.646 0.222 41.116);
169
+ --chart-2: oklch(0.6 0.118 184.704);
170
+ --chart-3: oklch(0.398 0.07 227.392);
171
+ --chart-4: oklch(0.828 0.189 84.429);
172
+ --chart-5: oklch(0.769 0.188 70.08);
173
+ --sidebar: oklch(0.985 0 0);
174
+ --sidebar-foreground: oklch(0.145 0 0);
175
+ --sidebar-primary: oklch(0.205 0 0);
176
+ --sidebar-primary-foreground: oklch(0.985 0 0);
177
+ --sidebar-accent: oklch(0.97 0 0);
178
+ --sidebar-accent-foreground: oklch(0.205 0 0);
179
+ --sidebar-border: oklch(0.922 0 0);
180
+ --sidebar-ring: oklch(0.708 0 0);
181
+ }
182
+
183
+ .dark {
184
+ --background: oklch(0.145 0 0);
185
+ --foreground: oklch(0.985 0 0);
186
+ --card: oklch(0.205 0 0);
187
+ --card-foreground: oklch(0.985 0 0);
188
+ --popover: oklch(0.205 0 0);
189
+ --popover-foreground: oklch(0.985 0 0);
190
+ --primary: oklch(0.922 0 0);
191
+ --primary-foreground: oklch(0.205 0 0);
192
+ --secondary: oklch(0.269 0 0);
193
+ --secondary-foreground: oklch(0.985 0 0);
194
+ --muted: oklch(0.269 0 0);
195
+ --muted-foreground: oklch(0.708 0 0);
196
+ --accent: oklch(0.269 0 0);
197
+ --accent-foreground: oklch(0.985 0 0);
198
+ --destructive: oklch(0.704 0.191 22.216);
199
+ --destructive-foreground: oklch(0.985 0 0);
200
+ --border: oklch(1 0 0 / 10%);
201
+ --input: oklch(1 0 0 / 15%);
202
+ --ring: oklch(0.556 0 0);
203
+ --chart-1: oklch(0.488 0.243 264.376);
204
+ --chart-2: oklch(0.696 0.17 162.48);
205
+ --chart-3: oklch(0.769 0.188 70.08);
206
+ --chart-4: oklch(0.627 0.265 303.9);
207
+ --chart-5: oklch(0.645 0.246 16.439);
208
+ --sidebar: oklch(0.205 0 0);
209
+ --sidebar-foreground: oklch(0.985 0 0);
210
+ --sidebar-primary: oklch(0.488 0.243 264.376);
211
+ --sidebar-primary-foreground: oklch(0.985 0 0);
212
+ --sidebar-accent: oklch(0.269 0 0);
213
+ --sidebar-accent-foreground: oklch(0.985 0 0);
214
+ --sidebar-border: oklch(1 0 0 / 10%);
215
+ --sidebar-ring: oklch(0.556 0 0);
216
+ }
217
+
218
+ /* define theme — maps CSS variables to Tailwind utilities */
219
+ @theme inline {
220
+ --font-sans: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
221
+
222
+ --radius-sm: calc(var(--radius) - 4px);
223
+ --radius-md: calc(var(--radius) - 2px);
224
+ --radius-lg: var(--radius);
225
+ --radius-xl: calc(var(--radius) + 4px);
226
+
227
+ --color-background: var(--background);
228
+ --color-foreground: var(--foreground);
229
+
230
+ --color-card: var(--card);
231
+ --color-card-foreground: var(--card-foreground);
232
+
233
+ --color-popover: var(--popover);
234
+ --color-popover-foreground: var(--popover-foreground);
235
+
236
+ --color-primary: var(--primary);
237
+ --color-primary-foreground: var(--primary-foreground);
238
+
239
+ --color-secondary: var(--secondary);
240
+ --color-secondary-foreground: var(--secondary-foreground);
241
+
242
+ --color-muted: var(--muted);
243
+ --color-muted-foreground: var(--muted-foreground);
244
+
245
+ --color-accent: var(--accent);
246
+ --color-accent-foreground: var(--accent-foreground);
247
+
248
+ --color-destructive: var(--destructive);
249
+ --color-destructive-foreground: var(--destructive-foreground);
250
+
251
+ --color-border: var(--border);
252
+ --color-input: var(--input);
253
+ --color-ring: var(--ring);
254
+
255
+ --color-chart-1: var(--chart-1);
256
+ --color-chart-2: var(--chart-2);
257
+ --color-chart-3: var(--chart-3);
258
+ --color-chart-4: var(--chart-4);
259
+ --color-chart-5: var(--chart-5);
260
+
261
+ --color-sidebar: var(--sidebar);
262
+ --color-sidebar-foreground: var(--sidebar-foreground);
263
+ --color-sidebar-primary: var(--sidebar-primary);
264
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
265
+ --color-sidebar-accent: var(--sidebar-accent);
266
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
267
+ --color-sidebar-border: var(--sidebar-border);
268
+ --color-sidebar-ring: var(--sidebar-ring);
269
+
270
+ /* Design token mappings — from packages/ui/src/tokens.css */
271
+ --color-status-success: var(--status-success);
272
+ --color-status-success-light: var(--status-success-light);
273
+ --color-status-error: var(--status-error);
274
+ --color-status-error-light: var(--status-error-light);
275
+ --color-status-warning: var(--status-warning);
276
+ --color-status-warning-light: var(--status-warning-light);
277
+ --color-status-info: var(--status-info);
278
+ --color-status-info-light: var(--status-info-light);
279
+ --color-brand-primary: var(--brand-primary);
280
+ --color-brand-accent: var(--brand-accent);
281
+
282
+ /* Agent color mappings */
283
+ --color-agent-career: var(--agent-career);
284
+ --color-agent-university: var(--agent-university);
285
+ --color-agent-scholarship: var(--agent-scholarship);
286
+ --color-agent-student-life: var(--agent-student-life);
287
+ --color-agent-ai-futures: var(--agent-ai-futures);
288
+ --color-agent-parent: var(--agent-parent);
289
+
290
+ /* Typography token mappings */
291
+ --font-size-xs: var(--font-size-xs);
292
+ --font-size-sm: var(--font-size-sm);
293
+ --font-size-base: var(--font-size-base);
294
+ --font-size-lg: var(--font-size-lg);
295
+ --font-size-xl: var(--font-size-xl);
296
+ --font-size-2xl: var(--font-size-2xl);
297
+ --font-size-3xl: var(--font-size-3xl);
298
+ --line-height-tight: var(--line-height-tight);
299
+ --line-height-snug: var(--line-height-snug);
300
+ --line-height-normal: var(--line-height-normal);
301
+ --line-height-relaxed: var(--line-height-relaxed);
302
+ --letter-spacing-tight: var(--letter-spacing-tight);
303
+ --letter-spacing-normal: var(--letter-spacing-normal);
304
+ --letter-spacing-wide: var(--letter-spacing-wide);
305
+ }
306
+
307
+ /*
308
+ The default border color has changed to `currentcolor` in Tailwind CSS v4,
309
+ so we've added these compatibility styles to make sure everything still
310
+ looks the same as it did with Tailwind CSS v3.
311
+
312
+ If we ever want to remove these styles, we need to add an explicit border
313
+ color utility to any element that depends on these defaults.
314
+ */
315
+ @layer base {
316
+ *,
317
+ ::after,
318
+ ::before,
319
+ ::backdrop,
320
+ ::file-selector-button {
321
+ border-color: var(--color-gray-200, currentcolor);
322
+ }
323
+ * {
324
+ @apply border-border outline-ring/50;
325
+ }
326
+ body {
327
+ @apply bg-background text-foreground;
328
+ }
329
+ }
@@ -0,0 +1,10 @@
1
+ import { createNavigation } from "next-intl/navigation";
2
+
3
+ import { routing } from "./routing";
4
+
5
+ // Registry items import navigation primitives from here
6
+ // (`@/i18n/navigation`), never from `next/link` or `next/navigation`
7
+ // directly — that's what keeps locale-prefixed URLs correct without
8
+ // every item having to know the routing config.
9
+ export const { Link, redirect, usePathname, useRouter } =
10
+ createNavigation(routing);
@@ -0,0 +1,51 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import { getRequestConfig } from "next-intl/server";
5
+
6
+ import { routing } from "./routing";
7
+
8
+ const MESSAGES_ROOT = path.join(process.cwd(), "messages");
9
+
10
+ /**
11
+ * Registry items ship one message file per item —
12
+ * `messages/<locale>/<item>.json` — instead of one giant per-locale
13
+ * file. The file name (minus `.json`) becomes the namespace a
14
+ * component reads with `useTranslations("<item>")`, so installing an
15
+ * item is just dropping its message file in; nothing here needs to
16
+ * change and no component is edited.
17
+ */
18
+ function loadMessages(locale: string): Record<string, unknown> {
19
+ const localeDir = path.join(MESSAGES_ROOT, locale);
20
+
21
+ let files: string[];
22
+ try {
23
+ files = readdirSync(localeDir).filter((file) => file.endsWith(".json"));
24
+ } catch {
25
+ return {};
26
+ }
27
+
28
+ return files.reduce<Record<string, unknown>>((messages, file) => {
29
+ const namespace = path.basename(file, ".json");
30
+ messages[namespace] = JSON.parse(
31
+ readFileSync(path.join(localeDir, file), "utf8")
32
+ );
33
+ return messages;
34
+ }, {});
35
+ }
36
+
37
+ export default getRequestConfig(async ({ requestLocale }) => {
38
+ let locale = await requestLocale;
39
+
40
+ if (
41
+ !locale ||
42
+ !routing.locales.includes(locale as (typeof routing.locales)[number])
43
+ ) {
44
+ locale = routing.defaultLocale;
45
+ }
46
+
47
+ return {
48
+ locale,
49
+ messages: loadMessages(locale),
50
+ };
51
+ });
@@ -0,0 +1,13 @@
1
+ import { defineRouting } from "next-intl/routing";
2
+
3
+ export const routing = defineRouting({
4
+ locales: ["en"],
5
+ defaultLocale: "en",
6
+ // Locale prefix only appears in the URL when it isn't the default
7
+ // locale (e.g. /dashboard, not /en/dashboard) — a single-locale
8
+ // deployment needs nothing beyond this config and the shipped
9
+ // English messages to work.
10
+ localePrefix: "as-needed",
11
+ });
12
+
13
+ export const { locales, defaultLocale } = routing;
@@ -0,0 +1,84 @@
1
+ import "server-only";
2
+
3
+ /**
4
+ * Your composition root (ADR-0005).
5
+ *
6
+ * This file is yours — Intelligo generated it once and will not
7
+ * overwrite it. It is the single place the application wires itself
8
+ * together: which plans exist, which features they grant, and how the
9
+ * execution boundary reaches your billing implementation.
10
+ *
11
+ * Import-side-effect registration is deliberately not used: a
12
+ * registration that silently does not happen looks exactly like one
13
+ * that did.
14
+ */
15
+
16
+ import {
17
+ checkQuota,
18
+ recordTokenUsage,
19
+ releaseReservation,
20
+ } from "@intelligo-dev/billing";
21
+ import {
22
+ registerProductFeatures,
23
+ registerProductPlans,
24
+ setDefaultProductSlug,
25
+ } from "@intelligo-dev/billing/plans";
26
+ import { createExecutions } from "@intelligo-dev/executions";
27
+
28
+ import { PLANS, FEATURES } from "./plans";
29
+
30
+ /** Identifies your product to the billing engine. */
31
+ export const PRODUCT_SLUG = "__APP_SLUG__";
32
+
33
+ /**
34
+ * What your product can be asked to do. Capability strings are your
35
+ * vocabulary — Intelligo only groups and meters them.
36
+ */
37
+ export const CAPABILITIES = {
38
+ assistantMessage: "assistant.message",
39
+ } as const;
40
+
41
+ let composed = false;
42
+
43
+ export function composeIntelligo(): void {
44
+ if (composed) return;
45
+ composed = true;
46
+
47
+ setDefaultProductSlug(PRODUCT_SLUG);
48
+ registerProductPlans(PRODUCT_SLUG, PLANS);
49
+ registerProductFeatures(PRODUCT_SLUG, FEATURES);
50
+ }
51
+
52
+ export const executions = createExecutions({
53
+ async checkEntitlement({ workspaceId, requestId, model }) {
54
+ // Passing requestId makes admission atomic: the worst-case cost is
55
+ // reserved in the same transaction that reads the balance, so
56
+ // concurrent requests cannot all pass.
57
+ const quota = await checkQuota(workspaceId, { modelId: model, requestId });
58
+ return {
59
+ allowed: quota.allowed,
60
+ reason: quota.reason,
61
+ estimatedMnt: quota.estimatedMnt,
62
+ usingTrialCredits: quota.usingTrialCredits,
63
+ };
64
+ },
65
+
66
+ async settleUsage(s) {
67
+ await recordTokenUsage({
68
+ workspaceId: s.workspaceId,
69
+ userId: s.userId ?? "",
70
+ model: s.model ?? "unknown",
71
+ agent: s.capability,
72
+ inputTokens: s.inputTokens,
73
+ outputTokens: s.outputTokens,
74
+ totalTokens: s.totalTokens,
75
+ usingTrialCredits: s.usingTrialCredits,
76
+ requestId: s.requestId,
77
+ metadata: s.metadata,
78
+ });
79
+ },
80
+
81
+ async releaseHold({ requestId }) {
82
+ await releaseReservation(requestId);
83
+ },
84
+ });
@@ -0,0 +1,53 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ import { notFound } from "next/navigation";
4
+ import { NextIntlClientProvider } from "next-intl";
5
+ import { getMessages, getTranslations } from "next-intl/server";
6
+
7
+ import "../globals.css";
8
+
9
+ import { ThemeProvider } from "@/components/shell/theme-provider";
10
+ import { Toaster } from "@/components/ui/sonner";
11
+ import { routing } from "@/i18n/routing";
12
+
13
+ type Props = {
14
+ children: ReactNode;
15
+ params: Promise<{ locale: string }>;
16
+ };
17
+
18
+ export function generateStaticParams() {
19
+ return routing.locales.map((locale) => ({ locale }));
20
+ }
21
+
22
+ export async function generateMetadata() {
23
+ const t = await getTranslations("app");
24
+ return {
25
+ title: t("title"),
26
+ description: t("description"),
27
+ };
28
+ }
29
+
30
+ export default async function RootLayout({ children, params }: Props) {
31
+ const { locale } = await params;
32
+
33
+ if (!routing.locales.includes(locale as (typeof routing.locales)[number])) {
34
+ notFound();
35
+ }
36
+
37
+ const messages = await getMessages();
38
+
39
+ return (
40
+ // suppressHydrationWarning: next-themes sets the class attribute on
41
+ // <html> before hydration (see components/shell/theme-provider.tsx).
42
+ <html lang={locale} suppressHydrationWarning>
43
+ <body>
44
+ <ThemeProvider>
45
+ <NextIntlClientProvider messages={messages}>
46
+ {children}
47
+ <Toaster />
48
+ </NextIntlClientProvider>
49
+ </ThemeProvider>
50
+ </body>
51
+ </html>
52
+ );
53
+ }
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "__APP_NAME__",
3
+ "description": "Built on Intelligo"
4
+ }
@@ -0,0 +1,11 @@
1
+ import createIntlMiddleware from "next-intl/middleware";
2
+
3
+ import { routing } from "./i18n/routing";
4
+
5
+ export default createIntlMiddleware(routing);
6
+
7
+ export const config = {
8
+ // Run on everything except API routes, Next internals and static
9
+ // assets.
10
+ matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
11
+ };
@@ -0,0 +1,19 @@
1
+ import createNextIntlPlugin from "next-intl/plugin";
2
+
3
+ const withNextIntl = createNextIntlPlugin("./i18n/request.ts");
4
+
5
+ /** @type {import('next').NextConfig} */
6
+ const nextConfig = {
7
+ // Intelligo packages ship TypeScript source, so Next compiles them
8
+ // with your app.
9
+ transpilePackages: [
10
+ "@intelligo-dev/admin",
11
+ "@intelligo-dev/audit",
12
+ "@intelligo-dev/auth",
13
+ "@intelligo-dev/billing",
14
+ "@intelligo-dev/core",
15
+ "@intelligo-dev/executions",
16
+ ],
17
+ };
18
+
19
+ export default withNextIntl(nextConfig);
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "__APP_NAME__",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "next dev --turbopack --port 3000",
8
+ "build": "next build",
9
+ "start": "next start --port 3000",
10
+ "type-check": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@intelligo-dev/admin": "__INTELLIGO_DEP__",
14
+ "@intelligo-dev/audit": "__INTELLIGO_DEP__",
15
+ "@intelligo-dev/auth": "__INTELLIGO_DEP__",
16
+ "@intelligo-dev/billing": "__INTELLIGO_DEP__",
17
+ "@intelligo-dev/core": "__INTELLIGO_DEP__",
18
+ "@intelligo-dev/executions": "__INTELLIGO_DEP__",
19
+ "@intelligo-dev/ui": "__INTELLIGO_DEP__",
20
+ "@radix-ui/react-slot": "^1.2.4",
21
+ "class-variance-authority": "^0.7.1",
22
+ "clsx": "^2.1.1",
23
+ "lucide-react": "^0.563.0",
24
+ "next": "^16.2.3",
25
+ "next-intl": "^4.9.2",
26
+ "next-themes": "^0.4.6",
27
+ "react": "^19.0.0",
28
+ "react-dom": "^19.0.0",
29
+ "server-only": "^0.0.1",
30
+ "sonner": "^2.0.7",
31
+ "tailwind-merge": "^3.4.0"
32
+ },
33
+ "devDependencies": {
34
+ "@tailwindcss/postcss": "^4",
35
+ "@types/node": "^22.0.0",
36
+ "@types/react": "^19.0.0",
37
+ "@types/react-dom": "^19.0.0",
38
+ "tailwindcss": "^4",
39
+ "tw-animate-css": "^1.4.0",
40
+ "typescript": "^5.7.2"
41
+ }
42
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Your plans and feature gates.
3
+ *
4
+ * This file is yours. Intelligo's billing engine knows only the shape;
5
+ * the names, prices, and limits are entirely your product's, and it
6
+ * reads them through the registry your composition root populates
7
+ * (ADR-0006). Two limit keys are special because the credit engine
8
+ * reads them directly: `monthlyCreditMnt` and `rolloverEnabled`.
9
+ */
10
+
11
+ import type { PlanConfig } from "@intelligo-dev/billing/plans";
12
+
13
+ export const PLANS: Record<string, PlanConfig> = {
14
+ free: {
15
+ name: "Free",
16
+ slug: "free",
17
+ description: "Get started",
18
+ descriptionMn: "",
19
+ priceOneTime: 0,
20
+ targetAudience: "Everyone",
21
+ aiModelLabel: "Base",
22
+ limits: { monthlyCreditMnt: 2_000, rolloverEnabled: false },
23
+ features: [],
24
+ featuresMn: [],
25
+ },
26
+ pro: {
27
+ name: "Pro",
28
+ slug: "pro",
29
+ description: "For daily use",
30
+ descriptionMn: "",
31
+ priceOneTime: 20_000,
32
+ targetAudience: "Teams",
33
+ aiModelLabel: "Advanced",
34
+ limits: { monthlyCreditMnt: 60_000, rolloverEnabled: true },
35
+ features: [],
36
+ featuresMn: [],
37
+ },
38
+ };
39
+
40
+ /** Which plans grant which feature. An unregistered feature is denied. */
41
+ export const FEATURES: Record<string, readonly string[]> = {
42
+ assistant: ["free", "pro"],
43
+ };
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;