@lumerahq/cli 0.19.3-dev.0 → 0.19.4
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/README.md +5 -3
- package/dist/chunk-5T22627H.js +289 -0
- package/dist/chunk-H357NP7T.js +77 -0
- package/dist/{chunk-AUYOTENF.js → chunk-HU7RYLUF.js} +1 -1
- package/dist/{chunk-53NOF33P.js → chunk-SU26C4GL.js} +9 -49
- package/dist/{chunk-GKI2HQJC.js → chunk-UH5X43VV.js} +30 -3
- package/dist/{deps-ULTIIDYK.js → deps-GCOH3TXL.js} +4 -2
- package/dist/{dev-5JMHMS4U.js → dev-JLSTLIMZ.js} +14 -2
- package/dist/index.js +14 -14
- package/dist/{init-37XOMJLU.js → init-CBZAIERZ.js} +54 -24
- package/dist/{register-JJUMS4FI.js → register-N2LF3CCK.js} +1 -1
- package/dist/{resources-4IUZYKGX.js → resources-RHF2MDF7.js} +237 -159
- package/dist/{run-5ZOSPBGO.js → run-XJLB4AFD.js} +1 -1
- package/dist/{skills-TNJHMV4F.js → skills-REOKLNEF.js} +1 -1
- package/dist/{templates-M3RDNDDY.js → templates-LNUOTNLN.js} +2 -3
- package/package.json +1 -1
- package/templates/default/.agents/skills/.gitkeep +0 -0
- package/templates/default/AGENTS.md +151 -0
- package/templates/default/README.md +18 -0
- package/templates/default/_gitignore +14 -0
- package/templates/default/architecture.md +29 -0
- package/templates/default/biome.json +38 -0
- package/templates/default/components.json +21 -0
- package/templates/default/icon.svg +29 -0
- package/templates/default/index.html +16 -0
- package/templates/default/package.json +53 -0
- package/templates/default/platform/automations/.gitkeep +0 -0
- package/templates/default/platform/collections/.gitkeep +0 -0
- package/templates/default/platform/hooks/.gitkeep +0 -0
- package/templates/default/pyproject.toml +14 -0
- package/templates/default/scripts/.gitkeep +0 -0
- package/templates/default/src/components/layout.tsx +11 -0
- package/templates/default/src/lib/auth.ts +9 -0
- package/templates/default/src/lib/queries.ts +17 -0
- package/templates/default/src/lib/utils.ts +6 -0
- package/templates/default/src/main.tsx +130 -0
- package/templates/default/src/routes/__root.tsx +10 -0
- package/templates/default/src/routes/index.tsx +87 -0
- package/templates/default/src/styles.css +128 -0
- package/templates/default/template.json +7 -0
- package/templates/default/tsconfig.json +22 -0
- package/templates/default/vite.config.ts +28 -0
- package/dist/chunk-OQW5E7UT.js +0 -159
- package/dist/chunk-XDTWVFPE.js +0 -120
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
syncDeps
|
|
3
|
-
} from "./chunk-XDTWVFPE.js";
|
|
4
1
|
import {
|
|
5
2
|
deploy
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SU26C4GL.js";
|
|
4
|
+
import {
|
|
5
|
+
projectResourceDepsEnabled,
|
|
6
|
+
syncDeps
|
|
7
|
+
} from "./chunk-5T22627H.js";
|
|
7
8
|
import {
|
|
8
9
|
loadEnv
|
|
9
10
|
} from "./chunk-2CR762KB.js";
|
|
10
11
|
import {
|
|
11
12
|
createApiClient
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UH5X43VV.js";
|
|
13
14
|
import {
|
|
14
15
|
findProjectRoot,
|
|
15
16
|
getApiUrl,
|
|
@@ -28,8 +29,95 @@ import "./chunk-PNKVD2UK.js";
|
|
|
28
29
|
import pc2 from "picocolors";
|
|
29
30
|
import prompts from "prompts";
|
|
30
31
|
import { execFileSync, execSync } from "child_process";
|
|
31
|
-
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
32
|
-
import { join, resolve } from "path";
|
|
32
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
|
|
33
|
+
import { join as join2, resolve } from "path";
|
|
34
|
+
|
|
35
|
+
// src/lib/lint/index.ts
|
|
36
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
37
|
+
import { basename, join } from "path";
|
|
38
|
+
|
|
39
|
+
// src/lib/lint/rules/collection-schema.ts
|
|
40
|
+
function isRecord(value) {
|
|
41
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
42
|
+
}
|
|
43
|
+
function error(target, message, snippet) {
|
|
44
|
+
return {
|
|
45
|
+
ruleId: "collection-schema",
|
|
46
|
+
target,
|
|
47
|
+
severity: "error",
|
|
48
|
+
message,
|
|
49
|
+
snippet
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
var collectionSchemaRule = {
|
|
53
|
+
id: "collection-schema",
|
|
54
|
+
description: "Validates local collection JSON files before planning or applying resources.",
|
|
55
|
+
appliesTo: ["collection"],
|
|
56
|
+
check(target) {
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(target.source);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
62
|
+
return [error(target, `Invalid JSON: ${message}`)];
|
|
63
|
+
}
|
|
64
|
+
if (!isRecord(parsed)) {
|
|
65
|
+
return [error(target, "Collection file must contain a JSON object.")];
|
|
66
|
+
}
|
|
67
|
+
const issues = [];
|
|
68
|
+
const id = parsed.id;
|
|
69
|
+
const name = parsed.name;
|
|
70
|
+
const fields = parsed.fields;
|
|
71
|
+
const indexes = parsed.indexes;
|
|
72
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
73
|
+
issues.push(error(target, 'Missing required string field "id".'));
|
|
74
|
+
}
|
|
75
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
76
|
+
issues.push(error(target, 'Missing required string field "name".'));
|
|
77
|
+
} else {
|
|
78
|
+
if (/\s/.test(name)) {
|
|
79
|
+
issues.push(error(target, `Collection name "${name}" contains spaces; use underscores instead.`));
|
|
80
|
+
}
|
|
81
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
|
|
82
|
+
issues.push(error(target, `Collection name "${name}" contains invalid characters.`));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!Array.isArray(fields)) {
|
|
86
|
+
issues.push(error(target, 'Missing required array field "fields".'));
|
|
87
|
+
} else {
|
|
88
|
+
for (let i = 0; i < fields.length; i++) {
|
|
89
|
+
const field = fields[i];
|
|
90
|
+
if (!isRecord(field)) {
|
|
91
|
+
issues.push(error(target, `Field at index ${i} must be an object.`));
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (typeof field.name !== "string" || field.name.trim() === "") {
|
|
95
|
+
issues.push(error(target, `Field at index ${i} is missing required string field "name".`));
|
|
96
|
+
}
|
|
97
|
+
if (typeof field.type !== "string" || field.type.trim() === "") {
|
|
98
|
+
issues.push(error(target, `Field ${typeof field.name === "string" ? `"${field.name}"` : `at index ${i}`} is missing required string field "type".`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (indexes !== void 0) {
|
|
103
|
+
if (!Array.isArray(indexes)) {
|
|
104
|
+
issues.push(error(target, 'Optional field "indexes" must be an array when present.'));
|
|
105
|
+
} else {
|
|
106
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
107
|
+
const index = indexes[i];
|
|
108
|
+
if (!isRecord(index)) {
|
|
109
|
+
issues.push(error(target, `Index at index ${i} must be an object.`));
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!Array.isArray(index.fields) || !index.fields.every((field) => typeof field === "string" && field.trim() !== "")) {
|
|
113
|
+
issues.push(error(target, `Index at index ${i} must include a non-empty string array "fields".`));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return issues;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
33
121
|
|
|
34
122
|
// src/lib/lint/rules/llm-import.ts
|
|
35
123
|
var FROM_LUMERA_IMPORT_LLM = /^\s*from\s+lumera\s+import\s+\(?\s*(?:[\w\s,]*?\b)?llm\b/;
|
|
@@ -74,25 +162,37 @@ var llmImportRule = {
|
|
|
74
162
|
};
|
|
75
163
|
|
|
76
164
|
// src/lib/lint/registry.ts
|
|
77
|
-
var ALL_RULES = [llmImportRule];
|
|
165
|
+
var ALL_RULES = [collectionSchemaRule, llmImportRule];
|
|
78
166
|
|
|
79
167
|
// src/lib/lint/format.ts
|
|
80
168
|
import { relative } from "path";
|
|
81
169
|
import pc from "picocolors";
|
|
82
170
|
function printLintWarnings(warnings, projectRoot) {
|
|
83
171
|
if (warnings.length === 0) return;
|
|
172
|
+
const errors = warnings.filter((w) => w.severity === "error");
|
|
173
|
+
const advisory = warnings.filter((w) => w.severity !== "error");
|
|
84
174
|
console.log();
|
|
85
|
-
console.log(pc.bold(" Warnings:"));
|
|
175
|
+
console.log(pc.bold(errors.length > 0 ? " Lint issues:" : " Warnings:"));
|
|
86
176
|
for (const w of warnings) {
|
|
87
177
|
const rel = relative(projectRoot, w.target.filePath);
|
|
88
178
|
const loc = w.line ? `${rel}:${w.line}` : rel;
|
|
89
|
-
|
|
179
|
+
const isError = w.severity === "error";
|
|
180
|
+
const icon = isError ? pc.red("\u2717") : pc.yellow("\u26A0");
|
|
181
|
+
console.log(` ${icon} ${loc} ${pc.dim(`[${w.ruleId}]`)}`);
|
|
90
182
|
console.log(` ${w.message}`);
|
|
91
183
|
if (w.snippet) console.log(pc.dim(` > ${w.snippet}`));
|
|
92
184
|
}
|
|
93
185
|
console.log();
|
|
94
|
-
|
|
95
|
-
|
|
186
|
+
if (errors.length > 0) {
|
|
187
|
+
const n = errors.length;
|
|
188
|
+
console.log(pc.red(` ${n} error${n === 1 ? "" : "s"} \u2014 fix these before continuing.`));
|
|
189
|
+
if (advisory.length > 0) {
|
|
190
|
+
console.log(pc.dim(` ${advisory.length} warning${advisory.length === 1 ? "" : "s"} \u2014 advisory.`));
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
const n = advisory.length;
|
|
194
|
+
console.log(pc.dim(` ${n} warning${n === 1 ? "" : "s"} \u2014 advisory, will not block apply.`));
|
|
195
|
+
}
|
|
96
196
|
console.log();
|
|
97
197
|
}
|
|
98
198
|
function serializeLintWarnings(warnings, projectRoot) {
|
|
@@ -100,6 +200,7 @@ function serializeLintWarnings(warnings, projectRoot) {
|
|
|
100
200
|
ruleId: w.ruleId,
|
|
101
201
|
target: { kind: w.target.kind, name: w.target.name, filePath: relative(projectRoot, w.target.filePath) },
|
|
102
202
|
message: w.message,
|
|
203
|
+
severity: w.severity ?? "warning",
|
|
103
204
|
line: w.line,
|
|
104
205
|
snippet: w.snippet
|
|
105
206
|
}));
|
|
@@ -130,6 +231,22 @@ function buildAutomationTargets(localAutomations) {
|
|
|
130
231
|
source: a.code
|
|
131
232
|
}));
|
|
132
233
|
}
|
|
234
|
+
function buildCollectionTargets(platformDir, _filterName) {
|
|
235
|
+
const collectionsDir = join(platformDir, "collections");
|
|
236
|
+
if (!existsSync(collectionsDir)) return [];
|
|
237
|
+
const targets = [];
|
|
238
|
+
for (const entry of readdirSync(collectionsDir, { withFileTypes: true })) {
|
|
239
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
240
|
+
const filePath = join(collectionsDir, entry.name);
|
|
241
|
+
targets.push({
|
|
242
|
+
kind: "collection",
|
|
243
|
+
name: basename(entry.name, ".json"),
|
|
244
|
+
filePath,
|
|
245
|
+
source: readFileSync(filePath, "utf-8")
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return targets;
|
|
249
|
+
}
|
|
133
250
|
|
|
134
251
|
// src/commands/resources.ts
|
|
135
252
|
init_auth();
|
|
@@ -148,82 +265,20 @@ function safePrintLint(warnings, projectRoot) {
|
|
|
148
265
|
if (process.env.LUMERA_DEBUG) console.error("[lint] print failed:", err);
|
|
149
266
|
}
|
|
150
267
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
268
|
+
function lintCollectionFiles(projectRoot, platformDir, filterName) {
|
|
269
|
+
const issues = runLint({ projectRoot, targets: buildCollectionTargets(platformDir, filterName) });
|
|
270
|
+
const errors = issues.filter((issue) => issue.severity === "error");
|
|
271
|
+
if (errors.length === 0) return;
|
|
272
|
+
printLintWarnings(issues, projectRoot);
|
|
273
|
+
throw new Error(`Found ${errors.length} collection lint error(s)`);
|
|
155
274
|
}
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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;
|
|
275
|
+
function detectPackageManager() {
|
|
276
|
+
for (const pm of ["bun", "pnpm", "yarn", "npm"]) {
|
|
277
|
+
try {
|
|
278
|
+
execFileSync(pm, ["--version"], { stdio: "ignore" });
|
|
279
|
+
return pm;
|
|
280
|
+
} catch {
|
|
199
281
|
}
|
|
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;
|
|
227
282
|
}
|
|
228
283
|
return "npm";
|
|
229
284
|
}
|
|
@@ -503,9 +558,6 @@ ${pc2.dim("Resources:")}
|
|
|
503
558
|
${pc2.dim("Options:")}
|
|
504
559
|
--yes, -y Skip confirmation prompt (for CI/CD)
|
|
505
560
|
--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)
|
|
509
561
|
|
|
510
562
|
${pc2.dim("Examples:")}
|
|
511
563
|
lumera apply # Apply everything (shows plan, asks to confirm)
|
|
@@ -514,8 +566,6 @@ ${pc2.dim("Examples:")}
|
|
|
514
566
|
lumera apply agents -y # Apply agents without confirmation
|
|
515
567
|
lumera apply app # Deploy frontend
|
|
516
568
|
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
|
|
519
569
|
`);
|
|
520
570
|
}
|
|
521
571
|
function showPullHelp() {
|
|
@@ -641,29 +691,29 @@ function parseResource(resourcePath) {
|
|
|
641
691
|
return { type, name };
|
|
642
692
|
}
|
|
643
693
|
function getPlatformDir() {
|
|
644
|
-
if (
|
|
645
|
-
return
|
|
694
|
+
if (existsSync2(join2(process.cwd(), "platform"))) {
|
|
695
|
+
return join2(process.cwd(), "platform");
|
|
646
696
|
}
|
|
647
|
-
if (
|
|
648
|
-
return
|
|
697
|
+
if (existsSync2(join2(process.cwd(), "lumera_platform"))) {
|
|
698
|
+
return join2(process.cwd(), "lumera_platform");
|
|
649
699
|
}
|
|
650
|
-
return
|
|
700
|
+
return join2(process.cwd(), "platform");
|
|
651
701
|
}
|
|
652
702
|
function toSafeFilename(name) {
|
|
653
703
|
return name.replace(/\s+/g, "_").replace(/[^a-zA-Z0-9_-]/g, "").toLowerCase();
|
|
654
704
|
}
|
|
655
705
|
function loadLocalCollections(platformDir, filterName) {
|
|
656
|
-
const collectionsDir =
|
|
657
|
-
if (!
|
|
706
|
+
const collectionsDir = join2(platformDir, "collections");
|
|
707
|
+
if (!existsSync2(collectionsDir)) {
|
|
658
708
|
return [];
|
|
659
709
|
}
|
|
660
710
|
const collections = [];
|
|
661
711
|
const errors = [];
|
|
662
|
-
for (const file of
|
|
712
|
+
for (const file of readdirSync2(collectionsDir)) {
|
|
663
713
|
if (!file.endsWith(".json")) continue;
|
|
664
|
-
const filePath =
|
|
714
|
+
const filePath = join2(collectionsDir, file);
|
|
665
715
|
try {
|
|
666
|
-
const content =
|
|
716
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
667
717
|
const collection = JSON.parse(content);
|
|
668
718
|
if (filterName && collection.name !== filterName && collection.id !== filterName) {
|
|
669
719
|
continue;
|
|
@@ -684,6 +734,27 @@ function loadLocalCollections(platformDir, filterName) {
|
|
|
684
734
|
errors.push(`${file}: collection name "${collection.name}" contains invalid characters`);
|
|
685
735
|
continue;
|
|
686
736
|
}
|
|
737
|
+
if (!Array.isArray(collection.fields)) {
|
|
738
|
+
errors.push(`${file}: missing fields array`);
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
for (let i = 0; i < collection.fields.length; i++) {
|
|
742
|
+
const field = collection.fields[i];
|
|
743
|
+
if (!field || typeof field !== "object") {
|
|
744
|
+
errors.push(`${file}: field at index ${i} must be an object`);
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
if (typeof field.name !== "string" || field.name.trim() === "") {
|
|
748
|
+
errors.push(`${file}: field at index ${i} is missing name`);
|
|
749
|
+
}
|
|
750
|
+
if (typeof field.type !== "string" || field.type.trim() === "") {
|
|
751
|
+
errors.push(`${file}: field ${typeof field.name === "string" ? `"${field.name}"` : `at index ${i}`} is missing type`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
if (collection.indexes !== void 0 && !Array.isArray(collection.indexes)) {
|
|
755
|
+
errors.push(`${file}: indexes must be an array when present`);
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
687
758
|
const existingById = collections.find((c) => c.id === collection.id);
|
|
688
759
|
if (existingById) {
|
|
689
760
|
errors.push(`${file}: duplicate collection id "${collection.id}" (also defined in another file)`);
|
|
@@ -709,27 +780,27 @@ function loadLocalCollections(platformDir, filterName) {
|
|
|
709
780
|
return collections;
|
|
710
781
|
}
|
|
711
782
|
function loadLocalAutomations(platformDir, filterName, appName) {
|
|
712
|
-
const automationsDir =
|
|
713
|
-
if (!
|
|
783
|
+
const automationsDir = join2(platformDir, "automations");
|
|
784
|
+
if (!existsSync2(automationsDir)) {
|
|
714
785
|
return [];
|
|
715
786
|
}
|
|
716
787
|
const automations = [];
|
|
717
788
|
const errors = [];
|
|
718
|
-
for (const entry of
|
|
789
|
+
for (const entry of readdirSync2(automationsDir, { withFileTypes: true })) {
|
|
719
790
|
if (!entry.isDirectory()) continue;
|
|
720
|
-
const automationDir =
|
|
721
|
-
const configPath =
|
|
722
|
-
const mainPath =
|
|
723
|
-
if (!
|
|
791
|
+
const automationDir = join2(automationsDir, entry.name);
|
|
792
|
+
const configPath = join2(automationDir, "config.json");
|
|
793
|
+
const mainPath = join2(automationDir, "main.py");
|
|
794
|
+
if (!existsSync2(configPath)) {
|
|
724
795
|
errors.push(`${entry.name}: missing config.json`);
|
|
725
796
|
continue;
|
|
726
797
|
}
|
|
727
|
-
if (!
|
|
798
|
+
if (!existsSync2(mainPath)) {
|
|
728
799
|
errors.push(`${entry.name}: missing main.py`);
|
|
729
800
|
continue;
|
|
730
801
|
}
|
|
731
802
|
try {
|
|
732
|
-
const configContent =
|
|
803
|
+
const configContent = readFileSync2(configPath, "utf-8");
|
|
733
804
|
const rawConfig = JSON.parse(configContent);
|
|
734
805
|
if (filterName && rawConfig.external_id !== filterName && rawConfig.name !== filterName && entry.name !== filterName) {
|
|
735
806
|
continue;
|
|
@@ -756,7 +827,7 @@ function loadLocalAutomations(platformDir, filterName, appName) {
|
|
|
756
827
|
errors.push(`${entry.name}: duplicate external_id "${config.external_id}" (also defined in ${existingByExtId.automation.name})`);
|
|
757
828
|
continue;
|
|
758
829
|
}
|
|
759
|
-
let code =
|
|
830
|
+
let code = readFileSync2(mainPath, "utf-8");
|
|
760
831
|
if (appName) {
|
|
761
832
|
code = code.replaceAll("{{app}}", appName);
|
|
762
833
|
}
|
|
@@ -775,15 +846,15 @@ function loadLocalAutomations(platformDir, filterName, appName) {
|
|
|
775
846
|
return automations;
|
|
776
847
|
}
|
|
777
848
|
function loadLocalHooks(platformDir, filterName, appName) {
|
|
778
|
-
const hooksDir =
|
|
779
|
-
if (!
|
|
849
|
+
const hooksDir = join2(platformDir, "hooks");
|
|
850
|
+
if (!existsSync2(hooksDir)) {
|
|
780
851
|
return [];
|
|
781
852
|
}
|
|
782
853
|
const hooks = [];
|
|
783
|
-
for (const file of
|
|
854
|
+
for (const file of readdirSync2(hooksDir)) {
|
|
784
855
|
if (!file.endsWith(".js") && !file.endsWith(".ts")) continue;
|
|
785
|
-
const filePath =
|
|
786
|
-
const content =
|
|
856
|
+
const filePath = join2(hooksDir, file);
|
|
857
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
787
858
|
const config = parseHookConfig(content);
|
|
788
859
|
if (!config) {
|
|
789
860
|
console.log(pc2.yellow(` \u26A0 Skipping ${file}: could not parse config export`));
|
|
@@ -1099,12 +1170,12 @@ async function planAutomations(api, localAutomations) {
|
|
|
1099
1170
|
return changes;
|
|
1100
1171
|
}
|
|
1101
1172
|
function loadLocalMailboxes(platformDir, filterName) {
|
|
1102
|
-
const mailboxesDir =
|
|
1103
|
-
if (!
|
|
1173
|
+
const mailboxesDir = join2(platformDir, "mailboxes");
|
|
1174
|
+
if (!existsSync2(mailboxesDir)) {
|
|
1104
1175
|
return [];
|
|
1105
1176
|
}
|
|
1106
1177
|
const mailboxes = [];
|
|
1107
|
-
for (const file of
|
|
1178
|
+
for (const file of readdirSync2(mailboxesDir)) {
|
|
1108
1179
|
if (!file.endsWith(".json")) continue;
|
|
1109
1180
|
const slug = file.replace(/\.json$/, "").trim();
|
|
1110
1181
|
if (!slug) {
|
|
@@ -1114,10 +1185,10 @@ function loadLocalMailboxes(platformDir, filterName) {
|
|
|
1114
1185
|
if (filterName && slug !== filterName) {
|
|
1115
1186
|
continue;
|
|
1116
1187
|
}
|
|
1117
|
-
const filePath =
|
|
1188
|
+
const filePath = join2(mailboxesDir, file);
|
|
1118
1189
|
let raw;
|
|
1119
1190
|
try {
|
|
1120
|
-
raw = JSON.parse(
|
|
1191
|
+
raw = JSON.parse(readFileSync2(filePath, "utf-8"));
|
|
1121
1192
|
} catch (e) {
|
|
1122
1193
|
console.log(pc2.yellow(` \u26A0 Skipping ${file}: invalid JSON (${e.message})`));
|
|
1123
1194
|
continue;
|
|
@@ -1196,8 +1267,8 @@ async function pullMailboxes(api, platformDir, filterName) {
|
|
|
1196
1267
|
console.log(pc2.dim(" (no mailboxes)"));
|
|
1197
1268
|
return;
|
|
1198
1269
|
}
|
|
1199
|
-
const outDir =
|
|
1200
|
-
if (!
|
|
1270
|
+
const outDir = join2(platformDir, "mailboxes");
|
|
1271
|
+
if (!existsSync2(outDir)) {
|
|
1201
1272
|
mkdirSync(outDir, { recursive: true });
|
|
1202
1273
|
}
|
|
1203
1274
|
let count = 0;
|
|
@@ -1205,7 +1276,7 @@ async function pullMailboxes(api, platformDir, filterName) {
|
|
|
1205
1276
|
if (filterName && mb.slug !== filterName) continue;
|
|
1206
1277
|
const body = {};
|
|
1207
1278
|
if (mb.description) body.description = mb.description;
|
|
1208
|
-
const file =
|
|
1279
|
+
const file = join2(outDir, `${toSafeFilename(mb.slug)}.json`);
|
|
1209
1280
|
writeFileSync(file, JSON.stringify(body, null, 2) + "\n");
|
|
1210
1281
|
console.log(pc2.green(" \u2713"), `pulled mailbox ${mb.slug} ${pc2.dim(`\u2192 ${file}`)}`);
|
|
1211
1282
|
count++;
|
|
@@ -1464,7 +1535,7 @@ async function applyApp(args) {
|
|
|
1464
1535
|
if (!skipBuild) {
|
|
1465
1536
|
console.log(pc2.dim(" Building..."));
|
|
1466
1537
|
try {
|
|
1467
|
-
const pm = detectPackageManager(
|
|
1538
|
+
const pm = detectPackageManager();
|
|
1468
1539
|
execSync(`${pm} run build`, { cwd: projectRoot, stdio: "inherit" });
|
|
1469
1540
|
} catch {
|
|
1470
1541
|
throw new Error("Build failed");
|
|
@@ -1474,7 +1545,7 @@ async function applyApp(args) {
|
|
|
1474
1545
|
await deploy({ token, appName, appTitle, distDir, apiUrl });
|
|
1475
1546
|
}
|
|
1476
1547
|
async function pullCollections(api, platformDir, filterName, appName) {
|
|
1477
|
-
const collectionsDir =
|
|
1548
|
+
const collectionsDir = join2(platformDir, "collections");
|
|
1478
1549
|
mkdirSync(collectionsDir, { recursive: true });
|
|
1479
1550
|
const collections = await api.listCollections();
|
|
1480
1551
|
for (const collection of collections) {
|
|
@@ -1518,13 +1589,13 @@ async function pullCollections(api, platformDir, filterName, appName) {
|
|
|
1518
1589
|
}).filter((idx) => idx !== null)
|
|
1519
1590
|
};
|
|
1520
1591
|
const fileName = toSafeFilename(localName);
|
|
1521
|
-
const filePath =
|
|
1592
|
+
const filePath = join2(collectionsDir, `${fileName}.json`);
|
|
1522
1593
|
writeFileSync(filePath, JSON.stringify(localFormat, null, 2) + "\n");
|
|
1523
1594
|
console.log(pc2.green(" \u2713"), `${localName} \u2192 collections/${fileName}.json`);
|
|
1524
1595
|
}
|
|
1525
1596
|
}
|
|
1526
1597
|
async function pullAutomations(api, platformDir, filterName, projectId) {
|
|
1527
|
-
const automationsDir =
|
|
1598
|
+
const automationsDir = join2(platformDir, "automations");
|
|
1528
1599
|
mkdirSync(automationsDir, { recursive: true });
|
|
1529
1600
|
const automations = await api.listAutomations({ include_code: true });
|
|
1530
1601
|
for (const automation of automations) {
|
|
@@ -1535,7 +1606,7 @@ async function pullAutomations(api, platformDir, filterName, projectId) {
|
|
|
1535
1606
|
continue;
|
|
1536
1607
|
}
|
|
1537
1608
|
const dirName = automation.external_id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1538
|
-
const automationDir =
|
|
1609
|
+
const automationDir = join2(automationsDir, dirName);
|
|
1539
1610
|
mkdirSync(automationDir, { recursive: true });
|
|
1540
1611
|
const config = {
|
|
1541
1612
|
external_id: automation.external_id,
|
|
@@ -1568,13 +1639,13 @@ async function pullAutomations(api, platformDir, filterName, projectId) {
|
|
|
1568
1639
|
}
|
|
1569
1640
|
} catch {
|
|
1570
1641
|
}
|
|
1571
|
-
writeFileSync(
|
|
1572
|
-
writeFileSync(
|
|
1642
|
+
writeFileSync(join2(automationDir, "config.json"), JSON.stringify(config, null, 2) + "\n");
|
|
1643
|
+
writeFileSync(join2(automationDir, "main.py"), automation.code || "");
|
|
1573
1644
|
console.log(pc2.green(" \u2713"), `${automation.name} \u2192 automations/${dirName}/`);
|
|
1574
1645
|
}
|
|
1575
1646
|
}
|
|
1576
1647
|
async function pullHooks(api, platformDir, filterName, appName, projectId) {
|
|
1577
|
-
const hooksDir =
|
|
1648
|
+
const hooksDir = join2(platformDir, "hooks");
|
|
1578
1649
|
mkdirSync(hooksDir, { recursive: true });
|
|
1579
1650
|
const hooks = await api.listHooks();
|
|
1580
1651
|
for (const hook of hooks) {
|
|
@@ -1596,31 +1667,31 @@ export default async function handler({ record, app, http }) {
|
|
|
1596
1667
|
${hook.script.split("\n").map((line) => " " + line).join("\n")}
|
|
1597
1668
|
}
|
|
1598
1669
|
`;
|
|
1599
|
-
writeFileSync(
|
|
1670
|
+
writeFileSync(join2(hooksDir, fileName), content);
|
|
1600
1671
|
console.log(pc2.green(" \u2713"), `${hook.name} \u2192 hooks/${fileName}`);
|
|
1601
1672
|
}
|
|
1602
1673
|
}
|
|
1603
1674
|
function loadLocalAgents(platformDir, filterName, appName) {
|
|
1604
|
-
const agentsDir =
|
|
1605
|
-
if (!
|
|
1675
|
+
const agentsDir = join2(platformDir, "agents");
|
|
1676
|
+
if (!existsSync2(agentsDir)) return [];
|
|
1606
1677
|
const agents = [];
|
|
1607
1678
|
const errors = [];
|
|
1608
|
-
for (const entry of
|
|
1679
|
+
for (const entry of readdirSync2(agentsDir, { withFileTypes: true })) {
|
|
1609
1680
|
if (!entry.isDirectory()) continue;
|
|
1610
|
-
const agentDir =
|
|
1611
|
-
const configPath =
|
|
1612
|
-
const promptPath =
|
|
1613
|
-
const policyPath =
|
|
1614
|
-
if (!
|
|
1681
|
+
const agentDir = join2(agentsDir, entry.name);
|
|
1682
|
+
const configPath = join2(agentDir, "config.json");
|
|
1683
|
+
const promptPath = join2(agentDir, "system_prompt.md");
|
|
1684
|
+
const policyPath = join2(agentDir, "policy.js");
|
|
1685
|
+
if (!existsSync2(configPath)) {
|
|
1615
1686
|
errors.push(`${entry.name}: missing config.json`);
|
|
1616
1687
|
continue;
|
|
1617
1688
|
}
|
|
1618
|
-
if (!
|
|
1689
|
+
if (!existsSync2(promptPath)) {
|
|
1619
1690
|
errors.push(`${entry.name}: missing system_prompt.md`);
|
|
1620
1691
|
continue;
|
|
1621
1692
|
}
|
|
1622
1693
|
try {
|
|
1623
|
-
const config = JSON.parse(
|
|
1694
|
+
const config = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
1624
1695
|
if (filterName && config.external_id !== filterName && config.name !== filterName && entry.name !== filterName) {
|
|
1625
1696
|
continue;
|
|
1626
1697
|
}
|
|
@@ -1643,8 +1714,8 @@ function loadLocalAgents(platformDir, filterName, appName) {
|
|
|
1643
1714
|
errors.push(`${entry.name}: missing name in config.json`);
|
|
1644
1715
|
continue;
|
|
1645
1716
|
}
|
|
1646
|
-
let systemPrompt =
|
|
1647
|
-
let policyScript =
|
|
1717
|
+
let systemPrompt = readFileSync2(promptPath, "utf-8");
|
|
1718
|
+
let policyScript = existsSync2(policyPath) ? readFileSync2(policyPath, "utf-8") : "";
|
|
1648
1719
|
if (appName) {
|
|
1649
1720
|
systemPrompt = systemPrompt.replaceAll("{{app}}", appName);
|
|
1650
1721
|
policyScript = policyScript.replaceAll("{{app}}", appName);
|
|
@@ -1774,7 +1845,7 @@ async function applyAgents(api, localAgents, projectId) {
|
|
|
1774
1845
|
return errors;
|
|
1775
1846
|
}
|
|
1776
1847
|
async function pullAgents(api, platformDir, filterName, projectId) {
|
|
1777
|
-
const agentsDir =
|
|
1848
|
+
const agentsDir = join2(platformDir, "agents");
|
|
1778
1849
|
mkdirSync(agentsDir, { recursive: true });
|
|
1779
1850
|
const agents = await api.listAgents(projectId ? { project_id: projectId } : void 0);
|
|
1780
1851
|
let skillIdToSlug = /* @__PURE__ */ new Map();
|
|
@@ -1789,7 +1860,7 @@ async function pullAgents(api, platformDir, filterName, projectId) {
|
|
|
1789
1860
|
continue;
|
|
1790
1861
|
}
|
|
1791
1862
|
const dirName = agent.external_id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1792
|
-
const agentDir =
|
|
1863
|
+
const agentDir = join2(agentsDir, dirName);
|
|
1793
1864
|
mkdirSync(agentDir, { recursive: true });
|
|
1794
1865
|
const skillSlugs = [];
|
|
1795
1866
|
if (agent.skill_ids) {
|
|
@@ -1806,10 +1877,10 @@ async function pullAgents(api, platformDir, filterName, projectId) {
|
|
|
1806
1877
|
if (agent.model) config.model = agent.model;
|
|
1807
1878
|
if (skillSlugs.length > 0) config.skills = skillSlugs;
|
|
1808
1879
|
if (agent.policy_enabled) config.policy_enabled = true;
|
|
1809
|
-
writeFileSync(
|
|
1810
|
-
writeFileSync(
|
|
1880
|
+
writeFileSync(join2(agentDir, "config.json"), JSON.stringify(config, null, 2) + "\n");
|
|
1881
|
+
writeFileSync(join2(agentDir, "system_prompt.md"), agent.system_prompt || "");
|
|
1811
1882
|
if (agent.policy_script) {
|
|
1812
|
-
writeFileSync(
|
|
1883
|
+
writeFileSync(join2(agentDir, "policy.js"), agent.policy_script);
|
|
1813
1884
|
}
|
|
1814
1885
|
console.log(pc2.green(" \u2713"), `${agent.name} \u2192 agents/${dirName}/`);
|
|
1815
1886
|
}
|
|
@@ -2447,7 +2518,10 @@ async function plan(args) {
|
|
|
2447
2518
|
console.log(pc2.cyan(pc2.bold(" Plan")));
|
|
2448
2519
|
console.log(pc2.dim(" Comparing local files to remote state..."));
|
|
2449
2520
|
console.log();
|
|
2450
|
-
|
|
2521
|
+
if (!type || type === "collections") {
|
|
2522
|
+
lintCollectionFiles(projectRoot, platformDir, name || void 0);
|
|
2523
|
+
}
|
|
2524
|
+
await syncDeps(projectRoot, { ignorePermissionDenied: true });
|
|
2451
2525
|
const allChanges = [];
|
|
2452
2526
|
let collections;
|
|
2453
2527
|
try {
|
|
@@ -2568,13 +2642,9 @@ async function apply(args) {
|
|
|
2568
2642
|
const appName = getAppName(projectRoot);
|
|
2569
2643
|
const api = createApiClient(void 0, void 0, appName);
|
|
2570
2644
|
const projectId = getProjectId(projectRoot);
|
|
2571
|
-
const positionalArgs =
|
|
2645
|
+
const positionalArgs = args.filter((a) => !a.startsWith("-"));
|
|
2572
2646
|
const { type, name } = parseResource(positionalArgs[0]);
|
|
2573
2647
|
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
|
-
}
|
|
2578
2648
|
if (type === "app") {
|
|
2579
2649
|
console.log();
|
|
2580
2650
|
console.log(pc2.cyan(pc2.bold(" Apply")));
|
|
@@ -2586,7 +2656,10 @@ async function apply(args) {
|
|
|
2586
2656
|
console.log();
|
|
2587
2657
|
return;
|
|
2588
2658
|
}
|
|
2589
|
-
|
|
2659
|
+
if (!type || type === "collections") {
|
|
2660
|
+
lintCollectionFiles(projectRoot, platformDir, name || void 0);
|
|
2661
|
+
}
|
|
2662
|
+
await syncDeps(projectRoot, { ignorePermissionDenied: true });
|
|
2590
2663
|
let collections;
|
|
2591
2664
|
try {
|
|
2592
2665
|
const remoteCollections = await api.listCollections();
|
|
@@ -2615,9 +2688,9 @@ async function apply(args) {
|
|
|
2615
2688
|
}
|
|
2616
2689
|
}
|
|
2617
2690
|
let willDeployApp = false;
|
|
2618
|
-
if (!type
|
|
2691
|
+
if (!type) {
|
|
2619
2692
|
try {
|
|
2620
|
-
if (
|
|
2693
|
+
if (existsSync2(join2(projectRoot, "dist")) || existsSync2(join2(projectRoot, "src"))) {
|
|
2621
2694
|
willDeployApp = true;
|
|
2622
2695
|
}
|
|
2623
2696
|
} catch {
|
|
@@ -2795,6 +2868,11 @@ async function pull(args) {
|
|
|
2795
2868
|
console.log(pc2.bold(" Collections:"));
|
|
2796
2869
|
await pullCollections(api, platformDir, name || void 0, appName);
|
|
2797
2870
|
console.log();
|
|
2871
|
+
if (!name && await projectResourceDepsEnabled(projectRoot)) {
|
|
2872
|
+
console.log(pc2.bold(" Resource shares:"));
|
|
2873
|
+
await syncDeps(projectRoot, { write: true, legacyWhenDisabled: false, ignorePermissionDenied: true });
|
|
2874
|
+
console.log();
|
|
2875
|
+
}
|
|
2798
2876
|
}
|
|
2799
2877
|
if (!type || type === "automations") {
|
|
2800
2878
|
console.log(pc2.bold(" Automations:"));
|