norn-cli 1.6.1 → 1.6.2

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 (40) hide show
  1. package/AGENTS.md +9 -1
  2. package/CHANGELOG.md +12 -0
  3. package/dist/cli.js +161 -59
  4. package/package.json +1 -1
  5. package/out/assertionRunner.js +0 -537
  6. package/out/chatParticipant.js +0 -722
  7. package/out/cli/colors.js +0 -129
  8. package/out/cli/formatters/assertion.js +0 -75
  9. package/out/cli/formatters/index.js +0 -23
  10. package/out/cli/formatters/response.js +0 -106
  11. package/out/cli/formatters/summary.js +0 -187
  12. package/out/cli/redaction.js +0 -237
  13. package/out/cli/reporters/html.js +0 -634
  14. package/out/cli/reporters/index.js +0 -22
  15. package/out/cli/reporters/junit.js +0 -211
  16. package/out/cli.js +0 -989
  17. package/out/codeLensProvider.js +0 -248
  18. package/out/compareContentProvider.js +0 -85
  19. package/out/completionProvider.js +0 -2404
  20. package/out/contractDecorationProvider.js +0 -243
  21. package/out/coverageCalculator.js +0 -837
  22. package/out/coveragePanel.js +0 -545
  23. package/out/diagnosticProvider.js +0 -1113
  24. package/out/environmentProvider.js +0 -442
  25. package/out/extension.js +0 -1114
  26. package/out/httpClient.js +0 -269
  27. package/out/jsonFileReader.js +0 -320
  28. package/out/nornPrompt.js +0 -580
  29. package/out/nornapiParser.js +0 -326
  30. package/out/parser.js +0 -725
  31. package/out/responsePanel.js +0 -4674
  32. package/out/schemaGenerator.js +0 -393
  33. package/out/scriptRunner.js +0 -419
  34. package/out/sequenceRunner.js +0 -3046
  35. package/out/swaggerBodyIntellisenseCache.js +0 -147
  36. package/out/swaggerParser.js +0 -419
  37. package/out/test/coverageCalculator.test.js +0 -100
  38. package/out/test/extension.test.js +0 -48
  39. package/out/testProvider.js +0 -658
  40. package/out/validationCache.js +0 -245
package/AGENTS.md CHANGED
@@ -48,12 +48,20 @@ Skills should capture lessons learned and patterns discovered during implementat
48
48
  **MANDATORY:** When the user asks to publish, prepare a release, bump version, or create a patch:
49
49
  1. **FIRST** invoke the Test Verification subagent using `runSubagent`
50
50
  2. Wait for it to complete and report results
51
- 3. Only proceed if all tests pass
51
+ 3. Only if all tests pass, invoke the Website Documentation Review subagent using `runSubagent`
52
+ 4. Only proceed if tests pass and the docs review reports either:
53
+ - website docs were updated and the website build passed, or
54
+ - no docs changes were needed because the release is only bug fixes, refactors, or design/styling work
52
55
 
53
56
  The Test Verification agent runs:
54
57
  - `npm run compile` (must have 0 errors)
55
58
  - `node ./dist/cli.js ./tests/Regression/ -e prelive` (all tests must pass)
56
59
 
60
+ The Website Documentation Review agent:
61
+ - reviews release changes in `/Users/petercrest/Worktable/Projects/vsApi`
62
+ - updates `/Users/petercrest/Worktable/Projects/norn_website` when user-facing features changed or new ones were added
63
+ - skips docs work for bug fixes, refactors, internal cleanup, and design/styling-only changes
64
+
57
65
  **Trigger words:** publish, release, version, patch, bump, deploy
58
66
 
59
67
  ## Publishing Ownership
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to the "Norn" extension will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.6.2] - 2026-03-07
8
+
9
+ ### Fixed
10
+ - **`.nornenv` IntelliSense and Diagnostics**:
11
+ - Fixed `[env:` completion in `.nornenv` files so accepting the suggestion after typing `[` no longer inserts `[[env:`.
12
+ - Fixed `.nornenv` diagnostics and syntax highlighting so secret names that start with `var` (for example `secret varnaCityApiKey = ...`) are no longer flagged as invalid.
13
+
14
+ - **Encrypted Secret UX (Extension + CLI)**:
15
+ - Fixed secret-key cache placement so new secret-key caches are created in the project root `.norn-cache` instead of nested folders beside individual `.nornenv` files.
16
+ - Reused an existing secret-key cache when one is already present in the project, avoiding duplicate `.norn-cache` folders.
17
+ - Fixed Windows CRLF handling for `.nornenv` secret parsing so the `Encrypt Secret` CodeLens remains available reliably on Windows and secret rewrites preserve the file's original line endings.
18
+
7
19
  ## [1.6.1] - 2026-03-04
8
20
 
9
21
  ### Fixed
package/dist/cli.js CHANGED
@@ -11599,8 +11599,8 @@ var require_follow_redirects = __commonJS({
11599
11599
  }
11600
11600
  return parsed;
11601
11601
  }
11602
- function resolveUrl(relative4, base) {
11603
- return useNativeURL ? new URL2(relative4, base) : parseUrl(url2.resolve(base, relative4));
11602
+ function resolveUrl(relative5, base) {
11603
+ return useNativeURL ? new URL2(relative5, base) : parseUrl(url2.resolve(base, relative5));
11604
11604
  }
11605
11605
  function validateUrl(input2) {
11606
11606
  if (/^\[/.test(input2.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input2.hostname)) {
@@ -15697,49 +15697,49 @@ var require_fast_uri = __commonJS({
15697
15697
  schemelessOptions.skipEscape = true;
15698
15698
  return serialize(resolved, schemelessOptions);
15699
15699
  }
15700
- function resolveComponent(base, relative4, options, skipNormalization) {
15700
+ function resolveComponent(base, relative5, options, skipNormalization) {
15701
15701
  const target = {};
15702
15702
  if (!skipNormalization) {
15703
15703
  base = parse2(serialize(base, options), options);
15704
- relative4 = parse2(serialize(relative4, options), options);
15704
+ relative5 = parse2(serialize(relative5, options), options);
15705
15705
  }
15706
15706
  options = options || {};
15707
- if (!options.tolerant && relative4.scheme) {
15708
- target.scheme = relative4.scheme;
15709
- target.userinfo = relative4.userinfo;
15710
- target.host = relative4.host;
15711
- target.port = relative4.port;
15712
- target.path = removeDotSegments(relative4.path || "");
15713
- target.query = relative4.query;
15707
+ if (!options.tolerant && relative5.scheme) {
15708
+ target.scheme = relative5.scheme;
15709
+ target.userinfo = relative5.userinfo;
15710
+ target.host = relative5.host;
15711
+ target.port = relative5.port;
15712
+ target.path = removeDotSegments(relative5.path || "");
15713
+ target.query = relative5.query;
15714
15714
  } else {
15715
- if (relative4.userinfo !== void 0 || relative4.host !== void 0 || relative4.port !== void 0) {
15716
- target.userinfo = relative4.userinfo;
15717
- target.host = relative4.host;
15718
- target.port = relative4.port;
15719
- target.path = removeDotSegments(relative4.path || "");
15720
- target.query = relative4.query;
15715
+ if (relative5.userinfo !== void 0 || relative5.host !== void 0 || relative5.port !== void 0) {
15716
+ target.userinfo = relative5.userinfo;
15717
+ target.host = relative5.host;
15718
+ target.port = relative5.port;
15719
+ target.path = removeDotSegments(relative5.path || "");
15720
+ target.query = relative5.query;
15721
15721
  } else {
15722
- if (!relative4.path) {
15722
+ if (!relative5.path) {
15723
15723
  target.path = base.path;
15724
- if (relative4.query !== void 0) {
15725
- target.query = relative4.query;
15724
+ if (relative5.query !== void 0) {
15725
+ target.query = relative5.query;
15726
15726
  } else {
15727
15727
  target.query = base.query;
15728
15728
  }
15729
15729
  } else {
15730
- if (relative4.path[0] === "/") {
15731
- target.path = removeDotSegments(relative4.path);
15730
+ if (relative5.path[0] === "/") {
15731
+ target.path = removeDotSegments(relative5.path);
15732
15732
  } else {
15733
15733
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
15734
- target.path = "/" + relative4.path;
15734
+ target.path = "/" + relative5.path;
15735
15735
  } else if (!base.path) {
15736
- target.path = relative4.path;
15736
+ target.path = relative5.path;
15737
15737
  } else {
15738
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative4.path;
15738
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative5.path;
15739
15739
  }
15740
15740
  target.path = removeDotSegments(target.path);
15741
15741
  }
15742
- target.query = relative4.query;
15742
+ target.query = relative5.query;
15743
15743
  }
15744
15744
  target.userinfo = base.userinfo;
15745
15745
  target.host = base.host;
@@ -15747,7 +15747,7 @@ var require_fast_uri = __commonJS({
15747
15747
  }
15748
15748
  target.scheme = base.scheme;
15749
15749
  }
15750
- target.fragment = relative4.fragment;
15750
+ target.fragment = relative5.fragment;
15751
15751
  return target;
15752
15752
  }
15753
15753
  function equal(uriA, uriB, options) {
@@ -30775,6 +30775,31 @@ var path4 = __toESM(require("path"));
30775
30775
  var NORN_CACHE_DIR = ".norn-cache";
30776
30776
  var CACHE_GITIGNORE_FILE = ".gitignore";
30777
30777
  var CACHE_GITIGNORE_CONTENT = "*\n";
30778
+ var PROJECT_ROOT_MARKERS = [".git", "package.json", "pnpm-workspace.yaml", "package-lock.json", "yarn.lock", ".nornenv"];
30779
+ function getSearchStartDirectory(targetPath) {
30780
+ const resolvedPath = path4.resolve(targetPath);
30781
+ try {
30782
+ const stats = fs5.statSync(resolvedPath);
30783
+ return stats.isDirectory() ? resolvedPath : path4.dirname(resolvedPath);
30784
+ } catch {
30785
+ return path4.dirname(resolvedPath);
30786
+ }
30787
+ }
30788
+ function findProjectRoot(targetPath) {
30789
+ let dir = getSearchStartDirectory(targetPath);
30790
+ let projectRoot;
30791
+ while (true) {
30792
+ if (PROJECT_ROOT_MARKERS.some((marker) => fs5.existsSync(path4.join(dir, marker)))) {
30793
+ projectRoot = dir;
30794
+ }
30795
+ const parent = path4.dirname(dir);
30796
+ if (parent === dir) {
30797
+ break;
30798
+ }
30799
+ dir = parent;
30800
+ }
30801
+ return projectRoot ?? getSearchStartDirectory(targetPath);
30802
+ }
30778
30803
  function ensureNornCacheGitignore(cacheDir) {
30779
30804
  const gitignorePath = path4.join(cacheDir, CACHE_GITIGNORE_FILE);
30780
30805
  try {
@@ -30790,46 +30815,114 @@ function ensureNornCacheGitignore(cacheDir) {
30790
30815
  // src/secrets/keyStore.ts
30791
30816
  var CACHE_FILE = "secret-keys.json";
30792
30817
  var CACHE_VERSION = 1;
30818
+ var CACHE_SCAN_IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "out"]);
30793
30819
  var sessionKeys = /* @__PURE__ */ new Map();
30794
- function getSearchStartDirectory(targetPath) {
30795
- const resolvedPath = path5.resolve(targetPath);
30820
+ var projectSecretKeyCacheDirs = /* @__PURE__ */ new Map();
30821
+ function getCacheFilePath(cacheDir) {
30822
+ return path5.join(cacheDir, CACHE_FILE);
30823
+ }
30824
+ function cacheFileExists(cacheDir) {
30825
+ const cacheFilePath = getCacheFilePath(cacheDir);
30796
30826
  try {
30797
- const stats = fs6.statSync(resolvedPath);
30798
- return stats.isDirectory() ? resolvedPath : path5.dirname(resolvedPath);
30827
+ return fs6.existsSync(cacheFilePath) && fs6.statSync(cacheFilePath).isFile();
30799
30828
  } catch {
30800
- return path5.dirname(resolvedPath);
30829
+ return false;
30801
30830
  }
30802
30831
  }
30803
- function findExistingCacheDir(targetPath) {
30804
- let dir = getSearchStartDirectory(targetPath);
30805
- while (true) {
30832
+ function getProjectSecretKeyCacheDirs(projectRoot) {
30833
+ const cached = projectSecretKeyCacheDirs.get(projectRoot);
30834
+ if (cached && cached.every((cacheDir) => cacheFileExists(cacheDir))) {
30835
+ return cached;
30836
+ }
30837
+ const results = [];
30838
+ const queue = [projectRoot];
30839
+ while (queue.length > 0) {
30840
+ const dir = queue.shift();
30841
+ if (!dir) {
30842
+ continue;
30843
+ }
30806
30844
  const cacheDir = path5.join(dir, NORN_CACHE_DIR);
30807
- if (fs6.existsSync(cacheDir) && fs6.statSync(cacheDir).isDirectory()) {
30808
- return cacheDir;
30845
+ if (cacheFileExists(cacheDir)) {
30846
+ results.push(cacheDir);
30809
30847
  }
30810
- const parent = path5.dirname(dir);
30811
- if (parent === dir) {
30812
- return void 0;
30848
+ let entries;
30849
+ try {
30850
+ entries = fs6.readdirSync(dir, { withFileTypes: true });
30851
+ } catch {
30852
+ continue;
30853
+ }
30854
+ for (const entry of entries) {
30855
+ if (!entry.isDirectory()) {
30856
+ continue;
30857
+ }
30858
+ if (entry.name === NORN_CACHE_DIR || CACHE_SCAN_IGNORED_DIRS.has(entry.name)) {
30859
+ continue;
30860
+ }
30861
+ queue.push(path5.join(dir, entry.name));
30813
30862
  }
30814
- dir = parent;
30815
30863
  }
30864
+ results.sort();
30865
+ projectSecretKeyCacheDirs.set(projectRoot, results);
30866
+ return results;
30867
+ }
30868
+ function getCacheDistance(targetPath, cacheDir) {
30869
+ const relative5 = path5.relative(getSearchStartDirectory(targetPath), cacheDir);
30870
+ if (!relative5 || relative5 === ".") {
30871
+ return 0;
30872
+ }
30873
+ return relative5.split(path5.sep).filter((segment) => segment !== "").length;
30874
+ }
30875
+ function findExistingSecretKeyCacheDir(targetPath) {
30876
+ const projectRoot = findProjectRoot(targetPath);
30877
+ const candidates = getProjectSecretKeyCacheDirs(projectRoot);
30878
+ let bestMatch;
30879
+ let bestScore = Number.POSITIVE_INFINITY;
30880
+ for (const candidate of candidates) {
30881
+ const score = getCacheDistance(targetPath, candidate);
30882
+ if (score < bestScore || score === bestScore && candidate < (bestMatch ?? candidate)) {
30883
+ bestMatch = candidate;
30884
+ bestScore = score;
30885
+ }
30886
+ }
30887
+ return bestMatch;
30888
+ }
30889
+ function rememberSecretKeyCacheDir(targetPath, cacheDir) {
30890
+ const projectRoot = findProjectRoot(targetPath);
30891
+ const cached = projectSecretKeyCacheDirs.get(projectRoot) ?? [];
30892
+ if (cached.includes(cacheDir)) {
30893
+ return;
30894
+ }
30895
+ projectSecretKeyCacheDirs.set(projectRoot, [...cached, cacheDir].sort());
30816
30896
  }
30817
30897
  function ensureCacheDir(targetPath) {
30818
- const existing = findExistingCacheDir(targetPath);
30819
- const cacheDir = existing ?? path5.join(getSearchStartDirectory(targetPath), NORN_CACHE_DIR);
30898
+ const existing = findExistingSecretKeyCacheDir(targetPath);
30899
+ const projectRoot = findProjectRoot(targetPath);
30900
+ const cacheDir = existing ?? path5.join(projectRoot, NORN_CACHE_DIR);
30820
30901
  if (!fs6.existsSync(cacheDir)) {
30821
30902
  fs6.mkdirSync(cacheDir, { recursive: true });
30822
30903
  }
30823
30904
  ensureNornCacheGitignore(cacheDir);
30905
+ rememberSecretKeyCacheDir(targetPath, cacheDir);
30824
30906
  return cacheDir;
30825
30907
  }
30826
- function getCachePath(targetPath) {
30827
- const cacheDir = ensureCacheDir(targetPath);
30828
- return path5.join(cacheDir, CACHE_FILE);
30908
+ function getReadCachePath(targetPath) {
30909
+ const existing = findExistingSecretKeyCacheDir(targetPath);
30910
+ if (existing) {
30911
+ return getCacheFilePath(existing);
30912
+ }
30913
+ const projectRoot = findProjectRoot(targetPath);
30914
+ const projectCacheDir = path5.join(projectRoot, NORN_CACHE_DIR);
30915
+ if (fs6.existsSync(projectCacheDir) && fs6.statSync(projectCacheDir).isDirectory()) {
30916
+ return getCacheFilePath(projectCacheDir);
30917
+ }
30918
+ return void 0;
30919
+ }
30920
+ function getWriteCachePath(targetPath) {
30921
+ return getCacheFilePath(ensureCacheDir(targetPath));
30829
30922
  }
30830
30923
  function readCache(targetPath) {
30831
- const cachePath = getCachePath(targetPath);
30832
- if (!fs6.existsSync(cachePath)) {
30924
+ const cachePath = getReadCachePath(targetPath);
30925
+ if (!cachePath || !fs6.existsSync(cachePath)) {
30833
30926
  return { version: CACHE_VERSION, keys: {} };
30834
30927
  }
30835
30928
  try {
@@ -30843,7 +30936,7 @@ function readCache(targetPath) {
30843
30936
  }
30844
30937
  }
30845
30938
  function writeCache(targetPath, cache) {
30846
- const cachePath = getCachePath(targetPath);
30939
+ const cachePath = getWriteCachePath(targetPath);
30847
30940
  fs6.writeFileSync(cachePath, JSON.stringify(cache, null, 2), { encoding: "utf8", mode: 384 });
30848
30941
  try {
30849
30942
  fs6.chmodSync(cachePath, 384);
@@ -31173,8 +31266,8 @@ function mergeConfigs(target, source, targetFilePath, sourceFilePath, variableOr
31173
31266
  }
31174
31267
  function toDisplayPath(filePath, entryFilePath) {
31175
31268
  const entryDir = path6.dirname(entryFilePath);
31176
- const relative4 = path6.relative(entryDir, filePath);
31177
- return relative4 && relative4 !== "" ? relative4 : path6.basename(filePath);
31269
+ const relative5 = path6.relative(entryDir, filePath);
31270
+ return relative5 && relative5 !== "" ? relative5 : path6.basename(filePath);
31178
31271
  }
31179
31272
  function loadAndResolveEnvFile(filePath) {
31180
31273
  const content = fs7.readFileSync(filePath, "utf-8");
@@ -31263,8 +31356,17 @@ var fs8 = __toESM(require("fs"));
31263
31356
  var path7 = __toESM(require("path"));
31264
31357
  var envRegex2 = /^\s*\[env:([a-zA-Z_][a-zA-Z0-9_-]*)\]\s*$/;
31265
31358
  var secretRegex2 = /^(\s*secret\s+)([a-zA-Z_][a-zA-Z0-9_]*)(\s*=\s*)(.+)$/;
31359
+ function splitContentLines(content) {
31360
+ return content.split(/\r?\n/);
31361
+ }
31362
+ function detectEol(content) {
31363
+ return content.includes("\r\n") ? "\r\n" : "\n";
31364
+ }
31365
+ function isNornenvFilePath(filePath) {
31366
+ return path7.basename(filePath).toLowerCase() === ".nornenv";
31367
+ }
31266
31368
  function extractSecretLines(content, filePath) {
31267
- const lines = content.split("\n");
31369
+ const lines = splitContentLines(content);
31268
31370
  const secrets = [];
31269
31371
  let currentEnv;
31270
31372
  for (let i = 0; i < lines.length; i++) {
@@ -31304,7 +31406,7 @@ function extractSecretLines(content, filePath) {
31304
31406
  return secrets;
31305
31407
  }
31306
31408
  function updateSecretLineValue(content, lineNumber, newValue) {
31307
- const lines = content.split("\n");
31409
+ const lines = splitContentLines(content);
31308
31410
  if (lineNumber < 0 || lineNumber >= lines.length) {
31309
31411
  throw new Error(`Line ${lineNumber + 1} is out of range.`);
31310
31412
  }
@@ -31314,7 +31416,7 @@ function updateSecretLineValue(content, lineNumber, newValue) {
31314
31416
  throw new Error(`Line ${lineNumber + 1} is not a secret declaration.`);
31315
31417
  }
31316
31418
  lines[lineNumber] = `${secretMatch[1]}${secretMatch[2]}${secretMatch[3]}${newValue}`;
31317
- return lines.join("\n");
31419
+ return lines.join(detectEol(content));
31318
31420
  }
31319
31421
  function findSecretLine(content, variableName, envName) {
31320
31422
  const secretLines = extractSecretLines(content);
@@ -31346,7 +31448,7 @@ function discoverNornenvFiles(targetPath) {
31346
31448
  }
31347
31449
  const stats = fs8.statSync(resolved);
31348
31450
  if (stats.isFile()) {
31349
- return path7.basename(resolved) === ".nornenv" ? [resolved] : [];
31451
+ return isNornenvFilePath(resolved) ? [resolved] : [];
31350
31452
  }
31351
31453
  const results = [];
31352
31454
  const walk = (dir) => {
@@ -31360,7 +31462,7 @@ function discoverNornenvFiles(targetPath) {
31360
31462
  walk(fullPath);
31361
31463
  continue;
31362
31464
  }
31363
- if (entry.isFile() && entry.name === ".nornenv") {
31465
+ if (entry.isFile() && isNornenvFilePath(entry.name)) {
31364
31466
  results.push(fullPath);
31365
31467
  }
31366
31468
  }
@@ -31411,8 +31513,8 @@ function formatSecretError(err, envFilePath) {
31411
31513
  if (!envFilePath) {
31412
31514
  return `${err.message}`;
31413
31515
  }
31414
- const relative4 = path8.relative(path8.dirname(envFilePath), err.filePath);
31415
- const fileLabel = relative4 && relative4 !== "" ? relative4 : path8.basename(err.filePath);
31516
+ const relative5 = path8.relative(path8.dirname(envFilePath), err.filePath);
31517
+ const fileLabel = relative5 && relative5 !== "" ? relative5 : path8.basename(err.filePath);
31416
31518
  const lineLabel = err.line >= 0 ? `${fileLabel}:${err.line + 1}` : fileLabel;
31417
31519
  return `${lineLabel} - ${err.message}`;
31418
31520
  }
@@ -31465,7 +31567,7 @@ Usage:
31465
31567
 
31466
31568
  Notes:
31467
31569
  - Secrets are stored as ENC[NORN_AGE_V1:kid=<id>:<payload>] in .nornenv.
31468
- - Keys are cached locally in .norn-cache/secret-keys.json (gitignored).
31570
+ - Keys are cached locally in the project .norn-cache/secret-keys.json (gitignored).
31469
31571
  - norn run auto-prompts once for missing kids when interactive.
31470
31572
  `);
31471
31573
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "norn-cli",
3
3
  "displayName": "Norn - REST Client",
4
4
  "description": "A powerful REST client for making HTTP requests with sequences, variables, scripts, and cookie support",
5
- "version": "1.6.1",
5
+ "version": "1.6.2",
6
6
  "publisher": "Norn-PeterKrustanov",
7
7
  "author": {
8
8
  "name": "Peter Krastanov"