@lumerahq/cli 0.19.3 → 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/dist/{chunk-P5HFNAVN.js → chunk-5T22627H.js} +12 -3
- package/dist/{chunk-AUYOTENF.js → chunk-HU7RYLUF.js} +1 -1
- package/dist/{chunk-JKXLKK5I.js → chunk-UH5X43VV.js} +16 -2
- package/dist/{deps-EC3VRNN7.js → deps-GCOH3TXL.js} +2 -2
- package/dist/{dev-R43VQCZD.js → dev-JLSTLIMZ.js} +8 -3
- package/dist/index.js +13 -13
- package/dist/{init-TDIQAOG4.js → init-CBZAIERZ.js} +26 -16
- package/dist/{register-JFJADKAJ.js → register-N2LF3CCK.js} +1 -1
- package/dist/{resources-OP7EECKZ.js → resources-RHF2MDF7.js} +221 -71
- package/dist/{run-EJP5WCQU.js → run-XJLB4AFD.js} +1 -1
- package/dist/{skills-TNJHMV4F.js → skills-REOKLNEF.js} +1 -1
- package/package.json +1 -1
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
loadEnv
|
|
3
3
|
} from "./chunk-2CR762KB.js";
|
|
4
4
|
import {
|
|
5
|
-
createApiClient
|
|
6
|
-
|
|
5
|
+
createApiClient,
|
|
6
|
+
isApiErrorStatus
|
|
7
|
+
} from "./chunk-UH5X43VV.js";
|
|
7
8
|
import {
|
|
8
9
|
findProjectRoot,
|
|
9
10
|
getAppName
|
|
@@ -86,6 +87,10 @@ async function syncResourceShareDeps(projectRoot, api, projectExternalId, opts)
|
|
|
86
87
|
try {
|
|
87
88
|
shares = await listIncomingCollectionShares(api, projectExternalId);
|
|
88
89
|
} catch (e) {
|
|
90
|
+
if (opts.ignorePermissionDenied && isApiErrorStatus(e, 403)) {
|
|
91
|
+
console.log(pc.yellow(" \u26A0"), "Skipping resource-share sync \u2014 current token cannot list project shares (403).");
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
89
94
|
if (!opts.quiet) {
|
|
90
95
|
console.log(pc.red(" \u2717"), `Failed to list incoming resource shares \u2014 ${e}`);
|
|
91
96
|
}
|
|
@@ -170,7 +175,11 @@ async function syncDeps(projectRoot, options = {}) {
|
|
|
170
175
|
const quiet = options.quiet === true;
|
|
171
176
|
const write = options.write === true;
|
|
172
177
|
if (flagOn) {
|
|
173
|
-
return syncResourceShareDeps(root, api, appName, {
|
|
178
|
+
return syncResourceShareDeps(root, api, appName, {
|
|
179
|
+
write,
|
|
180
|
+
quiet,
|
|
181
|
+
ignorePermissionDenied: options.ignorePermissionDenied === true
|
|
182
|
+
});
|
|
174
183
|
}
|
|
175
184
|
if (options.legacyWhenDisabled === false) {
|
|
176
185
|
return true;
|
|
@@ -31,7 +31,7 @@ async function fetchSkillsList() {
|
|
|
31
31
|
if (!listRes.ok) {
|
|
32
32
|
throw new Error(`Failed to fetch skills list: ${listRes.status}`);
|
|
33
33
|
}
|
|
34
|
-
return listRes.json();
|
|
34
|
+
return await listRes.json();
|
|
35
35
|
}
|
|
36
36
|
async function fetchSkillContent(slug) {
|
|
37
37
|
const baseUrl = getBaseUrl();
|
|
@@ -25,6 +25,19 @@ while (__pkgDir !== "/") {
|
|
|
25
25
|
}
|
|
26
26
|
var pkg = JSON.parse(readFileSync(resolve(__pkgDir, "package.json"), "utf-8"));
|
|
27
27
|
var CLI_USER_AGENT = `lumera-cli/${pkg.version}`;
|
|
28
|
+
var ApiError = class extends Error {
|
|
29
|
+
status;
|
|
30
|
+
body;
|
|
31
|
+
constructor(status, body) {
|
|
32
|
+
super(`API request failed: ${status} ${body}`);
|
|
33
|
+
this.name = "ApiError";
|
|
34
|
+
this.status = status;
|
|
35
|
+
this.body = body;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
function isApiErrorStatus(error, status) {
|
|
39
|
+
return error instanceof ApiError && error.status === status;
|
|
40
|
+
}
|
|
28
41
|
var ApiClient = class {
|
|
29
42
|
baseUrl;
|
|
30
43
|
token;
|
|
@@ -66,12 +79,12 @@ var ApiClient = class {
|
|
|
66
79
|
});
|
|
67
80
|
if (!response.ok) {
|
|
68
81
|
const text = await response.text();
|
|
69
|
-
throw new
|
|
82
|
+
throw new ApiError(response.status, text);
|
|
70
83
|
}
|
|
71
84
|
if (response.status === 204) {
|
|
72
85
|
return {};
|
|
73
86
|
}
|
|
74
|
-
return response.json();
|
|
87
|
+
return await response.json();
|
|
75
88
|
}
|
|
76
89
|
// Collections
|
|
77
90
|
async getMe() {
|
|
@@ -325,5 +338,6 @@ function createApiClient(token, baseUrl, projectExternalId) {
|
|
|
325
338
|
}
|
|
326
339
|
|
|
327
340
|
export {
|
|
341
|
+
isApiErrorStatus,
|
|
328
342
|
createApiClient
|
|
329
343
|
};
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
deps,
|
|
3
3
|
projectResourceDepsEnabled,
|
|
4
4
|
syncDeps
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-5T22627H.js";
|
|
6
6
|
import "./chunk-2CR762KB.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-UH5X43VV.js";
|
|
8
8
|
import "./chunk-ZH3NVYEQ.js";
|
|
9
9
|
import "./chunk-FJFIWC7G.js";
|
|
10
10
|
import "./chunk-PNKVD2UK.js";
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
projectResourceDepsEnabled,
|
|
6
6
|
syncDeps
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5T22627H.js";
|
|
8
8
|
import {
|
|
9
9
|
loadEnv
|
|
10
10
|
} from "./chunk-2CR762KB.js";
|
|
11
11
|
import {
|
|
12
12
|
createApiClient
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UH5X43VV.js";
|
|
14
14
|
import {
|
|
15
15
|
findProjectRoot,
|
|
16
16
|
getApiUrl,
|
|
@@ -126,7 +126,12 @@ async function dev2(args) {
|
|
|
126
126
|
const appTitle = getAppTitle(projectRoot);
|
|
127
127
|
const apiUrl = getApiUrl();
|
|
128
128
|
if (await projectResourceDepsEnabled(projectRoot)) {
|
|
129
|
-
await syncDeps(projectRoot, {
|
|
129
|
+
await syncDeps(projectRoot, {
|
|
130
|
+
write: true,
|
|
131
|
+
quiet: true,
|
|
132
|
+
legacyWhenDisabled: false,
|
|
133
|
+
ignorePermissionDenied: true
|
|
134
|
+
});
|
|
130
135
|
}
|
|
131
136
|
if (!flags["skip-setup"]) {
|
|
132
137
|
const fresh = await isFreshProject(projectRoot);
|
package/dist/index.js
CHANGED
|
@@ -219,39 +219,39 @@ async function main() {
|
|
|
219
219
|
switch (command) {
|
|
220
220
|
// Resource commands
|
|
221
221
|
case "plan":
|
|
222
|
-
await import("./resources-
|
|
222
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.plan(args.slice(1)));
|
|
223
223
|
break;
|
|
224
224
|
case "apply":
|
|
225
|
-
await import("./resources-
|
|
225
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.apply(args.slice(1)));
|
|
226
226
|
break;
|
|
227
227
|
case "pull":
|
|
228
|
-
await import("./resources-
|
|
228
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.pull(args.slice(1)));
|
|
229
229
|
break;
|
|
230
230
|
case "destroy":
|
|
231
|
-
await import("./resources-
|
|
231
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.destroy(args.slice(1)));
|
|
232
232
|
break;
|
|
233
233
|
case "list":
|
|
234
|
-
await import("./resources-
|
|
234
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.list(args.slice(1)));
|
|
235
235
|
break;
|
|
236
236
|
case "show":
|
|
237
|
-
await import("./resources-
|
|
237
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.show(args.slice(1)));
|
|
238
238
|
break;
|
|
239
239
|
case "diff":
|
|
240
|
-
await import("./resources-
|
|
240
|
+
await import("./resources-RHF2MDF7.js").then((m) => m.diff(args.slice(1)));
|
|
241
241
|
break;
|
|
242
242
|
// Development
|
|
243
243
|
case "dev":
|
|
244
|
-
await import("./dev-
|
|
244
|
+
await import("./dev-JLSTLIMZ.js").then((m) => m.dev(args.slice(1)));
|
|
245
245
|
break;
|
|
246
246
|
case "run":
|
|
247
|
-
await import("./run-
|
|
247
|
+
await import("./run-XJLB4AFD.js").then((m) => m.run(args.slice(1)));
|
|
248
248
|
break;
|
|
249
249
|
// Project
|
|
250
250
|
case "init":
|
|
251
|
-
await import("./init-
|
|
251
|
+
await import("./init-CBZAIERZ.js").then((m) => m.init(args.slice(1)));
|
|
252
252
|
break;
|
|
253
253
|
case "register":
|
|
254
|
-
await import("./register-
|
|
254
|
+
await import("./register-N2LF3CCK.js").then((m) => m.register(args.slice(1)));
|
|
255
255
|
break;
|
|
256
256
|
case "templates":
|
|
257
257
|
await import("./templates-LNUOTNLN.js").then((m) => m.templates(subcommand, args.slice(2)));
|
|
@@ -264,11 +264,11 @@ async function main() {
|
|
|
264
264
|
break;
|
|
265
265
|
// Skills
|
|
266
266
|
case "skills":
|
|
267
|
-
await import("./skills-
|
|
267
|
+
await import("./skills-REOKLNEF.js").then((m) => m.skills(subcommand, args.slice(2)));
|
|
268
268
|
break;
|
|
269
269
|
// Dependencies
|
|
270
270
|
case "deps":
|
|
271
|
-
await import("./deps-
|
|
271
|
+
await import("./deps-GCOH3TXL.js").then((m) => m.deps(args.slice(1)));
|
|
272
272
|
break;
|
|
273
273
|
// Auth
|
|
274
274
|
case "login":
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
installAllSkills,
|
|
3
3
|
syncClaudeMd
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HU7RYLUF.js";
|
|
5
5
|
import {
|
|
6
6
|
spinner
|
|
7
7
|
} from "./chunk-BHYDYR75.js";
|
|
8
8
|
import {
|
|
9
9
|
createApiClient
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-UH5X43VV.js";
|
|
11
11
|
import {
|
|
12
12
|
getToken,
|
|
13
13
|
init_auth,
|
|
@@ -287,19 +287,24 @@ async function init(args) {
|
|
|
287
287
|
}
|
|
288
288
|
projectName = response.projectName;
|
|
289
289
|
}
|
|
290
|
-
if (
|
|
290
|
+
if (!projectName) {
|
|
291
|
+
console.log(pc.red(" Error: Project name is required"));
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
const finalProjectName = projectName;
|
|
295
|
+
if (!/^[a-z0-9-]+$/.test(finalProjectName)) {
|
|
291
296
|
console.log(pc.red(" Error: Project name must use lowercase letters, numbers, and hyphens only"));
|
|
292
297
|
process.exit(1);
|
|
293
298
|
}
|
|
294
299
|
if (!directory) {
|
|
295
300
|
if (nonInteractive) {
|
|
296
|
-
directory =
|
|
301
|
+
directory = finalProjectName;
|
|
297
302
|
} else {
|
|
298
303
|
const response = await prompts({
|
|
299
304
|
type: "text",
|
|
300
305
|
name: "directory",
|
|
301
306
|
message: "Where should we create the project?",
|
|
302
|
-
initial:
|
|
307
|
+
initial: finalProjectName
|
|
303
308
|
});
|
|
304
309
|
if (!response.directory) {
|
|
305
310
|
console.log(pc.red("Cancelled"));
|
|
@@ -308,14 +313,19 @@ async function init(args) {
|
|
|
308
313
|
directory = response.directory;
|
|
309
314
|
}
|
|
310
315
|
}
|
|
311
|
-
|
|
312
|
-
|
|
316
|
+
if (!directory) {
|
|
317
|
+
console.log(pc.red(" Error: Directory is required"));
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
const finalDirectory = directory;
|
|
321
|
+
const projectTitle = toTitleCase(finalProjectName);
|
|
322
|
+
const targetDir = resolve(process.cwd(), finalDirectory);
|
|
313
323
|
if (existsSync(targetDir)) {
|
|
314
324
|
if (nonInteractive) {
|
|
315
325
|
if (opts.force) {
|
|
316
326
|
rmSync(targetDir, { recursive: true });
|
|
317
327
|
} else {
|
|
318
|
-
console.log(pc.red(` Error: Directory ${
|
|
328
|
+
console.log(pc.red(` Error: Directory ${finalDirectory} already exists`));
|
|
319
329
|
console.log(pc.dim(" Use --force (-f) to overwrite"));
|
|
320
330
|
process.exit(1);
|
|
321
331
|
}
|
|
@@ -323,7 +333,7 @@ async function init(args) {
|
|
|
323
333
|
const { overwrite } = await prompts({
|
|
324
334
|
type: "confirm",
|
|
325
335
|
name: "overwrite",
|
|
326
|
-
message: `Directory ${
|
|
336
|
+
message: `Directory ${finalDirectory} already exists. Overwrite?`,
|
|
327
337
|
initial: false
|
|
328
338
|
});
|
|
329
339
|
if (!overwrite) {
|
|
@@ -336,9 +346,9 @@ async function init(args) {
|
|
|
336
346
|
mkdirSync(targetDir, { recursive: true });
|
|
337
347
|
console.log();
|
|
338
348
|
if (templateName !== "default") {
|
|
339
|
-
console.log(pc.dim(` Creating ${
|
|
349
|
+
console.log(pc.dim(` Creating ${finalProjectName} from template ${pc.cyan(templateName)}...`));
|
|
340
350
|
} else {
|
|
341
|
-
console.log(pc.dim(` Creating ${
|
|
351
|
+
console.log(pc.dim(` Creating ${finalProjectName}...`));
|
|
342
352
|
}
|
|
343
353
|
console.log();
|
|
344
354
|
const templatePkgPath = join(templateDir, "package.json");
|
|
@@ -346,9 +356,9 @@ async function init(args) {
|
|
|
346
356
|
const sourceName = templatePkg.name || "my-lumera-app";
|
|
347
357
|
const sourceTitle = templatePkg.lumera?.name || toTitleCase(sourceName);
|
|
348
358
|
const replacements = [
|
|
349
|
-
["{{projectName}}",
|
|
359
|
+
["{{projectName}}", finalProjectName],
|
|
350
360
|
["{{projectTitle}}", projectTitle],
|
|
351
|
-
[sourceName,
|
|
361
|
+
[sourceName, finalProjectName],
|
|
352
362
|
[sourceTitle, projectTitle]
|
|
353
363
|
];
|
|
354
364
|
copyDir(templateDir, targetDir, replacements);
|
|
@@ -366,7 +376,7 @@ async function init(args) {
|
|
|
366
376
|
listFiles(targetDir);
|
|
367
377
|
if (isGitInstalled()) {
|
|
368
378
|
const stopGit = spinner("Initializing git repository...");
|
|
369
|
-
if (initGitRepo(targetDir,
|
|
379
|
+
if (initGitRepo(targetDir, finalProjectName)) {
|
|
370
380
|
stopGit(pc.green("\u2713") + pc.dim(" Git repository initialized with initial commit"));
|
|
371
381
|
} else {
|
|
372
382
|
stopGit(pc.yellow("\u26A0") + pc.dim(" Failed to initialize git repository"));
|
|
@@ -428,7 +438,7 @@ async function init(args) {
|
|
|
428
438
|
const api = createApiClient(token);
|
|
429
439
|
const stopRegister = spinner("Registering project on Lumera...");
|
|
430
440
|
try {
|
|
431
|
-
const project = await api.registerProject(
|
|
441
|
+
const project = await api.registerProject(finalProjectName);
|
|
432
442
|
setProjectId(targetDir, project.id);
|
|
433
443
|
stopRegister(pc.green("\u2713") + pc.dim(` Project registered (${project.id})`));
|
|
434
444
|
registered = true;
|
|
@@ -441,7 +451,7 @@ async function init(args) {
|
|
|
441
451
|
console.log();
|
|
442
452
|
console.log(pc.green(pc.bold(" Done!")), "Next steps:");
|
|
443
453
|
console.log();
|
|
444
|
-
console.log(pc.cyan(` cd ${
|
|
454
|
+
console.log(pc.cyan(` cd ${finalDirectory}`));
|
|
445
455
|
if (!opts.install) {
|
|
446
456
|
console.log(pc.cyan(" pnpm install"));
|
|
447
457
|
}
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
projectResourceDepsEnabled,
|
|
6
6
|
syncDeps
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5T22627H.js";
|
|
8
8
|
import {
|
|
9
9
|
loadEnv
|
|
10
10
|
} from "./chunk-2CR762KB.js";
|
|
11
11
|
import {
|
|
12
12
|
createApiClient
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UH5X43VV.js";
|
|
14
14
|
import {
|
|
15
15
|
findProjectRoot,
|
|
16
16
|
getApiUrl,
|
|
@@ -29,8 +29,95 @@ import "./chunk-PNKVD2UK.js";
|
|
|
29
29
|
import pc2 from "picocolors";
|
|
30
30
|
import prompts from "prompts";
|
|
31
31
|
import { execFileSync, execSync } from "child_process";
|
|
32
|
-
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
33
|
-
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
|
+
};
|
|
34
121
|
|
|
35
122
|
// src/lib/lint/rules/llm-import.ts
|
|
36
123
|
var FROM_LUMERA_IMPORT_LLM = /^\s*from\s+lumera\s+import\s+\(?\s*(?:[\w\s,]*?\b)?llm\b/;
|
|
@@ -75,25 +162,37 @@ var llmImportRule = {
|
|
|
75
162
|
};
|
|
76
163
|
|
|
77
164
|
// src/lib/lint/registry.ts
|
|
78
|
-
var ALL_RULES = [llmImportRule];
|
|
165
|
+
var ALL_RULES = [collectionSchemaRule, llmImportRule];
|
|
79
166
|
|
|
80
167
|
// src/lib/lint/format.ts
|
|
81
168
|
import { relative } from "path";
|
|
82
169
|
import pc from "picocolors";
|
|
83
170
|
function printLintWarnings(warnings, projectRoot) {
|
|
84
171
|
if (warnings.length === 0) return;
|
|
172
|
+
const errors = warnings.filter((w) => w.severity === "error");
|
|
173
|
+
const advisory = warnings.filter((w) => w.severity !== "error");
|
|
85
174
|
console.log();
|
|
86
|
-
console.log(pc.bold(" Warnings:"));
|
|
175
|
+
console.log(pc.bold(errors.length > 0 ? " Lint issues:" : " Warnings:"));
|
|
87
176
|
for (const w of warnings) {
|
|
88
177
|
const rel = relative(projectRoot, w.target.filePath);
|
|
89
178
|
const loc = w.line ? `${rel}:${w.line}` : rel;
|
|
90
|
-
|
|
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}]`)}`);
|
|
91
182
|
console.log(` ${w.message}`);
|
|
92
183
|
if (w.snippet) console.log(pc.dim(` > ${w.snippet}`));
|
|
93
184
|
}
|
|
94
185
|
console.log();
|
|
95
|
-
|
|
96
|
-
|
|
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
|
+
}
|
|
97
196
|
console.log();
|
|
98
197
|
}
|
|
99
198
|
function serializeLintWarnings(warnings, projectRoot) {
|
|
@@ -101,6 +200,7 @@ function serializeLintWarnings(warnings, projectRoot) {
|
|
|
101
200
|
ruleId: w.ruleId,
|
|
102
201
|
target: { kind: w.target.kind, name: w.target.name, filePath: relative(projectRoot, w.target.filePath) },
|
|
103
202
|
message: w.message,
|
|
203
|
+
severity: w.severity ?? "warning",
|
|
104
204
|
line: w.line,
|
|
105
205
|
snippet: w.snippet
|
|
106
206
|
}));
|
|
@@ -131,6 +231,22 @@ function buildAutomationTargets(localAutomations) {
|
|
|
131
231
|
source: a.code
|
|
132
232
|
}));
|
|
133
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
|
+
}
|
|
134
250
|
|
|
135
251
|
// src/commands/resources.ts
|
|
136
252
|
init_auth();
|
|
@@ -149,6 +265,13 @@ function safePrintLint(warnings, projectRoot) {
|
|
|
149
265
|
if (process.env.LUMERA_DEBUG) console.error("[lint] print failed:", err);
|
|
150
266
|
}
|
|
151
267
|
}
|
|
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)`);
|
|
274
|
+
}
|
|
152
275
|
function detectPackageManager() {
|
|
153
276
|
for (const pm of ["bun", "pnpm", "yarn", "npm"]) {
|
|
154
277
|
try {
|
|
@@ -568,29 +691,29 @@ function parseResource(resourcePath) {
|
|
|
568
691
|
return { type, name };
|
|
569
692
|
}
|
|
570
693
|
function getPlatformDir() {
|
|
571
|
-
if (
|
|
572
|
-
return
|
|
694
|
+
if (existsSync2(join2(process.cwd(), "platform"))) {
|
|
695
|
+
return join2(process.cwd(), "platform");
|
|
573
696
|
}
|
|
574
|
-
if (
|
|
575
|
-
return
|
|
697
|
+
if (existsSync2(join2(process.cwd(), "lumera_platform"))) {
|
|
698
|
+
return join2(process.cwd(), "lumera_platform");
|
|
576
699
|
}
|
|
577
|
-
return
|
|
700
|
+
return join2(process.cwd(), "platform");
|
|
578
701
|
}
|
|
579
702
|
function toSafeFilename(name) {
|
|
580
703
|
return name.replace(/\s+/g, "_").replace(/[^a-zA-Z0-9_-]/g, "").toLowerCase();
|
|
581
704
|
}
|
|
582
705
|
function loadLocalCollections(platformDir, filterName) {
|
|
583
|
-
const collectionsDir =
|
|
584
|
-
if (!
|
|
706
|
+
const collectionsDir = join2(platformDir, "collections");
|
|
707
|
+
if (!existsSync2(collectionsDir)) {
|
|
585
708
|
return [];
|
|
586
709
|
}
|
|
587
710
|
const collections = [];
|
|
588
711
|
const errors = [];
|
|
589
|
-
for (const file of
|
|
712
|
+
for (const file of readdirSync2(collectionsDir)) {
|
|
590
713
|
if (!file.endsWith(".json")) continue;
|
|
591
|
-
const filePath =
|
|
714
|
+
const filePath = join2(collectionsDir, file);
|
|
592
715
|
try {
|
|
593
|
-
const content =
|
|
716
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
594
717
|
const collection = JSON.parse(content);
|
|
595
718
|
if (filterName && collection.name !== filterName && collection.id !== filterName) {
|
|
596
719
|
continue;
|
|
@@ -611,6 +734,27 @@ function loadLocalCollections(platformDir, filterName) {
|
|
|
611
734
|
errors.push(`${file}: collection name "${collection.name}" contains invalid characters`);
|
|
612
735
|
continue;
|
|
613
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
|
+
}
|
|
614
758
|
const existingById = collections.find((c) => c.id === collection.id);
|
|
615
759
|
if (existingById) {
|
|
616
760
|
errors.push(`${file}: duplicate collection id "${collection.id}" (also defined in another file)`);
|
|
@@ -636,27 +780,27 @@ function loadLocalCollections(platformDir, filterName) {
|
|
|
636
780
|
return collections;
|
|
637
781
|
}
|
|
638
782
|
function loadLocalAutomations(platformDir, filterName, appName) {
|
|
639
|
-
const automationsDir =
|
|
640
|
-
if (!
|
|
783
|
+
const automationsDir = join2(platformDir, "automations");
|
|
784
|
+
if (!existsSync2(automationsDir)) {
|
|
641
785
|
return [];
|
|
642
786
|
}
|
|
643
787
|
const automations = [];
|
|
644
788
|
const errors = [];
|
|
645
|
-
for (const entry of
|
|
789
|
+
for (const entry of readdirSync2(automationsDir, { withFileTypes: true })) {
|
|
646
790
|
if (!entry.isDirectory()) continue;
|
|
647
|
-
const automationDir =
|
|
648
|
-
const configPath =
|
|
649
|
-
const mainPath =
|
|
650
|
-
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)) {
|
|
651
795
|
errors.push(`${entry.name}: missing config.json`);
|
|
652
796
|
continue;
|
|
653
797
|
}
|
|
654
|
-
if (!
|
|
798
|
+
if (!existsSync2(mainPath)) {
|
|
655
799
|
errors.push(`${entry.name}: missing main.py`);
|
|
656
800
|
continue;
|
|
657
801
|
}
|
|
658
802
|
try {
|
|
659
|
-
const configContent =
|
|
803
|
+
const configContent = readFileSync2(configPath, "utf-8");
|
|
660
804
|
const rawConfig = JSON.parse(configContent);
|
|
661
805
|
if (filterName && rawConfig.external_id !== filterName && rawConfig.name !== filterName && entry.name !== filterName) {
|
|
662
806
|
continue;
|
|
@@ -683,7 +827,7 @@ function loadLocalAutomations(platformDir, filterName, appName) {
|
|
|
683
827
|
errors.push(`${entry.name}: duplicate external_id "${config.external_id}" (also defined in ${existingByExtId.automation.name})`);
|
|
684
828
|
continue;
|
|
685
829
|
}
|
|
686
|
-
let code =
|
|
830
|
+
let code = readFileSync2(mainPath, "utf-8");
|
|
687
831
|
if (appName) {
|
|
688
832
|
code = code.replaceAll("{{app}}", appName);
|
|
689
833
|
}
|
|
@@ -702,15 +846,15 @@ function loadLocalAutomations(platformDir, filterName, appName) {
|
|
|
702
846
|
return automations;
|
|
703
847
|
}
|
|
704
848
|
function loadLocalHooks(platformDir, filterName, appName) {
|
|
705
|
-
const hooksDir =
|
|
706
|
-
if (!
|
|
849
|
+
const hooksDir = join2(platformDir, "hooks");
|
|
850
|
+
if (!existsSync2(hooksDir)) {
|
|
707
851
|
return [];
|
|
708
852
|
}
|
|
709
853
|
const hooks = [];
|
|
710
|
-
for (const file of
|
|
854
|
+
for (const file of readdirSync2(hooksDir)) {
|
|
711
855
|
if (!file.endsWith(".js") && !file.endsWith(".ts")) continue;
|
|
712
|
-
const filePath =
|
|
713
|
-
const content =
|
|
856
|
+
const filePath = join2(hooksDir, file);
|
|
857
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
714
858
|
const config = parseHookConfig(content);
|
|
715
859
|
if (!config) {
|
|
716
860
|
console.log(pc2.yellow(` \u26A0 Skipping ${file}: could not parse config export`));
|
|
@@ -1026,12 +1170,12 @@ async function planAutomations(api, localAutomations) {
|
|
|
1026
1170
|
return changes;
|
|
1027
1171
|
}
|
|
1028
1172
|
function loadLocalMailboxes(platformDir, filterName) {
|
|
1029
|
-
const mailboxesDir =
|
|
1030
|
-
if (!
|
|
1173
|
+
const mailboxesDir = join2(platformDir, "mailboxes");
|
|
1174
|
+
if (!existsSync2(mailboxesDir)) {
|
|
1031
1175
|
return [];
|
|
1032
1176
|
}
|
|
1033
1177
|
const mailboxes = [];
|
|
1034
|
-
for (const file of
|
|
1178
|
+
for (const file of readdirSync2(mailboxesDir)) {
|
|
1035
1179
|
if (!file.endsWith(".json")) continue;
|
|
1036
1180
|
const slug = file.replace(/\.json$/, "").trim();
|
|
1037
1181
|
if (!slug) {
|
|
@@ -1041,10 +1185,10 @@ function loadLocalMailboxes(platformDir, filterName) {
|
|
|
1041
1185
|
if (filterName && slug !== filterName) {
|
|
1042
1186
|
continue;
|
|
1043
1187
|
}
|
|
1044
|
-
const filePath =
|
|
1188
|
+
const filePath = join2(mailboxesDir, file);
|
|
1045
1189
|
let raw;
|
|
1046
1190
|
try {
|
|
1047
|
-
raw = JSON.parse(
|
|
1191
|
+
raw = JSON.parse(readFileSync2(filePath, "utf-8"));
|
|
1048
1192
|
} catch (e) {
|
|
1049
1193
|
console.log(pc2.yellow(` \u26A0 Skipping ${file}: invalid JSON (${e.message})`));
|
|
1050
1194
|
continue;
|
|
@@ -1123,8 +1267,8 @@ async function pullMailboxes(api, platformDir, filterName) {
|
|
|
1123
1267
|
console.log(pc2.dim(" (no mailboxes)"));
|
|
1124
1268
|
return;
|
|
1125
1269
|
}
|
|
1126
|
-
const outDir =
|
|
1127
|
-
if (!
|
|
1270
|
+
const outDir = join2(platformDir, "mailboxes");
|
|
1271
|
+
if (!existsSync2(outDir)) {
|
|
1128
1272
|
mkdirSync(outDir, { recursive: true });
|
|
1129
1273
|
}
|
|
1130
1274
|
let count = 0;
|
|
@@ -1132,7 +1276,7 @@ async function pullMailboxes(api, platformDir, filterName) {
|
|
|
1132
1276
|
if (filterName && mb.slug !== filterName) continue;
|
|
1133
1277
|
const body = {};
|
|
1134
1278
|
if (mb.description) body.description = mb.description;
|
|
1135
|
-
const file =
|
|
1279
|
+
const file = join2(outDir, `${toSafeFilename(mb.slug)}.json`);
|
|
1136
1280
|
writeFileSync(file, JSON.stringify(body, null, 2) + "\n");
|
|
1137
1281
|
console.log(pc2.green(" \u2713"), `pulled mailbox ${mb.slug} ${pc2.dim(`\u2192 ${file}`)}`);
|
|
1138
1282
|
count++;
|
|
@@ -1401,7 +1545,7 @@ async function applyApp(args) {
|
|
|
1401
1545
|
await deploy({ token, appName, appTitle, distDir, apiUrl });
|
|
1402
1546
|
}
|
|
1403
1547
|
async function pullCollections(api, platformDir, filterName, appName) {
|
|
1404
|
-
const collectionsDir =
|
|
1548
|
+
const collectionsDir = join2(platformDir, "collections");
|
|
1405
1549
|
mkdirSync(collectionsDir, { recursive: true });
|
|
1406
1550
|
const collections = await api.listCollections();
|
|
1407
1551
|
for (const collection of collections) {
|
|
@@ -1445,13 +1589,13 @@ async function pullCollections(api, platformDir, filterName, appName) {
|
|
|
1445
1589
|
}).filter((idx) => idx !== null)
|
|
1446
1590
|
};
|
|
1447
1591
|
const fileName = toSafeFilename(localName);
|
|
1448
|
-
const filePath =
|
|
1592
|
+
const filePath = join2(collectionsDir, `${fileName}.json`);
|
|
1449
1593
|
writeFileSync(filePath, JSON.stringify(localFormat, null, 2) + "\n");
|
|
1450
1594
|
console.log(pc2.green(" \u2713"), `${localName} \u2192 collections/${fileName}.json`);
|
|
1451
1595
|
}
|
|
1452
1596
|
}
|
|
1453
1597
|
async function pullAutomations(api, platformDir, filterName, projectId) {
|
|
1454
|
-
const automationsDir =
|
|
1598
|
+
const automationsDir = join2(platformDir, "automations");
|
|
1455
1599
|
mkdirSync(automationsDir, { recursive: true });
|
|
1456
1600
|
const automations = await api.listAutomations({ include_code: true });
|
|
1457
1601
|
for (const automation of automations) {
|
|
@@ -1462,7 +1606,7 @@ async function pullAutomations(api, platformDir, filterName, projectId) {
|
|
|
1462
1606
|
continue;
|
|
1463
1607
|
}
|
|
1464
1608
|
const dirName = automation.external_id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1465
|
-
const automationDir =
|
|
1609
|
+
const automationDir = join2(automationsDir, dirName);
|
|
1466
1610
|
mkdirSync(automationDir, { recursive: true });
|
|
1467
1611
|
const config = {
|
|
1468
1612
|
external_id: automation.external_id,
|
|
@@ -1495,13 +1639,13 @@ async function pullAutomations(api, platformDir, filterName, projectId) {
|
|
|
1495
1639
|
}
|
|
1496
1640
|
} catch {
|
|
1497
1641
|
}
|
|
1498
|
-
writeFileSync(
|
|
1499
|
-
writeFileSync(
|
|
1642
|
+
writeFileSync(join2(automationDir, "config.json"), JSON.stringify(config, null, 2) + "\n");
|
|
1643
|
+
writeFileSync(join2(automationDir, "main.py"), automation.code || "");
|
|
1500
1644
|
console.log(pc2.green(" \u2713"), `${automation.name} \u2192 automations/${dirName}/`);
|
|
1501
1645
|
}
|
|
1502
1646
|
}
|
|
1503
1647
|
async function pullHooks(api, platformDir, filterName, appName, projectId) {
|
|
1504
|
-
const hooksDir =
|
|
1648
|
+
const hooksDir = join2(platformDir, "hooks");
|
|
1505
1649
|
mkdirSync(hooksDir, { recursive: true });
|
|
1506
1650
|
const hooks = await api.listHooks();
|
|
1507
1651
|
for (const hook of hooks) {
|
|
@@ -1523,31 +1667,31 @@ export default async function handler({ record, app, http }) {
|
|
|
1523
1667
|
${hook.script.split("\n").map((line) => " " + line).join("\n")}
|
|
1524
1668
|
}
|
|
1525
1669
|
`;
|
|
1526
|
-
writeFileSync(
|
|
1670
|
+
writeFileSync(join2(hooksDir, fileName), content);
|
|
1527
1671
|
console.log(pc2.green(" \u2713"), `${hook.name} \u2192 hooks/${fileName}`);
|
|
1528
1672
|
}
|
|
1529
1673
|
}
|
|
1530
1674
|
function loadLocalAgents(platformDir, filterName, appName) {
|
|
1531
|
-
const agentsDir =
|
|
1532
|
-
if (!
|
|
1675
|
+
const agentsDir = join2(platformDir, "agents");
|
|
1676
|
+
if (!existsSync2(agentsDir)) return [];
|
|
1533
1677
|
const agents = [];
|
|
1534
1678
|
const errors = [];
|
|
1535
|
-
for (const entry of
|
|
1679
|
+
for (const entry of readdirSync2(agentsDir, { withFileTypes: true })) {
|
|
1536
1680
|
if (!entry.isDirectory()) continue;
|
|
1537
|
-
const agentDir =
|
|
1538
|
-
const configPath =
|
|
1539
|
-
const promptPath =
|
|
1540
|
-
const policyPath =
|
|
1541
|
-
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)) {
|
|
1542
1686
|
errors.push(`${entry.name}: missing config.json`);
|
|
1543
1687
|
continue;
|
|
1544
1688
|
}
|
|
1545
|
-
if (!
|
|
1689
|
+
if (!existsSync2(promptPath)) {
|
|
1546
1690
|
errors.push(`${entry.name}: missing system_prompt.md`);
|
|
1547
1691
|
continue;
|
|
1548
1692
|
}
|
|
1549
1693
|
try {
|
|
1550
|
-
const config = JSON.parse(
|
|
1694
|
+
const config = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
1551
1695
|
if (filterName && config.external_id !== filterName && config.name !== filterName && entry.name !== filterName) {
|
|
1552
1696
|
continue;
|
|
1553
1697
|
}
|
|
@@ -1570,8 +1714,8 @@ function loadLocalAgents(platformDir, filterName, appName) {
|
|
|
1570
1714
|
errors.push(`${entry.name}: missing name in config.json`);
|
|
1571
1715
|
continue;
|
|
1572
1716
|
}
|
|
1573
|
-
let systemPrompt =
|
|
1574
|
-
let policyScript =
|
|
1717
|
+
let systemPrompt = readFileSync2(promptPath, "utf-8");
|
|
1718
|
+
let policyScript = existsSync2(policyPath) ? readFileSync2(policyPath, "utf-8") : "";
|
|
1575
1719
|
if (appName) {
|
|
1576
1720
|
systemPrompt = systemPrompt.replaceAll("{{app}}", appName);
|
|
1577
1721
|
policyScript = policyScript.replaceAll("{{app}}", appName);
|
|
@@ -1701,7 +1845,7 @@ async function applyAgents(api, localAgents, projectId) {
|
|
|
1701
1845
|
return errors;
|
|
1702
1846
|
}
|
|
1703
1847
|
async function pullAgents(api, platformDir, filterName, projectId) {
|
|
1704
|
-
const agentsDir =
|
|
1848
|
+
const agentsDir = join2(platformDir, "agents");
|
|
1705
1849
|
mkdirSync(agentsDir, { recursive: true });
|
|
1706
1850
|
const agents = await api.listAgents(projectId ? { project_id: projectId } : void 0);
|
|
1707
1851
|
let skillIdToSlug = /* @__PURE__ */ new Map();
|
|
@@ -1716,7 +1860,7 @@ async function pullAgents(api, platformDir, filterName, projectId) {
|
|
|
1716
1860
|
continue;
|
|
1717
1861
|
}
|
|
1718
1862
|
const dirName = agent.external_id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1719
|
-
const agentDir =
|
|
1863
|
+
const agentDir = join2(agentsDir, dirName);
|
|
1720
1864
|
mkdirSync(agentDir, { recursive: true });
|
|
1721
1865
|
const skillSlugs = [];
|
|
1722
1866
|
if (agent.skill_ids) {
|
|
@@ -1733,10 +1877,10 @@ async function pullAgents(api, platformDir, filterName, projectId) {
|
|
|
1733
1877
|
if (agent.model) config.model = agent.model;
|
|
1734
1878
|
if (skillSlugs.length > 0) config.skills = skillSlugs;
|
|
1735
1879
|
if (agent.policy_enabled) config.policy_enabled = true;
|
|
1736
|
-
writeFileSync(
|
|
1737
|
-
writeFileSync(
|
|
1880
|
+
writeFileSync(join2(agentDir, "config.json"), JSON.stringify(config, null, 2) + "\n");
|
|
1881
|
+
writeFileSync(join2(agentDir, "system_prompt.md"), agent.system_prompt || "");
|
|
1738
1882
|
if (agent.policy_script) {
|
|
1739
|
-
writeFileSync(
|
|
1883
|
+
writeFileSync(join2(agentDir, "policy.js"), agent.policy_script);
|
|
1740
1884
|
}
|
|
1741
1885
|
console.log(pc2.green(" \u2713"), `${agent.name} \u2192 agents/${dirName}/`);
|
|
1742
1886
|
}
|
|
@@ -2374,7 +2518,10 @@ async function plan(args) {
|
|
|
2374
2518
|
console.log(pc2.cyan(pc2.bold(" Plan")));
|
|
2375
2519
|
console.log(pc2.dim(" Comparing local files to remote state..."));
|
|
2376
2520
|
console.log();
|
|
2377
|
-
|
|
2521
|
+
if (!type || type === "collections") {
|
|
2522
|
+
lintCollectionFiles(projectRoot, platformDir, name || void 0);
|
|
2523
|
+
}
|
|
2524
|
+
await syncDeps(projectRoot, { ignorePermissionDenied: true });
|
|
2378
2525
|
const allChanges = [];
|
|
2379
2526
|
let collections;
|
|
2380
2527
|
try {
|
|
@@ -2509,7 +2656,10 @@ async function apply(args) {
|
|
|
2509
2656
|
console.log();
|
|
2510
2657
|
return;
|
|
2511
2658
|
}
|
|
2512
|
-
|
|
2659
|
+
if (!type || type === "collections") {
|
|
2660
|
+
lintCollectionFiles(projectRoot, platformDir, name || void 0);
|
|
2661
|
+
}
|
|
2662
|
+
await syncDeps(projectRoot, { ignorePermissionDenied: true });
|
|
2513
2663
|
let collections;
|
|
2514
2664
|
try {
|
|
2515
2665
|
const remoteCollections = await api.listCollections();
|
|
@@ -2540,7 +2690,7 @@ async function apply(args) {
|
|
|
2540
2690
|
let willDeployApp = false;
|
|
2541
2691
|
if (!type) {
|
|
2542
2692
|
try {
|
|
2543
|
-
if (
|
|
2693
|
+
if (existsSync2(join2(projectRoot, "dist")) || existsSync2(join2(projectRoot, "src"))) {
|
|
2544
2694
|
willDeployApp = true;
|
|
2545
2695
|
}
|
|
2546
2696
|
} catch {
|
|
@@ -2720,7 +2870,7 @@ async function pull(args) {
|
|
|
2720
2870
|
console.log();
|
|
2721
2871
|
if (!name && await projectResourceDepsEnabled(projectRoot)) {
|
|
2722
2872
|
console.log(pc2.bold(" Resource shares:"));
|
|
2723
|
-
await syncDeps(projectRoot, { write: true, legacyWhenDisabled: false });
|
|
2873
|
+
await syncDeps(projectRoot, { write: true, legacyWhenDisabled: false, ignorePermissionDenied: true });
|
|
2724
2874
|
console.log();
|
|
2725
2875
|
}
|
|
2726
2876
|
}
|