@lumerahq/cli 0.36.5 → 0.37.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 CHANGED
@@ -51,6 +51,30 @@ isolated overlay, so an older project lock cannot select a pre-Functions SDK.
51
51
  Function modules and test locations are configured under
52
52
  `[tool.lumera.functions]` in `pyproject.toml`.
53
53
 
54
+ ## Project display name
55
+
56
+ `lumera init` and `lumera register` copy the registered project's current
57
+ display name into `lumera.name`. If initialization cannot register, it omits
58
+ the generated display name so a first deploy preserves the server's name.
59
+
60
+ Set the display name in `package.json`:
61
+
62
+ ```json
63
+ {
64
+ "name": "pr-review-hub",
65
+ "lumera": { "name": "PR Review Hub" }
66
+ }
67
+ ```
68
+
69
+ Every app deployment applies `lumera.name` to the linked project before
70
+ publishing, including App API-only and frontend-only deployments. The npm
71
+ `name` remains the stable project identifier. Omitting `lumera.name` preserves
72
+ the current project name. Renaming requires project edit access; a rejected
73
+ rename stops deployment. UI renames update this field in GitHub through a durable
74
+ workflow before changing the project name. The UI waits for the save to finish.
75
+ Pull current repository changes into external checkouts before deploying:
76
+ an old checkout also reapplies its configured name, even after a UI rename.
77
+
54
78
  ## Scaffolding Projects
55
79
 
56
80
  ### Interactive Mode
@@ -3,7 +3,7 @@ import {
3
3
  getCredentials,
4
4
  getTokenSource,
5
5
  init_auth
6
- } from "./chunk-JLVVHTBY.js";
6
+ } from "./chunk-MBTOP7VA.js";
7
7
  import {
8
8
  fetchWithRetry
9
9
  } from "./chunk-FJFIWC7G.js";
@@ -4,11 +4,11 @@ import {
4
4
  import {
5
5
  createApiClient,
6
6
  isApiErrorStatus
7
- } from "./chunk-RV7WQAK3.js";
7
+ } from "./chunk-YJJGLYF7.js";
8
8
  import {
9
9
  findProjectRoot,
10
10
  getAppName
11
- } from "./chunk-JLVVHTBY.js";
11
+ } from "./chunk-MBTOP7VA.js";
12
12
 
13
13
  // src/commands/deps.ts
14
14
  import pc from "picocolors";
@@ -8,7 +8,10 @@ import {
8
8
  } from "./chunk-CHUCWVFV.js";
9
9
  import {
10
10
  userContextHeaders
11
- } from "./chunk-RV7WQAK3.js";
11
+ } from "./chunk-YJJGLYF7.js";
12
+ import {
13
+ readPackageJson
14
+ } from "./chunk-MBTOP7VA.js";
12
15
  import {
13
16
  fetchWithRetry
14
17
  } from "./chunk-FJFIWC7G.js";
@@ -20,6 +23,46 @@ import { existsSync, readFileSync } from "fs";
20
23
  import { resolve, dirname } from "path";
21
24
  import archiver from "archiver";
22
25
  import pc from "picocolors";
26
+ async function syncProjectName(projectRoot, apiUrl, token, projectExternalId) {
27
+ const name = readPackageJson(projectRoot).lumera?.name;
28
+ if (name === void 0) return;
29
+ if (typeof name !== "string" || !name.trim()) {
30
+ throw new Error("package.json lumera.name must be a nonempty string");
31
+ }
32
+ const res = await fetchWithRetry(`${apiUrl}/projects/${encodeURIComponent(projectExternalId)}/settings`, {
33
+ method: "PATCH",
34
+ headers: {
35
+ Authorization: `Bearer ${token}`,
36
+ "Content-Type": "application/json",
37
+ "X-Lumera-Client": "lumera-cli",
38
+ ...userContextHeaders()
39
+ },
40
+ body: JSON.stringify({ name: name.trim() })
41
+ });
42
+ if (!res.ok) {
43
+ throw new Error(`Failed to update project name: ${await res.text()}`);
44
+ }
45
+ if (res.status === 202) {
46
+ const started = await res.json();
47
+ if (!started.task_id) throw new Error("Project rename returned no task ID");
48
+ const deadline = Date.now() + 18e4;
49
+ for (; ; ) {
50
+ const status = await fetchWithRetry(`${apiUrl}/workflow-runs/${encodeURIComponent(started.task_id)}`, {
51
+ headers: { Authorization: `Bearer ${token}`, "X-Lumera-Client": "lumera-cli", ...userContextHeaders() }
52
+ });
53
+ if (!status.ok) throw new Error(`Failed to check project rename: ${await status.text()}`);
54
+ const run = await status.json();
55
+ if (run.terminal) {
56
+ if (run.state !== "completed" || run.result?.value?.status !== "applied") {
57
+ throw new Error(`Project rename did not complete: ${JSON.stringify(run.failure ?? run.result)}. Task: ${started.task_id}`);
58
+ }
59
+ break;
60
+ }
61
+ if (Date.now() >= deadline) throw new Error(`Project rename is still running. Task: ${started.task_id}`);
62
+ await new Promise((resolve2) => setTimeout(resolve2, 1e3));
63
+ }
64
+ }
65
+ }
23
66
  function formatTimestampVersion(date = /* @__PURE__ */ new Date()) {
24
67
  const pad = (value) => String(value).padStart(2, "0");
25
68
  return [
@@ -160,6 +203,7 @@ async function deployAppAPI(options) {
160
203
  }
161
204
  const version = resolveDeploymentVersion(options.projectRoot, gitRelease, options.version);
162
205
  console.log(pc.dim(`Version: ${version}`));
206
+ await syncProjectName(options.projectRoot, options.apiUrl, options.token, options.appName);
163
207
  await deployProjectRuntime({
164
208
  apiUrl: options.apiUrl,
165
209
  token: options.token,
@@ -168,9 +212,8 @@ async function deployAppAPI(options) {
168
212
  });
169
213
  return version;
170
214
  }
171
- async function ensureAppRecord(apiBase, token, appName, appTitle, accessLevel) {
215
+ async function ensureAppRecord(apiBase, token, appName, accessLevel) {
172
216
  const appData = {
173
- name: appTitle,
174
217
  hosting_type: "s3"
175
218
  };
176
219
  if (accessLevel) {
@@ -189,12 +232,13 @@ async function ensureAppRecord(apiBase, token, appName, appTitle, accessLevel) {
189
232
  if (!res.ok) {
190
233
  throw new Error(`Failed to register app: ${await res.text()}`);
191
234
  }
235
+ const payload = await res.json();
236
+ return payload.app?.name || appName;
192
237
  }
193
238
  async function deploy(options) {
194
239
  const {
195
240
  token,
196
241
  appName,
197
- appTitle,
198
242
  distDir,
199
243
  apiUrl,
200
244
  accessLevel,
@@ -207,8 +251,9 @@ async function deploy(options) {
207
251
  const gitRelease = resolveGitReleaseState(projectDir);
208
252
  assertDeployableGitRelease(gitRelease);
209
253
  const projectRuntime = skipAppAPI ? { kind: "none" } : resolveConfiguredProjectRuntime(projectDir);
254
+ await syncProjectName(projectDir, apiUrl, token, appName);
210
255
  console.log(pc.dim("Ensuring app record..."));
211
- await ensureAppRecord(apiUrl, token, appName, appTitle, accessLevel);
256
+ const appTitle = await ensureAppRecord(apiUrl, token, appName, accessLevel);
212
257
  console.log(pc.cyan(`Preparing ${appTitle}...`));
213
258
  const version = options.reuseFrontend ? options.version || process.env.LUMERA_APP_VERSION?.trim() || gitRelease.sha.slice(0, 7) : resolveDeploymentVersion(projectDir, gitRelease, options.version);
214
259
  if (options.reuseFrontend && !gitRelease.sha) {
@@ -258,9 +303,8 @@ Deployed! ${launchUrl}`));
258
303
  }
259
304
  return { launchUrl, appUrl, version };
260
305
  }
261
- async function registerDevApp(apiBase, token, appName, appTitle, appUrl) {
306
+ async function registerDevApp(apiBase, token, appName, appUrl) {
262
307
  const appData = {
263
- name: appTitle,
264
308
  dev_iframe_url: appUrl
265
309
  };
266
310
  const res = await fetchWithRetry(`${apiBase}/custom-apps/${encodeURIComponent(appName)}/registration`, {
@@ -276,6 +320,8 @@ async function registerDevApp(apiBase, token, appName, appTitle, appUrl) {
276
320
  if (!res.ok) {
277
321
  throw new Error(`Failed to register app: ${await res.text()}`);
278
322
  }
323
+ const payload = await res.json();
324
+ return payload.app?.name || appName;
279
325
  }
280
326
  function isPortInUse(port, host = "127.0.0.1") {
281
327
  return new Promise((resolve2) => {
@@ -355,7 +401,6 @@ async function dev(options) {
355
401
  const {
356
402
  token,
357
403
  appName,
358
- appTitle,
359
404
  port,
360
405
  host,
361
406
  appUrl = `http://localhost:${port}`,
@@ -369,7 +414,7 @@ async function dev(options) {
369
414
  return;
370
415
  }
371
416
  console.log(pc.dim(" Registering dev preview URL..."));
372
- await registerDevApp(apiUrl, token, appName, appTitle, appUrl);
417
+ const appTitle = await registerDevApp(apiUrl, token, appName, appUrl);
373
418
  console.log();
374
419
  console.log(pc.cyan(pc.bold(` ${appTitle} - Dev Server`)));
375
420
  console.log();
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getBaseUrl
3
- } from "./chunk-JLVVHTBY.js";
3
+ } from "./chunk-MBTOP7VA.js";
4
4
  import {
5
5
  fetchWithRetry
6
6
  } from "./chunk-FJFIWC7G.js";
@@ -139,10 +139,6 @@ function getAppName(projectRoot) {
139
139
  const pkg = readPackageJson(projectRoot);
140
140
  return pkg.name;
141
141
  }
142
- function getAppTitle(projectRoot) {
143
- const pkg = readPackageJson(projectRoot);
144
- return pkg.lumera?.name || pkg.name;
145
- }
146
142
  function detectProjectVersion(projectRoot) {
147
143
  const pkg = readPackageJson(projectRoot);
148
144
  if (pkg.lumera?.version !== void 0) {
@@ -160,12 +156,13 @@ function getProjectId(projectRoot) {
160
156
  const pkg = readPackageJson(projectRoot);
161
157
  return pkg.lumera?.project_id;
162
158
  }
163
- function setProjectId(projectRoot, projectId) {
159
+ function setRegisteredProject(projectRoot, project) {
164
160
  const pkgPath = join2(projectRoot, "package.json");
165
161
  const raw = readFileSync2(pkgPath, "utf-8");
166
162
  const pkg = JSON.parse(raw);
167
163
  if (!pkg.lumera) pkg.lumera = {};
168
- pkg.lumera.project_id = projectId;
164
+ pkg.lumera.project_id = project.id;
165
+ pkg.lumera.name = project.name;
169
166
  writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
170
167
  }
171
168
  function getBaseUrl() {
@@ -195,10 +192,9 @@ export {
195
192
  findProjectRoot,
196
193
  readPackageJson,
197
194
  getAppName,
198
- getAppTitle,
199
195
  detectProjectVersion,
200
196
  getProjectId,
201
- setProjectId,
197
+ setRegisteredProject,
202
198
  getBaseUrl,
203
199
  getApiUrl
204
200
  };
@@ -2,7 +2,7 @@ import {
2
2
  getCredentials,
3
3
  getToken,
4
4
  init_auth
5
- } from "./chunk-JLVVHTBY.js";
5
+ } from "./chunk-MBTOP7VA.js";
6
6
  import {
7
7
  fetchWithRetry
8
8
  } from "./chunk-FJFIWC7G.js";
@@ -2,10 +2,10 @@ import {
2
2
  deps,
3
3
  syncDeps,
4
4
  syncResourceShareDeps
5
- } from "./chunk-Z7GPNMK5.js";
5
+ } from "./chunk-D35II5BI.js";
6
6
  import "./chunk-2CR762KB.js";
7
- import "./chunk-RV7WQAK3.js";
8
- import "./chunk-JLVVHTBY.js";
7
+ import "./chunk-YJJGLYF7.js";
8
+ import "./chunk-MBTOP7VA.js";
9
9
  import "./chunk-FJFIWC7G.js";
10
10
  import "./chunk-PNKVD2UK.js";
11
11
  export {
@@ -1,27 +1,26 @@
1
1
  import {
2
2
  dev
3
- } from "./chunk-LPKJZUG2.js";
3
+ } from "./chunk-FJIEHZWL.js";
4
4
  import "./chunk-UGAIWMQV.js";
5
5
  import "./chunk-CHUCWVFV.js";
6
6
  import "./chunk-PIY3XXJI.js";
7
7
  import "./chunk-KI57NBGA.js";
8
8
  import {
9
9
  syncDeps
10
- } from "./chunk-Z7GPNMK5.js";
10
+ } from "./chunk-D35II5BI.js";
11
11
  import {
12
12
  loadEnv
13
13
  } from "./chunk-2CR762KB.js";
14
14
  import {
15
15
  createApiClient
16
- } from "./chunk-RV7WQAK3.js";
16
+ } from "./chunk-YJJGLYF7.js";
17
17
  import {
18
18
  findProjectRoot,
19
19
  getApiUrl,
20
20
  getAppName,
21
- getAppTitle,
22
21
  getToken,
23
22
  init_auth
24
- } from "./chunk-JLVVHTBY.js";
23
+ } from "./chunk-MBTOP7VA.js";
25
24
  import "./chunk-FJFIWC7G.js";
26
25
  import "./chunk-PNKVD2UK.js";
27
26
 
@@ -126,7 +125,6 @@ async function dev2(args) {
126
125
  loadEnv(projectRoot);
127
126
  const token = getToken(projectRoot);
128
127
  const appName = getAppName(projectRoot);
129
- const appTitle = getAppTitle(projectRoot);
130
128
  const apiUrl = getApiUrl();
131
129
  await syncDeps(projectRoot, {
132
130
  write: true,
@@ -162,7 +160,6 @@ async function dev2(args) {
162
160
  await dev({
163
161
  token,
164
162
  appName,
165
- appTitle,
166
163
  port,
167
164
  host,
168
165
  appUrl,
package/dist/index.js CHANGED
@@ -243,32 +243,32 @@ async function main() {
243
243
  switch (command) {
244
244
  // Resource commands
245
245
  case "plan":
246
- await import("./resources-S7IGFX2W.js").then((m) => m.plan(args.slice(1)));
246
+ await import("./resources-E5JW74Z5.js").then((m) => m.plan(args.slice(1)));
247
247
  break;
248
248
  case "apply":
249
- await import("./resources-S7IGFX2W.js").then((m) => m.apply(args.slice(1)));
249
+ await import("./resources-E5JW74Z5.js").then((m) => m.apply(args.slice(1)));
250
250
  break;
251
251
  case "pull":
252
- await import("./resources-S7IGFX2W.js").then((m) => m.pull(args.slice(1)));
252
+ await import("./resources-E5JW74Z5.js").then((m) => m.pull(args.slice(1)));
253
253
  break;
254
254
  case "destroy":
255
- await import("./resources-S7IGFX2W.js").then((m) => m.destroy(args.slice(1)));
255
+ await import("./resources-E5JW74Z5.js").then((m) => m.destroy(args.slice(1)));
256
256
  break;
257
257
  case "list":
258
- await import("./resources-S7IGFX2W.js").then((m) => m.list(args.slice(1)));
258
+ await import("./resources-E5JW74Z5.js").then((m) => m.list(args.slice(1)));
259
259
  break;
260
260
  case "show":
261
- await import("./resources-S7IGFX2W.js").then((m) => m.show(args.slice(1)));
261
+ await import("./resources-E5JW74Z5.js").then((m) => m.show(args.slice(1)));
262
262
  break;
263
263
  case "diff":
264
- await import("./resources-S7IGFX2W.js").then((m) => m.diff(args.slice(1)));
264
+ await import("./resources-E5JW74Z5.js").then((m) => m.diff(args.slice(1)));
265
265
  break;
266
266
  // Development
267
267
  case "dev":
268
- await import("./dev-PDBCCR3L.js").then((m) => m.dev(args.slice(1)));
268
+ await import("./dev-UH3RUSL5.js").then((m) => m.dev(args.slice(1)));
269
269
  break;
270
270
  case "run":
271
- await import("./run-TMJPC4PK.js").then((m) => m.run(args.slice(1)));
271
+ await import("./run-BDHWM3WZ.js").then((m) => m.run(args.slice(1)));
272
272
  break;
273
273
  case "functions":
274
274
  await import("./functions-C5SGYMZK.js").then(
@@ -282,27 +282,27 @@ async function main() {
282
282
  break;
283
283
  // Project
284
284
  case "init":
285
- await import("./init-LZAXKILN.js").then((m) => m.init(args.slice(1)));
285
+ await import("./init-PAQ33ZFU.js").then((m) => m.init(args.slice(1)));
286
286
  break;
287
287
  case "register":
288
- await import("./register-MOO266NL.js").then((m) => m.register(args.slice(1)));
288
+ await import("./register-A4RZWHWF.js").then((m) => m.register(args.slice(1)));
289
289
  break;
290
290
  case "templates":
291
291
  await import("./templates-LNUOTNLN.js").then((m) => m.templates(subcommand, args.slice(2)));
292
292
  break;
293
293
  case "status":
294
- await import("./status-NPNIY3QP.js").then((m) => m.status(args.slice(1)));
294
+ await import("./status-T2QGWEU6.js").then((m) => m.status(args.slice(1)));
295
295
  break;
296
296
  case "migrate":
297
- await import("./migrate-NWTNC2V3.js").then((m) => m.migrate(args.slice(1)));
297
+ await import("./migrate-H47N34N5.js").then((m) => m.migrate(args.slice(1)));
298
298
  break;
299
299
  // Skills
300
300
  case "skills":
301
- await import("./skills-LWOZFY5X.js").then((m) => m.skills(subcommand, args.slice(2)));
301
+ await import("./skills-GZYWPF2I.js").then((m) => m.skills(subcommand, args.slice(2)));
302
302
  break;
303
303
  // Dependencies
304
304
  case "deps":
305
- await import("./deps-SIKW7IW2.js").then((m) => m.deps(args.slice(1)));
305
+ await import("./deps-YRGLEW2Y.js").then((m) => m.deps(args.slice(1)));
306
306
  break;
307
307
  // Feature flags (read the in-sandbox snapshot)
308
308
  case "flags":
@@ -310,13 +310,13 @@ async function main() {
310
310
  break;
311
311
  // Auth
312
312
  case "login":
313
- await import("./auth-MJO5DFOF.js").then((m) => m.login(args.slice(1)));
313
+ await import("./auth-LQTZKGIV.js").then((m) => m.login(args.slice(1)));
314
314
  break;
315
315
  case "logout":
316
- await import("./auth-MJO5DFOF.js").then((m) => m.logout(args.slice(1)));
316
+ await import("./auth-LQTZKGIV.js").then((m) => m.logout(args.slice(1)));
317
317
  break;
318
318
  case "whoami":
319
- await import("./auth-MJO5DFOF.js").then((m) => m.whoami());
319
+ await import("./auth-LQTZKGIV.js").then((m) => m.whoami());
320
320
  break;
321
321
  // Convenience aliases
322
322
  case "help":
@@ -8,16 +8,17 @@ import {
8
8
  import {
9
9
  installAllSkills,
10
10
  syncSkillDescriptions
11
- } from "./chunk-XT3G4EDM.js";
11
+ } from "./chunk-GRQGEL75.js";
12
12
  import {
13
13
  createApiClient,
14
14
  validateProjectNameLength
15
- } from "./chunk-RV7WQAK3.js";
15
+ } from "./chunk-YJJGLYF7.js";
16
16
  import {
17
17
  getToken,
18
18
  init_auth,
19
- setProjectId
20
- } from "./chunk-JLVVHTBY.js";
19
+ readPackageJson,
20
+ setRegisteredProject
21
+ } from "./chunk-MBTOP7VA.js";
21
22
  import "./chunk-FJFIWC7G.js";
22
23
  import "./chunk-PNKVD2UK.js";
23
24
 
@@ -362,6 +363,13 @@ async function init(args) {
362
363
  [sourceTitle, projectTitle]
363
364
  ];
364
365
  copyDir(templateDir, targetDir, replacements);
366
+ if (existsSync(join(targetDir, "package.json"))) {
367
+ const generatedPackage = readPackageJson(targetDir);
368
+ if (generatedPackage.lumera?.name !== void 0) {
369
+ delete generatedPackage.lumera.name;
370
+ writeFileSync(join(targetDir, "package.json"), JSON.stringify(generatedPackage, null, 2) + "\n");
371
+ }
372
+ }
365
373
  const installCommand = existsSync(join(targetDir, "pnpm-lock.yaml")) ? "pnpm install --frozen-lockfile" : "pnpm install";
366
374
  const hasPythonProject = existsSync(join(targetDir, "pyproject.toml"));
367
375
  function listFiles(dir, prefix = "") {
@@ -447,7 +455,7 @@ async function init(args) {
447
455
  const stopRegister = spinner("Registering project on Lumera...");
448
456
  try {
449
457
  const project = await api.registerProject(finalProjectName);
450
- setProjectId(targetDir, project.id);
458
+ setRegisteredProject(targetDir, project);
451
459
  stopRegister(pc.green("\u2713") + pc.dim(` Project registered (${project.id})`));
452
460
  registered = true;
453
461
  } catch (e) {
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  detectProjectVersion,
10
10
  findProjectRoot
11
- } from "./chunk-JLVVHTBY.js";
11
+ } from "./chunk-MBTOP7VA.js";
12
12
  import "./chunk-PNKVD2UK.js";
13
13
 
14
14
  // src/commands/migrate.ts
@@ -7,13 +7,13 @@ import {
7
7
  import {
8
8
  createApiClient,
9
9
  validateProjectExternalId
10
- } from "./chunk-RV7WQAK3.js";
10
+ } from "./chunk-YJJGLYF7.js";
11
11
  import {
12
12
  findProjectRoot,
13
13
  getAppName,
14
14
  getProjectId,
15
- setProjectId
16
- } from "./chunk-JLVVHTBY.js";
15
+ setRegisteredProject
16
+ } from "./chunk-MBTOP7VA.js";
17
17
  import "./chunk-FJFIWC7G.js";
18
18
  import "./chunk-PNKVD2UK.js";
19
19
 
@@ -26,7 +26,7 @@ ${pc.dim("Usage:")}
26
26
 
27
27
  ${pc.dim("Description:")}
28
28
  Register (or re-link) the current project with the Lumera platform.
29
- Creates an lm_projects record and writes the project ID to package.json.
29
+ Creates an lm_projects record and writes its ID and current display name to package.json.
30
30
 
31
31
  ${pc.dim("Options:")}
32
32
  --force, -f Re-register even if project_id already exists in package.json
@@ -64,7 +64,7 @@ async function register(args) {
64
64
  const stop = spinner(`Registering "${appName}" on Lumera...`);
65
65
  try {
66
66
  const project = await api.registerProject(appName);
67
- setProjectId(projectRoot, project.id);
67
+ setRegisteredProject(projectRoot, project);
68
68
  stop(pc.green("\u2713") + ` Project registered: ${pc.dim(project.id)}`);
69
69
  } catch (e) {
70
70
  stop(pc.red("\u2717") + ` Failed to register project`);
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  deploy,
3
3
  deployAppAPI
4
- } from "./chunk-LPKJZUG2.js";
4
+ } from "./chunk-FJIEHZWL.js";
5
5
  import "./chunk-UGAIWMQV.js";
6
6
  import "./chunk-CHUCWVFV.js";
7
7
  import "./chunk-PIY3XXJI.js";
8
8
  import "./chunk-KI57NBGA.js";
9
9
  import {
10
10
  syncDeps
11
- } from "./chunk-Z7GPNMK5.js";
11
+ } from "./chunk-D35II5BI.js";
12
12
  import {
13
13
  loadEnv
14
14
  } from "./chunk-2CR762KB.js";
@@ -17,16 +17,15 @@ import {
17
17
  createApiClient,
18
18
  isApiErrorStatus,
19
19
  validateCollectionNameLength
20
- } from "./chunk-RV7WQAK3.js";
20
+ } from "./chunk-YJJGLYF7.js";
21
21
  import {
22
22
  findProjectRoot,
23
23
  getApiUrl,
24
24
  getAppName,
25
- getAppTitle,
26
25
  getProjectId,
27
26
  getToken,
28
27
  init_auth
29
- } from "./chunk-JLVVHTBY.js";
28
+ } from "./chunk-MBTOP7VA.js";
30
29
  import {
31
30
  fetchWithRetry
32
31
  } from "./chunk-FJFIWC7G.js";
@@ -2806,7 +2805,6 @@ async function applyApp(args) {
2806
2805
  loadEnv(projectRoot);
2807
2806
  const token = getToken(projectRoot);
2808
2807
  const appName = getAppName(projectRoot);
2809
- const appTitle = getAppTitle(projectRoot);
2810
2808
  const apiUrl = getApiUrl();
2811
2809
  if (!skipBuild) {
2812
2810
  console.log(pc2.dim(" Building..."));
@@ -2832,7 +2830,7 @@ async function applyApp(args) {
2832
2830
  `Invalid --access-level "${accessLevel}". Valid values: ${validAccessLevels.join(", ")}`
2833
2831
  );
2834
2832
  }
2835
- await deploy({ token, appName, appTitle, distDir, apiUrl, accessLevel, skipAppAPI, reuseFrontend });
2833
+ await deploy({ token, appName, distDir, apiUrl, accessLevel, skipAppAPI, reuseFrontend });
2836
2834
  }
2837
2835
  async function applyAppAPI(projectRoot) {
2838
2836
  await deployAppAPI({
@@ -4257,13 +4255,15 @@ async function destroyApp(skipConfirm) {
4257
4255
  console.log(pc2.yellow(` App "${appName}" not found in Lumera.`));
4258
4256
  return;
4259
4257
  }
4260
- console.log(pc2.dim(` App to delete: ${appRecord.name} (${appRecord.external_id})`));
4258
+ const project = await createApiClient(token, void 0, appName).getProjectByExternalId(appName).catch(() => null);
4259
+ const projectName = project?.name || appRecord.external_id;
4260
+ console.log(pc2.dim(` App to delete: ${projectName} (${appRecord.external_id})`));
4261
4261
  console.log();
4262
4262
  if (!skipConfirm) {
4263
4263
  const { confirmed } = await prompts({
4264
4264
  type: "confirm",
4265
4265
  name: "confirmed",
4266
- message: `Delete app "${appRecord.name}" from Lumera?`,
4266
+ message: `Delete app "${projectName}" from Lumera?`,
4267
4267
  initial: false
4268
4268
  });
4269
4269
  if (!confirmed) {
@@ -4284,7 +4284,7 @@ async function destroyApp(skipConfirm) {
4284
4284
  if (!deleteRes.ok) {
4285
4285
  throw new Error(`Failed to delete app: ${await deleteRes.text()}`);
4286
4286
  }
4287
- console.log(pc2.green(" \u2713"), `App "${appRecord.name}" deleted from Lumera.`);
4287
+ console.log(pc2.green(" \u2713"), `App "${projectName}" deleted from Lumera.`);
4288
4288
  }
4289
4289
  async function showResource(api, platformDir, resourceType, resourceName, appName, projectId) {
4290
4290
  if (resourceType === "views") {
@@ -4493,10 +4493,10 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
4493
4493
  loadEnv(projectRoot);
4494
4494
  const token = getToken(projectRoot);
4495
4495
  const appName2 = getAppName(projectRoot);
4496
- const appTitle = getAppTitle(projectRoot);
4496
+ const project = await api.getProjectByExternalId(appName2).catch(() => null);
4497
4497
  let apiUrl = getApiUrl().replace(/\/+$/, "").replace(/\/api$/, "");
4498
4498
  console.log();
4499
- console.log(pc2.bold(` App: ${appTitle || appName2}`));
4499
+ console.log(pc2.bold(` App: ${project?.name || appName2}`));
4500
4500
  console.log();
4501
4501
  console.log(` External ID: ${appName2}`);
4502
4502
  const filterParam = encodeURIComponent(JSON.stringify({ external_id: appName2 }));
@@ -3,14 +3,14 @@ import {
3
3
  } from "./chunk-2CR762KB.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-RV7WQAK3.js";
6
+ } from "./chunk-YJJGLYF7.js";
7
7
  import {
8
8
  findProjectRoot,
9
9
  getApiUrl,
10
10
  getAppName,
11
11
  getToken,
12
12
  init_auth
13
- } from "./chunk-JLVVHTBY.js";
13
+ } from "./chunk-MBTOP7VA.js";
14
14
  import {
15
15
  fetchWithRetry
16
16
  } from "./chunk-FJFIWC7G.js";
@@ -8,8 +8,8 @@ import {
8
8
  slugToDirName,
9
9
  slugToFilename,
10
10
  syncSkillDescriptions
11
- } from "./chunk-XT3G4EDM.js";
12
- import "./chunk-JLVVHTBY.js";
11
+ } from "./chunk-GRQGEL75.js";
12
+ import "./chunk-MBTOP7VA.js";
13
13
  import "./chunk-FJFIWC7G.js";
14
14
  import "./chunk-PNKVD2UK.js";
15
15
 
@@ -1,14 +1,16 @@
1
+ import {
2
+ createApiClient
3
+ } from "./chunk-YJJGLYF7.js";
1
4
  import {
2
5
  detectProjectVersion,
3
6
  findProjectRoot,
4
7
  getAppName,
5
- getAppTitle,
6
8
  getBaseUrl,
7
9
  getToken,
8
10
  getTokenSource,
9
11
  init_auth,
10
12
  readPackageJson
11
- } from "./chunk-JLVVHTBY.js";
13
+ } from "./chunk-MBTOP7VA.js";
12
14
  import {
13
15
  fetchWithRetry
14
16
  } from "./chunk-FJFIWC7G.js";
@@ -67,7 +69,6 @@ ${pc.dim("Description:")}
67
69
  const pkg = readPackageJson(projectRoot);
68
70
  const version = detectProjectVersion(projectRoot);
69
71
  const appName = getAppName(projectRoot);
70
- const appTitle = getAppTitle(projectRoot);
71
72
  const tokenSource = getTokenSource(projectRoot);
72
73
  let userEmail;
73
74
  let companyName;
@@ -91,7 +92,8 @@ ${pc.dim("Description:")}
91
92
  console.log(pc.bold(` ${identity}`));
92
93
  console.log();
93
94
  }
94
- console.log(pc.bold(`Project: ${appTitle}`));
95
+ const project = tokenValid && token ? await createApiClient(token, void 0, appName).getProjectByExternalId(appName).catch(() => null) : null;
96
+ console.log(pc.bold(`Project: ${project?.name || appName}`));
95
97
  console.log(pc.dim(` Name: ${appName}`));
96
98
  console.log(pc.dim(` Version: ${pkg.version || "0.0.0"}`));
97
99
  console.log(pc.dim(` Structure: v${version}${version === 0 ? " (legacy)" : ""}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.36.5",
3
+ "version": "0.37.0",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {