norn-cli 2.3.0 → 2.4.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/.claude/skills/norn-social-campaign/SKILL.md +70 -0
- package/CHANGELOG.md +6 -0
- package/demos/nornenv-region-refactor/README.md +64 -0
- package/dist/cli.js +360 -1
- package/out/apiResponseIntellisenseCache.js +394 -0
- package/out/assertionRunner.js +567 -0
- package/out/cacheDir.js +136 -0
- package/out/chatParticipant.js +763 -0
- package/out/cli/colors.js +127 -0
- package/out/cli/formatters/assertion.js +102 -0
- package/out/cli/formatters/index.js +23 -0
- package/out/cli/formatters/response.js +106 -0
- package/out/cli/formatters/summary.js +246 -0
- package/out/cli/redaction.js +237 -0
- package/out/cli/reporters/html.js +689 -0
- package/out/cli/reporters/index.js +22 -0
- package/out/cli/reporters/junit.js +226 -0
- package/out/codeLensProvider.js +351 -0
- package/out/compareContentProvider.js +85 -0
- package/out/completionProvider.js +3739 -0
- package/out/contractAssertionSummary.js +225 -0
- package/out/contractDecorationProvider.js +243 -0
- package/out/coverageCalculator.js +879 -0
- package/out/coveragePanel.js +597 -0
- package/out/debug/breakpointResolver.js +84 -0
- package/out/debug/breakpoints.js +52 -0
- package/out/debug/nornDebugAdapter.js +166 -0
- package/out/debug/nornDebugSession.js +613 -0
- package/out/debug/sequenceLocationIndex.js +77 -0
- package/out/debug/types.js +3 -0
- package/out/deepClone.js +21 -0
- package/out/diagnosticProvider.js +2554 -0
- package/out/environmentParser.js +736 -0
- package/out/environmentProvider.js +544 -0
- package/out/environmentTemplates.js +146 -0
- package/out/errors/formatError.js +113 -0
- package/out/errors/nornError.js +29 -0
- package/out/formUrlEncoded.js +89 -0
- package/out/httpClient.js +348 -0
- package/out/httpRuntimeOptions.js +16 -0
- package/out/importErrors.js +31 -0
- package/out/inlayHintResolver.js +70 -0
- package/out/jsonFileReader.js +323 -0
- package/out/mcpClient.js +193 -0
- package/out/mcpConfig.js +184 -0
- package/out/mcpToolIntellisenseCache.js +96 -0
- package/out/mcpToolSchema.js +50 -0
- package/out/nornConfig.js +132 -0
- package/out/nornHoverProvider.js +124 -0
- package/out/nornInlayHintsProvider.js +191 -0
- package/out/nornPrompt.js +755 -0
- package/out/nornSqlParser.js +286 -0
- package/out/nornapiHoverProvider.js +135 -0
- package/out/nornapiInlayHintsProvider.js +94 -0
- package/out/nornapiParser.js +324 -0
- package/out/nornenvCodeActionProvider.js +101 -0
- package/out/nornenvDecorationProvider.js +239 -0
- package/out/nornenvFoldingProvider.js +63 -0
- package/out/nornenvHoverProvider.js +114 -0
- package/out/nornenvInlayHintsProvider.js +99 -0
- package/out/nornenvLanguageModel.js +187 -0
- package/out/nornenvRegionRefactor.js +267 -0
- package/out/nornsqlHoverProvider.js +95 -0
- package/out/nornsqlInlayHintsProvider.js +114 -0
- package/out/parser.js +839 -0
- package/out/pathAccess.js +28 -0
- package/out/postmanImportPanel.js +732 -0
- package/out/postmanImportPlanner.js +1155 -0
- package/out/postmanImportSidebarView.js +532 -0
- package/out/quotedString.js +35 -0
- package/out/requestPreparation.js +179 -0
- package/out/requestValidation.js +146 -0
- package/out/responsePanel.js +7754 -0
- package/out/schemaGenerator.js +562 -0
- package/out/scriptRunner.js +419 -0
- package/out/secrets/cliSecrets.js +415 -0
- package/out/secrets/crypto.js +105 -0
- package/out/secrets/envFileSecrets.js +177 -0
- package/out/secrets/keyStore.js +259 -0
- package/out/sequenceDeclaration.js +15 -0
- package/out/sequenceRunner.js +3590 -0
- package/out/sqlAdapterRunner.js +122 -0
- package/out/sqlBuiltInAdapters.js +604 -0
- package/out/sqlConfig.js +184 -0
- package/out/starterCatalog.js +554 -0
- package/out/stringUtils.js +25 -0
- package/out/swaggerBodyIntellisenseCache.js +114 -0
- package/out/swaggerParser.js +464 -0
- package/out/testProvider.js +767 -0
- package/out/theoryCaseLoader.js +113 -0
- package/out/validationCache.js +211 -0
- package/package.json +6 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared resolver used by every Norn file type's inlay hint provider.
|
|
4
|
+
*
|
|
5
|
+
* The provider's job is to assemble the right scope (just env vars for .nornapi,
|
|
6
|
+
* env + file-locals + sequence-locals for .norn, etc.) and hand the resulting
|
|
7
|
+
* `variables` map to `resolveInlayValueLabel` for each `{{...}}` token it finds.
|
|
8
|
+
*
|
|
9
|
+
* Unresolvable references — response refs (`{{$1.body.id}}`), unknown names, cycles —
|
|
10
|
+
* return `undefined`. The provider then renders nothing inline (no `(runtime)` label).
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.parseInlayReference = parseInlayReference;
|
|
14
|
+
exports.resolveInlayValueLabel = resolveInlayValueLabel;
|
|
15
|
+
const environmentTemplates_1 = require("./environmentTemplates");
|
|
16
|
+
const ENV_TOKEN_REGEX = /^\$env\.([a-zA-Z_][a-zA-Z0-9_]*)$/;
|
|
17
|
+
const PLAIN_TOKEN_REGEX = /^([a-zA-Z_][a-zA-Z0-9_]*)(?:\.[a-zA-Z_][a-zA-Z0-9_-]*|\[\d+\])*$/;
|
|
18
|
+
const RESPONSE_TOKEN_REGEX = /^\$\d+/;
|
|
19
|
+
/**
|
|
20
|
+
* Parses a token like `name`, `$env.name`, `name.path.to.value`, or `$1.body.id`.
|
|
21
|
+
* Returns the bare root variable name (the part we can look up at edit time)
|
|
22
|
+
* or `undefined` for response refs / malformed tokens we can never resolve statically.
|
|
23
|
+
*/
|
|
24
|
+
function parseInlayReference(rawReference) {
|
|
25
|
+
const trimmed = rawReference.trim();
|
|
26
|
+
if (RESPONSE_TOKEN_REGEX.test(trimmed)) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const envMatch = trimmed.match(ENV_TOKEN_REGEX);
|
|
30
|
+
if (envMatch) {
|
|
31
|
+
return { name: envMatch[1], envOnly: true };
|
|
32
|
+
}
|
|
33
|
+
const plainMatch = trimmed.match(PLAIN_TOKEN_REGEX);
|
|
34
|
+
if (plainMatch) {
|
|
35
|
+
return { name: plainMatch[1], envOnly: false };
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const MAX_INLAY_LENGTH = 80;
|
|
40
|
+
function truncateInlayValue(value) {
|
|
41
|
+
return value.length > MAX_INLAY_LENGTH ? `${value.slice(0, MAX_INLAY_LENGTH - 3)}...` : value;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Looks up a reference in `variables` and returns the label to render inline,
|
|
45
|
+
* or `undefined` to skip (unknown name, response ref, cycle, etc).
|
|
46
|
+
*
|
|
47
|
+
* `variables` should already be the fully-merged scope the caller wants resolved
|
|
48
|
+
* against — innermost wins. For example, in a `.norn` sequence the caller merges
|
|
49
|
+
* `env + file-locals + sequence-locals` before calling this.
|
|
50
|
+
*/
|
|
51
|
+
function resolveInlayValueLabel(rawReference, variables, secretNames) {
|
|
52
|
+
const parsed = parseInlayReference(rawReference);
|
|
53
|
+
if (!parsed) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
if (!Object.prototype.hasOwnProperty.call(variables, parsed.name)) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
const resolved = (0, environmentTemplates_1.resolveEnvironmentTemplateValue)(parsed.name, variables, secretNames);
|
|
60
|
+
if (resolved.errors.length > 0) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
const isSecret = resolved.secret || secretNames.has(parsed.name);
|
|
64
|
+
return {
|
|
65
|
+
label: isSecret ? '(secret)' : `"${truncateInlayValue(resolved.value)}"`,
|
|
66
|
+
secret: isSecret,
|
|
67
|
+
value: resolved.value
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=inlayHintResolver.js.map
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.JsonVariableStore = void 0;
|
|
37
|
+
exports.isJsonCommand = isJsonCommand;
|
|
38
|
+
exports.parseJsonCommand = parseJsonCommand;
|
|
39
|
+
exports.readJsonFile = readJsonFile;
|
|
40
|
+
exports.getNestedValue = getNestedValue;
|
|
41
|
+
exports.setNestedValue = setNestedValue;
|
|
42
|
+
exports.isPropertyAssignment = isPropertyAssignment;
|
|
43
|
+
exports.parsePropertyAssignment = parsePropertyAssignment;
|
|
44
|
+
exports.valueToString = valueToString;
|
|
45
|
+
exports.substituteVariablesWithJson = substituteVariablesWithJson;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const pathAccess_1 = require("./pathAccess");
|
|
49
|
+
/**
|
|
50
|
+
* Checks if a line is a JSON file command.
|
|
51
|
+
* Formats:
|
|
52
|
+
* var name = run readJson ./path/to/file.json
|
|
53
|
+
* var name = run readJson "/path with spaces/file.json"
|
|
54
|
+
*/
|
|
55
|
+
function isJsonCommand(line) {
|
|
56
|
+
const trimmed = line.trim();
|
|
57
|
+
return /^var\s+[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*run\s+readJson\s+/i.test(trimmed);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parses a JSON file command line.
|
|
61
|
+
* Format: var <name> = run readJson <path>
|
|
62
|
+
*/
|
|
63
|
+
function parseJsonCommand(line) {
|
|
64
|
+
const trimmed = line.trim();
|
|
65
|
+
// Match: var varName = run readJson path
|
|
66
|
+
const match = trimmed.match(/^var\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*run\s+readJson\s+(.+)$/i);
|
|
67
|
+
if (!match) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
let filePath = match[2].trim();
|
|
71
|
+
// Remove quotes if present
|
|
72
|
+
if ((filePath.startsWith('"') && filePath.endsWith('"')) ||
|
|
73
|
+
(filePath.startsWith("'") && filePath.endsWith("'"))) {
|
|
74
|
+
filePath = filePath.slice(1, -1);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
varName: match[1],
|
|
78
|
+
filePath
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Reads and parses a JSON file.
|
|
83
|
+
*/
|
|
84
|
+
function readJsonFile(filePath, workingDir) {
|
|
85
|
+
// Resolve relative paths
|
|
86
|
+
const resolvedPath = path.isAbsolute(filePath)
|
|
87
|
+
? filePath
|
|
88
|
+
: path.resolve(workingDir || process.cwd(), filePath);
|
|
89
|
+
try {
|
|
90
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: `File not found: ${resolvedPath}`,
|
|
94
|
+
filePath: resolvedPath
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
98
|
+
const data = JSON.parse(content);
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
data,
|
|
102
|
+
filePath: resolvedPath
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
error: `Failed to read/parse JSON file: ${message}`,
|
|
110
|
+
filePath: resolvedPath
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Gets a value from an object using a dot-notation path.
|
|
116
|
+
* Supports array indexing with brackets: obj.array[0].property
|
|
117
|
+
*
|
|
118
|
+
* @param obj The source object
|
|
119
|
+
* @param path The dot-notation path (e.g., "user.addresses[0].city")
|
|
120
|
+
* @returns The value at the path, or undefined if not found
|
|
121
|
+
*/
|
|
122
|
+
function getNestedValue(obj, path) {
|
|
123
|
+
return (0, pathAccess_1.getNestedPathValue)(obj, path);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Sets a value in an object using a dot-notation path.
|
|
127
|
+
* Creates intermediate objects/arrays as needed.
|
|
128
|
+
* Supports array indexing with brackets: obj.array[0].property
|
|
129
|
+
*
|
|
130
|
+
* @param obj The source object to modify
|
|
131
|
+
* @param path The dot-notation path (e.g., "user.addresses[0].city")
|
|
132
|
+
* @param value The value to set
|
|
133
|
+
* @returns true if successful, false if the path is invalid
|
|
134
|
+
*/
|
|
135
|
+
function setNestedValue(obj, path, value) {
|
|
136
|
+
if (!path || obj === null || obj === undefined || typeof obj !== 'object') {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
// Convert [0] to .0 and split by dots
|
|
140
|
+
const parts = path.replace(/\[(\d+)\]/g, '.$1').split('.').filter(p => p !== '');
|
|
141
|
+
if (parts.length === 0) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
let current = obj;
|
|
145
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
146
|
+
const part = parts[i];
|
|
147
|
+
const nextPart = parts[i + 1];
|
|
148
|
+
if (current[part] === undefined || current[part] === null) {
|
|
149
|
+
// Create intermediate object or array based on next part
|
|
150
|
+
current[part] = /^\d+$/.test(nextPart) ? [] : {};
|
|
151
|
+
}
|
|
152
|
+
current = current[part];
|
|
153
|
+
if (typeof current !== 'object') {
|
|
154
|
+
return false; // Can't navigate further
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const lastPart = parts[parts.length - 1];
|
|
158
|
+
current[lastPart] = value;
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Checks if a line is a property assignment command.
|
|
163
|
+
* Format: varName.property.path = value
|
|
164
|
+
* varName[0].property = value
|
|
165
|
+
*/
|
|
166
|
+
function isPropertyAssignment(line) {
|
|
167
|
+
const trimmed = line.trim();
|
|
168
|
+
// Match: identifier followed by . or [ then = something
|
|
169
|
+
// But NOT starting with 'var ' or other keywords
|
|
170
|
+
return /^[a-zA-Z_][a-zA-Z0-9_]*[\.\[]/.test(trimmed) &&
|
|
171
|
+
/=/.test(trimmed) &&
|
|
172
|
+
!trimmed.startsWith('var ') &&
|
|
173
|
+
!trimmed.startsWith('run ') &&
|
|
174
|
+
!trimmed.startsWith('print ') &&
|
|
175
|
+
!trimmed.startsWith('assert ') &&
|
|
176
|
+
!trimmed.startsWith('sequence ');
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Parses a property assignment command.
|
|
180
|
+
* Format: varName.path.to.property = value
|
|
181
|
+
* Returns the variable name, the property path, and the value.
|
|
182
|
+
*/
|
|
183
|
+
function parsePropertyAssignment(line) {
|
|
184
|
+
const trimmed = line.trim();
|
|
185
|
+
// Match: varName.path = value OR varName[0].path = value
|
|
186
|
+
const match = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])+)\s*=\s*(.+)$/);
|
|
187
|
+
if (!match) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const varName = match[1];
|
|
191
|
+
let propertyPath = match[2];
|
|
192
|
+
const value = match[3].trim();
|
|
193
|
+
// Remove leading dot if present
|
|
194
|
+
if (propertyPath.startsWith('.')) {
|
|
195
|
+
propertyPath = propertyPath.substring(1);
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
varName,
|
|
199
|
+
propertyPath,
|
|
200
|
+
value
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Converts a value to a string for use in variable substitution.
|
|
205
|
+
*/
|
|
206
|
+
function valueToString(value) {
|
|
207
|
+
if (value === null) {
|
|
208
|
+
return 'null';
|
|
209
|
+
}
|
|
210
|
+
if (value === undefined) {
|
|
211
|
+
return '';
|
|
212
|
+
}
|
|
213
|
+
if (typeof value === 'object') {
|
|
214
|
+
return JSON.stringify(value);
|
|
215
|
+
}
|
|
216
|
+
return String(value);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Storage for JSON objects loaded during sequence execution.
|
|
220
|
+
* Maps variable names to their parsed JSON data.
|
|
221
|
+
*/
|
|
222
|
+
class JsonVariableStore {
|
|
223
|
+
jsonObjects = new Map();
|
|
224
|
+
/**
|
|
225
|
+
* Stores a JSON object under the given variable name.
|
|
226
|
+
*/
|
|
227
|
+
set(varName, data) {
|
|
228
|
+
this.jsonObjects.set(varName, data);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Gets the raw JSON object for a variable.
|
|
232
|
+
*/
|
|
233
|
+
get(varName) {
|
|
234
|
+
return this.jsonObjects.get(varName);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Checks if a variable is a JSON object.
|
|
238
|
+
*/
|
|
239
|
+
has(varName) {
|
|
240
|
+
return this.jsonObjects.has(varName);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Gets a nested value from a JSON variable.
|
|
244
|
+
* @param varName The variable name
|
|
245
|
+
* @param path Optional dot-notation path within the object
|
|
246
|
+
*/
|
|
247
|
+
getValue(varName, path) {
|
|
248
|
+
const obj = this.jsonObjects.get(varName);
|
|
249
|
+
if (obj === undefined) {
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
if (!path) {
|
|
253
|
+
return obj;
|
|
254
|
+
}
|
|
255
|
+
return getNestedValue(obj, path);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Clears all stored JSON objects.
|
|
259
|
+
*/
|
|
260
|
+
clear() {
|
|
261
|
+
this.jsonObjects.clear();
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Gets all variable names that have JSON objects stored.
|
|
265
|
+
*/
|
|
266
|
+
getVariableNames() {
|
|
267
|
+
return Array.from(this.jsonObjects.keys());
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
exports.JsonVariableStore = JsonVariableStore;
|
|
271
|
+
/**
|
|
272
|
+
* Substitutes variables in text, with support for JSON object property access.
|
|
273
|
+
* Handles both simple variables {{varName}} and nested access {{varName.property.path}}
|
|
274
|
+
*
|
|
275
|
+
* @param text The text containing variable references
|
|
276
|
+
* @param variables Simple string variables (name -> value)
|
|
277
|
+
* @param jsonStore Optional JSON variable store for complex objects
|
|
278
|
+
*/
|
|
279
|
+
function substituteVariablesWithJson(text, variables, jsonStore) {
|
|
280
|
+
// Match {{varName}}, {{$env.name}}, or {{varName.path.to.property}}
|
|
281
|
+
return text.replace(/\{\{(\$env|[a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])*)\}\}/g, (match, varName, pathPart) => {
|
|
282
|
+
// First check if it's a JSON object with a path
|
|
283
|
+
if (jsonStore && jsonStore.has(varName)) {
|
|
284
|
+
const path = pathPart ? pathPart.replace(/^\./, '') : '';
|
|
285
|
+
const value = jsonStore.getValue(varName, path);
|
|
286
|
+
return valueToString(value);
|
|
287
|
+
}
|
|
288
|
+
if (pathPart && varName in variables && typeof variables[varName] === 'object' && variables[varName] !== null) {
|
|
289
|
+
const path = pathPart.replace(/^\./, '');
|
|
290
|
+
const value = getNestedValue(variables[varName], path);
|
|
291
|
+
return value === undefined && varName === '$env' ? match : valueToString(value);
|
|
292
|
+
}
|
|
293
|
+
// Check if there's a path and we have a simple variable that might be JSON
|
|
294
|
+
if (pathPart && varName in variables && typeof variables[varName] === 'string') {
|
|
295
|
+
// Try to parse the variable value as JSON
|
|
296
|
+
try {
|
|
297
|
+
const parsed = JSON.parse(variables[varName]);
|
|
298
|
+
const path = pathPart.replace(/^\./, '');
|
|
299
|
+
const value = getNestedValue(parsed, path);
|
|
300
|
+
if (value === undefined && varName === '$env') {
|
|
301
|
+
return match;
|
|
302
|
+
}
|
|
303
|
+
return valueToString(value);
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
if (varName === '$env') {
|
|
307
|
+
return match;
|
|
308
|
+
}
|
|
309
|
+
// Not JSON, fall through to simple substitution
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Simple variable substitution
|
|
313
|
+
if (varName in variables) {
|
|
314
|
+
if (varName === '$env') {
|
|
315
|
+
return match;
|
|
316
|
+
}
|
|
317
|
+
return variables[varName];
|
|
318
|
+
}
|
|
319
|
+
// Variable not found, return original
|
|
320
|
+
return match;
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=jsonFileReader.js.map
|
package/out/mcpClient.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.McpSessionManager = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
40
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
41
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
42
|
+
const mcpConfig_1 = require("./mcpConfig");
|
|
43
|
+
const mcpToolIntellisenseCache_1 = require("./mcpToolIntellisenseCache");
|
|
44
|
+
const schemaGenerator_1 = require("./schemaGenerator");
|
|
45
|
+
class NornJsonSchemaValidator {
|
|
46
|
+
getValidator(schema) {
|
|
47
|
+
return (input) => {
|
|
48
|
+
const result = (0, schemaGenerator_1.validateAgainstSchemaObjectDetailed)(input, schema);
|
|
49
|
+
if (result.valid) {
|
|
50
|
+
return {
|
|
51
|
+
valid: true,
|
|
52
|
+
data: input,
|
|
53
|
+
errorMessage: undefined
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
data: undefined,
|
|
59
|
+
errorMessage: result.errorStrings?.join('; ') || 'Schema validation failed'
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function getNornClientVersion() {
|
|
65
|
+
try {
|
|
66
|
+
const packageJsonPath = path.resolve(__dirname, '..', 'package.json');
|
|
67
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
68
|
+
return typeof packageJson.version === 'string' ? packageJson.version : '0.0.0';
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return '0.0.0';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const NORN_CLIENT_VERSION = getNornClientVersion();
|
|
75
|
+
function createClient() {
|
|
76
|
+
return new index_js_1.Client({ name: 'norn', version: NORN_CLIENT_VERSION }, {
|
|
77
|
+
capabilities: {},
|
|
78
|
+
jsonSchemaValidator: new NornJsonSchemaValidator()
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function extractTextContent(content) {
|
|
82
|
+
return content
|
|
83
|
+
.filter(block => block.type === 'text' && typeof block.text === 'string')
|
|
84
|
+
.map(block => block.text)
|
|
85
|
+
.join('\n');
|
|
86
|
+
}
|
|
87
|
+
function normalizeStructuredContent(value) {
|
|
88
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
class McpSessionManager {
|
|
94
|
+
sessions = new Map();
|
|
95
|
+
resolveSessionTarget(startPath, alias, envVariables) {
|
|
96
|
+
const { filePath, server } = (0, mcpConfig_1.resolveMcpServer)(startPath, alias, envVariables);
|
|
97
|
+
return {
|
|
98
|
+
sessionKey: `${filePath}::${alias.toLowerCase()}`,
|
|
99
|
+
server
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async createSession(server, sessionKey) {
|
|
103
|
+
const client = createClient();
|
|
104
|
+
if (server.transport === 'stdio') {
|
|
105
|
+
const [command, ...args] = server.command;
|
|
106
|
+
const transport = new stdio_js_1.StdioClientTransport({
|
|
107
|
+
command,
|
|
108
|
+
args,
|
|
109
|
+
cwd: server.cwd
|
|
110
|
+
});
|
|
111
|
+
await client.connect(transport);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const transport = new streamableHttp_js_1.StreamableHTTPClientTransport(new URL(server.url), {
|
|
115
|
+
requestInit: server.headers ? { headers: server.headers } : undefined
|
|
116
|
+
});
|
|
117
|
+
await client.connect(transport, server.timeoutMs ? { timeout: server.timeoutMs } : undefined);
|
|
118
|
+
}
|
|
119
|
+
const session = {
|
|
120
|
+
client,
|
|
121
|
+
config: server,
|
|
122
|
+
toolsByName: new Map()
|
|
123
|
+
};
|
|
124
|
+
this.sessions.set(sessionKey, session);
|
|
125
|
+
return session;
|
|
126
|
+
}
|
|
127
|
+
async getSession(startPath, alias, envVariables) {
|
|
128
|
+
const target = this.resolveSessionTarget(startPath, alias, envVariables);
|
|
129
|
+
const existing = this.sessions.get(target.sessionKey);
|
|
130
|
+
if (existing) {
|
|
131
|
+
return existing;
|
|
132
|
+
}
|
|
133
|
+
return this.createSession(target.server, target.sessionKey);
|
|
134
|
+
}
|
|
135
|
+
async listTools(startPath, alias, envVariables) {
|
|
136
|
+
const session = await this.getSession(startPath, alias, envVariables);
|
|
137
|
+
const tools = [];
|
|
138
|
+
let cursor;
|
|
139
|
+
do {
|
|
140
|
+
const response = await session.client.listTools(cursor ? { cursor } : undefined, session.config.transport === 'http' && session.config.timeoutMs
|
|
141
|
+
? { timeout: session.config.timeoutMs }
|
|
142
|
+
: undefined);
|
|
143
|
+
for (const tool of response.tools) {
|
|
144
|
+
tools.push(tool);
|
|
145
|
+
session.toolsByName.set(tool.name.toLowerCase(), tool);
|
|
146
|
+
}
|
|
147
|
+
cursor = response.nextCursor;
|
|
148
|
+
} while (cursor);
|
|
149
|
+
const { filePath } = (0, mcpConfig_1.resolveMcpServer)(startPath, alias, envVariables);
|
|
150
|
+
(0, mcpToolIntellisenseCache_1.saveMcpToolsForAlias)(filePath, alias, tools);
|
|
151
|
+
return { tools };
|
|
152
|
+
}
|
|
153
|
+
async getToolDefinition(startPath, alias, toolName, envVariables) {
|
|
154
|
+
const session = await this.getSession(startPath, alias, envVariables);
|
|
155
|
+
const normalizedToolName = toolName.toLowerCase();
|
|
156
|
+
const cached = session.toolsByName.get(normalizedToolName);
|
|
157
|
+
if (cached) {
|
|
158
|
+
return cached;
|
|
159
|
+
}
|
|
160
|
+
const listed = await this.listTools(startPath, alias, envVariables);
|
|
161
|
+
const resolved = listed.tools.find(tool => tool.name.toLowerCase() === normalizedToolName);
|
|
162
|
+
if (!resolved) {
|
|
163
|
+
throw new Error(`Tool '${toolName}' was not found on MCP server '${alias}'.`);
|
|
164
|
+
}
|
|
165
|
+
return resolved;
|
|
166
|
+
}
|
|
167
|
+
async callTool(startPath, alias, toolName, args, envVariables) {
|
|
168
|
+
const session = await this.getSession(startPath, alias, envVariables);
|
|
169
|
+
await this.getToolDefinition(startPath, alias, toolName, envVariables);
|
|
170
|
+
const result = await session.client.callTool({
|
|
171
|
+
name: toolName,
|
|
172
|
+
arguments: args
|
|
173
|
+
}, undefined, session.config.transport === 'http' && session.config.timeoutMs
|
|
174
|
+
? { timeout: session.config.timeoutMs }
|
|
175
|
+
: undefined);
|
|
176
|
+
const content = (result.content || []);
|
|
177
|
+
return {
|
|
178
|
+
content,
|
|
179
|
+
structuredContent: normalizeStructuredContent(result.structuredContent),
|
|
180
|
+
isError: Boolean(result.isError),
|
|
181
|
+
text: extractTextContent(content),
|
|
182
|
+
server: alias,
|
|
183
|
+
tool: toolName
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
async closeAll() {
|
|
187
|
+
const sessions = Array.from(this.sessions.values());
|
|
188
|
+
this.sessions.clear();
|
|
189
|
+
await Promise.allSettled(sessions.map(session => session.client.close()));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.McpSessionManager = McpSessionManager;
|
|
193
|
+
//# sourceMappingURL=mcpClient.js.map
|