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.
Files changed (3) hide show
  1. package/README.md +9 -7
  2. package/dist/cli.js +56 -12
  3. 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 jiraTicket=NORN-482 \
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. CLI values override authored defaults. Both
91
- forms require one file plus `--sequence`, reject undeclared or missing required parameters before
92
- execution, and cannot be combined with `@data`/`@theory` cases. `--recording` writes the normal
93
- secret-masked agent recording to an exact artifact path instead of the rolling local cache.
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(`${flag} requires <name>=${source === "json" ? "<file.json>" : "<value>"}.`);
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 < 1) {
213003
- throw new Error(`${flag} requires <name>=${source === "json" ? "<file.json>" : "<value>"}.`);
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 '${assignment.name}' must reference a .json file: ${assignment.value}`
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 '${assignment.name}' from '${filePath}': ${error2 instanceof Error ? error2.message : String(error2)}`
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 '${assignment.name}' from '${filePath}': ${error2 instanceof Error ? error2.message : String(error2)}`
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
- for (const assignment of assignments) {
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 = arg;
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>=<value> Supply a string parameter to --sequence (repeatable)
213613
- --param-json <name>=<file.json>
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 jiraTicket=NORN-482
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.1.0",
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,