@lumerahq/cli 0.30.1 → 0.30.3-dev.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.
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  createApiClient,
6
6
  isApiErrorStatus
7
- } from "./chunk-U3ZLIXGD.js";
7
+ } from "./chunk-KZEOP5YF.js";
8
8
  import {
9
9
  findProjectRoot,
10
10
  getAppName
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  userContextHeaders
3
- } from "./chunk-U3ZLIXGD.js";
3
+ } from "./chunk-KZEOP5YF.js";
4
4
  import {
5
5
  fetchWithRetry
6
6
  } from "./chunk-FJFIWC7G.js";
@@ -282,6 +282,12 @@ var ApiClient = class {
282
282
  const result = await this.request(`/api/agents?${params}`);
283
283
  return result.agents || [];
284
284
  }
285
+ async compileAgentPolicy(script) {
286
+ await this.request("/api/agents/policy/compile", {
287
+ method: "POST",
288
+ body: JSON.stringify({ script })
289
+ });
290
+ }
285
291
  async createAgent(def) {
286
292
  return this.request("/api/agents", {
287
293
  method: "POST",
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  deps,
3
3
  syncDeps
4
- } from "./chunk-YMWDIAUG.js";
4
+ } from "./chunk-2K2LYH5O.js";
5
5
  import "./chunk-2CR762KB.js";
6
- import "./chunk-U3ZLIXGD.js";
6
+ import "./chunk-KZEOP5YF.js";
7
7
  import "./chunk-JLVVHTBY.js";
8
8
  import "./chunk-FJFIWC7G.js";
9
9
  import "./chunk-PNKVD2UK.js";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  dev
3
- } from "./chunk-LVIIWE44.js";
3
+ } from "./chunk-ASPEK3I6.js";
4
4
  import {
5
5
  syncDeps
6
- } from "./chunk-YMWDIAUG.js";
6
+ } from "./chunk-2K2LYH5O.js";
7
7
  import {
8
8
  loadEnv
9
9
  } from "./chunk-2CR762KB.js";
10
10
  import {
11
11
  createApiClient
12
- } from "./chunk-U3ZLIXGD.js";
12
+ } from "./chunk-KZEOP5YF.js";
13
13
  import {
14
14
  findProjectRoot,
15
15
  getApiUrl,
package/dist/index.js CHANGED
@@ -240,32 +240,32 @@ async function main() {
240
240
  switch (command) {
241
241
  // Resource commands
242
242
  case "plan":
243
- await import("./resources-5YWEX6DG.js").then((m) => m.plan(args.slice(1)));
243
+ await import("./resources-ZSFI5CJM.js").then((m) => m.plan(args.slice(1)));
244
244
  break;
245
245
  case "apply":
246
- await import("./resources-5YWEX6DG.js").then((m) => m.apply(args.slice(1)));
246
+ await import("./resources-ZSFI5CJM.js").then((m) => m.apply(args.slice(1)));
247
247
  break;
248
248
  case "pull":
249
- await import("./resources-5YWEX6DG.js").then((m) => m.pull(args.slice(1)));
249
+ await import("./resources-ZSFI5CJM.js").then((m) => m.pull(args.slice(1)));
250
250
  break;
251
251
  case "destroy":
252
- await import("./resources-5YWEX6DG.js").then((m) => m.destroy(args.slice(1)));
252
+ await import("./resources-ZSFI5CJM.js").then((m) => m.destroy(args.slice(1)));
253
253
  break;
254
254
  case "list":
255
- await import("./resources-5YWEX6DG.js").then((m) => m.list(args.slice(1)));
255
+ await import("./resources-ZSFI5CJM.js").then((m) => m.list(args.slice(1)));
256
256
  break;
257
257
  case "show":
258
- await import("./resources-5YWEX6DG.js").then((m) => m.show(args.slice(1)));
258
+ await import("./resources-ZSFI5CJM.js").then((m) => m.show(args.slice(1)));
259
259
  break;
260
260
  case "diff":
261
- await import("./resources-5YWEX6DG.js").then((m) => m.diff(args.slice(1)));
261
+ await import("./resources-ZSFI5CJM.js").then((m) => m.diff(args.slice(1)));
262
262
  break;
263
263
  // Development
264
264
  case "dev":
265
- await import("./dev-IWTA5G5M.js").then((m) => m.dev(args.slice(1)));
265
+ await import("./dev-XBOHUDPR.js").then((m) => m.dev(args.slice(1)));
266
266
  break;
267
267
  case "run":
268
- await import("./run-SLLDX3RF.js").then((m) => m.run(args.slice(1)));
268
+ await import("./run-EBTA3FKD.js").then((m) => m.run(args.slice(1)));
269
269
  break;
270
270
  case "functions":
271
271
  await import("./functions-55J6E2RF.js").then(
@@ -279,10 +279,10 @@ async function main() {
279
279
  break;
280
280
  // Project
281
281
  case "init":
282
- await import("./init-I4FECTEL.js").then((m) => m.init(args.slice(1)));
282
+ await import("./init-L6D5OHMY.js").then((m) => m.init(args.slice(1)));
283
283
  break;
284
284
  case "register":
285
- await import("./register-OBQZPOGC.js").then((m) => m.register(args.slice(1)));
285
+ await import("./register-SBXONOFY.js").then((m) => m.register(args.slice(1)));
286
286
  break;
287
287
  case "templates":
288
288
  await import("./templates-LNUOTNLN.js").then((m) => m.templates(subcommand, args.slice(2)));
@@ -299,7 +299,7 @@ async function main() {
299
299
  break;
300
300
  // Dependencies
301
301
  case "deps":
302
- await import("./deps-NHVL4DMD.js").then((m) => m.deps(args.slice(1)));
302
+ await import("./deps-22CUIP2M.js").then((m) => m.deps(args.slice(1)));
303
303
  break;
304
304
  // Feature flags (read the in-sandbox snapshot)
305
305
  case "flags":
@@ -12,7 +12,7 @@ import {
12
12
  import {
13
13
  createApiClient,
14
14
  validateProjectNameLength
15
- } from "./chunk-U3ZLIXGD.js";
15
+ } from "./chunk-KZEOP5YF.js";
16
16
  import {
17
17
  getToken,
18
18
  init_auth,
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  createApiClient,
9
9
  validateProjectExternalId
10
- } from "./chunk-U3ZLIXGD.js";
10
+ } from "./chunk-KZEOP5YF.js";
11
11
  import {
12
12
  findProjectRoot,
13
13
  getAppName,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  deploy
3
- } from "./chunk-LVIIWE44.js";
3
+ } from "./chunk-ASPEK3I6.js";
4
4
  import {
5
5
  syncDeps
6
- } from "./chunk-YMWDIAUG.js";
6
+ } from "./chunk-2K2LYH5O.js";
7
7
  import {
8
8
  loadEnv
9
9
  } from "./chunk-2CR762KB.js";
@@ -12,7 +12,7 @@ import {
12
12
  createApiClient,
13
13
  isApiErrorStatus,
14
14
  validateCollectionNameLength
15
- } from "./chunk-U3ZLIXGD.js";
15
+ } from "./chunk-KZEOP5YF.js";
16
16
  import {
17
17
  findProjectRoot,
18
18
  getApiUrl,
@@ -38,6 +38,32 @@ import { join as join2, resolve } from "path";
38
38
  import { existsSync, readFileSync, readdirSync } from "fs";
39
39
  import { basename, dirname, join } from "path";
40
40
 
41
+ // src/lib/lint/rules/agent-policy-compile.ts
42
+ function compileError(target, message) {
43
+ return {
44
+ ruleId: "agent-policy-compile",
45
+ target,
46
+ severity: "error",
47
+ message
48
+ };
49
+ }
50
+ var agentPolicyCompileRule = {
51
+ id: "agent-policy-compile",
52
+ description: "Verifies agent policy scripts compile with the backend runtime compiler.",
53
+ appliesTo: ["agent-policy"],
54
+ async check(target, ctx) {
55
+ if (!target.source.trim() || !ctx.api) return [];
56
+ const script = ctx.appName ? target.source.replaceAll("{{app}}", ctx.appName) : target.source;
57
+ try {
58
+ await ctx.api.compileAgentPolicy(script);
59
+ } catch (err) {
60
+ const detail = err instanceof ApiError ? err.body.trim() : err instanceof Error ? err.message : String(err);
61
+ return [compileError(target, `Agent policy failed to compile on the backend: ${detail}`)];
62
+ }
63
+ return [];
64
+ }
65
+ };
66
+
41
67
  // src/lib/lint/rules/automation-config.ts
42
68
  function isRecord(value) {
43
69
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -776,6 +802,7 @@ var llmImportRule = {
776
802
 
777
803
  // src/lib/lint/registry.ts
778
804
  var ALL_RULES = [
805
+ agentPolicyCompileRule,
779
806
  automationConfigRule,
780
807
  automationMainGuardRule,
781
808
  collectionSchemaRule,
@@ -889,6 +916,42 @@ function buildHookTargets(platformDir, filterName) {
889
916
  }
890
917
  return targets;
891
918
  }
919
+ function buildAgentPolicyTargets(platformDir, filterName, appName) {
920
+ const agentsDir = join(platformDir, "agents");
921
+ if (!existsSync(agentsDir)) return [];
922
+ const targets = [];
923
+ for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
924
+ if (!entry.isDirectory()) continue;
925
+ const agentDir = join(agentsDir, entry.name);
926
+ const policyPath = join(agentDir, "policy.js");
927
+ if (!existsSync(policyPath)) continue;
928
+ let externalID = appName ? `${appName}:${entry.name}` : "";
929
+ let displayName = "";
930
+ const configPath = join(agentDir, "config.json");
931
+ if (existsSync(configPath)) {
932
+ try {
933
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
934
+ if (typeof config.external_id === "string" && config.external_id.trim()) {
935
+ externalID = config.external_id;
936
+ }
937
+ if (typeof config.name === "string") {
938
+ displayName = config.name;
939
+ }
940
+ } catch {
941
+ }
942
+ }
943
+ if (filterName && entry.name !== filterName && externalID !== filterName && displayName !== filterName) {
944
+ continue;
945
+ }
946
+ targets.push({
947
+ kind: "agent-policy",
948
+ name: externalID || displayName || entry.name,
949
+ filePath: policyPath,
950
+ source: readFileSync(policyPath, "utf-8")
951
+ });
952
+ }
953
+ return targets;
954
+ }
892
955
 
893
956
  // src/commands/resources.ts
894
957
  init_auth();
@@ -940,6 +1003,18 @@ async function lintHookFiles(api, projectRoot, platformDir, appName, filterName)
940
1003
  throw new Error(`Found ${result.errors.length} hook lint error(s)`);
941
1004
  }
942
1005
  }
1006
+ async function lintAgentPolicyFiles(api, projectRoot, platformDir, appName, filterName) {
1007
+ const result = await safeLintPass("agent policy", async () => {
1008
+ const targets = buildAgentPolicyTargets(platformDir, filterName, appName);
1009
+ if (targets.length === 0) return [];
1010
+ return runLint({ projectRoot, targets, api, appName });
1011
+ });
1012
+ if (!result || result.issues.length === 0) return;
1013
+ safePrintLint(result.issues, projectRoot);
1014
+ if (result.errors.length > 0) {
1015
+ throw new Error(`Found ${result.errors.length} agent policy lint error(s)`);
1016
+ }
1017
+ }
943
1018
  var PACKAGE_MANAGERS = ["bun", "pnpm", "yarn", "npm"];
944
1019
  var PACKAGE_MANAGER_VALUE_FLAGS = /* @__PURE__ */ new Set(["--package-manager"]);
945
1020
  function isPackageManager(value) {
@@ -2177,6 +2252,8 @@ async function applyAutomations(api, localAutomations, projectId) {
2177
2252
  const remoteByExternalId = new Map(remoteAutomations.filter((a) => a.external_id).map((a) => [a.external_id, a]));
2178
2253
  for (const { automation, code } of localAutomations) {
2179
2254
  const remote = remoteByExternalId.get(automation.external_id);
2255
+ const appliedAction = remote ? "updated" : "created";
2256
+ let baseApplied = false;
2180
2257
  const payload = {
2181
2258
  external_id: automation.external_id,
2182
2259
  name: automation.name,
@@ -2194,35 +2271,35 @@ async function applyAutomations(api, localAutomations, projectId) {
2194
2271
  if (remote) {
2195
2272
  await api.updateAutomation(remote.id, payload);
2196
2273
  automationId = remote.id;
2197
- console.log(pc2.green(" \u2713"), `${automation.name} (updated)`);
2198
2274
  } else {
2199
2275
  const created = await api.createAutomation(payload);
2200
2276
  automationId = created.id;
2201
- console.log(pc2.green(" \u2713"), `${automation.name} (created)`);
2202
2277
  }
2278
+ baseApplied = true;
2203
2279
  const localPresets = automation.inputs?.presets;
2280
+ let current;
2204
2281
  if (localPresets) {
2205
2282
  await syncPresets(api, automationId, localPresets);
2206
2283
  }
2207
2284
  if (automation.schedule) {
2208
- await setSchedule(api, automationId, automation.schedule, localPresets || {});
2285
+ current = await setSchedule(api, automationId, automation.schedule, localPresets || {});
2209
2286
  } else if (remote?.schedule) {
2210
2287
  await api.updateAutomation(automationId, { schedule: "", schedule_tz: "" });
2211
2288
  console.log(pc2.dim(` Cleared schedule`));
2212
2289
  }
2213
2290
  if (localPresets) {
2214
- const current = await api.getAutomation(automationId).catch(() => null);
2215
- if (current) {
2216
- await deleteStalePresets(
2217
- api,
2218
- automationId,
2219
- localPresets,
2220
- current.schedule_preset_id ? /* @__PURE__ */ new Set([current.schedule_preset_id]) : /* @__PURE__ */ new Set()
2221
- );
2222
- }
2291
+ current ??= await api.getAutomation(automationId);
2292
+ await deleteStalePresets(
2293
+ api,
2294
+ automationId,
2295
+ localPresets,
2296
+ current.schedule_preset_id ? /* @__PURE__ */ new Set([current.schedule_preset_id]) : /* @__PURE__ */ new Set()
2297
+ );
2223
2298
  }
2299
+ console.log(pc2.green(" \u2713"), `${automation.name} (${appliedAction})`);
2224
2300
  } catch (e) {
2225
- console.log(pc2.red(" \u2717"), `${automation.name}: ${e}`);
2301
+ const partialState = baseApplied ? ` (${appliedAction}, configuration failed)` : "";
2302
+ console.log(pc2.red(" \u2717"), `${automation.name}${partialState}: ${e}`);
2226
2303
  errors++;
2227
2304
  }
2228
2305
  }
@@ -2234,16 +2311,20 @@ async function syncPresets(api, automationId, localPresets) {
2234
2311
  for (const [presetKey, preset] of Object.entries(localPresets)) {
2235
2312
  const presetName = preset.label || presetKey;
2236
2313
  const existing = remoteByName.get(presetName);
2237
- try {
2238
- if (existing) {
2314
+ if (existing) {
2315
+ try {
2239
2316
  await api.updatePreset(existing.id, { name: presetName, inputs: preset.inputs });
2240
2317
  console.log(pc2.dim(` Updated preset: ${presetName}`));
2241
- } else {
2318
+ } catch (e) {
2319
+ throw new Error(`Failed to update preset '${presetName}': ${String(e)}`);
2320
+ }
2321
+ } else {
2322
+ try {
2242
2323
  await api.createPreset(automationId, { name: presetName, inputs: preset.inputs });
2243
2324
  console.log(pc2.dim(` Created preset: ${presetName}`));
2325
+ } catch (e) {
2326
+ throw new Error(`Failed to create preset '${presetName}': ${String(e)}`);
2244
2327
  }
2245
- } catch (e) {
2246
- console.log(pc2.yellow(` \u26A0 Failed to sync preset ${presetName}: ${e}`));
2247
2328
  }
2248
2329
  }
2249
2330
  }
@@ -2260,7 +2341,7 @@ async function deleteStalePresets(api, automationId, localPresets, preservePrese
2260
2341
  await api.deletePreset(remote.id);
2261
2342
  console.log(pc2.dim(` Deleted preset: ${remote.name}`));
2262
2343
  } catch (e) {
2263
- console.log(pc2.yellow(` \u26A0 Failed to delete preset ${remote.name}: ${e}`));
2344
+ throw new Error(`Failed to delete preset '${remote.name}': ${String(e)}`);
2264
2345
  }
2265
2346
  }
2266
2347
  }
@@ -2269,19 +2350,28 @@ async function setSchedule(api, automationId, schedule, localPresets) {
2269
2350
  const remotePresets = await api.listPresets(automationId);
2270
2351
  const preset = remotePresets.find((p) => p.name === presetName);
2271
2352
  if (!preset) {
2272
- console.log(pc2.yellow(` \u26A0 Schedule preset '${schedule.preset}' not found, skipping schedule`));
2273
- return;
2353
+ throw new Error(`Schedule preset '${presetName}' not found`);
2274
2354
  }
2355
+ const expectedSchedule = {
2356
+ schedule: schedule.cron,
2357
+ schedule_tz: schedule.timezone || "UTC",
2358
+ schedule_preset_id: preset.id
2359
+ };
2360
+ await api.updateAutomation(automationId, expectedSchedule);
2361
+ let persisted;
2275
2362
  try {
2276
- await api.updateAutomation(automationId, {
2277
- schedule: schedule.cron,
2278
- schedule_tz: schedule.timezone || "UTC",
2279
- schedule_preset_id: preset.id
2280
- });
2281
- console.log(pc2.dim(` Set schedule: ${schedule.cron}`));
2363
+ persisted = await api.getAutomation(automationId);
2282
2364
  } catch (e) {
2283
- console.log(pc2.yellow(` \u26A0 Failed to set schedule: ${e}`));
2365
+ throw new Error(`Failed to verify persisted schedule: ${String(e)}`);
2366
+ }
2367
+ const mismatches = Object.keys(expectedSchedule).filter((field) => persisted[field] !== expectedSchedule[field]).map(
2368
+ (field) => `${field} expected ${JSON.stringify(expectedSchedule[field])}, got ${JSON.stringify(persisted[field])}`
2369
+ );
2370
+ if (mismatches.length > 0) {
2371
+ throw new Error(`Schedule update was not persisted: ${mismatches.join("; ")}`);
2284
2372
  }
2373
+ console.log(pc2.dim(` Set schedule: ${schedule.cron}`));
2374
+ return persisted;
2285
2375
  }
2286
2376
  async function applyHooks(api, localHooks, collections, projectId) {
2287
2377
  let errors = 0;
@@ -3733,6 +3823,9 @@ async function plan(args) {
3733
3823
  if (!type || type === "hooks") {
3734
3824
  await lintHookFiles(api, projectRoot, platformDir, appName, name || void 0);
3735
3825
  }
3826
+ if (!type || type === "agents") {
3827
+ await lintAgentPolicyFiles(api, projectRoot, platformDir, appName, name || void 0);
3828
+ }
3736
3829
  await syncDeps(projectRoot, { ignorePermissionDenied: true });
3737
3830
  const allChanges = [];
3738
3831
  let planValidationErrors = 0;
@@ -3900,6 +3993,9 @@ async function apply(args) {
3900
3993
  if (!type || type === "hooks") {
3901
3994
  await lintHookFiles(api, projectRoot, platformDir, appName, name || void 0);
3902
3995
  }
3996
+ if (!type || type === "agents") {
3997
+ await lintAgentPolicyFiles(api, projectRoot, platformDir, appName, name || void 0);
3998
+ }
3903
3999
  await syncDeps(projectRoot, { ignorePermissionDenied: true });
3904
4000
  let collections;
3905
4001
  try {
@@ -4519,6 +4615,7 @@ async function diff(args) {
4519
4615
  export {
4520
4616
  apply,
4521
4617
  applyAgents,
4618
+ applyAutomations,
4522
4619
  applyCollections,
4523
4620
  applySkills,
4524
4621
  convertCollectionToApiFormat,
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2CR762KB.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-U3ZLIXGD.js";
6
+ } from "./chunk-KZEOP5YF.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.30.1",
3
+ "version": "0.30.3-dev.0",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {
@@ -12,8 +12,8 @@ importers:
12
12
  .:
13
13
  dependencies:
14
14
  '@lumerahq/ui':
15
- specifier: ^0.14.0
16
- version: 0.14.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
15
+ specifier: ^0.14.1
16
+ version: 0.14.1(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
17
17
  '@tanstack/react-query':
18
18
  specifier: ^5.90.11
19
19
  version: 5.101.0(react@19.2.7)
@@ -457,8 +457,8 @@ packages:
457
457
  '@jridgewell/trace-mapping@0.3.31':
458
458
  resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
459
459
 
460
- '@lumerahq/ui@0.14.0':
461
- resolution: {integrity: sha512-zV3HaeNkUJwieStWVf44UJzb+BDES+Dhjr5DfBxqCcc2mlKPm19TjWCjett6GfweFe9NhSgoIOk1txUm76B/GA==}
460
+ '@lumerahq/ui@0.14.1':
461
+ resolution: {integrity: sha512-GCyW9xQJ9cQzYP2wBTogGdUGTwIcwQvz5YXAp2AafaKiNSCy9f72PeXdEjNJfcTDbpatTiECfwJiT5Kw+wzqoQ==}
462
462
  peerDependencies:
463
463
  react: ^18.0.0 || ^19.0.0
464
464
  react-dom: ^18.0.0 || ^19.0.0
@@ -2311,7 +2311,7 @@ snapshots:
2311
2311
  '@jridgewell/resolve-uri': 3.1.2
2312
2312
  '@jridgewell/sourcemap-codec': 1.5.5
2313
2313
 
2314
- '@lumerahq/ui@0.14.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
2314
+ '@lumerahq/ui@0.14.1(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
2315
2315
  dependencies:
2316
2316
  '@base-ui/react': 1.5.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
2317
2317
  '@tanstack/react-query': 5.101.0(react@19.2.7)