@lumerahq/cli 0.18.13 → 0.19.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.
@@ -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" });
@@ -208,11 +219,6 @@ var ApiClient = class {
208
219
  method: "DELETE"
209
220
  });
210
221
  }
211
- // MCP Servers
212
- async listMCPServers() {
213
- const result = await this.request("/api/mcp-servers");
214
- return result.servers || [];
215
- }
216
222
  // Agent Skills (for slug-to-ID resolution)
217
223
  async listAgentSkills() {
218
224
  const result = await this.request("/api/lm_agent_skills?limit=100");
@@ -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-GKI2HQJC.js";
7
7
  import {
8
8
  findProjectRoot,
9
9
  getAppName
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  deps,
3
3
  syncDeps
4
- } from "./chunk-MVUNFAVL.js";
4
+ } from "./chunk-XDTWVFPE.js";
5
5
  import "./chunk-2CR762KB.js";
6
- import "./chunk-IBE6ACPE.js";
6
+ import "./chunk-GKI2HQJC.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-GKI2HQJC.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-MKCLJUH3.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-MKCLJUH3.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-MKCLJUH3.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-MKCLJUH3.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-MKCLJUH3.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-MKCLJUH3.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-MKCLJUH3.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-EVREBGR6.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-5ZOSPBGO.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-37XOMJLU.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-JJUMS4FI.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-ULTIIDYK.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-GKI2HQJC.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-GKI2HQJC.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-XDTWVFPE.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-GKI2HQJC.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();
@@ -1308,6 +1433,13 @@ function loadLocalAgents(platformDir, filterName, appName) {
1308
1433
  if (filterName && config.external_id !== filterName && config.name !== filterName && entry.name !== filterName) {
1309
1434
  continue;
1310
1435
  }
1436
+ if (config.mcp_servers !== void 0) {
1437
+ console.log(
1438
+ pc.yellow(
1439
+ ` \u26A0 ${entry.name}: "mcp_servers" is deprecated and ignored \u2014 MCP servers are now scoped per project in Platform \u2192 Integrations.`
1440
+ )
1441
+ );
1442
+ }
1311
1443
  if (!config.external_id) {
1312
1444
  if (appName) {
1313
1445
  config.external_id = `${appName}:${entry.name}`;
@@ -1357,17 +1489,6 @@ async function planAgents(api, localAgents, projectId) {
1357
1489
  } catch {
1358
1490
  }
1359
1491
  }
1360
- let mcpNameToId = /* @__PURE__ */ new Map();
1361
- let mcpIdToName = /* @__PURE__ */ new Map();
1362
- const hasMcpRefs = localAgents.some((a) => a.agent.mcp_servers && a.agent.mcp_servers.length > 0) || remoteAgents.some((a) => a.mcp_server_ids && a.mcp_server_ids.length > 0);
1363
- if (hasMcpRefs) {
1364
- try {
1365
- const servers = await api.listMCPServers();
1366
- mcpNameToId = new Map(servers.map((s) => [s.name, s.id]));
1367
- mcpIdToName = new Map(servers.map((s) => [s.id, s.name]));
1368
- } catch {
1369
- }
1370
- }
1371
1492
  for (const { agent, systemPrompt, policyScript } of localAgents) {
1372
1493
  const remote = remoteByExternalId.get(agent.external_id);
1373
1494
  if (!remote) {
@@ -1391,16 +1512,6 @@ async function planAgents(api, localAgents, projectId) {
1391
1512
  if (removedSlugs.length) parts.push(`-${removedSlugs.join(", -")}`);
1392
1513
  diffs.push(`skills (${parts.join(", ")})`);
1393
1514
  }
1394
- const localMcpIds = (agent.mcp_servers || []).map((n) => mcpNameToId.get(n) || n).sort();
1395
- const remoteMcpIds = [...remote.mcp_server_ids || []].sort();
1396
- if (localMcpIds.join(",") !== remoteMcpIds.join(",")) {
1397
- const addedNames = localMcpIds.filter((id) => !remoteMcpIds.includes(id)).map((id) => mcpIdToName.get(id) || id);
1398
- const removedNames = remoteMcpIds.filter((id) => !localMcpIds.includes(id)).map((id) => mcpIdToName.get(id) || id);
1399
- const parts = [];
1400
- if (addedNames.length) parts.push(`+${addedNames.join(", +")}`);
1401
- if (removedNames.length) parts.push(`-${removedNames.join(", -")}`);
1402
- diffs.push(`mcp_servers (${parts.join(", ")})`);
1403
- }
1404
1515
  if (diffs.length > 0) {
1405
1516
  const textDiffs = [];
1406
1517
  if (diffs.includes("system_prompt")) {
@@ -1431,16 +1542,6 @@ async function applyAgents(api, localAgents, projectId) {
1431
1542
  console.log(pc.yellow(` \u26A0 Could not fetch skills for resolution: ${e}`));
1432
1543
  }
1433
1544
  }
1434
- let mcpMap = /* @__PURE__ */ new Map();
1435
- const hasMcpRefs = localAgents.some((a) => a.agent.mcp_servers && a.agent.mcp_servers.length > 0);
1436
- if (hasMcpRefs) {
1437
- try {
1438
- const servers = await api.listMCPServers();
1439
- mcpMap = new Map(servers.map((s) => [s.name, s.id]));
1440
- } catch (e) {
1441
- console.log(pc.yellow(` \u26A0 Could not fetch MCP servers for resolution: ${e}`));
1442
- }
1443
- }
1444
1545
  for (const { agent, systemPrompt, policyScript } of localAgents) {
1445
1546
  const remote = remoteByExternalId.get(agent.external_id);
1446
1547
  const skillIds = [];
@@ -1454,17 +1555,6 @@ async function applyAgents(api, localAgents, projectId) {
1454
1555
  }
1455
1556
  }
1456
1557
  }
1457
- const mcpServerIds = [];
1458
- if (agent.mcp_servers) {
1459
- for (const name of agent.mcp_servers) {
1460
- const id = mcpMap.get(name);
1461
- if (id) {
1462
- mcpServerIds.push(id);
1463
- } else {
1464
- console.log(pc.yellow(` \u26A0 MCP server "${name}" not found, skipping`));
1465
- }
1466
- }
1467
- }
1468
1558
  const payload = {
1469
1559
  external_id: agent.external_id,
1470
1560
  name: agent.name,
@@ -1472,7 +1562,6 @@ async function applyAgents(api, localAgents, projectId) {
1472
1562
  system_prompt: systemPrompt,
1473
1563
  model: agent.model || "",
1474
1564
  skill_ids: skillIds,
1475
- mcp_server_ids: mcpServerIds,
1476
1565
  policy_script: policyScript || "",
1477
1566
  policy_enabled: agent.policy_enabled || false,
1478
1567
  policy_description: agent.policy_description || ""
@@ -1503,12 +1592,6 @@ async function pullAgents(api, platformDir, filterName, projectId) {
1503
1592
  skillIdToSlug = new Map(skills.map((s) => [s.id, s.slug]));
1504
1593
  } catch {
1505
1594
  }
1506
- let mcpIdToName = /* @__PURE__ */ new Map();
1507
- try {
1508
- const servers = await api.listMCPServers();
1509
- mcpIdToName = new Map(servers.map((s) => [s.id, s.name]));
1510
- } catch {
1511
- }
1512
1595
  for (const agent of agents) {
1513
1596
  if (!agent.external_id || agent.managed) continue;
1514
1597
  if (filterName && agent.external_id !== filterName && agent.name !== filterName) {
@@ -1524,13 +1607,6 @@ async function pullAgents(api, platformDir, filterName, projectId) {
1524
1607
  if (slug) skillSlugs.push(slug);
1525
1608
  }
1526
1609
  }
1527
- const mcpServerNames = [];
1528
- if (agent.mcp_server_ids) {
1529
- for (const id of agent.mcp_server_ids) {
1530
- const name = mcpIdToName.get(id);
1531
- if (name) mcpServerNames.push(name);
1532
- }
1533
- }
1534
1610
  const config = {
1535
1611
  external_id: agent.external_id,
1536
1612
  name: agent.name
@@ -1538,7 +1614,6 @@ async function pullAgents(api, platformDir, filterName, projectId) {
1538
1614
  if (agent.description) config.description = agent.description;
1539
1615
  if (agent.model) config.model = agent.model;
1540
1616
  if (skillSlugs.length > 0) config.skills = skillSlugs;
1541
- if (mcpServerNames.length > 0) config.mcp_servers = mcpServerNames;
1542
1617
  if (agent.policy_enabled) config.policy_enabled = true;
1543
1618
  writeFileSync(join(agentDir, "config.json"), JSON.stringify(config, null, 2) + "\n");
1544
1619
  writeFileSync(join(agentDir, "system_prompt.md"), agent.system_prompt || "");
@@ -1663,6 +1738,30 @@ async function listResources(api, platformDir, filterType, appName, projectId) {
1663
1738
  }
1664
1739
  }
1665
1740
  }
1741
+ if (!filterType || filterType === "mailboxes") {
1742
+ const localMailboxes = loadLocalMailboxes(platformDir);
1743
+ const remoteMailboxes = await api.listMailboxes();
1744
+ const remoteBySlug = new Map(remoteMailboxes.map((m) => [m.slug, m]));
1745
+ const localSlugs = new Set(localMailboxes.map((m) => m.slug));
1746
+ for (const mb of localMailboxes) {
1747
+ const remote = remoteBySlug.get(mb.slug);
1748
+ if (!remote) {
1749
+ results.push({ name: mb.slug, type: "mailboxes", status: "local-only" });
1750
+ continue;
1751
+ }
1752
+ const descChanged = (mb.description ?? "").trim() !== (remote.description ?? "").trim();
1753
+ if (descChanged) {
1754
+ results.push({ name: mb.slug, type: "mailboxes", status: "changed", details: "description" });
1755
+ } else {
1756
+ results.push({ name: mb.slug, type: "mailboxes", status: "synced" });
1757
+ }
1758
+ }
1759
+ for (const remote of remoteMailboxes) {
1760
+ if (!localSlugs.has(remote.slug)) {
1761
+ results.push({ name: remote.slug, type: "mailboxes", status: "remote-only" });
1762
+ }
1763
+ }
1764
+ }
1666
1765
  return results;
1667
1766
  }
1668
1767
  function planCollectionDelete(collections, platformDir) {
@@ -2077,11 +2176,32 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
2077
2176
  const skills = local?.agent.skills || remote?.skill_ids || [];
2078
2177
  console.log(` Skills: ${skills.join(", ")}`);
2079
2178
  }
2080
- if (local?.agent.mcp_servers?.length || remote?.mcp_server_ids?.length) {
2081
- const mcps = local?.agent.mcp_servers || remote?.mcp_server_ids || [];
2082
- console.log(` MCP Servers: ${mcps.join(", ")}`);
2179
+ console.log();
2180
+ } else if (resourceType === "mailboxes") {
2181
+ const localMailboxes = loadLocalMailboxes(platformDir, resourceName);
2182
+ const remoteMailboxes = await api.listMailboxes();
2183
+ const local = localMailboxes[0];
2184
+ const remote = remoteMailboxes.find((m) => m.slug === resourceName);
2185
+ if (!local && !remote) {
2186
+ console.log(pc.red(` Mailbox "${resourceName}" not found`));
2187
+ process.exit(1);
2083
2188
  }
2084
2189
  console.log();
2190
+ console.log(pc.bold(` Mailbox: ${local?.slug || remote?.slug}`));
2191
+ console.log();
2192
+ if (local && remote) {
2193
+ const descChanged = (local.description ?? "").trim() !== (remote.description ?? "").trim();
2194
+ console.log(` Status: ${descChanged ? pc.yellow("changed") : pc.green("synced")}`);
2195
+ } else if (local) {
2196
+ console.log(` Status: ${pc.yellow("local only")}`);
2197
+ } else {
2198
+ console.log(` Status: ${pc.cyan("remote only")}`);
2199
+ }
2200
+ if (remote?.email) console.log(` Email: ${remote.email}`);
2201
+ const desc = local?.description || remote?.description;
2202
+ if (desc) console.log(` Description: ${desc}`);
2203
+ if (remote?.project_id) console.log(` Project: ${remote.project_id}`);
2204
+ console.log();
2085
2205
  } else if (resourceType === "app") {
2086
2206
  const projectRoot = findProjectRoot();
2087
2207
  loadEnv(projectRoot);
@@ -2173,6 +2293,13 @@ async function plan(args) {
2173
2293
  allChanges.push(...changes);
2174
2294
  }
2175
2295
  }
2296
+ if (!type || type === "mailboxes") {
2297
+ const localMailboxes = loadLocalMailboxes(platformDir, name || void 0);
2298
+ if (localMailboxes.length > 0) {
2299
+ const changes = await planMailboxes(api, localMailboxes);
2300
+ allChanges.push(...changes);
2301
+ }
2302
+ }
2176
2303
  if (allChanges.length === 0) {
2177
2304
  if (jsonOutput) {
2178
2305
  console.log(JSON.stringify({ changes: [], warnings: [] }));
@@ -2274,12 +2401,14 @@ async function apply(args) {
2274
2401
  const localAutomations = !type || type === "automations" ? loadLocalAutomations(platformDir, name || void 0, appName) : [];
2275
2402
  const localHooks = !type || type === "hooks" ? loadLocalHooks(platformDir, name || void 0, appName) : [];
2276
2403
  const localAgents = !type || type === "agents" ? loadLocalAgents(platformDir, name || void 0, appName) : [];
2404
+ const localMailboxes = !type || type === "mailboxes" ? loadLocalMailboxes(platformDir, name || void 0) : [];
2277
2405
  if (localCollections.length > 0) allChanges.push(...await planCollections(api, localCollections));
2278
2406
  if (localAutomations.length > 0) allChanges.push(...await planAutomations(api, localAutomations));
2279
2407
  if (localHooks.length > 0) allChanges.push(...await planHooks(api, localHooks, collections));
2280
2408
  if (localAgents.length > 0) allChanges.push(...await planAgents(api, localAgents, projectId));
2409
+ if (localMailboxes.length > 0) allChanges.push(...await planMailboxes(api, localMailboxes));
2281
2410
  if (name) {
2282
- const hasLocal = localCollections.length > 0 || localAutomations.length > 0 || localHooks.length > 0 || localAgents.length > 0;
2411
+ const hasLocal = localCollections.length > 0 || localAutomations.length > 0 || localHooks.length > 0 || localAgents.length > 0 || localMailboxes.length > 0;
2283
2412
  if (!hasLocal) {
2284
2413
  console.log();
2285
2414
  console.log(pc.red(` Resource "${name}" not found locally`));
@@ -2370,6 +2499,11 @@ async function apply(args) {
2370
2499
  totalErrors += await applyAgents(api, localAgents, projectId);
2371
2500
  console.log();
2372
2501
  }
2502
+ if (localMailboxes.length > 0) {
2503
+ console.log(pc.bold(" Mailboxes:"));
2504
+ totalErrors += await applyMailboxes(api, localMailboxes);
2505
+ console.log();
2506
+ }
2373
2507
  if (willDeployApp) {
2374
2508
  console.log(pc.bold(" App:"));
2375
2509
  await applyApp(args);
@@ -2476,6 +2610,11 @@ async function pull(args) {
2476
2610
  await pullAgents(api, platformDir, name || void 0, projectId);
2477
2611
  console.log();
2478
2612
  }
2613
+ if (!type || type === "mailboxes") {
2614
+ console.log(pc.bold(" Mailboxes:"));
2615
+ await pullMailboxes(api, platformDir, name || void 0);
2616
+ console.log();
2617
+ }
2479
2618
  console.log(pc.green(" Done!"));
2480
2619
  console.log();
2481
2620
  }
@@ -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-GKI2HQJC.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.19.0",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {