Skip to content

Commit c9b5fe3

Browse files
committed
fix(import): fail on undetectable entrypoint instead of silent fallback
extractEntrypoint() now returns undefined when it cannot find a file with a known extension (.py/.ts/.js) in the API's entryPoint array, instead of silently falling back to main.py. Adds --entrypoint flag so users can specify the entrypoint manually when auto-detection fails. Constraint: AWS API returns modified entryPoint array with otel wrappers, not original Rejected: Silent fallback to main.py | wrong entrypoint causes silent deploy failures Confidence: high Scope-risk: narrow
1 parent afcaca0 commit c9b5fe3

3 files changed

Lines changed: 43 additions & 18 deletions

File tree

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Import Command
44

5-
- [ ] **Entrypoint detection: fail instead of silent fallback**
5+
- [x] **Entrypoint detection: fail instead of silent fallback**
66
- Currently `extractEntrypoint()` in `import-runtime.ts` silently falls back to `main.py` if it can't determine the
77
entrypoint from the API's modified `entryPoint` array.
88
- Change: fail with a clear error message if auto-detection fails and `--entrypoint` was not provided.

src/cli/commands/import/import-runtime.ts

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,26 @@ const reset = '\x1b[0m';
3030
* The array may contain wrapper commands like "opentelemetry-instrument"
3131
* before the actual Python/TS file (e.g. ["opentelemetry-instrument", "main.py"]).
3232
*/
33-
function extractEntrypoint(entryPoint?: string[]): string {
34-
if (!entryPoint || entryPoint.length === 0) return 'main.py';
33+
function extractEntrypoint(entryPoint?: string[]): string | undefined {
34+
if (!entryPoint || entryPoint.length === 0) return undefined;
3535
// Find the first entry that looks like a source file
36-
const sourceFile = entryPoint.find(e => /\.(py|ts|js)$/.test(e));
37-
return sourceFile ?? entryPoint[entryPoint.length - 1] ?? 'main.py';
36+
return entryPoint.find(e => /\.(py|ts|js)$/.test(e));
3837
}
3938

4039
/**
4140
* Map an AWS GetAgentRuntime response to the CLI AgentEnvSpec format.
4241
*/
43-
function toAgentEnvSpec(runtime: AgentRuntimeDetail, localName: string, codeLocation: string): AgentEnvSpec {
42+
function toAgentEnvSpec(
43+
runtime: AgentRuntimeDetail,
44+
localName: string,
45+
codeLocation: string,
46+
entrypoint: string
47+
): AgentEnvSpec {
4448
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any */
4549
const spec: AgentEnvSpec = {
4650
name: localName,
4751
build: runtime.build,
48-
entrypoint: extractEntrypoint(runtime.entryPoint) as any,
52+
entrypoint: entrypoint as any,
4953
codeLocation: codeLocation as any,
5054
runtimeVersion: (runtime.runtimeVersion ?? 'PYTHON_3_12') as any,
5155
protocol: runtime.protocol as any,
@@ -154,7 +158,26 @@ export async function handleImportRuntime(options: ImportResourceOptions): Promi
154158
onProgress(`Runtime: ${runtimeDetail.agentRuntimeName} → local name: ${localName}`);
155159
logger.endStep('success');
156160

157-
// 4. Validate source path
161+
// 4. Resolve entrypoint
162+
logger.startStep('Resolve entrypoint');
163+
const entrypoint = options.entrypoint ?? extractEntrypoint(runtimeDetail.entryPoint);
164+
if (!entrypoint) {
165+
const error =
166+
'Could not determine entrypoint from runtime configuration.\n Please re-run with --entrypoint <file> to specify it manually.';
167+
logger.endStep('error', error);
168+
logger.finalize(false);
169+
return {
170+
success: false,
171+
error,
172+
resourceType: 'runtime',
173+
resourceName: localName,
174+
logPath: logger.getRelativeLogPath(),
175+
};
176+
}
177+
onProgress(`Entrypoint: ${entrypoint}`);
178+
logger.endStep('success');
179+
180+
// 5. Validate source path
158181
logger.startStep('Validate source path');
159182
if (!options.code) {
160183
const error =
@@ -185,7 +208,7 @@ export async function handleImportRuntime(options: ImportResourceOptions): Promi
185208
}
186209
logger.endStep('success');
187210

188-
// 5. Check for duplicates
211+
// 6. Check for duplicates
189212
logger.startStep('Check for duplicates');
190213
const projectSpec = await ctx.configIO.readProjectSpec();
191214
const existingNames = new Set(projectSpec.runtimes.map(r => r.name));
@@ -203,28 +226,28 @@ export async function handleImportRuntime(options: ImportResourceOptions): Promi
203226
}
204227
logger.endStep('success');
205228

206-
// 6. Copy source code
229+
// 7. Copy source code
207230
logger.startStep('Copy agent source');
208231
const codeLocation = `app/${localName}/`;
209232
await copyAgentSource({
210233
sourcePath,
211234
agentName: localName,
212235
projectRoot: ctx.projectRoot,
213236
build: runtimeDetail.build,
214-
entrypoint: extractEntrypoint(runtimeDetail.entryPoint),
237+
entrypoint,
215238
onProgress,
216239
});
217240
logger.endStep('success');
218241

219-
// 7. Add to project config
242+
// 8. Add to project config
220243
logger.startStep('Update project config');
221-
const agentSpec = toAgentEnvSpec(runtimeDetail, localName, codeLocation);
244+
const agentSpec = toAgentEnvSpec(runtimeDetail, localName, codeLocation, entrypoint);
222245
projectSpec.runtimes.push(agentSpec);
223246
await ctx.configIO.writeProjectSpec(projectSpec);
224247
onProgress(`Added runtime "${localName}" to agentcore.json`);
225248
logger.endStep('success');
226249

227-
// 8. Build and synth CDK
250+
// 9. Build and synth CDK
228251
logger.startStep('Build and synth CDK');
229252
onProgress('Building CDK project...');
230253
const cdkProject = new LocalCdkProject(ctx.projectRoot);
@@ -273,13 +296,13 @@ export async function handleImportRuntime(options: ImportResourceOptions): Promi
273296
await toolkitWrapper.dispose();
274297
logger.endStep('success');
275298

276-
// 9. Publish CDK assets
299+
// 10. Publish CDK assets
277300
logger.startStep('Publish CDK assets');
278301
onProgress('Publishing CDK assets to S3...');
279302
await publishCdkAssets(assemblyDirectory, target.region, onProgress);
280303
logger.endStep('success');
281304

282-
// 10. Phase 1: Deploy companion resources
305+
// 11. Phase 1: Deploy companion resources
283306
logger.startStep('Phase 1: Deploy companion resources');
284307
onProgress('Phase 1: Deploying companion resources (IAM roles, policies)...');
285308
const phase1Result = await executePhase1({
@@ -303,7 +326,7 @@ export async function handleImportRuntime(options: ImportResourceOptions): Promi
303326
}
304327
logger.endStep('success');
305328

306-
// 11. Phase 2: Import the runtime resource
329+
// 12. Phase 2: Import the runtime resource
307330
logger.startStep('Phase 2: Import runtime resource');
308331
onProgress('Reading deployed template...');
309332
const deployedTemplate = await getDeployedTemplate(target.region, stackName);
@@ -382,7 +405,7 @@ export async function handleImportRuntime(options: ImportResourceOptions): Promi
382405
}
383406
logger.endStep('success');
384407

385-
// 12. Update deployed state
408+
// 13. Update deployed state
386409
logger.startStep('Update deployed state');
387410
await updateDeployedState(ctx.configIO, targetName, stackName, [
388411
{
@@ -426,6 +449,7 @@ export function registerImportRuntime(importCmd: Command): void {
426449
.description('Import an existing AgentCore Runtime from your AWS account')
427450
.option('--id <runtimeId>', 'Runtime ID to import')
428451
.requiredOption('--code <path>', 'Path to the agent source code directory')
452+
.option('--entrypoint <file>', 'Entrypoint file (auto-detected from runtime, e.g. main.py)')
429453
.option('--target <target>', 'Deployment target name')
430454
.option('--name <name>', 'Local name for the imported runtime')
431455
.option('-y, --yes', 'Auto-confirm prompts')

src/cli/commands/import/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,6 @@ export interface ImportResourceOptions {
105105
code?: string;
106106
target?: string;
107107
name?: string;
108+
entrypoint?: string;
108109
yes?: boolean;
109110
}

0 commit comments

Comments
 (0)