@lumerahq/cli 0.18.13 → 0.18.14

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.
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2CR762KB.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-IBE6ACPE.js";
6
+ } from "./chunk-PVR5DD4G.js";
7
7
  import {
8
8
  findProjectRoot,
9
9
  getAppName
@@ -184,6 +184,17 @@ var ApiClient = class {
184
184
  method: "DELETE"
185
185
  });
186
186
  }
187
+ // Mailboxes
188
+ async listMailboxes() {
189
+ const result = await this.request("/api/mailboxes");
190
+ return result.mailboxes || [];
191
+ }
192
+ async createMailbox(def) {
193
+ return this.request("/api/mailboxes", {
194
+ method: "POST",
195
+ body: JSON.stringify(def)
196
+ });
197
+ }
187
198
  // Agents
188
199
  async listAgents(opts) {
189
200
  const params = new URLSearchParams({ limit: "100" });
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  deps,
3
3
  syncDeps
4
- } from "./chunk-MVUNFAVL.js";
4
+ } from "./chunk-PMRN3STN.js";
5
5
  import "./chunk-2CR762KB.js";
6
- import "./chunk-IBE6ACPE.js";
6
+ import "./chunk-PVR5DD4G.js";
7
7
  import "./chunk-ZH3NVYEQ.js";
8
8
  import "./chunk-FJFIWC7G.js";
9
9
  import "./chunk-PNKVD2UK.js";
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-2CR762KB.js";
7
7
  import {
8
8
  createApiClient
9
- } from "./chunk-IBE6ACPE.js";
9
+ } from "./chunk-PVR5DD4G.js";
10
10
  import {
11
11
  findProjectRoot,
12
12
  getApiUrl,
package/dist/index.js CHANGED
@@ -219,39 +219,39 @@ async function main() {
219
219
  switch (command) {
220
220
  // Resource commands
221
221
  case "plan":
222
- await import("./resources-KPRAWMQN.js").then((m) => m.plan(args.slice(1)));
222
+ await import("./resources-4F2PYBBR.js").then((m) => m.plan(args.slice(1)));
223
223
  break;
224
224
  case "apply":
225
- await import("./resources-KPRAWMQN.js").then((m) => m.apply(args.slice(1)));
225
+ await import("./resources-4F2PYBBR.js").then((m) => m.apply(args.slice(1)));
226
226
  break;
227
227
  case "pull":
228
- await import("./resources-KPRAWMQN.js").then((m) => m.pull(args.slice(1)));
228
+ await import("./resources-4F2PYBBR.js").then((m) => m.pull(args.slice(1)));
229
229
  break;
230
230
  case "destroy":
231
- await import("./resources-KPRAWMQN.js").then((m) => m.destroy(args.slice(1)));
231
+ await import("./resources-4F2PYBBR.js").then((m) => m.destroy(args.slice(1)));
232
232
  break;
233
233
  case "list":
234
- await import("./resources-KPRAWMQN.js").then((m) => m.list(args.slice(1)));
234
+ await import("./resources-4F2PYBBR.js").then((m) => m.list(args.slice(1)));
235
235
  break;
236
236
  case "show":
237
- await import("./resources-KPRAWMQN.js").then((m) => m.show(args.slice(1)));
237
+ await import("./resources-4F2PYBBR.js").then((m) => m.show(args.slice(1)));
238
238
  break;
239
239
  case "diff":
240
- await import("./resources-KPRAWMQN.js").then((m) => m.diff(args.slice(1)));
240
+ await import("./resources-4F2PYBBR.js").then((m) => m.diff(args.slice(1)));
241
241
  break;
242
242
  // Development
243
243
  case "dev":
244
- await import("./dev-R2CZ7IYR.js").then((m) => m.dev(args.slice(1)));
244
+ await import("./dev-FT5OXF5S.js").then((m) => m.dev(args.slice(1)));
245
245
  break;
246
246
  case "run":
247
- await import("./run-JZX3E4UX.js").then((m) => m.run(args.slice(1)));
247
+ await import("./run-6KT4I7J7.js").then((m) => m.run(args.slice(1)));
248
248
  break;
249
249
  // Project
250
250
  case "init":
251
- await import("./init-F4UYW6LD.js").then((m) => m.init(args.slice(1)));
251
+ await import("./init-2T6GQ7HJ.js").then((m) => m.init(args.slice(1)));
252
252
  break;
253
253
  case "register":
254
- await import("./register-QPUXYBZ7.js").then((m) => m.register(args.slice(1)));
254
+ await import("./register-F23I2BE3.js").then((m) => m.register(args.slice(1)));
255
255
  break;
256
256
  case "templates":
257
257
  await import("./templates-M3RDNDDY.js").then((m) => m.templates(subcommand, args.slice(2)));
@@ -268,7 +268,7 @@ async function main() {
268
268
  break;
269
269
  // Dependencies
270
270
  case "deps":
271
- await import("./deps-AVTV7FP3.js").then((m) => m.deps(args.slice(1)));
271
+ await import("./deps-JVAT5WIB.js").then((m) => m.deps(args.slice(1)));
272
272
  break;
273
273
  // Auth
274
274
  case "login":
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-BHYDYR75.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-IBE6ACPE.js";
10
+ } from "./chunk-PVR5DD4G.js";
11
11
  import {
12
12
  getToken,
13
13
  init_auth,
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-BHYDYR75.js";
7
7
  import {
8
8
  createApiClient
9
- } from "./chunk-IBE6ACPE.js";
9
+ } from "./chunk-PVR5DD4G.js";
10
10
  import {
11
11
  findProjectRoot,
12
12
  getAppName,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  syncDeps
3
- } from "./chunk-MVUNFAVL.js";
3
+ } from "./chunk-PMRN3STN.js";
4
4
  import {
5
5
  deploy
6
6
  } from "./chunk-SU26C4GL.js";
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-2CR762KB.js";
10
10
  import {
11
11
  createApiClient
12
- } from "./chunk-IBE6ACPE.js";
12
+ } from "./chunk-PVR5DD4G.js";
13
13
  import {
14
14
  findProjectRoot,
15
15
  getApiUrl,
@@ -282,12 +282,15 @@ ${pc.dim("Resources:")}
282
282
  hooks Plan only hooks
283
283
  agents Plan only agents
284
284
  agents/<name> Plan single agent
285
+ mailboxes Plan only mailboxes
286
+ mailboxes/<slug> Plan single mailbox
285
287
  app Plan app deployment
286
288
 
287
289
  ${pc.dim("Examples:")}
288
290
  lumera plan # Plan all resources
289
291
  lumera plan collections # Plan only collections
290
292
  lumera plan automations/sync # Plan single automation
293
+ lumera plan mailboxes # Plan mailbox configs
291
294
  `);
292
295
  }
293
296
  function showApplyHelp() {
@@ -307,6 +310,8 @@ ${pc.dim("Resources:")}
307
310
  hooks Apply only hooks
308
311
  agents Apply only agents
309
312
  agents/<name> Apply single agent
313
+ mailboxes Apply only mailboxes
314
+ mailboxes/<slug> Apply single mailbox
310
315
  app Deploy the frontend app
311
316
 
312
317
  ${pc.dim("Options:")}
@@ -344,6 +349,8 @@ ${pc.dim("Resources:")}
344
349
  hooks Pull only hooks
345
350
  agents Pull only agents
346
351
  agents/<name> Pull single agent
352
+ mailboxes Pull only mailboxes
353
+ mailboxes/<slug> Pull single mailbox
347
354
 
348
355
  ${pc.dim("Examples:")}
349
356
  lumera pull # Pull all (safe \u2014 warns on conflicts)
@@ -395,6 +402,7 @@ ${pc.dim("Types:")}
395
402
  automations List only automations
396
403
  hooks List only hooks
397
404
  agents List only agents
405
+ mailboxes List only mailboxes
398
406
 
399
407
  ${pc.dim("Options:")}
400
408
  --all Include remote-only resources
@@ -418,6 +426,7 @@ ${pc.dim("Resources:")}
418
426
  automations/<name> Show automation details
419
427
  hooks/<name> Show hook details
420
428
  agents/<name> Show agent details
429
+ mailboxes/<slug> Show mailbox details
421
430
  app Show app details
422
431
 
423
432
  ${pc.dim("Examples:")}
@@ -433,7 +442,7 @@ function parseResource(resourcePath) {
433
442
  const parts = resourcePath.split("/");
434
443
  const type = parts[0];
435
444
  const name = parts.slice(1).join("/") || null;
436
- const validTypes = ["collections", "automations", "hooks", "agents", "app"];
445
+ const validTypes = ["collections", "automations", "hooks", "agents", "mailboxes", "app"];
437
446
  if (!validTypes.includes(type)) {
438
447
  console.log(pc.red(` Unknown resource type "${type}". Valid types: ${validTypes.join(", ")}`));
439
448
  process.exit(1);
@@ -898,6 +907,122 @@ async function planAutomations(api, localAutomations) {
898
907
  }
899
908
  return changes;
900
909
  }
910
+ function loadLocalMailboxes(platformDir, filterName) {
911
+ const mailboxesDir = join(platformDir, "mailboxes");
912
+ if (!existsSync(mailboxesDir)) {
913
+ return [];
914
+ }
915
+ const mailboxes = [];
916
+ for (const file of readdirSync(mailboxesDir)) {
917
+ if (!file.endsWith(".json")) continue;
918
+ const slug = file.replace(/\.json$/, "").trim();
919
+ if (!slug) {
920
+ console.log(pc.yellow(` \u26A0 Skipping ${file}: empty filename`));
921
+ continue;
922
+ }
923
+ if (filterName && slug !== filterName) {
924
+ continue;
925
+ }
926
+ const filePath = join(mailboxesDir, file);
927
+ let raw;
928
+ try {
929
+ raw = JSON.parse(readFileSync(filePath, "utf-8"));
930
+ } catch (e) {
931
+ console.log(pc.yellow(` \u26A0 Skipping ${file}: invalid JSON (${e.message})`));
932
+ continue;
933
+ }
934
+ if (!isPlainObject(raw)) {
935
+ console.log(pc.yellow(` \u26A0 Skipping ${file}: top-level value must be an object`));
936
+ continue;
937
+ }
938
+ const descriptionRaw = raw["description"];
939
+ const description = typeof descriptionRaw === "string" ? descriptionRaw : void 0;
940
+ mailboxes.push({ slug, description });
941
+ }
942
+ return mailboxes;
943
+ }
944
+ async function planMailboxes(api, localMailboxes) {
945
+ const changes = [];
946
+ if (localMailboxes.length === 0) return changes;
947
+ const remote = await api.listMailboxes();
948
+ const remoteBySlug = new Map(remote.map((m) => [m.slug, m]));
949
+ for (const mb of localMailboxes) {
950
+ const existing = remoteBySlug.get(mb.slug);
951
+ if (!existing) {
952
+ changes.push({
953
+ type: "create",
954
+ resource: "mailbox",
955
+ id: mb.slug,
956
+ name: mb.slug,
957
+ details: mb.description ? `description: ${mb.description}` : void 0
958
+ });
959
+ continue;
960
+ }
961
+ const localDesc = (mb.description ?? "").trim();
962
+ const remoteDesc = (existing.description ?? "").trim();
963
+ if (localDesc !== remoteDesc) {
964
+ changes.push({
965
+ type: "update",
966
+ resource: "mailbox",
967
+ id: existing.id,
968
+ name: mb.slug,
969
+ details: "description differs (read-only remotely; update in UI or DB)"
970
+ });
971
+ }
972
+ }
973
+ return changes;
974
+ }
975
+ async function applyMailboxes(api, localMailboxes) {
976
+ if (localMailboxes.length === 0) return 0;
977
+ let errors = 0;
978
+ const remote = await api.listMailboxes();
979
+ const remoteBySlug = new Map(remote.map((m) => [m.slug, m]));
980
+ for (const mb of localMailboxes) {
981
+ const existing = remoteBySlug.get(mb.slug);
982
+ if (existing) {
983
+ const localDesc = (mb.description ?? "").trim();
984
+ const remoteDesc = (existing.description ?? "").trim();
985
+ if (localDesc !== remoteDesc) {
986
+ console.log(pc.yellow(" \u26A0"), `mailbox ${mb.slug}: description drift not reconcilable by CLI (no PATCH endpoint yet) \u2014 update in the UI or DB`);
987
+ } else {
988
+ console.log(pc.green(" \u2713"), pc.dim(`mailbox ${mb.slug} already exists (${existing.email})`));
989
+ }
990
+ continue;
991
+ }
992
+ try {
993
+ const created = await api.createMailbox({ slug: mb.slug, description: mb.description });
994
+ console.log(pc.green(" \u2713"), `created mailbox ${mb.slug} ${pc.dim(`\u2192 ${created.email}`)}`);
995
+ } catch (e) {
996
+ console.log(pc.red(" \u2717"), `mailbox ${mb.slug}: ${e.message}`);
997
+ errors++;
998
+ }
999
+ }
1000
+ return errors;
1001
+ }
1002
+ async function pullMailboxes(api, platformDir, filterName) {
1003
+ const mailboxes = await api.listMailboxes();
1004
+ if (mailboxes.length === 0) {
1005
+ console.log(pc.dim(" (no mailboxes)"));
1006
+ return;
1007
+ }
1008
+ const outDir = join(platformDir, "mailboxes");
1009
+ if (!existsSync(outDir)) {
1010
+ mkdirSync(outDir, { recursive: true });
1011
+ }
1012
+ let count = 0;
1013
+ for (const mb of mailboxes) {
1014
+ if (filterName && mb.slug !== filterName) continue;
1015
+ const body = {};
1016
+ if (mb.description) body.description = mb.description;
1017
+ const file = join(outDir, `${toSafeFilename(mb.slug)}.json`);
1018
+ writeFileSync(file, JSON.stringify(body, null, 2) + "\n");
1019
+ console.log(pc.green(" \u2713"), `pulled mailbox ${mb.slug} ${pc.dim(`\u2192 ${file}`)}`);
1020
+ count++;
1021
+ }
1022
+ if (count === 0 && filterName) {
1023
+ console.log(pc.yellow(` \u26A0 mailbox '${filterName}' not found on remote`));
1024
+ }
1025
+ }
901
1026
  async function planHooks(api, localHooks, collections) {
902
1027
  const changes = [];
903
1028
  const remoteHooks = await api.listHooks();
@@ -1663,6 +1788,30 @@ async function listResources(api, platformDir, filterType, appName, projectId) {
1663
1788
  }
1664
1789
  }
1665
1790
  }
1791
+ if (!filterType || filterType === "mailboxes") {
1792
+ const localMailboxes = loadLocalMailboxes(platformDir);
1793
+ const remoteMailboxes = await api.listMailboxes();
1794
+ const remoteBySlug = new Map(remoteMailboxes.map((m) => [m.slug, m]));
1795
+ const localSlugs = new Set(localMailboxes.map((m) => m.slug));
1796
+ for (const mb of localMailboxes) {
1797
+ const remote = remoteBySlug.get(mb.slug);
1798
+ if (!remote) {
1799
+ results.push({ name: mb.slug, type: "mailboxes", status: "local-only" });
1800
+ continue;
1801
+ }
1802
+ const descChanged = (mb.description ?? "").trim() !== (remote.description ?? "").trim();
1803
+ if (descChanged) {
1804
+ results.push({ name: mb.slug, type: "mailboxes", status: "changed", details: "description" });
1805
+ } else {
1806
+ results.push({ name: mb.slug, type: "mailboxes", status: "synced" });
1807
+ }
1808
+ }
1809
+ for (const remote of remoteMailboxes) {
1810
+ if (!localSlugs.has(remote.slug)) {
1811
+ results.push({ name: remote.slug, type: "mailboxes", status: "remote-only" });
1812
+ }
1813
+ }
1814
+ }
1666
1815
  return results;
1667
1816
  }
1668
1817
  function planCollectionDelete(collections, platformDir) {
@@ -2082,6 +2231,31 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2082
2231
  console.log(` MCP Servers: ${mcps.join(", ")}`);
2083
2232
  }
2084
2233
  console.log();
2234
+ } else if (resourceType === "mailboxes") {
2235
+ const localMailboxes = loadLocalMailboxes(platformDir, resourceName);
2236
+ const remoteMailboxes = await api.listMailboxes();
2237
+ const local = localMailboxes[0];
2238
+ const remote = remoteMailboxes.find((m) => m.slug === resourceName);
2239
+ if (!local && !remote) {
2240
+ console.log(pc.red(` Mailbox "${resourceName}" not found`));
2241
+ process.exit(1);
2242
+ }
2243
+ console.log();
2244
+ console.log(pc.bold(` Mailbox: ${local?.slug || remote?.slug}`));
2245
+ console.log();
2246
+ if (local && remote) {
2247
+ const descChanged = (local.description ?? "").trim() !== (remote.description ?? "").trim();
2248
+ console.log(` Status: ${descChanged ? pc.yellow("changed") : pc.green("synced")}`);
2249
+ } else if (local) {
2250
+ console.log(` Status: ${pc.yellow("local only")}`);
2251
+ } else {
2252
+ console.log(` Status: ${pc.cyan("remote only")}`);
2253
+ }
2254
+ if (remote?.email) console.log(` Email: ${remote.email}`);
2255
+ const desc = local?.description || remote?.description;
2256
+ if (desc) console.log(` Description: ${desc}`);
2257
+ if (remote?.project_id) console.log(` Project: ${remote.project_id}`);
2258
+ console.log();
2085
2259
  } else if (resourceType === "app") {
2086
2260
  const projectRoot = findProjectRoot();
2087
2261
  loadEnv(projectRoot);
@@ -2173,6 +2347,13 @@ async function plan(args) {
2173
2347
  allChanges.push(...changes);
2174
2348
  }
2175
2349
  }
2350
+ if (!type || type === "mailboxes") {
2351
+ const localMailboxes = loadLocalMailboxes(platformDir, name || void 0);
2352
+ if (localMailboxes.length > 0) {
2353
+ const changes = await planMailboxes(api, localMailboxes);
2354
+ allChanges.push(...changes);
2355
+ }
2356
+ }
2176
2357
  if (allChanges.length === 0) {
2177
2358
  if (jsonOutput) {
2178
2359
  console.log(JSON.stringify({ changes: [], warnings: [] }));
@@ -2274,12 +2455,14 @@ async function apply(args) {
2274
2455
  const localAutomations = !type || type === "automations" ? loadLocalAutomations(platformDir, name || void 0, appName) : [];
2275
2456
  const localHooks = !type || type === "hooks" ? loadLocalHooks(platformDir, name || void 0, appName) : [];
2276
2457
  const localAgents = !type || type === "agents" ? loadLocalAgents(platformDir, name || void 0, appName) : [];
2458
+ const localMailboxes = !type || type === "mailboxes" ? loadLocalMailboxes(platformDir, name || void 0) : [];
2277
2459
  if (localCollections.length > 0) allChanges.push(...await planCollections(api, localCollections));
2278
2460
  if (localAutomations.length > 0) allChanges.push(...await planAutomations(api, localAutomations));
2279
2461
  if (localHooks.length > 0) allChanges.push(...await planHooks(api, localHooks, collections));
2280
2462
  if (localAgents.length > 0) allChanges.push(...await planAgents(api, localAgents, projectId));
2463
+ if (localMailboxes.length > 0) allChanges.push(...await planMailboxes(api, localMailboxes));
2281
2464
  if (name) {
2282
- const hasLocal = localCollections.length > 0 || localAutomations.length > 0 || localHooks.length > 0 || localAgents.length > 0;
2465
+ const hasLocal = localCollections.length > 0 || localAutomations.length > 0 || localHooks.length > 0 || localAgents.length > 0 || localMailboxes.length > 0;
2283
2466
  if (!hasLocal) {
2284
2467
  console.log();
2285
2468
  console.log(pc.red(` Resource "${name}" not found locally`));
@@ -2370,6 +2553,11 @@ async function apply(args) {
2370
2553
  totalErrors += await applyAgents(api, localAgents, projectId);
2371
2554
  console.log();
2372
2555
  }
2556
+ if (localMailboxes.length > 0) {
2557
+ console.log(pc.bold(" Mailboxes:"));
2558
+ totalErrors += await applyMailboxes(api, localMailboxes);
2559
+ console.log();
2560
+ }
2373
2561
  if (willDeployApp) {
2374
2562
  console.log(pc.bold(" App:"));
2375
2563
  await applyApp(args);
@@ -2476,6 +2664,11 @@ async function pull(args) {
2476
2664
  await pullAgents(api, platformDir, name || void 0, projectId);
2477
2665
  console.log();
2478
2666
  }
2667
+ if (!type || type === "mailboxes") {
2668
+ console.log(pc.bold(" Mailboxes:"));
2669
+ await pullMailboxes(api, platformDir, name || void 0);
2670
+ console.log();
2671
+ }
2479
2672
  console.log(pc.green(" Done!"));
2480
2673
  console.log();
2481
2674
  }
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2CR762KB.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-IBE6ACPE.js";
6
+ } from "./chunk-PVR5DD4G.js";
7
7
  import {
8
8
  findProjectRoot,
9
9
  getApiUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.18.13",
3
+ "version": "0.18.14",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {