@lumerahq/cli 0.31.1 → 0.32.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 +3 -1
- package/dist/{chunk-IVII7OZZ.js → chunk-KE26WWAQ.js} +232 -21
- package/dist/{dev-7ZQRZJXX.js → dev-ANVS25HB.js} +1 -1
- package/dist/index.js +12 -10
- package/dist/{resources-FHL4IUQD.js → resources-4677FHV6.js} +36 -8
- package/package.json +1 -1
- package/templates/default/package.json +1 -1
- package/templates/default/pnpm-lock.yaml +5 -5
package/README.md
CHANGED
|
@@ -20,7 +20,9 @@ lumera whoami # Show current user
|
|
|
20
20
|
lumera status # Show project info
|
|
21
21
|
|
|
22
22
|
lumera dev # Start dev server
|
|
23
|
-
lumera apply app # Deploy frontend
|
|
23
|
+
lumera apply app # Deploy frontend and App API
|
|
24
|
+
lumera apply app-api # Deploy only the App API
|
|
25
|
+
lumera apply app --skip-app-api # Deploy only the frontend
|
|
24
26
|
lumera destroy app # Delete app from Lumera
|
|
25
27
|
|
|
26
28
|
lumera plan # Preview changes (with inline diffs)
|
|
@@ -8,10 +8,168 @@ import {
|
|
|
8
8
|
// src/lib/deploy.ts
|
|
9
9
|
import { execSync, spawn } from "child_process";
|
|
10
10
|
import { createConnection } from "net";
|
|
11
|
-
import { existsSync, readFileSync } from "fs";
|
|
12
|
-
import { resolve, dirname } from "path";
|
|
13
|
-
import
|
|
11
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
12
|
+
import { resolve as resolve2, dirname } from "path";
|
|
13
|
+
import archiver2 from "archiver";
|
|
14
14
|
import pc from "picocolors";
|
|
15
|
+
|
|
16
|
+
// src/lib/app-runtime-deploy.ts
|
|
17
|
+
import { execFileSync } from "child_process";
|
|
18
|
+
import {
|
|
19
|
+
cpSync,
|
|
20
|
+
existsSync,
|
|
21
|
+
mkdtempSync,
|
|
22
|
+
rmSync
|
|
23
|
+
} from "fs";
|
|
24
|
+
import { tmpdir } from "os";
|
|
25
|
+
import { basename, join, relative, resolve, sep } from "path";
|
|
26
|
+
import archiver from "archiver";
|
|
27
|
+
var APP_RUNTIME_RELATIVE_DIR = join("app-api", "lambda");
|
|
28
|
+
var APP_RUNTIME_PYTHON_VERSION = "3.13";
|
|
29
|
+
var APP_RUNTIME_PYTHON_PLATFORM = "aarch64-manylinux_2_34";
|
|
30
|
+
var MAX_APP_RUNTIME_ZIP_BYTES = 45 * 1024 * 1024;
|
|
31
|
+
var APP_RUNTIME_TARGET_ARGS = [
|
|
32
|
+
"--python-version",
|
|
33
|
+
APP_RUNTIME_PYTHON_VERSION,
|
|
34
|
+
"--python-platform",
|
|
35
|
+
APP_RUNTIME_PYTHON_PLATFORM
|
|
36
|
+
];
|
|
37
|
+
var EXCLUDED_SOURCE_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
38
|
+
".git",
|
|
39
|
+
".venv",
|
|
40
|
+
"venv",
|
|
41
|
+
"__pycache__",
|
|
42
|
+
".pytest_cache",
|
|
43
|
+
".ruff_cache",
|
|
44
|
+
".lambda-build",
|
|
45
|
+
"build",
|
|
46
|
+
"dist"
|
|
47
|
+
]);
|
|
48
|
+
function resolveAppRuntimeDirectory(projectRoot) {
|
|
49
|
+
const runtimeDir = resolve(projectRoot, APP_RUNTIME_RELATIVE_DIR);
|
|
50
|
+
if (!existsSync(runtimeDir)) return void 0;
|
|
51
|
+
if (!existsSync(join(runtimeDir, "server.py"))) {
|
|
52
|
+
throw new Error(`App API directory exists but is missing ${join(APP_RUNTIME_RELATIVE_DIR, "server.py")}`);
|
|
53
|
+
}
|
|
54
|
+
return runtimeDir;
|
|
55
|
+
}
|
|
56
|
+
function shouldCopyRuntimeSource(runtimeDir, sourcePath) {
|
|
57
|
+
const sourceRelativePath = relative(runtimeDir, sourcePath);
|
|
58
|
+
if (!sourceRelativePath) return true;
|
|
59
|
+
const segments = sourceRelativePath.split(sep);
|
|
60
|
+
if (segments.some((segment) => EXCLUDED_SOURCE_DIRECTORIES.has(segment))) return false;
|
|
61
|
+
if (segments.some((segment) => segment === ".env" || segment.startsWith(".env."))) return false;
|
|
62
|
+
const name = basename(sourcePath);
|
|
63
|
+
if (name === "lambda.zip") return false;
|
|
64
|
+
return !name.endsWith(".pyc") && !name.endsWith(".pyo");
|
|
65
|
+
}
|
|
66
|
+
function copyRuntimeSources(runtimeDir, buildDir) {
|
|
67
|
+
cpSync(runtimeDir, buildDir, {
|
|
68
|
+
recursive: true,
|
|
69
|
+
filter: (candidate) => shouldCopyRuntimeSource(runtimeDir, candidate)
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function ensureUVAvailable(runCommand) {
|
|
73
|
+
try {
|
|
74
|
+
runCommand("uv", ["--version"], { stdio: "ignore" });
|
|
75
|
+
} catch (error) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
"uv is required to package App APIs. Install uv: https://docs.astral.sh/uv/",
|
|
78
|
+
{ cause: error }
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function createZip(directory) {
|
|
83
|
+
return new Promise((resolveZip, reject) => {
|
|
84
|
+
const chunks = [];
|
|
85
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
86
|
+
archive.on("data", (chunk) => chunks.push(chunk));
|
|
87
|
+
archive.on("end", () => resolveZip(Buffer.concat(chunks)));
|
|
88
|
+
archive.on("error", reject);
|
|
89
|
+
archive.directory(directory, false);
|
|
90
|
+
archive.finalize();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async function packageAppRuntime(runtimeDir, options = {}) {
|
|
94
|
+
if (!existsSync(join(runtimeDir, "server.py"))) {
|
|
95
|
+
throw new Error(`App API must contain server.py at ${join(runtimeDir, "server.py")}`);
|
|
96
|
+
}
|
|
97
|
+
const requirementsPath = join(runtimeDir, "requirements.txt");
|
|
98
|
+
const pyprojectPath = join(runtimeDir, "pyproject.toml");
|
|
99
|
+
if (!existsSync(requirementsPath) && !existsSync(pyprojectPath)) {
|
|
100
|
+
throw new Error("App API must contain requirements.txt or pyproject.toml");
|
|
101
|
+
}
|
|
102
|
+
const buildDir = mkdtempSync(join(tmpdir(), "lumera-app-runtime-"));
|
|
103
|
+
const runCommand = options.runCommand ?? execFileSync;
|
|
104
|
+
try {
|
|
105
|
+
ensureUVAvailable(runCommand);
|
|
106
|
+
let installRequirementsPath = requirementsPath;
|
|
107
|
+
let generatedRequirements = false;
|
|
108
|
+
if (!existsSync(requirementsPath)) {
|
|
109
|
+
installRequirementsPath = join(buildDir, "requirements.generated.txt");
|
|
110
|
+
generatedRequirements = true;
|
|
111
|
+
runCommand(
|
|
112
|
+
"uv",
|
|
113
|
+
[
|
|
114
|
+
"pip",
|
|
115
|
+
"compile",
|
|
116
|
+
"pyproject.toml",
|
|
117
|
+
...APP_RUNTIME_TARGET_ARGS,
|
|
118
|
+
"-o",
|
|
119
|
+
installRequirementsPath
|
|
120
|
+
],
|
|
121
|
+
{
|
|
122
|
+
cwd: runtimeDir,
|
|
123
|
+
stdio: "inherit"
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
runCommand("uv", [
|
|
128
|
+
"pip",
|
|
129
|
+
"install",
|
|
130
|
+
"--target",
|
|
131
|
+
buildDir,
|
|
132
|
+
...APP_RUNTIME_TARGET_ARGS,
|
|
133
|
+
"--only-binary=:all:",
|
|
134
|
+
"-r",
|
|
135
|
+
installRequirementsPath
|
|
136
|
+
], { stdio: "inherit" });
|
|
137
|
+
if (generatedRequirements) {
|
|
138
|
+
rmSync(installRequirementsPath, { force: true });
|
|
139
|
+
}
|
|
140
|
+
copyRuntimeSources(runtimeDir, buildDir);
|
|
141
|
+
const lambdaZip = await createZip(buildDir);
|
|
142
|
+
if (lambdaZip.length > MAX_APP_RUNTIME_ZIP_BYTES) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`App API package is too large (${lambdaZip.length} bytes > ${MAX_APP_RUNTIME_ZIP_BYTES}); remove heavy dependencies or split the app`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
return lambdaZip;
|
|
148
|
+
} finally {
|
|
149
|
+
rmSync(buildDir, { recursive: true, force: true });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function deployAppRuntime(options) {
|
|
153
|
+
const formData = new FormData();
|
|
154
|
+
formData.append("lambda_zip", new Blob([new Uint8Array(options.lambdaZip)]), "lambda.zip");
|
|
155
|
+
formData.append("version", options.version);
|
|
156
|
+
const res = await fetchWithRetry(`${options.apiUrl}/app-runtimes/current-project/deploy`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: {
|
|
159
|
+
Authorization: `Bearer ${options.token}`,
|
|
160
|
+
"X-Lumera-Client": "lumera-cli",
|
|
161
|
+
"X-Lumera-Project": options.projectExternalId,
|
|
162
|
+
...userContextHeaders()
|
|
163
|
+
},
|
|
164
|
+
body: formData
|
|
165
|
+
});
|
|
166
|
+
if (!res.ok) {
|
|
167
|
+
throw new Error(`App API deploy failed: ${await res.text()}`);
|
|
168
|
+
}
|
|
169
|
+
await res.arrayBuffer();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/lib/deploy.ts
|
|
15
173
|
function formatTimestampVersion(date = /* @__PURE__ */ new Date()) {
|
|
16
174
|
const pad = (value) => String(value).padStart(2, "0");
|
|
17
175
|
return [
|
|
@@ -25,7 +183,7 @@ function formatTimestampVersion(date = /* @__PURE__ */ new Date()) {
|
|
|
25
183
|
].join("") + "Z";
|
|
26
184
|
}
|
|
27
185
|
function resolveVersion(cwd, gitRelease = resolveGitReleaseState(cwd)) {
|
|
28
|
-
const pkgPath =
|
|
186
|
+
const pkgPath = resolve2(cwd, "package.json");
|
|
29
187
|
try {
|
|
30
188
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
31
189
|
if (pkg?.version && pkg.version !== "0.0.0") return String(pkg.version);
|
|
@@ -37,13 +195,13 @@ function resolveVersion(cwd, gitRelease = resolveGitReleaseState(cwd)) {
|
|
|
37
195
|
function resolveUIVersion(cwd) {
|
|
38
196
|
try {
|
|
39
197
|
const installed = JSON.parse(
|
|
40
|
-
readFileSync(
|
|
198
|
+
readFileSync(resolve2(cwd, "node_modules/@lumerahq/ui/package.json"), "utf-8")
|
|
41
199
|
);
|
|
42
200
|
if (installed?.version) return String(installed.version);
|
|
43
201
|
} catch {
|
|
44
202
|
}
|
|
45
203
|
try {
|
|
46
|
-
const pkg = JSON.parse(readFileSync(
|
|
204
|
+
const pkg = JSON.parse(readFileSync(resolve2(cwd, "package.json"), "utf-8"));
|
|
47
205
|
const spec = pkg?.dependencies?.["@lumerahq/ui"] ?? pkg?.devDependencies?.["@lumerahq/ui"];
|
|
48
206
|
if (!spec) return "none";
|
|
49
207
|
const cleaned = String(spec).replace(/^[\^~>=<\s]+/, "");
|
|
@@ -79,16 +237,56 @@ function resolveGitReleaseState(cwd) {
|
|
|
79
237
|
}
|
|
80
238
|
}
|
|
81
239
|
async function createTarball(distDir) {
|
|
82
|
-
return new Promise((
|
|
240
|
+
return new Promise((resolve3, reject) => {
|
|
83
241
|
const chunks = [];
|
|
84
|
-
const archive =
|
|
242
|
+
const archive = archiver2("tar", { gzip: true });
|
|
85
243
|
archive.on("data", (chunk) => chunks.push(chunk));
|
|
86
|
-
archive.on("end", () =>
|
|
244
|
+
archive.on("end", () => resolve3(Buffer.concat(chunks)));
|
|
87
245
|
archive.on("error", reject);
|
|
88
246
|
archive.directory(distDir, false);
|
|
89
247
|
archive.finalize();
|
|
90
248
|
});
|
|
91
249
|
}
|
|
250
|
+
async function packageResolvedAppRuntime(appRuntimeDir) {
|
|
251
|
+
console.log(pc.dim("Packaging App API..."));
|
|
252
|
+
const lambdaZip = await packageAppRuntime(appRuntimeDir);
|
|
253
|
+
console.log(pc.dim(`App API package: ${(lambdaZip.length / 1024).toFixed(1)} KB`));
|
|
254
|
+
return lambdaZip;
|
|
255
|
+
}
|
|
256
|
+
async function packageOptionalAppRuntime(projectRoot) {
|
|
257
|
+
const appRuntimeDir = resolveAppRuntimeDirectory(projectRoot);
|
|
258
|
+
return appRuntimeDir ? packageResolvedAppRuntime(appRuntimeDir) : void 0;
|
|
259
|
+
}
|
|
260
|
+
async function packageRequiredAppRuntime(projectRoot) {
|
|
261
|
+
const appRuntimeDir = resolveAppRuntimeDirectory(projectRoot);
|
|
262
|
+
if (!appRuntimeDir) {
|
|
263
|
+
throw new Error("App API not found. Expected app-api/lambda/server.py");
|
|
264
|
+
}
|
|
265
|
+
return packageResolvedAppRuntime(appRuntimeDir);
|
|
266
|
+
}
|
|
267
|
+
function resolveDeploymentVersion(projectRoot, gitRelease, explicitVersion) {
|
|
268
|
+
return explicitVersion || process.env.LUMERA_APP_VERSION?.trim() || resolveVersion(projectRoot, gitRelease);
|
|
269
|
+
}
|
|
270
|
+
async function publishAppRuntime(options) {
|
|
271
|
+
console.log(pc.cyan("Deploying App API..."));
|
|
272
|
+
await deployAppRuntime(options);
|
|
273
|
+
console.log(pc.green("App API deployed!"));
|
|
274
|
+
}
|
|
275
|
+
async function deployAppAPI(options) {
|
|
276
|
+
const gitRelease = resolveGitReleaseState(options.projectRoot);
|
|
277
|
+
assertDeployableGitRelease(gitRelease);
|
|
278
|
+
const lambdaZip = await packageRequiredAppRuntime(options.projectRoot);
|
|
279
|
+
const version = resolveDeploymentVersion(options.projectRoot, gitRelease, options.version);
|
|
280
|
+
console.log(pc.dim(`Version: ${version}`));
|
|
281
|
+
await publishAppRuntime({
|
|
282
|
+
apiUrl: options.apiUrl,
|
|
283
|
+
token: options.token,
|
|
284
|
+
projectExternalId: options.appName,
|
|
285
|
+
version,
|
|
286
|
+
lambdaZip
|
|
287
|
+
});
|
|
288
|
+
return version;
|
|
289
|
+
}
|
|
92
290
|
async function ensureAppRecord(apiBase, token, appName, appTitle, accessLevel) {
|
|
93
291
|
const appData = {
|
|
94
292
|
name: appTitle,
|
|
@@ -118,18 +316,20 @@ async function deploy(options) {
|
|
|
118
316
|
appTitle,
|
|
119
317
|
distDir,
|
|
120
318
|
apiUrl,
|
|
121
|
-
accessLevel
|
|
319
|
+
accessLevel,
|
|
320
|
+
skipAppAPI
|
|
122
321
|
} = options;
|
|
123
|
-
if (!
|
|
322
|
+
if (!existsSync2(distDir)) {
|
|
124
323
|
throw new Error(`dist directory not found: ${distDir}`);
|
|
125
324
|
}
|
|
126
325
|
const projectDir = dirname(distDir);
|
|
127
326
|
const gitRelease = resolveGitReleaseState(projectDir);
|
|
128
327
|
assertDeployableGitRelease(gitRelease);
|
|
328
|
+
const lambdaZip = skipAppAPI ? void 0 : await packageOptionalAppRuntime(projectDir);
|
|
129
329
|
console.log(pc.dim("Ensuring app record..."));
|
|
130
330
|
await ensureAppRecord(apiUrl, token, appName, appTitle, accessLevel);
|
|
131
|
-
console.log(pc.cyan(`
|
|
132
|
-
const version =
|
|
331
|
+
console.log(pc.cyan(`Preparing ${appTitle}...`));
|
|
332
|
+
const version = resolveDeploymentVersion(projectDir, gitRelease, options.version);
|
|
133
333
|
console.log(pc.dim(`Version: ${version}`));
|
|
134
334
|
const tarball = await createTarball(distDir);
|
|
135
335
|
console.log(pc.dim(`Package: ${(tarball.length / 1024).toFixed(1)} KB`));
|
|
@@ -139,6 +339,16 @@ async function deploy(options) {
|
|
|
139
339
|
if (gitRelease.sha) formData.append("git_sha", gitRelease.sha);
|
|
140
340
|
const uiVersion = resolveUIVersion(projectDir);
|
|
141
341
|
if (uiVersion) formData.append("ui_version", uiVersion);
|
|
342
|
+
if (lambdaZip) {
|
|
343
|
+
await publishAppRuntime({
|
|
344
|
+
apiUrl,
|
|
345
|
+
token,
|
|
346
|
+
projectExternalId: appName,
|
|
347
|
+
version,
|
|
348
|
+
lambdaZip
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
console.log(pc.cyan(`Deploying ${appTitle} frontend...`));
|
|
142
352
|
const res = await fetchWithRetry(`${apiUrl}/custom-apps/${appName}/deploy`, {
|
|
143
353
|
method: "POST",
|
|
144
354
|
headers: {
|
|
@@ -181,19 +391,19 @@ async function registerDevApp(apiBase, token, appName, appTitle, appUrl) {
|
|
|
181
391
|
}
|
|
182
392
|
}
|
|
183
393
|
function isPortInUse(port, host = "127.0.0.1") {
|
|
184
|
-
return new Promise((
|
|
394
|
+
return new Promise((resolve3) => {
|
|
185
395
|
const socket = createConnection({ port, host });
|
|
186
396
|
socket.once("connect", () => {
|
|
187
397
|
socket.destroy();
|
|
188
|
-
|
|
398
|
+
resolve3(true);
|
|
189
399
|
});
|
|
190
400
|
socket.once("error", () => {
|
|
191
401
|
socket.destroy();
|
|
192
|
-
|
|
402
|
+
resolve3(false);
|
|
193
403
|
});
|
|
194
404
|
socket.setTimeout(1e3, () => {
|
|
195
405
|
socket.destroy();
|
|
196
|
-
|
|
406
|
+
resolve3(false);
|
|
197
407
|
});
|
|
198
408
|
});
|
|
199
409
|
}
|
|
@@ -213,7 +423,7 @@ function parsePackageManagerSpecifier(value) {
|
|
|
213
423
|
}
|
|
214
424
|
function packageManagerFromPackageJson(projectRoot) {
|
|
215
425
|
try {
|
|
216
|
-
const pkg = JSON.parse(readFileSync(
|
|
426
|
+
const pkg = JSON.parse(readFileSync(resolve2(projectRoot, "package.json"), "utf-8"));
|
|
217
427
|
return parsePackageManagerSpecifier(pkg.packageManager);
|
|
218
428
|
} catch {
|
|
219
429
|
return void 0;
|
|
@@ -243,7 +453,7 @@ function detectRunner(projectRoot) {
|
|
|
243
453
|
];
|
|
244
454
|
for (const { pm, file } of lockfileManagers) {
|
|
245
455
|
const runner = runnerForPackageManager(pm);
|
|
246
|
-
if (
|
|
456
|
+
if (existsSync2(resolve2(projectRoot, file)) && commandAvailable(runner[0])) return runner;
|
|
247
457
|
}
|
|
248
458
|
for (const pm of ["pnpm", "bun", "yarn", "npm"]) {
|
|
249
459
|
const runner = runnerForPackageManager(pm);
|
|
@@ -296,8 +506,8 @@ async function dev(options) {
|
|
|
296
506
|
);
|
|
297
507
|
const runner = detectRunner(projectRoot);
|
|
298
508
|
const viteArgs = ["vite", "--port", String(port), "--strictPort"];
|
|
299
|
-
const sandboxConfig =
|
|
300
|
-
if (
|
|
509
|
+
const sandboxConfig = resolve2(projectRoot, "vite.sandbox.config.ts");
|
|
510
|
+
if (existsSync2(sandboxConfig)) {
|
|
301
511
|
viteArgs.push("--config", "vite.sandbox.config.ts");
|
|
302
512
|
}
|
|
303
513
|
if (host) {
|
|
@@ -325,6 +535,7 @@ async function dev(options) {
|
|
|
325
535
|
}
|
|
326
536
|
|
|
327
537
|
export {
|
|
538
|
+
deployAppAPI,
|
|
328
539
|
deploy,
|
|
329
540
|
dev
|
|
330
541
|
};
|
package/dist/index.js
CHANGED
|
@@ -193,12 +193,14 @@ ${pc.dim("Resource Types:")}
|
|
|
193
193
|
automations Background Python scripts
|
|
194
194
|
hooks Collection lifecycle handlers
|
|
195
195
|
agents AI agents with skills and prompts
|
|
196
|
-
app Frontend
|
|
196
|
+
app Frontend and App API
|
|
197
|
+
app-api FastAPI application backend
|
|
197
198
|
|
|
198
199
|
${pc.dim("Examples:")}
|
|
199
200
|
lumera apply # Apply all resources
|
|
200
201
|
lumera apply collections/users # Apply single collection
|
|
201
|
-
lumera apply app # Deploy frontend
|
|
202
|
+
lumera apply app # Deploy frontend and App API
|
|
203
|
+
lumera apply app-api # Deploy only the App API
|
|
202
204
|
lumera list # List all resources
|
|
203
205
|
lumera show collections/users # Show collection details
|
|
204
206
|
lumera run scripts/seed.py # Run a script
|
|
@@ -240,29 +242,29 @@ async function main() {
|
|
|
240
242
|
switch (command) {
|
|
241
243
|
// Resource commands
|
|
242
244
|
case "plan":
|
|
243
|
-
await import("./resources-
|
|
245
|
+
await import("./resources-4677FHV6.js").then((m) => m.plan(args.slice(1)));
|
|
244
246
|
break;
|
|
245
247
|
case "apply":
|
|
246
|
-
await import("./resources-
|
|
248
|
+
await import("./resources-4677FHV6.js").then((m) => m.apply(args.slice(1)));
|
|
247
249
|
break;
|
|
248
250
|
case "pull":
|
|
249
|
-
await import("./resources-
|
|
251
|
+
await import("./resources-4677FHV6.js").then((m) => m.pull(args.slice(1)));
|
|
250
252
|
break;
|
|
251
253
|
case "destroy":
|
|
252
|
-
await import("./resources-
|
|
254
|
+
await import("./resources-4677FHV6.js").then((m) => m.destroy(args.slice(1)));
|
|
253
255
|
break;
|
|
254
256
|
case "list":
|
|
255
|
-
await import("./resources-
|
|
257
|
+
await import("./resources-4677FHV6.js").then((m) => m.list(args.slice(1)));
|
|
256
258
|
break;
|
|
257
259
|
case "show":
|
|
258
|
-
await import("./resources-
|
|
260
|
+
await import("./resources-4677FHV6.js").then((m) => m.show(args.slice(1)));
|
|
259
261
|
break;
|
|
260
262
|
case "diff":
|
|
261
|
-
await import("./resources-
|
|
263
|
+
await import("./resources-4677FHV6.js").then((m) => m.diff(args.slice(1)));
|
|
262
264
|
break;
|
|
263
265
|
// Development
|
|
264
266
|
case "dev":
|
|
265
|
-
await import("./dev-
|
|
267
|
+
await import("./dev-ANVS25HB.js").then((m) => m.dev(args.slice(1)));
|
|
266
268
|
break;
|
|
267
269
|
case "run":
|
|
268
270
|
await import("./run-7KJPBOIY.js").then((m) => m.run(args.slice(1)));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
deploy
|
|
3
|
-
|
|
2
|
+
deploy,
|
|
3
|
+
deployAppAPI
|
|
4
|
+
} from "./chunk-KE26WWAQ.js";
|
|
4
5
|
import {
|
|
5
6
|
syncDeps
|
|
6
7
|
} from "./chunk-HS63O4MH.js";
|
|
@@ -1457,11 +1458,13 @@ ${pc2.dim("Resources:")}
|
|
|
1457
1458
|
skills/<slug> Apply single project skill
|
|
1458
1459
|
mailboxes Apply only mailboxes
|
|
1459
1460
|
mailboxes/<slug> Apply single mailbox
|
|
1460
|
-
app Deploy the frontend
|
|
1461
|
+
app Deploy the frontend and App API
|
|
1462
|
+
app-api Deploy only the App API
|
|
1461
1463
|
|
|
1462
1464
|
${pc2.dim("Options:")}
|
|
1463
1465
|
--yes, -y Skip confirmation prompt (for CI/CD)
|
|
1464
|
-
--skip-build Skip build
|
|
1466
|
+
--skip-build Skip the frontend build; still package the App API
|
|
1467
|
+
--skip-app-api Publish only the frontend, even if an App API exists
|
|
1465
1468
|
--no-app Apply resources only; do not build/deploy the app
|
|
1466
1469
|
--resources-only Alias for --no-app
|
|
1467
1470
|
--package-manager Package manager for app builds (pnpm, bun, yarn, npm)
|
|
@@ -1471,8 +1474,10 @@ ${pc2.dim("Examples:")}
|
|
|
1471
1474
|
lumera apply collections # Apply all collections
|
|
1472
1475
|
lumera apply collections/users # Apply single collection
|
|
1473
1476
|
lumera apply agents -y # Apply agents without confirmation
|
|
1474
|
-
lumera apply app # Deploy frontend
|
|
1475
|
-
lumera apply app
|
|
1477
|
+
lumera apply app # Deploy frontend and App API
|
|
1478
|
+
lumera apply app-api # Deploy only the App API
|
|
1479
|
+
lumera apply app --skip-build # Reuse dist; package and deploy App API
|
|
1480
|
+
lumera apply app --skip-app-api # Deploy only the frontend
|
|
1476
1481
|
lumera apply --no-app -y # Apply resources without app build/deploy
|
|
1477
1482
|
lumera apply app --package-manager pnpm
|
|
1478
1483
|
`);
|
|
@@ -2478,6 +2483,7 @@ async function applyHooks(api, localHooks, collections, projectId) {
|
|
|
2478
2483
|
}
|
|
2479
2484
|
async function applyApp(args) {
|
|
2480
2485
|
const skipBuild = args.includes("--skip-build");
|
|
2486
|
+
const skipAppAPI = args.includes("--skip-app-api");
|
|
2481
2487
|
const projectRoot = findProjectRoot();
|
|
2482
2488
|
loadEnv(projectRoot);
|
|
2483
2489
|
const token = getToken(projectRoot);
|
|
@@ -2508,7 +2514,15 @@ async function applyApp(args) {
|
|
|
2508
2514
|
`Invalid --access-level "${accessLevel}". Valid values: ${validAccessLevels.join(", ")}`
|
|
2509
2515
|
);
|
|
2510
2516
|
}
|
|
2511
|
-
await deploy({ token, appName, appTitle, distDir, apiUrl, accessLevel });
|
|
2517
|
+
await deploy({ token, appName, appTitle, distDir, apiUrl, accessLevel, skipAppAPI });
|
|
2518
|
+
}
|
|
2519
|
+
async function applyAppAPI(projectRoot) {
|
|
2520
|
+
await deployAppAPI({
|
|
2521
|
+
token: getToken(projectRoot),
|
|
2522
|
+
appName: getAppName(projectRoot),
|
|
2523
|
+
projectRoot,
|
|
2524
|
+
apiUrl: getApiUrl()
|
|
2525
|
+
});
|
|
2512
2526
|
}
|
|
2513
2527
|
async function pullCollections(api, platformDir, filterName, projectId) {
|
|
2514
2528
|
const collectionsDir = join2(platformDir, "collections");
|
|
@@ -4351,11 +4365,25 @@ async function apply(args) {
|
|
|
4351
4365
|
}
|
|
4352
4366
|
const projectRoot = findProjectRoot();
|
|
4353
4367
|
loadEnv(projectRoot);
|
|
4368
|
+
const positionalArgs = getPositionalArgs(args);
|
|
4369
|
+
if (positionalArgs[0] === "app-api") {
|
|
4370
|
+
if (args.includes("--skip-app-api")) {
|
|
4371
|
+
throw new Error("Cannot combine app-api resource with --skip-app-api");
|
|
4372
|
+
}
|
|
4373
|
+
console.log();
|
|
4374
|
+
console.log(pc2.cyan(pc2.bold(" Apply")));
|
|
4375
|
+
console.log();
|
|
4376
|
+
console.log(pc2.bold(" App API:"));
|
|
4377
|
+
await applyAppAPI(projectRoot);
|
|
4378
|
+
console.log();
|
|
4379
|
+
console.log(pc2.green(" Done!"));
|
|
4380
|
+
console.log();
|
|
4381
|
+
return;
|
|
4382
|
+
}
|
|
4354
4383
|
const platformDir = getPlatformDir();
|
|
4355
4384
|
const appName = getAppName(projectRoot);
|
|
4356
4385
|
const api = createApiClient(void 0, void 0, appName);
|
|
4357
4386
|
const projectId = getProjectId(projectRoot);
|
|
4358
|
-
const positionalArgs = getPositionalArgs(args);
|
|
4359
4387
|
const { type, name } = parseResource(positionalArgs[0]);
|
|
4360
4388
|
const autoConfirm = args.includes("--yes") || args.includes("-y") || !!process.env.CI;
|
|
4361
4389
|
const skipApp = args.includes("--no-app") || args.includes("--resources-only");
|
package/package.json
CHANGED
|
@@ -19,8 +19,8 @@ importers:
|
|
|
19
19
|
.:
|
|
20
20
|
dependencies:
|
|
21
21
|
'@lumerahq/ui':
|
|
22
|
-
specifier: ^0.
|
|
23
|
-
version: 0.
|
|
22
|
+
specifier: ^0.20.0
|
|
23
|
+
version: 0.20.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
|
24
24
|
'@tanstack/react-query':
|
|
25
25
|
specifier: ^5.90.11
|
|
26
26
|
version: 5.101.0(react@19.2.7)
|
|
@@ -464,8 +464,8 @@ packages:
|
|
|
464
464
|
'@jridgewell/trace-mapping@0.3.31':
|
|
465
465
|
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
|
466
466
|
|
|
467
|
-
'@lumerahq/ui@0.
|
|
468
|
-
resolution: {integrity: sha512-
|
|
467
|
+
'@lumerahq/ui@0.20.0':
|
|
468
|
+
resolution: {integrity: sha512-WMfDaM122WUvoZ/JYmoCGxqE13zKTwCnEJCx/BYu5NFFZMb2I+5ZhMMgFmMLSX112AmxPe30FjnqBtEEhhIuZw==}
|
|
469
469
|
peerDependencies:
|
|
470
470
|
react: ^18.0.0 || ^19.0.0
|
|
471
471
|
react-dom: ^18.0.0 || ^19.0.0
|
|
@@ -2318,7 +2318,7 @@ snapshots:
|
|
|
2318
2318
|
'@jridgewell/resolve-uri': 3.1.2
|
|
2319
2319
|
'@jridgewell/sourcemap-codec': 1.5.5
|
|
2320
2320
|
|
|
2321
|
-
'@lumerahq/ui@0.
|
|
2321
|
+
'@lumerahq/ui@0.20.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
|
|
2322
2322
|
dependencies:
|
|
2323
2323
|
'@base-ui/react': 1.5.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
|
2324
2324
|
'@tanstack/react-query': 5.101.0(react@19.2.7)
|