@aws/ml-container-creator 0.2.5 → 0.3.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 (72) hide show
  1. package/bin/cli.js +45 -4
  2. package/config/bootstrap-stack.json +14 -0
  3. package/infra/ci-harness/package-lock.json +22 -9
  4. package/package.json +7 -8
  5. package/servers/base-image-picker/index.js +3 -3
  6. package/servers/base-image-picker/manifest.json +4 -2
  7. package/servers/instance-sizer/index.js +564 -0
  8. package/servers/instance-sizer/lib/instance-ranker.js +270 -0
  9. package/servers/instance-sizer/lib/model-resolver.js +269 -0
  10. package/servers/instance-sizer/lib/vram-estimator.js +177 -0
  11. package/servers/instance-sizer/manifest.json +17 -0
  12. package/servers/instance-sizer/package.json +15 -0
  13. package/servers/{instance-recommender → lib}/catalogs/instances.json +136 -34
  14. package/servers/{base-image-picker → lib}/catalogs/model-servers.json +302 -254
  15. package/servers/lib/catalogs/model-sizes.json +131 -0
  16. package/servers/lib/catalogs/models.json +632 -0
  17. package/servers/{model-picker → lib}/catalogs/popular-diffusors.json +32 -10
  18. package/servers/{model-picker → lib}/catalogs/popular-transformers.json +59 -26
  19. package/servers/{base-image-picker → lib}/catalogs/python-slim.json +12 -12
  20. package/servers/lib/schemas/image-catalog.schema.json +6 -12
  21. package/servers/lib/schemas/instances.schema.json +29 -0
  22. package/servers/lib/schemas/model-catalog.schema.json +12 -10
  23. package/servers/lib/schemas/unified-model-catalog.schema.json +129 -0
  24. package/servers/model-picker/index.js +4 -4
  25. package/servers/model-picker/manifest.json +2 -3
  26. package/servers/region-picker/index.js +1 -1
  27. package/servers/region-picker/manifest.json +1 -1
  28. package/src/app.js +36 -0
  29. package/src/lib/architecture-sync.js +171 -0
  30. package/src/lib/arn-detection.js +22 -0
  31. package/src/lib/bootstrap-command-handler.js +120 -0
  32. package/src/lib/cli-handler.js +3 -3
  33. package/src/lib/config-manager.js +47 -1
  34. package/src/lib/configuration-manager.js +2 -2
  35. package/src/lib/cross-cutting-checker.js +460 -0
  36. package/src/lib/deployment-entry-schema.js +1 -2
  37. package/src/lib/dry-run-validator.js +78 -0
  38. package/src/lib/generation-validator.js +102 -0
  39. package/src/lib/mcp-validator-config.js +89 -0
  40. package/src/lib/payload-builder.js +153 -0
  41. package/src/lib/prompt-runner.js +866 -149
  42. package/src/lib/prompts.js +2 -2
  43. package/src/lib/registry-command-handler.js +236 -0
  44. package/src/lib/registry-loader.js +5 -5
  45. package/src/lib/schema-sync.js +203 -0
  46. package/src/lib/schema-validation-engine.js +195 -0
  47. package/src/lib/secret-classification.js +56 -0
  48. package/src/lib/secrets-command-handler.js +550 -0
  49. package/src/lib/service-model-parser.js +102 -0
  50. package/src/lib/validate-runner.js +216 -0
  51. package/src/lib/validation-report.js +140 -0
  52. package/src/lib/validators/base-validator.js +36 -0
  53. package/src/lib/validators/catalog-validator.js +177 -0
  54. package/src/lib/validators/enum-validator.js +120 -0
  55. package/src/lib/validators/required-field-validator.js +150 -0
  56. package/src/lib/validators/type-validator.js +313 -0
  57. package/src/prompt-adapter.js +3 -2
  58. package/templates/Dockerfile +1 -1
  59. package/templates/do/build +37 -5
  60. package/templates/do/config +15 -3
  61. package/templates/do/deploy +60 -5
  62. package/templates/do/logs +18 -3
  63. package/templates/do/run +15 -1
  64. package/templates/do/validate +61 -0
  65. package/servers/instance-recommender/LICENSE +0 -202
  66. package/servers/instance-recommender/index.js +0 -284
  67. package/servers/instance-recommender/manifest.json +0 -16
  68. package/servers/instance-recommender/package.json +0 -15
  69. /package/servers/{model-picker → lib}/catalogs/jumpstart-public.json +0 -0
  70. /package/servers/{region-picker → lib}/catalogs/regions.json +0 -0
  71. /package/servers/{base-image-picker → lib}/catalogs/triton-backends.json +0 -0
  72. /package/servers/{base-image-picker → lib}/catalogs/triton.json +0 -0
package/src/app.js CHANGED
@@ -119,6 +119,23 @@ export async function run(projectName, options) {
119
119
  let answers;
120
120
  if (configManager.shouldSkipPrompts()) {
121
121
  console.log('\n🚀 Skipping prompts - using configuration from other sources');
122
+
123
+ // Fail-fast if required parameters are missing
124
+ const missing = configManager.getMissingRequiredParameters();
125
+ if (missing.length > 0) {
126
+ console.error('\n❌ Cannot skip prompts — required parameters are missing:\n');
127
+ for (const param of missing) {
128
+ const matrix = configManager._getParameterMatrix()[param];
129
+ const cliFlag = matrix?.cliOption ? `--${matrix.cliOption}` : '';
130
+ const envVar = matrix?.envVar || '';
131
+ const hints = [cliFlag, envVar].filter(Boolean).join(' or ');
132
+ console.error(` • ${param}${hints ? ` (${hints})` : ''}`);
133
+ }
134
+ console.error('\n Provide these via CLI flags, environment variables, or a config file.');
135
+ console.error(' Run "ml-container-creator --help" for available options.\n');
136
+ process.exit(1);
137
+ }
138
+
122
139
  answers = configManager.getFinalConfiguration();
123
140
 
124
141
  // Infer modelSource from model name prefix if not set
@@ -188,6 +205,23 @@ export async function run(projectName, options) {
188
205
 
189
206
  // --- Phase: Writing ---
190
207
  const destDir = path.resolve(answers.destinationDir);
208
+
209
+ // Safety guard: refuse to generate into the generator's own directory
210
+ const destPkgPath = path.join(destDir, 'package.json');
211
+ if (fs.existsSync(destPkgPath)) {
212
+ try {
213
+ const destPkg = JSON.parse(fs.readFileSync(destPkgPath, 'utf8'));
214
+ if (destPkg.name === '@aws/ml-container-creator') {
215
+ console.log('\n❌ Refusing to generate into the generator\'s own directory.');
216
+ console.log(' This would overwrite the generator source files.');
217
+ console.log(' Use --project-dir or provide a project name instead.\n');
218
+ return;
219
+ }
220
+ } catch {
221
+ // If we can't read/parse package.json, it's not the generator dir — proceed
222
+ }
223
+ }
224
+
191
225
  fs.mkdirSync(destDir, { recursive: true });
192
226
 
193
227
  await writeProject(TEMPLATE_DIR, destDir, answers, registryConfigManager, tritonBackends, configManager);
@@ -476,7 +510,9 @@ async function _ensureTemplateVariables(answers, registryConfigManager = null) {
476
510
  chatTemplate: null,
477
511
  chatTemplateSource: null,
478
512
  hfToken: null,
513
+ hfTokenArn: null,
479
514
  ngcApiKey: null,
515
+ ngcTokenArn: null,
480
516
  envVars: {},
481
517
  inferenceAmiVersion: null,
482
518
  accelerator: null,
@@ -0,0 +1,171 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * Architecture Sync
6
+ *
7
+ * Fetches model registry source files from server GitHub repositories
8
+ * and extracts supported model_type values into the model-servers catalog.
9
+ */
10
+
11
+ import { readFileSync, writeFileSync } from 'node:fs';
12
+
13
+ /**
14
+ * Parse vLLM's model registry Python source to extract model_type keys.
15
+ *
16
+ * vLLM's registry maps architecture class names to (module, impl_class) tuples:
17
+ * "LlamaForCausalLM": ("llama", "LlamaForCausalLM"),
18
+ * "Qwen2ForCausalLM": ("qwen2", "Qwen2ForCausalLM"),
19
+ *
20
+ * The module name (first tuple element) corresponds to the model_type.
21
+ * Also matches older formats where model_type is used directly as dict key.
22
+ *
23
+ * @param {string} source - Python source code content
24
+ * @returns {string[]} Sorted array of model_type strings
25
+ */
26
+ export const parseVllmRegistry = (source) => {
27
+ const modelTypes = new Set();
28
+ const patterns = [
29
+ // Tuple value format: ("module_name", "ClassName") — extract module_name
30
+ /\("([a-z][a-z0-9_]*)"\s*,\s*"[A-Z]/g,
31
+ // Direct lowercase key format (older registries): "model_type": (
32
+ /"([a-z][a-z0-9_]*)":\s*\(/g,
33
+ // Direct lowercase key format: "model_type": ClassName
34
+ /"([a-z][a-z0-9_]*)":\s*[A-Z]/g,
35
+ // Direct lowercase key format: "model_type": [
36
+ /"([a-z][a-z0-9_]*)":\s*\[/g
37
+ ];
38
+ for (const pattern of patterns) {
39
+ let match;
40
+ while ((match = pattern.exec(source)) !== null) {
41
+ modelTypes.add(match[1]);
42
+ }
43
+ }
44
+ return [...modelTypes].sort();
45
+ };
46
+
47
+ /**
48
+ * Parse SGLang's model_registry.py to extract model_type keys.
49
+ *
50
+ * Matches patterns like:
51
+ * "llama": ModelClass,
52
+ * "qwen2": (ModulePath, ClassName),
53
+ *
54
+ * @param {string} source - Python source code content
55
+ * @returns {string[]} Sorted array of model_type strings
56
+ */
57
+ export const parseSglangRegistry = (source) => {
58
+ const modelTypes = new Set();
59
+ const patterns = [
60
+ /"([a-z][a-z0-9_]*)":\s*\(/g,
61
+ /"([a-z][a-z0-9_]*)":\s*[A-Z]/g,
62
+ /"([a-z][a-z0-9_]*)":\s*\[/g
63
+ ];
64
+ for (const pattern of patterns) {
65
+ let match;
66
+ while ((match = pattern.exec(source)) !== null) {
67
+ modelTypes.add(match[1]);
68
+ }
69
+ }
70
+ return [...modelTypes].sort();
71
+ };
72
+
73
+ /**
74
+ * Parse TensorRT-LLM's models __init__.py to extract model_type keys.
75
+ *
76
+ * Matches patterns from MODEL_MAP or similar dict structures:
77
+ * "llama": LlamaForCausalLM,
78
+ * "gpt2": GPT2LMHeadModel,
79
+ *
80
+ * @param {string} source - Python source code content
81
+ * @returns {string[]} Sorted array of model_type strings
82
+ */
83
+ export const parseTensorRTRegistry = (source) => {
84
+ const modelTypes = new Set();
85
+ const patterns = [
86
+ /"([a-z][a-z0-9_]*)":\s*[A-Z]/g,
87
+ /"([a-z][a-z0-9_]*)":\s*\(/g,
88
+ /'([a-z][a-z0-9_]*)':\s*[A-Z]/g,
89
+ /'([a-z][a-z0-9_]*)':\s*\(/g
90
+ ];
91
+ for (const pattern of patterns) {
92
+ let match;
93
+ while ((match = pattern.exec(source)) !== null) {
94
+ modelTypes.add(match[1]);
95
+ }
96
+ }
97
+ return [...modelTypes].sort();
98
+ };
99
+
100
+ /**
101
+ * Configuration mapping each server to its GitHub repository,
102
+ * registry file path, tag prefix, and parser function.
103
+ */
104
+ export const SERVER_REGISTRY_SOURCES = {
105
+ vllm: {
106
+ repo: 'vllm-project/vllm',
107
+ file: 'vllm/model_executor/models/registry.py',
108
+ tagPrefix: 'v',
109
+ parser: parseVllmRegistry
110
+ },
111
+ sglang: {
112
+ repo: 'sgl-project/sglang',
113
+ file: 'python/sglang/srt/models/model_registry.py',
114
+ tagPrefix: 'v',
115
+ parser: parseSglangRegistry
116
+ },
117
+ 'tensorrt-llm': {
118
+ repo: 'NVIDIA/TensorRT-LLM',
119
+ file: 'tensorrt_llm/models/__init__.py',
120
+ tagPrefix: 'v',
121
+ parser: parseTensorRTRegistry
122
+ }
123
+ };
124
+
125
+ /**
126
+ * Sync supported model architectures from server GitHub repositories
127
+ * into the model-servers catalog.
128
+ *
129
+ * For each server entry in the catalog that has a matching source config,
130
+ * fetches the model registry file from GitHub at the version tag and
131
+ * parses it to extract supported model_type values.
132
+ *
133
+ * @param {string} catalogPath - Path to model-servers.json
134
+ * @returns {object} Summary with counts and failures
135
+ */
136
+ export const syncArchitectures = async (catalogPath) => {
137
+ const catalog = JSON.parse(readFileSync(catalogPath, 'utf8'));
138
+ const summary = { servers: [], failures: [] };
139
+
140
+ for (const [server, entries] of Object.entries(catalog)) {
141
+ const source = SERVER_REGISTRY_SOURCES[server];
142
+ if (!source) continue;
143
+
144
+ for (const entry of entries) {
145
+ const version = entry.labels?.framework_version;
146
+ if (!version) continue;
147
+
148
+ const tag = `${source.tagPrefix}${version}`;
149
+ const url = `https://raw.githubusercontent.com/${source.repo}/${tag}/${source.file}`;
150
+
151
+ try {
152
+ const response = await fetch(url);
153
+ if (!response.ok) {
154
+ summary.failures.push({ server, version, reason: `HTTP ${response.status}` });
155
+ console.log(` ⚠️ ${server} ${version}: fetch failed (HTTP ${response.status})`);
156
+ continue;
157
+ }
158
+ const content = await response.text();
159
+ entry.supportedModelTypes = source.parser(content);
160
+ summary.servers.push({ server, version, count: entry.supportedModelTypes.length });
161
+ console.log(` ✓ ${server} ${version}: ${entry.supportedModelTypes.length} architectures`);
162
+ } catch (err) {
163
+ summary.failures.push({ server, version, reason: err.message });
164
+ console.log(` ⚠️ ${server} ${version}: fetch failed (${err.message})`);
165
+ }
166
+ }
167
+ }
168
+
169
+ writeFileSync(catalogPath, JSON.stringify(catalog, null, 4));
170
+ return summary;
171
+ };
@@ -0,0 +1,22 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * ARN Detection Utility
6
+ *
7
+ * Provides a pure function for distinguishing AWS Secrets Manager ARNs
8
+ * from plaintext values. Used by the prompt flow and CLI to determine
9
+ * whether user input should be treated as a secret reference or a
10
+ * literal token value.
11
+ */
12
+
13
+ const SECRETS_MANAGER_ARN_PREFIX = 'arn:aws:secretsmanager:';
14
+
15
+ /**
16
+ * Determines if a value is a Secrets Manager ARN.
17
+ * @param {*} value - The input value to check
18
+ * @returns {boolean} True if the value is a Secrets Manager ARN
19
+ */
20
+ export function isSecretsManagerArn(value) {
21
+ return typeof value === 'string' && value.startsWith(SECRETS_MANAGER_ARN_PREFIX);
22
+ }
@@ -24,6 +24,8 @@ import { fileURLToPath } from 'node:url';
24
24
  import BootstrapConfig from './bootstrap-config.js';
25
25
  import AwsProfileParser from './aws-profile-parser.js';
26
26
  import AssetManager from './asset-manager.js';
27
+ import McpCommandHandler from './mcp-command-handler.js';
28
+ import RegistryCommandHandler from './registry-command-handler.js';
27
29
  import { runPrompts } from '../prompt-adapter.js';
28
30
 
29
31
  const __filename = fileURLToPath(import.meta.url);
@@ -45,6 +47,12 @@ export default class BootstrapCommandHandler {
45
47
  * @param {object} options - Parsed CLI options
46
48
  */
47
49
  async handle(args, options) {
50
+ // Handle legacy --sync-schemas flag for backward compatibility
51
+ if (options['sync-schemas']) {
52
+ await this._handleSyncSchemas();
53
+ if (args.length === 0) return;
54
+ }
55
+
48
56
  if (args.length === 0) {
49
57
  await this._handleInteractiveSetup(options);
50
58
  return;
@@ -74,6 +82,9 @@ export default class BootstrapCommandHandler {
74
82
  case 'update':
75
83
  await this._handleUpdate(options);
76
84
  break;
85
+ case 'sync-schemas':
86
+ await this._handleSyncSchemas();
87
+ break;
77
88
  default:
78
89
  console.log(`Unknown bootstrap subcommand: ${subcommand}`);
79
90
  this._showHelp();
@@ -302,6 +313,9 @@ export default class BootstrapCommandHandler {
302
313
 
303
314
  // Display summary
304
315
  this._displaySummary(profileName, profileData);
316
+
317
+ // Step 6: Post-setup chain (mcp init → sync-architectures → sync-schemas)
318
+ await this._runPostSetupChain(options);
305
319
  }
306
320
 
307
321
  /**
@@ -927,6 +941,35 @@ export default class BootstrapCommandHandler {
927
941
  console.log(`\n Done. ${toRemove.length} removed, ${after.length} remaining.`);
928
942
  }
929
943
 
944
+ /**
945
+ * Handle sync-schemas subcommand: download service models and verify AWS CLI.
946
+ */
947
+ async _handleSyncSchemas() {
948
+ console.log('\n📦 Schema Sync — Downloading AWS service models...\n');
949
+
950
+ // Verify AWS CLI is installed
951
+ try {
952
+ const version = execSync('aws --version', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
953
+ console.log(` AWS CLI: ${version}`);
954
+ } catch {
955
+ console.log(' ⚠️ AWS CLI not found.');
956
+ console.log(' Install: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html');
957
+ console.log(' Continuing without AWS CLI verification...\n');
958
+ }
959
+
960
+ // Dynamic import to avoid circular dependencies
961
+ const { syncSchemas } = await import('./schema-sync.js');
962
+ const result = await syncSchemas();
963
+
964
+ if (result.success) {
965
+ console.log('\n ✅ Schema sync complete.');
966
+ } else {
967
+ console.log('\n ⚠️ Schema sync completed with errors (some services may be unavailable).');
968
+ }
969
+
970
+ console.log(` Manifest written: lastSynced = ${result.manifest.lastSynced}\n`);
971
+ }
972
+
930
973
  /**
931
974
  * Re-deploy bootstrap infrastructure using the active profile.
932
975
  * No prompts — reads all config from the existing profile and re-applies
@@ -1016,6 +1059,74 @@ export default class BootstrapCommandHandler {
1016
1059
  // Save updated profile
1017
1060
  this.config.setProfile(name, profileConfig);
1018
1061
  console.log(`\n✅ Update complete for profile "${name}"`);
1062
+
1063
+ // Re-run post-setup chain after updating AWS resources
1064
+ await this._runPostSetupChain(options);
1065
+ }
1066
+
1067
+ /**
1068
+ * Run the post-setup chain: mcp init → registry sync-architectures → sync-schemas.
1069
+ * Each step is independent — failures are collected and reported at the end.
1070
+ *
1071
+ * @param {object} options - Parsed CLI options (checks skipPostSetup)
1072
+ */
1073
+ async _runPostSetupChain(options = {}) {
1074
+ if (options['skip-post-setup']) {
1075
+ console.log('\n⏭️ Skipping post-setup chain (--skip-post-setup)');
1076
+ return;
1077
+ }
1078
+
1079
+ console.log('\n🔗 Running post-setup configuration...\n');
1080
+
1081
+ const failures = [];
1082
+
1083
+ // 1. MCP init — register bundled MCP servers
1084
+ console.log('📡 Registering MCP servers...');
1085
+ try {
1086
+ const generatorAdapter = {
1087
+ destinationPath(...segments) {
1088
+ return path.resolve(process.cwd(), ...segments);
1089
+ }
1090
+ };
1091
+ const mcpHandler = new McpCommandHandler(generatorAdapter);
1092
+ await mcpHandler.handle(['init'], {});
1093
+ } catch (error) {
1094
+ failures.push({ step: 'mcp init', error: error.message });
1095
+ console.log(` ⚠️ mcp init failed: ${error.message}`);
1096
+ }
1097
+
1098
+ // 2. Registry sync-architectures — populate supportedModelTypes
1099
+ console.log('\n📋 Syncing model architecture registry...');
1100
+ try {
1101
+ const registryHandler = new RegistryCommandHandler();
1102
+ await registryHandler.handle(['sync-architectures'], {});
1103
+ } catch (error) {
1104
+ failures.push({ step: 'registry sync-architectures', error: error.message });
1105
+ console.log(` ⚠️ registry sync-architectures failed: ${error.message}`);
1106
+ }
1107
+
1108
+ // 3. Schema sync — download AWS service models
1109
+ console.log('\n📐 Syncing service schemas...');
1110
+ try {
1111
+ await this._handleSyncSchemas();
1112
+ } catch (error) {
1113
+ failures.push({ step: 'sync-schemas', error: error.message });
1114
+ console.log(` ⚠️ sync-schemas failed: ${error.message}`);
1115
+ }
1116
+
1117
+ // Report results
1118
+ if (failures.length === 0) {
1119
+ console.log('\n✅ Bootstrap complete — all systems operational');
1120
+ } else {
1121
+ console.log(`\n⚠️ Bootstrap complete with ${failures.length} warning${failures.length === 1 ? '' : 's'}:`);
1122
+ for (const { step, error } of failures) {
1123
+ console.log(` • ${step}: ${error}`);
1124
+ }
1125
+ console.log('\n These steps can be re-run individually:');
1126
+ console.log(' ml-container-creator mcp init');
1127
+ console.log(' ml-container-creator registry sync-architectures');
1128
+ console.log(' ml-container-creator bootstrap sync-schemas');
1129
+ }
1019
1130
  }
1020
1131
 
1021
1132
  /**
@@ -1204,12 +1315,20 @@ export default class BootstrapCommandHandler {
1204
1315
  Effect: 'Allow',
1205
1316
  Action: [
1206
1317
  's3:GetObject',
1318
+ 's3:PutObject',
1319
+ 's3:AbortMultipartUpload',
1207
1320
  's3:ListBucket'
1208
1321
  ],
1209
1322
  Resource: [
1210
1323
  'arn:aws:s3:::ml-container-creator-*',
1211
1324
  'arn:aws:s3:::ml-container-creator-*/*'
1212
1325
  ]
1326
+ },
1327
+ {
1328
+ Sid: 'SNSPublish',
1329
+ Effect: 'Allow',
1330
+ Action: 'sns:Publish',
1331
+ Resource: 'arn:aws:sns:*:*:ml-container-creator-*'
1213
1332
  }
1214
1333
  ]
1215
1334
  };
@@ -1611,6 +1730,7 @@ SETUP OPTIONS:
1611
1730
  --skip-s3 Skip S3 bucket creation
1612
1731
  --ci Provision CI testing infrastructure
1613
1732
  --skip-ci Skip CI infrastructure provisioning
1733
+ --skip-post-setup Skip post-setup chain (mcp init, sync-architectures, sync-schemas)
1614
1734
 
1615
1735
  STATUS OPTIONS:
1616
1736
  --verify Check each active resource against AWS APIs for drift detection
@@ -280,9 +280,9 @@ TRANSFORMER MODEL EXAMPLES:
280
280
 
281
281
  REGISTRY CONTRIBUTION:
282
282
  To contribute to the catalogs:
283
- - Framework Catalog: servers/base-image-picker/catalogs/model-servers.json
284
- - Model Catalog: servers/model-picker/catalogs/popular-transformers.json
285
- - Instance Catalog: servers/instance-recommender/catalogs/instances.json
283
+ - Framework Catalog: servers/lib/catalogs/model-servers.json
284
+ - Model Catalog: servers/lib/catalogs/popular-transformers.json
285
+ - Instance Catalog: servers/lib/catalogs/instances.json
286
286
 
287
287
  See docs/REGISTRY_CONTRIBUTION_GUIDE.md for detailed contribution guidelines.
288
288
 
@@ -29,7 +29,7 @@ import ParameterSchemaValidator from './parameter-schema-validator.js';
29
29
 
30
30
  const __configMgrFilename = fileURLToPath(import.meta.url);
31
31
  const __configMgrDir = dirname(__configMgrFilename);
32
- const tritonBackendsCatalogPath = resolve(__configMgrDir, '../../servers/base-image-picker/catalogs/triton-backends.json');
32
+ const tritonBackendsCatalogPath = resolve(__configMgrDir, '../../servers/lib/catalogs/triton-backends.json');
33
33
 
34
34
  function loadTritonBackendsFromCatalog() {
35
35
  try {
@@ -300,6 +300,15 @@ export default class ConfigManager {
300
300
  finalConfig.hfToken = this._resolveHfToken(finalConfig.hfToken);
301
301
  }
302
302
 
303
+ // Mutual exclusion: ARN takes precedence over plaintext when both are set
304
+ // (CLI validation should prevent this, but enforce at config level too)
305
+ if (finalConfig.hfTokenArn) {
306
+ finalConfig.hfToken = null;
307
+ }
308
+ if (finalConfig.ngcTokenArn) {
309
+ finalConfig.ngcApiKey = null;
310
+ }
311
+
303
312
  // Map awsRoleArn to roleArn for templates
304
313
  if (finalConfig.awsRoleArn) {
305
314
  finalConfig.roleArn = finalConfig.awsRoleArn;
@@ -643,6 +652,28 @@ export default class ConfigManager {
643
652
  default: null,
644
653
  valueSpace: 'bounded'
645
654
  },
655
+ hfTokenArn: {
656
+ cliOption: 'hf-token-arn',
657
+ envVar: null,
658
+ configFile: true,
659
+ packageJson: false,
660
+ mcp: false,
661
+ promptable: false,
662
+ required: false,
663
+ default: null,
664
+ valueSpace: 'bounded'
665
+ },
666
+ ngcTokenArn: {
667
+ cliOption: 'ngc-token-arn',
668
+ envVar: null,
669
+ configFile: true,
670
+ packageJson: false,
671
+ mcp: false,
672
+ promptable: false,
673
+ required: false,
674
+ default: null,
675
+ valueSpace: 'bounded'
676
+ },
646
677
  deploymentTarget: {
647
678
  cliOption: 'deployment-target',
648
679
  envVar: 'ML_DEPLOYMENT_TARGET',
@@ -1675,6 +1706,18 @@ export default class ConfigManager {
1675
1706
  }
1676
1707
  }
1677
1708
 
1709
+ // Validate mutual exclusion: plaintext token and ARN cannot both be set
1710
+ if (this.config.hfToken && this.config.hfTokenArn) {
1711
+ errors.push('Cannot specify both --hf-token and --hf-token-arn. Use one or the other.');
1712
+ }
1713
+ if (this.config.ngcTokenArn) {
1714
+ // Check ngcToken from CLI options (Commander converts --ngc-token to ngcToken)
1715
+ const ngcTokenFromCli = this.options['ngc-token'];
1716
+ if (ngcTokenFromCli) {
1717
+ errors.push('Cannot specify both --ngc-token and --ngc-token-arn. Use one or the other.');
1718
+ }
1719
+ }
1720
+
1678
1721
  // Validate AWS Role ARN format if provided
1679
1722
  if (this.config.awsRoleArn) {
1680
1723
  try {
@@ -1927,6 +1970,9 @@ export default class ConfigManager {
1927
1970
  // For required parameters: fill auto-generatable values
1928
1971
  if (this.config[param] === undefined || this.config[param] === null) {
1929
1972
  if (param === 'instanceType') {
1973
+ // If instance-sizer is configured and model is known, defer to sizer
1974
+ // The sizer query happens in PromptRunner after model is selected
1975
+ // For now, set a heuristic default that may be overridden by the sizer
1930
1976
  const arch = architecture || 'http';
1931
1977
  this.config[param] = arch === 'http' ? 'ml.m5.large' : 'ml.g5.xlarge';
1932
1978
  } else if (param === 'modelFormat') {
@@ -619,8 +619,8 @@ export default class ConfigurationManager {
619
619
  */
620
620
  _generateSubmissionInstructions(registryType, config, configEntry) {
621
621
  const registryFile = registryType === 'framework'
622
- ? 'servers/base-image-picker/catalogs/model-servers.json'
623
- : 'servers/model-picker/catalogs/popular-transformers.json';
622
+ ? 'servers/lib/catalogs/model-servers.json'
623
+ : 'servers/lib/catalogs/popular-transformers.json';
624
624
 
625
625
  const registryName = registryType === 'framework'
626
626
  ? 'Framework_Catalog'