norn-cli 4.2.0 → 4.3.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.
Files changed (3) hide show
  1. package/README.md +6 -0
  2. package/dist/cli.js +252 -4
  3. package/package.json +9 -3
package/README.md CHANGED
@@ -74,6 +74,12 @@ npm install -g norn-cli
74
74
  norn ./tests/smoke.norn -e dev
75
75
  ```
76
76
 
77
+ Update an existing global installation from npm with:
78
+
79
+ ```bash
80
+ norn update
81
+ ```
82
+
77
83
  For a hosted agent workflow, select one sequence and supply its runtime inputs explicitly:
78
84
 
79
85
  ```bash
package/dist/cli.js CHANGED
@@ -101266,7 +101266,7 @@ var require_cross_spawn = __commonJS({
101266
101266
  var cp2 = require("child_process");
101267
101267
  var parse6 = require_parse2();
101268
101268
  var enoent = require_enoent();
101269
- function spawn7(command, args, options) {
101269
+ function spawn8(command, args, options) {
101270
101270
  const parsed = parse6(command, args, options);
101271
101271
  const spawned = cp2.spawn(parsed.command, parsed.args, parsed.options);
101272
101272
  enoent.hookChildProcess(spawned, parsed);
@@ -101278,8 +101278,8 @@ var require_cross_spawn = __commonJS({
101278
101278
  result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
101279
101279
  return result;
101280
101280
  }
101281
- module2.exports = spawn7;
101282
- module2.exports.spawn = spawn7;
101281
+ module2.exports = spawn8;
101282
+ module2.exports.spawn = spawn8;
101283
101283
  module2.exports.sync = spawnSync3;
101284
101284
  module2.exports._parse = parse6;
101285
101285
  module2.exports._enoent = enoent;
@@ -186020,6 +186020,25 @@ function mcpToolAllowsAdditionalProperties(source) {
186020
186020
  return schema.additionalProperties !== false;
186021
186021
  }
186022
186022
 
186023
+ // src/agents/agentActivity.ts
186024
+ function describeAgentActivity(event) {
186025
+ if (event.state === "completed") {
186026
+ return `Completed agent ${event.agent}`;
186027
+ }
186028
+ switch (event.kind) {
186029
+ case "preparing":
186030
+ return `${event.agent} \xB7 Preparing tools and context`;
186031
+ case "model":
186032
+ return `${event.agent} \xB7 Waiting for model response`;
186033
+ case "mcp":
186034
+ return `${event.agent} \xB7 Calling MCP tool ${event.name ?? "tool"}`;
186035
+ case "knowledge":
186036
+ return `${event.agent} \xB7 Loading knowledge`;
186037
+ case "agent":
186038
+ return `${event.agent} \xB7 Invoking agent ${event.name ?? "agent"}`;
186039
+ }
186040
+ }
186041
+
186023
186042
  // src/agents/judgeExpectations.ts
186024
186043
  var fs14 = __toESM(require("fs"));
186025
186044
  var path15 = __toESM(require("path"));
@@ -199525,6 +199544,18 @@ var AgentInvocationRunner = class {
199525
199544
  nextInvocationId() {
199526
199545
  return `agent-${(0, import_crypto7.randomUUID)()}`;
199527
199546
  }
199547
+ emitActivity(hop, state, kind, name) {
199548
+ this.options.onInvocationActivity?.({
199549
+ state,
199550
+ invocationId: hop.invocationId,
199551
+ ...hop.parentInvocationId ? { parentInvocationId: hop.parentInvocationId } : {},
199552
+ ...hop.hopPath ? { hopPath: hop.hopPath } : {},
199553
+ agent: hop.agent,
199554
+ depth: hop.depth,
199555
+ kind,
199556
+ ...name ? { name } : {}
199557
+ });
199558
+ }
199528
199559
  validateContract(agent, kind, value) {
199529
199560
  const builtIn = agent.builtInContracts?.[kind];
199530
199561
  if (builtIn) {
@@ -199665,6 +199696,11 @@ var AgentInvocationRunner = class {
199665
199696
  };
199666
199697
  parentHop.output.toolCalls.push(record2);
199667
199698
  const startedAt = Date.now();
199699
+ const activityKind = definition.kind === "agent" ? "agent" : definition.kind === "knowledge" ? "knowledge" : "mcp";
199700
+ const reportsActivity = definition.kind !== "context";
199701
+ if (reportsActivity) {
199702
+ this.emitActivity(parentHop, "active", activityKind, definition.name);
199703
+ }
199668
199704
  try {
199669
199705
  const output2 = await execute(input2, context);
199670
199706
  record2.output = cloneSnapshotValue(output2, null);
@@ -199677,6 +199713,9 @@ var AgentInvocationRunner = class {
199677
199713
  if (context.childInvocationId) {
199678
199714
  record2.childInvocationId = context.childInvocationId;
199679
199715
  }
199716
+ if (reportsActivity) {
199717
+ this.emitActivity(parentHop, "active", "model");
199718
+ }
199680
199719
  }
199681
199720
  }
199682
199721
  };
@@ -199739,6 +199778,7 @@ var AgentInvocationRunner = class {
199739
199778
  depth,
199740
199779
  cycle: hop.cycle
199741
199780
  });
199781
+ this.emitActivity(hop, "active", "preparing");
199742
199782
  let resolvedModel;
199743
199783
  let fatalToolError;
199744
199784
  let preparedKnowledge;
@@ -199866,6 +199906,7 @@ var AgentInvocationRunner = class {
199866
199906
  let providerResponse;
199867
199907
  let correctionConversation = conversation;
199868
199908
  for (; ; ) {
199909
+ this.emitActivity(hop, "active", "model");
199869
199910
  providerResponse = await adapter2.run({
199870
199911
  ...providerRequest,
199871
199912
  conversation: correctionConversation
@@ -199964,11 +200005,15 @@ var AgentInvocationRunner = class {
199964
200005
  }
199965
200006
  } finally {
199966
200007
  await this.options.mcpSessionManager?.closeScope(invocationId);
200008
+ if (this.options.signal?.aborted) {
200009
+ this.emitActivity(hop, "completed", "model");
200010
+ }
199967
200011
  }
199968
200012
  hop.output.model = hop.output.model || resolvedModel?.displayName || "";
199969
200013
  hop.output.ms = hop.output.ms || Date.now() - startedAt;
199970
200014
  this.options.knowledgeCoordinator?.recordInvocation(agent, hop, preparedKnowledge);
199971
200015
  this.options.onInvocationProgress?.({ phase: "completed", hop });
200016
+ this.emitActivity(hop, "completed", "model");
199972
200017
  return hop;
199973
200018
  }
199974
200019
  /**
@@ -206997,6 +207042,16 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
206997
207042
  nestingDepth,
206998
207043
  agentEvent
206999
207044
  });
207045
+ },
207046
+ onInvocationActivity: (agentActivity) => {
207047
+ onProgress?.({
207048
+ currentStep: stepIdx + 1,
207049
+ totalSteps,
207050
+ stepType: "judge",
207051
+ stepDescription: describeAgentActivity(agentActivity),
207052
+ nestingDepth,
207053
+ agentActivity
207054
+ });
207000
207055
  }
207001
207056
  });
207002
207057
  const run = await runner.run(
@@ -207135,6 +207190,16 @@ ${describeUnmetExpectations(verdict).map((line2) => ` - ${line2}`).join("\n")}`
207135
207190
  nestingDepth,
207136
207191
  agentEvent
207137
207192
  });
207193
+ },
207194
+ onInvocationActivity: (agentActivity) => {
207195
+ onProgress?.({
207196
+ currentStep: stepIdx + 1,
207197
+ totalSteps,
207198
+ stepType: "agent",
207199
+ stepDescription: describeAgentActivity(agentActivity),
207200
+ nestingDepth,
207201
+ agentActivity
207202
+ });
207138
207203
  }
207139
207204
  });
207140
207205
  const run = await runner.run(agent, input2);
@@ -213122,6 +213187,170 @@ function installCliRunCancellation(onCancellationRequested, forceExit = (code) =
213122
213187
  };
213123
213188
  }
213124
213189
 
213190
+ // src/cli/agentActivityProgress.ts
213191
+ var liveDisplays = /* @__PURE__ */ new Set();
213192
+ function clearAllAgentActivityProgressDisplays() {
213193
+ for (const display of [...liveDisplays]) {
213194
+ display.clear();
213195
+ }
213196
+ }
213197
+ var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
213198
+ function formatElapsed(ms) {
213199
+ const seconds = Math.max(0, Math.floor(ms / 1e3));
213200
+ if (seconds < 60) {
213201
+ return `${seconds}s`;
213202
+ }
213203
+ return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
213204
+ }
213205
+ function fitLine(value, columns) {
213206
+ const terminalColumns = columns && columns >= 40 ? columns : 100;
213207
+ const width = Math.max(24, terminalColumns - 1);
213208
+ return value.length <= width ? value : `${value.slice(0, Math.max(1, width - 1))}\u2026`;
213209
+ }
213210
+ function createAgentActivityProgressDisplay(stream4 = process.stderr, now = Date.now) {
213211
+ const enabled = stream4.isTTY === true;
213212
+ const active = /* @__PURE__ */ new Map();
213213
+ let order = 0;
213214
+ let frame = 0;
213215
+ let lineVisible = false;
213216
+ let timer;
213217
+ let display;
213218
+ const current = () => [...active.values()].sort((left, right) => right.event.depth - left.event.depth || right.order - left.order)[0];
213219
+ const render = () => {
213220
+ if (!enabled) {
213221
+ return;
213222
+ }
213223
+ const item = current();
213224
+ if (!item) {
213225
+ if (lineVisible) {
213226
+ stream4.write("\r\x1B[2K");
213227
+ lineVisible = false;
213228
+ }
213229
+ return;
213230
+ }
213231
+ const elapsed = formatElapsed(now() - item.startedAt);
213232
+ const line2 = fitLine(`${FRAMES[frame++ % FRAMES.length]} ${item.description} ${elapsed}`, stream4.columns);
213233
+ stream4.write(`\r\x1B[2K${line2}`);
213234
+ lineVisible = true;
213235
+ };
213236
+ const ensureTimer = () => {
213237
+ if (!enabled || timer) {
213238
+ return;
213239
+ }
213240
+ timer = setInterval(render, 120);
213241
+ timer.unref?.();
213242
+ };
213243
+ const stopTimer = () => {
213244
+ if (!timer) {
213245
+ return;
213246
+ }
213247
+ clearInterval(timer);
213248
+ timer = void 0;
213249
+ };
213250
+ const clear = () => {
213251
+ active.clear();
213252
+ stopTimer();
213253
+ render();
213254
+ liveDisplays.delete(display);
213255
+ };
213256
+ display = {
213257
+ update: (event, description) => {
213258
+ if (!enabled) {
213259
+ return;
213260
+ }
213261
+ if (event.state === "completed") {
213262
+ active.delete(event.invocationId);
213263
+ } else {
213264
+ active.set(event.invocationId, {
213265
+ event,
213266
+ description,
213267
+ startedAt: now(),
213268
+ order: order++
213269
+ });
213270
+ }
213271
+ if (active.size > 0) {
213272
+ liveDisplays.add(display);
213273
+ ensureTimer();
213274
+ } else {
213275
+ stopTimer();
213276
+ liveDisplays.delete(display);
213277
+ }
213278
+ render();
213279
+ },
213280
+ clear,
213281
+ dispose: clear
213282
+ };
213283
+ return display;
213284
+ }
213285
+
213286
+ // src/cli/update.ts
213287
+ var import_child_process5 = require("child_process");
213288
+ var NORN_NPM_PACKAGE = "norn-cli@latest";
213289
+ function resolveUpdateInvocation(platform2 = process.platform, commandShell = process.env.ComSpec) {
213290
+ if (platform2 === "win32") {
213291
+ return {
213292
+ command: commandShell || "cmd.exe",
213293
+ args: ["/d", "/s", "/c", `npm install --global ${NORN_NPM_PACKAGE}`]
213294
+ };
213295
+ }
213296
+ return {
213297
+ command: "npm",
213298
+ args: ["install", "--global", NORN_NPM_PACKAGE]
213299
+ };
213300
+ }
213301
+ function printUpdateHelp() {
213302
+ console.log(`
213303
+ Usage: norn update
213304
+
213305
+ Updates the globally installed Norn CLI to the latest npm release.
213306
+ Equivalent to: npm install --global ${NORN_NPM_PACKAGE}
213307
+ `);
213308
+ }
213309
+ async function handleUpdateCommand(args, spawnProcess = import_child_process5.spawn) {
213310
+ if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
213311
+ printUpdateHelp();
213312
+ return 0;
213313
+ }
213314
+ if (args.length > 0) {
213315
+ console.error(`Error: norn update does not accept arguments: ${args.join(" ")}`);
213316
+ console.error("Run norn update --help for usage.");
213317
+ return 1;
213318
+ }
213319
+ console.log("Updating Norn CLI to the latest version...");
213320
+ const invocation = resolveUpdateInvocation();
213321
+ return new Promise((resolve29) => {
213322
+ let settled = false;
213323
+ const finish = (exitCode) => {
213324
+ if (!settled) {
213325
+ settled = true;
213326
+ resolve29(exitCode);
213327
+ }
213328
+ };
213329
+ const child = spawnProcess(invocation.command, invocation.args, {
213330
+ stdio: "inherit",
213331
+ windowsHide: true
213332
+ });
213333
+ child.once("error", (error2) => {
213334
+ console.error(`Unable to start npm: ${error2.message}`);
213335
+ console.error(`Run npm install --global ${NORN_NPM_PACKAGE} manually.`);
213336
+ finish(1);
213337
+ });
213338
+ child.once("close", (code, signal) => {
213339
+ if (settled) {
213340
+ return;
213341
+ }
213342
+ if (code === 0) {
213343
+ console.log("Norn CLI update complete. Run norn --version to verify the installed version.");
213344
+ finish(0);
213345
+ return;
213346
+ }
213347
+ const detail = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
213348
+ console.error(`Norn CLI update failed (${detail}).`);
213349
+ finish(1);
213350
+ });
213351
+ });
213352
+ }
213353
+
213125
213354
  // src/cli.ts
213126
213355
  function handleImportResolutionErrors(errors, colors) {
213127
213356
  const { blockingErrors, warningErrors } = splitImportResolutionErrors(errors);
@@ -213270,10 +213499,19 @@ function redactSequenceResultForJson(result, redaction) {
213270
213499
  }
213271
213500
  function createAgentProgressCallback(sequenceName, colors, redaction, verbose) {
213272
213501
  let printedHeading = false;
213502
+ const activityDisplay = createAgentActivityProgressDisplay();
213273
213503
  return (info) => {
213504
+ if (info.agentActivity) {
213505
+ activityDisplay.update(
213506
+ info.agentActivity,
213507
+ redactString(describeAgentActivity(info.agentActivity), redaction)
213508
+ );
213509
+ return;
213510
+ }
213274
213511
  if (info.stepType !== "agent" || !info.stepResult?.agent) {
213275
213512
  return;
213276
213513
  }
213514
+ activityDisplay.clear();
213277
213515
  if (!printedHeading) {
213278
213516
  console.log("");
213279
213517
  console.log(colors.bold(`Agent trace: ${sequenceName}`));
@@ -213627,6 +213865,7 @@ function printHelp() {
213627
213865
  Norn CLI - Run HTTP requests and test sequences
213628
213866
 
213629
213867
  Usage: norn <file.norn|directory> [options]
213868
+ norn update
213630
213869
  norn replay <recording.json> [--json] [--from <agent-or-hop>] [--env <name>]
213631
213870
  norn secrets <command> [options]
213632
213871
  norn contracts <sync|check> <file.nornapi> [options]
@@ -213666,6 +213905,9 @@ Options:
213666
213905
  -V, --version Show the installed Norn CLI version
213667
213906
  -h, --help Show this help message
213668
213907
 
213908
+ Maintenance:
213909
+ norn update Update the global Norn CLI to the latest npm release
213910
+
213669
213911
  Report Generation:
213670
213912
  Use --output-dir for auto-generated timestamped filenames:
213671
213913
  norn tests.norn --output-dir ./reports
@@ -214032,6 +214274,10 @@ async function runAllSequences(fileContent, variables, cookieJar, workingDir, ap
214032
214274
  }
214033
214275
  async function main() {
214034
214276
  const rawArgs = process.argv.slice(2);
214277
+ if (rawArgs[0] === "update") {
214278
+ const exitCode = await handleUpdateCommand(rawArgs.slice(1));
214279
+ process.exit(exitCode);
214280
+ }
214035
214281
  if (rawArgs[0] === "replay") {
214036
214282
  const exitCode = await handleReplayCommand(rawArgs.slice(1));
214037
214283
  process.exit(exitCode);
@@ -214050,7 +214296,7 @@ async function main() {
214050
214296
  }
214051
214297
  const args = rawArgs[0] === "run" ? rawArgs.slice(1) : rawArgs;
214052
214298
  if (args.length === 1 && (args[0] === "--version" || args[0] === "-V")) {
214053
- console.log(`Norn CLI ${"4.2.0"}`);
214299
+ console.log(`Norn CLI ${"4.3.0"}`);
214054
214300
  return;
214055
214301
  }
214056
214302
  if (args.length === 0) {
@@ -214174,6 +214420,7 @@ async function main() {
214174
214420
  }
214175
214421
  if (!options.request) {
214176
214422
  activeRunCancellation = installCliRunCancellation((signal) => {
214423
+ clearAllAgentActivityProgressDisplays();
214177
214424
  console.error(colors.warning(`Cancellation requested by ${signal}; stopping the current run...`));
214178
214425
  });
214179
214426
  }
@@ -214625,6 +214872,7 @@ ${colors.error("Errors:")}`);
214625
214872
  activeRunCancellation = void 0;
214626
214873
  }
214627
214874
  main().catch((error2) => {
214875
+ clearAllAgentActivityProgressDisplays();
214628
214876
  if (isSequenceCancelledError(error2) && activeRunCancellation?.getReceivedSignal()) {
214629
214877
  const exitCode = activeRunCancellation.getExitCode();
214630
214878
  activeRunCancellation.dispose();
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.2.0",
5
+ "version": "4.3.1",
6
6
  "publisher": "Norn-PeterKrustanov",
7
7
  "author": {
8
8
  "name": "Peter Krastanov"
@@ -61,6 +61,7 @@
61
61
  "onCommand:norn.debugSequence",
62
62
  "onCommand:norn.debug.runToNextContractFailure",
63
63
  "onCommand:norn.showAgentGraph",
64
+ "onCommand:norn.showAgentRunResults",
64
65
  "onDebugResolve:norn"
65
66
  ],
66
67
  "main": "./dist/extension.js",
@@ -92,6 +93,11 @@
92
93
  "title": "Show Agent Graph",
93
94
  "category": "Norn"
94
95
  },
96
+ {
97
+ "command": "norn.showAgentRunResults",
98
+ "title": "Show Agent Run Results",
99
+ "category": "Norn"
100
+ },
95
101
  {
96
102
  "command": "norn.knowledge.toggleMarkup",
97
103
  "title": "Knowledge: Toggle Markup",
@@ -625,7 +631,7 @@
625
631
  "test:regression": "node ./tests/test-scripts/run-regression.js",
626
632
  "test:prerelease": "npm test && npm run test:regression",
627
633
  "publish:npm": "node -e \"const fs=require('fs');const p=require('./package.json');p.name='norn-cli';fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')\" && npm publish; exit_code=$?; node -e \"const fs=require('fs');const p=require('./package.json');p.name='norn';fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')\"; exit $exit_code",
628
- "publish:vsce": "node -e \"const p=require('./package.json');p.name='norn';require('fs').writeFileSync('package.json',JSON.stringify(p,null,2))\" && npx vsce publish",
634
+ "publish:vsce": "vsce publish",
629
635
  "publish:all": "npm run publish:npm && npm run publish:vsce"
630
636
  },
631
637
  "devDependencies": {
@@ -637,6 +643,7 @@
637
643
  "@types/vscode": "^1.125.0",
638
644
  "@vscode/test-cli": "^0.0.15",
639
645
  "@vscode/test-electron": "^3.1.0",
646
+ "@vscode/vsce": "^3.9.2",
640
647
  "@xterm/addon-fit": "^0.11.0",
641
648
  "@xterm/xterm": "^6.0.0",
642
649
  "esbuild": "^0.27.2",
@@ -654,7 +661,6 @@
654
661
  "axios": "^1.13.2",
655
662
  "chalk": "^5.4.1",
656
663
  "mssql": "^12.2.0",
657
- "norn-cli": "^1.10.6",
658
664
  "openai": "^6.49.0",
659
665
  "pg": "^8.20.0",
660
666
  "tough-cookie": "^6.0.0"