@lumerahq/cli 0.19.2 → 0.19.3

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 (41) hide show
  1. package/README.md +5 -3
  2. package/dist/chunk-H357NP7T.js +77 -0
  3. package/dist/{chunk-GKI2HQJC.js → chunk-JKXLKK5I.js} +14 -1
  4. package/dist/chunk-P5HFNAVN.js +280 -0
  5. package/dist/{deps-ULTIIDYK.js → deps-EC3VRNN7.js} +4 -2
  6. package/dist/{dev-EVREBGR6.js → dev-R43VQCZD.js} +8 -1
  7. package/dist/index.js +13 -13
  8. package/dist/{init-37XOMJLU.js → init-TDIQAOG4.js} +29 -9
  9. package/dist/{register-JJUMS4FI.js → register-JFJADKAJ.js} +1 -1
  10. package/dist/{resources-IPAIPW63.js → resources-OP7EECKZ.js} +10 -4
  11. package/dist/{run-5ZOSPBGO.js → run-EJP5WCQU.js} +1 -1
  12. package/dist/{templates-M3RDNDDY.js → templates-LNUOTNLN.js} +2 -3
  13. package/package.json +1 -1
  14. package/templates/default/.agents/skills/.gitkeep +0 -0
  15. package/templates/default/AGENTS.md +151 -0
  16. package/templates/default/README.md +18 -0
  17. package/templates/default/_gitignore +14 -0
  18. package/templates/default/architecture.md +29 -0
  19. package/templates/default/biome.json +38 -0
  20. package/templates/default/components.json +21 -0
  21. package/templates/default/icon.svg +29 -0
  22. package/templates/default/index.html +16 -0
  23. package/templates/default/package.json +53 -0
  24. package/templates/default/platform/automations/.gitkeep +0 -0
  25. package/templates/default/platform/collections/.gitkeep +0 -0
  26. package/templates/default/platform/hooks/.gitkeep +0 -0
  27. package/templates/default/pyproject.toml +14 -0
  28. package/templates/default/scripts/.gitkeep +0 -0
  29. package/templates/default/src/components/layout.tsx +11 -0
  30. package/templates/default/src/lib/auth.ts +9 -0
  31. package/templates/default/src/lib/queries.ts +17 -0
  32. package/templates/default/src/lib/utils.ts +6 -0
  33. package/templates/default/src/main.tsx +130 -0
  34. package/templates/default/src/routes/__root.tsx +10 -0
  35. package/templates/default/src/routes/index.tsx +87 -0
  36. package/templates/default/src/styles.css +128 -0
  37. package/templates/default/template.json +7 -0
  38. package/templates/default/tsconfig.json +22 -0
  39. package/templates/default/vite.config.ts +28 -0
  40. package/dist/chunk-OQW5E7UT.js +0 -159
  41. package/dist/chunk-XDTWVFPE.js +0 -120
@@ -0,0 +1,87 @@
1
+ import { createFileRoute } from '@tanstack/react-router';
2
+ import { useContext } from 'react';
3
+ import { MessageSquare, LayoutGrid, Bot, Workflow } from 'lucide-react';
4
+ import { AuthContext } from '../lib/auth';
5
+
6
+ export const Route = createFileRoute('/')({
7
+ component: HomePage,
8
+ });
9
+
10
+ function HomePage() {
11
+ const auth = useContext(AuthContext);
12
+
13
+ return (
14
+ <div className="space-y-10">
15
+ {/* Header */}
16
+ <div>
17
+ <h1 className="text-2xl font-semibold tracking-tight">Welcome{auth?.user?.name ? `, ${auth.user.name}` : ''}</h1>
18
+ <p className="text-muted-foreground mt-2 text-[0.95rem] leading-relaxed">Your app is ready. Use Studio to start building.</p>
19
+ </div>
20
+
21
+ {/* Getting Started */}
22
+ <div className="rounded-xl bg-card p-6 shadow-[0_1px_3px_0_oklch(0_0_0/0.04),0_1px_2px_-1px_oklch(0_0_0/0.04)] border border-border/60 hover:shadow-[0_4px_12px_-2px_oklch(0_0_0/0.06)] transition-shadow duration-300">
23
+ <div className="flex items-start gap-4">
24
+ <div className="rounded-xl bg-gradient-to-br from-primary/10 to-primary/5 p-3 ring-1 ring-primary/10">
25
+ <MessageSquare className="size-6 text-primary" />
26
+ </div>
27
+ <div className="space-y-2">
28
+ <h2 className="font-semibold text-lg tracking-tight">Build with Studio</h2>
29
+ <p className="text-muted-foreground text-sm leading-relaxed">
30
+ Switch to the <strong>Chat</strong> tab and tell the agent what you want to build.
31
+ It will set up your data, write the logic, and build your UI — all from a conversation.
32
+ </p>
33
+ </div>
34
+ </div>
35
+ </div>
36
+
37
+ {/* Example prompts */}
38
+ <div>
39
+ <h2 className="text-sm font-semibold uppercase tracking-wider text-muted-foreground mb-4">Try asking the agent</h2>
40
+ <div className="flex flex-wrap gap-2">
41
+ {[
42
+ 'Build an invoice processing app with approval workflows',
43
+ 'Track vendor payments and flag overdue invoices',
44
+ 'Create a month-end close checklist with task assignments',
45
+ 'Set up an AP inbox that triages emails with AI',
46
+ 'Build an expense tracker with categories and a dashboard',
47
+ ].map((prompt) => (
48
+ <span
49
+ key={prompt}
50
+ className="inline-flex items-center px-3.5 py-2 rounded-lg text-sm bg-card border border-border/60 text-muted-foreground shadow-[0_1px_2px_0_oklch(0_0_0/0.03)] hover:border-primary/30 hover:text-foreground transition-all duration-200 cursor-default"
51
+ >
52
+ {prompt}
53
+ </span>
54
+ ))}
55
+ </div>
56
+ </div>
57
+
58
+ {/* What you can build */}
59
+ <div>
60
+ <h2 className="text-sm font-semibold uppercase tracking-wider text-muted-foreground mb-5">What you can build</h2>
61
+ <div className="grid gap-4 md:grid-cols-3">
62
+ <div className="group rounded-xl bg-card p-5 space-y-3 shadow-[0_1px_2px_0_oklch(0_0_0/0.03)] border border-border/60 hover:shadow-[0_4px_12px_-2px_oklch(0_0_0/0.06)] hover:border-border transition-all duration-300">
63
+ <LayoutGrid className="size-5 text-primary transition-colors duration-300" />
64
+ <h3 className="font-medium text-sm tracking-tight">Internal apps</h3>
65
+ <p className="text-xs text-muted-foreground leading-relaxed">
66
+ Back-office tools for your team — dashboards, approval queues, and operational workflows.
67
+ </p>
68
+ </div>
69
+ <div className="group rounded-xl bg-card p-5 space-y-3 shadow-[0_1px_2px_0_oklch(0_0_0/0.03)] border border-border/60 hover:shadow-[0_4px_12px_-2px_oklch(0_0_0/0.06)] hover:border-border transition-all duration-300">
70
+ <Bot className="size-5 text-primary transition-colors duration-300" />
71
+ <h3 className="font-medium text-sm tracking-tight">Agent-powered workflows</h3>
72
+ <p className="text-xs text-muted-foreground leading-relaxed">
73
+ AI agents that extract data, run reviews, draft outputs, and route exceptions — humans stay in the loop.
74
+ </p>
75
+ </div>
76
+ <div className="group rounded-xl bg-card p-5 space-y-3 shadow-[0_1px_2px_0_oklch(0_0_0/0.03)] border border-border/60 hover:shadow-[0_4px_12px_-2px_oklch(0_0_0/0.06)] hover:border-border transition-all duration-300">
77
+ <Workflow className="size-5 text-primary transition-colors duration-300" />
78
+ <h3 className="font-medium text-sm tracking-tight">Automations</h3>
79
+ <p className="text-xs text-muted-foreground leading-relaxed">
80
+ Connect to your systems, process inbound emails, and trigger actions automatically — with a full audit trail.
81
+ </p>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ );
87
+ }
@@ -0,0 +1,128 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+ @import "shadcn/tailwind.css";
4
+
5
+ @custom-variant dark (&:is(.dark *));
6
+
7
+ body {
8
+ @apply m-0;
9
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
10
+ font-feature-settings: "cv11", "ss01";
11
+ -webkit-font-smoothing: antialiased;
12
+ -moz-osx-font-smoothing: grayscale;
13
+ }
14
+
15
+ /* Lumera brand theme + full shadcn variable set */
16
+
17
+ :root {
18
+ --radius: 0.75rem;
19
+
20
+ /* Warm off-white background */
21
+ --background: oklch(0.985 0.002 60);
22
+ /* Near-black foreground with warm undertone */
23
+ --foreground: oklch(0.16 0.01 60);
24
+ /* Cards: true white */
25
+ --card: oklch(1 0 0);
26
+ --card-foreground: oklch(0.16 0.01 60);
27
+ --popover: oklch(1 0 0);
28
+ --popover-foreground: oklch(0.16 0.01 60);
29
+ /* Lumera coral-orange primary */
30
+ --primary: oklch(0.62 0.17 35);
31
+ --primary-foreground: oklch(0.995 0 0);
32
+ /* Secondary */
33
+ --secondary: oklch(0.955 0.005 60);
34
+ --secondary-foreground: oklch(0.16 0.01 60);
35
+ /* Warm stone muted tones */
36
+ --muted: oklch(0.955 0.005 60);
37
+ --muted-foreground: oklch(0.50 0.01 60);
38
+ /* Accent — lighter orange tint */
39
+ --accent: oklch(0.95 0.03 50);
40
+ --accent-foreground: oklch(0.35 0.12 35);
41
+ /* Destructive */
42
+ --destructive: oklch(0.55 0.2 25);
43
+ --destructive-foreground: oklch(0.995 0 0);
44
+ /* Warm border */
45
+ --border: oklch(0.91 0.005 60);
46
+ --input: oklch(0.91 0.005 60);
47
+ --ring: oklch(0.62 0.17 35);
48
+ /* Charts */
49
+ --chart-1: oklch(0.646 0.222 41.116);
50
+ --chart-2: oklch(0.6 0.118 184.704);
51
+ --chart-3: oklch(0.398 0.07 227.392);
52
+ --chart-4: oklch(0.828 0.189 84.429);
53
+ --chart-5: oklch(0.769 0.188 70.08);
54
+ /* Sidebar */
55
+ --sidebar: oklch(0.985 0.002 60);
56
+ --sidebar-foreground: oklch(0.16 0.01 60);
57
+ --sidebar-primary: oklch(0.62 0.17 35);
58
+ --sidebar-primary-foreground: oklch(0.995 0 0);
59
+ --sidebar-accent: oklch(0.95 0.03 50);
60
+ --sidebar-accent-foreground: oklch(0.35 0.12 35);
61
+ --sidebar-border: oklch(0.91 0.005 60);
62
+ --sidebar-ring: oklch(0.62 0.17 35);
63
+
64
+ /* Semantic status colors */
65
+ --success: oklch(0.55 0.14 145);
66
+ --success-foreground: oklch(0.995 0 0);
67
+ --warning: oklch(0.75 0.15 70);
68
+ --warning-foreground: oklch(0.25 0.05 60);
69
+
70
+ /* Accent gradient for logo mark */
71
+ --accent-gradient-from: oklch(0.62 0.17 35);
72
+ --accent-gradient-to: oklch(0.58 0.19 25);
73
+ }
74
+
75
+ @theme inline {
76
+ --color-background: var(--background);
77
+ --color-foreground: var(--foreground);
78
+ --color-card: var(--card);
79
+ --color-card-foreground: var(--card-foreground);
80
+ --color-popover: var(--popover);
81
+ --color-popover-foreground: var(--popover-foreground);
82
+ --color-primary: var(--primary);
83
+ --color-primary-foreground: var(--primary-foreground);
84
+ --color-secondary: var(--secondary);
85
+ --color-secondary-foreground: var(--secondary-foreground);
86
+ --color-muted: var(--muted);
87
+ --color-muted-foreground: var(--muted-foreground);
88
+ --color-accent: var(--accent);
89
+ --color-accent-foreground: var(--accent-foreground);
90
+ --color-destructive: var(--destructive);
91
+ --color-destructive-foreground: var(--destructive-foreground);
92
+ --color-border: var(--border);
93
+ --color-input: var(--input);
94
+ --color-ring: var(--ring);
95
+ --color-chart-1: var(--chart-1);
96
+ --color-chart-2: var(--chart-2);
97
+ --color-chart-3: var(--chart-3);
98
+ --color-chart-4: var(--chart-4);
99
+ --color-chart-5: var(--chart-5);
100
+ --color-sidebar: var(--sidebar);
101
+ --color-sidebar-foreground: var(--sidebar-foreground);
102
+ --color-sidebar-primary: var(--sidebar-primary);
103
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
104
+ --color-sidebar-accent: var(--sidebar-accent);
105
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
106
+ --color-sidebar-border: var(--sidebar-border);
107
+ --color-sidebar-ring: var(--sidebar-ring);
108
+ --color-success: var(--success);
109
+ --color-success-foreground: var(--success-foreground);
110
+ --color-warning: var(--warning);
111
+ --color-warning-foreground: var(--warning-foreground);
112
+ --radius-sm: calc(var(--radius) * 0.6);
113
+ --radius-md: calc(var(--radius) * 0.8);
114
+ --radius-lg: var(--radius);
115
+ --radius-xl: calc(var(--radius) * 1.4);
116
+ --radius-2xl: calc(var(--radius) * 1.8);
117
+ --radius-3xl: calc(var(--radius) * 2.2);
118
+ --radius-4xl: calc(var(--radius) * 2.6);
119
+ }
120
+
121
+ @layer base {
122
+ * {
123
+ @apply border-border outline-ring/50;
124
+ }
125
+ body {
126
+ @apply bg-background text-foreground;
127
+ }
128
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "default",
3
+ "title": "Blank Starter",
4
+ "description": "A minimal blank canvas with React frontend and Lumera platform scaffolding. No pre-built collections or agents — start from scratch.",
5
+ "category": "General",
6
+ "version": "1.1.0"
7
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "include": ["**/*.ts", "**/*.tsx"],
3
+ "compilerOptions": {
4
+ "target": "ES2022",
5
+ "jsx": "react-jsx",
6
+ "module": "ESNext",
7
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
8
+ "types": ["vite/client"],
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "verbatimModuleSyntax": true,
12
+ "noEmit": true,
13
+ "skipLibCheck": true,
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "paths": {
19
+ "@/*": ["./src/*"]
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,28 @@
1
+ import tailwindcss from '@tailwindcss/vite';
2
+ import { tanstackRouter } from '@tanstack/router-plugin/vite';
3
+ import viteReact from '@vitejs/plugin-react';
4
+ import { defineConfig } from 'vite';
5
+
6
+ export default defineConfig({
7
+ base: './', // Use relative paths for S3 hosting at subpaths
8
+ plugins: [
9
+ tanstackRouter({
10
+ target: 'react',
11
+ autoCodeSplitting: true,
12
+ }),
13
+ viteReact(),
14
+ tailwindcss(),
15
+ ],
16
+ resolve: {
17
+ alias: {
18
+ '@': new URL('./src', import.meta.url).pathname,
19
+ },
20
+ dedupe: ['react', 'react-dom'],
21
+ },
22
+ server: {
23
+ allowedHosts: [
24
+ 'mac.lumerahq.com',
25
+ 'untunable-del-nonephemerally.ngrok-free.dev',
26
+ ],
27
+ },
28
+ });
@@ -1,159 +0,0 @@
1
- import {
2
- fetchWithRetry
3
- } from "./chunk-FJFIWC7G.js";
4
-
5
- // src/lib/templates.ts
6
- import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "fs";
7
- import { homedir, tmpdir } from "os";
8
- import { join } from "path";
9
- import { spawnSync } from "child_process";
10
- var GITHUB_OWNER = "lumerahq";
11
- var GITHUB_REPO = "app-templates";
12
- var GITHUB_REF = "main";
13
- function getCacheDir() {
14
- return join(homedir(), ".lumera", "templates");
15
- }
16
- function readCacheMeta() {
17
- const metaPath = join(getCacheDir(), ".cache-meta.json");
18
- if (!existsSync(metaPath)) return null;
19
- try {
20
- return JSON.parse(readFileSync(metaPath, "utf-8"));
21
- } catch {
22
- return null;
23
- }
24
- }
25
- async function fetchLatestSha() {
26
- const url = `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/commits/${GITHUB_REF}`;
27
- const res = await fetchWithRetry(url, {
28
- headers: { Accept: "application/vnd.github.sha" }
29
- });
30
- if (!res.ok) throw new Error(`GitHub API error: ${res.status}`);
31
- return (await res.text()).trim();
32
- }
33
- async function downloadAndExtract(commitSha) {
34
- const cacheDir = getCacheDir();
35
- const tarballUrl = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/archive/refs/heads/${GITHUB_REF}.tar.gz`;
36
- const res = await fetchWithRetry(tarballUrl);
37
- if (!res.ok) throw new Error(`Failed to download templates: ${res.status}`);
38
- const tmpFile = join(tmpdir(), `lumera-templates-${Date.now()}.tar.gz`);
39
- const buffer = Buffer.from(await res.arrayBuffer());
40
- writeFileSync(tmpFile, buffer);
41
- mkdirSync(cacheDir, { recursive: true });
42
- for (const entry of readdirSync(cacheDir)) {
43
- if (entry === ".cache-meta.json" || entry === ".refs") continue;
44
- rmSync(join(cacheDir, entry), { recursive: true, force: true });
45
- }
46
- const result = spawnSync("tar", ["xzf", tmpFile, "-C", cacheDir, "--strip-components=1"], { stdio: "ignore" });
47
- if (result.status !== 0) throw new Error("Failed to extract template archive");
48
- unlinkSync(tmpFile);
49
- const meta = { commitSha, fetchedAt: (/* @__PURE__ */ new Date()).toISOString() };
50
- writeFileSync(join(cacheDir, ".cache-meta.json"), JSON.stringify(meta));
51
- return cacheDir;
52
- }
53
- async function ensureRemoteCache() {
54
- const cacheDir = getCacheDir();
55
- const cached = readCacheMeta();
56
- const latestSha = await fetchLatestSha();
57
- if (cached?.commitSha === latestSha && existsSync(cacheDir)) {
58
- return cacheDir;
59
- }
60
- console.log(" Fetching latest templates...");
61
- return downloadAndExtract(latestSha);
62
- }
63
- function parseTemplateRef(input) {
64
- const atIndex = input.lastIndexOf("@");
65
- if (atIndex > 0) {
66
- return {
67
- name: input.slice(0, atIndex),
68
- ref: input.slice(atIndex + 1)
69
- };
70
- }
71
- return { name: input, ref: null };
72
- }
73
- async function ensureRemoteCacheForRef(ref) {
74
- const refCacheDir = join(getCacheDir(), ".refs", ref);
75
- if (existsSync(refCacheDir) && existsSync(join(refCacheDir, ".cache-meta.json"))) {
76
- return refCacheDir;
77
- }
78
- console.log(` Fetching templates at ref "${ref}"...`);
79
- const urls = [
80
- `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/archive/refs/tags/${ref}.tar.gz`,
81
- `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/archive/refs/heads/${ref}.tar.gz`,
82
- `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/archive/${ref}.tar.gz`
83
- ];
84
- let res = null;
85
- for (const url of urls) {
86
- const attempt = await fetchWithRetry(url);
87
- if (attempt.ok) {
88
- res = attempt;
89
- break;
90
- }
91
- }
92
- if (!res) {
93
- throw new Error(
94
- `Failed to fetch templates at ref "${ref}". Ensure the tag, branch, or SHA exists in ${GITHUB_OWNER}/${GITHUB_REPO}.`
95
- );
96
- }
97
- const tmpFile = join(tmpdir(), `lumera-templates-${ref}-${Date.now()}.tar.gz`);
98
- const buffer = Buffer.from(await res.arrayBuffer());
99
- writeFileSync(tmpFile, buffer);
100
- mkdirSync(refCacheDir, { recursive: true });
101
- const result = spawnSync("tar", ["xzf", tmpFile, "-C", refCacheDir, "--strip-components=1"], { stdio: "ignore" });
102
- if (result.status !== 0) throw new Error("Failed to extract template archive");
103
- unlinkSync(tmpFile);
104
- const meta = { commitSha: ref, fetchedAt: (/* @__PURE__ */ new Date()).toISOString() };
105
- writeFileSync(join(refCacheDir, ".cache-meta.json"), JSON.stringify(meta));
106
- return refCacheDir;
107
- }
108
- function listTemplates(baseDir) {
109
- if (!existsSync(baseDir)) return [];
110
- const templates = [];
111
- for (const entry of readdirSync(baseDir, { withFileTypes: true })) {
112
- if (!entry.isDirectory()) continue;
113
- const metaPath = join(baseDir, entry.name, "template.json");
114
- if (!existsSync(metaPath)) continue;
115
- try {
116
- const raw = JSON.parse(readFileSync(metaPath, "utf-8"));
117
- if (!raw.name || !raw.title || !raw.description || !raw.category) {
118
- console.warn(`Warning: Skipping template "${entry.name}" \u2014 template.json is missing required fields (name, title, description, category)`);
119
- continue;
120
- }
121
- templates.push(raw);
122
- } catch {
123
- console.warn(`Warning: Skipping template "${entry.name}" \u2014 invalid template.json`);
124
- }
125
- }
126
- return templates.sort((a, b) => {
127
- if (a.name === "default") return -1;
128
- if (b.name === "default") return 1;
129
- return a.title.localeCompare(b.title);
130
- });
131
- }
132
- async function resolveTemplate(nameWithRef) {
133
- const { name, ref } = parseTemplateRef(nameWithRef);
134
- const cacheDir = ref ? await ensureRemoteCacheForRef(ref) : await ensureRemoteCache();
135
- const dir = join(cacheDir, name);
136
- if (!existsSync(dir) || !existsSync(join(dir, "template.json"))) {
137
- const available = listTemplates(cacheDir).map((t) => t.name).join(", ");
138
- if (ref) {
139
- throw new Error(
140
- `Template "${name}" not found at ref "${ref}". Available: ${available || "none"}`
141
- );
142
- }
143
- throw new Error(
144
- `Template "${name}" not found. Available: ${available || "none"}
145
- Cache location: ${cacheDir}
146
- Try deleting the cache and retrying: rm -rf ${cacheDir}`
147
- );
148
- }
149
- return dir;
150
- }
151
- async function listAllTemplates() {
152
- const cacheDir = await ensureRemoteCache();
153
- return listTemplates(cacheDir);
154
- }
155
-
156
- export {
157
- resolveTemplate,
158
- listAllTemplates
159
- };
@@ -1,120 +0,0 @@
1
- import {
2
- loadEnv
3
- } from "./chunk-2CR762KB.js";
4
- import {
5
- createApiClient
6
- } from "./chunk-GKI2HQJC.js";
7
- import {
8
- findProjectRoot,
9
- getAppName
10
- } from "./chunk-ZH3NVYEQ.js";
11
-
12
- // src/commands/deps.ts
13
- import pc from "picocolors";
14
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
15
- import { join } from "path";
16
- var DEPS_FILE = "platform/project_deps.json";
17
- function loadDeps(projectRoot) {
18
- const depsPath = join(projectRoot, DEPS_FILE);
19
- if (!existsSync(depsPath)) return null;
20
- try {
21
- return JSON.parse(readFileSync(depsPath, "utf-8"));
22
- } catch {
23
- return null;
24
- }
25
- }
26
- async function syncDeps(projectRoot) {
27
- const root = projectRoot ?? findProjectRoot();
28
- loadEnv(root);
29
- const deps2 = loadDeps(root);
30
- if (!deps2 || !deps2.dependencies || Object.keys(deps2.dependencies).length === 0) {
31
- return true;
32
- }
33
- const appName = getAppName(root);
34
- const api = createApiClient(void 0, void 0, appName);
35
- let allOk = true;
36
- for (const [depProject, config] of Object.entries(deps2.dependencies)) {
37
- try {
38
- const manifest = await api.getProjectManifest(depProject);
39
- const remoteNames = new Set(manifest.collections.map((c) => c.name));
40
- for (const col of config.collections) {
41
- if (remoteNames.has(col)) {
42
- console.log(pc.green(" \u2713"), `${depProject}/${col}`);
43
- } else {
44
- console.log(pc.red(" \u2717"), `${depProject}/${col} \u2014 not found in project manifest`);
45
- allOk = false;
46
- }
47
- }
48
- } catch (e) {
49
- console.log(pc.red(" \u2717"), `${depProject}: failed to fetch manifest \u2014 ${e}`);
50
- allOk = false;
51
- }
52
- }
53
- return allOk;
54
- }
55
- async function deps(args) {
56
- const sub = args[0];
57
- const projectRoot = findProjectRoot();
58
- loadEnv(projectRoot);
59
- if (sub === "sync") {
60
- console.log();
61
- console.log(pc.cyan(pc.bold(" Deps Sync")));
62
- console.log(pc.dim(" Verifying cross-project dependencies..."));
63
- console.log();
64
- const ok = await syncDeps(projectRoot);
65
- if (!ok) {
66
- console.log();
67
- console.log(pc.red(" Some dependencies could not be resolved."));
68
- process.exit(1);
69
- }
70
- console.log();
71
- console.log(pc.green(" All dependencies verified."));
72
- return;
73
- }
74
- if (sub === "list") {
75
- const depsData = loadDeps(projectRoot);
76
- if (!depsData || Object.keys(depsData.dependencies).length === 0) {
77
- console.log(pc.dim(" No dependencies declared in platform/project_deps.json"));
78
- return;
79
- }
80
- console.log();
81
- console.log(pc.cyan(pc.bold(" Project Dependencies")));
82
- console.log();
83
- for (const [depProject, config] of Object.entries(depsData.dependencies)) {
84
- console.log(` ${pc.bold(depProject)}`);
85
- for (const col of config.collections) {
86
- console.log(` \u2022 ${col}`);
87
- }
88
- }
89
- console.log();
90
- return;
91
- }
92
- if (sub === "init") {
93
- const depsPath = join(projectRoot, DEPS_FILE);
94
- if (existsSync(depsPath)) {
95
- console.log(pc.yellow(" platform/project_deps.json already exists."));
96
- return;
97
- }
98
- const platformDir = join(projectRoot, "platform");
99
- mkdirSync(platformDir, { recursive: true });
100
- const initial = { dependencies: {} };
101
- writeFileSync(depsPath, JSON.stringify(initial, null, 2) + "\n");
102
- console.log(pc.green(" \u2713"), "Created platform/project_deps.json");
103
- return;
104
- }
105
- console.log(`
106
- ${pc.bold("lumera deps")} \u2014 manage cross-project dependencies
107
-
108
- ${pc.bold("Commands:")}
109
- sync Fetch manifests and verify declared dependencies exist
110
- list Show declared dependencies
111
- init Create an empty platform/project_deps.json
112
-
113
- ${pc.bold("Dependency file:")} platform/project_deps.json
114
- `);
115
- }
116
-
117
- export {
118
- syncDeps,
119
- deps
120
- };