@lumerahq/cli 0.12.0 → 0.13.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.
@@ -146,6 +146,42 @@ var ApiClient = class {
146
146
  method: "DELETE"
147
147
  });
148
148
  }
149
+ // Agents
150
+ async listAgents() {
151
+ const result = await this.request("/api/agents?limit=100");
152
+ return result.agents || [];
153
+ }
154
+ async createAgent(def) {
155
+ return this.request("/api/agents", {
156
+ method: "POST",
157
+ body: JSON.stringify(def)
158
+ });
159
+ }
160
+ async updateAgent(id, def) {
161
+ return this.request(`/api/agents/${id}`, {
162
+ method: "PATCH",
163
+ body: JSON.stringify(def)
164
+ });
165
+ }
166
+ async deleteAgent(id) {
167
+ await this.request(`/api/agents/${id}`, {
168
+ method: "DELETE"
169
+ });
170
+ }
171
+ // Agent Skills (for slug-to-ID resolution)
172
+ async listAgentSkills() {
173
+ const result = await this.request("/api/lm_agent_skills?limit=100");
174
+ return result.skills || [];
175
+ }
176
+ // Agent Invoke
177
+ async invokeAgent(agentId, message, sessionId) {
178
+ const body = { message };
179
+ if (sessionId) body.session_id = sessionId;
180
+ return this.request(`/api/agents/${agentId}/invoke`, {
181
+ method: "POST",
182
+ body: JSON.stringify(body)
183
+ });
184
+ }
149
185
  };
150
186
  function createApiClient(token, baseUrl) {
151
187
  return new ApiClient(token, baseUrl);
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  createApiClient,
6
6
  loadEnv
7
- } from "./chunk-UGFGRGNP.js";
7
+ } from "./chunk-7ZGIC6F7.js";
8
8
  import {
9
9
  getToken
10
10
  } from "./chunk-NDLYGKS6.js";
package/dist/index.js CHANGED
@@ -139,7 +139,7 @@ ${pc.dim("Resource Commands:")}
139
139
 
140
140
  ${pc.dim("Development:")}
141
141
  ${pc.cyan("dev")} Start dev server
142
- ${pc.cyan("run")} <target> Run script or trigger automation
142
+ ${pc.cyan("run")} <target> Run script, trigger automation, or invoke agent
143
143
 
144
144
  ${pc.dim("Project:")}
145
145
  ${pc.cyan("init")} [name] Scaffold a new project
@@ -167,6 +167,7 @@ ${pc.dim("Resource Types:")}
167
167
  collections Data collections (schemas)
168
168
  automations Background Python scripts
169
169
  hooks Collection lifecycle handlers
170
+ agents AI agents with skills and prompts
170
171
  app Frontend application
171
172
 
172
173
  ${pc.dim("Examples:")}
@@ -177,6 +178,7 @@ ${pc.dim("Examples:")}
177
178
  lumera show collections/users # Show collection details
178
179
  lumera run scripts/seed.py # Run a script
179
180
  lumera run automations/sync # Trigger automation
181
+ lumera run agents/support "Hello" # Invoke an agent
180
182
  lumera dev # Start dev server
181
183
 
182
184
  ${pc.dim("Documentation:")}
@@ -208,29 +210,29 @@ async function main() {
208
210
  switch (command) {
209
211
  // Resource commands
210
212
  case "plan":
211
- await import("./resources-4AP5AXH5.js").then((m) => m.plan(args.slice(1)));
213
+ await import("./resources-JDUZSI43.js").then((m) => m.plan(args.slice(1)));
212
214
  break;
213
215
  case "apply":
214
- await import("./resources-4AP5AXH5.js").then((m) => m.apply(args.slice(1)));
216
+ await import("./resources-JDUZSI43.js").then((m) => m.apply(args.slice(1)));
215
217
  break;
216
218
  case "pull":
217
- await import("./resources-4AP5AXH5.js").then((m) => m.pull(args.slice(1)));
219
+ await import("./resources-JDUZSI43.js").then((m) => m.pull(args.slice(1)));
218
220
  break;
219
221
  case "destroy":
220
- await import("./resources-4AP5AXH5.js").then((m) => m.destroy(args.slice(1)));
222
+ await import("./resources-JDUZSI43.js").then((m) => m.destroy(args.slice(1)));
221
223
  break;
222
224
  case "list":
223
- await import("./resources-4AP5AXH5.js").then((m) => m.list(args.slice(1)));
225
+ await import("./resources-JDUZSI43.js").then((m) => m.list(args.slice(1)));
224
226
  break;
225
227
  case "show":
226
- await import("./resources-4AP5AXH5.js").then((m) => m.show(args.slice(1)));
228
+ await import("./resources-JDUZSI43.js").then((m) => m.show(args.slice(1)));
227
229
  break;
228
230
  // Development
229
231
  case "dev":
230
- await import("./dev-IFEYXWVJ.js").then((m) => m.dev(args.slice(1)));
232
+ await import("./dev-LNH47WSS.js").then((m) => m.dev(args.slice(1)));
231
233
  break;
232
234
  case "run":
233
- await import("./run-PBM4WOJT.js").then((m) => m.run(args.slice(1)));
235
+ await import("./run-3UBV3SVA.js").then((m) => m.run(args.slice(1)));
234
236
  break;
235
237
  // Project
236
238
  case "init":
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  createApiClient,
6
6
  loadEnv
7
- } from "./chunk-UGFGRGNP.js";
7
+ } from "./chunk-7ZGIC6F7.js";
8
8
  import {
9
9
  getToken
10
10
  } from "./chunk-NDLYGKS6.js";
@@ -35,6 +35,8 @@ ${pc.dim("Resources:")}
35
35
  automations Plan only automations
36
36
  automations/<name> Plan single automation
37
37
  hooks Plan only hooks
38
+ agents Plan only agents
39
+ agents/<name> Plan single agent
38
40
  app Plan app deployment
39
41
 
40
42
  ${pc.dim("Examples:")}
@@ -58,6 +60,8 @@ ${pc.dim("Resources:")}
58
60
  automations Apply only automations
59
61
  automations/<name> Apply single automation
60
62
  hooks Apply only hooks
63
+ agents Apply only agents
64
+ agents/<name> Apply single agent
61
65
  app Deploy the frontend app
62
66
 
63
67
  ${pc.dim("Options:")}
@@ -86,6 +90,8 @@ ${pc.dim("Resources:")}
86
90
  automations Pull only automations
87
91
  automations/<name> Pull single automation
88
92
  hooks Pull only hooks
93
+ agents Pull only agents
94
+ agents/<name> Pull single agent
89
95
 
90
96
  ${pc.dim("Examples:")}
91
97
  lumera pull # Pull all resources
@@ -108,6 +114,8 @@ ${pc.dim("Resources:")}
108
114
  automations Destroy only automations
109
115
  automations/<name> Destroy single automation
110
116
  hooks Destroy only hooks
117
+ agents Destroy only agents
118
+ agents/<name> Destroy single agent
111
119
  app Delete app registration
112
120
 
113
121
  ${pc.dim("Options:")}
@@ -133,6 +141,7 @@ ${pc.dim("Types:")}
133
141
  collections List only collections
134
142
  automations List only automations
135
143
  hooks List only hooks
144
+ agents List only agents
136
145
 
137
146
  ${pc.dim("Options:")}
138
147
  --all Include remote-only resources
@@ -155,6 +164,7 @@ ${pc.dim("Resources:")}
155
164
  collections/<name> Show collection details
156
165
  automations/<name> Show automation details
157
166
  hooks/<name> Show hook details
167
+ agents/<name> Show agent details
158
168
  app Show app details
159
169
 
160
170
  ${pc.dim("Examples:")}
@@ -170,7 +180,7 @@ function parseResource(resourcePath) {
170
180
  const parts = resourcePath.split("/");
171
181
  const type = parts[0];
172
182
  const name = parts.slice(1).join("/") || null;
173
- if (!["collections", "automations", "hooks", "app"].includes(type)) {
183
+ if (!["collections", "automations", "hooks", "agents", "app"].includes(type)) {
174
184
  return { type: null, name: null };
175
185
  }
176
186
  return { type, name };
@@ -823,6 +833,165 @@ ${hook.script.split("\n").map((line) => " " + line).join("\n")}
823
833
  console.log(pc.green(" \u2713"), `${hook.name} \u2192 hooks/${fileName}`);
824
834
  }
825
835
  }
836
+ function loadLocalAgents(platformDir, filterName, appName) {
837
+ const agentsDir = join(platformDir, "agents");
838
+ if (!existsSync(agentsDir)) return [];
839
+ const agents = [];
840
+ const errors = [];
841
+ for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
842
+ if (!entry.isDirectory()) continue;
843
+ const agentDir = join(agentsDir, entry.name);
844
+ const configPath = join(agentDir, "config.json");
845
+ const promptPath = join(agentDir, "system_prompt.md");
846
+ if (!existsSync(configPath)) {
847
+ errors.push(`${entry.name}: missing config.json`);
848
+ continue;
849
+ }
850
+ if (!existsSync(promptPath)) {
851
+ errors.push(`${entry.name}: missing system_prompt.md`);
852
+ continue;
853
+ }
854
+ try {
855
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
856
+ if (filterName && config.external_id !== filterName && config.name !== filterName && entry.name !== filterName) {
857
+ continue;
858
+ }
859
+ if (!config.external_id) {
860
+ if (appName) {
861
+ config.external_id = `${appName}:${entry.name}`;
862
+ } else {
863
+ errors.push(`${entry.name}: missing external_id in config.json`);
864
+ continue;
865
+ }
866
+ }
867
+ if (!config.name) {
868
+ errors.push(`${entry.name}: missing name in config.json`);
869
+ continue;
870
+ }
871
+ const systemPrompt = readFileSync(promptPath, "utf-8");
872
+ agents.push({ agent: config, systemPrompt });
873
+ } catch (e) {
874
+ errors.push(`${entry.name}: failed to parse config.json - ${e}`);
875
+ }
876
+ }
877
+ if (errors.length > 0) {
878
+ console.log(pc.red(" Agent errors:"));
879
+ for (const err of errors) {
880
+ console.log(pc.red(` \u2717 ${err}`));
881
+ }
882
+ throw new Error(`Found ${errors.length} agent error(s)`);
883
+ }
884
+ return agents;
885
+ }
886
+ async function planAgents(api, localAgents) {
887
+ const changes = [];
888
+ const remoteAgents = await api.listAgents();
889
+ const remoteByExternalId = new Map(
890
+ remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a])
891
+ );
892
+ for (const { agent, systemPrompt } of localAgents) {
893
+ const remote = remoteByExternalId.get(agent.external_id);
894
+ if (!remote) {
895
+ changes.push({ type: "create", resource: "agent", id: agent.external_id, name: agent.name });
896
+ } else {
897
+ const diffs = [];
898
+ if (remote.name !== agent.name) diffs.push("name");
899
+ if ((remote.description || "") !== (agent.description || "")) diffs.push("description");
900
+ if ((remote.system_prompt || "").trim() !== systemPrompt.trim()) diffs.push("system_prompt");
901
+ if (diffs.length > 0) {
902
+ changes.push({ type: "update", resource: "agent", id: agent.external_id, name: agent.name, details: `changed: ${diffs.join(", ")}` });
903
+ }
904
+ }
905
+ }
906
+ return changes;
907
+ }
908
+ async function applyAgents(api, localAgents) {
909
+ let errors = 0;
910
+ const remoteAgents = await api.listAgents();
911
+ const remoteByExternalId = new Map(
912
+ remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a])
913
+ );
914
+ let skillMap = /* @__PURE__ */ new Map();
915
+ const hasSkillRefs = localAgents.some((a) => a.agent.skills && a.agent.skills.length > 0);
916
+ if (hasSkillRefs) {
917
+ try {
918
+ const skills = await api.listAgentSkills();
919
+ skillMap = new Map(skills.map((s) => [s.slug, s.id]));
920
+ } catch (e) {
921
+ console.log(pc.yellow(` \u26A0 Could not fetch skills for resolution: ${e}`));
922
+ }
923
+ }
924
+ for (const { agent, systemPrompt } of localAgents) {
925
+ const remote = remoteByExternalId.get(agent.external_id);
926
+ const skillIds = [];
927
+ if (agent.skills) {
928
+ for (const slug of agent.skills) {
929
+ const id = skillMap.get(slug);
930
+ if (id) {
931
+ skillIds.push(id);
932
+ } else {
933
+ console.log(pc.yellow(` \u26A0 Skill "${slug}" not found, skipping`));
934
+ }
935
+ }
936
+ }
937
+ const payload = {
938
+ external_id: agent.external_id,
939
+ name: agent.name,
940
+ description: agent.description || "",
941
+ system_prompt: systemPrompt,
942
+ skill_ids: skillIds
943
+ };
944
+ try {
945
+ if (remote) {
946
+ await api.updateAgent(remote.id, payload);
947
+ console.log(pc.green(" \u2713"), `${agent.name} (updated)`);
948
+ } else {
949
+ await api.createAgent(payload);
950
+ console.log(pc.green(" \u2713"), `${agent.name} (created)`);
951
+ }
952
+ } catch (e) {
953
+ console.log(pc.red(" \u2717"), `${agent.name}: ${e}`);
954
+ errors++;
955
+ }
956
+ }
957
+ return errors;
958
+ }
959
+ async function pullAgents(api, platformDir, filterName) {
960
+ const agentsDir = join(platformDir, "agents");
961
+ mkdirSync(agentsDir, { recursive: true });
962
+ const agents = await api.listAgents();
963
+ let skillIdToSlug = /* @__PURE__ */ new Map();
964
+ try {
965
+ const skills = await api.listAgentSkills();
966
+ skillIdToSlug = new Map(skills.map((s) => [s.id, s.slug]));
967
+ } catch {
968
+ }
969
+ for (const agent of agents) {
970
+ if (!agent.external_id || agent.managed) continue;
971
+ if (filterName && agent.external_id !== filterName && agent.name !== filterName) {
972
+ continue;
973
+ }
974
+ const dirName = agent.external_id.replace(/[^a-zA-Z0-9_-]/g, "_");
975
+ const agentDir = join(agentsDir, dirName);
976
+ mkdirSync(agentDir, { recursive: true });
977
+ const skillSlugs = [];
978
+ if (agent.skill_ids) {
979
+ for (const id of agent.skill_ids) {
980
+ const slug = skillIdToSlug.get(id);
981
+ if (slug) skillSlugs.push(slug);
982
+ }
983
+ }
984
+ const config = {
985
+ external_id: agent.external_id,
986
+ name: agent.name
987
+ };
988
+ if (agent.description) config.description = agent.description;
989
+ if (skillSlugs.length > 0) config.skills = skillSlugs;
990
+ writeFileSync(join(agentDir, "config.json"), JSON.stringify(config, null, 2) + "\n");
991
+ writeFileSync(join(agentDir, "system_prompt.md"), agent.system_prompt || "");
992
+ console.log(pc.green(" \u2713"), `${agent.name} \u2192 agents/${dirName}/`);
993
+ }
994
+ }
826
995
  async function listResources(api, platformDir, filterType, appName) {
827
996
  const results = [];
828
997
  if (!filterType || filterType === "collections") {
@@ -907,6 +1076,32 @@ async function listResources(api, platformDir, filterType, appName) {
907
1076
  }
908
1077
  }
909
1078
  }
1079
+ if (!filterType || filterType === "agents") {
1080
+ const localAgents = loadLocalAgents(platformDir, void 0, appName);
1081
+ const remoteAgents = await api.listAgents();
1082
+ const remoteByExternalId = new Map(remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a]));
1083
+ const localIds = new Set(localAgents.map((a) => a.agent.external_id));
1084
+ for (const { agent, systemPrompt } of localAgents) {
1085
+ const remote = remoteByExternalId.get(agent.external_id);
1086
+ if (!remote) {
1087
+ results.push({ name: agent.name, type: "agents", status: "local-only" });
1088
+ } else {
1089
+ const promptChanged = (remote.system_prompt || "").trim() !== systemPrompt.trim();
1090
+ const nameChanged = remote.name !== agent.name;
1091
+ if (promptChanged || nameChanged) {
1092
+ results.push({ name: agent.name, type: "agents", status: "changed", details: promptChanged ? "system_prompt" : "name" });
1093
+ } else {
1094
+ results.push({ name: agent.name, type: "agents", status: "synced" });
1095
+ }
1096
+ }
1097
+ }
1098
+ for (const remote of remoteAgents) {
1099
+ if (!remote.external_id || remote.managed) continue;
1100
+ if (!localIds.has(remote.external_id)) {
1101
+ results.push({ name: remote.name, type: "agents", status: "remote-only" });
1102
+ }
1103
+ }
1104
+ }
910
1105
  return results;
911
1106
  }
912
1107
  function planCollectionDelete(collections, platformDir) {
@@ -1016,6 +1211,20 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
1016
1211
  }
1017
1212
  }
1018
1213
  }
1214
+ if (!resourceType || resourceType === "agents") {
1215
+ try {
1216
+ const localAgents = loadLocalAgents(platformDir, resourceName || void 0, appName);
1217
+ const remoteAgents = await api.listAgents();
1218
+ const remoteByExternalId = new Map(remoteAgents.filter((a) => a.external_id).map((a) => [a.external_id, a]));
1219
+ for (const { agent } of localAgents) {
1220
+ const remote = remoteByExternalId.get(agent.external_id);
1221
+ if (remote) {
1222
+ toDelete.push({ type: "agent", id: agent.external_id, name: agent.name, remoteId: remote.id });
1223
+ }
1224
+ }
1225
+ } catch {
1226
+ }
1227
+ }
1019
1228
  if (toDelete.length === 0) {
1020
1229
  console.log(pc.green(" \u2713 No resources found to delete"));
1021
1230
  return;
@@ -1038,10 +1247,20 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
1038
1247
  return;
1039
1248
  }
1040
1249
  }
1250
+ const agents = toDelete.filter((r) => r.type === "agent");
1041
1251
  const hooks = toDelete.filter((r) => r.type === "hook");
1042
1252
  const automations = toDelete.filter((r) => r.type === "automation");
1043
1253
  const collections = toDelete.filter((r) => r.type === "collection");
1044
1254
  let errors = 0;
1255
+ for (const resource of agents) {
1256
+ try {
1257
+ await api.deleteAgent(resource.remoteId);
1258
+ console.log(pc.green(" \u2713"), `Deleted agent: ${resource.name}`);
1259
+ } catch (e) {
1260
+ console.log(pc.red(" \u2717"), `Failed to delete agent ${resource.name}: ${e}`);
1261
+ errors++;
1262
+ }
1263
+ }
1045
1264
  for (const resource of hooks) {
1046
1265
  try {
1047
1266
  await api.deleteHook(resource.remoteId);
@@ -1250,6 +1469,43 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
1250
1469
  console.log(` Trigger: ${local?.hook.trigger || remote?.event}`);
1251
1470
  console.log(` Enabled: ${local?.hook.enabled !== false || remote?.enabled}`);
1252
1471
  console.log();
1472
+ } else if (resourceType === "agents") {
1473
+ const localAgents = loadLocalAgents(platformDir, resourceName, appName);
1474
+ const remoteAgents = await api.listAgents();
1475
+ const local = localAgents[0];
1476
+ const remote = remoteAgents.find((a) => a.external_id === resourceName || a.name === resourceName);
1477
+ if (!local && !remote) {
1478
+ console.log(pc.red(` Agent "${resourceName}" not found`));
1479
+ process.exit(1);
1480
+ }
1481
+ console.log();
1482
+ console.log(pc.bold(` Agent: ${local?.agent.name || remote?.name}`));
1483
+ console.log();
1484
+ if (local && remote) {
1485
+ const promptChanged = (remote.system_prompt || "").trim() !== local.systemPrompt.trim();
1486
+ const nameChanged = remote.name !== local.agent.name;
1487
+ const descChanged = (remote.description || "") !== (local.agent.description || "");
1488
+ if (promptChanged || nameChanged || descChanged) {
1489
+ console.log(` Status: ${pc.yellow("changed")}`);
1490
+ } else {
1491
+ console.log(` Status: ${pc.green("synced")}`);
1492
+ }
1493
+ } else if (local) {
1494
+ console.log(` Status: ${pc.yellow("local only")}`);
1495
+ } else {
1496
+ console.log(` Status: ${pc.cyan("remote only")}`);
1497
+ }
1498
+ if (local?.agent.external_id || remote?.external_id) {
1499
+ console.log(` External ID: ${local?.agent.external_id || remote?.external_id}`);
1500
+ }
1501
+ if (local?.agent.description || remote?.description) {
1502
+ console.log(` Description: ${local?.agent.description || remote?.description}`);
1503
+ }
1504
+ if (local?.agent.skills?.length || remote?.skill_ids?.length) {
1505
+ const skills = local?.agent.skills || remote?.skill_ids || [];
1506
+ console.log(` Skills: ${skills.join(", ")}`);
1507
+ }
1508
+ console.log();
1253
1509
  } else if (resourceType === "app") {
1254
1510
  const projectRoot = findProjectRoot();
1255
1511
  loadEnv(projectRoot);
@@ -1325,6 +1581,13 @@ async function plan(args) {
1325
1581
  allChanges.push(...changes);
1326
1582
  }
1327
1583
  }
1584
+ if (!type || type === "agents") {
1585
+ const localAgents = loadLocalAgents(platformDir, name || void 0, appName);
1586
+ if (localAgents.length > 0) {
1587
+ const changes = await planAgents(api, localAgents);
1588
+ allChanges.push(...changes);
1589
+ }
1590
+ }
1328
1591
  if (allChanges.length === 0) {
1329
1592
  console.log(pc.green(" \u2713 No changes detected"));
1330
1593
  console.log();
@@ -1416,6 +1679,17 @@ async function apply(args) {
1416
1679
  process.exit(1);
1417
1680
  }
1418
1681
  }
1682
+ if (!type || type === "agents") {
1683
+ const localAgents = loadLocalAgents(platformDir, name || void 0, appName);
1684
+ if (localAgents.length > 0) {
1685
+ console.log(pc.bold(" Agents:"));
1686
+ totalErrors += await applyAgents(api, localAgents);
1687
+ console.log();
1688
+ } else if (name) {
1689
+ console.log(pc.red(` Agent "${name}" not found locally`));
1690
+ process.exit(1);
1691
+ }
1692
+ }
1419
1693
  if (!type) {
1420
1694
  try {
1421
1695
  const projectRoot2 = findProjectRoot();
@@ -1463,6 +1737,11 @@ async function pull(args) {
1463
1737
  await pullHooks(api, platformDir, name || void 0);
1464
1738
  console.log();
1465
1739
  }
1740
+ if (!type || type === "agents") {
1741
+ console.log(pc.bold(" Agents:"));
1742
+ await pullAgents(api, platformDir, name || void 0);
1743
+ console.log();
1744
+ }
1466
1745
  console.log(pc.green(" Done!"));
1467
1746
  console.log();
1468
1747
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createApiClient,
3
3
  loadEnv
4
- } from "./chunk-UGFGRGNP.js";
4
+ } from "./chunk-7ZGIC6F7.js";
5
5
  import {
6
6
  getToken
7
7
  } from "./chunk-NDLYGKS6.js";
@@ -21,16 +21,18 @@ ${pc.dim("Usage:")}
21
21
  lumera run <target> [options]
22
22
 
23
23
  ${pc.dim("Description:")}
24
- Run a script locally or trigger an automation on the platform.
24
+ Run a script locally, trigger an automation, or invoke an agent.
25
25
 
26
26
  ${pc.dim("Targets:")}
27
27
  scripts/<name>.py Run a Python script locally using uv
28
28
  automations/<name> Trigger an automation run on the platform
29
+ agents/<name> "msg" Invoke an agent on the platform
29
30
 
30
31
  ${pc.dim("Options:")}
31
32
  --local Run automation code locally instead of on platform
32
33
  --preset <name> Use a specific preset when triggering automation
33
34
  --input <json> Pass JSON input to the automation
35
+ --session <id> Use a specific session for agent invocation
34
36
  --help, -h Show this help
35
37
 
36
38
  ${pc.dim("Examples:")}
@@ -38,6 +40,7 @@ ${pc.dim("Examples:")}
38
40
  lumera run automations/sync # Trigger automation on platform
39
41
  lumera run automations/sync --preset daily
40
42
  lumera run automations/sync --local # Run automation code locally
43
+ lumera run agents/support "Hello" # Invoke an agent
41
44
 
42
45
  ${pc.dim("Notes:")}
43
46
  - Scripts can declare dependencies using PEP 723 inline metadata
@@ -48,6 +51,7 @@ ${pc.dim("Notes:")}
48
51
  function parseFlags(args) {
49
52
  const flags = {};
50
53
  let target = "";
54
+ const positionalArgs = [];
51
55
  for (let i = 0; i < args.length; i++) {
52
56
  const arg = args[i];
53
57
  if (arg.startsWith("--")) {
@@ -61,9 +65,11 @@ function parseFlags(args) {
61
65
  }
62
66
  } else if (!target) {
63
67
  target = arg;
68
+ } else {
69
+ positionalArgs.push(arg);
64
70
  }
65
71
  }
66
- return { target, flags };
72
+ return { target, flags, positionalArgs };
67
73
  }
68
74
  async function runScript(scriptPath, projectRoot) {
69
75
  const fullScriptPath = resolve(projectRoot, scriptPath);
@@ -226,12 +232,65 @@ async function runAutomationLocally(automationName, projectRoot, flags) {
226
232
  process.on("SIGINT", () => uv.kill("SIGINT"));
227
233
  process.on("SIGTERM", () => uv.kill("SIGTERM"));
228
234
  }
235
+ async function invokeAgent(agentName, message, flags) {
236
+ if (!message) {
237
+ console.error(pc.red(" Message is required."));
238
+ console.error(pc.dim(' Usage: lumera run agents/<name> "Your message here"'));
239
+ process.exit(1);
240
+ }
241
+ const api = createApiClient();
242
+ console.log();
243
+ console.log(pc.cyan(pc.bold(" Invoke Agent")));
244
+ console.log();
245
+ const agents = await api.listAgents();
246
+ const agent = agents.find(
247
+ (a) => a.external_id === agentName || a.name === agentName
248
+ );
249
+ if (!agent) {
250
+ console.error(pc.red(` Agent "${agentName}" not found on platform.`));
251
+ console.error(pc.dim(" Run `lumera list agents` to see available agents."));
252
+ process.exit(1);
253
+ }
254
+ console.log(pc.dim(` Agent: ${agent.name}`));
255
+ const sessionId = flags.session ? String(flags.session) : void 0;
256
+ if (sessionId) {
257
+ console.log(pc.dim(` Session: ${sessionId}`));
258
+ }
259
+ console.log();
260
+ try {
261
+ const result = await api.invokeAgent(
262
+ agent.external_id || agent.id,
263
+ message,
264
+ sessionId
265
+ );
266
+ if (result.success) {
267
+ console.log(result.output);
268
+ } else {
269
+ console.error(pc.red(` Agent error: ${result.error}`));
270
+ process.exit(1);
271
+ }
272
+ if (result.tool_calls && result.tool_calls.length > 0) {
273
+ console.log();
274
+ console.log(pc.dim(` Tool calls: ${result.tool_calls.length}`));
275
+ for (const tc of result.tool_calls) {
276
+ console.log(pc.dim(` - ${tc.tool} (${tc.success ? "ok" : "failed"})`));
277
+ }
278
+ }
279
+ if (result.session_id) {
280
+ console.log(pc.dim(` Session: ${result.session_id}`));
281
+ }
282
+ } catch (e) {
283
+ console.error(pc.red(` Failed to invoke agent: ${e}`));
284
+ process.exit(1);
285
+ }
286
+ console.log();
287
+ }
229
288
  async function run(args) {
230
289
  if (args.includes("--help") || args.includes("-h") || args.length === 0) {
231
290
  showHelp();
232
291
  process.exit(args.length === 0 ? 1 : 0);
233
292
  }
234
- const { target, flags } = parseFlags(args);
293
+ const { target, flags, positionalArgs } = parseFlags(args);
235
294
  let projectRoot;
236
295
  try {
237
296
  projectRoot = findProjectRoot();
@@ -248,6 +307,12 @@ async function run(args) {
248
307
  }
249
308
  return;
250
309
  }
310
+ if (target.startsWith("agents/")) {
311
+ const agentName = target.replace("agents/", "");
312
+ const message = positionalArgs.join(" ") || void 0;
313
+ await invokeAgent(agentName, message, flags);
314
+ return;
315
+ }
251
316
  await runScript(target, projectRoot);
252
317
  }
253
318
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {