norn-cli 4.1.0 → 4.2.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/README.md +9 -7
- package/dist/cli.js +56 -12
- package/package.json +1 -6
package/README.md
CHANGED
|
@@ -79,18 +79,20 @@ For a hosted agent workflow, select one sequence and supply its runtime inputs e
|
|
|
79
79
|
```bash
|
|
80
80
|
norn run ./workflows/ticket.norn \
|
|
81
81
|
--sequence HandleTicket \
|
|
82
|
-
--param
|
|
82
|
+
--param NORN-482 \
|
|
83
83
|
--param-json context=./inputs/ticket-context.json \
|
|
84
84
|
--recording ./artifacts/ticket-run.json \
|
|
85
85
|
--json
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
`--param name=value` is repeatable and preserves the value as a string.
|
|
89
|
-
`--param-json name=file.json` is also repeatable; it parses the file and assigns its typed JSON
|
|
90
|
-
value directly to the declared sequence parameter.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
`--param [name=]value` is repeatable and preserves the value as a string.
|
|
89
|
+
`--param-json [name=]file.json` is also repeatable; it parses the file and assigns its typed JSON
|
|
90
|
+
value directly to the declared sequence parameter. Unnamed values follow declaration order and
|
|
91
|
+
skip parameters supplied by name, so named and positional inputs can be mixed. CLI values override
|
|
92
|
+
authored defaults. Both forms require one file plus `--sequence`, reject undeclared or missing
|
|
93
|
+
required parameters before execution, and cannot be combined with `@data`/`@theory` cases.
|
|
94
|
+
`--recording` writes the normal secret-masked agent recording to an exact artifact path instead of
|
|
95
|
+
the rolling local cache.
|
|
94
96
|
|
|
95
97
|
Ordinary sequence runs handle `SIGINT` and `SIGTERM` as graceful cancellation requests. A repeated
|
|
96
98
|
signal forces exit; cancelled processes use status 130 (`SIGINT`) or 143 (`SIGTERM`).
|
package/dist/cli.js
CHANGED
|
@@ -212996,11 +212996,19 @@ var PARAMETER_NAME = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
|
212996
212996
|
function parseCliSequenceParameterAssignment(authored2, source) {
|
|
212997
212997
|
const flag = source === "json" ? "--param-json" : "--param";
|
|
212998
212998
|
if (authored2 === void 0) {
|
|
212999
|
-
throw new Error(
|
|
212999
|
+
throw new Error(
|
|
213000
|
+
`${flag} requires ${source === "json" ? "<file.json>" : "<value>"} or <name>=${source === "json" ? "<file.json>" : "<value>"}.`
|
|
213001
|
+
);
|
|
213000
213002
|
}
|
|
213001
213003
|
const separator = authored2.indexOf("=");
|
|
213002
|
-
if (separator <
|
|
213003
|
-
|
|
213004
|
+
if (separator < 0) {
|
|
213005
|
+
if (source === "json" && authored2.trim().length === 0) {
|
|
213006
|
+
throw new Error("--param-json requires a non-empty .json file path.");
|
|
213007
|
+
}
|
|
213008
|
+
return { source, value: authored2 };
|
|
213009
|
+
}
|
|
213010
|
+
if (separator === 0) {
|
|
213011
|
+
throw new Error(`${flag} requires a parameter name before '='.`);
|
|
213004
213012
|
}
|
|
213005
213013
|
const name = authored2.slice(0, separator).trim();
|
|
213006
213014
|
const value = authored2.slice(separator + 1);
|
|
@@ -213014,9 +213022,10 @@ function parseCliSequenceParameterAssignment(authored2, source) {
|
|
|
213014
213022
|
}
|
|
213015
213023
|
function loadJsonParameter(assignment, cwd) {
|
|
213016
213024
|
const filePath = path41.resolve(cwd, assignment.value);
|
|
213025
|
+
const parameterLabel = assignment.name ? ` '${assignment.name}'` : "";
|
|
213017
213026
|
if (path41.extname(filePath).toLowerCase() !== ".json") {
|
|
213018
213027
|
throw new Error(
|
|
213019
|
-
`JSON parameter
|
|
213028
|
+
`JSON parameter${parameterLabel} must reference a .json file: ${assignment.value}`
|
|
213020
213029
|
);
|
|
213021
213030
|
}
|
|
213022
213031
|
let content;
|
|
@@ -213024,14 +213033,14 @@ function loadJsonParameter(assignment, cwd) {
|
|
|
213024
213033
|
content = fs35.readFileSync(filePath, "utf8");
|
|
213025
213034
|
} catch (error2) {
|
|
213026
213035
|
throw new Error(
|
|
213027
|
-
`Could not read JSON parameter
|
|
213036
|
+
`Could not read JSON parameter${parameterLabel} from '${filePath}': ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
213028
213037
|
);
|
|
213029
213038
|
}
|
|
213030
213039
|
try {
|
|
213031
213040
|
return JSON.parse(content.replace(/^\uFEFF/, ""));
|
|
213032
213041
|
} catch (error2) {
|
|
213033
213042
|
throw new Error(
|
|
213034
|
-
`Could not parse JSON parameter
|
|
213043
|
+
`Could not parse JSON parameter${parameterLabel} from '${filePath}': ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
213035
213044
|
);
|
|
213036
213045
|
}
|
|
213037
213046
|
}
|
|
@@ -213044,7 +213053,10 @@ function resolveCliSequenceParameters(declarations, assignments, cwd = process.c
|
|
|
213044
213053
|
}
|
|
213045
213054
|
}
|
|
213046
213055
|
const supplied = /* @__PURE__ */ new Set();
|
|
213047
|
-
|
|
213056
|
+
const namedAssignments = assignments.filter(
|
|
213057
|
+
(assignment) => assignment.name !== void 0
|
|
213058
|
+
);
|
|
213059
|
+
for (const assignment of namedAssignments) {
|
|
213048
213060
|
if (!declaredByName.has(assignment.name)) {
|
|
213049
213061
|
const available = declarations.map((parameter) => parameter.name).join(", ") || "none";
|
|
213050
213062
|
throw new Error(
|
|
@@ -213057,6 +213069,19 @@ function resolveCliSequenceParameters(declarations, assignments, cwd = process.c
|
|
|
213057
213069
|
supplied.add(assignment.name);
|
|
213058
213070
|
resolved[assignment.name] = assignment.source === "json" ? loadJsonParameter(assignment, cwd) : assignment.value;
|
|
213059
213071
|
}
|
|
213072
|
+
const positionalAssignments = assignments.filter((assignment) => assignment.name === void 0);
|
|
213073
|
+
const availableDeclarations = declarations.filter((parameter) => !supplied.has(parameter.name));
|
|
213074
|
+
if (positionalAssignments.length > availableDeclarations.length) {
|
|
213075
|
+
throw new Error(
|
|
213076
|
+
`Too many positional sequence parameters: received ${positionalAssignments.length}, but only ${availableDeclarations.length} parameter${availableDeclarations.length === 1 ? "" : "s"} remain.`
|
|
213077
|
+
);
|
|
213078
|
+
}
|
|
213079
|
+
for (let index = 0; index < positionalAssignments.length; index++) {
|
|
213080
|
+
const assignment = positionalAssignments[index];
|
|
213081
|
+
const parameter = availableDeclarations[index];
|
|
213082
|
+
supplied.add(parameter.name);
|
|
213083
|
+
resolved[parameter.name] = assignment.source === "json" ? loadJsonParameter({ ...assignment, name: parameter.name }, cwd) : assignment.value;
|
|
213084
|
+
}
|
|
213060
213085
|
const missing = declarations.filter((parameter) => parameter.defaultValue === void 0 && !supplied.has(parameter.name)).map((parameter) => parameter.name);
|
|
213061
213086
|
if (missing.length > 0) {
|
|
213062
213087
|
throw new Error(
|
|
@@ -213505,7 +213530,8 @@ function parseArgs(args) {
|
|
|
213505
213530
|
refactorRegionPattern: false,
|
|
213506
213531
|
writeRefactor: false,
|
|
213507
213532
|
yes: false,
|
|
213508
|
-
sequenceParameters: []
|
|
213533
|
+
sequenceParameters: [],
|
|
213534
|
+
unexpectedPositionals: []
|
|
213509
213535
|
};
|
|
213510
213536
|
for (let i2 = 0; i2 < args.length; i2++) {
|
|
213511
213537
|
const arg = args[i2];
|
|
@@ -213587,7 +213613,11 @@ function parseArgs(args) {
|
|
|
213587
213613
|
printHelp();
|
|
213588
213614
|
process.exit(0);
|
|
213589
213615
|
} else if (!arg.startsWith("-")) {
|
|
213590
|
-
options.file
|
|
213616
|
+
if (options.file) {
|
|
213617
|
+
options.unexpectedPositionals.push(arg);
|
|
213618
|
+
} else {
|
|
213619
|
+
options.file = arg;
|
|
213620
|
+
}
|
|
213591
213621
|
}
|
|
213592
213622
|
}
|
|
213593
213623
|
return options;
|
|
@@ -213609,9 +213639,11 @@ Options:
|
|
|
213609
213639
|
-s, --sequence <name> Run a specific sequence by name (single file only)
|
|
213610
213640
|
-r, --request <name> Run a specific named request (single file only)
|
|
213611
213641
|
-e, --env <name> Use environment from .nornenv (e.g., dev, prod)
|
|
213612
|
-
--param <name
|
|
213613
|
-
|
|
213642
|
+
--param [<name>=]<value>
|
|
213643
|
+
Supply a string parameter to --sequence (repeatable)
|
|
213644
|
+
--param-json [<name>=]<file.json>
|
|
213614
213645
|
Parse a JSON file into a --sequence parameter (repeatable)
|
|
213646
|
+
Unnamed parameters follow sequence declaration order
|
|
213615
213647
|
--recording <file.json>
|
|
213616
213648
|
Write the agent recording to an exact artifact path
|
|
213617
213649
|
-t, --timeout <time> Request timeout override (e.g. 180s, 3m, 300000ms; default: norn.config.json or 30s)
|
|
@@ -213631,6 +213663,7 @@ Options:
|
|
|
213631
213663
|
--refactor-region-pattern
|
|
213632
213664
|
Refactor a flat .nornenv STAGE_REGION matrix to templates
|
|
213633
213665
|
--write Apply --refactor-region-pattern instead of printing result
|
|
213666
|
+
-V, --version Show the installed Norn CLI version
|
|
213634
213667
|
-h, --help Show this help message
|
|
213635
213668
|
|
|
213636
213669
|
Report Generation:
|
|
@@ -213665,7 +213698,7 @@ Examples:
|
|
|
213665
213698
|
norn tests/ # Run all tests in directory (recursive)
|
|
213666
213699
|
norn tests/Regression/ -e prelive # Run regression suite with environment
|
|
213667
213700
|
norn api-tests.norn -s auth # Run 'auth' sequence by name
|
|
213668
|
-
norn agent-flow.norn -s HandleTicket --param
|
|
213701
|
+
norn agent-flow.norn -s HandleTicket --param NORN-482
|
|
213669
213702
|
norn agent-flow.norn -s HandleTicket --param-json context=./ticket.json
|
|
213670
213703
|
norn agent-flow.norn -s HandleTicket --recording ./artifacts/run.json
|
|
213671
213704
|
norn api-tests.norn -r LoginRequest # Run 'LoginRequest' named request
|
|
@@ -214016,11 +214049,22 @@ async function main() {
|
|
|
214016
214049
|
process.exit(exitCode);
|
|
214017
214050
|
}
|
|
214018
214051
|
const args = rawArgs[0] === "run" ? rawArgs.slice(1) : rawArgs;
|
|
214052
|
+
if (args.length === 1 && (args[0] === "--version" || args[0] === "-V")) {
|
|
214053
|
+
console.log(`Norn CLI ${"4.2.0"}`);
|
|
214054
|
+
return;
|
|
214055
|
+
}
|
|
214019
214056
|
if (args.length === 0) {
|
|
214020
214057
|
printHelp();
|
|
214021
214058
|
process.exit(1);
|
|
214022
214059
|
}
|
|
214023
214060
|
const options = parseArgs(args);
|
|
214061
|
+
if (options.unexpectedPositionals.length > 0) {
|
|
214062
|
+
const unexpected = options.unexpectedPositionals[0];
|
|
214063
|
+
console.error(
|
|
214064
|
+
`Error: Unexpected positional argument '${unexpected}'. To pass a sequence value, use --param ${unexpected} or --param name=${unexpected}.`
|
|
214065
|
+
);
|
|
214066
|
+
process.exit(1);
|
|
214067
|
+
}
|
|
214024
214068
|
if (options.contracts) {
|
|
214025
214069
|
setContractModeOverride(true);
|
|
214026
214070
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "norn-cli",
|
|
3
3
|
"displayName": "Norn",
|
|
4
4
|
"description": "Version-controlled API and database tests. Author in VS Code, then run the same files from the CLI and CI.",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.2.0",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|
|
@@ -578,11 +578,6 @@
|
|
|
578
578
|
},
|
|
579
579
|
"description": "Workspace-relative glob patterns for .norn files or folders to hide from the Norn Test Explorer. Useful for negative fixtures, live-only tests, or documentation demos."
|
|
580
580
|
},
|
|
581
|
-
"norn.terminal.focusMode": {
|
|
582
|
-
"type": "boolean",
|
|
583
|
-
"default": true,
|
|
584
|
-
"description": "Close the primary Side Bar and bottom Panel when opening the Norn terminal."
|
|
585
|
-
},
|
|
586
581
|
"norn.terminal.intelliSense.selectFirstSuggestion": {
|
|
587
582
|
"type": "boolean",
|
|
588
583
|
"default": false,
|