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.
Files changed (92) hide show
  1. package/.claude/skills/norn-social-campaign/SKILL.md +70 -0
  2. package/CHANGELOG.md +6 -0
  3. package/demos/nornenv-region-refactor/README.md +64 -0
  4. package/dist/cli.js +360 -1
  5. package/out/apiResponseIntellisenseCache.js +394 -0
  6. package/out/assertionRunner.js +567 -0
  7. package/out/cacheDir.js +136 -0
  8. package/out/chatParticipant.js +763 -0
  9. package/out/cli/colors.js +127 -0
  10. package/out/cli/formatters/assertion.js +102 -0
  11. package/out/cli/formatters/index.js +23 -0
  12. package/out/cli/formatters/response.js +106 -0
  13. package/out/cli/formatters/summary.js +246 -0
  14. package/out/cli/redaction.js +237 -0
  15. package/out/cli/reporters/html.js +689 -0
  16. package/out/cli/reporters/index.js +22 -0
  17. package/out/cli/reporters/junit.js +226 -0
  18. package/out/codeLensProvider.js +351 -0
  19. package/out/compareContentProvider.js +85 -0
  20. package/out/completionProvider.js +3739 -0
  21. package/out/contractAssertionSummary.js +225 -0
  22. package/out/contractDecorationProvider.js +243 -0
  23. package/out/coverageCalculator.js +879 -0
  24. package/out/coveragePanel.js +597 -0
  25. package/out/debug/breakpointResolver.js +84 -0
  26. package/out/debug/breakpoints.js +52 -0
  27. package/out/debug/nornDebugAdapter.js +166 -0
  28. package/out/debug/nornDebugSession.js +613 -0
  29. package/out/debug/sequenceLocationIndex.js +77 -0
  30. package/out/debug/types.js +3 -0
  31. package/out/deepClone.js +21 -0
  32. package/out/diagnosticProvider.js +2554 -0
  33. package/out/environmentParser.js +736 -0
  34. package/out/environmentProvider.js +544 -0
  35. package/out/environmentTemplates.js +146 -0
  36. package/out/errors/formatError.js +113 -0
  37. package/out/errors/nornError.js +29 -0
  38. package/out/formUrlEncoded.js +89 -0
  39. package/out/httpClient.js +348 -0
  40. package/out/httpRuntimeOptions.js +16 -0
  41. package/out/importErrors.js +31 -0
  42. package/out/inlayHintResolver.js +70 -0
  43. package/out/jsonFileReader.js +323 -0
  44. package/out/mcpClient.js +193 -0
  45. package/out/mcpConfig.js +184 -0
  46. package/out/mcpToolIntellisenseCache.js +96 -0
  47. package/out/mcpToolSchema.js +50 -0
  48. package/out/nornConfig.js +132 -0
  49. package/out/nornHoverProvider.js +124 -0
  50. package/out/nornInlayHintsProvider.js +191 -0
  51. package/out/nornPrompt.js +755 -0
  52. package/out/nornSqlParser.js +286 -0
  53. package/out/nornapiHoverProvider.js +135 -0
  54. package/out/nornapiInlayHintsProvider.js +94 -0
  55. package/out/nornapiParser.js +324 -0
  56. package/out/nornenvCodeActionProvider.js +101 -0
  57. package/out/nornenvDecorationProvider.js +239 -0
  58. package/out/nornenvFoldingProvider.js +63 -0
  59. package/out/nornenvHoverProvider.js +114 -0
  60. package/out/nornenvInlayHintsProvider.js +99 -0
  61. package/out/nornenvLanguageModel.js +187 -0
  62. package/out/nornenvRegionRefactor.js +267 -0
  63. package/out/nornsqlHoverProvider.js +95 -0
  64. package/out/nornsqlInlayHintsProvider.js +114 -0
  65. package/out/parser.js +839 -0
  66. package/out/pathAccess.js +28 -0
  67. package/out/postmanImportPanel.js +732 -0
  68. package/out/postmanImportPlanner.js +1155 -0
  69. package/out/postmanImportSidebarView.js +532 -0
  70. package/out/quotedString.js +35 -0
  71. package/out/requestPreparation.js +179 -0
  72. package/out/requestValidation.js +146 -0
  73. package/out/responsePanel.js +7754 -0
  74. package/out/schemaGenerator.js +562 -0
  75. package/out/scriptRunner.js +419 -0
  76. package/out/secrets/cliSecrets.js +415 -0
  77. package/out/secrets/crypto.js +105 -0
  78. package/out/secrets/envFileSecrets.js +177 -0
  79. package/out/secrets/keyStore.js +259 -0
  80. package/out/sequenceDeclaration.js +15 -0
  81. package/out/sequenceRunner.js +3590 -0
  82. package/out/sqlAdapterRunner.js +122 -0
  83. package/out/sqlBuiltInAdapters.js +604 -0
  84. package/out/sqlConfig.js +184 -0
  85. package/out/starterCatalog.js +554 -0
  86. package/out/stringUtils.js +25 -0
  87. package/out/swaggerBodyIntellisenseCache.js +114 -0
  88. package/out/swaggerParser.js +464 -0
  89. package/out/testProvider.js +767 -0
  90. package/out/theoryCaseLoader.js +113 -0
  91. package/out/validationCache.js +211 -0
  92. 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
@@ -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