norn-cli 1.6.0 → 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.
- package/AGENTS.md +9 -1
- package/CHANGELOG.md +23 -0
- package/dist/cli.js +246 -80
- package/package.json +1 -1
- package/out/assertionRunner.js +0 -537
- package/out/chatParticipant.js +0 -722
- package/out/cli/colors.js +0 -129
- package/out/cli/formatters/assertion.js +0 -75
- package/out/cli/formatters/index.js +0 -23
- package/out/cli/formatters/response.js +0 -106
- package/out/cli/formatters/summary.js +0 -187
- package/out/cli/redaction.js +0 -237
- package/out/cli/reporters/html.js +0 -634
- package/out/cli/reporters/index.js +0 -22
- package/out/cli/reporters/junit.js +0 -211
- package/out/cli.js +0 -989
- package/out/codeLensProvider.js +0 -248
- package/out/compareContentProvider.js +0 -85
- package/out/completionProvider.js +0 -2404
- package/out/contractDecorationProvider.js +0 -243
- package/out/coverageCalculator.js +0 -837
- package/out/coveragePanel.js +0 -545
- package/out/diagnosticProvider.js +0 -1113
- package/out/environmentProvider.js +0 -442
- package/out/extension.js +0 -1114
- package/out/httpClient.js +0 -269
- package/out/jsonFileReader.js +0 -320
- package/out/nornPrompt.js +0 -580
- package/out/nornapiParser.js +0 -326
- package/out/parser.js +0 -725
- package/out/responsePanel.js +0 -4674
- package/out/schemaGenerator.js +0 -393
- package/out/scriptRunner.js +0 -419
- package/out/sequenceRunner.js +0 -3046
- package/out/swaggerBodyIntellisenseCache.js +0 -147
- package/out/swaggerParser.js +0 -419
- package/out/test/coverageCalculator.test.js +0 -100
- package/out/test/extension.test.js +0 -48
- package/out/testProvider.js +0 -658
- 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
|
|
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,29 @@ 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
|
+
|
|
19
|
+
## [1.6.1] - 2026-03-04
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- **Parameterized Debug Launch (VS Code)**:
|
|
23
|
+
- Fixed `Debug Sequence` launch from editor/CodeLens so `@data` / `@theory` case arguments are passed into the debug session.
|
|
24
|
+
- Added case selection prompt when multiple parameterized cases exist, so a single concrete case is debugged without unresolved placeholders.
|
|
25
|
+
|
|
26
|
+
- **CLI `--sequence` Parameterized Execution**:
|
|
27
|
+
- Fixed `node ./dist/cli.js <file> -s <sequence>` to execute `@data` / `@theory` cases (instead of ignoring case args and failing on unresolved variables).
|
|
28
|
+
- Aligned `--sequence` behavior with full test execution mode for parameterized sequences.
|
|
29
|
+
|
|
7
30
|
## [1.6.0] - 2026-03-04
|
|
8
31
|
|
|
9
32
|
### Added
|
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(
|
|
11603
|
-
return useNativeURL ? new URL2(
|
|
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,
|
|
15700
|
+
function resolveComponent(base, relative5, options, skipNormalization) {
|
|
15701
15701
|
const target = {};
|
|
15702
15702
|
if (!skipNormalization) {
|
|
15703
15703
|
base = parse2(serialize(base, options), options);
|
|
15704
|
-
|
|
15704
|
+
relative5 = parse2(serialize(relative5, options), options);
|
|
15705
15705
|
}
|
|
15706
15706
|
options = options || {};
|
|
15707
|
-
if (!options.tolerant &&
|
|
15708
|
-
target.scheme =
|
|
15709
|
-
target.userinfo =
|
|
15710
|
-
target.host =
|
|
15711
|
-
target.port =
|
|
15712
|
-
target.path = removeDotSegments(
|
|
15713
|
-
target.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 (
|
|
15716
|
-
target.userinfo =
|
|
15717
|
-
target.host =
|
|
15718
|
-
target.port =
|
|
15719
|
-
target.path = removeDotSegments(
|
|
15720
|
-
target.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 (!
|
|
15722
|
+
if (!relative5.path) {
|
|
15723
15723
|
target.path = base.path;
|
|
15724
|
-
if (
|
|
15725
|
-
target.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 (
|
|
15731
|
-
target.path = removeDotSegments(
|
|
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 = "/" +
|
|
15734
|
+
target.path = "/" + relative5.path;
|
|
15735
15735
|
} else if (!base.path) {
|
|
15736
|
-
target.path =
|
|
15736
|
+
target.path = relative5.path;
|
|
15737
15737
|
} else {
|
|
15738
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
30795
|
-
|
|
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
|
-
|
|
30798
|
-
return stats.isDirectory() ? resolvedPath : path5.dirname(resolvedPath);
|
|
30827
|
+
return fs6.existsSync(cacheFilePath) && fs6.statSync(cacheFilePath).isFile();
|
|
30799
30828
|
} catch {
|
|
30800
|
-
return
|
|
30829
|
+
return false;
|
|
30801
30830
|
}
|
|
30802
30831
|
}
|
|
30803
|
-
function
|
|
30804
|
-
|
|
30805
|
-
|
|
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 (
|
|
30808
|
-
|
|
30845
|
+
if (cacheFileExists(cacheDir)) {
|
|
30846
|
+
results.push(cacheDir);
|
|
30809
30847
|
}
|
|
30810
|
-
|
|
30811
|
-
|
|
30812
|
-
|
|
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 =
|
|
30819
|
-
const
|
|
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
|
|
30827
|
-
const
|
|
30828
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
31177
|
-
return
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
31415
|
-
const fileLabel =
|
|
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
|
}
|
|
@@ -32376,34 +32478,98 @@ ${fileContent}` : fileContent;
|
|
|
32376
32478
|
}
|
|
32377
32479
|
}
|
|
32378
32480
|
}
|
|
32379
|
-
|
|
32380
|
-
|
|
32381
|
-
|
|
32382
|
-
|
|
32383
|
-
|
|
32384
|
-
|
|
32385
|
-
|
|
32386
|
-
|
|
32387
|
-
|
|
32388
|
-
|
|
32389
|
-
|
|
32390
|
-
|
|
32391
|
-
|
|
32392
|
-
|
|
32393
|
-
|
|
32481
|
+
let theoryData = targetSeq.theoryData;
|
|
32482
|
+
if (theoryData?.source && theoryData.cases.length === 0) {
|
|
32483
|
+
try {
|
|
32484
|
+
const cases = await loadTheoryFile(theoryData.source, workingDir);
|
|
32485
|
+
theoryData = { ...theoryData, cases };
|
|
32486
|
+
} catch (error) {
|
|
32487
|
+
const message = `Failed to load theory file '${theoryData.source}': ${error instanceof Error ? error.message : String(error)}`;
|
|
32488
|
+
const failedResult = {
|
|
32489
|
+
name: targetSeq.name,
|
|
32490
|
+
success: false,
|
|
32491
|
+
responses: [],
|
|
32492
|
+
scriptResults: [],
|
|
32493
|
+
assertionResults: [],
|
|
32494
|
+
steps: [],
|
|
32495
|
+
errors: [message],
|
|
32496
|
+
duration: 0
|
|
32497
|
+
};
|
|
32498
|
+
if (options.output === "json") {
|
|
32499
|
+
console.log(JSON.stringify({ success: false, results: [failedResult] }, null, 2));
|
|
32500
|
+
} else {
|
|
32501
|
+
const lines = formatSequenceResult(failedResult, { colors, verbose: options.verbose, redaction: redaction2 });
|
|
32502
|
+
for (const line2 of lines) {
|
|
32503
|
+
console.log(line2);
|
|
32504
|
+
}
|
|
32505
|
+
const summaryLines = formatRunSummary([failedResult], Date.now() - startTime, colors);
|
|
32506
|
+
for (const line2 of summaryLines) {
|
|
32507
|
+
console.log(line2);
|
|
32508
|
+
}
|
|
32509
|
+
}
|
|
32510
|
+
process.exit(1);
|
|
32511
|
+
}
|
|
32512
|
+
}
|
|
32513
|
+
const sequenceResults = [];
|
|
32514
|
+
if (theoryData && theoryData.cases.length > 0) {
|
|
32515
|
+
for (let caseIdx = 0; caseIdx < theoryData.cases.length; caseIdx++) {
|
|
32516
|
+
const caseParams = theoryData.cases[caseIdx];
|
|
32517
|
+
const caseLabel = formatCaseLabel(caseParams);
|
|
32518
|
+
const caseArgs = { ...defaultArgs };
|
|
32519
|
+
for (const [key, value] of Object.entries(caseParams)) {
|
|
32520
|
+
caseArgs[key] = String(value);
|
|
32521
|
+
}
|
|
32522
|
+
const caseResult = await runSequenceWithJar(
|
|
32523
|
+
targetSeq.content,
|
|
32524
|
+
variables,
|
|
32525
|
+
cookieJar,
|
|
32526
|
+
workingDir,
|
|
32527
|
+
fileContentWithImports,
|
|
32528
|
+
void 0,
|
|
32529
|
+
void 0,
|
|
32530
|
+
caseArgs,
|
|
32531
|
+
apiDefinitions,
|
|
32532
|
+
tagFilterOptions,
|
|
32533
|
+
importResult.sequenceSources,
|
|
32534
|
+
{ filePath, sequenceName: targetSeq.name, environment: envValidationContext }
|
|
32535
|
+
);
|
|
32536
|
+
caseResult.name = `${targetSeq.name}${caseLabel}`;
|
|
32537
|
+
sequenceResults.push(caseResult);
|
|
32538
|
+
}
|
|
32539
|
+
} else {
|
|
32540
|
+
const seqResult = await runSequenceWithJar(
|
|
32541
|
+
targetSeq.content,
|
|
32542
|
+
variables,
|
|
32543
|
+
cookieJar,
|
|
32544
|
+
workingDir,
|
|
32545
|
+
fileContentWithImports,
|
|
32546
|
+
void 0,
|
|
32547
|
+
void 0,
|
|
32548
|
+
defaultArgs,
|
|
32549
|
+
apiDefinitions,
|
|
32550
|
+
tagFilterOptions,
|
|
32551
|
+
importResult.sequenceSources,
|
|
32552
|
+
{ filePath, sequenceName: targetSeq.name, environment: envValidationContext }
|
|
32553
|
+
);
|
|
32554
|
+
seqResult.name = targetSeq.name;
|
|
32555
|
+
sequenceResults.push(seqResult);
|
|
32556
|
+
}
|
|
32394
32557
|
if (options.output === "json") {
|
|
32395
|
-
|
|
32558
|
+
const success = sequenceResults.every((result2) => result2.success);
|
|
32559
|
+
console.log(JSON.stringify({ success, results: sequenceResults }, null, 2));
|
|
32396
32560
|
} else {
|
|
32397
|
-
const
|
|
32398
|
-
|
|
32399
|
-
|
|
32561
|
+
for (const result2 of sequenceResults) {
|
|
32562
|
+
const lines = formatSequenceResult(result2, { colors, verbose: options.verbose, redaction: redaction2 });
|
|
32563
|
+
for (const line2 of lines) {
|
|
32564
|
+
console.log(line2);
|
|
32565
|
+
}
|
|
32400
32566
|
}
|
|
32401
|
-
const summaryLines = formatRunSummary(
|
|
32567
|
+
const summaryLines = formatRunSummary(sequenceResults, Date.now() - startTime, colors);
|
|
32402
32568
|
for (const line2 of summaryLines) {
|
|
32403
32569
|
console.log(line2);
|
|
32404
32570
|
}
|
|
32405
32571
|
}
|
|
32406
|
-
process.exit(
|
|
32572
|
+
process.exit(sequenceResults.every((result2) => result2.success) ? 0 : 1);
|
|
32407
32573
|
}
|
|
32408
32574
|
}
|
|
32409
32575
|
let totalTestCount = 0;
|
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.
|
|
5
|
+
"version": "1.6.2",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|