@lumerahq/cli 0.10.0 → 0.11.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/dist/chunk-CHRKCAIZ.js +155 -0
- package/dist/{chunk-WRAZC6SJ.js → chunk-HIYM7EM2.js} +17 -1
- package/dist/dev-2HVDP3NX.js +155 -0
- package/dist/index.js +166 -14
- package/dist/init-VNJNSU4Q.js +440 -0
- package/dist/{resources-PGBVCS2K.js → resources-GTG3QMVV.js} +2 -4
- package/dist/{run-WIRQDYYX.js → run-47GF5VVS.js} +2 -4
- package/dist/templates-6KMZWOYH.js +194 -0
- package/package.json +1 -1
- package/dist/chunk-2CR762KB.js +0 -18
- package/dist/dev-BHBF4ECH.js +0 -87
- package/dist/init-EDSRR3YM.js +0 -360
- package/templates/default/ARCHITECTURE.md +0 -80
- package/templates/default/CLAUDE.md +0 -238
- package/templates/default/README.md +0 -59
- package/templates/default/biome.json +0 -38
- package/templates/default/gitignore +0 -9
- package/templates/default/index.html +0 -13
- package/templates/default/package.json.hbs +0 -47
- package/templates/default/platform/automations/.gitkeep +0 -0
- package/templates/default/platform/collections/example_items.json +0 -26
- package/templates/default/platform/hooks/.gitkeep +0 -0
- package/templates/default/pyproject.toml.hbs +0 -14
- package/templates/default/scripts/seed-demo.py +0 -35
- package/templates/default/src/components/Sidebar.tsx +0 -82
- package/templates/default/src/components/StatCard.tsx +0 -25
- package/templates/default/src/components/layout.tsx +0 -13
- package/templates/default/src/lib/queries.ts +0 -27
- package/templates/default/src/main.tsx +0 -131
- package/templates/default/src/routes/__root.tsx +0 -10
- package/templates/default/src/routes/index.tsx +0 -88
- package/templates/default/src/routes/settings.tsx +0 -21
- package/templates/default/src/styles.css +0 -44
- package/templates/default/tsconfig.json +0 -23
- package/templates/default/vite.config.ts +0 -28
package/dist/dev-BHBF4ECH.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
dev
|
|
3
|
-
} from "./chunk-CDZZ3JYU.js";
|
|
4
|
-
import {
|
|
5
|
-
loadEnv
|
|
6
|
-
} from "./chunk-2CR762KB.js";
|
|
7
|
-
import {
|
|
8
|
-
getToken
|
|
9
|
-
} from "./chunk-NDLYGKS6.js";
|
|
10
|
-
import {
|
|
11
|
-
findProjectRoot,
|
|
12
|
-
getApiUrl,
|
|
13
|
-
getAppName,
|
|
14
|
-
getAppTitle
|
|
15
|
-
} from "./chunk-D2BLSEGR.js";
|
|
16
|
-
|
|
17
|
-
// src/commands/dev.ts
|
|
18
|
-
import pc from "picocolors";
|
|
19
|
-
function parseFlags(args) {
|
|
20
|
-
const result = {};
|
|
21
|
-
for (let i = 0; i < args.length; i++) {
|
|
22
|
-
const arg = args[i];
|
|
23
|
-
if (arg.startsWith("--")) {
|
|
24
|
-
const key = arg.slice(2);
|
|
25
|
-
const next = args[i + 1];
|
|
26
|
-
if (next && !next.startsWith("--")) {
|
|
27
|
-
result[key] = next;
|
|
28
|
-
i++;
|
|
29
|
-
} else {
|
|
30
|
-
result[key] = true;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return result;
|
|
35
|
-
}
|
|
36
|
-
function showHelp() {
|
|
37
|
-
console.log(`
|
|
38
|
-
${pc.dim("Usage:")}
|
|
39
|
-
lumera dev [options]
|
|
40
|
-
|
|
41
|
-
${pc.dim("Description:")}
|
|
42
|
-
Start the development server with Lumera registration.
|
|
43
|
-
Registers your app with Lumera and starts a local dev server.
|
|
44
|
-
|
|
45
|
-
${pc.dim("Options:")}
|
|
46
|
-
--port <number> Dev server port (default: 8080)
|
|
47
|
-
--url <url> App URL for dev mode (default: http://localhost:{port})
|
|
48
|
-
--help, -h Show this help
|
|
49
|
-
|
|
50
|
-
${pc.dim("Environment variables:")}
|
|
51
|
-
LUMERA_TOKEN API token (overrides \`lumera login\`)
|
|
52
|
-
LUMERA_API_URL API base URL (default: https://app.lumerahq.com/api)
|
|
53
|
-
PORT Dev server port (default: 8080)
|
|
54
|
-
APP_URL App URL for dev mode
|
|
55
|
-
|
|
56
|
-
${pc.dim("Examples:")}
|
|
57
|
-
lumera dev # Start dev server on port 8080
|
|
58
|
-
lumera dev --port 3000 # Start dev server on port 3000
|
|
59
|
-
lumera dev --url http://192.168.1.100:8080 # Custom URL for mobile testing
|
|
60
|
-
`);
|
|
61
|
-
}
|
|
62
|
-
async function dev2(args) {
|
|
63
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
64
|
-
showHelp();
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
const flags = parseFlags(args);
|
|
68
|
-
const projectRoot = findProjectRoot();
|
|
69
|
-
loadEnv(projectRoot);
|
|
70
|
-
const token = getToken(projectRoot);
|
|
71
|
-
const appName = getAppName(projectRoot);
|
|
72
|
-
const appTitle = getAppTitle(projectRoot);
|
|
73
|
-
const apiUrl = getApiUrl();
|
|
74
|
-
const port = Number(flags.port || process.env.PORT || "8080");
|
|
75
|
-
const appUrl = typeof flags.url === "string" ? flags.url : process.env.APP_URL;
|
|
76
|
-
await dev({
|
|
77
|
-
token,
|
|
78
|
-
appName,
|
|
79
|
-
appTitle,
|
|
80
|
-
port,
|
|
81
|
-
appUrl,
|
|
82
|
-
apiUrl
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
export {
|
|
86
|
-
dev2 as dev
|
|
87
|
-
};
|
package/dist/init-EDSRR3YM.js
DELETED
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
installAllSkills,
|
|
3
|
-
syncClaudeMd
|
|
4
|
-
} from "./chunk-UP3GV4HN.js";
|
|
5
|
-
import "./chunk-D2BLSEGR.js";
|
|
6
|
-
|
|
7
|
-
// src/commands/init.ts
|
|
8
|
-
import pc from "picocolors";
|
|
9
|
-
import prompts from "prompts";
|
|
10
|
-
import { execSync } from "child_process";
|
|
11
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, rmSync } from "fs";
|
|
12
|
-
import { join, dirname, resolve } from "path";
|
|
13
|
-
import { fileURLToPath } from "url";
|
|
14
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
15
|
-
var __dirname = dirname(__filename);
|
|
16
|
-
function getTemplatesDir() {
|
|
17
|
-
const candidates = [
|
|
18
|
-
// From dist/index.js or dist/commands/init.js
|
|
19
|
-
resolve(__dirname, "../templates/default"),
|
|
20
|
-
resolve(__dirname, "../../templates/default"),
|
|
21
|
-
// From src during development
|
|
22
|
-
resolve(__dirname, "../../../templates/default")
|
|
23
|
-
];
|
|
24
|
-
for (const candidate of candidates) {
|
|
25
|
-
if (existsSync(candidate)) {
|
|
26
|
-
return candidate;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
throw new Error(`Templates directory not found. Searched: ${candidates.join(", ")}`);
|
|
30
|
-
}
|
|
31
|
-
function toTitleCase(str) {
|
|
32
|
-
return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
33
|
-
}
|
|
34
|
-
function processTemplate(content, vars) {
|
|
35
|
-
let result = content;
|
|
36
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
37
|
-
result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value);
|
|
38
|
-
}
|
|
39
|
-
return result;
|
|
40
|
-
}
|
|
41
|
-
function copyDir(src, dest, vars) {
|
|
42
|
-
if (!existsSync(dest)) {
|
|
43
|
-
mkdirSync(dest, { recursive: true });
|
|
44
|
-
}
|
|
45
|
-
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
46
|
-
const srcPath = join(src, entry.name);
|
|
47
|
-
let destName = entry.name;
|
|
48
|
-
if (destName.endsWith(".hbs")) {
|
|
49
|
-
destName = destName.slice(0, -4);
|
|
50
|
-
}
|
|
51
|
-
if (destName === "gitignore") {
|
|
52
|
-
destName = ".gitignore";
|
|
53
|
-
}
|
|
54
|
-
const destPath = join(dest, destName);
|
|
55
|
-
if (entry.isDirectory()) {
|
|
56
|
-
copyDir(srcPath, destPath, vars);
|
|
57
|
-
} else {
|
|
58
|
-
const content = readFileSync(srcPath, "utf-8");
|
|
59
|
-
const processed = processTemplate(content, vars);
|
|
60
|
-
writeFileSync(destPath, processed);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
function isGitInstalled() {
|
|
65
|
-
try {
|
|
66
|
-
execSync("git --version", { stdio: "ignore" });
|
|
67
|
-
return true;
|
|
68
|
-
} catch {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function initGitRepo(targetDir, projectName) {
|
|
73
|
-
try {
|
|
74
|
-
execSync("git init", { cwd: targetDir, stdio: "ignore" });
|
|
75
|
-
execSync("git add -A", { cwd: targetDir, stdio: "ignore" });
|
|
76
|
-
execSync(`git commit -m "Initial commit: scaffold ${projectName}"`, {
|
|
77
|
-
cwd: targetDir,
|
|
78
|
-
stdio: "ignore"
|
|
79
|
-
});
|
|
80
|
-
return true;
|
|
81
|
-
} catch {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function isUvInstalled() {
|
|
86
|
-
try {
|
|
87
|
-
execSync("uv --version", { stdio: "ignore" });
|
|
88
|
-
return true;
|
|
89
|
-
} catch {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
function installUv() {
|
|
94
|
-
try {
|
|
95
|
-
try {
|
|
96
|
-
execSync("curl -LsSf https://astral.sh/uv/install.sh | sh", {
|
|
97
|
-
stdio: "inherit",
|
|
98
|
-
shell: "/bin/bash"
|
|
99
|
-
});
|
|
100
|
-
return true;
|
|
101
|
-
} catch {
|
|
102
|
-
execSync("wget -qO- https://astral.sh/uv/install.sh | sh", {
|
|
103
|
-
stdio: "inherit",
|
|
104
|
-
shell: "/bin/bash"
|
|
105
|
-
});
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
} catch {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function createPythonVenv(targetDir) {
|
|
113
|
-
try {
|
|
114
|
-
execSync("uv venv", { cwd: targetDir, stdio: "ignore" });
|
|
115
|
-
execSync("uv pip install lumera", { cwd: targetDir, stdio: "ignore" });
|
|
116
|
-
return true;
|
|
117
|
-
} catch {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
function parseArgs(args) {
|
|
122
|
-
const result = {
|
|
123
|
-
projectName: void 0,
|
|
124
|
-
directory: void 0,
|
|
125
|
-
install: false,
|
|
126
|
-
yes: false,
|
|
127
|
-
force: false,
|
|
128
|
-
help: false
|
|
129
|
-
};
|
|
130
|
-
for (let i = 0; i < args.length; i++) {
|
|
131
|
-
const arg = args[i];
|
|
132
|
-
if (arg === "--help" || arg === "-h") {
|
|
133
|
-
result.help = true;
|
|
134
|
-
} else if (arg === "--install" || arg === "-i") {
|
|
135
|
-
result.install = true;
|
|
136
|
-
} else if (arg === "--yes" || arg === "-y") {
|
|
137
|
-
result.yes = true;
|
|
138
|
-
} else if (arg === "--force" || arg === "-f") {
|
|
139
|
-
result.force = true;
|
|
140
|
-
} else if (arg === "--dir" || arg === "-d") {
|
|
141
|
-
result.directory = args[++i];
|
|
142
|
-
} else if (!arg.startsWith("-") && !result.projectName) {
|
|
143
|
-
result.projectName = arg;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return result;
|
|
147
|
-
}
|
|
148
|
-
function showHelp() {
|
|
149
|
-
console.log(`
|
|
150
|
-
${pc.dim("Usage:")}
|
|
151
|
-
lumera init [name] [options]
|
|
152
|
-
|
|
153
|
-
${pc.dim("Description:")}
|
|
154
|
-
Scaffold a new Lumera project.
|
|
155
|
-
|
|
156
|
-
${pc.dim("Options:")}
|
|
157
|
-
--yes, -y Non-interactive mode (requires project name)
|
|
158
|
-
--dir, -d <path> Target directory (defaults to project name)
|
|
159
|
-
--force, -f Overwrite existing directory without prompting
|
|
160
|
-
--install, -i Install dependencies after scaffolding
|
|
161
|
-
--help, -h Show this help
|
|
162
|
-
|
|
163
|
-
${pc.dim("Interactive mode:")}
|
|
164
|
-
lumera init # Prompt for project name and directory
|
|
165
|
-
lumera init my-project # Prompt for directory only
|
|
166
|
-
|
|
167
|
-
${pc.dim("Non-interactive mode:")}
|
|
168
|
-
lumera init my-project -y # Create ./my-project
|
|
169
|
-
lumera init my-project -y -d ./apps # Create ./apps
|
|
170
|
-
lumera init my-project -y -f # Overwrite if exists
|
|
171
|
-
lumera init my-project -y -i # Create and install deps
|
|
172
|
-
|
|
173
|
-
${pc.dim("CI/CD example:")}
|
|
174
|
-
lumera init my-app -y -f -i # Full non-interactive setup
|
|
175
|
-
`);
|
|
176
|
-
}
|
|
177
|
-
async function init(args) {
|
|
178
|
-
const opts = parseArgs(args);
|
|
179
|
-
if (opts.help) {
|
|
180
|
-
showHelp();
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
console.log();
|
|
184
|
-
console.log(pc.cyan(pc.bold(" Create Lumera App")));
|
|
185
|
-
console.log();
|
|
186
|
-
let projectName = opts.projectName;
|
|
187
|
-
let directory = opts.directory;
|
|
188
|
-
const nonInteractive = opts.yes;
|
|
189
|
-
if (nonInteractive && !projectName) {
|
|
190
|
-
console.log(pc.red(" Error: Project name is required in non-interactive mode"));
|
|
191
|
-
console.log(pc.dim(" Usage: lumera init <name> -y"));
|
|
192
|
-
process.exit(1);
|
|
193
|
-
}
|
|
194
|
-
if (!projectName) {
|
|
195
|
-
const response = await prompts({
|
|
196
|
-
type: "text",
|
|
197
|
-
name: "projectName",
|
|
198
|
-
message: "What is your project name?",
|
|
199
|
-
initial: "my-lumera-app",
|
|
200
|
-
validate: (value) => {
|
|
201
|
-
if (!value) return "Project name is required";
|
|
202
|
-
if (!/^[a-z0-9-]+$/.test(value)) {
|
|
203
|
-
return "Use lowercase letters, numbers, and hyphens only";
|
|
204
|
-
}
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
if (!response.projectName) {
|
|
209
|
-
console.log(pc.red("Cancelled"));
|
|
210
|
-
process.exit(1);
|
|
211
|
-
}
|
|
212
|
-
projectName = response.projectName;
|
|
213
|
-
}
|
|
214
|
-
if (!/^[a-z0-9-]+$/.test(projectName)) {
|
|
215
|
-
console.log(pc.red(" Error: Project name must use lowercase letters, numbers, and hyphens only"));
|
|
216
|
-
process.exit(1);
|
|
217
|
-
}
|
|
218
|
-
if (!directory) {
|
|
219
|
-
if (nonInteractive) {
|
|
220
|
-
directory = projectName;
|
|
221
|
-
} else {
|
|
222
|
-
const response = await prompts({
|
|
223
|
-
type: "text",
|
|
224
|
-
name: "directory",
|
|
225
|
-
message: "Where should we create the project?",
|
|
226
|
-
initial: projectName
|
|
227
|
-
});
|
|
228
|
-
if (!response.directory) {
|
|
229
|
-
console.log(pc.red("Cancelled"));
|
|
230
|
-
process.exit(1);
|
|
231
|
-
}
|
|
232
|
-
directory = response.directory;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
const projectTitle = toTitleCase(projectName);
|
|
236
|
-
const projectInitial = projectTitle.charAt(0).toUpperCase();
|
|
237
|
-
const targetDir = resolve(process.cwd(), directory);
|
|
238
|
-
if (existsSync(targetDir)) {
|
|
239
|
-
if (nonInteractive) {
|
|
240
|
-
if (opts.force) {
|
|
241
|
-
rmSync(targetDir, { recursive: true });
|
|
242
|
-
} else {
|
|
243
|
-
console.log(pc.red(` Error: Directory ${directory} already exists`));
|
|
244
|
-
console.log(pc.dim(" Use --force (-f) to overwrite"));
|
|
245
|
-
process.exit(1);
|
|
246
|
-
}
|
|
247
|
-
} else {
|
|
248
|
-
const { overwrite } = await prompts({
|
|
249
|
-
type: "confirm",
|
|
250
|
-
name: "overwrite",
|
|
251
|
-
message: `Directory ${directory} already exists. Overwrite?`,
|
|
252
|
-
initial: false
|
|
253
|
-
});
|
|
254
|
-
if (!overwrite) {
|
|
255
|
-
console.log(pc.red("Cancelled"));
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
rmSync(targetDir, { recursive: true });
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
mkdirSync(targetDir, { recursive: true });
|
|
262
|
-
console.log();
|
|
263
|
-
console.log(pc.dim(` Creating ${projectName} in ${directory}...`));
|
|
264
|
-
console.log();
|
|
265
|
-
const templatesDir = getTemplatesDir();
|
|
266
|
-
const vars = {
|
|
267
|
-
projectName,
|
|
268
|
-
projectTitle,
|
|
269
|
-
projectInitial
|
|
270
|
-
};
|
|
271
|
-
copyDir(templatesDir, targetDir, vars);
|
|
272
|
-
function listFiles(dir, prefix = "") {
|
|
273
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
274
|
-
const relativePath = prefix + entry.name;
|
|
275
|
-
if (entry.isDirectory()) {
|
|
276
|
-
listFiles(join(dir, entry.name), relativePath + "/");
|
|
277
|
-
} else {
|
|
278
|
-
console.log(pc.green(" \u2713"), pc.dim(relativePath));
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
listFiles(targetDir);
|
|
283
|
-
if (isGitInstalled()) {
|
|
284
|
-
console.log();
|
|
285
|
-
console.log(pc.dim(" Initializing git repository..."));
|
|
286
|
-
if (initGitRepo(targetDir, projectName)) {
|
|
287
|
-
console.log(pc.green(" \u2713"), pc.dim("Git repository initialized with initial commit"));
|
|
288
|
-
} else {
|
|
289
|
-
console.log(pc.yellow(" \u26A0"), pc.dim("Failed to initialize git repository"));
|
|
290
|
-
}
|
|
291
|
-
} else {
|
|
292
|
-
console.log();
|
|
293
|
-
console.log(pc.yellow(" \u26A0"), pc.dim("Git not found - skipping repository initialization"));
|
|
294
|
-
}
|
|
295
|
-
let uvAvailable = isUvInstalled();
|
|
296
|
-
if (!uvAvailable) {
|
|
297
|
-
console.log();
|
|
298
|
-
console.log(pc.dim(" Installing uv (Python package manager)..."));
|
|
299
|
-
if (installUv()) {
|
|
300
|
-
console.log(pc.green(" \u2713"), pc.dim("uv installed successfully"));
|
|
301
|
-
uvAvailable = true;
|
|
302
|
-
} else {
|
|
303
|
-
console.log(pc.yellow(" \u26A0"), pc.dim("Failed to install uv - install manually: https://docs.astral.sh/uv/"));
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
if (uvAvailable) {
|
|
307
|
-
console.log();
|
|
308
|
-
console.log(pc.dim(" Creating Python venv with Lumera SDK..."));
|
|
309
|
-
if (createPythonVenv(targetDir)) {
|
|
310
|
-
console.log(pc.green(" \u2713"), pc.dim("Python venv created (.venv/) with lumera SDK"));
|
|
311
|
-
} else {
|
|
312
|
-
console.log(pc.yellow(" \u26A0"), pc.dim("Failed to create Python venv"));
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
if (opts.install) {
|
|
316
|
-
console.log();
|
|
317
|
-
console.log(pc.dim(" Installing dependencies..."));
|
|
318
|
-
try {
|
|
319
|
-
execSync("pnpm install", { cwd: targetDir, stdio: "inherit" });
|
|
320
|
-
console.log(pc.green(" \u2713"), pc.dim("Dependencies installed"));
|
|
321
|
-
} catch {
|
|
322
|
-
console.log(pc.yellow(" \u26A0"), pc.dim("Failed to install dependencies"));
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
console.log();
|
|
326
|
-
console.log(pc.dim(" Installing Lumera skills for AI agents..."));
|
|
327
|
-
try {
|
|
328
|
-
const { installed, failed } = await installAllSkills(targetDir);
|
|
329
|
-
if (failed > 0) {
|
|
330
|
-
console.log(pc.yellow(" \u26A0"), pc.dim(`Installed ${installed} skills (${failed} failed)`));
|
|
331
|
-
} else {
|
|
332
|
-
console.log(pc.green(" \u2713"), pc.dim(`${installed} Lumera skills installed`));
|
|
333
|
-
}
|
|
334
|
-
syncClaudeMd(targetDir);
|
|
335
|
-
if (isGitInstalled()) {
|
|
336
|
-
try {
|
|
337
|
-
execSync('git add -A && git commit -m "chore: install lumera skills"', {
|
|
338
|
-
cwd: targetDir,
|
|
339
|
-
stdio: "ignore"
|
|
340
|
-
});
|
|
341
|
-
} catch {
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
} catch (err) {
|
|
345
|
-
console.log(pc.yellow(" \u26A0"), pc.dim(`Failed to install skills: ${err}`));
|
|
346
|
-
}
|
|
347
|
-
console.log();
|
|
348
|
-
console.log(pc.green(pc.bold(" Done!")), "Next steps:");
|
|
349
|
-
console.log();
|
|
350
|
-
console.log(pc.cyan(` cd ${directory}`));
|
|
351
|
-
if (!opts.install) {
|
|
352
|
-
console.log(pc.cyan(" pnpm install"));
|
|
353
|
-
}
|
|
354
|
-
console.log(pc.cyan(" lumera login"));
|
|
355
|
-
console.log(pc.cyan(" pnpm dev"));
|
|
356
|
-
console.log();
|
|
357
|
-
}
|
|
358
|
-
export {
|
|
359
|
-
init
|
|
360
|
-
};
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# {{projectTitle}} — Architecture
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
{{projectTitle}} is a Lumera embedded app — a React frontend served inside the Lumera platform iframe, backed by collections, hooks, and automations managed through the Lumera CLI.
|
|
6
|
-
|
|
7
|
-
## System Diagram
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
┌─────────────────────────────────────────────────┐
|
|
11
|
-
│ Lumera Platform │
|
|
12
|
-
│ │
|
|
13
|
-
│ ┌───────────┐ postMessage ┌────────────┐ │
|
|
14
|
-
│ │ Host UI │ ◄──────────────► │ App │ │
|
|
15
|
-
│ │ │ (auth, init) │ (iframe) │ │
|
|
16
|
-
│ └─────┬─────┘ └─────┬──────┘ │
|
|
17
|
-
│ │ │ │
|
|
18
|
-
│ │ REST API │ │
|
|
19
|
-
│ ▼ ▼ │
|
|
20
|
-
│ ┌──────────────────────────────────────────┐ │
|
|
21
|
-
│ │ Lumera API │ │
|
|
22
|
-
│ │ - Collections (CRUD, SQL, search) │ │
|
|
23
|
-
│ │ - Automations (run, poll, cancel) │ │
|
|
24
|
-
│ │ - File storage (upload, download) │ │
|
|
25
|
-
│ └──────────────┬───────────────────────────┘ │
|
|
26
|
-
│ │ │
|
|
27
|
-
│ ▼ │
|
|
28
|
-
│ ┌──────────────────────────────────────────┐ │
|
|
29
|
-
│ │ Tenant Database (PocketBase/SQLite) │ │
|
|
30
|
-
│ │ - example_items │ │
|
|
31
|
-
│ │ - (add your collections here) │ │
|
|
32
|
-
│ └──────────────────────────────────────────┘ │
|
|
33
|
-
└─────────────────────────────────────────────────┘
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Frontend (`src/`)
|
|
37
|
-
|
|
38
|
-
React app using TanStack Router (file-based routing) and TanStack Query for data fetching. Embedded in Lumera via iframe with postMessage bridge for authentication.
|
|
39
|
-
|
|
40
|
-
| Directory | Purpose |
|
|
41
|
-
|------------------|--------------------------------------|
|
|
42
|
-
| `src/routes/` | Pages — file names map to URL paths |
|
|
43
|
-
| `src/components/`| Shared React components |
|
|
44
|
-
| `src/lib/` | API helpers, query functions |
|
|
45
|
-
| `src/main.tsx` | App entry — auth bridge, router init |
|
|
46
|
-
|
|
47
|
-
**Key patterns:**
|
|
48
|
-
- Auth context flows from `main.tsx` via `AuthContext`
|
|
49
|
-
- Data fetching uses `pbList`, `pbSql` from `@lumerahq/ui/lib`
|
|
50
|
-
- Styling via Tailwind CSS 4 with theme tokens in `styles.css`
|
|
51
|
-
|
|
52
|
-
## Platform Resources (`platform/`)
|
|
53
|
-
|
|
54
|
-
Declarative definitions deployed via `lumera apply`.
|
|
55
|
-
|
|
56
|
-
| Directory | Purpose |
|
|
57
|
-
|--------------------------|----------------------------------|
|
|
58
|
-
| `platform/collections/` | Collection schemas (JSON) |
|
|
59
|
-
| `platform/automations/` | Background Python scripts |
|
|
60
|
-
| `platform/hooks/` | Server-side JS on collection events |
|
|
61
|
-
|
|
62
|
-
## Scripts (`scripts/`)
|
|
63
|
-
|
|
64
|
-
Local Python scripts run via `lumera run`. Used for seeding data, migrations, and ad-hoc operations. All scripts should be idempotent.
|
|
65
|
-
|
|
66
|
-
## Data Flow
|
|
67
|
-
|
|
68
|
-
1. **User opens app** → Lumera host sends auth payload via postMessage
|
|
69
|
-
2. **App authenticates** → Stores session token in `AuthContext`
|
|
70
|
-
3. **App fetches data** → Calls Lumera API via `@lumerahq/ui/lib` helpers
|
|
71
|
-
4. **Data mutations** → API calls trigger collection hooks if configured
|
|
72
|
-
5. **Background work** → Automations run async via `createRun` / `pollRun`
|
|
73
|
-
|
|
74
|
-
## Collections
|
|
75
|
-
|
|
76
|
-
| Collection | Purpose |
|
|
77
|
-
|------------------|----------------------------|
|
|
78
|
-
| `example_items` | Starter collection (replace with your own) |
|
|
79
|
-
|
|
80
|
-
_Update this table as you add collections._
|