@enactprotocol/shared 1.2.13 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +44 -0
  2. package/dist/config.d.ts +164 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +386 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/constants.d.ts +15 -5
  7. package/dist/constants.d.ts.map +1 -0
  8. package/dist/constants.js +24 -8
  9. package/dist/constants.js.map +1 -0
  10. package/dist/execution/command.d.ts +102 -0
  11. package/dist/execution/command.d.ts.map +1 -0
  12. package/dist/execution/command.js +262 -0
  13. package/dist/execution/command.js.map +1 -0
  14. package/dist/execution/index.d.ts +12 -0
  15. package/dist/execution/index.d.ts.map +1 -0
  16. package/dist/execution/index.js +17 -0
  17. package/dist/execution/index.js.map +1 -0
  18. package/dist/execution/runtime.d.ts +82 -0
  19. package/dist/execution/runtime.d.ts.map +1 -0
  20. package/dist/execution/runtime.js +273 -0
  21. package/dist/execution/runtime.js.map +1 -0
  22. package/dist/execution/types.d.ts +306 -0
  23. package/dist/execution/types.d.ts.map +1 -0
  24. package/dist/execution/types.js +14 -0
  25. package/dist/execution/types.js.map +1 -0
  26. package/dist/execution/validation.d.ts +43 -0
  27. package/dist/execution/validation.d.ts.map +1 -0
  28. package/dist/execution/validation.js +430 -0
  29. package/dist/execution/validation.js.map +1 -0
  30. package/dist/index.d.ts +21 -21
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +49 -25
  33. package/dist/index.js.map +1 -0
  34. package/dist/manifest/index.d.ts +7 -0
  35. package/dist/manifest/index.d.ts.map +1 -0
  36. package/dist/manifest/index.js +10 -0
  37. package/dist/manifest/index.js.map +1 -0
  38. package/dist/manifest/loader.d.ts +76 -0
  39. package/dist/manifest/loader.d.ts.map +1 -0
  40. package/dist/manifest/loader.js +146 -0
  41. package/dist/manifest/loader.js.map +1 -0
  42. package/dist/manifest/parser.d.ts +64 -0
  43. package/dist/manifest/parser.d.ts.map +1 -0
  44. package/dist/manifest/parser.js +135 -0
  45. package/dist/manifest/parser.js.map +1 -0
  46. package/dist/manifest/validator.d.ts +95 -0
  47. package/dist/manifest/validator.d.ts.map +1 -0
  48. package/dist/manifest/validator.js +258 -0
  49. package/dist/manifest/validator.js.map +1 -0
  50. package/dist/paths.d.ts +57 -0
  51. package/dist/paths.d.ts.map +1 -0
  52. package/dist/paths.js +93 -0
  53. package/dist/paths.js.map +1 -0
  54. package/dist/registry.d.ts +73 -0
  55. package/dist/registry.d.ts.map +1 -0
  56. package/dist/registry.js +147 -0
  57. package/dist/registry.js.map +1 -0
  58. package/dist/resolver.d.ts +89 -0
  59. package/dist/resolver.d.ts.map +1 -0
  60. package/dist/resolver.js +282 -0
  61. package/dist/resolver.js.map +1 -0
  62. package/dist/types/index.d.ts +6 -0
  63. package/dist/types/index.d.ts.map +1 -0
  64. package/dist/types/index.js +5 -0
  65. package/dist/types/index.js.map +1 -0
  66. package/dist/types/manifest.d.ts +201 -0
  67. package/dist/types/manifest.d.ts.map +1 -0
  68. package/dist/types/manifest.js +13 -0
  69. package/dist/types/manifest.js.map +1 -0
  70. package/dist/types.d.ts +5 -132
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/types.js +5 -3
  73. package/dist/types.js.map +1 -0
  74. package/dist/utils/fs.d.ts +105 -0
  75. package/dist/utils/fs.d.ts.map +1 -0
  76. package/dist/utils/fs.js +233 -0
  77. package/dist/utils/fs.js.map +1 -0
  78. package/dist/utils/logger.d.ts +102 -25
  79. package/dist/utils/logger.d.ts.map +1 -0
  80. package/dist/utils/logger.js +214 -57
  81. package/dist/utils/logger.js.map +1 -0
  82. package/dist/utils/version.d.ts +60 -2
  83. package/dist/utils/version.d.ts.map +1 -0
  84. package/dist/utils/version.js +255 -31
  85. package/dist/utils/version.js.map +1 -0
  86. package/package.json +16 -58
  87. package/src/config.ts +510 -0
  88. package/src/constants.ts +36 -0
  89. package/src/execution/command.ts +314 -0
  90. package/src/execution/index.ts +73 -0
  91. package/src/execution/runtime.ts +308 -0
  92. package/src/execution/types.ts +379 -0
  93. package/src/execution/validation.ts +508 -0
  94. package/src/index.ts +238 -30
  95. package/src/manifest/index.ts +36 -0
  96. package/src/manifest/loader.ts +187 -0
  97. package/src/manifest/parser.ts +173 -0
  98. package/src/manifest/validator.ts +309 -0
  99. package/src/paths.ts +108 -0
  100. package/src/registry.ts +219 -0
  101. package/src/resolver.ts +345 -0
  102. package/src/types/index.ts +30 -0
  103. package/src/types/manifest.ts +255 -0
  104. package/src/types.ts +5 -188
  105. package/src/utils/fs.ts +281 -0
  106. package/src/utils/logger.ts +270 -59
  107. package/src/utils/version.ts +304 -36
  108. package/tests/config.test.ts +515 -0
  109. package/tests/execution/command.test.ts +317 -0
  110. package/tests/execution/validation.test.ts +384 -0
  111. package/tests/fixtures/invalid-tool.yaml +4 -0
  112. package/tests/fixtures/valid-tool.md +62 -0
  113. package/tests/fixtures/valid-tool.yaml +40 -0
  114. package/tests/index.test.ts +8 -0
  115. package/tests/manifest/loader.test.ts +291 -0
  116. package/tests/manifest/parser.test.ts +345 -0
  117. package/tests/manifest/validator.test.ts +394 -0
  118. package/tests/manifest-types.test.ts +358 -0
  119. package/tests/paths.test.ts +153 -0
  120. package/tests/registry.test.ts +231 -0
  121. package/tests/resolver.test.ts +272 -0
  122. package/tests/utils/fs.test.ts +388 -0
  123. package/tests/utils/logger.test.ts +480 -0
  124. package/tests/utils/version.test.ts +390 -0
  125. package/tsconfig.json +12 -0
  126. package/dist/LocalToolResolver.d.ts +0 -84
  127. package/dist/LocalToolResolver.js +0 -353
  128. package/dist/api/enact-api.d.ts +0 -130
  129. package/dist/api/enact-api.js +0 -428
  130. package/dist/api/index.d.ts +0 -2
  131. package/dist/api/index.js +0 -2
  132. package/dist/api/types.d.ts +0 -103
  133. package/dist/api/types.js +0 -1
  134. package/dist/core/DaggerExecutionProvider.d.ts +0 -169
  135. package/dist/core/DaggerExecutionProvider.js +0 -1029
  136. package/dist/core/DirectExecutionProvider.d.ts +0 -23
  137. package/dist/core/DirectExecutionProvider.js +0 -406
  138. package/dist/core/EnactCore.d.ts +0 -162
  139. package/dist/core/EnactCore.js +0 -597
  140. package/dist/core/NativeExecutionProvider.d.ts +0 -9
  141. package/dist/core/NativeExecutionProvider.js +0 -16
  142. package/dist/core/index.d.ts +0 -3
  143. package/dist/core/index.js +0 -3
  144. package/dist/exec/index.d.ts +0 -3
  145. package/dist/exec/index.js +0 -3
  146. package/dist/exec/logger.d.ts +0 -11
  147. package/dist/exec/logger.js +0 -57
  148. package/dist/exec/validate.d.ts +0 -5
  149. package/dist/exec/validate.js +0 -167
  150. package/dist/lib/enact-direct.d.ts +0 -150
  151. package/dist/lib/enact-direct.js +0 -159
  152. package/dist/lib/index.d.ts +0 -1
  153. package/dist/lib/index.js +0 -1
  154. package/dist/security/index.d.ts +0 -3
  155. package/dist/security/index.js +0 -3
  156. package/dist/security/security.d.ts +0 -23
  157. package/dist/security/security.js +0 -137
  158. package/dist/security/sign.d.ts +0 -103
  159. package/dist/security/sign.js +0 -666
  160. package/dist/security/verification-enforcer.d.ts +0 -53
  161. package/dist/security/verification-enforcer.js +0 -204
  162. package/dist/services/McpCoreService.d.ts +0 -98
  163. package/dist/services/McpCoreService.js +0 -124
  164. package/dist/services/index.d.ts +0 -1
  165. package/dist/services/index.js +0 -1
  166. package/dist/utils/config.d.ts +0 -111
  167. package/dist/utils/config.js +0 -342
  168. package/dist/utils/env-loader.d.ts +0 -54
  169. package/dist/utils/env-loader.js +0 -270
  170. package/dist/utils/help.d.ts +0 -36
  171. package/dist/utils/help.js +0 -248
  172. package/dist/utils/index.d.ts +0 -7
  173. package/dist/utils/index.js +0 -7
  174. package/dist/utils/silent-monitor.d.ts +0 -67
  175. package/dist/utils/silent-monitor.js +0 -242
  176. package/dist/utils/timeout.d.ts +0 -5
  177. package/dist/utils/timeout.js +0 -23
  178. package/dist/web/env-manager-server.d.ts +0 -29
  179. package/dist/web/env-manager-server.js +0 -367
  180. package/dist/web/index.d.ts +0 -1
  181. package/dist/web/index.js +0 -1
  182. package/src/LocalToolResolver.ts +0 -424
  183. package/src/api/enact-api.ts +0 -604
  184. package/src/api/index.ts +0 -2
  185. package/src/api/types.ts +0 -114
  186. package/src/core/DaggerExecutionProvider.ts +0 -1357
  187. package/src/core/DirectExecutionProvider.ts +0 -484
  188. package/src/core/EnactCore.ts +0 -847
  189. package/src/core/index.ts +0 -3
  190. package/src/exec/index.ts +0 -3
  191. package/src/exec/logger.ts +0 -63
  192. package/src/exec/validate.ts +0 -238
  193. package/src/lib/enact-direct.ts +0 -254
  194. package/src/lib/index.ts +0 -1
  195. package/src/services/McpCoreService.ts +0 -201
  196. package/src/services/index.ts +0 -1
  197. package/src/utils/config.ts +0 -438
  198. package/src/utils/env-loader.ts +0 -370
  199. package/src/utils/help.ts +0 -257
  200. package/src/utils/index.ts +0 -7
  201. package/src/utils/silent-monitor.ts +0 -328
  202. package/src/utils/timeout.ts +0 -26
  203. package/src/web/env-manager-server.ts +0 -465
  204. package/src/web/index.ts +0 -1
  205. package/src/web/static/app.js +0 -663
  206. package/src/web/static/index.html +0 -117
  207. package/src/web/static/style.css +0 -291
@@ -0,0 +1,508 @@
1
+ /**
2
+ * Input validation using JSON Schema
3
+ *
4
+ * Validates tool inputs against the manifest's inputSchema.
5
+ */
6
+
7
+ import type { JSONSchema7 } from "json-schema";
8
+ import type { InputValidationError, InputValidationResult } from "./types";
9
+
10
+ /**
11
+ * Validate inputs against a JSON Schema
12
+ *
13
+ * @param inputs - The inputs to validate
14
+ * @param schema - The JSON Schema to validate against
15
+ * @returns Validation result with errors and coerced values
16
+ */
17
+ export function validateInputs(
18
+ inputs: Record<string, unknown>,
19
+ schema: JSONSchema7 | undefined
20
+ ): InputValidationResult {
21
+ // If no schema, everything is valid
22
+ if (!schema) {
23
+ return { valid: true, errors: [], coercedValues: inputs };
24
+ }
25
+
26
+ const errors: InputValidationError[] = [];
27
+ const coercedValues: Record<string, unknown> = { ...inputs };
28
+
29
+ // Check schema type (should be object)
30
+ if (schema.type !== "object") {
31
+ return { valid: true, errors: [], coercedValues: inputs };
32
+ }
33
+
34
+ const properties = schema.properties || {};
35
+ const required = schema.required || [];
36
+
37
+ // Check required properties
38
+ for (const propName of required) {
39
+ if (inputs[propName] === undefined) {
40
+ errors.push({
41
+ path: `params.${propName}`,
42
+ message: `Missing required parameter: ${propName}`,
43
+ expected: "value",
44
+ });
45
+ }
46
+ }
47
+
48
+ // Validate each property
49
+ for (const [propName, propValue] of Object.entries(inputs)) {
50
+ const propSchema = properties[propName] as JSONSchema7 | undefined;
51
+
52
+ if (propSchema) {
53
+ const propErrors = validateProperty(propName, propValue, propSchema);
54
+ errors.push(...propErrors);
55
+
56
+ // Attempt type coercion
57
+ const coerced = coerceValue(propValue, propSchema);
58
+ if (coerced !== undefined) {
59
+ coercedValues[propName] = coerced;
60
+ }
61
+ }
62
+ }
63
+
64
+ // Check for additional properties if not allowed
65
+ if (schema.additionalProperties === false) {
66
+ for (const propName of Object.keys(inputs)) {
67
+ if (!properties[propName]) {
68
+ errors.push({
69
+ path: `params.${propName}`,
70
+ message: `Unknown parameter: ${propName}`,
71
+ });
72
+ }
73
+ }
74
+ }
75
+
76
+ return {
77
+ valid: errors.length === 0,
78
+ errors,
79
+ coercedValues,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Validate a single property against its schema
85
+ */
86
+ function validateProperty(
87
+ name: string,
88
+ value: unknown,
89
+ schema: JSONSchema7
90
+ ): InputValidationError[] {
91
+ const errors: InputValidationError[] = [];
92
+ const path = `params.${name}`;
93
+
94
+ // Type validation
95
+ if (schema.type) {
96
+ const typeValid = validateType(value, schema.type);
97
+ if (!typeValid) {
98
+ errors.push({
99
+ path,
100
+ message: `Invalid type for ${name}: expected ${schema.type}, got ${typeof value}`,
101
+ expected: String(schema.type),
102
+ actual: value,
103
+ });
104
+ return errors; // Skip further validation if type is wrong
105
+ }
106
+ }
107
+
108
+ // String validations
109
+ if (typeof value === "string") {
110
+ // minLength
111
+ if (schema.minLength !== undefined && value.length < schema.minLength) {
112
+ errors.push({
113
+ path,
114
+ message: `${name} must be at least ${schema.minLength} characters`,
115
+ expected: `minLength: ${schema.minLength}`,
116
+ actual: value,
117
+ });
118
+ }
119
+
120
+ // maxLength
121
+ if (schema.maxLength !== undefined && value.length > schema.maxLength) {
122
+ errors.push({
123
+ path,
124
+ message: `${name} must be at most ${schema.maxLength} characters`,
125
+ expected: `maxLength: ${schema.maxLength}`,
126
+ actual: value,
127
+ });
128
+ }
129
+
130
+ // pattern
131
+ if (schema.pattern) {
132
+ const regex = new RegExp(schema.pattern);
133
+ if (!regex.test(value)) {
134
+ errors.push({
135
+ path,
136
+ message: `${name} must match pattern: ${schema.pattern}`,
137
+ expected: `pattern: ${schema.pattern}`,
138
+ actual: value,
139
+ });
140
+ }
141
+ }
142
+
143
+ // format (basic support)
144
+ if (schema.format) {
145
+ const formatError = validateFormat(value, schema.format);
146
+ if (formatError) {
147
+ errors.push({
148
+ path,
149
+ message: `${name}: ${formatError}`,
150
+ expected: `format: ${schema.format}`,
151
+ actual: value,
152
+ });
153
+ }
154
+ }
155
+ }
156
+
157
+ // Number validations
158
+ if (typeof value === "number") {
159
+ // minimum
160
+ if (schema.minimum !== undefined && value < schema.minimum) {
161
+ errors.push({
162
+ path,
163
+ message: `${name} must be >= ${schema.minimum}`,
164
+ expected: `minimum: ${schema.minimum}`,
165
+ actual: value,
166
+ });
167
+ }
168
+
169
+ // maximum
170
+ if (schema.maximum !== undefined && value > schema.maximum) {
171
+ errors.push({
172
+ path,
173
+ message: `${name} must be <= ${schema.maximum}`,
174
+ expected: `maximum: ${schema.maximum}`,
175
+ actual: value,
176
+ });
177
+ }
178
+
179
+ // exclusiveMinimum
180
+ if (schema.exclusiveMinimum !== undefined && value <= schema.exclusiveMinimum) {
181
+ errors.push({
182
+ path,
183
+ message: `${name} must be > ${schema.exclusiveMinimum}`,
184
+ expected: `exclusiveMinimum: ${schema.exclusiveMinimum}`,
185
+ actual: value,
186
+ });
187
+ }
188
+
189
+ // exclusiveMaximum
190
+ if (schema.exclusiveMaximum !== undefined && value >= schema.exclusiveMaximum) {
191
+ errors.push({
192
+ path,
193
+ message: `${name} must be < ${schema.exclusiveMaximum}`,
194
+ expected: `exclusiveMaximum: ${schema.exclusiveMaximum}`,
195
+ actual: value,
196
+ });
197
+ }
198
+
199
+ // multipleOf
200
+ if (schema.multipleOf !== undefined && value % schema.multipleOf !== 0) {
201
+ errors.push({
202
+ path,
203
+ message: `${name} must be a multiple of ${schema.multipleOf}`,
204
+ expected: `multipleOf: ${schema.multipleOf}`,
205
+ actual: value,
206
+ });
207
+ }
208
+ }
209
+
210
+ // Enum validation
211
+ if (schema.enum && !schema.enum.includes(value as string | number | boolean | null)) {
212
+ errors.push({
213
+ path,
214
+ message: `${name} must be one of: ${schema.enum.join(", ")}`,
215
+ expected: `enum: [${schema.enum.join(", ")}]`,
216
+ actual: value,
217
+ });
218
+ }
219
+
220
+ // Const validation
221
+ if (schema.const !== undefined && value !== schema.const) {
222
+ errors.push({
223
+ path,
224
+ message: `${name} must be: ${schema.const}`,
225
+ expected: `const: ${schema.const}`,
226
+ actual: value,
227
+ });
228
+ }
229
+
230
+ // Array validations
231
+ if (Array.isArray(value)) {
232
+ // minItems
233
+ if (schema.minItems !== undefined && value.length < schema.minItems) {
234
+ errors.push({
235
+ path,
236
+ message: `${name} must have at least ${schema.minItems} items`,
237
+ expected: `minItems: ${schema.minItems}`,
238
+ actual: value,
239
+ });
240
+ }
241
+
242
+ // maxItems
243
+ if (schema.maxItems !== undefined && value.length > schema.maxItems) {
244
+ errors.push({
245
+ path,
246
+ message: `${name} must have at most ${schema.maxItems} items`,
247
+ expected: `maxItems: ${schema.maxItems}`,
248
+ actual: value,
249
+ });
250
+ }
251
+
252
+ // uniqueItems
253
+ if (schema.uniqueItems) {
254
+ const seen = new Set();
255
+ const hasDuplicates = value.some((item) => {
256
+ const key = JSON.stringify(item);
257
+ if (seen.has(key)) return true;
258
+ seen.add(key);
259
+ return false;
260
+ });
261
+ if (hasDuplicates) {
262
+ errors.push({
263
+ path,
264
+ message: `${name} must contain unique items`,
265
+ expected: "uniqueItems: true",
266
+ actual: value,
267
+ });
268
+ }
269
+ }
270
+ }
271
+
272
+ return errors;
273
+ }
274
+
275
+ /**
276
+ * Validate a value matches the expected type
277
+ */
278
+ function validateType(value: unknown, type: JSONSchema7["type"]): boolean {
279
+ if (type === undefined) return true;
280
+
281
+ // Handle array of types
282
+ if (Array.isArray(type)) {
283
+ return type.some((t) => validateType(value, t));
284
+ }
285
+
286
+ switch (type) {
287
+ case "string":
288
+ return typeof value === "string";
289
+ case "number":
290
+ return typeof value === "number" && !Number.isNaN(value);
291
+ case "integer":
292
+ return typeof value === "number" && Number.isInteger(value);
293
+ case "boolean":
294
+ return typeof value === "boolean";
295
+ case "array":
296
+ return Array.isArray(value);
297
+ case "object":
298
+ return typeof value === "object" && value !== null && !Array.isArray(value);
299
+ case "null":
300
+ return value === null;
301
+ default:
302
+ return true;
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Validate string format
308
+ */
309
+ function validateFormat(value: string, format: string): string | null {
310
+ switch (format) {
311
+ case "email": {
312
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
313
+ if (!emailRegex.test(value)) {
314
+ return "Invalid email format";
315
+ }
316
+ break;
317
+ }
318
+
319
+ case "uri":
320
+ case "url": {
321
+ try {
322
+ new URL(value);
323
+ } catch {
324
+ return "Invalid URL format";
325
+ }
326
+ break;
327
+ }
328
+
329
+ case "date": {
330
+ const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
331
+ if (!dateRegex.test(value) || Number.isNaN(Date.parse(value))) {
332
+ return "Invalid date format (expected YYYY-MM-DD)";
333
+ }
334
+ break;
335
+ }
336
+
337
+ case "date-time": {
338
+ if (Number.isNaN(Date.parse(value))) {
339
+ return "Invalid date-time format";
340
+ }
341
+ break;
342
+ }
343
+
344
+ case "time": {
345
+ const timeRegex = /^\d{2}:\d{2}(:\d{2})?$/;
346
+ if (!timeRegex.test(value)) {
347
+ return "Invalid time format (expected HH:MM or HH:MM:SS)";
348
+ }
349
+ break;
350
+ }
351
+
352
+ case "uuid": {
353
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
354
+ if (!uuidRegex.test(value)) {
355
+ return "Invalid UUID format";
356
+ }
357
+ break;
358
+ }
359
+
360
+ case "hostname": {
361
+ const hostnameRegex =
362
+ /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
363
+ if (!hostnameRegex.test(value)) {
364
+ return "Invalid hostname format";
365
+ }
366
+ break;
367
+ }
368
+
369
+ case "ipv4": {
370
+ const ipv4Regex =
371
+ /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
372
+ if (!ipv4Regex.test(value)) {
373
+ return "Invalid IPv4 format";
374
+ }
375
+ break;
376
+ }
377
+
378
+ // Note: Add more formats as needed
379
+ }
380
+
381
+ return null;
382
+ }
383
+
384
+ /**
385
+ * Attempt to coerce a value to match the schema type
386
+ */
387
+ function coerceValue(value: unknown, schema: JSONSchema7): unknown {
388
+ if (value === undefined || value === null) {
389
+ return schema.default;
390
+ }
391
+
392
+ const type = schema.type;
393
+ if (!type || Array.isArray(type)) {
394
+ return value;
395
+ }
396
+
397
+ // String coercion
398
+ if (type === "string" && typeof value !== "string") {
399
+ return String(value);
400
+ }
401
+
402
+ // Number coercion
403
+ if ((type === "number" || type === "integer") && typeof value === "string") {
404
+ const parsed = Number(value);
405
+ if (!Number.isNaN(parsed)) {
406
+ if (type === "integer") {
407
+ return Math.floor(parsed);
408
+ }
409
+ return parsed;
410
+ }
411
+ }
412
+
413
+ // Boolean coercion
414
+ if (type === "boolean" && typeof value === "string") {
415
+ if (value.toLowerCase() === "true") return true;
416
+ if (value.toLowerCase() === "false") return false;
417
+ }
418
+
419
+ return value;
420
+ }
421
+
422
+ /**
423
+ * Apply default values from schema to inputs
424
+ *
425
+ * @param inputs - Current inputs
426
+ * @param schema - Input schema with defaults
427
+ * @returns Inputs with defaults applied
428
+ */
429
+ export function applyDefaults(
430
+ inputs: Record<string, unknown>,
431
+ schema: JSONSchema7 | undefined
432
+ ): Record<string, unknown> {
433
+ if (!schema || schema.type !== "object" || !schema.properties) {
434
+ return inputs;
435
+ }
436
+
437
+ const result = { ...inputs };
438
+
439
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
440
+ if (result[propName] === undefined) {
441
+ const prop = propSchema as JSONSchema7;
442
+ if (prop.default !== undefined) {
443
+ result[propName] = prop.default;
444
+ }
445
+ }
446
+ }
447
+
448
+ return result;
449
+ }
450
+
451
+ /**
452
+ * Get the list of required parameters from a schema
453
+ *
454
+ * @param schema - Input schema
455
+ * @returns Array of required parameter names
456
+ */
457
+ export function getRequiredParams(schema: JSONSchema7 | undefined): string[] {
458
+ if (!schema || !schema.required) {
459
+ return [];
460
+ }
461
+ return [...schema.required];
462
+ }
463
+
464
+ /**
465
+ * Get parameter info from schema for help/documentation
466
+ *
467
+ * @param schema - Input schema
468
+ * @returns Map of parameter name to info
469
+ */
470
+ export function getParamInfo(
471
+ schema: JSONSchema7 | undefined
472
+ ): Map<
473
+ string,
474
+ { type: string; description?: string | undefined; required: boolean; default?: unknown }
475
+ > {
476
+ const info = new Map<
477
+ string,
478
+ { type: string; description?: string | undefined; required: boolean; default?: unknown }
479
+ >();
480
+
481
+ if (!schema || schema.type !== "object" || !schema.properties) {
482
+ return info;
483
+ }
484
+
485
+ const required = new Set(schema.required || []);
486
+
487
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
488
+ const prop = propSchema as JSONSchema7;
489
+ const entry: {
490
+ type: string;
491
+ description?: string | undefined;
492
+ required: boolean;
493
+ default?: unknown;
494
+ } = {
495
+ type: Array.isArray(prop.type) ? prop.type.join(" | ") : prop.type || "any",
496
+ required: required.has(propName),
497
+ };
498
+ if (prop.description !== undefined) {
499
+ entry.description = prop.description;
500
+ }
501
+ if (prop.default !== undefined) {
502
+ entry.default = prop.default;
503
+ }
504
+ info.set(propName, entry);
505
+ }
506
+
507
+ return info;
508
+ }