@vaudience/aigentflow-flow-validator 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PARITY.md +76 -35
- package/README.md +2 -2
- package/dist/cli.js +157 -1
- package/dist/index.cjs +154 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +154 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -302,6 +302,17 @@ function isValidGoDuration(input) {
|
|
|
302
302
|
var RESERVED_STEP_ID_CHAR = ".";
|
|
303
303
|
var RESERVED_STEP_ID_ORCHESTRATOR = "orchestrator";
|
|
304
304
|
var DISPLAY_NAME_MAX_LEN = 80;
|
|
305
|
+
var YAML11_BOOL_WORDS = new Set(
|
|
306
|
+
["y", "yes", "on", "n", "no", "off"].flatMap((w) => [
|
|
307
|
+
w,
|
|
308
|
+
w.toUpperCase(),
|
|
309
|
+
w[0].toUpperCase() + w.slice(1)
|
|
310
|
+
])
|
|
311
|
+
);
|
|
312
|
+
function isBoolLike(v) {
|
|
313
|
+
if (v === null || typeof v === "boolean") return true;
|
|
314
|
+
return typeof v === "string" && YAML11_BOOL_WORDS.has(v);
|
|
315
|
+
}
|
|
305
316
|
function validateBasicStructure(flow, issues) {
|
|
306
317
|
if (!issues.nonEmptyStringOf(flow, "aigentflow_version", "")) {
|
|
307
318
|
issues.error({
|
|
@@ -331,6 +342,14 @@ function validateBasicStructure(flow, issues) {
|
|
|
331
342
|
});
|
|
332
343
|
}
|
|
333
344
|
}
|
|
345
|
+
if (flow.strict_templates !== void 0 && !isBoolLike(flow.strict_templates)) {
|
|
346
|
+
issues.error({
|
|
347
|
+
field: "strict_templates",
|
|
348
|
+
message: "strict_templates must be a boolean (true or false)",
|
|
349
|
+
code: "invalid_type",
|
|
350
|
+
suggestion: "Write 'strict_templates: true' or remove the key"
|
|
351
|
+
});
|
|
352
|
+
}
|
|
334
353
|
const start = issues.stringOf(flow, "start", "");
|
|
335
354
|
if (start === null || start === "") {
|
|
336
355
|
issues.error({
|
|
@@ -430,7 +449,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
430
449
|
|
|
431
450
|
// src/spec/aigentflow-spec.json
|
|
432
451
|
var aigentflow_spec_default = {
|
|
433
|
-
specVersion: "2.
|
|
452
|
+
specVersion: "2.827.0",
|
|
434
453
|
inputSchemaVersion: 1,
|
|
435
454
|
executorSchemes: [
|
|
436
455
|
"a2a",
|
|
@@ -454,6 +473,7 @@ var aigentflow_spec_default = {
|
|
|
454
473
|
"geocoding",
|
|
455
474
|
"geovisualize",
|
|
456
475
|
"getmd",
|
|
476
|
+
"here",
|
|
457
477
|
"homeassistant",
|
|
458
478
|
"hosting",
|
|
459
479
|
"http",
|
|
@@ -1255,6 +1275,11 @@ var aigentflow_spec_default = {
|
|
|
1255
1275
|
}
|
|
1256
1276
|
}
|
|
1257
1277
|
},
|
|
1278
|
+
templateDefaultPiped: {
|
|
1279
|
+
code: "template_default_piped",
|
|
1280
|
+
messageFormat: "`default` takes the value FIRST, and a pipe passes the piped value LAST: `%s` is `default <fallback> <value>`, so the fallback is always the result",
|
|
1281
|
+
suggestion: 'write the value first and the fallback second, as a call: `{{ default .x "fallback" }}`'
|
|
1282
|
+
},
|
|
1258
1283
|
complianceResidency: {
|
|
1259
1284
|
code: "compliance_residency_unenforced",
|
|
1260
1285
|
euResidencyCodes: ["EU", "DE", "FR", "IE", "NL", "IT", "ES"],
|
|
@@ -1553,6 +1578,7 @@ var aigentflow_spec_default = {
|
|
|
1553
1578
|
rate_limit: "FlowRateLimitConfig",
|
|
1554
1579
|
start: "scalar",
|
|
1555
1580
|
steps: "map<StepDefinition>",
|
|
1581
|
+
strict_templates: "scalar",
|
|
1556
1582
|
tags: "list<scalar>",
|
|
1557
1583
|
template: "FlowTemplateMetadata",
|
|
1558
1584
|
tool_discovery: "scalar",
|
|
@@ -1914,6 +1940,11 @@ var EXAMPLES = {
|
|
|
1914
1940
|
name: p.name,
|
|
1915
1941
|
re: new RegExp(p.expr, p.flags)
|
|
1916
1942
|
}))};
|
|
1943
|
+
var TEMPLATE_DEFAULT_PIPED = {
|
|
1944
|
+
code: aigentflow_spec_default.templateDefaultPiped.code,
|
|
1945
|
+
messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
|
|
1946
|
+
suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
|
|
1947
|
+
};
|
|
1917
1948
|
|
|
1918
1949
|
// src/validators/executors.ts
|
|
1919
1950
|
var SCHEME_SEPARATOR = "://";
|
|
@@ -3877,6 +3908,117 @@ function splitPipeline(body) {
|
|
|
3877
3908
|
return stages;
|
|
3878
3909
|
}
|
|
3879
3910
|
|
|
3911
|
+
// src/template/gotmpl-default.ts
|
|
3912
|
+
var FUNCTION_NAME = "default";
|
|
3913
|
+
var PIPE = "|";
|
|
3914
|
+
var OPEN_PAREN = "(";
|
|
3915
|
+
var CLOSE_PAREN = ")";
|
|
3916
|
+
var MAX_NESTING_DEPTH = 100;
|
|
3917
|
+
var PIPELINE_KEYWORDS = /^(?:else\s+if|else\s+with|if|range|with|template|block)(?![\w$.])/;
|
|
3918
|
+
var NO_PIPELINE = /^(?:define|else|end|break|continue)(?![\w$.])/;
|
|
3919
|
+
var DECLARATION = /^\$\w*(?:\s*,\s*\$\w*)*\s*:?=/;
|
|
3920
|
+
function pipedDefaultPipelines(src) {
|
|
3921
|
+
const { actions, fatal } = lexActions(src);
|
|
3922
|
+
if (fatal) return [];
|
|
3923
|
+
const found = /* @__PURE__ */ new Set();
|
|
3924
|
+
try {
|
|
3925
|
+
for (const action of actions) {
|
|
3926
|
+
if (action.isComment || action.body === "") continue;
|
|
3927
|
+
scanAction(action.body, found);
|
|
3928
|
+
}
|
|
3929
|
+
} catch (err) {
|
|
3930
|
+
if (!(err instanceof RangeError)) throw err;
|
|
3931
|
+
return [];
|
|
3932
|
+
}
|
|
3933
|
+
return [...found].sort();
|
|
3934
|
+
}
|
|
3935
|
+
function scanAction(body, found) {
|
|
3936
|
+
let rest = body;
|
|
3937
|
+
const kw = PIPELINE_KEYWORDS.exec(rest);
|
|
3938
|
+
if (!kw && NO_PIPELINE.test(rest)) return;
|
|
3939
|
+
if (kw) {
|
|
3940
|
+
rest = rest.slice(kw[0].length).trimStart();
|
|
3941
|
+
const keyword = kw[0].replace(/\s+/g, " ");
|
|
3942
|
+
if (keyword === "template" || keyword === "block") {
|
|
3943
|
+
const q2 = rest[0];
|
|
3944
|
+
if (q2 === '"' || q2 === "`") {
|
|
3945
|
+
const end = scanString(rest, 0, q2);
|
|
3946
|
+
if (end === -1) return;
|
|
3947
|
+
rest = rest.slice(end + 1).trimStart();
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
if (rest === "") return;
|
|
3952
|
+
parsePipeline(rest, 0, false, found, 0);
|
|
3953
|
+
}
|
|
3954
|
+
function parsePipeline(src, start, nested, found, depth) {
|
|
3955
|
+
if (depth > MAX_NESTING_DEPTH) return { text: src.slice(start), end: src.length };
|
|
3956
|
+
let i = start;
|
|
3957
|
+
const n = src.length;
|
|
3958
|
+
let decl = "";
|
|
3959
|
+
const decMatch = DECLARATION.exec(src.slice(i));
|
|
3960
|
+
if (decMatch) {
|
|
3961
|
+
decl = decMatch[0].replace(/\s+/g, " ") + " ";
|
|
3962
|
+
i += decMatch[0].length;
|
|
3963
|
+
}
|
|
3964
|
+
const commands = [];
|
|
3965
|
+
let current = [];
|
|
3966
|
+
let firstWords = [];
|
|
3967
|
+
let pipedDefault = false;
|
|
3968
|
+
const pushCommand = () => {
|
|
3969
|
+
if (current.length > 0) {
|
|
3970
|
+
commands.push(current);
|
|
3971
|
+
firstWords.push(current[0]);
|
|
3972
|
+
}
|
|
3973
|
+
current = [];
|
|
3974
|
+
};
|
|
3975
|
+
while (i < n) {
|
|
3976
|
+
const c = src[i];
|
|
3977
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
3978
|
+
i += 1;
|
|
3979
|
+
continue;
|
|
3980
|
+
}
|
|
3981
|
+
if (c === '"' || c === "`" || c === "'") {
|
|
3982
|
+
const end = scanString(src, i, c);
|
|
3983
|
+
const stop = end === -1 ? n - 1 : end;
|
|
3984
|
+
current.push(src.slice(i, stop + 1));
|
|
3985
|
+
i = stop + 1;
|
|
3986
|
+
continue;
|
|
3987
|
+
}
|
|
3988
|
+
if (c === OPEN_PAREN) {
|
|
3989
|
+
const inner = parsePipeline(src, i + 1, true, found, depth + 1);
|
|
3990
|
+
current.push(`(${inner.text})`);
|
|
3991
|
+
i = inner.end;
|
|
3992
|
+
continue;
|
|
3993
|
+
}
|
|
3994
|
+
if (c === CLOSE_PAREN) {
|
|
3995
|
+
if (nested) {
|
|
3996
|
+
i += 1;
|
|
3997
|
+
break;
|
|
3998
|
+
}
|
|
3999
|
+
i += 1;
|
|
4000
|
+
continue;
|
|
4001
|
+
}
|
|
4002
|
+
if (c === PIPE) {
|
|
4003
|
+
pushCommand();
|
|
4004
|
+
i += 1;
|
|
4005
|
+
continue;
|
|
4006
|
+
}
|
|
4007
|
+
let j = i;
|
|
4008
|
+
while (j < n && !/[\s|()"`']/.test(src[j])) j += 1;
|
|
4009
|
+
if (j === i) j = i + 1;
|
|
4010
|
+
current.push(src.slice(i, j));
|
|
4011
|
+
i = j;
|
|
4012
|
+
}
|
|
4013
|
+
pushCommand();
|
|
4014
|
+
firstWords.forEach((word, idx) => {
|
|
4015
|
+
if (idx > 0 && word === FUNCTION_NAME) pipedDefault = true;
|
|
4016
|
+
});
|
|
4017
|
+
const text = decl + commands.map((cmd) => cmd.join(" ")).join(` ${PIPE} `);
|
|
4018
|
+
if (pipedDefault) found.add(text);
|
|
4019
|
+
return { text, end: i };
|
|
4020
|
+
}
|
|
4021
|
+
|
|
3880
4022
|
// src/validators/processingOperations.ts
|
|
3881
4023
|
var PROCESSING_OP_GUARD_KEY = PROCESSING_OPERATIONS.guardKey;
|
|
3882
4024
|
var PROCESSING_PHASES = ["pre_processing", "post_processing"];
|
|
@@ -4023,6 +4165,17 @@ function checkTemplateString(value, field, stepID, issues, stats, opts, demote =
|
|
|
4023
4165
|
});
|
|
4024
4166
|
}
|
|
4025
4167
|
}
|
|
4168
|
+
if (errors.length > 0) return;
|
|
4169
|
+
for (const pipe of pipedDefaultPipelines(value)) {
|
|
4170
|
+
report({
|
|
4171
|
+
field,
|
|
4172
|
+
message: TEMPLATE_DEFAULT_PIPED.messageFormat.replace("%s", () => pipe),
|
|
4173
|
+
code: TEMPLATE_DEFAULT_PIPED.code,
|
|
4174
|
+
stepId: stepID,
|
|
4175
|
+
context: `Template: ${value}`,
|
|
4176
|
+
suggestion: TEMPLATE_DEFAULT_PIPED.suggestion
|
|
4177
|
+
});
|
|
4178
|
+
}
|
|
4026
4179
|
}
|
|
4027
4180
|
function walkStrings2(basePath, value, stepID, issues, stats, opts, check, demote = false) {
|
|
4028
4181
|
if (isString(value)) {
|