@aws/ml-container-creator 0.2.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 (143) hide show
  1. package/LICENSE +202 -0
  2. package/LICENSE-THIRD-PARTY +68620 -0
  3. package/NOTICE +2 -0
  4. package/README.md +106 -0
  5. package/bin/cli.js +365 -0
  6. package/config/defaults.json +32 -0
  7. package/config/presets/transformers-djl.json +26 -0
  8. package/config/presets/transformers-gpu.json +24 -0
  9. package/config/presets/transformers-lmi.json +27 -0
  10. package/package.json +129 -0
  11. package/servers/README.md +419 -0
  12. package/servers/base-image-picker/catalogs/model-servers.json +1191 -0
  13. package/servers/base-image-picker/catalogs/python-slim.json +38 -0
  14. package/servers/base-image-picker/catalogs/triton-backends.json +51 -0
  15. package/servers/base-image-picker/catalogs/triton.json +38 -0
  16. package/servers/base-image-picker/index.js +495 -0
  17. package/servers/base-image-picker/manifest.json +17 -0
  18. package/servers/base-image-picker/package.json +15 -0
  19. package/servers/hyperpod-cluster-picker/LICENSE +202 -0
  20. package/servers/hyperpod-cluster-picker/index.js +424 -0
  21. package/servers/hyperpod-cluster-picker/manifest.json +14 -0
  22. package/servers/hyperpod-cluster-picker/package.json +17 -0
  23. package/servers/instance-recommender/LICENSE +202 -0
  24. package/servers/instance-recommender/catalogs/instances.json +852 -0
  25. package/servers/instance-recommender/index.js +284 -0
  26. package/servers/instance-recommender/manifest.json +16 -0
  27. package/servers/instance-recommender/package.json +15 -0
  28. package/servers/lib/LICENSE +202 -0
  29. package/servers/lib/bedrock-client.js +160 -0
  30. package/servers/lib/custom-validators.js +46 -0
  31. package/servers/lib/dynamic-resolver.js +36 -0
  32. package/servers/lib/package.json +11 -0
  33. package/servers/lib/schemas/image-catalog.schema.json +185 -0
  34. package/servers/lib/schemas/instances.schema.json +124 -0
  35. package/servers/lib/schemas/manifest.schema.json +64 -0
  36. package/servers/lib/schemas/model-catalog.schema.json +91 -0
  37. package/servers/lib/schemas/regions.schema.json +26 -0
  38. package/servers/lib/schemas/triton-backends.schema.json +51 -0
  39. package/servers/model-picker/catalogs/jumpstart-public.json +66 -0
  40. package/servers/model-picker/catalogs/popular-diffusors.json +88 -0
  41. package/servers/model-picker/catalogs/popular-transformers.json +226 -0
  42. package/servers/model-picker/index.js +1693 -0
  43. package/servers/model-picker/manifest.json +18 -0
  44. package/servers/model-picker/package.json +20 -0
  45. package/servers/region-picker/LICENSE +202 -0
  46. package/servers/region-picker/catalogs/regions.json +263 -0
  47. package/servers/region-picker/index.js +230 -0
  48. package/servers/region-picker/manifest.json +16 -0
  49. package/servers/region-picker/package.json +15 -0
  50. package/src/app.js +1007 -0
  51. package/src/copy-tpl.js +77 -0
  52. package/src/lib/accelerator-validator.js +39 -0
  53. package/src/lib/asset-manager.js +385 -0
  54. package/src/lib/aws-profile-parser.js +181 -0
  55. package/src/lib/bootstrap-command-handler.js +1647 -0
  56. package/src/lib/bootstrap-config.js +238 -0
  57. package/src/lib/ci-register-helpers.js +124 -0
  58. package/src/lib/ci-report-helpers.js +158 -0
  59. package/src/lib/ci-stage-helpers.js +268 -0
  60. package/src/lib/cli-handler.js +529 -0
  61. package/src/lib/comment-generator.js +544 -0
  62. package/src/lib/community-reports-validator.js +91 -0
  63. package/src/lib/config-manager.js +2106 -0
  64. package/src/lib/configuration-exporter.js +204 -0
  65. package/src/lib/configuration-manager.js +695 -0
  66. package/src/lib/configuration-matcher.js +221 -0
  67. package/src/lib/cpu-validator.js +36 -0
  68. package/src/lib/cuda-validator.js +57 -0
  69. package/src/lib/deployment-config-resolver.js +103 -0
  70. package/src/lib/deployment-entry-schema.js +125 -0
  71. package/src/lib/deployment-registry.js +598 -0
  72. package/src/lib/docker-introspection-validator.js +51 -0
  73. package/src/lib/engine-prefix-resolver.js +60 -0
  74. package/src/lib/huggingface-client.js +172 -0
  75. package/src/lib/key-value-parser.js +37 -0
  76. package/src/lib/known-flags-validator.js +200 -0
  77. package/src/lib/manifest-cli.js +280 -0
  78. package/src/lib/mcp-client.js +303 -0
  79. package/src/lib/mcp-command-handler.js +532 -0
  80. package/src/lib/neuron-validator.js +80 -0
  81. package/src/lib/parameter-schema-validator.js +284 -0
  82. package/src/lib/prompt-runner.js +1349 -0
  83. package/src/lib/prompts.js +1138 -0
  84. package/src/lib/registry-command-handler.js +519 -0
  85. package/src/lib/registry-loader.js +198 -0
  86. package/src/lib/rocm-validator.js +80 -0
  87. package/src/lib/schema-validator.js +157 -0
  88. package/src/lib/sensitive-redactor.js +59 -0
  89. package/src/lib/template-engine.js +156 -0
  90. package/src/lib/template-manager.js +341 -0
  91. package/src/lib/validation-engine.js +314 -0
  92. package/src/prompt-adapter.js +63 -0
  93. package/templates/Dockerfile +300 -0
  94. package/templates/IAM_PERMISSIONS.md +84 -0
  95. package/templates/MIGRATION.md +488 -0
  96. package/templates/PROJECT_README.md +439 -0
  97. package/templates/TEMPLATE_SYSTEM.md +243 -0
  98. package/templates/buildspec.yml +64 -0
  99. package/templates/code/chat_template.jinja +1 -0
  100. package/templates/code/flask/gunicorn_config.py +35 -0
  101. package/templates/code/flask/wsgi.py +10 -0
  102. package/templates/code/model_handler.py +387 -0
  103. package/templates/code/serve +300 -0
  104. package/templates/code/serve.py +175 -0
  105. package/templates/code/serving.properties +105 -0
  106. package/templates/code/start_server.py +39 -0
  107. package/templates/code/start_server.sh +39 -0
  108. package/templates/diffusors/Dockerfile +72 -0
  109. package/templates/diffusors/patch_image_api.py +35 -0
  110. package/templates/diffusors/serve +115 -0
  111. package/templates/diffusors/start_server.sh +114 -0
  112. package/templates/do/.gitkeep +1 -0
  113. package/templates/do/README.md +541 -0
  114. package/templates/do/build +83 -0
  115. package/templates/do/ci +681 -0
  116. package/templates/do/clean +811 -0
  117. package/templates/do/config +260 -0
  118. package/templates/do/deploy +1560 -0
  119. package/templates/do/export +306 -0
  120. package/templates/do/logs +319 -0
  121. package/templates/do/manifest +12 -0
  122. package/templates/do/push +119 -0
  123. package/templates/do/register +580 -0
  124. package/templates/do/run +113 -0
  125. package/templates/do/submit +417 -0
  126. package/templates/do/test +1147 -0
  127. package/templates/hyperpod/configmap.yaml +24 -0
  128. package/templates/hyperpod/deployment.yaml +71 -0
  129. package/templates/hyperpod/pvc.yaml +42 -0
  130. package/templates/hyperpod/service.yaml +17 -0
  131. package/templates/nginx-diffusors.conf +74 -0
  132. package/templates/nginx-predictors.conf +47 -0
  133. package/templates/nginx-tensorrt.conf +74 -0
  134. package/templates/requirements.txt +61 -0
  135. package/templates/sample_model/test_inference.py +123 -0
  136. package/templates/sample_model/train_abalone.py +252 -0
  137. package/templates/test/test_endpoint.sh +79 -0
  138. package/templates/test/test_local_image.sh +80 -0
  139. package/templates/test/test_model_handler.py +180 -0
  140. package/templates/triton/Dockerfile +128 -0
  141. package/templates/triton/config.pbtxt +163 -0
  142. package/templates/triton/model.py +130 -0
  143. package/templates/triton/requirements.txt +11 -0
@@ -0,0 +1,532 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * MCP Command Handler
6
+ *
7
+ * Handles the `mcp` CLI subcommand tree for managing MCP server
8
+ * configurations in config/mcp.json.
9
+ *
10
+ * Subcommands:
11
+ * add <name> -- <command> [args...] Add or update an MCP server
12
+ * list List configured MCP servers
13
+ * get <name> Show full server configuration
14
+ * remove <name> Remove an MCP server
15
+ */
16
+
17
+ import fs from 'fs';
18
+ import path from 'path';
19
+ import { execSync } from 'child_process';
20
+ import { fileURLToPath } from 'url';
21
+
22
+ const CONFIG_FILENAME = 'config/mcp.json';
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = path.dirname(__filename);
25
+ const SERVERS_DIR = path.resolve(__dirname, '../../servers');
26
+
27
+ export default class McpCommandHandler {
28
+ constructor(generator) {
29
+ this.generator = generator;
30
+ }
31
+
32
+ /**
33
+ * Dispatch mcp subcommands.
34
+ * @param {string[]} args - Remaining positional args after 'mcp'
35
+ * @param {object} options - Parsed CLI options
36
+ */
37
+ async handle(args, options) {
38
+ if (args.length === 0) {
39
+ this._showMcpHelp();
40
+ return;
41
+ }
42
+
43
+ const subcommand = args[0].toLowerCase();
44
+
45
+ switch (subcommand) {
46
+ case 'add':
47
+ await this._handleAdd(args.slice(1), options);
48
+ break;
49
+ case 'init':
50
+ await this._handleInit();
51
+ break;
52
+ case 'list':
53
+ this._handleList(options);
54
+ break;
55
+ case 'get':
56
+ this._handleGet(args[1]);
57
+ break;
58
+ case 'remove':
59
+ await this._handleRemove(args[1]);
60
+ break;
61
+ default:
62
+ console.log(`Unknown mcp subcommand: ${subcommand}`);
63
+ this._showMcpHelp();
64
+ break;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * mcp add <name> -- <command> [args...]
70
+ * Supports: -e KEY=VALUE, --tool-name <toolName>, --limit <num>
71
+ * @param {string[]} positionalArgs - Args after 'add'
72
+ * @param {object} options - Parsed CLI options
73
+ */
74
+ async _handleAdd(positionalArgs, options) {
75
+ if (positionalArgs.length === 0) {
76
+ console.log('Usage: ml-container-creator mcp add <name> -- <command> [args...]');
77
+ return;
78
+ }
79
+
80
+ const name = positionalArgs[0];
81
+ const isBundled = options.bundled === true || options.bundled === 'true';
82
+
83
+ let command, commandArgs;
84
+
85
+ if (isBundled) {
86
+ // Resolve bundled server
87
+ const resolved = this._resolveBundledServer(name);
88
+ if (!resolved) return;
89
+
90
+ // Lazy dependency installation
91
+ const installed = await this._installBundledDependencies(resolved.serverDir, name);
92
+ if (!installed) return;
93
+
94
+ command = 'node';
95
+ commandArgs = [resolved.entryPoint];
96
+ } else {
97
+ // Find the '--' separator to split name from command
98
+ const separatorIndex = positionalArgs.indexOf('--');
99
+ if (separatorIndex === -1 || separatorIndex + 1 >= positionalArgs.length) {
100
+ console.log('Usage: ml-container-creator mcp add <name> -- <command> [args...]');
101
+ console.log('The "--" separator is required between the server name and the command.');
102
+ return;
103
+ }
104
+
105
+ const commandParts = positionalArgs.slice(separatorIndex + 1);
106
+ command = commandParts[0];
107
+ commandArgs = commandParts.slice(1);
108
+ }
109
+
110
+ // Build server config
111
+ const serverConfig = { command };
112
+ if (commandArgs.length > 0) {
113
+ serverConfig.args = commandArgs;
114
+ }
115
+
116
+ // Parse -e KEY=VALUE env flags
117
+ const env = this._parseEnvFlags(options);
118
+ if (Object.keys(env).length > 0) {
119
+ serverConfig.env = env;
120
+ }
121
+
122
+ // Parse --tool-name
123
+ const toolName = options['tool-name'] || options.toolName;
124
+ if (toolName) {
125
+ serverConfig.toolName = toolName;
126
+ }
127
+
128
+ // Parse --limit
129
+ const limit = options.limit;
130
+ if (limit !== undefined) {
131
+ const parsed = parseInt(limit, 10);
132
+ if (isNaN(parsed) || parsed < 1) {
133
+ console.log('Error: --limit must be a positive integer');
134
+ return;
135
+ }
136
+ serverConfig.limit = parsed;
137
+ }
138
+
139
+ // Read existing config
140
+ const config = this._readConfig();
141
+
142
+ // Check if server already exists
143
+ if (config.mcpServers && config.mcpServers[name]) {
144
+ const answers = await this.generator.prompt([{
145
+ type: 'confirm',
146
+ name: 'overwrite',
147
+ message: `MCP server "${name}" already exists. Overwrite?`,
148
+ default: false
149
+ }]);
150
+ if (!answers.overwrite) {
151
+ console.log('Aborted.');
152
+ return;
153
+ }
154
+ }
155
+
156
+ // Add server
157
+ if (!config.mcpServers) {
158
+ config.mcpServers = {};
159
+ }
160
+ config.mcpServers[name] = serverConfig;
161
+
162
+ this._writeConfig(config);
163
+ console.log(`✅ MCP server "${name}" added successfully.`);
164
+ }
165
+
166
+ /**
167
+ * mcp init
168
+ * Registers all bundled servers in one shot, creating or updating
169
+ * config/mcp.json. Existing non-MCP keys are preserved.
170
+ */
171
+ async _handleInit() {
172
+ const bundled = this._getAvailableBundledServers();
173
+ if (bundled.length === 0) {
174
+ console.log('No bundled servers found in servers/ directory.');
175
+ return;
176
+ }
177
+
178
+ const config = this._readConfig();
179
+ if (!config.mcpServers) {
180
+ config.mcpServers = {};
181
+ }
182
+
183
+ let added = 0;
184
+ let skipped = 0;
185
+
186
+ for (const server of bundled) {
187
+ if (config.mcpServers[server.name]) {
188
+ skipped++;
189
+ continue;
190
+ }
191
+
192
+ const resolved = this._resolveBundledServer(server.name);
193
+ if (!resolved) continue;
194
+
195
+ const installed = await this._installBundledDependencies(resolved.serverDir, server.name);
196
+ if (!installed) continue;
197
+
198
+ config.mcpServers[server.name] = {
199
+ command: 'node',
200
+ args: [resolved.entryPoint]
201
+ };
202
+ added++;
203
+ }
204
+
205
+ this._writeConfig(config);
206
+
207
+ if (added > 0) {
208
+ console.log(`\n✅ Initialized ${added} bundled MCP server(s).`);
209
+ }
210
+ if (skipped > 0) {
211
+ console.log(` ${skipped} server(s) already configured (skipped).`);
212
+ }
213
+ if (added === 0 && skipped > 0) {
214
+ console.log('\nAll bundled servers are already configured.');
215
+ }
216
+ console.log(`\nConfig written to ${this._getConfigPath()}`);
217
+ }
218
+
219
+ /**
220
+ * mcp list [--bundled]
221
+ * @param {object} options - Parsed CLI options
222
+ */
223
+ _handleList(options) {
224
+ const isBundled = options.bundled === true || options.bundled === 'true';
225
+
226
+ if (isBundled) {
227
+ this._listBundledServers();
228
+ return;
229
+ }
230
+
231
+ const config = this._readConfig();
232
+ const servers = config.mcpServers;
233
+
234
+ if (!servers || Object.keys(servers).length === 0) {
235
+ console.log('No MCP servers configured.');
236
+ console.log('Use "ml-container-creator mcp add <name> -- <command> [args...]" to add one.');
237
+ return;
238
+ }
239
+
240
+ console.log('\nConfigured MCP servers:\n');
241
+ for (const [name, serverConfig] of Object.entries(servers)) {
242
+ const args = serverConfig.args ? serverConfig.args.join(' ') : '';
243
+ console.log(` ${name}: ${serverConfig.command} ${args}`.trimEnd());
244
+ }
245
+ console.log('');
246
+ }
247
+
248
+ /**
249
+ * mcp get <name>
250
+ * @param {string} name - Server name
251
+ */
252
+ _handleGet(name) {
253
+ if (!name) {
254
+ console.log('Usage: ml-container-creator mcp get <name>');
255
+ return;
256
+ }
257
+
258
+ const config = this._readConfig();
259
+ const servers = config.mcpServers;
260
+
261
+ if (!servers || !servers[name]) {
262
+ const available = servers ? Object.keys(servers) : [];
263
+ console.log(`Error: MCP server "${name}" not found.`);
264
+ if (available.length > 0) {
265
+ console.log(`Available servers: ${available.join(', ')}`);
266
+ }
267
+ return;
268
+ }
269
+
270
+ const serverConfig = servers[name];
271
+ console.log(`\nMCP server: ${name}\n`);
272
+ console.log(` command: ${serverConfig.command}`);
273
+ console.log(` args: ${serverConfig.args ? serverConfig.args.join(' ') : '(none)'}`);
274
+ console.log(` env: ${serverConfig.env ? JSON.stringify(serverConfig.env) : '(none)'}`);
275
+ console.log(` toolName: ${serverConfig.toolName || '(default: get_ml_config)'}`);
276
+ console.log(` limit: ${serverConfig.limit || '(default: 10)'}`);
277
+ console.log('');
278
+ }
279
+
280
+ /**
281
+ * mcp remove <name>
282
+ * @param {string} name - Server name
283
+ */
284
+ async _handleRemove(name) {
285
+ if (!name) {
286
+ console.log('Usage: ml-container-creator mcp remove <name>');
287
+ return;
288
+ }
289
+
290
+ const config = this._readConfig();
291
+ const servers = config.mcpServers;
292
+
293
+ if (!servers || !servers[name]) {
294
+ console.log(`Error: MCP server "${name}" not found.`);
295
+ return;
296
+ }
297
+
298
+ delete servers[name];
299
+
300
+ // If last server removed, remove the mcpServers key entirely
301
+ if (Object.keys(servers).length === 0) {
302
+ delete config.mcpServers;
303
+ }
304
+
305
+ this._writeConfig(config);
306
+ console.log(`✅ MCP server "${name}" removed.`);
307
+ }
308
+
309
+ /**
310
+ * Show mcp usage help.
311
+ */
312
+ _showMcpHelp() {
313
+ console.log(`
314
+ MCP Server Management
315
+
316
+ USAGE:
317
+ ml-container-creator mcp <subcommand> [options]
318
+
319
+ SUBCOMMANDS:
320
+ init Add all bundled servers at once
321
+ add <name> -- <command> [args...] Add an MCP server
322
+ add <name> --bundled Add a bundled MCP server
323
+ list List configured MCP servers
324
+ list --bundled List available bundled servers
325
+ get <name> Show full server configuration
326
+ remove <name> Remove an MCP server
327
+
328
+ ADD OPTIONS:
329
+ -e KEY=VALUE Set environment variable (repeatable)
330
+ --tool-name <toolName> MCP tool name (default: get_ml_config)
331
+ --limit <num> Max results per parameter (default: 10)
332
+ --bundled Use a bundled server from servers/
333
+
334
+ EXAMPLES:
335
+ ml-container-creator mcp init
336
+ ml-container-creator mcp add team-config -- node servers/instance-recommender/index.js
337
+ ml-container-creator mcp add instance-recommender --bundled
338
+ ml-container-creator mcp add corp-policy -- npx -y @corp/mcp-policy -e TEAM_ID=ml-platform
339
+ ml-container-creator mcp list
340
+ ml-container-creator mcp list --bundled
341
+ ml-container-creator mcp get team-config
342
+ ml-container-creator mcp remove team-config
343
+ `);
344
+ }
345
+
346
+ /**
347
+ * Resolve a bundled server from the servers/ directory.
348
+ * @param {string} name - Server name (directory name under servers/)
349
+ * @returns {{ serverDir: string, entryPoint: string } | null}
350
+ */
351
+ _resolveBundledServer(name) {
352
+ const serverDir = path.join(SERVERS_DIR, name);
353
+ const pkgPath = path.join(serverDir, 'package.json');
354
+
355
+ if (!fs.existsSync(pkgPath)) {
356
+ const available = this._getAvailableBundledServers();
357
+ console.log(`Error: Bundled server "${name}" not found.`);
358
+ if (available.length > 0) {
359
+ console.log(`Available bundled servers: ${available.map(s => s.name).join(', ')}`);
360
+ } else {
361
+ console.log('No bundled servers are available.');
362
+ }
363
+ return null;
364
+ }
365
+
366
+ let pkg;
367
+ try {
368
+ pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
369
+ } catch (err) {
370
+ console.log(`Error reading package.json for bundled server "${name}": ${err.message}`);
371
+ return null;
372
+ }
373
+
374
+ const main = pkg.main || 'index.js';
375
+ const entryPoint = path.join(serverDir, main);
376
+
377
+ return { serverDir, entryPoint };
378
+ }
379
+
380
+ /**
381
+ * Install dependencies for a bundled server if not already installed.
382
+ * @param {string} serverDir - Absolute path to the server directory
383
+ * @param {string} name - Server name for display
384
+ * @returns {boolean} true if dependencies are ready, false on failure
385
+ */
386
+ async _installBundledDependencies(serverDir, name) {
387
+ const nodeModulesDir = path.join(serverDir, 'node_modules');
388
+
389
+ if (fs.existsSync(nodeModulesDir)) {
390
+ return true;
391
+ }
392
+
393
+ console.log(`Installing dependencies for ${name}...`);
394
+ try {
395
+ execSync('npm install --production', {
396
+ cwd: serverDir,
397
+ stdio: 'pipe',
398
+ timeout: 60000
399
+ });
400
+ return true;
401
+ } catch (err) {
402
+ const stderr = err.stderr ? err.stderr.toString() : err.message;
403
+ console.log(`Error: Failed to install dependencies for "${name}".`);
404
+ console.log(stderr);
405
+ return false;
406
+ }
407
+ }
408
+
409
+ /**
410
+ * List available bundled servers from the servers/ directory.
411
+ */
412
+ _listBundledServers() {
413
+ const servers = this._getAvailableBundledServers();
414
+
415
+ if (servers.length === 0) {
416
+ console.log('No bundled servers available.');
417
+ return;
418
+ }
419
+
420
+ console.log('\nAvailable bundled servers:\n');
421
+ for (const server of servers) {
422
+ console.log(` ${server.name}: ${server.description}`);
423
+ }
424
+ console.log('\nUse "ml-container-creator mcp add <name> --bundled" to add one.');
425
+ console.log('');
426
+ }
427
+
428
+ /**
429
+ * Get list of available bundled servers with metadata.
430
+ * @returns {Array<{ name: string, description: string }>}
431
+ */
432
+ _getAvailableBundledServers() {
433
+ if (!fs.existsSync(SERVERS_DIR)) {
434
+ return [];
435
+ }
436
+
437
+ const entries = fs.readdirSync(SERVERS_DIR, { withFileTypes: true });
438
+ const servers = [];
439
+
440
+ for (const entry of entries) {
441
+ if (!entry.isDirectory()) continue;
442
+
443
+ const pkgPath = path.join(SERVERS_DIR, entry.name, 'package.json');
444
+ if (!fs.existsSync(pkgPath)) continue;
445
+
446
+ try {
447
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
448
+
449
+ // Skip non-server packages (e.g. shared libraries) —
450
+ // a bundled MCP server must declare @modelcontextprotocol/sdk
451
+ // as a dependency.
452
+ const deps = pkg.dependencies || {};
453
+ if (!deps['@modelcontextprotocol/sdk']) continue;
454
+
455
+ servers.push({
456
+ name: entry.name,
457
+ description: pkg.description || '(no description)'
458
+ });
459
+ } catch (_) {
460
+ servers.push({
461
+ name: entry.name,
462
+ description: '(unable to read package.json)'
463
+ });
464
+ }
465
+ }
466
+
467
+ return servers;
468
+ }
469
+
470
+ /**
471
+ * Parse -e KEY=VALUE flags from options.
472
+ * @param {object} options
473
+ * @returns {object} env key-value pairs
474
+ */
475
+ _parseEnvFlags(options) {
476
+ const env = {};
477
+ const envFlags = options.e || options.env;
478
+ if (!envFlags) return env;
479
+
480
+ const entries = Array.isArray(envFlags) ? envFlags : [envFlags];
481
+ for (const entry of entries) {
482
+ const eqIndex = entry.indexOf('=');
483
+ if (eqIndex > 0) {
484
+ const key = entry.slice(0, eqIndex);
485
+ const value = entry.slice(eqIndex + 1);
486
+ env[key] = value;
487
+ }
488
+ }
489
+ return env;
490
+ }
491
+
492
+ /**
493
+ * Read config/mcp.json, preserving all keys.
494
+ * Returns empty object if file doesn't exist.
495
+ * @returns {object}
496
+ */
497
+ _readConfig() {
498
+ const configPath = this._getConfigPath();
499
+ if (!fs.existsSync(configPath)) {
500
+ return {};
501
+ }
502
+ try {
503
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
504
+ } catch (err) {
505
+ console.log(`Error reading ${CONFIG_FILENAME}: ${err.message}`);
506
+ return {};
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Write config to config/mcp.json, preserving non-MCP keys.
512
+ * @param {object} config
513
+ */
514
+ _writeConfig(config) {
515
+ const configPath = this._getConfigPath();
516
+ const dir = path.dirname(configPath);
517
+ if (!fs.existsSync(dir)) {
518
+ fs.mkdirSync(dir, { recursive: true });
519
+ }
520
+ fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
521
+ }
522
+
523
+ /**
524
+ * Resolve the config file path.
525
+ * @returns {string}
526
+ */
527
+ _getConfigPath() {
528
+ return this.generator.destinationPath
529
+ ? this.generator.destinationPath(CONFIG_FILENAME)
530
+ : path.resolve(CONFIG_FILENAME);
531
+ }
532
+ }
@@ -0,0 +1,80 @@
1
+ import AcceleratorValidator from './accelerator-validator.js';
2
+
3
+ /**
4
+ * Neuron SDK accelerator validator.
5
+ * Implements Neuron SDK semantic versioning.
6
+ *
7
+ * Requirements: 4.11, 4.12, 4.13, 4.14, 4.22
8
+ */
9
+ export default class NeuronValidator extends AcceleratorValidator {
10
+ /**
11
+ * Validate Neuron SDK version compatibility.
12
+ * Neuron SDK uses semantic versioning (e.g., 2.15.0, 2.16.0).
13
+ * Major version must match, minor version must be >= required.
14
+ *
15
+ * @param {Object} frameworkConfig - Framework accelerator requirements
16
+ * @param {Object} instanceConfig - Instance accelerator capabilities
17
+ * @returns {Object} ValidationResult
18
+ */
19
+ validate(frameworkConfig, instanceConfig) {
20
+ const required = frameworkConfig.accelerator;
21
+ const provided = instanceConfig.accelerator;
22
+
23
+ // Parse required Neuron SDK version
24
+ const requiredVersion = this.parseVersion(required.version);
25
+
26
+ // Check if instance supports required Neuron SDK version
27
+ const compatibleVersions = provided.versions.filter(v => {
28
+ const providedVersion = this.parseVersion(v);
29
+ return this.isCompatible(requiredVersion, providedVersion);
30
+ });
31
+
32
+ if (compatibleVersions.length === 0) {
33
+ return {
34
+ compatible: false,
35
+ error: this.getVersionMismatchMessage(required.version, provided.versions)
36
+ };
37
+ }
38
+
39
+ return {
40
+ compatible: true,
41
+ info: `Using Neuron SDK ${compatibleVersions[0]} (compatible with required ${required.version})`
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Parse semantic version string into components.
47
+ *
48
+ * @param {string} versionString - Version string (e.g., "2.15.0")
49
+ * @returns {Object} Parsed version with major, minor, patch
50
+ */
51
+ parseVersion(versionString) {
52
+ const [major, minor, patch] = versionString.split('.').map(Number);
53
+ return { major, minor, patch };
54
+ }
55
+
56
+ /**
57
+ * Check if provided version is compatible with required version.
58
+ * Neuron SDK: major must match, minor must be >= required.
59
+ *
60
+ * @param {Object} required - Required version
61
+ * @param {Object} provided - Provided version
62
+ * @returns {boolean} True if compatible
63
+ */
64
+ isCompatible(required, provided) {
65
+ return provided.major === required.major &&
66
+ provided.minor >= required.minor;
67
+ }
68
+
69
+ /**
70
+ * Get user-friendly error message for Neuron SDK version mismatch.
71
+ *
72
+ * @param {string} required - Required Neuron SDK version
73
+ * @param {Array<string>} provided - Provided Neuron SDK versions
74
+ * @returns {string} User-friendly error message
75
+ */
76
+ getVersionMismatchMessage(required, provided) {
77
+ return `Framework requires Neuron SDK ${required}, but instance only supports ${provided.join(', ')}. ` +
78
+ 'Consider using ml.inf2 instances for Neuron SDK 2.15+ support.';
79
+ }
80
+ }