@lumerahq/cli 0.35.1 → 0.35.2
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.
package/dist/index.js
CHANGED
|
@@ -243,25 +243,25 @@ async function main() {
|
|
|
243
243
|
switch (command) {
|
|
244
244
|
// Resource commands
|
|
245
245
|
case "plan":
|
|
246
|
-
await import("./resources-
|
|
246
|
+
await import("./resources-E32DYKLL.js").then((m) => m.plan(args.slice(1)));
|
|
247
247
|
break;
|
|
248
248
|
case "apply":
|
|
249
|
-
await import("./resources-
|
|
249
|
+
await import("./resources-E32DYKLL.js").then((m) => m.apply(args.slice(1)));
|
|
250
250
|
break;
|
|
251
251
|
case "pull":
|
|
252
|
-
await import("./resources-
|
|
252
|
+
await import("./resources-E32DYKLL.js").then((m) => m.pull(args.slice(1)));
|
|
253
253
|
break;
|
|
254
254
|
case "destroy":
|
|
255
|
-
await import("./resources-
|
|
255
|
+
await import("./resources-E32DYKLL.js").then((m) => m.destroy(args.slice(1)));
|
|
256
256
|
break;
|
|
257
257
|
case "list":
|
|
258
|
-
await import("./resources-
|
|
258
|
+
await import("./resources-E32DYKLL.js").then((m) => m.list(args.slice(1)));
|
|
259
259
|
break;
|
|
260
260
|
case "show":
|
|
261
|
-
await import("./resources-
|
|
261
|
+
await import("./resources-E32DYKLL.js").then((m) => m.show(args.slice(1)));
|
|
262
262
|
break;
|
|
263
263
|
case "diff":
|
|
264
|
-
await import("./resources-
|
|
264
|
+
await import("./resources-E32DYKLL.js").then((m) => m.diff(args.slice(1)));
|
|
265
265
|
break;
|
|
266
266
|
// Development
|
|
267
267
|
case "dev":
|
|
@@ -382,7 +382,7 @@ var automationMainGuardRule = {
|
|
|
382
382
|
if (entrypoint === null) return [];
|
|
383
383
|
const guard = findTopLevelMainGuard(target.source);
|
|
384
384
|
if (guard === null) return [];
|
|
385
|
-
const
|
|
385
|
+
const warning3 = {
|
|
386
386
|
ruleId: "automation-main-guard",
|
|
387
387
|
target,
|
|
388
388
|
severity: "warning",
|
|
@@ -390,7 +390,7 @@ var automationMainGuardRule = {
|
|
|
390
390
|
snippet: guard.snippet,
|
|
391
391
|
message: `Function automations must not contain a top-level __main__ guard. Lumera invokes "${entrypoint}" automatically; remove the guard.`
|
|
392
392
|
};
|
|
393
|
-
return [
|
|
393
|
+
return [warning3];
|
|
394
394
|
}
|
|
395
395
|
};
|
|
396
396
|
|
|
@@ -407,6 +407,14 @@ function error(target, message, snippet) {
|
|
|
407
407
|
snippet
|
|
408
408
|
};
|
|
409
409
|
}
|
|
410
|
+
function warning(target, message) {
|
|
411
|
+
return {
|
|
412
|
+
ruleId: "collection-schema",
|
|
413
|
+
target,
|
|
414
|
+
severity: "warning",
|
|
415
|
+
message
|
|
416
|
+
};
|
|
417
|
+
}
|
|
410
418
|
var collectionSchemaRule = {
|
|
411
419
|
id: "collection-schema",
|
|
412
420
|
description: "Validates local collection JSON files before planning or applying resources.",
|
|
@@ -466,6 +474,19 @@ var collectionSchemaRule = {
|
|
|
466
474
|
if (typeof field.type !== "string" || field.type.trim() === "") {
|
|
467
475
|
issues.push(error(target, `Field ${typeof field.name === "string" ? `"${field.name}"` : `at index ${i}`} is missing required string field "type".`));
|
|
468
476
|
}
|
|
477
|
+
if (field.type === "lumera_file" || field.type === "file") {
|
|
478
|
+
const label = typeof field.name === "string" ? `"${field.name}"` : `at index ${i}`;
|
|
479
|
+
const ignored = [
|
|
480
|
+
...isRecord3(field.options) && field.options.maxSelect !== void 0 ? ['"options.maxSelect"'] : [],
|
|
481
|
+
...field.multiple !== void 0 ? ['"multiple"'] : []
|
|
482
|
+
];
|
|
483
|
+
if (ignored.length > 0) {
|
|
484
|
+
issues.push(warning(
|
|
485
|
+
target,
|
|
486
|
+
`File field ${label} sets ${ignored.join(" and ")}, which collection files ignore; it deploys with maxSelect ${field.maxSelect ?? 1}. Set top-level "maxSelect" instead. A file field's maxSelect cannot change after it is first deployed.`
|
|
487
|
+
));
|
|
488
|
+
}
|
|
489
|
+
}
|
|
469
490
|
}
|
|
470
491
|
}
|
|
471
492
|
if (audit !== void 0 && typeof audit !== "boolean") {
|
|
@@ -817,7 +838,7 @@ function error2(target, message, line) {
|
|
|
817
838
|
...line !== void 0 ? { line } : {}
|
|
818
839
|
};
|
|
819
840
|
}
|
|
820
|
-
function
|
|
841
|
+
function warning2(target, message, line) {
|
|
821
842
|
return {
|
|
822
843
|
ruleId: "hook-verify",
|
|
823
844
|
target,
|
|
@@ -897,7 +918,7 @@ var hookVerifyRule = {
|
|
|
897
918
|
if (errors.length > 0) return errors;
|
|
898
919
|
const advisories = [];
|
|
899
920
|
if (finding.legacyHandlerPattern) {
|
|
900
|
-
advisories.push(
|
|
921
|
+
advisories.push(warning2(target, LEGACY_HANDLER_MIGRATION_HINT));
|
|
901
922
|
}
|
|
902
923
|
let script = finding.scriptBody ?? "";
|
|
903
924
|
if (ctx.appName) {
|
|
@@ -1193,16 +1214,19 @@ async function safeLintPass(label, build) {
|
|
|
1193
1214
|
return null;
|
|
1194
1215
|
}
|
|
1195
1216
|
}
|
|
1196
|
-
async function lintCollectionFiles(projectRoot, platformDir, filterName) {
|
|
1217
|
+
async function lintCollectionFiles(projectRoot, platformDir, filterName, printWarnings = true) {
|
|
1197
1218
|
const result = await safeLintPass(
|
|
1198
1219
|
"collection",
|
|
1199
1220
|
async () => runLint({ projectRoot, targets: buildCollectionTargets(platformDir, filterName) })
|
|
1200
1221
|
);
|
|
1201
|
-
if (!result || result.issues.length === 0) return;
|
|
1202
|
-
|
|
1222
|
+
if (!result || result.issues.length === 0) return [];
|
|
1223
|
+
if (printWarnings || result.errors.length > 0) {
|
|
1224
|
+
safePrintLint(result.issues, projectRoot);
|
|
1225
|
+
}
|
|
1203
1226
|
if (result.errors.length > 0) {
|
|
1204
1227
|
throw new Error(`Found ${result.errors.length} collection lint error(s)`);
|
|
1205
1228
|
}
|
|
1229
|
+
return result.issues;
|
|
1206
1230
|
}
|
|
1207
1231
|
async function lintHookFiles(api, projectRoot, platformDir, appName, filterName) {
|
|
1208
1232
|
const result = await safeLintPass("hook", async () => {
|
|
@@ -2111,6 +2135,20 @@ function fieldsDiffer(local, remote) {
|
|
|
2111
2135
|
}
|
|
2112
2136
|
return false;
|
|
2113
2137
|
}
|
|
2138
|
+
function lumeraFileMaxSelectErrors(local, remote) {
|
|
2139
|
+
const remoteFields = new Map(remote.schema.map((f) => [f.name, f]));
|
|
2140
|
+
const errors = [];
|
|
2141
|
+
for (const field of local.fields) {
|
|
2142
|
+
const deployed = remoteFields.get(field.name);
|
|
2143
|
+
if (mapFieldType(field.type) !== "lumera_file" || deployed?.type !== "lumera_file") continue;
|
|
2144
|
+
const deployedMaxSelect = Math.max(Number(normalizeFieldOption("lumera_file", "maxSelect", deployed.options?.maxSelect)), 1);
|
|
2145
|
+
const requestedMaxSelect = Math.max(Number(normalizeFieldOption("lumera_file", "maxSelect", field.maxSelect)), 1);
|
|
2146
|
+
if (deployedMaxSelect === requestedMaxSelect) continue;
|
|
2147
|
+
const requested = field.maxSelect === void 0 ? "omitting maxSelect (defaults to 1)" : `maxSelect ${requestedMaxSelect}`;
|
|
2148
|
+
errors.push(`File field "${field.name}" is deployed with maxSelect ${deployedMaxSelect}; ${requested} would change it, which is not supported. Keep maxSelect ${deployedMaxSelect}.`);
|
|
2149
|
+
}
|
|
2150
|
+
return errors;
|
|
2151
|
+
}
|
|
2114
2152
|
function localIndexesToSql(local, appName) {
|
|
2115
2153
|
if (!local.indexes || local.indexes.length === 0) return [];
|
|
2116
2154
|
return local.indexes.map((idx) => buildIndexSql(local, idx, appName)).sort();
|
|
@@ -2224,13 +2262,15 @@ async function planCollections(api, localCollections, appName, projectId) {
|
|
|
2224
2262
|
const f = remoteFieldMap.get(name);
|
|
2225
2263
|
fieldDetails.push({ action: "-", name: f.name, type: f.type, required: f.required });
|
|
2226
2264
|
}
|
|
2265
|
+
const errors = lumeraFileMaxSelectErrors(local, remote);
|
|
2227
2266
|
changes.push({
|
|
2228
2267
|
type: "update",
|
|
2229
2268
|
resource: "collection",
|
|
2230
2269
|
id: local.id,
|
|
2231
2270
|
name: local.name,
|
|
2232
2271
|
details: details.join(", "),
|
|
2233
|
-
fieldDetails
|
|
2272
|
+
fieldDetails,
|
|
2273
|
+
...errors.length > 0 ? { errors } : {}
|
|
2234
2274
|
});
|
|
2235
2275
|
}
|
|
2236
2276
|
}
|
|
@@ -4422,8 +4462,9 @@ async function plan(args) {
|
|
|
4422
4462
|
console.log(pc2.cyan(pc2.bold(" Plan")));
|
|
4423
4463
|
console.log(pc2.dim(" Comparing local files to remote state..."));
|
|
4424
4464
|
console.log();
|
|
4465
|
+
let collectionLintWarnings = [];
|
|
4425
4466
|
if (!type || type === "collections") {
|
|
4426
|
-
await lintCollectionFiles(projectRoot, platformDir, name || void 0);
|
|
4467
|
+
collectionLintWarnings = await lintCollectionFiles(projectRoot, platformDir, name || void 0, false);
|
|
4427
4468
|
}
|
|
4428
4469
|
if (!type || type === "hooks") {
|
|
4429
4470
|
await lintHookFiles(api, projectRoot, platformDir, appName, name || void 0);
|
|
@@ -4498,6 +4539,7 @@ async function plan(args) {
|
|
|
4498
4539
|
}
|
|
4499
4540
|
}
|
|
4500
4541
|
const lintWarnings = [
|
|
4542
|
+
...collectionLintWarnings,
|
|
4501
4543
|
...agentLintWarnings,
|
|
4502
4544
|
...await safeLint(projectRoot, localAutomations)
|
|
4503
4545
|
];
|
|
@@ -4518,6 +4560,7 @@ async function plan(args) {
|
|
|
4518
4560
|
safePrintLint(lintWarnings, projectRoot);
|
|
4519
4561
|
return;
|
|
4520
4562
|
}
|
|
4563
|
+
const collectionErrorCount = allChanges.reduce((count, change) => count + (change.errors?.length ?? 0), 0);
|
|
4521
4564
|
if (jsonOutput) {
|
|
4522
4565
|
const warnings = [];
|
|
4523
4566
|
for (const change of allChanges) {
|
|
@@ -4528,8 +4571,9 @@ async function plan(args) {
|
|
|
4528
4571
|
}
|
|
4529
4572
|
}
|
|
4530
4573
|
}
|
|
4531
|
-
|
|
4532
|
-
|
|
4574
|
+
const validationErrors = planValidationErrors + collectionErrorCount;
|
|
4575
|
+
console.log(JSON.stringify({ changes: allChanges, warnings, validationErrors, lintWarnings: serializeLintWarnings(lintWarnings, projectRoot) }));
|
|
4576
|
+
if (validationErrors > 0) process.exitCode = 1;
|
|
4533
4577
|
return;
|
|
4534
4578
|
}
|
|
4535
4579
|
console.log(pc2.bold(" Changes:"));
|
|
@@ -4547,6 +4591,10 @@ async function plan(args) {
|
|
|
4547
4591
|
}
|
|
4548
4592
|
console.log();
|
|
4549
4593
|
}
|
|
4594
|
+
if (change.errors && change.errors.length > 0) {
|
|
4595
|
+
for (const message of change.errors) console.log(pc2.red(` \u2717 ${message}`));
|
|
4596
|
+
console.log();
|
|
4597
|
+
}
|
|
4550
4598
|
if (change.textDiffs && change.textDiffs.length > 0) {
|
|
4551
4599
|
const maxDiffLines = 20;
|
|
4552
4600
|
for (const td of change.textDiffs) {
|
|
@@ -4572,6 +4620,11 @@ async function plan(args) {
|
|
|
4572
4620
|
safePrintLint(lintWarnings, projectRoot);
|
|
4573
4621
|
if (planValidationErrors > 0) {
|
|
4574
4622
|
console.log(pc2.red(` \u2717 Fix ${planValidationErrors} agent skill validation error${planValidationErrors === 1 ? "" : "s"} before applying.`));
|
|
4623
|
+
}
|
|
4624
|
+
if (collectionErrorCount > 0) {
|
|
4625
|
+
console.log(pc2.red(` \u2717 Fix ${collectionErrorCount} collection error${collectionErrorCount === 1 ? "" : "s"} before applying.`));
|
|
4626
|
+
}
|
|
4627
|
+
if (planValidationErrors > 0 || collectionErrorCount > 0) {
|
|
4575
4628
|
process.exitCode = 1;
|
|
4576
4629
|
} else {
|
|
4577
4630
|
console.log(pc2.dim(` Run 'lumera apply' to apply these changes.`));
|
|
@@ -4679,6 +4732,15 @@ async function apply(args) {
|
|
|
4679
4732
|
console.log();
|
|
4680
4733
|
process.exit(1);
|
|
4681
4734
|
}
|
|
4735
|
+
const collectionErrors = allChanges.flatMap((change) => (change.errors ?? []).map((message) => `${change.resource} ${change.name}: ${message}`));
|
|
4736
|
+
if (collectionErrors.length > 0) {
|
|
4737
|
+
console.log();
|
|
4738
|
+
for (const message of collectionErrors) console.log(pc2.red(` \u2717 ${message}`));
|
|
4739
|
+
console.log();
|
|
4740
|
+
console.log(pc2.red(` \u2717 Apply aborted: fix ${collectionErrors.length} collection error${collectionErrors.length === 1 ? "" : "s"} first. Nothing was applied.`));
|
|
4741
|
+
console.log();
|
|
4742
|
+
process.exit(1);
|
|
4743
|
+
}
|
|
4682
4744
|
let willDeployApp = false;
|
|
4683
4745
|
if (!type && !skipApp) {
|
|
4684
4746
|
try {
|
|
@@ -5297,6 +5359,7 @@ export {
|
|
|
5297
5359
|
loadLocalCollections,
|
|
5298
5360
|
loadLocalSkills,
|
|
5299
5361
|
localProjectSkillSlug,
|
|
5362
|
+
lumeraFileMaxSelectErrors,
|
|
5300
5363
|
plan,
|
|
5301
5364
|
planAgents,
|
|
5302
5365
|
planCollections,
|