Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d555e26
feat: add agentcore import command for starter toolkit migration
jesseturner21 Mar 19, 2026
0117149
refactor: require existing project for import, fix memory support
jesseturner21 Mar 19, 2026
9032c8d
fix: set up Python venv after import and fix setuptools auto-discovery
jesseturner21 Mar 19, 2026
31de3d7
feat: import credential providers from starter toolkit YAML
jesseturner21 Mar 19, 2026
1a345fb
fix: guard memory mode check with typeof string to prevent empty YAML…
jesseturner21 Mar 19, 2026
6f62b33
fix: use Array.isArray guard for VPC networkConfig subnets/securityGr…
jesseturner21 Mar 19, 2026
1115349
fix: make import idempotent by only importing newly-added resources
jesseturner21 Mar 19, 2026
4ee5f69
fix: defer target resolution for undeployed starter toolkit imports
jesseturner21 Mar 19, 2026
da0b448
test: add Test Group 1 unit tests for no-memory agent import path
jesseturner21 Mar 19, 2026
ccb6891
test: add 30 unit tests for multi-agent import scenarios
jesseturner21 Mar 19, 2026
633d971
Revert "test: add 30 unit tests for multi-agent import scenarios"
jesseturner21 Mar 19, 2026
382b221
test: add merge-logic unit tests for CLI-native create then import sc…
jesseturner21 Mar 19, 2026
64522bb
test: add merge-logic unit tests for CLI-native create then import sc…
jesseturner21 Mar 19, 2026
225f146
Fix import for container builds and multi-agent stacks
jesseturner21 Mar 20, 2026
66be002
fix(import): map OAuth providers correctly and fix region mismatch
jesseturner21 Mar 22, 2026
ee7b5c4
fix(import): handle symlinks and excluded dirs in source copy, warn o…
jesseturner21 Mar 23, 2026
45e3729
fix(import): fix multi-agent memory import and skip Python setup for …
jesseturner21 Mar 23, 2026
1ee57d1
fix(build): add jsx: automatic to esbuild config to fix TUI crash
jesseturner21 Mar 24, 2026
89fb271
fix(import): warn about memory env var mismatch after import
jesseturner21 Mar 24, 2026
da37718
fix(import): warn about memory env var mismatch with diff-style hint
jesseturner21 Mar 24, 2026
bd7f3cb
fix(tui): hide import command from interactive TUI menu
jesseturner21 Mar 24, 2026
7e0149b
fix(import): auto-bootstrap CDK environment before asset publishing
jesseturner21 Mar 24, 2026
d60f798
fix(import): resolve lint and format issues in import files
jesseturner21 Mar 24, 2026
f5e7d0d
fix(tests): update tests for optional OAuth discoveryUrl schema change
jesseturner21 Mar 24, 2026
8518add
fix(tests): hoist bootstrap mocks in idempotency test to survive clea…
jesseturner21 Mar 24, 2026
75a7b3d
logs
jesseturner21 Mar 26, 2026
66a44f0
fix(import): rollback config on CDK/CloudFormation phase failure
jesseturner21 Mar 26, 2026
d9257db
fix(import): map STM_ONLY memory to zero strategies
jesseturner21 Mar 26, 2026
c9cca83
feat(import): extract custom JWT authorizer config from starter toolk…
jesseturner21 Mar 26, 2026
955c886
fix: add yaml@2 and regenerate package-lock.json for CI compatibility
jesseturner21 Mar 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ await esbuild.build({
platform: 'node',
format: 'esm',
minify: true,
jsx: 'automatic',
// Inject require shim for ESM compatibility with CommonJS dependencies
banner: {
js: `import { createRequire } from 'module'; const require = createRequire(import.meta.url);`,
Expand Down
4,402 changes: 2,257 additions & 2,145 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,15 @@
"ink-spinner": "^5.0.0",
"js-yaml": "^4.1.1",
"react": "^19.2.3",
"yaml": "^2.8.3",
"zod": "^4.3.5"
},
"peerDependencies": {
"aws-cdk-lib": "^2.243.0",
"constructs": "^10.0.0"
},
"devDependencies": {
"@aws-sdk/client-cognito-identity-provider": "^3.1017.0",
"@aws-sdk/client-cognito-identity-provider": "^3.1018.0",
"@eslint/js": "^9.39.2",
"@modelcontextprotocol/sdk": "^1.0.0",
"@secretlint/secretlint-rule-preset-recommend": "^11.3.0",
Expand Down
2 changes: 2 additions & 0 deletions src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { registerDev } from './commands/dev';
import { registerEval } from './commands/eval';
import { registerFetch } from './commands/fetch';
import { registerHelp } from './commands/help';
import { registerImport } from './commands/import';
import { registerInvoke } from './commands/invoke';
import { registerLogs } from './commands/logs';
import { registerPackage } from './commands/package';
Expand Down Expand Up @@ -138,6 +139,7 @@ export function registerCommands(program: Command) {
registerEval(program);
registerFetch(program);
registerHelp(program);
registerImport(program);
registerInvoke(program);
registerLogs(program);
registerPackage(program);
Expand Down
268 changes: 268 additions & 0 deletions src/cli/commands/import/__tests__/container-agent-import.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
/**
* Test Group 6: Container (Docker) Agent Import
*/
import { RUNTIME_TYPE_MAP } from '../constants';
import { buildImportTemplate, filterCompanionOnlyTemplate } from '../template-utils';
import { parseStarterToolkitYaml } from '../yaml-parser';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';

function writeTempYaml(content: string): string {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'test6-'));
const filePath = path.join(dir, '.bedrock_agentcore.yaml');
fs.writeFileSync(filePath, content, 'utf-8');
return filePath;
}

function cleanupTempFile(filePath: string): void {
try {
fs.unlinkSync(filePath);
fs.rmdirSync(path.dirname(filePath));
} catch {
/* noop */
}
}

const AGENT_YAML_TEMPLATE = (overrides: string) => `
default_agent: my_agent
agents:
my_agent:
name: my_agent
entrypoint: main.py
${overrides}
aws:
account: '111122223333'
region: us-east-1
network_configuration:
network_mode: PUBLIC
protocol_configuration:
server_protocol: HTTP
observability:
enabled: true
bedrock_agentcore:
agent_id: null
`;

describe('deployment_type mapping', () => {
const tempFiles: string[] = [];
afterEach(() => {
for (const f of tempFiles) cleanupTempFile(f);
tempFiles.length = 0;
});

it('container -> Container', () => {
const f = writeTempYaml(AGENT_YAML_TEMPLATE('deployment_type: container\n runtime_type: PYTHON_3_12'));
tempFiles.push(f);
expect(parseStarterToolkitYaml(f).agents[0]!.build).toBe('Container');
});

it('direct_code_deploy -> CodeZip', () => {
const f = writeTempYaml(AGENT_YAML_TEMPLATE('deployment_type: direct_code_deploy\n runtime_type: PYTHON_3_12'));
tempFiles.push(f);
expect(parseStarterToolkitYaml(f).agents[0]!.build).toBe('CodeZip');
});

it('missing -> Container (default)', () => {
const f = writeTempYaml(AGENT_YAML_TEMPLATE('runtime_type: PYTHON_3_12'));
tempFiles.push(f);
expect(parseStarterToolkitYaml(f).agents[0]!.build).toBe('Container');
});
});

describe('runtime_type handling', () => {
const tempFiles: string[] = [];
afterEach(() => {
for (const f of tempFiles) cleanupTempFile(f);
tempFiles.length = 0;
});

it('null -> PYTHON_3_12', () => {
const f = writeTempYaml(AGENT_YAML_TEMPLATE('deployment_type: container\n runtime_type: null'));
tempFiles.push(f);
expect(parseStarterToolkitYaml(f).agents[0]!.runtimeVersion).toBe('PYTHON_3_12');
});

it('missing -> PYTHON_3_12', () => {
const f = writeTempYaml(AGENT_YAML_TEMPLATE('deployment_type: container'));
tempFiles.push(f);
expect(parseStarterToolkitYaml(f).agents[0]!.runtimeVersion).toBe('PYTHON_3_12');
});

it('PYTHON_3_13 -> PYTHON_3_13', () => {
const f = writeTempYaml(AGENT_YAML_TEMPLATE('deployment_type: container\n runtime_type: PYTHON_3_13'));
tempFiles.push(f);
expect(parseStarterToolkitYaml(f).agents[0]!.runtimeVersion).toBe('PYTHON_3_13');
});

it('unrecognized -> PYTHON_3_12 (not python3.12)', () => {
const f = writeTempYaml(AGENT_YAML_TEMPLATE('deployment_type: container\n runtime_type: some_unknown'));
tempFiles.push(f);
const rv = parseStarterToolkitYaml(f).agents[0]!.runtimeVersion;
expect(rv).toBe('PYTHON_3_12');
expect(rv).not.toBe('python3.12');
});
});

describe('RUNTIME_TYPE_MAP', () => {
it('maps known types', () => {
expect(RUNTIME_TYPE_MAP.PYTHON_3_10).toBe('PYTHON_3_10');
expect(RUNTIME_TYPE_MAP.PYTHON_3_11).toBe('PYTHON_3_11');
expect(RUNTIME_TYPE_MAP.PYTHON_3_12).toBe('PYTHON_3_12');
expect(RUNTIME_TYPE_MAP.PYTHON_3_13).toBe('PYTHON_3_13');
});

it('undefined for invalid keys', () => {
expect(RUNTIME_TYPE_MAP['null' as keyof typeof RUNTIME_TYPE_MAP]).toBeUndefined();
expect(RUNTIME_TYPE_MAP['undefined' as keyof typeof RUNTIME_TYPE_MAP]).toBeUndefined();
expect(RUNTIME_TYPE_MAP['python_3_12' as keyof typeof RUNTIME_TYPE_MAP]).toBeUndefined();
});
});

describe('full container agent parse', () => {
const tempFiles: string[] = [];
afterEach(() => {
for (const f of tempFiles) cleanupTempFile(f);
tempFiles.length = 0;
});

it('parses complete container agent with agent_id', () => {
const yaml = `
default_agent: container_agent
agents:
container_agent:
name: container_agent
entrypoint: main.py
deployment_type: container
runtime_type: null
language: python
aws:
account: '123456789012'
region: us-west-2
network_configuration:
network_mode: PUBLIC
protocol_configuration:
server_protocol: HTTP
observability:
enabled: true
bedrock_agentcore:
agent_id: abc123def456
agent_arn: arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/abc123def456
`;
const f = writeTempYaml(yaml);
tempFiles.push(f);
const parsed = parseStarterToolkitYaml(f);
const agent = parsed.agents[0]!;
expect(agent.build).toBe('Container');
expect(agent.runtimeVersion).toBe('PYTHON_3_12');
expect(agent.physicalAgentId).toBe('abc123def456');
expect(parsed.awsTarget.account).toBe('123456789012');
});

it('parses container agent with VPC', () => {
const yaml = `
default_agent: vpc_agent
agents:
vpc_agent:
name: vpc_agent
entrypoint: main.py
deployment_type: container
runtime_type: null
aws:
account: '123456789012'
region: us-east-1
network_configuration:
network_mode: VPC
network_mode_config:
subnets:
- subnet-12345678
security_groups:
- sg-11112222
protocol_configuration:
server_protocol: MCP
observability:
enabled: false
bedrock_agentcore:
agent_id: null
`;
const f = writeTempYaml(yaml);
tempFiles.push(f);
const agent = parseStarterToolkitYaml(f).agents[0]!;
expect(agent.build).toBe('Container');
expect(agent.networkMode).toBe('VPC');
expect(agent.networkConfig!.subnets).toContain('subnet-12345678');
expect(agent.protocol).toBe('MCP');
expect(agent.enableOtel).toBe(false);
});
});

describe('import template for container agents', () => {
it('buildImportTemplate sets DeletionPolicy: Retain', () => {
const deployed = {
AWSTemplateFormatVersion: '2010-09-09',
Resources: { Role: { Type: 'AWS::IAM::Role', Properties: {} } },
};
const synth = {
AWSTemplateFormatVersion: '2010-09-09',
Resources: {
Role: { Type: 'AWS::IAM::Role', Properties: {} },
RT: { Type: 'AWS::BedrockAgentCore::Runtime', Properties: { AgentRuntimeName: 'x' }, DependsOn: ['CR'] },
CR: { Type: 'AWS::CloudFormation::CustomResource', Properties: {} },
},
};
const result = buildImportTemplate(deployed, synth, ['RT']);
expect(result.Resources.RT).toBeDefined();
expect(result.Resources.RT!.DeletionPolicy).toBe('Retain');
expect(result.Resources.RT!.DependsOn).toBeUndefined();
expect(result.Resources.CR).toBeUndefined();
});

it('filterCompanionOnlyTemplate removes primary resources', () => {
const synth = {
AWSTemplateFormatVersion: '2010-09-09',
Resources: {
Role: { Type: 'AWS::IAM::Role', Properties: {} },
RT: { Type: 'AWS::BedrockAgentCore::Runtime', Properties: {} },
Lambda: { Type: 'AWS::Lambda::Function', Properties: {} },
},
Outputs: {
RTId: { Value: { 'Fn::GetAtt': ['RT', 'AgentRuntimeId'] } },
LambdaArn: { Value: { 'Fn::GetAtt': ['Lambda', 'Arn'] } },
},
};
const filtered = filterCompanionOnlyTemplate(synth);
expect(filtered.Resources.RT).toBeUndefined();
expect(filtered.Resources.Role).toBeDefined();
expect(filtered.Resources.Lambda).toBeDefined();
expect(filtered.Outputs!.RTId).toBeUndefined();
expect(filtered.Outputs!.LambdaArn).toBeDefined();
});
});

describe('container source code', () => {
let tempDir: string;
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test6-src-'));
});
afterEach(() => {
fs.rmSync(tempDir, { recursive: true, force: true });
});

it('may contain Dockerfile', () => {
fs.writeFileSync(path.join(tempDir, 'Dockerfile'), 'FROM python:3.12\n');
fs.writeFileSync(path.join(tempDir, 'main.py'), 'print("hi")');
expect(fs.readdirSync(tempDir)).toContain('Dockerfile');
});

it('may lack pyproject.toml', () => {
fs.writeFileSync(path.join(tempDir, 'Dockerfile'), 'FROM python:3.12\n');
expect(fs.existsSync(path.join(tempDir, 'pyproject.toml'))).toBe(false);
});
});

describe('defaults alignment', () => {
it('CLI default matches starter toolkit default', () => {
expect('container').toBe('container');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "MyProject",
"version": 1,
"agents": [
{
"type": "AgentCoreRuntime",
"name": "existing_agent",
"build": "CodeZip",
"entrypoint": "main.py",
"codeLocation": "app/existing_agent",
"runtimeVersion": "PYTHON_3_12",
"protocol": "HTTP",
"networkMode": "PUBLIC",
"instrumentation": {
"enableOtel": true
}
}
],
"memories": [
{
"type": "AgentCoreMemory",
"name": "existing_agent_memory",
"eventExpiryDuration": 30,
"strategies": [
{
"type": "SEMANTIC"
}
]
}
],
"credentials": [
{
"type": "ApiKeyCredentialProvider",
"name": "my_api_key"
}
],
"evaluators": [],
"onlineEvalConfigs": []
}
28 changes: 28 additions & 0 deletions src/cli/commands/import/__tests__/fixtures/different-agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
default_agent: new_toolkit_agent
agents:
new_toolkit_agent:
name: new_toolkit_agent
entrypoint: main.py
deployment_type: direct_code_deploy
runtime_type: PYTHON_3_12
source_path: null
aws:
account: '123456789012'
region: us-west-2
network_configuration:
network_mode: PUBLIC
protocol_configuration:
server_protocol: HTTP
observability:
enabled: true
bedrock_agentcore:
agent_id: AGENT_NEW_123
agent_arn: arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/AGENT_NEW_123
agent_session_id: null
memory:
mode: STM_AND_LTM
memory_id: MEM_NEW_456
memory_arn: arn:aws:bedrock-agentcore:us-west-2:123456789012:memory/MEM_NEW_456
memory_name: new_toolkit_memory
event_expiry_days: 60
api_key_credential_provider_name: new_api_key_cred
Loading
Loading