@lumerahq/cli 0.12.0 → 0.13.1
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);
|
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
|
|
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-
|
|
213
|
+
await import("./resources-PKLA2XDG.js").then((m) => m.plan(args.slice(1)));
|
|
212
214
|
break;
|
|
213
215
|
case "apply":
|
|
214
|
-
await import("./resources-
|
|
216
|
+
await import("./resources-PKLA2XDG.js").then((m) => m.apply(args.slice(1)));
|
|
215
217
|
break;
|
|
216
218
|
case "pull":
|
|
217
|
-
await import("./resources-
|
|
219
|
+
await import("./resources-PKLA2XDG.js").then((m) => m.pull(args.slice(1)));
|
|
218
220
|
break;
|
|
219
221
|
case "destroy":
|
|
220
|
-
await import("./resources-
|
|
222
|
+
await import("./resources-PKLA2XDG.js").then((m) => m.destroy(args.slice(1)));
|
|
221
223
|
break;
|
|
222
224
|
case "list":
|
|
223
|
-
await import("./resources-
|
|
225
|
+
await import("./resources-PKLA2XDG.js").then((m) => m.list(args.slice(1)));
|
|
224
226
|
break;
|
|
225
227
|
case "show":
|
|
226
|
-
await import("./resources-
|
|
228
|
+
await import("./resources-PKLA2XDG.js").then((m) => m.show(args.slice(1)));
|
|
227
229
|
break;
|
|
228
230
|
// Development
|
|
229
231
|
case "dev":
|
|
230
|
-
await import("./dev-
|
|
232
|
+
await import("./dev-LNH47WSS.js").then((m) => m.dev(args.slice(1)));
|
|
231
233
|
break;
|
|
232
234
|
case "run":
|
|
233
|
-
await import("./run-
|
|
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-
|
|
7
|
+
} from "./chunk-7ZGIC6F7.js";
|
|
8
8
|
import {
|
|
9
9
|
getToken
|
|
10
10
|
} from "./chunk-NDLYGKS6.js";
|
|
@@ -18,8 +18,19 @@ import {
|
|
|
18
18
|
// src/commands/resources.ts
|
|
19
19
|
import pc from "picocolors";
|
|
20
20
|
import prompts from "prompts";
|
|
21
|
+
import { execFileSync, execSync } from "child_process";
|
|
21
22
|
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
22
23
|
import { join, resolve } from "path";
|
|
24
|
+
function detectPackageManager() {
|
|
25
|
+
for (const pm of ["bun", "pnpm", "yarn", "npm"]) {
|
|
26
|
+
try {
|
|
27
|
+
execFileSync(pm, ["--version"], { stdio: "ignore" });
|
|
28
|
+
return pm;
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return "npm";
|
|
33
|
+
}
|
|
23
34
|
function showPlanHelp() {
|
|
24
35
|
console.log(`
|
|
25
36
|
${pc.dim("Usage:")}
|
|
@@ -35,6 +46,8 @@ ${pc.dim("Resources:")}
|
|
|
35
46
|
automations Plan only automations
|
|
36
47
|
automations/<name> Plan single automation
|
|
37
48
|
hooks Plan only hooks
|
|
49
|
+
agents Plan only agents
|
|
50
|
+
agents/<name> Plan single agent
|
|
38
51
|
app Plan app deployment
|
|
39
52
|
|
|
40
53
|
${pc.dim("Examples:")}
|
|
@@ -58,6 +71,8 @@ ${pc.dim("Resources:")}
|
|
|
58
71
|
automations Apply only automations
|
|
59
72
|
automations/<name> Apply single automation
|
|
60
73
|
hooks Apply only hooks
|
|
74
|
+
agents Apply only agents
|
|
75
|
+
agents/<name> Apply single agent
|
|
61
76
|
app Deploy the frontend app
|
|
62
77
|
|
|
63
78
|
${pc.dim("Options:")}
|
|
@@ -86,6 +101,8 @@ ${pc.dim("Resources:")}
|
|
|
86
101
|
automations Pull only automations
|
|
87
102
|
automations/<name> Pull single automation
|
|
88
103
|
hooks Pull only hooks
|
|
104
|
+
agents Pull only agents
|
|
105
|
+
agents/<name> Pull single agent
|
|
89
106
|
|
|
90
107
|
${pc.dim("Examples:")}
|
|
91
108
|
lumera pull # Pull all resources
|
|
@@ -108,6 +125,8 @@ ${pc.dim("Resources:")}
|
|
|
108
125
|
automations Destroy only automations
|
|
109
126
|
automations/<name> Destroy single automation
|
|
110
127
|
hooks Destroy only hooks
|
|
128
|
+
agents Destroy only agents
|
|
129
|
+
agents/<name> Destroy single agent
|
|
111
130
|
app Delete app registration
|
|
112
131
|
|
|
113
132
|
${pc.dim("Options:")}
|
|
@@ -133,6 +152,7 @@ ${pc.dim("Types:")}
|
|
|
133
152
|
collections List only collections
|
|
134
153
|
automations List only automations
|
|
135
154
|
hooks List only hooks
|
|
155
|
+
agents List only agents
|
|
136
156
|
|
|
137
157
|
${pc.dim("Options:")}
|
|
138
158
|
--all Include remote-only resources
|
|
@@ -155,6 +175,7 @@ ${pc.dim("Resources:")}
|
|
|
155
175
|
collections/<name> Show collection details
|
|
156
176
|
automations/<name> Show automation details
|
|
157
177
|
hooks/<name> Show hook details
|
|
178
|
+
agents/<name> Show agent details
|
|
158
179
|
app Show app details
|
|
159
180
|
|
|
160
181
|
${pc.dim("Examples:")}
|
|
@@ -170,7 +191,7 @@ function parseResource(resourcePath) {
|
|
|
170
191
|
const parts = resourcePath.split("/");
|
|
171
192
|
const type = parts[0];
|
|
172
193
|
const name = parts.slice(1).join("/") || null;
|
|
173
|
-
if (!["collections", "automations", "hooks", "app"].includes(type)) {
|
|
194
|
+
if (!["collections", "automations", "hooks", "agents", "app"].includes(type)) {
|
|
174
195
|
return { type: null, name: null };
|
|
175
196
|
}
|
|
176
197
|
return { type, name };
|
|
@@ -701,9 +722,9 @@ async function applyApp(args) {
|
|
|
701
722
|
const apiUrl = getApiUrl();
|
|
702
723
|
if (!skipBuild) {
|
|
703
724
|
console.log(pc.dim(" Building..."));
|
|
704
|
-
const { execSync } = await import("child_process");
|
|
705
725
|
try {
|
|
706
|
-
|
|
726
|
+
const pm = detectPackageManager();
|
|
727
|
+
execSync(`${pm} run build`, { cwd: projectRoot, stdio: "inherit" });
|
|
707
728
|
} catch {
|
|
708
729
|
throw new Error("Build failed");
|
|
709
730
|
}
|
|
@@ -823,6 +844,165 @@ ${hook.script.split("\n").map((line) => " " + line).join("\n")}
|
|
|
823
844
|
console.log(pc.green(" \u2713"), `${hook.name} \u2192 hooks/${fileName}`);
|
|
824
845
|
}
|
|
825
846
|
}
|
|
847
|
+
function loadLocalAgents(platformDir, filterName, appName) {
|
|
848
|
+
const agentsDir = join(platformDir, "agents");
|
|
849
|
+
if (!existsSync(agentsDir)) return [];
|
|
850
|
+
const agents = [];
|
|
851
|
+
const errors = [];
|
|
852
|
+
for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
853
|
+
if (!entry.isDirectory()) continue;
|
|
854
|
+
const agentDir = join(agentsDir, entry.name);
|
|
855
|
+
const configPath = join(agentDir, "config.json");
|
|
856
|
+
const promptPath = join(agentDir, "system_prompt.md");
|
|
857
|
+
if (!existsSync(configPath)) {
|
|
858
|
+
errors.push(`${entry.name}: missing config.json`);
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
if (!existsSync(promptPath)) {
|
|
862
|
+
errors.push(`${entry.name}: missing system_prompt.md`);
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
try {
|
|
866
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
867
|
+
if (filterName && config.external_id !== filterName && config.name !== filterName && entry.name !== filterName) {
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
if (!config.external_id) {
|
|
871
|
+
if (appName) {
|
|
872
|
+
config.external_id = `${appName}:${entry.name}`;
|
|
873
|
+
} else {
|
|
874
|
+
errors.push(`${entry.name}: missing external_id in config.json`);
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
if (!config.name) {
|
|
879
|
+
errors.push(`${entry.name}: missing name in config.json`);
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
const systemPrompt = readFileSync(promptPath, "utf-8");
|
|
883
|
+
agents.push({ agent: config, systemPrompt });
|
|
884
|
+
} catch (e) {
|
|
885
|
+
errors.push(`${entry.name}: failed to parse config.json - ${e}`);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (errors.length > 0) {
|
|
889
|
+
console.log(pc.red(" Agent errors:"));
|
|
890
|
+
for (const err of errors) {
|
|
891
|
+
console.log(pc.red(` \u2717 ${err}`));
|
|
892
|
+
}
|
|
893
|
+
throw new Error(`Found ${errors.length} agent error(s)`);
|
|
894
|
+
}
|
|
895
|
+
return agents;
|
|
896
|
+
}
|
|
897
|
+
async function planAgents(api, localAgents) {
|
|
898
|
+
const changes = [];
|
|
899
|
+
const remoteAgents = await api.listAgents();
|
|
900
|
+
const remoteByExternalId = new Map(
|
|
901
|
+
remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a])
|
|
902
|
+
);
|
|
903
|
+
for (const { agent, systemPrompt } of localAgents) {
|
|
904
|
+
const remote = remoteByExternalId.get(agent.external_id);
|
|
905
|
+
if (!remote) {
|
|
906
|
+
changes.push({ type: "create", resource: "agent", id: agent.external_id, name: agent.name });
|
|
907
|
+
} else {
|
|
908
|
+
const diffs = [];
|
|
909
|
+
if (remote.name !== agent.name) diffs.push("name");
|
|
910
|
+
if ((remote.description || "") !== (agent.description || "")) diffs.push("description");
|
|
911
|
+
if ((remote.system_prompt || "").trim() !== systemPrompt.trim()) diffs.push("system_prompt");
|
|
912
|
+
if (diffs.length > 0) {
|
|
913
|
+
changes.push({ type: "update", resource: "agent", id: agent.external_id, name: agent.name, details: `changed: ${diffs.join(", ")}` });
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return changes;
|
|
918
|
+
}
|
|
919
|
+
async function applyAgents(api, localAgents) {
|
|
920
|
+
let errors = 0;
|
|
921
|
+
const remoteAgents = await api.listAgents();
|
|
922
|
+
const remoteByExternalId = new Map(
|
|
923
|
+
remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a])
|
|
924
|
+
);
|
|
925
|
+
let skillMap = /* @__PURE__ */ new Map();
|
|
926
|
+
const hasSkillRefs = localAgents.some((a) => a.agent.skills && a.agent.skills.length > 0);
|
|
927
|
+
if (hasSkillRefs) {
|
|
928
|
+
try {
|
|
929
|
+
const skills = await api.listAgentSkills();
|
|
930
|
+
skillMap = new Map(skills.map((s) => [s.slug, s.id]));
|
|
931
|
+
} catch (e) {
|
|
932
|
+
console.log(pc.yellow(` \u26A0 Could not fetch skills for resolution: ${e}`));
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
for (const { agent, systemPrompt } of localAgents) {
|
|
936
|
+
const remote = remoteByExternalId.get(agent.external_id);
|
|
937
|
+
const skillIds = [];
|
|
938
|
+
if (agent.skills) {
|
|
939
|
+
for (const slug of agent.skills) {
|
|
940
|
+
const id = skillMap.get(slug);
|
|
941
|
+
if (id) {
|
|
942
|
+
skillIds.push(id);
|
|
943
|
+
} else {
|
|
944
|
+
console.log(pc.yellow(` \u26A0 Skill "${slug}" not found, skipping`));
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
const payload = {
|
|
949
|
+
external_id: agent.external_id,
|
|
950
|
+
name: agent.name,
|
|
951
|
+
description: agent.description || "",
|
|
952
|
+
system_prompt: systemPrompt,
|
|
953
|
+
skill_ids: skillIds
|
|
954
|
+
};
|
|
955
|
+
try {
|
|
956
|
+
if (remote) {
|
|
957
|
+
await api.updateAgent(remote.id, payload);
|
|
958
|
+
console.log(pc.green(" \u2713"), `${agent.name} (updated)`);
|
|
959
|
+
} else {
|
|
960
|
+
await api.createAgent(payload);
|
|
961
|
+
console.log(pc.green(" \u2713"), `${agent.name} (created)`);
|
|
962
|
+
}
|
|
963
|
+
} catch (e) {
|
|
964
|
+
console.log(pc.red(" \u2717"), `${agent.name}: ${e}`);
|
|
965
|
+
errors++;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return errors;
|
|
969
|
+
}
|
|
970
|
+
async function pullAgents(api, platformDir, filterName) {
|
|
971
|
+
const agentsDir = join(platformDir, "agents");
|
|
972
|
+
mkdirSync(agentsDir, { recursive: true });
|
|
973
|
+
const agents = await api.listAgents();
|
|
974
|
+
let skillIdToSlug = /* @__PURE__ */ new Map();
|
|
975
|
+
try {
|
|
976
|
+
const skills = await api.listAgentSkills();
|
|
977
|
+
skillIdToSlug = new Map(skills.map((s) => [s.id, s.slug]));
|
|
978
|
+
} catch {
|
|
979
|
+
}
|
|
980
|
+
for (const agent of agents) {
|
|
981
|
+
if (!agent.external_id || agent.managed) continue;
|
|
982
|
+
if (filterName && agent.external_id !== filterName && agent.name !== filterName) {
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
const dirName = agent.external_id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
986
|
+
const agentDir = join(agentsDir, dirName);
|
|
987
|
+
mkdirSync(agentDir, { recursive: true });
|
|
988
|
+
const skillSlugs = [];
|
|
989
|
+
if (agent.skill_ids) {
|
|
990
|
+
for (const id of agent.skill_ids) {
|
|
991
|
+
const slug = skillIdToSlug.get(id);
|
|
992
|
+
if (slug) skillSlugs.push(slug);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
const config = {
|
|
996
|
+
external_id: agent.external_id,
|
|
997
|
+
name: agent.name
|
|
998
|
+
};
|
|
999
|
+
if (agent.description) config.description = agent.description;
|
|
1000
|
+
if (skillSlugs.length > 0) config.skills = skillSlugs;
|
|
1001
|
+
writeFileSync(join(agentDir, "config.json"), JSON.stringify(config, null, 2) + "\n");
|
|
1002
|
+
writeFileSync(join(agentDir, "system_prompt.md"), agent.system_prompt || "");
|
|
1003
|
+
console.log(pc.green(" \u2713"), `${agent.name} \u2192 agents/${dirName}/`);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
826
1006
|
async function listResources(api, platformDir, filterType, appName) {
|
|
827
1007
|
const results = [];
|
|
828
1008
|
if (!filterType || filterType === "collections") {
|
|
@@ -907,6 +1087,32 @@ async function listResources(api, platformDir, filterType, appName) {
|
|
|
907
1087
|
}
|
|
908
1088
|
}
|
|
909
1089
|
}
|
|
1090
|
+
if (!filterType || filterType === "agents") {
|
|
1091
|
+
const localAgents = loadLocalAgents(platformDir, void 0, appName);
|
|
1092
|
+
const remoteAgents = await api.listAgents();
|
|
1093
|
+
const remoteByExternalId = new Map(remoteAgents.filter((a) => a.external_id && !a.managed).map((a) => [a.external_id, a]));
|
|
1094
|
+
const localIds = new Set(localAgents.map((a) => a.agent.external_id));
|
|
1095
|
+
for (const { agent, systemPrompt } of localAgents) {
|
|
1096
|
+
const remote = remoteByExternalId.get(agent.external_id);
|
|
1097
|
+
if (!remote) {
|
|
1098
|
+
results.push({ name: agent.name, type: "agents", status: "local-only" });
|
|
1099
|
+
} else {
|
|
1100
|
+
const promptChanged = (remote.system_prompt || "").trim() !== systemPrompt.trim();
|
|
1101
|
+
const nameChanged = remote.name !== agent.name;
|
|
1102
|
+
if (promptChanged || nameChanged) {
|
|
1103
|
+
results.push({ name: agent.name, type: "agents", status: "changed", details: promptChanged ? "system_prompt" : "name" });
|
|
1104
|
+
} else {
|
|
1105
|
+
results.push({ name: agent.name, type: "agents", status: "synced" });
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
for (const remote of remoteAgents) {
|
|
1110
|
+
if (!remote.external_id || remote.managed) continue;
|
|
1111
|
+
if (!localIds.has(remote.external_id)) {
|
|
1112
|
+
results.push({ name: remote.name, type: "agents", status: "remote-only" });
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
910
1116
|
return results;
|
|
911
1117
|
}
|
|
912
1118
|
function planCollectionDelete(collections, platformDir) {
|
|
@@ -1016,6 +1222,20 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
1016
1222
|
}
|
|
1017
1223
|
}
|
|
1018
1224
|
}
|
|
1225
|
+
if (!resourceType || resourceType === "agents") {
|
|
1226
|
+
try {
|
|
1227
|
+
const localAgents = loadLocalAgents(platformDir, resourceName || void 0, appName);
|
|
1228
|
+
const remoteAgents = await api.listAgents();
|
|
1229
|
+
const remoteByExternalId = new Map(remoteAgents.filter((a) => a.external_id).map((a) => [a.external_id, a]));
|
|
1230
|
+
for (const { agent } of localAgents) {
|
|
1231
|
+
const remote = remoteByExternalId.get(agent.external_id);
|
|
1232
|
+
if (remote) {
|
|
1233
|
+
toDelete.push({ type: "agent", id: agent.external_id, name: agent.name, remoteId: remote.id });
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
} catch {
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1019
1239
|
if (toDelete.length === 0) {
|
|
1020
1240
|
console.log(pc.green(" \u2713 No resources found to delete"));
|
|
1021
1241
|
return;
|
|
@@ -1038,10 +1258,20 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
1038
1258
|
return;
|
|
1039
1259
|
}
|
|
1040
1260
|
}
|
|
1261
|
+
const agents = toDelete.filter((r) => r.type === "agent");
|
|
1041
1262
|
const hooks = toDelete.filter((r) => r.type === "hook");
|
|
1042
1263
|
const automations = toDelete.filter((r) => r.type === "automation");
|
|
1043
1264
|
const collections = toDelete.filter((r) => r.type === "collection");
|
|
1044
1265
|
let errors = 0;
|
|
1266
|
+
for (const resource of agents) {
|
|
1267
|
+
try {
|
|
1268
|
+
await api.deleteAgent(resource.remoteId);
|
|
1269
|
+
console.log(pc.green(" \u2713"), `Deleted agent: ${resource.name}`);
|
|
1270
|
+
} catch (e) {
|
|
1271
|
+
console.log(pc.red(" \u2717"), `Failed to delete agent ${resource.name}: ${e}`);
|
|
1272
|
+
errors++;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1045
1275
|
for (const resource of hooks) {
|
|
1046
1276
|
try {
|
|
1047
1277
|
await api.deleteHook(resource.remoteId);
|
|
@@ -1250,6 +1480,43 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
1250
1480
|
console.log(` Trigger: ${local?.hook.trigger || remote?.event}`);
|
|
1251
1481
|
console.log(` Enabled: ${local?.hook.enabled !== false || remote?.enabled}`);
|
|
1252
1482
|
console.log();
|
|
1483
|
+
} else if (resourceType === "agents") {
|
|
1484
|
+
const localAgents = loadLocalAgents(platformDir, resourceName, appName);
|
|
1485
|
+
const remoteAgents = await api.listAgents();
|
|
1486
|
+
const local = localAgents[0];
|
|
1487
|
+
const remote = remoteAgents.find((a) => a.external_id === resourceName || a.name === resourceName);
|
|
1488
|
+
if (!local && !remote) {
|
|
1489
|
+
console.log(pc.red(` Agent "${resourceName}" not found`));
|
|
1490
|
+
process.exit(1);
|
|
1491
|
+
}
|
|
1492
|
+
console.log();
|
|
1493
|
+
console.log(pc.bold(` Agent: ${local?.agent.name || remote?.name}`));
|
|
1494
|
+
console.log();
|
|
1495
|
+
if (local && remote) {
|
|
1496
|
+
const promptChanged = (remote.system_prompt || "").trim() !== local.systemPrompt.trim();
|
|
1497
|
+
const nameChanged = remote.name !== local.agent.name;
|
|
1498
|
+
const descChanged = (remote.description || "") !== (local.agent.description || "");
|
|
1499
|
+
if (promptChanged || nameChanged || descChanged) {
|
|
1500
|
+
console.log(` Status: ${pc.yellow("changed")}`);
|
|
1501
|
+
} else {
|
|
1502
|
+
console.log(` Status: ${pc.green("synced")}`);
|
|
1503
|
+
}
|
|
1504
|
+
} else if (local) {
|
|
1505
|
+
console.log(` Status: ${pc.yellow("local only")}`);
|
|
1506
|
+
} else {
|
|
1507
|
+
console.log(` Status: ${pc.cyan("remote only")}`);
|
|
1508
|
+
}
|
|
1509
|
+
if (local?.agent.external_id || remote?.external_id) {
|
|
1510
|
+
console.log(` External ID: ${local?.agent.external_id || remote?.external_id}`);
|
|
1511
|
+
}
|
|
1512
|
+
if (local?.agent.description || remote?.description) {
|
|
1513
|
+
console.log(` Description: ${local?.agent.description || remote?.description}`);
|
|
1514
|
+
}
|
|
1515
|
+
if (local?.agent.skills?.length || remote?.skill_ids?.length) {
|
|
1516
|
+
const skills = local?.agent.skills || remote?.skill_ids || [];
|
|
1517
|
+
console.log(` Skills: ${skills.join(", ")}`);
|
|
1518
|
+
}
|
|
1519
|
+
console.log();
|
|
1253
1520
|
} else if (resourceType === "app") {
|
|
1254
1521
|
const projectRoot = findProjectRoot();
|
|
1255
1522
|
loadEnv(projectRoot);
|
|
@@ -1325,6 +1592,13 @@ async function plan(args) {
|
|
|
1325
1592
|
allChanges.push(...changes);
|
|
1326
1593
|
}
|
|
1327
1594
|
}
|
|
1595
|
+
if (!type || type === "agents") {
|
|
1596
|
+
const localAgents = loadLocalAgents(platformDir, name || void 0, appName);
|
|
1597
|
+
if (localAgents.length > 0) {
|
|
1598
|
+
const changes = await planAgents(api, localAgents);
|
|
1599
|
+
allChanges.push(...changes);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1328
1602
|
if (allChanges.length === 0) {
|
|
1329
1603
|
console.log(pc.green(" \u2713 No changes detected"));
|
|
1330
1604
|
console.log();
|
|
@@ -1416,6 +1690,17 @@ async function apply(args) {
|
|
|
1416
1690
|
process.exit(1);
|
|
1417
1691
|
}
|
|
1418
1692
|
}
|
|
1693
|
+
if (!type || type === "agents") {
|
|
1694
|
+
const localAgents = loadLocalAgents(platformDir, name || void 0, appName);
|
|
1695
|
+
if (localAgents.length > 0) {
|
|
1696
|
+
console.log(pc.bold(" Agents:"));
|
|
1697
|
+
totalErrors += await applyAgents(api, localAgents);
|
|
1698
|
+
console.log();
|
|
1699
|
+
} else if (name) {
|
|
1700
|
+
console.log(pc.red(` Agent "${name}" not found locally`));
|
|
1701
|
+
process.exit(1);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1419
1704
|
if (!type) {
|
|
1420
1705
|
try {
|
|
1421
1706
|
const projectRoot2 = findProjectRoot();
|
|
@@ -1463,6 +1748,11 @@ async function pull(args) {
|
|
|
1463
1748
|
await pullHooks(api, platformDir, name || void 0);
|
|
1464
1749
|
console.log();
|
|
1465
1750
|
}
|
|
1751
|
+
if (!type || type === "agents") {
|
|
1752
|
+
console.log(pc.bold(" Agents:"));
|
|
1753
|
+
await pullAgents(api, platformDir, name || void 0);
|
|
1754
|
+
console.log();
|
|
1755
|
+
}
|
|
1466
1756
|
console.log(pc.green(" Done!"));
|
|
1467
1757
|
console.log();
|
|
1468
1758
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createApiClient,
|
|
3
3
|
loadEnv
|
|
4
|
-
} from "./chunk-
|
|
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
|
|
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 {
|