@lumerahq/cli 0.19.1 → 0.19.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.
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-XDTWVFPE.js";
|
|
4
4
|
import {
|
|
5
5
|
deploy
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-53NOF33P.js";
|
|
7
7
|
import {
|
|
8
8
|
loadEnv
|
|
9
9
|
} from "./chunk-2CR762KB.js";
|
|
@@ -25,19 +25,205 @@ import {
|
|
|
25
25
|
import "./chunk-PNKVD2UK.js";
|
|
26
26
|
|
|
27
27
|
// src/commands/resources.ts
|
|
28
|
-
import
|
|
28
|
+
import pc2 from "picocolors";
|
|
29
29
|
import prompts from "prompts";
|
|
30
30
|
import { execFileSync, execSync } from "child_process";
|
|
31
31
|
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
32
32
|
import { join, resolve } from "path";
|
|
33
|
+
|
|
34
|
+
// src/lib/lint/rules/llm-import.ts
|
|
35
|
+
var FROM_LUMERA_IMPORT_LLM = /^\s*from\s+lumera\s+import\s+\(?\s*(?:[\w\s,]*?\b)?llm\b/;
|
|
36
|
+
var LUMERA_LLM_SUBMODULE = /^\s*(?:from\s+lumera\.llm\b|import\s+lumera\.llm\b)/;
|
|
37
|
+
var llmImportRule = {
|
|
38
|
+
id: "llm-import",
|
|
39
|
+
description: "Flags imports of 'lumera.llm' \u2014 prefer custom agents for LLM/agentic capabilities.",
|
|
40
|
+
appliesTo: ["automation"],
|
|
41
|
+
check(target) {
|
|
42
|
+
const warnings = [];
|
|
43
|
+
const lines = target.source.split(/\r?\n/);
|
|
44
|
+
let inTriple = false;
|
|
45
|
+
for (let i = 0; i < lines.length; i++) {
|
|
46
|
+
const raw = lines[i];
|
|
47
|
+
if (inTriple) {
|
|
48
|
+
if (raw.includes(inTriple)) inTriple = false;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const tripleMatch = raw.match(/("""|''')/);
|
|
52
|
+
if (tripleMatch) {
|
|
53
|
+
const q = tripleMatch[1];
|
|
54
|
+
const rest = raw.slice(raw.indexOf(q) + 3);
|
|
55
|
+
if (!rest.includes(q)) {
|
|
56
|
+
inTriple = q;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const trimmed = raw.trimStart();
|
|
61
|
+
if (trimmed.startsWith("#")) continue;
|
|
62
|
+
if (FROM_LUMERA_IMPORT_LLM.test(raw) || LUMERA_LLM_SUBMODULE.test(raw)) {
|
|
63
|
+
warnings.push({
|
|
64
|
+
ruleId: "llm-import",
|
|
65
|
+
target,
|
|
66
|
+
line: i + 1,
|
|
67
|
+
snippet: raw.trim(),
|
|
68
|
+
message: "Imports 'lumera.llm'. Prefer a custom agent for LLM/agentic capabilities in new automations."
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return warnings;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/lib/lint/registry.ts
|
|
77
|
+
var ALL_RULES = [llmImportRule];
|
|
78
|
+
|
|
79
|
+
// src/lib/lint/format.ts
|
|
80
|
+
import { relative } from "path";
|
|
81
|
+
import pc from "picocolors";
|
|
82
|
+
function printLintWarnings(warnings, projectRoot) {
|
|
83
|
+
if (warnings.length === 0) return;
|
|
84
|
+
console.log();
|
|
85
|
+
console.log(pc.bold(" Warnings:"));
|
|
86
|
+
for (const w of warnings) {
|
|
87
|
+
const rel = relative(projectRoot, w.target.filePath);
|
|
88
|
+
const loc = w.line ? `${rel}:${w.line}` : rel;
|
|
89
|
+
console.log(` ${pc.yellow("\u26A0")} ${loc} ${pc.dim(`[${w.ruleId}]`)}`);
|
|
90
|
+
console.log(` ${w.message}`);
|
|
91
|
+
if (w.snippet) console.log(pc.dim(` > ${w.snippet}`));
|
|
92
|
+
}
|
|
93
|
+
console.log();
|
|
94
|
+
const n = warnings.length;
|
|
95
|
+
console.log(pc.dim(` ${n} warning${n === 1 ? "" : "s"} \u2014 advisory, will not block apply.`));
|
|
96
|
+
console.log();
|
|
97
|
+
}
|
|
98
|
+
function serializeLintWarnings(warnings, projectRoot) {
|
|
99
|
+
return warnings.map((w) => ({
|
|
100
|
+
ruleId: w.ruleId,
|
|
101
|
+
target: { kind: w.target.kind, name: w.target.name, filePath: relative(projectRoot, w.target.filePath) },
|
|
102
|
+
message: w.message,
|
|
103
|
+
line: w.line,
|
|
104
|
+
snippet: w.snippet
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/lib/lint/index.ts
|
|
109
|
+
function runLint(ctx) {
|
|
110
|
+
const out = [];
|
|
111
|
+
for (const rule of ALL_RULES) {
|
|
112
|
+
for (const t of ctx.targets) {
|
|
113
|
+
if (!rule.appliesTo.includes(t.kind)) continue;
|
|
114
|
+
try {
|
|
115
|
+
out.push(...rule.check(t, ctx));
|
|
116
|
+
} catch (err) {
|
|
117
|
+
if (process.env.LUMERA_DEBUG) {
|
|
118
|
+
console.error(`[lint] rule "${rule.id}" threw on ${t.filePath}:`, err);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
function buildAutomationTargets(localAutomations) {
|
|
126
|
+
return localAutomations.map((a) => ({
|
|
127
|
+
kind: "automation",
|
|
128
|
+
name: a.automation.external_id,
|
|
129
|
+
filePath: a.filePath,
|
|
130
|
+
source: a.code
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/commands/resources.ts
|
|
33
135
|
init_auth();
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
136
|
+
function safeLint(projectRoot, localAutomations) {
|
|
137
|
+
try {
|
|
138
|
+
return runLint({ projectRoot, targets: buildAutomationTargets(localAutomations) });
|
|
139
|
+
} catch (err) {
|
|
140
|
+
if (process.env.LUMERA_DEBUG) console.error("[lint] pass failed:", err);
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function safePrintLint(warnings, projectRoot) {
|
|
145
|
+
try {
|
|
146
|
+
printLintWarnings(warnings, projectRoot);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
if (process.env.LUMERA_DEBUG) console.error("[lint] print failed:", err);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
var PACKAGE_MANAGERS = ["bun", "pnpm", "yarn", "npm"];
|
|
152
|
+
var PACKAGE_MANAGER_VALUE_FLAGS = /* @__PURE__ */ new Set(["--package-manager"]);
|
|
153
|
+
function isPackageManager(value) {
|
|
154
|
+
return PACKAGE_MANAGERS.includes(value);
|
|
155
|
+
}
|
|
156
|
+
function commandAvailable(command) {
|
|
157
|
+
try {
|
|
158
|
+
execFileSync(command, ["--version"], { stdio: "ignore" });
|
|
159
|
+
return true;
|
|
160
|
+
} catch {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function parsePackageManagerSpecifier(value) {
|
|
165
|
+
const raw = (value || "").trim();
|
|
166
|
+
if (!raw) return void 0;
|
|
167
|
+
const name = raw.split("@")[0];
|
|
168
|
+
return isPackageManager(name) ? name : void 0;
|
|
169
|
+
}
|
|
170
|
+
function packageManagerFromPackageJson(projectRoot) {
|
|
171
|
+
try {
|
|
172
|
+
const pkg = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf-8"));
|
|
173
|
+
return parsePackageManagerSpecifier(pkg.packageManager);
|
|
174
|
+
} catch {
|
|
175
|
+
return void 0;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function getFlagValue(args, name) {
|
|
179
|
+
const long = `--${name}`;
|
|
180
|
+
for (let i = 0; i < args.length; i++) {
|
|
181
|
+
const arg = args[i];
|
|
182
|
+
if (arg === long) {
|
|
183
|
+
const next = args[i + 1];
|
|
184
|
+
return next && !next.startsWith("-") ? next : void 0;
|
|
185
|
+
}
|
|
186
|
+
if (arg.startsWith(`${long}=`)) {
|
|
187
|
+
return arg.slice(long.length + 1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
function getPositionalArgs(args) {
|
|
193
|
+
const out = [];
|
|
194
|
+
for (let i = 0; i < args.length; i++) {
|
|
195
|
+
const arg = args[i];
|
|
196
|
+
if (PACKAGE_MANAGER_VALUE_FLAGS.has(arg)) {
|
|
197
|
+
i++;
|
|
198
|
+
continue;
|
|
40
199
|
}
|
|
200
|
+
if (arg.startsWith("-")) continue;
|
|
201
|
+
out.push(arg);
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
function detectPackageManager(projectRoot, override) {
|
|
206
|
+
const requested = parsePackageManagerSpecifier(override || process.env.LUMERA_PACKAGE_MANAGER);
|
|
207
|
+
if (requested) {
|
|
208
|
+
if (!commandAvailable(requested)) {
|
|
209
|
+
throw new Error(`Package manager '${requested}' was requested but is not available on PATH`);
|
|
210
|
+
}
|
|
211
|
+
return requested;
|
|
212
|
+
}
|
|
213
|
+
const declared = packageManagerFromPackageJson(projectRoot);
|
|
214
|
+
if (declared && commandAvailable(declared)) return declared;
|
|
215
|
+
const lockfileManagers = [
|
|
216
|
+
{ pm: "pnpm", file: "pnpm-lock.yaml" },
|
|
217
|
+
{ pm: "bun", file: "bun.lockb" },
|
|
218
|
+
{ pm: "bun", file: "bun.lock" },
|
|
219
|
+
{ pm: "yarn", file: "yarn.lock" },
|
|
220
|
+
{ pm: "npm", file: "package-lock.json" }
|
|
221
|
+
];
|
|
222
|
+
for (const { pm, file } of lockfileManagers) {
|
|
223
|
+
if (existsSync(join(projectRoot, file)) && commandAvailable(pm)) return pm;
|
|
224
|
+
}
|
|
225
|
+
for (const pm of ["pnpm", "bun", "yarn", "npm"]) {
|
|
226
|
+
if (commandAvailable(pm)) return pm;
|
|
41
227
|
}
|
|
42
228
|
return "npm";
|
|
43
229
|
}
|
|
@@ -267,13 +453,13 @@ function normalizeLocalAutomation(config) {
|
|
|
267
453
|
}
|
|
268
454
|
function showPlanHelp() {
|
|
269
455
|
console.log(`
|
|
270
|
-
${
|
|
456
|
+
${pc2.dim("Usage:")}
|
|
271
457
|
lumera plan [resource]
|
|
272
458
|
|
|
273
|
-
${
|
|
459
|
+
${pc2.dim("Description:")}
|
|
274
460
|
Preview changes between local files and remote state.
|
|
275
461
|
|
|
276
|
-
${
|
|
462
|
+
${pc2.dim("Resources:")}
|
|
277
463
|
(none) Plan all resources
|
|
278
464
|
collections Plan only collections
|
|
279
465
|
collections/<name> Plan single collection
|
|
@@ -286,7 +472,7 @@ ${pc.dim("Resources:")}
|
|
|
286
472
|
mailboxes/<slug> Plan single mailbox
|
|
287
473
|
app Plan app deployment
|
|
288
474
|
|
|
289
|
-
${
|
|
475
|
+
${pc2.dim("Examples:")}
|
|
290
476
|
lumera plan # Plan all resources
|
|
291
477
|
lumera plan collections # Plan only collections
|
|
292
478
|
lumera plan automations/sync # Plan single automation
|
|
@@ -295,13 +481,13 @@ ${pc.dim("Examples:")}
|
|
|
295
481
|
}
|
|
296
482
|
function showApplyHelp() {
|
|
297
483
|
console.log(`
|
|
298
|
-
${
|
|
484
|
+
${pc2.dim("Usage:")}
|
|
299
485
|
lumera apply [resource]
|
|
300
486
|
|
|
301
|
-
${
|
|
487
|
+
${pc2.dim("Description:")}
|
|
302
488
|
Create or update resources from local files.
|
|
303
489
|
|
|
304
|
-
${
|
|
490
|
+
${pc2.dim("Resources:")}
|
|
305
491
|
(none) Apply all resources
|
|
306
492
|
collections Apply only collections
|
|
307
493
|
collections/<name> Apply single collection
|
|
@@ -314,33 +500,38 @@ ${pc.dim("Resources:")}
|
|
|
314
500
|
mailboxes/<slug> Apply single mailbox
|
|
315
501
|
app Deploy the frontend app
|
|
316
502
|
|
|
317
|
-
${
|
|
503
|
+
${pc2.dim("Options:")}
|
|
318
504
|
--yes, -y Skip confirmation prompt (for CI/CD)
|
|
319
505
|
--skip-build Skip build step when applying app
|
|
506
|
+
--no-app Apply resources only; do not build/deploy the app
|
|
507
|
+
--resources-only Alias for --no-app
|
|
508
|
+
--package-manager Package manager for app builds (pnpm, bun, yarn, npm)
|
|
320
509
|
|
|
321
|
-
${
|
|
510
|
+
${pc2.dim("Examples:")}
|
|
322
511
|
lumera apply # Apply everything (shows plan, asks to confirm)
|
|
323
512
|
lumera apply collections # Apply all collections
|
|
324
513
|
lumera apply collections/users # Apply single collection
|
|
325
514
|
lumera apply agents -y # Apply agents without confirmation
|
|
326
515
|
lumera apply app # Deploy frontend
|
|
327
516
|
lumera apply app --skip-build # Deploy without rebuilding
|
|
517
|
+
lumera apply --no-app -y # Apply resources without app build/deploy
|
|
518
|
+
lumera apply app --package-manager pnpm
|
|
328
519
|
`);
|
|
329
520
|
}
|
|
330
521
|
function showPullHelp() {
|
|
331
522
|
console.log(`
|
|
332
|
-
${
|
|
523
|
+
${pc2.dim("Usage:")}
|
|
333
524
|
lumera pull [resource] [--force]
|
|
334
525
|
|
|
335
|
-
${
|
|
526
|
+
${pc2.dim("Description:")}
|
|
336
527
|
Download remote state to local files.
|
|
337
528
|
Refuses to overwrite local files that have uncommitted changes
|
|
338
529
|
(use --force to override, or 'lumera diff' to inspect first).
|
|
339
530
|
|
|
340
|
-
${
|
|
531
|
+
${pc2.dim("Options:")}
|
|
341
532
|
--force, -f Overwrite local files even if they have changes
|
|
342
533
|
|
|
343
|
-
${
|
|
534
|
+
${pc2.dim("Resources:")}
|
|
344
535
|
(none) Pull all resources
|
|
345
536
|
collections Pull only collections
|
|
346
537
|
collections/<name> Pull single collection
|
|
@@ -352,7 +543,7 @@ ${pc.dim("Resources:")}
|
|
|
352
543
|
mailboxes Pull only mailboxes
|
|
353
544
|
mailboxes/<slug> Pull single mailbox
|
|
354
545
|
|
|
355
|
-
${
|
|
546
|
+
${pc2.dim("Examples:")}
|
|
356
547
|
lumera pull # Pull all (safe \u2014 warns on conflicts)
|
|
357
548
|
lumera pull agents # Pull only agents
|
|
358
549
|
lumera pull --force # Pull all, overwrite local changes
|
|
@@ -361,13 +552,13 @@ ${pc.dim("Examples:")}
|
|
|
361
552
|
}
|
|
362
553
|
function showDestroyHelp() {
|
|
363
554
|
console.log(`
|
|
364
|
-
${
|
|
555
|
+
${pc2.dim("Usage:")}
|
|
365
556
|
lumera destroy [resource]
|
|
366
557
|
|
|
367
|
-
${
|
|
558
|
+
${pc2.dim("Description:")}
|
|
368
559
|
Delete resources from remote.
|
|
369
560
|
|
|
370
|
-
${
|
|
561
|
+
${pc2.dim("Resources:")}
|
|
371
562
|
(none) Destroy all resources
|
|
372
563
|
collections Destroy only collections
|
|
373
564
|
collections/<name> Destroy single collection
|
|
@@ -378,11 +569,11 @@ ${pc.dim("Resources:")}
|
|
|
378
569
|
agents/<name> Destroy single agent
|
|
379
570
|
app Delete app registration
|
|
380
571
|
|
|
381
|
-
${
|
|
572
|
+
${pc2.dim("Options:")}
|
|
382
573
|
--confirm Skip confirmation prompt
|
|
383
574
|
--force-cycles Remove relation fields to break circular references before deleting
|
|
384
575
|
|
|
385
|
-
${
|
|
576
|
+
${pc2.dim("Examples:")}
|
|
386
577
|
lumera destroy # Destroy everything
|
|
387
578
|
lumera destroy collections/users # Destroy single collection
|
|
388
579
|
lumera destroy app # Delete app registration
|
|
@@ -390,13 +581,13 @@ ${pc.dim("Examples:")}
|
|
|
390
581
|
}
|
|
391
582
|
function showListHelp() {
|
|
392
583
|
console.log(`
|
|
393
|
-
${
|
|
584
|
+
${pc2.dim("Usage:")}
|
|
394
585
|
lumera list [type] [--all]
|
|
395
586
|
|
|
396
|
-
${
|
|
587
|
+
${pc2.dim("Description:")}
|
|
397
588
|
List resources with status. By default, remote-only resources are hidden.
|
|
398
589
|
|
|
399
|
-
${
|
|
590
|
+
${pc2.dim("Types:")}
|
|
400
591
|
(none) List all resources
|
|
401
592
|
collections List only collections
|
|
402
593
|
automations List only automations
|
|
@@ -404,10 +595,10 @@ ${pc.dim("Types:")}
|
|
|
404
595
|
agents List only agents
|
|
405
596
|
mailboxes List only mailboxes
|
|
406
597
|
|
|
407
|
-
${
|
|
598
|
+
${pc2.dim("Options:")}
|
|
408
599
|
--all Include remote-only resources
|
|
409
600
|
|
|
410
|
-
${
|
|
601
|
+
${pc2.dim("Examples:")}
|
|
411
602
|
lumera list # List local resources
|
|
412
603
|
lumera list --all # Include remote-only resources
|
|
413
604
|
lumera list collections # List only collections
|
|
@@ -415,13 +606,13 @@ ${pc.dim("Examples:")}
|
|
|
415
606
|
}
|
|
416
607
|
function showShowHelp() {
|
|
417
608
|
console.log(`
|
|
418
|
-
${
|
|
609
|
+
${pc2.dim("Usage:")}
|
|
419
610
|
lumera show <resource>
|
|
420
611
|
|
|
421
|
-
${
|
|
612
|
+
${pc2.dim("Description:")}
|
|
422
613
|
Show details of a single resource.
|
|
423
614
|
|
|
424
|
-
${
|
|
615
|
+
${pc2.dim("Resources:")}
|
|
425
616
|
collections/<name> Show collection details
|
|
426
617
|
automations/<name> Show automation details
|
|
427
618
|
hooks/<name> Show hook details
|
|
@@ -429,7 +620,7 @@ ${pc.dim("Resources:")}
|
|
|
429
620
|
mailboxes/<slug> Show mailbox details
|
|
430
621
|
app Show app details
|
|
431
622
|
|
|
432
|
-
${
|
|
623
|
+
${pc2.dim("Examples:")}
|
|
433
624
|
lumera show collections/users # Show collection details
|
|
434
625
|
lumera show automations/sync # Show automation details
|
|
435
626
|
lumera show app # Show app details
|
|
@@ -444,7 +635,7 @@ function parseResource(resourcePath) {
|
|
|
444
635
|
const name = parts.slice(1).join("/") || null;
|
|
445
636
|
const validTypes = ["collections", "automations", "hooks", "agents", "mailboxes", "app"];
|
|
446
637
|
if (!validTypes.includes(type)) {
|
|
447
|
-
console.log(
|
|
638
|
+
console.log(pc2.red(` Unknown resource type "${type}". Valid types: ${validTypes.join(", ")}`));
|
|
448
639
|
process.exit(1);
|
|
449
640
|
}
|
|
450
641
|
return { type, name };
|
|
@@ -509,9 +700,9 @@ function loadLocalCollections(platformDir, filterName) {
|
|
|
509
700
|
}
|
|
510
701
|
}
|
|
511
702
|
if (errors.length > 0) {
|
|
512
|
-
console.log(
|
|
703
|
+
console.log(pc2.red(" Collection errors:"));
|
|
513
704
|
for (const err of errors) {
|
|
514
|
-
console.log(
|
|
705
|
+
console.log(pc2.red(` \u2717 ${err}`));
|
|
515
706
|
}
|
|
516
707
|
throw new Error(`Found ${errors.length} collection error(s)`);
|
|
517
708
|
}
|
|
@@ -569,15 +760,15 @@ function loadLocalAutomations(platformDir, filterName, appName) {
|
|
|
569
760
|
if (appName) {
|
|
570
761
|
code = code.replaceAll("{{app}}", appName);
|
|
571
762
|
}
|
|
572
|
-
automations.push({ automation: config, code });
|
|
763
|
+
automations.push({ automation: config, code, filePath: mainPath });
|
|
573
764
|
} catch (e) {
|
|
574
765
|
errors.push(`${entry.name}: failed to parse config.json - ${e}`);
|
|
575
766
|
}
|
|
576
767
|
}
|
|
577
768
|
if (errors.length > 0) {
|
|
578
|
-
console.log(
|
|
769
|
+
console.log(pc2.red(" Automation errors:"));
|
|
579
770
|
for (const err of errors) {
|
|
580
|
-
console.log(
|
|
771
|
+
console.log(pc2.red(` \u2717 ${err}`));
|
|
581
772
|
}
|
|
582
773
|
throw new Error(`Found ${errors.length} automation error(s)`);
|
|
583
774
|
}
|
|
@@ -595,14 +786,14 @@ function loadLocalHooks(platformDir, filterName, appName) {
|
|
|
595
786
|
const content = readFileSync(filePath, "utf-8");
|
|
596
787
|
const config = parseHookConfig(content);
|
|
597
788
|
if (!config) {
|
|
598
|
-
console.log(
|
|
789
|
+
console.log(pc2.yellow(` \u26A0 Skipping ${file}: could not parse config export`));
|
|
599
790
|
continue;
|
|
600
791
|
}
|
|
601
792
|
if (!config.external_id) {
|
|
602
793
|
if (appName) {
|
|
603
794
|
config.external_id = `${appName}:${file.replace(/\.(js|ts)$/, "")}`;
|
|
604
795
|
} else {
|
|
605
|
-
console.log(
|
|
796
|
+
console.log(pc2.yellow(` \u26A0 Skipping ${file}: missing external_id in config`));
|
|
606
797
|
continue;
|
|
607
798
|
}
|
|
608
799
|
}
|
|
@@ -917,7 +1108,7 @@ function loadLocalMailboxes(platformDir, filterName) {
|
|
|
917
1108
|
if (!file.endsWith(".json")) continue;
|
|
918
1109
|
const slug = file.replace(/\.json$/, "").trim();
|
|
919
1110
|
if (!slug) {
|
|
920
|
-
console.log(
|
|
1111
|
+
console.log(pc2.yellow(` \u26A0 Skipping ${file}: empty filename`));
|
|
921
1112
|
continue;
|
|
922
1113
|
}
|
|
923
1114
|
if (filterName && slug !== filterName) {
|
|
@@ -928,11 +1119,11 @@ function loadLocalMailboxes(platformDir, filterName) {
|
|
|
928
1119
|
try {
|
|
929
1120
|
raw = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
930
1121
|
} catch (e) {
|
|
931
|
-
console.log(
|
|
1122
|
+
console.log(pc2.yellow(` \u26A0 Skipping ${file}: invalid JSON (${e.message})`));
|
|
932
1123
|
continue;
|
|
933
1124
|
}
|
|
934
1125
|
if (!isPlainObject(raw)) {
|
|
935
|
-
console.log(
|
|
1126
|
+
console.log(pc2.yellow(` \u26A0 Skipping ${file}: top-level value must be an object`));
|
|
936
1127
|
continue;
|
|
937
1128
|
}
|
|
938
1129
|
const descriptionRaw = raw["description"];
|
|
@@ -983,17 +1174,17 @@ async function applyMailboxes(api, localMailboxes) {
|
|
|
983
1174
|
const localDesc = (mb.description ?? "").trim();
|
|
984
1175
|
const remoteDesc = (existing.description ?? "").trim();
|
|
985
1176
|
if (localDesc !== remoteDesc) {
|
|
986
|
-
console.log(
|
|
1177
|
+
console.log(pc2.yellow(" \u26A0"), `mailbox ${mb.slug}: description drift not reconcilable by CLI (no PATCH endpoint yet) \u2014 update in the UI or DB`);
|
|
987
1178
|
} else {
|
|
988
|
-
console.log(
|
|
1179
|
+
console.log(pc2.green(" \u2713"), pc2.dim(`mailbox ${mb.slug} already exists (${existing.email})`));
|
|
989
1180
|
}
|
|
990
1181
|
continue;
|
|
991
1182
|
}
|
|
992
1183
|
try {
|
|
993
1184
|
const created = await api.createMailbox({ slug: mb.slug, description: mb.description });
|
|
994
|
-
console.log(
|
|
1185
|
+
console.log(pc2.green(" \u2713"), `created mailbox ${mb.slug} ${pc2.dim(`\u2192 ${created.email}`)}`);
|
|
995
1186
|
} catch (e) {
|
|
996
|
-
console.log(
|
|
1187
|
+
console.log(pc2.red(" \u2717"), `mailbox ${mb.slug}: ${e.message}`);
|
|
997
1188
|
errors++;
|
|
998
1189
|
}
|
|
999
1190
|
}
|
|
@@ -1002,7 +1193,7 @@ async function applyMailboxes(api, localMailboxes) {
|
|
|
1002
1193
|
async function pullMailboxes(api, platformDir, filterName) {
|
|
1003
1194
|
const mailboxes = await api.listMailboxes();
|
|
1004
1195
|
if (mailboxes.length === 0) {
|
|
1005
|
-
console.log(
|
|
1196
|
+
console.log(pc2.dim(" (no mailboxes)"));
|
|
1006
1197
|
return;
|
|
1007
1198
|
}
|
|
1008
1199
|
const outDir = join(platformDir, "mailboxes");
|
|
@@ -1016,11 +1207,11 @@ async function pullMailboxes(api, platformDir, filterName) {
|
|
|
1016
1207
|
if (mb.description) body.description = mb.description;
|
|
1017
1208
|
const file = join(outDir, `${toSafeFilename(mb.slug)}.json`);
|
|
1018
1209
|
writeFileSync(file, JSON.stringify(body, null, 2) + "\n");
|
|
1019
|
-
console.log(
|
|
1210
|
+
console.log(pc2.green(" \u2713"), `pulled mailbox ${mb.slug} ${pc2.dim(`\u2192 ${file}`)}`);
|
|
1020
1211
|
count++;
|
|
1021
1212
|
}
|
|
1022
1213
|
if (count === 0 && filterName) {
|
|
1023
|
-
console.log(
|
|
1214
|
+
console.log(pc2.yellow(` \u26A0 mailbox '${filterName}' not found on remote`));
|
|
1024
1215
|
}
|
|
1025
1216
|
}
|
|
1026
1217
|
async function planHooks(api, localHooks, collections) {
|
|
@@ -1031,7 +1222,7 @@ async function planHooks(api, localHooks, collections) {
|
|
|
1031
1222
|
const remote = remoteByExternalId.get(hook.external_id);
|
|
1032
1223
|
const collectionId = collections.get(hook.collection);
|
|
1033
1224
|
if (!collectionId) {
|
|
1034
|
-
console.log(
|
|
1225
|
+
console.log(pc2.yellow(` \u26A0 Skipping ${fileName}: collection '${hook.collection}' not found`));
|
|
1035
1226
|
continue;
|
|
1036
1227
|
}
|
|
1037
1228
|
if (!remote) {
|
|
@@ -1079,9 +1270,9 @@ async function applyCollections(api, localCollections) {
|
|
|
1079
1270
|
for (const local of localCollections) {
|
|
1080
1271
|
try {
|
|
1081
1272
|
await api.ensureCollection(local.name, { id: local.id });
|
|
1082
|
-
console.log(
|
|
1273
|
+
console.log(pc2.green(" \u2713"), `${local.name}`);
|
|
1083
1274
|
} catch (e) {
|
|
1084
|
-
console.log(
|
|
1275
|
+
console.log(pc2.red(" \u2717"), `${local.name}: ${e}`);
|
|
1085
1276
|
pass1Failed.add(local.name);
|
|
1086
1277
|
errors++;
|
|
1087
1278
|
}
|
|
@@ -1091,9 +1282,9 @@ async function applyCollections(api, localCollections) {
|
|
|
1091
1282
|
const apiFormat = convertCollectionToApiFormat(local);
|
|
1092
1283
|
try {
|
|
1093
1284
|
await api.ensureCollection(local.name, apiFormat);
|
|
1094
|
-
console.log(
|
|
1285
|
+
console.log(pc2.green(" \u2713"), `${local.name} (schema)`);
|
|
1095
1286
|
} catch (e) {
|
|
1096
|
-
console.log(
|
|
1287
|
+
console.log(pc2.red(" \u2717"), `${local.name} (schema): ${e}`);
|
|
1097
1288
|
errors++;
|
|
1098
1289
|
}
|
|
1099
1290
|
}
|
|
@@ -1102,9 +1293,9 @@ async function applyCollections(api, localCollections) {
|
|
|
1102
1293
|
const apiFormat = convertCollectionToApiFormat(local);
|
|
1103
1294
|
try {
|
|
1104
1295
|
await api.ensureCollection(local.name, apiFormat);
|
|
1105
|
-
console.log(
|
|
1296
|
+
console.log(pc2.green(" \u2713"), `${local.name}`);
|
|
1106
1297
|
} catch (e) {
|
|
1107
|
-
console.log(
|
|
1298
|
+
console.log(pc2.red(" \u2717"), `${local.name}: ${e}`);
|
|
1108
1299
|
errors++;
|
|
1109
1300
|
}
|
|
1110
1301
|
}
|
|
@@ -1134,11 +1325,11 @@ async function applyAutomations(api, localAutomations, projectId) {
|
|
|
1134
1325
|
if (remote) {
|
|
1135
1326
|
await api.updateAutomation(remote.id, payload);
|
|
1136
1327
|
automationId = remote.id;
|
|
1137
|
-
console.log(
|
|
1328
|
+
console.log(pc2.green(" \u2713"), `${automation.name} (updated)`);
|
|
1138
1329
|
} else {
|
|
1139
1330
|
const created = await api.createAutomation(payload);
|
|
1140
1331
|
automationId = created.id;
|
|
1141
|
-
console.log(
|
|
1332
|
+
console.log(pc2.green(" \u2713"), `${automation.name} (created)`);
|
|
1142
1333
|
}
|
|
1143
1334
|
const localPresets = automation.inputs?.presets;
|
|
1144
1335
|
if (localPresets) {
|
|
@@ -1148,7 +1339,7 @@ async function applyAutomations(api, localAutomations, projectId) {
|
|
|
1148
1339
|
await setSchedule(api, automationId, automation.schedule, localPresets || {});
|
|
1149
1340
|
} else if (remote?.schedule) {
|
|
1150
1341
|
await api.updateAutomation(automationId, { schedule: "", schedule_tz: "" });
|
|
1151
|
-
console.log(
|
|
1342
|
+
console.log(pc2.dim(` Cleared schedule`));
|
|
1152
1343
|
}
|
|
1153
1344
|
if (localPresets) {
|
|
1154
1345
|
const current = await api.getAutomation(automationId).catch(() => null);
|
|
@@ -1162,7 +1353,7 @@ async function applyAutomations(api, localAutomations, projectId) {
|
|
|
1162
1353
|
}
|
|
1163
1354
|
}
|
|
1164
1355
|
} catch (e) {
|
|
1165
|
-
console.log(
|
|
1356
|
+
console.log(pc2.red(" \u2717"), `${automation.name}: ${e}`);
|
|
1166
1357
|
errors++;
|
|
1167
1358
|
}
|
|
1168
1359
|
}
|
|
@@ -1177,13 +1368,13 @@ async function syncPresets(api, automationId, localPresets) {
|
|
|
1177
1368
|
try {
|
|
1178
1369
|
if (existing) {
|
|
1179
1370
|
await api.updatePreset(existing.id, { name: presetName, inputs: preset.inputs });
|
|
1180
|
-
console.log(
|
|
1371
|
+
console.log(pc2.dim(` Updated preset: ${presetName}`));
|
|
1181
1372
|
} else {
|
|
1182
1373
|
await api.createPreset(automationId, { name: presetName, inputs: preset.inputs });
|
|
1183
|
-
console.log(
|
|
1374
|
+
console.log(pc2.dim(` Created preset: ${presetName}`));
|
|
1184
1375
|
}
|
|
1185
1376
|
} catch (e) {
|
|
1186
|
-
console.log(
|
|
1377
|
+
console.log(pc2.yellow(` \u26A0 Failed to sync preset ${presetName}: ${e}`));
|
|
1187
1378
|
}
|
|
1188
1379
|
}
|
|
1189
1380
|
}
|
|
@@ -1198,9 +1389,9 @@ async function deleteStalePresets(api, automationId, localPresets, preservePrese
|
|
|
1198
1389
|
}
|
|
1199
1390
|
try {
|
|
1200
1391
|
await api.deletePreset(remote.id);
|
|
1201
|
-
console.log(
|
|
1392
|
+
console.log(pc2.dim(` Deleted preset: ${remote.name}`));
|
|
1202
1393
|
} catch (e) {
|
|
1203
|
-
console.log(
|
|
1394
|
+
console.log(pc2.yellow(` \u26A0 Failed to delete preset ${remote.name}: ${e}`));
|
|
1204
1395
|
}
|
|
1205
1396
|
}
|
|
1206
1397
|
}
|
|
@@ -1209,7 +1400,7 @@ async function setSchedule(api, automationId, schedule, localPresets) {
|
|
|
1209
1400
|
const remotePresets = await api.listPresets(automationId);
|
|
1210
1401
|
const preset = remotePresets.find((p) => p.name === presetName);
|
|
1211
1402
|
if (!preset) {
|
|
1212
|
-
console.log(
|
|
1403
|
+
console.log(pc2.yellow(` \u26A0 Schedule preset '${schedule.preset}' not found, skipping schedule`));
|
|
1213
1404
|
return;
|
|
1214
1405
|
}
|
|
1215
1406
|
try {
|
|
@@ -1218,9 +1409,9 @@ async function setSchedule(api, automationId, schedule, localPresets) {
|
|
|
1218
1409
|
schedule_tz: schedule.timezone || "UTC",
|
|
1219
1410
|
schedule_preset_id: preset.id
|
|
1220
1411
|
});
|
|
1221
|
-
console.log(
|
|
1412
|
+
console.log(pc2.dim(` Set schedule: ${schedule.cron}`));
|
|
1222
1413
|
} catch (e) {
|
|
1223
|
-
console.log(
|
|
1414
|
+
console.log(pc2.yellow(` \u26A0 Failed to set schedule: ${e}`));
|
|
1224
1415
|
}
|
|
1225
1416
|
}
|
|
1226
1417
|
async function applyHooks(api, localHooks, collections, projectId) {
|
|
@@ -1231,7 +1422,7 @@ async function applyHooks(api, localHooks, collections, projectId) {
|
|
|
1231
1422
|
const remote = remoteByExternalId.get(hook.external_id);
|
|
1232
1423
|
const collectionId = collections.get(hook.collection);
|
|
1233
1424
|
if (!collectionId) {
|
|
1234
|
-
console.log(
|
|
1425
|
+
console.log(pc2.red(` \u2717 ${fileName}: collection '${hook.collection}' not found. Apply the collection first or use 'lumera apply' to apply all resources.`));
|
|
1235
1426
|
errors++;
|
|
1236
1427
|
continue;
|
|
1237
1428
|
}
|
|
@@ -1250,13 +1441,13 @@ async function applyHooks(api, localHooks, collections, projectId) {
|
|
|
1250
1441
|
try {
|
|
1251
1442
|
if (remote) {
|
|
1252
1443
|
await api.updateHook(remote.id, payload);
|
|
1253
|
-
console.log(
|
|
1444
|
+
console.log(pc2.green(" \u2713"), `${payload.name} (updated)`);
|
|
1254
1445
|
} else {
|
|
1255
1446
|
await api.createHook(payload);
|
|
1256
|
-
console.log(
|
|
1447
|
+
console.log(pc2.green(" \u2713"), `${payload.name} (created)`);
|
|
1257
1448
|
}
|
|
1258
1449
|
} catch (e) {
|
|
1259
|
-
console.log(
|
|
1450
|
+
console.log(pc2.red(" \u2717"), `${payload.name}: ${e}`);
|
|
1260
1451
|
errors++;
|
|
1261
1452
|
}
|
|
1262
1453
|
}
|
|
@@ -1271,9 +1462,9 @@ async function applyApp(args) {
|
|
|
1271
1462
|
const appTitle = getAppTitle(projectRoot);
|
|
1272
1463
|
const apiUrl = getApiUrl();
|
|
1273
1464
|
if (!skipBuild) {
|
|
1274
|
-
console.log(
|
|
1465
|
+
console.log(pc2.dim(" Building..."));
|
|
1275
1466
|
try {
|
|
1276
|
-
const pm = detectPackageManager();
|
|
1467
|
+
const pm = detectPackageManager(projectRoot, getFlagValue(args, "package-manager"));
|
|
1277
1468
|
execSync(`${pm} run build`, { cwd: projectRoot, stdio: "inherit" });
|
|
1278
1469
|
} catch {
|
|
1279
1470
|
throw new Error("Build failed");
|
|
@@ -1329,7 +1520,7 @@ async function pullCollections(api, platformDir, filterName, appName) {
|
|
|
1329
1520
|
const fileName = toSafeFilename(localName);
|
|
1330
1521
|
const filePath = join(collectionsDir, `${fileName}.json`);
|
|
1331
1522
|
writeFileSync(filePath, JSON.stringify(localFormat, null, 2) + "\n");
|
|
1332
|
-
console.log(
|
|
1523
|
+
console.log(pc2.green(" \u2713"), `${localName} \u2192 collections/${fileName}.json`);
|
|
1333
1524
|
}
|
|
1334
1525
|
}
|
|
1335
1526
|
async function pullAutomations(api, platformDir, filterName, projectId) {
|
|
@@ -1379,7 +1570,7 @@ async function pullAutomations(api, platformDir, filterName, projectId) {
|
|
|
1379
1570
|
}
|
|
1380
1571
|
writeFileSync(join(automationDir, "config.json"), JSON.stringify(config, null, 2) + "\n");
|
|
1381
1572
|
writeFileSync(join(automationDir, "main.py"), automation.code || "");
|
|
1382
|
-
console.log(
|
|
1573
|
+
console.log(pc2.green(" \u2713"), `${automation.name} \u2192 automations/${dirName}/`);
|
|
1383
1574
|
}
|
|
1384
1575
|
}
|
|
1385
1576
|
async function pullHooks(api, platformDir, filterName, appName, projectId) {
|
|
@@ -1406,7 +1597,7 @@ ${hook.script.split("\n").map((line) => " " + line).join("\n")}
|
|
|
1406
1597
|
}
|
|
1407
1598
|
`;
|
|
1408
1599
|
writeFileSync(join(hooksDir, fileName), content);
|
|
1409
|
-
console.log(
|
|
1600
|
+
console.log(pc2.green(" \u2713"), `${hook.name} \u2192 hooks/${fileName}`);
|
|
1410
1601
|
}
|
|
1411
1602
|
}
|
|
1412
1603
|
function loadLocalAgents(platformDir, filterName, appName) {
|
|
@@ -1435,7 +1626,7 @@ function loadLocalAgents(platformDir, filterName, appName) {
|
|
|
1435
1626
|
}
|
|
1436
1627
|
if (config.mcp_servers !== void 0) {
|
|
1437
1628
|
console.log(
|
|
1438
|
-
|
|
1629
|
+
pc2.yellow(
|
|
1439
1630
|
` \u26A0 ${entry.name}: "mcp_servers" is deprecated and ignored \u2014 MCP servers are now scoped per project in Platform \u2192 Integrations.`
|
|
1440
1631
|
)
|
|
1441
1632
|
);
|
|
@@ -1464,9 +1655,9 @@ function loadLocalAgents(platformDir, filterName, appName) {
|
|
|
1464
1655
|
}
|
|
1465
1656
|
}
|
|
1466
1657
|
if (errors.length > 0) {
|
|
1467
|
-
console.log(
|
|
1658
|
+
console.log(pc2.red(" Agent errors:"));
|
|
1468
1659
|
for (const err of errors) {
|
|
1469
|
-
console.log(
|
|
1660
|
+
console.log(pc2.red(` \u2717 ${err}`));
|
|
1470
1661
|
}
|
|
1471
1662
|
throw new Error(`Found ${errors.length} agent error(s)`);
|
|
1472
1663
|
}
|
|
@@ -1539,7 +1730,7 @@ async function applyAgents(api, localAgents, projectId) {
|
|
|
1539
1730
|
const skills = await api.listAgentSkills();
|
|
1540
1731
|
skillMap = new Map(skills.map((s) => [s.slug, s.id]));
|
|
1541
1732
|
} catch (e) {
|
|
1542
|
-
console.log(
|
|
1733
|
+
console.log(pc2.yellow(` \u26A0 Could not fetch skills for resolution: ${e}`));
|
|
1543
1734
|
}
|
|
1544
1735
|
}
|
|
1545
1736
|
for (const { agent, systemPrompt, policyScript } of localAgents) {
|
|
@@ -1551,7 +1742,7 @@ async function applyAgents(api, localAgents, projectId) {
|
|
|
1551
1742
|
if (id) {
|
|
1552
1743
|
skillIds.push(id);
|
|
1553
1744
|
} else {
|
|
1554
|
-
console.log(
|
|
1745
|
+
console.log(pc2.yellow(` \u26A0 Skill "${slug}" not found, skipping`));
|
|
1555
1746
|
}
|
|
1556
1747
|
}
|
|
1557
1748
|
}
|
|
@@ -1569,14 +1760,14 @@ async function applyAgents(api, localAgents, projectId) {
|
|
|
1569
1760
|
try {
|
|
1570
1761
|
if (remote) {
|
|
1571
1762
|
await api.updateAgent(remote.id, payload);
|
|
1572
|
-
console.log(
|
|
1763
|
+
console.log(pc2.green(" \u2713"), `${agent.name} (updated)`);
|
|
1573
1764
|
} else {
|
|
1574
1765
|
if (projectId) payload.project_id = projectId;
|
|
1575
1766
|
await api.createAgent(payload);
|
|
1576
|
-
console.log(
|
|
1767
|
+
console.log(pc2.green(" \u2713"), `${agent.name} (created)`);
|
|
1577
1768
|
}
|
|
1578
1769
|
} catch (e) {
|
|
1579
|
-
console.log(
|
|
1770
|
+
console.log(pc2.red(" \u2717"), `${agent.name}: ${e}`);
|
|
1580
1771
|
errors++;
|
|
1581
1772
|
}
|
|
1582
1773
|
}
|
|
@@ -1620,7 +1811,7 @@ async function pullAgents(api, platformDir, filterName, projectId) {
|
|
|
1620
1811
|
if (agent.policy_script) {
|
|
1621
1812
|
writeFileSync(join(agentDir, "policy.js"), agent.policy_script);
|
|
1622
1813
|
}
|
|
1623
|
-
console.log(
|
|
1814
|
+
console.log(pc2.green(" \u2713"), `${agent.name} \u2192 agents/${dirName}/`);
|
|
1624
1815
|
}
|
|
1625
1816
|
}
|
|
1626
1817
|
async function listResources(api, platformDir, filterType, appName, projectId) {
|
|
@@ -1886,13 +2077,13 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
1886
2077
|
}
|
|
1887
2078
|
}
|
|
1888
2079
|
if (toDelete.length === 0) {
|
|
1889
|
-
console.log(
|
|
2080
|
+
console.log(pc2.green(" \u2713 No resources found to delete"));
|
|
1890
2081
|
return;
|
|
1891
2082
|
}
|
|
1892
|
-
console.log(
|
|
2083
|
+
console.log(pc2.bold(" Resources to delete:"));
|
|
1893
2084
|
console.log();
|
|
1894
2085
|
for (const item of toDelete) {
|
|
1895
|
-
console.log(
|
|
2086
|
+
console.log(pc2.red(` - ${item.type}: ${item.name}`));
|
|
1896
2087
|
}
|
|
1897
2088
|
console.log();
|
|
1898
2089
|
if (!skipConfirm) {
|
|
@@ -1903,7 +2094,7 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
1903
2094
|
initial: false
|
|
1904
2095
|
});
|
|
1905
2096
|
if (!confirmed) {
|
|
1906
|
-
console.log(
|
|
2097
|
+
console.log(pc2.dim(" Cancelled"));
|
|
1907
2098
|
return;
|
|
1908
2099
|
}
|
|
1909
2100
|
}
|
|
@@ -1915,45 +2106,45 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
1915
2106
|
for (const resource of agents) {
|
|
1916
2107
|
try {
|
|
1917
2108
|
await api.deleteAgent(resource.remoteId);
|
|
1918
|
-
console.log(
|
|
2109
|
+
console.log(pc2.green(" \u2713"), `Deleted agent: ${resource.name}`);
|
|
1919
2110
|
} catch (e) {
|
|
1920
|
-
console.log(
|
|
2111
|
+
console.log(pc2.red(" \u2717"), `Failed to delete agent ${resource.name}: ${e}`);
|
|
1921
2112
|
errors++;
|
|
1922
2113
|
}
|
|
1923
2114
|
}
|
|
1924
2115
|
for (const resource of hooks) {
|
|
1925
2116
|
try {
|
|
1926
2117
|
await api.deleteHook(resource.remoteId);
|
|
1927
|
-
console.log(
|
|
2118
|
+
console.log(pc2.green(" \u2713"), `Deleted hook: ${resource.name}`);
|
|
1928
2119
|
} catch (e) {
|
|
1929
|
-
console.log(
|
|
2120
|
+
console.log(pc2.red(" \u2717"), `Failed to delete hook ${resource.name}: ${e}`);
|
|
1930
2121
|
errors++;
|
|
1931
2122
|
}
|
|
1932
2123
|
}
|
|
1933
2124
|
for (const resource of automations) {
|
|
1934
2125
|
try {
|
|
1935
2126
|
await api.deleteAutomation(resource.remoteId);
|
|
1936
|
-
console.log(
|
|
2127
|
+
console.log(pc2.green(" \u2713"), `Deleted automation: ${resource.name}`);
|
|
1937
2128
|
} catch (e) {
|
|
1938
|
-
console.log(
|
|
2129
|
+
console.log(pc2.red(" \u2717"), `Failed to delete automation ${resource.name}: ${e}`);
|
|
1939
2130
|
errors++;
|
|
1940
2131
|
}
|
|
1941
2132
|
}
|
|
1942
2133
|
const deletePlan = planCollectionDelete(collections, platformDir);
|
|
1943
2134
|
if (deletePlan.cycleNames.length > 0 && !forceCycles) {
|
|
1944
2135
|
console.log();
|
|
1945
|
-
console.log(
|
|
2136
|
+
console.log(pc2.yellow(" Circular references detected:"));
|
|
1946
2137
|
for (const edge of deletePlan.cycleEdges) {
|
|
1947
|
-
console.log(
|
|
2138
|
+
console.log(pc2.yellow(` ${edge.from}.${edge.field} \u2192 ${edge.to}`));
|
|
1948
2139
|
}
|
|
1949
2140
|
console.log();
|
|
1950
|
-
console.log(
|
|
1951
|
-
console.log(
|
|
2141
|
+
console.log(pc2.dim(" To destroy these, relation fields forming the cycle must be removed first."));
|
|
2142
|
+
console.log(pc2.dim(" Use --force-cycles to proceed."));
|
|
1952
2143
|
console.log();
|
|
1953
2144
|
process.exit(1);
|
|
1954
2145
|
}
|
|
1955
2146
|
if (deletePlan.cycleNames.length > 0 && forceCycles) {
|
|
1956
|
-
console.log(
|
|
2147
|
+
console.log(pc2.dim(" Breaking circular references..."));
|
|
1957
2148
|
for (const edge of deletePlan.cycleEdges) {
|
|
1958
2149
|
const resource = collections.find((c) => c.name === edge.from);
|
|
1959
2150
|
if (!resource?.remoteId) continue;
|
|
@@ -1963,10 +2154,10 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
1963
2154
|
if (remote) {
|
|
1964
2155
|
const updatedSchema = remote.schema.filter((f) => f.name !== edge.field);
|
|
1965
2156
|
await api.ensureCollection(remote.name, { name: remote.name, schema: updatedSchema });
|
|
1966
|
-
console.log(
|
|
2157
|
+
console.log(pc2.green(" \u2713"), `Removed ${edge.from}.${edge.field}`);
|
|
1967
2158
|
}
|
|
1968
2159
|
} catch (e) {
|
|
1969
|
-
console.log(
|
|
2160
|
+
console.log(pc2.red(" \u2717"), `Failed to remove ${edge.from}.${edge.field}: ${e}`);
|
|
1970
2161
|
errors++;
|
|
1971
2162
|
}
|
|
1972
2163
|
}
|
|
@@ -1974,9 +2165,9 @@ async function destroyResources(api, platformDir, resourceType, resourceName, sk
|
|
|
1974
2165
|
for (const resource of deletePlan.sorted) {
|
|
1975
2166
|
try {
|
|
1976
2167
|
await api.deleteCollection(resource.remoteId);
|
|
1977
|
-
console.log(
|
|
2168
|
+
console.log(pc2.green(" \u2713"), `Deleted collection: ${resource.name}`);
|
|
1978
2169
|
} catch (e) {
|
|
1979
|
-
console.log(
|
|
2170
|
+
console.log(pc2.red(" \u2717"), `Failed to delete collection ${resource.name}: ${e}`);
|
|
1980
2171
|
errors++;
|
|
1981
2172
|
}
|
|
1982
2173
|
}
|
|
@@ -2006,10 +2197,10 @@ async function destroyApp(skipConfirm) {
|
|
|
2006
2197
|
const data = await searchRes.json();
|
|
2007
2198
|
const appRecord = data.items?.[0];
|
|
2008
2199
|
if (!appRecord) {
|
|
2009
|
-
console.log(
|
|
2200
|
+
console.log(pc2.yellow(` App "${appName}" not found in Lumera.`));
|
|
2010
2201
|
return;
|
|
2011
2202
|
}
|
|
2012
|
-
console.log(
|
|
2203
|
+
console.log(pc2.dim(` App to delete: ${appRecord.name} (${appRecord.external_id})`));
|
|
2013
2204
|
console.log();
|
|
2014
2205
|
if (!skipConfirm) {
|
|
2015
2206
|
const { confirmed } = await prompts({
|
|
@@ -2019,7 +2210,7 @@ async function destroyApp(skipConfirm) {
|
|
|
2019
2210
|
initial: false
|
|
2020
2211
|
});
|
|
2021
2212
|
if (!confirmed) {
|
|
2022
|
-
console.log(
|
|
2213
|
+
console.log(pc2.dim(" Cancelled"));
|
|
2023
2214
|
return;
|
|
2024
2215
|
}
|
|
2025
2216
|
}
|
|
@@ -2036,7 +2227,7 @@ async function destroyApp(skipConfirm) {
|
|
|
2036
2227
|
if (!deleteRes.ok) {
|
|
2037
2228
|
throw new Error(`Failed to delete app: ${await deleteRes.text()}`);
|
|
2038
2229
|
}
|
|
2039
|
-
console.log(
|
|
2230
|
+
console.log(pc2.green(" \u2713"), `App "${appRecord.name}" deleted from Lumera.`);
|
|
2040
2231
|
}
|
|
2041
2232
|
async function showResource(api, platformDir, resourceType, resourceName, appName, projectId) {
|
|
2042
2233
|
if (resourceType === "collections") {
|
|
@@ -2045,11 +2236,11 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
2045
2236
|
const local = localCollections[0];
|
|
2046
2237
|
const remote = remoteCollections.find((c) => c.name === resourceName || c.id === resourceName);
|
|
2047
2238
|
if (!local && !remote) {
|
|
2048
|
-
console.log(
|
|
2239
|
+
console.log(pc2.red(` Collection "${resourceName}" not found`));
|
|
2049
2240
|
process.exit(1);
|
|
2050
2241
|
}
|
|
2051
2242
|
console.log();
|
|
2052
|
-
console.log(
|
|
2243
|
+
console.log(pc2.bold(` Collection: ${resourceName}`));
|
|
2053
2244
|
console.log();
|
|
2054
2245
|
let collectionStatus;
|
|
2055
2246
|
let addedFields = [];
|
|
@@ -2066,38 +2257,38 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
2066
2257
|
collectionStatus = "remote-only";
|
|
2067
2258
|
}
|
|
2068
2259
|
const statusDisplay = {
|
|
2069
|
-
"synced":
|
|
2070
|
-
"changed":
|
|
2071
|
-
"local-only":
|
|
2072
|
-
"remote-only":
|
|
2260
|
+
"synced": pc2.green("synced"),
|
|
2261
|
+
"changed": pc2.yellow("changed"),
|
|
2262
|
+
"local-only": pc2.yellow("local only"),
|
|
2263
|
+
"remote-only": pc2.cyan("remote only")
|
|
2073
2264
|
};
|
|
2074
2265
|
console.log(` Status: ${statusDisplay[collectionStatus]}`);
|
|
2075
2266
|
console.log();
|
|
2076
2267
|
const addedSet = new Set(addedFields);
|
|
2077
2268
|
const removedSet = new Set(removedFields);
|
|
2078
|
-
console.log(
|
|
2269
|
+
console.log(pc2.bold(" Fields:"));
|
|
2079
2270
|
if (local) {
|
|
2080
2271
|
for (const field of local.fields) {
|
|
2081
|
-
const req = field.required ?
|
|
2272
|
+
const req = field.required ? pc2.red("*") : "";
|
|
2082
2273
|
if (addedSet.has(field.name)) {
|
|
2083
|
-
console.log(` ${
|
|
2274
|
+
console.log(` ${pc2.green("+")} ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
|
|
2084
2275
|
} else {
|
|
2085
|
-
console.log(` ${field.name}${req} ${
|
|
2276
|
+
console.log(` ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
|
|
2086
2277
|
}
|
|
2087
2278
|
}
|
|
2088
2279
|
}
|
|
2089
2280
|
if (remote) {
|
|
2090
2281
|
for (const field of remote.schema) {
|
|
2091
2282
|
if (removedSet.has(field.name)) {
|
|
2092
|
-
const req = field.required ?
|
|
2093
|
-
console.log(` ${
|
|
2283
|
+
const req = field.required ? pc2.red("*") : "";
|
|
2284
|
+
console.log(` ${pc2.red("-")} ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
|
|
2094
2285
|
}
|
|
2095
2286
|
}
|
|
2096
2287
|
}
|
|
2097
2288
|
if (!local && remote) {
|
|
2098
2289
|
for (const field of remote.schema) {
|
|
2099
|
-
const req = field.required ?
|
|
2100
|
-
console.log(` ${field.name}${req} ${
|
|
2290
|
+
const req = field.required ? pc2.red("*") : "";
|
|
2291
|
+
console.log(` ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
|
|
2101
2292
|
}
|
|
2102
2293
|
}
|
|
2103
2294
|
console.log();
|
|
@@ -2107,18 +2298,18 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
2107
2298
|
const local = localAutomations[0];
|
|
2108
2299
|
const remote = remoteAutomations.find((a) => a.external_id === resourceName || a.name === resourceName);
|
|
2109
2300
|
if (!local && !remote) {
|
|
2110
|
-
console.log(
|
|
2301
|
+
console.log(pc2.red(` Automation "${resourceName}" not found`));
|
|
2111
2302
|
process.exit(1);
|
|
2112
2303
|
}
|
|
2113
2304
|
console.log();
|
|
2114
|
-
console.log(
|
|
2305
|
+
console.log(pc2.bold(` Automation: ${local?.automation.name || remote?.name}`));
|
|
2115
2306
|
console.log();
|
|
2116
2307
|
if (local && remote) {
|
|
2117
|
-
console.log(` Status: ${
|
|
2308
|
+
console.log(` Status: ${pc2.green("synced")}`);
|
|
2118
2309
|
} else if (local) {
|
|
2119
|
-
console.log(` Status: ${
|
|
2310
|
+
console.log(` Status: ${pc2.yellow("local only")}`);
|
|
2120
2311
|
} else {
|
|
2121
|
-
console.log(` Status: ${
|
|
2312
|
+
console.log(` Status: ${pc2.cyan("remote only")}`);
|
|
2122
2313
|
}
|
|
2123
2314
|
if (local?.automation.description || remote?.description) {
|
|
2124
2315
|
console.log(` Description: ${local?.automation.description || remote?.description}`);
|
|
@@ -2130,11 +2321,11 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
2130
2321
|
const local = localHooks[0];
|
|
2131
2322
|
const remote = remoteHooks.find((h) => h.external_id === resourceName);
|
|
2132
2323
|
if (!local && !remote) {
|
|
2133
|
-
console.log(
|
|
2324
|
+
console.log(pc2.red(` Hook "${resourceName}" not found`));
|
|
2134
2325
|
process.exit(1);
|
|
2135
2326
|
}
|
|
2136
2327
|
console.log();
|
|
2137
|
-
console.log(
|
|
2328
|
+
console.log(pc2.bold(` Hook: ${local?.hook.external_id || remote?.external_id}`));
|
|
2138
2329
|
console.log();
|
|
2139
2330
|
console.log(` Collection: ${local?.hook.collection || remote?.collection_name}`);
|
|
2140
2331
|
console.log(` Trigger: ${local?.hook.trigger || remote?.event}`);
|
|
@@ -2146,25 +2337,25 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
2146
2337
|
const local = localAgents[0];
|
|
2147
2338
|
const remote = remoteAgents.find((a) => a.external_id === resourceName || a.name === resourceName);
|
|
2148
2339
|
if (!local && !remote) {
|
|
2149
|
-
console.log(
|
|
2340
|
+
console.log(pc2.red(` Agent "${resourceName}" not found`));
|
|
2150
2341
|
process.exit(1);
|
|
2151
2342
|
}
|
|
2152
2343
|
console.log();
|
|
2153
|
-
console.log(
|
|
2344
|
+
console.log(pc2.bold(` Agent: ${local?.agent.name || remote?.name}`));
|
|
2154
2345
|
console.log();
|
|
2155
2346
|
if (local && remote) {
|
|
2156
2347
|
const promptChanged = (remote.system_prompt || "").trim() !== local.systemPrompt.trim();
|
|
2157
2348
|
const nameChanged = remote.name !== local.agent.name;
|
|
2158
2349
|
const descChanged = (remote.description || "") !== (local.agent.description || "");
|
|
2159
2350
|
if (promptChanged || nameChanged || descChanged) {
|
|
2160
|
-
console.log(` Status: ${
|
|
2351
|
+
console.log(` Status: ${pc2.yellow("changed")}`);
|
|
2161
2352
|
} else {
|
|
2162
|
-
console.log(` Status: ${
|
|
2353
|
+
console.log(` Status: ${pc2.green("synced")}`);
|
|
2163
2354
|
}
|
|
2164
2355
|
} else if (local) {
|
|
2165
|
-
console.log(` Status: ${
|
|
2356
|
+
console.log(` Status: ${pc2.yellow("local only")}`);
|
|
2166
2357
|
} else {
|
|
2167
|
-
console.log(` Status: ${
|
|
2358
|
+
console.log(` Status: ${pc2.cyan("remote only")}`);
|
|
2168
2359
|
}
|
|
2169
2360
|
if (local?.agent.external_id || remote?.external_id) {
|
|
2170
2361
|
console.log(` External ID: ${local?.agent.external_id || remote?.external_id}`);
|
|
@@ -2183,19 +2374,19 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
2183
2374
|
const local = localMailboxes[0];
|
|
2184
2375
|
const remote = remoteMailboxes.find((m) => m.slug === resourceName);
|
|
2185
2376
|
if (!local && !remote) {
|
|
2186
|
-
console.log(
|
|
2377
|
+
console.log(pc2.red(` Mailbox "${resourceName}" not found`));
|
|
2187
2378
|
process.exit(1);
|
|
2188
2379
|
}
|
|
2189
2380
|
console.log();
|
|
2190
|
-
console.log(
|
|
2381
|
+
console.log(pc2.bold(` Mailbox: ${local?.slug || remote?.slug}`));
|
|
2191
2382
|
console.log();
|
|
2192
2383
|
if (local && remote) {
|
|
2193
2384
|
const descChanged = (local.description ?? "").trim() !== (remote.description ?? "").trim();
|
|
2194
|
-
console.log(` Status: ${descChanged ?
|
|
2385
|
+
console.log(` Status: ${descChanged ? pc2.yellow("changed") : pc2.green("synced")}`);
|
|
2195
2386
|
} else if (local) {
|
|
2196
|
-
console.log(` Status: ${
|
|
2387
|
+
console.log(` Status: ${pc2.yellow("local only")}`);
|
|
2197
2388
|
} else {
|
|
2198
|
-
console.log(` Status: ${
|
|
2389
|
+
console.log(` Status: ${pc2.cyan("remote only")}`);
|
|
2199
2390
|
}
|
|
2200
2391
|
if (remote?.email) console.log(` Email: ${remote.email}`);
|
|
2201
2392
|
const desc = local?.description || remote?.description;
|
|
@@ -2210,7 +2401,7 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
2210
2401
|
const appTitle = getAppTitle(projectRoot);
|
|
2211
2402
|
let apiUrl = getApiUrl().replace(/\/+$/, "").replace(/\/api$/, "");
|
|
2212
2403
|
console.log();
|
|
2213
|
-
console.log(
|
|
2404
|
+
console.log(pc2.bold(` App: ${appTitle || appName2}`));
|
|
2214
2405
|
console.log();
|
|
2215
2406
|
console.log(` External ID: ${appName2}`);
|
|
2216
2407
|
const filterParam = encodeURIComponent(JSON.stringify({ external_id: appName2 }));
|
|
@@ -2227,12 +2418,12 @@ async function showResource(api, platformDir, resourceType, resourceName, appNam
|
|
|
2227
2418
|
const data = await searchRes.json();
|
|
2228
2419
|
const appRecord = data.items?.[0];
|
|
2229
2420
|
if (appRecord) {
|
|
2230
|
-
console.log(` Status: ${
|
|
2421
|
+
console.log(` Status: ${pc2.green("deployed")}`);
|
|
2231
2422
|
if (appRecord.hosting_type) console.log(` Hosting: ${appRecord.hosting_type}`);
|
|
2232
2423
|
if (appRecord.current_version) console.log(` Version: ${appRecord.current_version}`);
|
|
2233
2424
|
if (appRecord.deployed_at) console.log(` Deployed: ${appRecord.deployed_at}`);
|
|
2234
2425
|
} else {
|
|
2235
|
-
console.log(` Status: ${
|
|
2426
|
+
console.log(` Status: ${pc2.yellow("not deployed")}`);
|
|
2236
2427
|
}
|
|
2237
2428
|
}
|
|
2238
2429
|
console.log();
|
|
@@ -2253,8 +2444,8 @@ async function plan(args) {
|
|
|
2253
2444
|
const positionalArgs = args.filter((a) => !a.startsWith("-"));
|
|
2254
2445
|
const { type, name } = parseResource(positionalArgs.filter((a) => a !== "--json")[0]);
|
|
2255
2446
|
console.log();
|
|
2256
|
-
console.log(
|
|
2257
|
-
console.log(
|
|
2447
|
+
console.log(pc2.cyan(pc2.bold(" Plan")));
|
|
2448
|
+
console.log(pc2.dim(" Comparing local files to remote state..."));
|
|
2258
2449
|
console.log();
|
|
2259
2450
|
await syncDeps(projectRoot);
|
|
2260
2451
|
const allChanges = [];
|
|
@@ -2272,8 +2463,9 @@ async function plan(args) {
|
|
|
2272
2463
|
allChanges.push(...changes);
|
|
2273
2464
|
}
|
|
2274
2465
|
}
|
|
2466
|
+
let localAutomations = [];
|
|
2275
2467
|
if (!type || type === "automations") {
|
|
2276
|
-
|
|
2468
|
+
localAutomations = loadLocalAutomations(platformDir, name || void 0, appName);
|
|
2277
2469
|
if (localAutomations.length > 0) {
|
|
2278
2470
|
const changes = await planAutomations(api, localAutomations);
|
|
2279
2471
|
allChanges.push(...changes);
|
|
@@ -2300,13 +2492,15 @@ async function plan(args) {
|
|
|
2300
2492
|
allChanges.push(...changes);
|
|
2301
2493
|
}
|
|
2302
2494
|
}
|
|
2495
|
+
const lintWarnings = safeLint(projectRoot, localAutomations);
|
|
2303
2496
|
if (allChanges.length === 0) {
|
|
2304
2497
|
if (jsonOutput) {
|
|
2305
|
-
console.log(JSON.stringify({ changes: [], warnings: [] }));
|
|
2498
|
+
console.log(JSON.stringify({ changes: [], warnings: [], lintWarnings: serializeLintWarnings(lintWarnings, projectRoot) }));
|
|
2306
2499
|
return;
|
|
2307
2500
|
}
|
|
2308
|
-
console.log(
|
|
2501
|
+
console.log(pc2.green(" \u2713 No changes detected"));
|
|
2309
2502
|
console.log();
|
|
2503
|
+
safePrintLint(lintWarnings, projectRoot);
|
|
2310
2504
|
return;
|
|
2311
2505
|
}
|
|
2312
2506
|
if (jsonOutput) {
|
|
@@ -2319,21 +2513,21 @@ async function plan(args) {
|
|
|
2319
2513
|
}
|
|
2320
2514
|
}
|
|
2321
2515
|
}
|
|
2322
|
-
console.log(JSON.stringify({ changes: allChanges, warnings }));
|
|
2516
|
+
console.log(JSON.stringify({ changes: allChanges, warnings, lintWarnings: serializeLintWarnings(lintWarnings, projectRoot) }));
|
|
2323
2517
|
return;
|
|
2324
2518
|
}
|
|
2325
|
-
console.log(
|
|
2519
|
+
console.log(pc2.bold(" Changes:"));
|
|
2326
2520
|
console.log();
|
|
2327
2521
|
for (const change of allChanges) {
|
|
2328
2522
|
const icon = change.type === "create" ? "+" : change.type === "update" ? "~" : "-";
|
|
2329
|
-
const color = change.type === "create" ?
|
|
2523
|
+
const color = change.type === "create" ? pc2.green : change.type === "update" ? pc2.yellow : pc2.red;
|
|
2330
2524
|
const details = change.details ? ` (${change.details})` : "";
|
|
2331
|
-
console.log(` ${color(icon)} ${change.resource}: ${change.name}${
|
|
2525
|
+
console.log(` ${color(icon)} ${change.resource}: ${change.name}${pc2.dim(details)}`);
|
|
2332
2526
|
if (change.fieldDetails && change.fieldDetails.length > 0) {
|
|
2333
2527
|
for (const field of change.fieldDetails) {
|
|
2334
|
-
const fColor = field.action === "+" ?
|
|
2528
|
+
const fColor = field.action === "+" ? pc2.green : pc2.red;
|
|
2335
2529
|
const req = field.required ? "*" : "";
|
|
2336
|
-
console.log(` ${fColor(field.action)} ${field.name}${req} ${
|
|
2530
|
+
console.log(` ${fColor(field.action)} ${field.name}${req} ${pc2.dim(`(${field.type})`)}`);
|
|
2337
2531
|
}
|
|
2338
2532
|
console.log();
|
|
2339
2533
|
}
|
|
@@ -2342,16 +2536,16 @@ async function plan(args) {
|
|
|
2342
2536
|
for (const td of change.textDiffs) {
|
|
2343
2537
|
const diffLines = computeLineDiff(td.oldText, td.newText);
|
|
2344
2538
|
if (diffLines.length > 0) {
|
|
2345
|
-
console.log(
|
|
2539
|
+
console.log(pc2.dim(` --- ${td.field}`));
|
|
2346
2540
|
const shown = diffLines.slice(0, maxDiffLines);
|
|
2347
2541
|
for (const dl of shown) {
|
|
2348
|
-
if (dl.type === "+") console.log(
|
|
2349
|
-
else if (dl.type === "-") console.log(
|
|
2350
|
-
else console.log(
|
|
2542
|
+
if (dl.type === "+") console.log(pc2.green(` + ${dl.line}`));
|
|
2543
|
+
else if (dl.type === "-") console.log(pc2.red(` - ${dl.line}`));
|
|
2544
|
+
else console.log(pc2.dim(` ${dl.line}`));
|
|
2351
2545
|
}
|
|
2352
2546
|
if (diffLines.length > maxDiffLines) {
|
|
2353
2547
|
const remaining = diffLines.length - maxDiffLines;
|
|
2354
|
-
console.log(
|
|
2548
|
+
console.log(pc2.dim(` ... ${remaining} more lines \u2014 use ${pc2.cyan(`lumera diff ${change.resource}s/${change.name}`)} for full diff`));
|
|
2355
2549
|
}
|
|
2356
2550
|
console.log();
|
|
2357
2551
|
}
|
|
@@ -2359,7 +2553,8 @@ async function plan(args) {
|
|
|
2359
2553
|
}
|
|
2360
2554
|
}
|
|
2361
2555
|
console.log();
|
|
2362
|
-
|
|
2556
|
+
safePrintLint(lintWarnings, projectRoot);
|
|
2557
|
+
console.log(pc2.dim(` Run 'lumera apply' to apply these changes.`));
|
|
2363
2558
|
console.log();
|
|
2364
2559
|
}
|
|
2365
2560
|
async function apply(args) {
|
|
@@ -2373,17 +2568,21 @@ async function apply(args) {
|
|
|
2373
2568
|
const appName = getAppName(projectRoot);
|
|
2374
2569
|
const api = createApiClient(void 0, void 0, appName);
|
|
2375
2570
|
const projectId = getProjectId(projectRoot);
|
|
2376
|
-
const positionalArgs = args
|
|
2571
|
+
const positionalArgs = getPositionalArgs(args);
|
|
2377
2572
|
const { type, name } = parseResource(positionalArgs[0]);
|
|
2378
2573
|
const autoConfirm = args.includes("--yes") || args.includes("-y") || !!process.env.CI;
|
|
2574
|
+
const skipApp = args.includes("--no-app") || args.includes("--resources-only");
|
|
2575
|
+
if (type === "app" && skipApp) {
|
|
2576
|
+
throw new Error("Cannot combine app resource with --no-app / --resources-only");
|
|
2577
|
+
}
|
|
2379
2578
|
if (type === "app") {
|
|
2380
2579
|
console.log();
|
|
2381
|
-
console.log(
|
|
2580
|
+
console.log(pc2.cyan(pc2.bold(" Apply")));
|
|
2382
2581
|
console.log();
|
|
2383
|
-
console.log(
|
|
2582
|
+
console.log(pc2.bold(" App:"));
|
|
2384
2583
|
await applyApp(args);
|
|
2385
2584
|
console.log();
|
|
2386
|
-
console.log(
|
|
2585
|
+
console.log(pc2.green(" Done!"));
|
|
2387
2586
|
console.log();
|
|
2388
2587
|
return;
|
|
2389
2588
|
}
|
|
@@ -2411,12 +2610,12 @@ async function apply(args) {
|
|
|
2411
2610
|
const hasLocal = localCollections.length > 0 || localAutomations.length > 0 || localHooks.length > 0 || localAgents.length > 0 || localMailboxes.length > 0;
|
|
2412
2611
|
if (!hasLocal) {
|
|
2413
2612
|
console.log();
|
|
2414
|
-
console.log(
|
|
2613
|
+
console.log(pc2.red(` Resource "${name}" not found locally`));
|
|
2415
2614
|
process.exit(1);
|
|
2416
2615
|
}
|
|
2417
2616
|
}
|
|
2418
2617
|
let willDeployApp = false;
|
|
2419
|
-
if (!type) {
|
|
2618
|
+
if (!type && !skipApp) {
|
|
2420
2619
|
try {
|
|
2421
2620
|
if (existsSync(join(projectRoot, "dist")) || existsSync(join(projectRoot, "src"))) {
|
|
2422
2621
|
willDeployApp = true;
|
|
@@ -2426,35 +2625,37 @@ async function apply(args) {
|
|
|
2426
2625
|
}
|
|
2427
2626
|
if (allChanges.length === 0 && !willDeployApp) {
|
|
2428
2627
|
console.log();
|
|
2429
|
-
console.log(
|
|
2628
|
+
console.log(pc2.green(" \u2713 Nothing to apply \u2014 local and remote are in sync."));
|
|
2430
2629
|
console.log();
|
|
2630
|
+
safePrintLint(safeLint(projectRoot, localAutomations), projectRoot);
|
|
2431
2631
|
return;
|
|
2432
2632
|
}
|
|
2433
2633
|
console.log();
|
|
2434
|
-
console.log(
|
|
2634
|
+
console.log(pc2.cyan(pc2.bold(" Apply")));
|
|
2435
2635
|
console.log();
|
|
2436
2636
|
const creates = allChanges.filter((c) => c.type === "create");
|
|
2437
2637
|
const updates = allChanges.filter((c) => c.type === "update");
|
|
2438
2638
|
if (allChanges.length > 0) {
|
|
2439
|
-
console.log(
|
|
2639
|
+
console.log(pc2.bold(" Plan:"));
|
|
2440
2640
|
for (const change of allChanges) {
|
|
2441
2641
|
const icon = change.type === "create" ? "+" : "~";
|
|
2442
|
-
const color = change.type === "create" ?
|
|
2642
|
+
const color = change.type === "create" ? pc2.green : pc2.yellow;
|
|
2443
2643
|
const details = change.details ? ` (${change.details})` : "";
|
|
2444
|
-
console.log(` ${color(icon)} ${change.resource}: ${change.name}${
|
|
2644
|
+
console.log(` ${color(icon)} ${change.resource}: ${change.name}${pc2.dim(details)}`);
|
|
2445
2645
|
}
|
|
2446
|
-
if (willDeployApp) console.log(` ${
|
|
2646
|
+
if (willDeployApp) console.log(` ${pc2.blue("\u25CF")} app: frontend build + deploy`);
|
|
2447
2647
|
console.log();
|
|
2448
2648
|
const parts = [];
|
|
2449
|
-
if (creates.length > 0) parts.push(
|
|
2450
|
-
if (updates.length > 0) parts.push(
|
|
2451
|
-
if (willDeployApp) parts.push(
|
|
2649
|
+
if (creates.length > 0) parts.push(pc2.green(`${creates.length} create`));
|
|
2650
|
+
if (updates.length > 0) parts.push(pc2.yellow(`${updates.length} update`));
|
|
2651
|
+
if (willDeployApp) parts.push(pc2.blue("1 app deploy"));
|
|
2452
2652
|
console.log(` ${parts.join(", ")}`);
|
|
2453
2653
|
console.log();
|
|
2454
2654
|
} else if (willDeployApp) {
|
|
2455
|
-
console.log(
|
|
2655
|
+
console.log(pc2.dim(" No infrastructure changes \u2014 deploying app only."));
|
|
2456
2656
|
console.log();
|
|
2457
2657
|
}
|
|
2658
|
+
safePrintLint(safeLint(projectRoot, localAutomations), projectRoot);
|
|
2458
2659
|
if (!autoConfirm && allChanges.length > 0) {
|
|
2459
2660
|
const { confirm } = await prompts({
|
|
2460
2661
|
type: "confirm",
|
|
@@ -2463,7 +2664,7 @@ async function apply(args) {
|
|
|
2463
2664
|
initial: true
|
|
2464
2665
|
});
|
|
2465
2666
|
if (!confirm) {
|
|
2466
|
-
console.log(
|
|
2667
|
+
console.log(pc2.dim(" Cancelled."));
|
|
2467
2668
|
console.log();
|
|
2468
2669
|
return;
|
|
2469
2670
|
}
|
|
@@ -2474,7 +2675,7 @@ async function apply(args) {
|
|
|
2474
2675
|
let totalUpdated = 0;
|
|
2475
2676
|
let totalSkipped = 0;
|
|
2476
2677
|
if (localCollections.length > 0) {
|
|
2477
|
-
console.log(
|
|
2678
|
+
console.log(pc2.bold(" Collections:"));
|
|
2478
2679
|
totalErrors += await applyCollections(api, localCollections);
|
|
2479
2680
|
console.log();
|
|
2480
2681
|
}
|
|
@@ -2485,42 +2686,42 @@ async function apply(args) {
|
|
|
2485
2686
|
} catch {
|
|
2486
2687
|
}
|
|
2487
2688
|
if (localAutomations.length > 0) {
|
|
2488
|
-
console.log(
|
|
2689
|
+
console.log(pc2.bold(" Automations:"));
|
|
2489
2690
|
totalErrors += await applyAutomations(api, localAutomations, projectId);
|
|
2490
2691
|
console.log();
|
|
2491
2692
|
}
|
|
2492
2693
|
if (localHooks.length > 0) {
|
|
2493
|
-
console.log(
|
|
2694
|
+
console.log(pc2.bold(" Hooks:"));
|
|
2494
2695
|
totalErrors += await applyHooks(api, localHooks, collections, projectId);
|
|
2495
2696
|
console.log();
|
|
2496
2697
|
}
|
|
2497
2698
|
if (localAgents.length > 0) {
|
|
2498
|
-
console.log(
|
|
2699
|
+
console.log(pc2.bold(" Agents:"));
|
|
2499
2700
|
totalErrors += await applyAgents(api, localAgents, projectId);
|
|
2500
2701
|
console.log();
|
|
2501
2702
|
}
|
|
2502
2703
|
if (localMailboxes.length > 0) {
|
|
2503
|
-
console.log(
|
|
2704
|
+
console.log(pc2.bold(" Mailboxes:"));
|
|
2504
2705
|
totalErrors += await applyMailboxes(api, localMailboxes);
|
|
2505
2706
|
console.log();
|
|
2506
2707
|
}
|
|
2507
2708
|
if (willDeployApp) {
|
|
2508
|
-
console.log(
|
|
2709
|
+
console.log(pc2.bold(" App:"));
|
|
2509
2710
|
await applyApp(args);
|
|
2510
2711
|
console.log();
|
|
2511
2712
|
}
|
|
2512
2713
|
totalCreated = creates.length;
|
|
2513
2714
|
totalUpdated = updates.length;
|
|
2514
2715
|
if (totalErrors > 0) {
|
|
2515
|
-
console.log(
|
|
2716
|
+
console.log(pc2.red(` \u2717 ${totalErrors} error${totalErrors > 1 ? "s" : ""} during apply.`));
|
|
2516
2717
|
console.log();
|
|
2517
2718
|
process.exit(1);
|
|
2518
2719
|
}
|
|
2519
2720
|
const summary = [];
|
|
2520
|
-
if (totalCreated > 0) summary.push(
|
|
2521
|
-
if (totalUpdated > 0) summary.push(
|
|
2522
|
-
if (willDeployApp) summary.push(
|
|
2523
|
-
console.log(
|
|
2721
|
+
if (totalCreated > 0) summary.push(pc2.green(`${totalCreated} created`));
|
|
2722
|
+
if (totalUpdated > 0) summary.push(pc2.yellow(`${totalUpdated} updated`));
|
|
2723
|
+
if (willDeployApp) summary.push(pc2.blue("app deployed"));
|
|
2724
|
+
console.log(pc2.green(" \u2713 Done!") + (summary.length > 0 ? ` ${pc2.dim("\u2014")} ${summary.join(", ")}` : ""));
|
|
2524
2725
|
console.log();
|
|
2525
2726
|
}
|
|
2526
2727
|
async function pull(args) {
|
|
@@ -2575,47 +2776,47 @@ async function pull(args) {
|
|
|
2575
2776
|
}
|
|
2576
2777
|
if (conflicts.length > 0) {
|
|
2577
2778
|
console.log();
|
|
2578
|
-
console.log(
|
|
2779
|
+
console.log(pc2.yellow(` \u26A0 ${conflicts.length} local file${conflicts.length > 1 ? "s have" : " has"} changes that would be lost:`));
|
|
2579
2780
|
for (const f of conflicts) {
|
|
2580
|
-
console.log(
|
|
2781
|
+
console.log(pc2.dim(` ${f}`));
|
|
2581
2782
|
}
|
|
2582
2783
|
console.log();
|
|
2583
|
-
console.log(
|
|
2584
|
-
console.log(
|
|
2784
|
+
console.log(pc2.dim(` Use ${pc2.cyan("lumera diff <resource>")} to inspect changes.`));
|
|
2785
|
+
console.log(pc2.dim(` Use ${pc2.cyan("lumera pull --force")} to overwrite local files.`));
|
|
2585
2786
|
console.log();
|
|
2586
2787
|
process.exit(1);
|
|
2587
2788
|
}
|
|
2588
2789
|
}
|
|
2589
2790
|
console.log();
|
|
2590
|
-
console.log(
|
|
2591
|
-
console.log(
|
|
2791
|
+
console.log(pc2.cyan(pc2.bold(" Pull")));
|
|
2792
|
+
console.log(pc2.dim(` Downloading remote state to ${platformDir}/...`));
|
|
2592
2793
|
console.log();
|
|
2593
2794
|
if (!type || type === "collections") {
|
|
2594
|
-
console.log(
|
|
2795
|
+
console.log(pc2.bold(" Collections:"));
|
|
2595
2796
|
await pullCollections(api, platformDir, name || void 0, appName);
|
|
2596
2797
|
console.log();
|
|
2597
2798
|
}
|
|
2598
2799
|
if (!type || type === "automations") {
|
|
2599
|
-
console.log(
|
|
2800
|
+
console.log(pc2.bold(" Automations:"));
|
|
2600
2801
|
await pullAutomations(api, platformDir, name || void 0, projectId);
|
|
2601
2802
|
console.log();
|
|
2602
2803
|
}
|
|
2603
2804
|
if (!type || type === "hooks") {
|
|
2604
|
-
console.log(
|
|
2805
|
+
console.log(pc2.bold(" Hooks:"));
|
|
2605
2806
|
await pullHooks(api, platformDir, name || void 0, appName, projectId);
|
|
2606
2807
|
console.log();
|
|
2607
2808
|
}
|
|
2608
2809
|
if (!type || type === "agents") {
|
|
2609
|
-
console.log(
|
|
2810
|
+
console.log(pc2.bold(" Agents:"));
|
|
2610
2811
|
await pullAgents(api, platformDir, name || void 0, projectId);
|
|
2611
2812
|
console.log();
|
|
2612
2813
|
}
|
|
2613
2814
|
if (!type || type === "mailboxes") {
|
|
2614
|
-
console.log(
|
|
2815
|
+
console.log(pc2.bold(" Mailboxes:"));
|
|
2615
2816
|
await pullMailboxes(api, platformDir, name || void 0);
|
|
2616
2817
|
console.log();
|
|
2617
2818
|
}
|
|
2618
|
-
console.log(
|
|
2819
|
+
console.log(pc2.green(" Done!"));
|
|
2619
2820
|
console.log();
|
|
2620
2821
|
}
|
|
2621
2822
|
async function destroy(args) {
|
|
@@ -2633,7 +2834,7 @@ async function destroy(args) {
|
|
|
2633
2834
|
const skipConfirm = args.includes("--confirm");
|
|
2634
2835
|
const forceCycles = args.includes("--force-cycles");
|
|
2635
2836
|
console.log();
|
|
2636
|
-
console.log(
|
|
2837
|
+
console.log(pc2.red(pc2.bold(" Destroy")));
|
|
2637
2838
|
console.log();
|
|
2638
2839
|
if (type === "app") {
|
|
2639
2840
|
await destroyApp(skipConfirm);
|
|
@@ -2657,19 +2858,19 @@ async function list(args) {
|
|
|
2657
2858
|
const positionalArgs = args.filter((a) => !a.startsWith("--"));
|
|
2658
2859
|
const filterType = positionalArgs[0];
|
|
2659
2860
|
console.log();
|
|
2660
|
-
console.log(
|
|
2861
|
+
console.log(pc2.cyan(pc2.bold(" Resources")));
|
|
2661
2862
|
console.log();
|
|
2662
2863
|
const allResources = await listResources(api, platformDir, filterType, appName, projectId);
|
|
2663
2864
|
const remoteOnlyCount = allResources.filter((r) => r.status === "remote-only").length;
|
|
2664
2865
|
const resources = showAll ? allResources : allResources.filter((r) => r.status !== "remote-only");
|
|
2665
2866
|
if (resources.length === 0 && remoteOnlyCount === 0) {
|
|
2666
|
-
console.log(
|
|
2867
|
+
console.log(pc2.dim(" No resources found"));
|
|
2667
2868
|
console.log();
|
|
2668
2869
|
return;
|
|
2669
2870
|
}
|
|
2670
2871
|
if (resources.length === 0 && remoteOnlyCount > 0) {
|
|
2671
|
-
console.log(
|
|
2672
|
-
console.log(
|
|
2872
|
+
console.log(pc2.dim(" No local resources found"));
|
|
2873
|
+
console.log(pc2.dim(` ${remoteOnlyCount} remote-only resource(s) hidden. Use --all to show.`));
|
|
2673
2874
|
console.log();
|
|
2674
2875
|
return;
|
|
2675
2876
|
}
|
|
@@ -2679,29 +2880,29 @@ async function list(args) {
|
|
|
2679
2880
|
byType.get(r.type).push(r);
|
|
2680
2881
|
}
|
|
2681
2882
|
for (const [type, items] of byType) {
|
|
2682
|
-
console.log(
|
|
2883
|
+
console.log(pc2.bold(` ${type.charAt(0).toUpperCase() + type.slice(1)}:`));
|
|
2683
2884
|
for (const item of items) {
|
|
2684
2885
|
let icon;
|
|
2685
2886
|
let color;
|
|
2686
2887
|
switch (item.status) {
|
|
2687
2888
|
case "synced":
|
|
2688
2889
|
icon = "\u2713";
|
|
2689
|
-
color =
|
|
2890
|
+
color = pc2.green;
|
|
2690
2891
|
break;
|
|
2691
2892
|
case "changed":
|
|
2692
2893
|
icon = "~";
|
|
2693
|
-
color =
|
|
2894
|
+
color = pc2.yellow;
|
|
2694
2895
|
break;
|
|
2695
2896
|
case "local-only":
|
|
2696
2897
|
icon = "+";
|
|
2697
|
-
color =
|
|
2898
|
+
color = pc2.cyan;
|
|
2698
2899
|
break;
|
|
2699
2900
|
case "remote-only":
|
|
2700
2901
|
icon = "?";
|
|
2701
|
-
color =
|
|
2902
|
+
color = pc2.dim;
|
|
2702
2903
|
break;
|
|
2703
2904
|
}
|
|
2704
|
-
const details = item.details ?
|
|
2905
|
+
const details = item.details ? pc2.dim(` (${item.details})`) : "";
|
|
2705
2906
|
console.log(` ${color(icon)} ${item.name}${details}`);
|
|
2706
2907
|
}
|
|
2707
2908
|
console.log();
|
|
@@ -2711,14 +2912,14 @@ async function list(args) {
|
|
|
2711
2912
|
const localOnly = resources.filter((r) => r.status === "local-only").length;
|
|
2712
2913
|
const displayedRemoteOnly = resources.filter((r) => r.status === "remote-only").length;
|
|
2713
2914
|
const summary = [];
|
|
2714
|
-
if (synced > 0) summary.push(
|
|
2715
|
-
if (changed > 0) summary.push(
|
|
2716
|
-
if (localOnly > 0) summary.push(
|
|
2717
|
-
if (displayedRemoteOnly > 0) summary.push(
|
|
2915
|
+
if (synced > 0) summary.push(pc2.green(`${synced} synced`));
|
|
2916
|
+
if (changed > 0) summary.push(pc2.yellow(`${changed} changed`));
|
|
2917
|
+
if (localOnly > 0) summary.push(pc2.cyan(`${localOnly} local-only`));
|
|
2918
|
+
if (displayedRemoteOnly > 0) summary.push(pc2.dim(`${displayedRemoteOnly} remote-only`));
|
|
2718
2919
|
console.log(` ${summary.join(" | ")}`);
|
|
2719
|
-
console.log(
|
|
2920
|
+
console.log(pc2.dim(` ${pc2.green("\u2713")} synced ${pc2.yellow("~")} changed ${pc2.cyan("+")} local-only ? remote-only`));
|
|
2720
2921
|
if (!showAll && remoteOnlyCount > 0) {
|
|
2721
|
-
console.log(
|
|
2922
|
+
console.log(pc2.dim(` ${remoteOnlyCount} remote-only resource(s) hidden. Use --all to show.`));
|
|
2722
2923
|
}
|
|
2723
2924
|
console.log();
|
|
2724
2925
|
}
|
|
@@ -2736,15 +2937,15 @@ async function show(args) {
|
|
|
2736
2937
|
const projectId = getProjectId(projectRoot);
|
|
2737
2938
|
const { type, name } = parseResource(args[0]);
|
|
2738
2939
|
if (!type) {
|
|
2739
|
-
console.log(
|
|
2740
|
-
console.log(
|
|
2940
|
+
console.log(pc2.red(` Invalid resource path: ${args[0]}`));
|
|
2941
|
+
console.log(pc2.dim(" Use format: <type>/<name> (e.g., collections/users)"));
|
|
2741
2942
|
process.exit(1);
|
|
2742
2943
|
}
|
|
2743
2944
|
if (type === "app") {
|
|
2744
2945
|
await showResource(api, platformDir, "app", "", appName, projectId);
|
|
2745
2946
|
} else if (!name) {
|
|
2746
|
-
console.log(
|
|
2747
|
-
console.log(
|
|
2947
|
+
console.log(pc2.red(` Resource name required`));
|
|
2948
|
+
console.log(pc2.dim(" Use format: <type>/<name> (e.g., collections/users)"));
|
|
2748
2949
|
process.exit(1);
|
|
2749
2950
|
} else {
|
|
2750
2951
|
await showResource(api, platformDir, type, name, appName, projectId);
|
|
@@ -2752,17 +2953,17 @@ async function show(args) {
|
|
|
2752
2953
|
}
|
|
2753
2954
|
function showDiffHelp() {
|
|
2754
2955
|
console.log(`
|
|
2755
|
-
${
|
|
2956
|
+
${pc2.bold("lumera diff")} - Show full diff between local and remote state
|
|
2756
2957
|
|
|
2757
|
-
${
|
|
2958
|
+
${pc2.dim("Usage:")}
|
|
2758
2959
|
lumera diff <resource>
|
|
2759
2960
|
|
|
2760
|
-
${
|
|
2961
|
+
${pc2.dim("Resources:")}
|
|
2761
2962
|
agents/<name> Diff agent (system_prompt, policy_script)
|
|
2762
2963
|
automations/<name> Diff automation code
|
|
2763
2964
|
hooks/<name> Diff hook script
|
|
2764
2965
|
|
|
2765
|
-
${
|
|
2966
|
+
${pc2.dim("Examples:")}
|
|
2766
2967
|
lumera diff agents/bank_activity_matcher
|
|
2767
2968
|
lumera diff automations/sync
|
|
2768
2969
|
lumera diff hooks/encoding_protect_create
|
|
@@ -2771,14 +2972,14 @@ ${pc.dim("Examples:")}
|
|
|
2771
2972
|
function renderFullDiff(field, oldText, newText) {
|
|
2772
2973
|
const diffLines = computeLineDiff(oldText, newText);
|
|
2773
2974
|
if (diffLines.length === 0) {
|
|
2774
|
-
console.log(
|
|
2975
|
+
console.log(pc2.dim(` ${field}: no changes`));
|
|
2775
2976
|
return;
|
|
2776
2977
|
}
|
|
2777
|
-
console.log(
|
|
2978
|
+
console.log(pc2.bold(` --- ${field}`));
|
|
2778
2979
|
for (const dl of diffLines) {
|
|
2779
|
-
if (dl.type === "+") console.log(
|
|
2780
|
-
else if (dl.type === "-") console.log(
|
|
2781
|
-
else console.log(
|
|
2980
|
+
if (dl.type === "+") console.log(pc2.green(` + ${dl.line}`));
|
|
2981
|
+
else if (dl.type === "-") console.log(pc2.red(` - ${dl.line}`));
|
|
2982
|
+
else console.log(pc2.dim(` ${dl.line}`));
|
|
2782
2983
|
}
|
|
2783
2984
|
console.log();
|
|
2784
2985
|
}
|
|
@@ -2796,8 +2997,8 @@ async function diff(args) {
|
|
|
2796
2997
|
const projectId = getProjectId(projectRoot);
|
|
2797
2998
|
const { type, name } = parseResource(args[0]);
|
|
2798
2999
|
if (!type || !name) {
|
|
2799
|
-
console.log(
|
|
2800
|
-
console.log(
|
|
3000
|
+
console.log(pc2.red(` Invalid resource path: ${args[0]}`));
|
|
3001
|
+
console.log(pc2.dim(" Use format: <type>/<name> (e.g., agents/bank_activity_matcher)"));
|
|
2801
3002
|
process.exit(1);
|
|
2802
3003
|
}
|
|
2803
3004
|
console.log();
|
|
@@ -2810,31 +3011,31 @@ async function diff(args) {
|
|
|
2810
3011
|
(a) => a.external_id === name || a.name === name || localExtId && a.external_id === localExtId
|
|
2811
3012
|
);
|
|
2812
3013
|
if (!local && !remote) {
|
|
2813
|
-
console.log(
|
|
3014
|
+
console.log(pc2.red(` Agent "${name}" not found locally or remotely`));
|
|
2814
3015
|
process.exit(1);
|
|
2815
3016
|
}
|
|
2816
3017
|
if (!local) {
|
|
2817
|
-
console.log(
|
|
3018
|
+
console.log(pc2.cyan(` Agent "${name}" exists only remotely (not in local files)`));
|
|
2818
3019
|
process.exit(0);
|
|
2819
3020
|
}
|
|
2820
3021
|
if (!remote) {
|
|
2821
|
-
console.log(
|
|
3022
|
+
console.log(pc2.yellow(` Agent "${name}" exists only locally (not yet deployed)`));
|
|
2822
3023
|
process.exit(0);
|
|
2823
3024
|
}
|
|
2824
|
-
console.log(
|
|
3025
|
+
console.log(pc2.bold(` Agent: ${local.agent.name}`));
|
|
2825
3026
|
console.log();
|
|
2826
3027
|
if (remote.name !== local.agent.name)
|
|
2827
|
-
console.log(` name: ${
|
|
3028
|
+
console.log(` name: ${pc2.red(remote.name)} \u2192 ${pc2.green(local.agent.name)}`);
|
|
2828
3029
|
if ((remote.description || "") !== (local.agent.description || ""))
|
|
2829
|
-
console.log(` description: ${
|
|
3030
|
+
console.log(` description: ${pc2.red(remote.description || "(empty)")} \u2192 ${pc2.green(local.agent.description || "(empty)")}`);
|
|
2830
3031
|
if ((remote.model || "") !== (local.agent.model || ""))
|
|
2831
|
-
console.log(` model: ${
|
|
3032
|
+
console.log(` model: ${pc2.red(remote.model || "(default)")} \u2192 ${pc2.green(local.agent.model || "(default)")}`);
|
|
2832
3033
|
if ((remote.policy_enabled || false) !== (local.agent.policy_enabled || false))
|
|
2833
|
-
console.log(` policy_enabled: ${
|
|
3034
|
+
console.log(` policy_enabled: ${pc2.red(String(remote.policy_enabled || false))} \u2192 ${pc2.green(String(local.agent.policy_enabled || false))}`);
|
|
2834
3035
|
const promptChanged = (remote.system_prompt || "").trim() !== local.systemPrompt.trim();
|
|
2835
3036
|
const policyChanged = (remote.policy_script || "").trim() !== (local.policyScript || "").trim();
|
|
2836
3037
|
if (!promptChanged && !policyChanged && remote.name === local.agent.name && (remote.description || "") === (local.agent.description || "") && (remote.model || "") === (local.agent.model || "") && (remote.policy_enabled || false) === (local.agent.policy_enabled || false)) {
|
|
2837
|
-
console.log(
|
|
3038
|
+
console.log(pc2.green(` \u2713 No changes`));
|
|
2838
3039
|
} else {
|
|
2839
3040
|
if (promptChanged) renderFullDiff("system_prompt.md", remote.system_prompt || "", local.systemPrompt);
|
|
2840
3041
|
if (policyChanged) renderFullDiff("policy.js", remote.policy_script || "", local.policyScript);
|
|
@@ -2848,24 +3049,24 @@ async function diff(args) {
|
|
|
2848
3049
|
(a) => a.external_id === name || a.name === name || localExtId && a.external_id === localExtId
|
|
2849
3050
|
);
|
|
2850
3051
|
if (!local && !remote) {
|
|
2851
|
-
console.log(
|
|
3052
|
+
console.log(pc2.red(` Automation "${name}" not found locally or remotely`));
|
|
2852
3053
|
process.exit(1);
|
|
2853
3054
|
}
|
|
2854
3055
|
if (!local) {
|
|
2855
|
-
console.log(
|
|
3056
|
+
console.log(pc2.cyan(` Automation "${name}" exists only remotely`));
|
|
2856
3057
|
process.exit(0);
|
|
2857
3058
|
}
|
|
2858
3059
|
if (!remote) {
|
|
2859
|
-
console.log(
|
|
3060
|
+
console.log(pc2.yellow(` Automation "${name}" exists only locally (not yet deployed)`));
|
|
2860
3061
|
process.exit(0);
|
|
2861
3062
|
}
|
|
2862
|
-
console.log(
|
|
3063
|
+
console.log(pc2.bold(` Automation: ${local.automation.name}`));
|
|
2863
3064
|
console.log();
|
|
2864
3065
|
if (remote.name !== local.automation.name)
|
|
2865
|
-
console.log(` name: ${
|
|
3066
|
+
console.log(` name: ${pc2.red(remote.name)} \u2192 ${pc2.green(local.automation.name)}`);
|
|
2866
3067
|
const codeChanged = (remote.code || "") !== local.code;
|
|
2867
3068
|
if (!codeChanged && remote.name === local.automation.name) {
|
|
2868
|
-
console.log(
|
|
3069
|
+
console.log(pc2.green(` \u2713 No changes`));
|
|
2869
3070
|
} else if (codeChanged) {
|
|
2870
3071
|
renderFullDiff("main.py", remote.code || "", local.code);
|
|
2871
3072
|
}
|
|
@@ -2878,29 +3079,29 @@ async function diff(args) {
|
|
|
2878
3079
|
(h) => h.external_id === name || h.name === name || localExtId && h.external_id === localExtId
|
|
2879
3080
|
);
|
|
2880
3081
|
if (!local && !remote) {
|
|
2881
|
-
console.log(
|
|
3082
|
+
console.log(pc2.red(` Hook "${name}" not found locally or remotely`));
|
|
2882
3083
|
process.exit(1);
|
|
2883
3084
|
}
|
|
2884
3085
|
if (!local) {
|
|
2885
|
-
console.log(
|
|
3086
|
+
console.log(pc2.cyan(` Hook "${name}" exists only remotely`));
|
|
2886
3087
|
process.exit(0);
|
|
2887
3088
|
}
|
|
2888
3089
|
if (!remote) {
|
|
2889
|
-
console.log(
|
|
3090
|
+
console.log(pc2.yellow(` Hook "${name}" exists only locally (not yet deployed)`));
|
|
2890
3091
|
process.exit(0);
|
|
2891
3092
|
}
|
|
2892
|
-
console.log(
|
|
3093
|
+
console.log(pc2.bold(` Hook: ${local.hook.external_id}`));
|
|
2893
3094
|
console.log();
|
|
2894
3095
|
if (remote.event !== local.hook.trigger)
|
|
2895
|
-
console.log(` trigger: ${
|
|
3096
|
+
console.log(` trigger: ${pc2.red(remote.event)} \u2192 ${pc2.green(local.hook.trigger)}`);
|
|
2896
3097
|
const scriptChanged = (remote.script || "").trim() !== local.script.trim();
|
|
2897
3098
|
if (!scriptChanged && remote.event === local.hook.trigger) {
|
|
2898
|
-
console.log(
|
|
3099
|
+
console.log(pc2.green(` \u2713 No changes`));
|
|
2899
3100
|
} else if (scriptChanged) {
|
|
2900
3101
|
renderFullDiff(local.fileName, remote.script || "", local.script);
|
|
2901
3102
|
}
|
|
2902
3103
|
} else {
|
|
2903
|
-
console.log(
|
|
3104
|
+
console.log(pc2.red(` Diff not supported for "${type}" \u2014 use agents, automations, or hooks`));
|
|
2904
3105
|
process.exit(1);
|
|
2905
3106
|
}
|
|
2906
3107
|
console.log();
|