@lumerahq/cli 0.26.0 → 0.29.1
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/{functions-PVOVWMON.js → chunk-QPVRRJFJ.js} +24 -13
- package/dist/functions-KP2NO4RC.js +15 -0
- package/dist/index.js +17 -10
- package/dist/project-OPA5UUMQ.js +125 -0
- package/dist/{resources-R53XKJL2.js → resources-P4VA2JPE.js} +140 -12
- package/package.json +1 -1
- package/templates/default/pyproject.toml +1 -1
- package/dist/{init-CUB3KH6M.js → init-ZR2SLJ3K.js} +3 -3
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import "./chunk-PNKVD2UK.js";
|
|
2
|
-
|
|
3
1
|
// src/commands/functions.ts
|
|
4
2
|
import { spawn } from "child_process";
|
|
5
3
|
import { existsSync } from "fs";
|
|
6
4
|
import { join, resolve } from "path";
|
|
7
5
|
import pc from "picocolors";
|
|
8
6
|
var SUPPORTED_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "inspect", "invoke", "test"]);
|
|
9
|
-
var FUNCTIONS_SDK_REQUIREMENT = "lumera[functions]>=0.
|
|
7
|
+
var FUNCTIONS_SDK_REQUIREMENT = "lumera[functions]>=0.32.0,<0.33.0";
|
|
10
8
|
var FUNCTIONS_RUNNER_PROTOCOL_VERSION = 1;
|
|
11
9
|
function errorMessage(error, fallback) {
|
|
12
10
|
return error instanceof Error && error.message ? error.message : fallback;
|
|
@@ -52,14 +50,18 @@ ${pc.dim("Usage:")}
|
|
|
52
50
|
lumera functions list
|
|
53
51
|
lumera functions inspect <function-id>
|
|
54
52
|
lumera functions invoke <function-id> --input <path|-> --local
|
|
53
|
+
lumera functions invoke <function-id> --input <path|-> --remote --project <project>
|
|
55
54
|
lumera functions test [-- <pytest-options>]
|
|
56
55
|
|
|
57
56
|
${pc.dim("Description:")}
|
|
58
|
-
Discover, inspect, invoke, and test configured Functions
|
|
59
|
-
|
|
57
|
+
Discover, inspect, invoke, and test configured Functions. Invocation can run
|
|
58
|
+
in a fresh local process or create a durable remote execution.
|
|
60
59
|
|
|
61
60
|
${pc.dim("Options:")}
|
|
62
|
-
--local
|
|
61
|
+
--local Run from the local project source
|
|
62
|
+
--remote Create a durable execution from the active release
|
|
63
|
+
--project <project> Project ID or external ID for remote invocation
|
|
64
|
+
--idempotency-key <k> Deduplicate matching remote invocation requests
|
|
63
65
|
--input <path|-> Read a JSON object from a project file or stdin (-)
|
|
64
66
|
--help, -h Show this help
|
|
65
67
|
|
|
@@ -67,6 +69,7 @@ ${pc.dim("Examples:")}
|
|
|
67
69
|
lumera functions list
|
|
68
70
|
lumera functions inspect invoices:approve
|
|
69
71
|
lumera functions invoke invoices:approve --input input.json --local
|
|
72
|
+
lumera functions invoke invoices:approve --input input.json --remote --project finance
|
|
70
73
|
cat input.json | lumera functions invoke invoices:approve --input - --local
|
|
71
74
|
lumera functions test
|
|
72
75
|
lumera functions test -- -k approve
|
|
@@ -76,10 +79,17 @@ function buildFunctionRunnerArgs(subcommand, args, projectRoot) {
|
|
|
76
79
|
if (!SUPPORTED_SUBCOMMANDS.has(subcommand)) {
|
|
77
80
|
throw new Error(`Unknown functions subcommand: ${subcommand}`);
|
|
78
81
|
}
|
|
79
|
-
if (subcommand === "invoke"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
)
|
|
82
|
+
if (subcommand === "invoke") {
|
|
83
|
+
const local = args.includes("--local");
|
|
84
|
+
const remote = args.includes("--remote");
|
|
85
|
+
if (local === remote) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"Pass exactly one of --local or --remote for Function invocation."
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (remote && !args.includes("--project")) {
|
|
91
|
+
throw new Error("--project is required for remote Function invocation.");
|
|
92
|
+
}
|
|
83
93
|
}
|
|
84
94
|
const runnerPrefix = ["run"];
|
|
85
95
|
if (existsSync(join(projectRoot, "uv.lock"))) {
|
|
@@ -203,10 +213,11 @@ async function functions(subcommand, args, dependencies = {}) {
|
|
|
203
213
|
process.exitCode = exitCode;
|
|
204
214
|
}
|
|
205
215
|
}
|
|
216
|
+
|
|
206
217
|
export {
|
|
207
|
-
buildFunctionRunnerArgs,
|
|
208
218
|
emitFunctionCliFailure,
|
|
209
219
|
findFunctionsProjectRoot,
|
|
210
|
-
|
|
211
|
-
runFunctionRunner
|
|
220
|
+
buildFunctionRunnerArgs,
|
|
221
|
+
runFunctionRunner,
|
|
222
|
+
functions
|
|
212
223
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildFunctionRunnerArgs,
|
|
3
|
+
emitFunctionCliFailure,
|
|
4
|
+
findFunctionsProjectRoot,
|
|
5
|
+
functions,
|
|
6
|
+
runFunctionRunner
|
|
7
|
+
} from "./chunk-QPVRRJFJ.js";
|
|
8
|
+
import "./chunk-PNKVD2UK.js";
|
|
9
|
+
export {
|
|
10
|
+
buildFunctionRunnerArgs,
|
|
11
|
+
emitFunctionCliFailure,
|
|
12
|
+
findFunctionsProjectRoot,
|
|
13
|
+
functions,
|
|
14
|
+
runFunctionRunner
|
|
15
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import pc from "picocolors";
|
|
|
12
12
|
|
|
13
13
|
// src/lib/command-output.ts
|
|
14
14
|
function commandOwnsStdout(command2) {
|
|
15
|
-
return command2 === "functions";
|
|
15
|
+
return command2 === "functions" || command2 === "project";
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// src/lib/update-check.ts
|
|
@@ -96,6 +96,7 @@ var COMMANDS = [
|
|
|
96
96
|
"dev",
|
|
97
97
|
"run",
|
|
98
98
|
"functions",
|
|
99
|
+
"project",
|
|
99
100
|
"init",
|
|
100
101
|
"register",
|
|
101
102
|
"templates",
|
|
@@ -158,6 +159,7 @@ ${pc.dim("Development:")}
|
|
|
158
159
|
${pc.cyan("dev")} Start dev server
|
|
159
160
|
${pc.cyan("run")} <target> Run script, trigger automation, or invoke agent
|
|
160
161
|
${pc.cyan("functions")} <command> Develop and test Functions locally
|
|
162
|
+
${pc.cyan("project release")} <command> Build and verify immutable releases
|
|
161
163
|
|
|
162
164
|
${pc.dim("Project:")}
|
|
163
165
|
${pc.cyan("init")} [name] Scaffold a new project
|
|
@@ -238,25 +240,25 @@ async function main() {
|
|
|
238
240
|
switch (command) {
|
|
239
241
|
// Resource commands
|
|
240
242
|
case "plan":
|
|
241
|
-
await import("./resources-
|
|
243
|
+
await import("./resources-P4VA2JPE.js").then((m) => m.plan(args.slice(1)));
|
|
242
244
|
break;
|
|
243
245
|
case "apply":
|
|
244
|
-
await import("./resources-
|
|
246
|
+
await import("./resources-P4VA2JPE.js").then((m) => m.apply(args.slice(1)));
|
|
245
247
|
break;
|
|
246
248
|
case "pull":
|
|
247
|
-
await import("./resources-
|
|
249
|
+
await import("./resources-P4VA2JPE.js").then((m) => m.pull(args.slice(1)));
|
|
248
250
|
break;
|
|
249
251
|
case "destroy":
|
|
250
|
-
await import("./resources-
|
|
252
|
+
await import("./resources-P4VA2JPE.js").then((m) => m.destroy(args.slice(1)));
|
|
251
253
|
break;
|
|
252
254
|
case "list":
|
|
253
|
-
await import("./resources-
|
|
255
|
+
await import("./resources-P4VA2JPE.js").then((m) => m.list(args.slice(1)));
|
|
254
256
|
break;
|
|
255
257
|
case "show":
|
|
256
|
-
await import("./resources-
|
|
258
|
+
await import("./resources-P4VA2JPE.js").then((m) => m.show(args.slice(1)));
|
|
257
259
|
break;
|
|
258
260
|
case "diff":
|
|
259
|
-
await import("./resources-
|
|
261
|
+
await import("./resources-P4VA2JPE.js").then((m) => m.diff(args.slice(1)));
|
|
260
262
|
break;
|
|
261
263
|
// Development
|
|
262
264
|
case "dev":
|
|
@@ -266,13 +268,18 @@ async function main() {
|
|
|
266
268
|
await import("./run-WHVUVIYB.js").then((m) => m.run(args.slice(1)));
|
|
267
269
|
break;
|
|
268
270
|
case "functions":
|
|
269
|
-
await import("./functions-
|
|
271
|
+
await import("./functions-KP2NO4RC.js").then(
|
|
270
272
|
(m) => m.functions(subcommand, args.slice(2))
|
|
271
273
|
);
|
|
272
274
|
break;
|
|
275
|
+
case "project":
|
|
276
|
+
await import("./project-OPA5UUMQ.js").then(
|
|
277
|
+
(m) => m.project(subcommand, args.slice(2))
|
|
278
|
+
);
|
|
279
|
+
break;
|
|
273
280
|
// Project
|
|
274
281
|
case "init":
|
|
275
|
-
await import("./init-
|
|
282
|
+
await import("./init-ZR2SLJ3K.js").then((m) => m.init(args.slice(1)));
|
|
276
283
|
break;
|
|
277
284
|
case "register":
|
|
278
285
|
await import("./register-HRLBT4FI.js").then((m) => m.register(args.slice(1)));
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findFunctionsProjectRoot
|
|
3
|
+
} from "./chunk-QPVRRJFJ.js";
|
|
4
|
+
import "./chunk-PNKVD2UK.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/project.ts
|
|
7
|
+
import { spawn } from "child_process";
|
|
8
|
+
import pc from "picocolors";
|
|
9
|
+
var RELEASE_SDK_REQUIREMENT = "lumera[release]>=0.32.0,<0.33.0";
|
|
10
|
+
var RELEASE_PROTOCOL_VERSION = 1;
|
|
11
|
+
var RELEASE_COMMANDS = /* @__PURE__ */ new Set([
|
|
12
|
+
"build",
|
|
13
|
+
"inspect",
|
|
14
|
+
"contract-test",
|
|
15
|
+
"deploy",
|
|
16
|
+
"stage",
|
|
17
|
+
"activate"
|
|
18
|
+
]);
|
|
19
|
+
function showHelp() {
|
|
20
|
+
console.log(`
|
|
21
|
+
${pc.dim("Usage:")}
|
|
22
|
+
lumera project release build [--output dist/releases]
|
|
23
|
+
lumera project release inspect <archive> [--expected-digest <sha256>]
|
|
24
|
+
lumera project release contract-test <archive> [--mode api|worker|all]
|
|
25
|
+
lumera project release deploy <archive> --project <project-id>
|
|
26
|
+
lumera project release stage <release-id> --project <project-id> --expected-generation <n>
|
|
27
|
+
lumera project release activate <deployment-id> --project <project-id> --expected-generation <n>
|
|
28
|
+
|
|
29
|
+
${pc.dim("Description:")}
|
|
30
|
+
Build, verify, and contract-test immutable Python 3.13/Linux/ARM64 project
|
|
31
|
+
releases, upload an inactive resource, stage a healthy runtime, and activate
|
|
32
|
+
it atomically with an expected generation.
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
function buildReleaseRunnerArgs(command, args, projectRoot) {
|
|
36
|
+
if (!RELEASE_COMMANDS.has(command)) {
|
|
37
|
+
throw new Error(`Unknown project release command: ${command}`);
|
|
38
|
+
}
|
|
39
|
+
return [
|
|
40
|
+
"run",
|
|
41
|
+
"--locked",
|
|
42
|
+
"--with",
|
|
43
|
+
RELEASE_SDK_REQUIREMENT,
|
|
44
|
+
"python",
|
|
45
|
+
"-m",
|
|
46
|
+
"lumera.releases",
|
|
47
|
+
"--project-root",
|
|
48
|
+
projectRoot,
|
|
49
|
+
command,
|
|
50
|
+
...args
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
async function runReleaseRunner(runnerArgs, projectRoot, spawnRunner = (command, args, options) => spawn(command, args, options)) {
|
|
54
|
+
return await new Promise((resolvePromise, reject) => {
|
|
55
|
+
const child = spawnRunner("uv", runnerArgs, {
|
|
56
|
+
cwd: projectRoot,
|
|
57
|
+
env: process.env,
|
|
58
|
+
shell: false,
|
|
59
|
+
stdio: ["inherit", "pipe", "inherit"]
|
|
60
|
+
});
|
|
61
|
+
let output = "";
|
|
62
|
+
child.stdout?.setEncoding("utf8");
|
|
63
|
+
child.stdout?.on("data", (chunk) => {
|
|
64
|
+
output += chunk;
|
|
65
|
+
});
|
|
66
|
+
child.once("error", (error) => {
|
|
67
|
+
reject(
|
|
68
|
+
error.code === "ENOENT" ? new Error("uv is not installed. Install it from https://docs.astral.sh/uv/") : error
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
child.once("close", (code) => {
|
|
72
|
+
try {
|
|
73
|
+
const envelope = JSON.parse(output);
|
|
74
|
+
if (envelope.protocol_version !== RELEASE_PROTOCOL_VERSION || typeof envelope.ok !== "boolean") {
|
|
75
|
+
throw new Error("invalid release runner protocol");
|
|
76
|
+
}
|
|
77
|
+
resolvePromise({ code: code ?? 1, envelope, raw: output });
|
|
78
|
+
} catch {
|
|
79
|
+
reject(
|
|
80
|
+
new Error(
|
|
81
|
+
`Release runner exited with status ${code ?? 1} before emitting a valid protocol envelope`
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async function project(subcommand, args, dependencies = {}) {
|
|
89
|
+
if (subcommand !== "release" || args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
90
|
+
showHelp();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const [releaseCommand, ...releaseArgs] = args;
|
|
94
|
+
const findProjectRoot = dependencies.findProjectRoot ?? findFunctionsProjectRoot;
|
|
95
|
+
const runRunner = dependencies.runRunner ?? runReleaseRunner;
|
|
96
|
+
const writeOutput = dependencies.writeOutput ?? ((value) => process.stdout.write(value));
|
|
97
|
+
try {
|
|
98
|
+
const projectRoot = findProjectRoot();
|
|
99
|
+
const result = await runRunner(
|
|
100
|
+
buildReleaseRunnerArgs(releaseCommand, releaseArgs, projectRoot),
|
|
101
|
+
projectRoot
|
|
102
|
+
);
|
|
103
|
+
writeOutput(result.raw);
|
|
104
|
+
if (result.code !== 0) process.exitCode = result.code;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
writeOutput(
|
|
107
|
+
`${JSON.stringify({
|
|
108
|
+
protocol_version: RELEASE_PROTOCOL_VERSION,
|
|
109
|
+
ok: false,
|
|
110
|
+
error: {
|
|
111
|
+
code: "release_cli_error",
|
|
112
|
+
message: error instanceof Error ? error.message : "Release command failed",
|
|
113
|
+
retryable: false
|
|
114
|
+
}
|
|
115
|
+
})}
|
|
116
|
+
`
|
|
117
|
+
);
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
buildReleaseRunnerArgs,
|
|
123
|
+
project,
|
|
124
|
+
runReleaseRunner
|
|
125
|
+
};
|
|
@@ -93,10 +93,132 @@ var automationConfigRule = {
|
|
|
93
93
|
}
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
-
// src/lib/lint/rules/
|
|
96
|
+
// src/lib/lint/rules/automation-main-guard.ts
|
|
97
|
+
import { spawnSync } from "child_process";
|
|
98
|
+
var PYTHON_AST_SCRIPT = String.raw`
|
|
99
|
+
import ast
|
|
100
|
+
import json
|
|
101
|
+
import sys
|
|
102
|
+
|
|
103
|
+
source = sys.stdin.read()
|
|
104
|
+
try:
|
|
105
|
+
module = ast.parse(source)
|
|
106
|
+
except SyntaxError:
|
|
107
|
+
print("null")
|
|
108
|
+
raise SystemExit(0)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def is_name(node):
|
|
112
|
+
return isinstance(node, ast.Name) and node.id == "__name__"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def is_main_string(node):
|
|
116
|
+
if isinstance(node, ast.Str):
|
|
117
|
+
return node.s == "__main__"
|
|
118
|
+
return isinstance(node, ast.Constant) and node.value == "__main__"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def is_main_guard(node):
|
|
122
|
+
test = node.test
|
|
123
|
+
if not isinstance(test, ast.Compare):
|
|
124
|
+
return False
|
|
125
|
+
if len(test.ops) != 1 or not isinstance(test.ops[0], ast.Eq):
|
|
126
|
+
return False
|
|
127
|
+
if len(test.comparators) != 1:
|
|
128
|
+
return False
|
|
129
|
+
right = test.comparators[0]
|
|
130
|
+
return (is_name(test.left) and is_main_string(right)) or (
|
|
131
|
+
is_main_string(test.left) and is_name(right)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
for statement in module.body:
|
|
136
|
+
if not isinstance(statement, ast.If) or not is_main_guard(statement):
|
|
137
|
+
continue
|
|
138
|
+
get_segment = getattr(ast, "get_source_segment", None)
|
|
139
|
+
condition = get_segment(source, statement.test) if get_segment else None
|
|
140
|
+
if not condition:
|
|
141
|
+
condition = '__name__ == "__main__"'
|
|
142
|
+
snippet = "if " + " ".join(condition.split()) + ":"
|
|
143
|
+
print(json.dumps({"line": statement.lineno, "snippet": snippet}))
|
|
144
|
+
break
|
|
145
|
+
else:
|
|
146
|
+
print("null")
|
|
147
|
+
`;
|
|
148
|
+
var cachedPython3;
|
|
149
|
+
function findPython3() {
|
|
150
|
+
if (cachedPython3 !== void 0) return cachedPython3;
|
|
151
|
+
for (const executable of ["python3", "python"]) {
|
|
152
|
+
const probe = spawnSync(executable, ["-I", "-S", "--version"], {
|
|
153
|
+
encoding: "utf8",
|
|
154
|
+
timeout: 2e3,
|
|
155
|
+
windowsHide: true
|
|
156
|
+
});
|
|
157
|
+
const version = `${probe.stdout ?? ""}${probe.stderr ?? ""}`.trim();
|
|
158
|
+
if (probe.status === 0 && /^Python 3(?:\.|\s|$)/.test(version)) {
|
|
159
|
+
cachedPython3 = executable;
|
|
160
|
+
return executable;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
cachedPython3 = null;
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
97
166
|
function isRecord2(value) {
|
|
98
167
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
99
168
|
}
|
|
169
|
+
function functionEntrypoint(config) {
|
|
170
|
+
if (!isRecord2(config) || !isRecord2(config.function)) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
const name = config.function.name;
|
|
174
|
+
return typeof name === "string" && name.trim() !== "" ? name.trim() : null;
|
|
175
|
+
}
|
|
176
|
+
function findTopLevelMainGuard(source) {
|
|
177
|
+
const python = findPython3();
|
|
178
|
+
if (python === null) return null;
|
|
179
|
+
const parsed = spawnSync(python, ["-I", "-S", "-c", PYTHON_AST_SCRIPT], {
|
|
180
|
+
input: source,
|
|
181
|
+
encoding: "utf8",
|
|
182
|
+
maxBuffer: 1024 * 1024,
|
|
183
|
+
timeout: 5e3,
|
|
184
|
+
windowsHide: true
|
|
185
|
+
});
|
|
186
|
+
if (parsed.status !== 0) return null;
|
|
187
|
+
try {
|
|
188
|
+
const value = JSON.parse(parsed.stdout.trim());
|
|
189
|
+
if (!isRecord2(value) || typeof value.line !== "number" || typeof value.snippet !== "string") {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
return { line: value.line, snippet: value.snippet };
|
|
193
|
+
} catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
var automationMainGuardRule = {
|
|
198
|
+
id: "automation-main-guard",
|
|
199
|
+
description: "Warns when a function automation contains a top-level __main__ guard.",
|
|
200
|
+
appliesTo: ["automation"],
|
|
201
|
+
check(target) {
|
|
202
|
+
const entrypoint = functionEntrypoint(target.config);
|
|
203
|
+
if (entrypoint === null) return [];
|
|
204
|
+
const guard = findTopLevelMainGuard(target.source);
|
|
205
|
+
if (guard === null) return [];
|
|
206
|
+
const warning2 = {
|
|
207
|
+
ruleId: "automation-main-guard",
|
|
208
|
+
target,
|
|
209
|
+
severity: "warning",
|
|
210
|
+
line: guard.line,
|
|
211
|
+
snippet: guard.snippet,
|
|
212
|
+
message: `Function automations must not contain a top-level __main__ guard. Lumera invokes "${entrypoint}" automatically; remove the guard.`
|
|
213
|
+
};
|
|
214
|
+
return [warning2];
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// src/lib/lint/rules/collection-schema.ts
|
|
219
|
+
function isRecord3(value) {
|
|
220
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
221
|
+
}
|
|
100
222
|
function error(target, message, snippet) {
|
|
101
223
|
return {
|
|
102
224
|
ruleId: "collection-schema",
|
|
@@ -118,7 +240,7 @@ var collectionSchemaRule = {
|
|
|
118
240
|
const message = err instanceof Error ? err.message : String(err);
|
|
119
241
|
return [error(target, `Invalid JSON: ${message}`)];
|
|
120
242
|
}
|
|
121
|
-
if (!
|
|
243
|
+
if (!isRecord3(parsed)) {
|
|
122
244
|
return [error(target, "Collection file must contain a JSON object.")];
|
|
123
245
|
}
|
|
124
246
|
const issues = [];
|
|
@@ -131,7 +253,7 @@ var collectionSchemaRule = {
|
|
|
131
253
|
const localFieldTypes = /* @__PURE__ */ new Map();
|
|
132
254
|
if (Array.isArray(fields)) {
|
|
133
255
|
for (const field of fields) {
|
|
134
|
-
if (
|
|
256
|
+
if (isRecord3(field) && typeof field.name === "string") {
|
|
135
257
|
localFieldTypes.set(field.name, field.type);
|
|
136
258
|
}
|
|
137
259
|
}
|
|
@@ -155,7 +277,7 @@ var collectionSchemaRule = {
|
|
|
155
277
|
} else {
|
|
156
278
|
for (let i = 0; i < fields.length; i++) {
|
|
157
279
|
const field = fields[i];
|
|
158
|
-
if (!
|
|
280
|
+
if (!isRecord3(field)) {
|
|
159
281
|
issues.push(error(target, `Field at index ${i} must be an object.`));
|
|
160
282
|
continue;
|
|
161
283
|
}
|
|
@@ -176,7 +298,7 @@ var collectionSchemaRule = {
|
|
|
176
298
|
} else {
|
|
177
299
|
for (let i = 0; i < indexes.length; i++) {
|
|
178
300
|
const index = indexes[i];
|
|
179
|
-
if (!
|
|
301
|
+
if (!isRecord3(index)) {
|
|
180
302
|
const hint = typeof index === "string" ? ' Indexes are objects like { "fields": ["title"], "unique": true }, not CREATE INDEX SQL strings.' : "";
|
|
181
303
|
issues.push(error(target, `Index at index ${i} must be an object.${hint}`));
|
|
182
304
|
continue;
|
|
@@ -193,7 +315,7 @@ var collectionSchemaRule = {
|
|
|
193
315
|
}
|
|
194
316
|
}
|
|
195
317
|
if (search !== void 0) {
|
|
196
|
-
if (!
|
|
318
|
+
if (!isRecord3(search)) {
|
|
197
319
|
issues.push(error(target, 'Optional field "search" must be an object when present.'));
|
|
198
320
|
} else {
|
|
199
321
|
const unknownSearchKeys = Object.keys(search).filter((key) => key !== "fulltext" && key !== "semantic");
|
|
@@ -205,14 +327,14 @@ var collectionSchemaRule = {
|
|
|
205
327
|
if (fulltext === void 0 && semantic === void 0) {
|
|
206
328
|
issues.push(error(target, '"search" must include "fulltext" or "semantic".'));
|
|
207
329
|
}
|
|
208
|
-
if (fulltext !== void 0 && !
|
|
330
|
+
if (fulltext !== void 0 && !isRecord3(fulltext)) {
|
|
209
331
|
issues.push(error(target, '"search.fulltext" must be an object when present.'));
|
|
210
|
-
} else if (
|
|
332
|
+
} else if (isRecord3(fulltext)) {
|
|
211
333
|
const allowed = /* @__PURE__ */ new Set(["fields", "language", "fuzzy_fields"]);
|
|
212
334
|
for (const key of Object.keys(fulltext).filter((key2) => !allowed.has(key2))) {
|
|
213
335
|
issues.push(error(target, `Unknown search.fulltext option "${key}".`));
|
|
214
336
|
}
|
|
215
|
-
if (!
|
|
337
|
+
if (!isRecord3(fulltext.fields) || Object.keys(fulltext.fields).length === 0) {
|
|
216
338
|
issues.push(error(target, '"search.fulltext.fields" must be a non-empty field-to-weight object.'));
|
|
217
339
|
} else {
|
|
218
340
|
if (Object.keys(fulltext.fields).length > 8) {
|
|
@@ -244,9 +366,9 @@ var collectionSchemaRule = {
|
|
|
244
366
|
}
|
|
245
367
|
}
|
|
246
368
|
}
|
|
247
|
-
if (semantic !== void 0 && !
|
|
369
|
+
if (semantic !== void 0 && !isRecord3(semantic)) {
|
|
248
370
|
issues.push(error(target, '"search.semantic" must be an object when present.'));
|
|
249
|
-
} else if (
|
|
371
|
+
} else if (isRecord3(semantic)) {
|
|
250
372
|
const allowed = /* @__PURE__ */ new Set(["fields", "model"]);
|
|
251
373
|
for (const key of Object.keys(semantic).filter((key2) => !allowed.has(key2))) {
|
|
252
374
|
issues.push(error(target, `Unknown search.semantic option "${key}".`));
|
|
@@ -653,7 +775,13 @@ var llmImportRule = {
|
|
|
653
775
|
};
|
|
654
776
|
|
|
655
777
|
// src/lib/lint/registry.ts
|
|
656
|
-
var ALL_RULES = [
|
|
778
|
+
var ALL_RULES = [
|
|
779
|
+
automationConfigRule,
|
|
780
|
+
automationMainGuardRule,
|
|
781
|
+
collectionSchemaRule,
|
|
782
|
+
hookVerifyRule,
|
|
783
|
+
llmImportRule
|
|
784
|
+
];
|
|
657
785
|
|
|
658
786
|
// src/lib/lint/format.ts
|
|
659
787
|
import { relative } from "path";
|
package/package.json
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
spinner
|
|
3
|
+
} from "./chunk-BHYDYR75.js";
|
|
1
4
|
import {
|
|
2
5
|
listAllTemplates,
|
|
3
6
|
resolveTemplate
|
|
@@ -6,9 +9,6 @@ import {
|
|
|
6
9
|
installAllSkills,
|
|
7
10
|
syncClaudeMd
|
|
8
11
|
} from "./chunk-VL5GDJKU.js";
|
|
9
|
-
import {
|
|
10
|
-
spinner
|
|
11
|
-
} from "./chunk-BHYDYR75.js";
|
|
12
12
|
import {
|
|
13
13
|
createApiClient,
|
|
14
14
|
validateProjectNameLength
|