agentcore-cli 0.31.1__py3-none-any.whl
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.
- agentcore/__init__.py +1 -0
- agentcore/agent_inspector/NOTICE +9 -0
- agentcore/agent_inspector/favicon.svg +60 -0
- agentcore/agent_inspector/index.css +1 -0
- agentcore/agent_inspector/index.html +14 -0
- agentcore/agent_inspector/index.js +289 -0
- agentcore/assets/AGENTS.md +60 -0
- agentcore/assets/README.md +108 -0
- agentcore/assets/agents/AGENTS.md +218 -0
- agentcore/assets/cdk/.prettierrc +8 -0
- agentcore/assets/cdk/README.md +26 -0
- agentcore/assets/cdk/bin/cdk.ts +216 -0
- agentcore/assets/cdk/cdk.json +88 -0
- agentcore/assets/cdk/gitignore.template +9 -0
- agentcore/assets/cdk/jest.config.js +9 -0
- agentcore/assets/cdk/lib/cdk-stack.ts +291 -0
- agentcore/assets/cdk/npmignore.template +6 -0
- agentcore/assets/cdk/package.json +30 -0
- agentcore/assets/cdk/test/cdk.test.ts +31 -0
- agentcore/assets/cdk/tsconfig.json +28 -0
- agentcore/assets/container/python/Dockerfile +47 -0
- agentcore/assets/container/python/dockerignore.template +27 -0
- agentcore/assets/container/typescript/Dockerfile +25 -0
- agentcore/assets/container/typescript/dockerignore.template +24 -0
- agentcore/assets/datasets/predefined-v1.jsonl +3 -0
- agentcore/assets/datasets/simulated-v1.jsonl +3 -0
- agentcore/assets/evaluators/autoevals-lambda/execution-role-policy.json +15 -0
- agentcore/assets/evaluators/autoevals-lambda/lambda_function.py +37 -0
- agentcore/assets/evaluators/autoevals-lambda/pyproject.toml +22 -0
- agentcore/assets/evaluators/deepeval-lambda/execution-role-policy.json +15 -0
- agentcore/assets/evaluators/deepeval-lambda/lambda_function.py +29 -0
- agentcore/assets/evaluators/deepeval-lambda/pyproject.toml +19 -0
- agentcore/assets/evaluators/python-lambda/execution-role-policy.json +10 -0
- agentcore/assets/evaluators/python-lambda/lambda_function.py +19 -0
- agentcore/assets/evaluators/python-lambda/pyproject.toml +15 -0
- agentcore/assets/harness/invoke.py.template +74 -0
- agentcore/assets/mcp/python/README.md +27 -0
- agentcore/assets/mcp/python/pyproject.toml +22 -0
- agentcore/assets/mcp/python/server.py +117 -0
- agentcore/assets/mcp/python-lambda/README.md +22 -0
- agentcore/assets/mcp/python-lambda/handler.py +144 -0
- agentcore/assets/mcp/python-lambda/pyproject.toml +15 -0
- agentcore/assets/python/a2a/googleadk/base/README.md +22 -0
- agentcore/assets/python/a2a/googleadk/base/gitignore.template +41 -0
- agentcore/assets/python/a2a/googleadk/base/main.py +124 -0
- agentcore/assets/python/a2a/googleadk/base/model/__init__.py +1 -0
- agentcore/assets/python/a2a/googleadk/base/model/load.py +41 -0
- agentcore/assets/python/a2a/googleadk/base/pyproject.toml +22 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/README.md +22 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/gitignore.template +41 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/main.py +144 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/model/__init__.py +1 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/model/load.py +123 -0
- agentcore/assets/python/a2a/langchain_langgraph/base/pyproject.toml +26 -0
- agentcore/assets/python/a2a/strands/base/README.md +22 -0
- agentcore/assets/python/a2a/strands/base/gitignore.template +41 -0
- agentcore/assets/python/a2a/strands/base/main.py +113 -0
- agentcore/assets/python/a2a/strands/base/model/__init__.py +1 -0
- agentcore/assets/python/a2a/strands/base/model/load.py +123 -0
- agentcore/assets/python/a2a/strands/base/pyproject.toml +23 -0
- agentcore/assets/python/a2a/strands/capabilities/memory/__init__.py +1 -0
- agentcore/assets/python/a2a/strands/capabilities/memory/session.py +46 -0
- agentcore/assets/python/agui/googleadk/base/README.md +30 -0
- agentcore/assets/python/agui/googleadk/base/gitignore.template +41 -0
- agentcore/assets/python/agui/googleadk/base/main.py +91 -0
- agentcore/assets/python/agui/googleadk/base/model/__init__.py +1 -0
- agentcore/assets/python/agui/googleadk/base/model/load.py +41 -0
- agentcore/assets/python/agui/googleadk/base/pyproject.toml +25 -0
- agentcore/assets/python/agui/langchain_langgraph/base/README.md +22 -0
- agentcore/assets/python/agui/langchain_langgraph/base/gitignore.template +41 -0
- agentcore/assets/python/agui/langchain_langgraph/base/main.py +130 -0
- agentcore/assets/python/agui/langchain_langgraph/base/model/__init__.py +1 -0
- agentcore/assets/python/agui/langchain_langgraph/base/model/load.py +123 -0
- agentcore/assets/python/agui/langchain_langgraph/base/pyproject.toml +31 -0
- agentcore/assets/python/agui/strands/base/README.md +22 -0
- agentcore/assets/python/agui/strands/base/gitignore.template +41 -0
- agentcore/assets/python/agui/strands/base/main.py +105 -0
- agentcore/assets/python/agui/strands/base/model/__init__.py +1 -0
- agentcore/assets/python/agui/strands/base/model/load.py +123 -0
- agentcore/assets/python/agui/strands/base/pyproject.toml +27 -0
- agentcore/assets/python/agui/strands/capabilities/memory/__init__.py +1 -0
- agentcore/assets/python/agui/strands/capabilities/memory/session.py +46 -0
- agentcore/assets/python/http/autogen/base/README.md +45 -0
- agentcore/assets/python/http/autogen/base/gitignore.template +41 -0
- agentcore/assets/python/http/autogen/base/main.py +145 -0
- agentcore/assets/python/http/autogen/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/autogen/base/mcp_client/client.py +28 -0
- agentcore/assets/python/http/autogen/base/model/__init__.py +1 -0
- agentcore/assets/python/http/autogen/base/model/load.py +136 -0
- agentcore/assets/python/http/autogen/base/pyproject.toml +34 -0
- agentcore/assets/python/http/bma/base/Dockerfile +41 -0
- agentcore/assets/python/http/bma/base/README.md +178 -0
- agentcore/assets/python/http/bma/base/client.py +200 -0
- agentcore/assets/python/http/bma/base/lifecycle/server.py +641 -0
- agentcore/assets/python/http/bma/base/otel/collector.yaml +69 -0
- agentcore/assets/python/http/bma/base/plugins/acr-report/.codex-plugin/plugin.json +6 -0
- agentcore/assets/python/http/bma/base/plugins/acr-report/skills/acr-report/SKILL.md +16 -0
- agentcore/assets/python/http/bma/base/policies/bma-acr-policy.json +11 -0
- agentcore/assets/python/http/bma/base/policies/bma-session-policy.json +35 -0
- agentcore/assets/python/http/bma/base/policies/bma-session-trust.json +20 -0
- agentcore/assets/python/http/bma/base/pyproject.toml +21 -0
- agentcore/assets/python/http/googleadk/base/README.md +45 -0
- agentcore/assets/python/http/googleadk/base/gitignore.template +41 -0
- agentcore/assets/python/http/googleadk/base/main.py +207 -0
- agentcore/assets/python/http/googleadk/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/googleadk/base/mcp_client/client.py +75 -0
- agentcore/assets/python/http/googleadk/base/model/__init__.py +1 -0
- agentcore/assets/python/http/googleadk/base/model/load.py +41 -0
- agentcore/assets/python/http/googleadk/base/pyproject.toml +24 -0
- agentcore/assets/python/http/langchain_langgraph/base/README.md +45 -0
- agentcore/assets/python/http/langchain_langgraph/base/gitignore.template +41 -0
- agentcore/assets/python/http/langchain_langgraph/base/main.py +227 -0
- agentcore/assets/python/http/langchain_langgraph/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/langchain_langgraph/base/mcp_client/client.py +79 -0
- agentcore/assets/python/http/langchain_langgraph/base/model/__init__.py +1 -0
- agentcore/assets/python/http/langchain_langgraph/base/model/load.py +123 -0
- agentcore/assets/python/http/langchain_langgraph/base/pyproject.toml +38 -0
- agentcore/assets/python/http/openaiagents/base/README.md +45 -0
- agentcore/assets/python/http/openaiagents/base/gitignore.template +41 -0
- agentcore/assets/python/http/openaiagents/base/main.py +200 -0
- agentcore/assets/python/http/openaiagents/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/openaiagents/base/mcp_client/client.py +74 -0
- agentcore/assets/python/http/openaiagents/base/model/__init__.py +1 -0
- agentcore/assets/python/http/openaiagents/base/model/load.py +37 -0
- agentcore/assets/python/http/openaiagents/base/pyproject.toml +21 -0
- agentcore/assets/python/http/strands/base/README.md +46 -0
- agentcore/assets/python/http/strands/base/gitignore.template +41 -0
- agentcore/assets/python/http/strands/base/main.py +714 -0
- agentcore/assets/python/http/strands/base/mcp_client/__init__.py +1 -0
- agentcore/assets/python/http/strands/base/mcp_client/client.py +116 -0
- agentcore/assets/python/http/strands/base/model/__init__.py +1 -0
- agentcore/assets/python/http/strands/base/model/load.py +239 -0
- agentcore/assets/python/http/strands/base/model/mantle_compat.py +21 -0
- agentcore/assets/python/http/strands/base/pyproject.toml +31 -0
- agentcore/assets/python/http/strands/base/skills/fetcher.py +279 -0
- agentcore/assets/python/http/strands/capabilities/execution-limits/hooks/execution_limits.py +54 -0
- agentcore/assets/python/http/strands/capabilities/memory/__init__.py +1 -0
- agentcore/assets/python/http/strands/capabilities/memory/session.py +47 -0
- agentcore/assets/python/http/strands/capabilities/payments/__init__.py +4 -0
- agentcore/assets/python/http/strands/capabilities/payments/payments.py +142 -0
- agentcore/assets/python/mcp/standalone/base/README.md +36 -0
- agentcore/assets/python/mcp/standalone/base/gitignore.template +41 -0
- agentcore/assets/python/mcp/standalone/base/main.py +83 -0
- agentcore/assets/python/mcp/standalone/base/pyproject.toml +16 -0
- agentcore/assets/typescript/.gitkeep +0 -0
- agentcore/assets/typescript/http/strands/base/README.md +42 -0
- agentcore/assets/typescript/http/strands/base/gitignore.template +22 -0
- agentcore/assets/typescript/http/strands/base/main.ts +149 -0
- agentcore/assets/typescript/http/strands/base/mcp_client/client.ts +11 -0
- agentcore/assets/typescript/http/strands/base/model/load.ts +102 -0
- agentcore/assets/typescript/http/strands/base/package.json +38 -0
- agentcore/assets/typescript/http/strands/base/tsconfig.json +19 -0
- agentcore/assets/typescript/http/strands/capabilities/memory/memory.ts +52 -0
- agentcore/assets/typescript/http/vercelai/base/README.md +42 -0
- agentcore/assets/typescript/http/vercelai/base/gitignore.template +22 -0
- agentcore/assets/typescript/http/vercelai/base/main.ts +70 -0
- agentcore/assets/typescript/http/vercelai/base/model/load.ts +121 -0
- agentcore/assets/typescript/http/vercelai/base/package.json +35 -0
- agentcore/assets/typescript/http/vercelai/base/tsconfig.json +19 -0
- agentcore/aws/__init__.py +263 -0
- agentcore/aws/_aws_errors.py +72 -0
- agentcore/aws/_http.py +176 -0
- agentcore/aws/_util.py +37 -0
- agentcore/aws/account.py +165 -0
- agentcore/aws/agentcore.py +971 -0
- agentcore/aws/agentcore_ab_tests.py +230 -0
- agentcore/aws/agentcore_batch_evaluation.py +227 -0
- agentcore/aws/agentcore_config_bundles.py +220 -0
- agentcore/aws/agentcore_control.py +994 -0
- agentcore/aws/agentcore_datasets.py +222 -0
- agentcore/aws/agentcore_harness.py +330 -0
- agentcore/aws/agentcore_payments.py +430 -0
- agentcore/aws/agentcore_recommendation.py +140 -0
- agentcore/aws/agui_parser.py +131 -0
- agentcore/aws/agui_types.py +342 -0
- agentcore/aws/api_client.py +87 -0
- agentcore/aws/arn.py +18 -0
- agentcore/aws/aws_context.py +23 -0
- agentcore/aws/bedrock.py +44 -0
- agentcore/aws/bedrock_agent.py +129 -0
- agentcore/aws/bedrock_import.py +252 -0
- agentcore/aws/bedrock_import_types.py +111 -0
- agentcore/aws/cloudwatch.py +136 -0
- agentcore/aws/connect_shell.py +266 -0
- agentcore/aws/iam.py +41 -0
- agentcore/aws/partition.py +58 -0
- agentcore/aws/policy_generation.py +116 -0
- agentcore/aws/region.py +54 -0
- agentcore/aws/retry.py +27 -0
- agentcore/aws/shell_framer.py +136 -0
- agentcore/aws/stage_endpoint.py +39 -0
- agentcore/aws/target_region.py +54 -0
- agentcore/aws/transaction_search.py +97 -0
- agentcore/cdk/__init__.py +10 -0
- agentcore/cdk/constants.py +13 -0
- agentcore/cdk/local_cdk_project.py +48 -0
- agentcore/cdk/toolkit.py +706 -0
- agentcore/cli/__init__.py +0 -0
- agentcore/cli/commands/__init__.py +0 -0
- agentcore/cli/commands/_jobs_common.py +39 -0
- agentcore/cli/commands/_run_wizards.py +639 -0
- agentcore/cli/commands/add/__init__.py +37 -0
- agentcore/cli/commands/add/_commander.py +52 -0
- agentcore/cli/commands/add/_common.py +47 -0
- agentcore/cli/commands/add/agent_wizard.py +528 -0
- agentcore/cli/commands/add/auth_options.py +154 -0
- agentcore/cli/commands/add/skill_action.py +105 -0
- agentcore/cli/commands/add/skill_command.py +87 -0
- agentcore/cli/commands/add/tool_action.py +160 -0
- agentcore/cli/commands/add/tool_command.py +117 -0
- agentcore/cli/commands/add/validate.py +333 -0
- agentcore/cli/commands/capacity_provider/__init__.py +86 -0
- agentcore/cli/commands/capacity_provider/action.py +114 -0
- agentcore/cli/commands/capacity_provider/constants.py +22 -0
- agentcore/cli/commands/config.py +91 -0
- agentcore/cli/commands/config_bundle.py +251 -0
- agentcore/cli/commands/create/__init__.py +643 -0
- agentcore/cli/commands/create/action.py +406 -0
- agentcore/cli/commands/create/dependencies.py +91 -0
- agentcore/cli/commands/create/harness_validate.py +82 -0
- agentcore/cli/commands/create/validate.py +195 -0
- agentcore/cli/commands/create/wizard.py +127 -0
- agentcore/cli/commands/dataset.py +119 -0
- agentcore/cli/commands/deploy/__init__.py +31 -0
- agentcore/cli/commands/deploy/_guards.py +25 -0
- agentcore/cli/commands/deploy/actions.py +840 -0
- agentcore/cli/commands/deploy/command.py +349 -0
- agentcore/cli/commands/deploy/progress.py +115 -0
- agentcore/cli/commands/deploy/types.py +68 -0
- agentcore/cli/commands/deploy/utils.py +43 -0
- agentcore/cli/commands/deploy/validate.py +21 -0
- agentcore/cli/commands/dev/__init__.py +5 -0
- agentcore/cli/commands/dev/_deps.py +50 -0
- agentcore/cli/commands/dev/browser_mode.py +270 -0
- agentcore/cli/commands/dev/command.py +450 -0
- agentcore/cli/commands/dev/terminal_mode.py +379 -0
- agentcore/cli/commands/exec/__init__.py +29 -0
- agentcore/cli/commands/exec/action.py +442 -0
- agentcore/cli/commands/exec/command.py +206 -0
- agentcore/cli/commands/exec/types.py +35 -0
- agentcore/cli/commands/export/__init__.py +114 -0
- agentcore/cli/commands/export/constants.py +21 -0
- agentcore/cli/commands/export/fetch_harness_spec.py +234 -0
- agentcore/cli/commands/export/harness_action.py +385 -0
- agentcore/cli/commands/export/harness_mapper.py +868 -0
- agentcore/cli/commands/export/harness_resolver.py +95 -0
- agentcore/cli/commands/export/types.py +90 -0
- agentcore/cli/commands/export/wizard.py +97 -0
- agentcore/cli/commands/feedback/__init__.py +69 -0
- agentcore/cli/commands/feedback/action.py +30 -0
- agentcore/cli/commands/feedback/consent_prompt.py +35 -0
- agentcore/cli/commands/feedback/wizard.py +49 -0
- agentcore/cli/commands/fetch/__init__.py +69 -0
- agentcore/cli/commands/fetch/action.py +62 -0
- agentcore/cli/commands/help.py +65 -0
- agentcore/cli/commands/import_/__init__.py +167 -0
- agentcore/cli/commands/import_/_js.py +72 -0
- agentcore/cli/commands/import_/actions.py +457 -0
- agentcore/cli/commands/import_/constants.py +45 -0
- agentcore/cli/commands/import_/import_evaluator.py +140 -0
- agentcore/cli/commands/import_/import_gateway.py +520 -0
- agentcore/cli/commands/import_/import_memory.py +128 -0
- agentcore/cli/commands/import_/import_online_eval.py +165 -0
- agentcore/cli/commands/import_/import_pipeline.py +145 -0
- agentcore/cli/commands/import_/import_runtime.py +214 -0
- agentcore/cli/commands/import_/import_utils.py +410 -0
- agentcore/cli/commands/import_/phase1_update.py +110 -0
- agentcore/cli/commands/import_/phase2_import.py +206 -0
- agentcore/cli/commands/import_/resource_import.py +228 -0
- agentcore/cli/commands/import_/template_utils.py +148 -0
- agentcore/cli/commands/import_/types.py +150 -0
- agentcore/cli/commands/import_/yaml_parser.py +265 -0
- agentcore/cli/commands/init.py +196 -0
- agentcore/cli/commands/invoke/__init__.py +32 -0
- agentcore/cli/commands/invoke/_deps.py +73 -0
- agentcore/cli/commands/invoke/action.py +840 -0
- agentcore/cli/commands/invoke/command.py +490 -0
- agentcore/cli/commands/invoke/constants.py +40 -0
- agentcore/cli/commands/invoke/guards.py +23 -0
- agentcore/cli/commands/invoke/interactive.py +604 -0
- agentcore/cli/commands/invoke/resolve.py +149 -0
- agentcore/cli/commands/invoke/resolve_prompt.py +60 -0
- agentcore/cli/commands/invoke/types.py +61 -0
- agentcore/cli/commands/invoke/validate.py +33 -0
- agentcore/cli/commands/job_actions.py +239 -0
- agentcore/cli/commands/job_history.py +380 -0
- agentcore/cli/commands/logs/__init__.py +53 -0
- agentcore/cli/commands/logs/action.py +159 -0
- agentcore/cli/commands/logs/filter_pattern.py +17 -0
- agentcore/cli/commands/logs/guards.py +51 -0
- agentcore/cli/commands/logs_evals.py +42 -0
- agentcore/cli/commands/package.py +118 -0
- agentcore/cli/commands/remove/__init__.py +172 -0
- agentcore/cli/commands/remove/skill_command.py +131 -0
- agentcore/cli/commands/remove/tool_command.py +59 -0
- agentcore/cli/commands/run.py +808 -0
- agentcore/cli/commands/run_ingest.py +97 -0
- agentcore/cli/commands/shared/__init__.py +0 -0
- agentcore/cli/commands/shared/arn_utils.py +9 -0
- agentcore/cli/commands/shared/filesystem_utils.py +421 -0
- agentcore/cli/commands/shared/header_utils.py +87 -0
- agentcore/cli/commands/shared/indexed_key_parser.py +76 -0
- agentcore/cli/commands/shared/lifecycle_utils.py +72 -0
- agentcore/cli/commands/shared/region_utils.py +20 -0
- agentcore/cli/commands/shared/vpc_utils.py +99 -0
- agentcore/cli/commands/status/__init__.py +30 -0
- agentcore/cli/commands/status/_guards.py +25 -0
- agentcore/cli/commands/status/action.py +741 -0
- agentcore/cli/commands/status/command.py +321 -0
- agentcore/cli/commands/status/constants.py +36 -0
- agentcore/cli/commands/status/format_knowledge_base.py +111 -0
- agentcore/cli/commands/telemetry.py +79 -0
- agentcore/cli/commands/traces/__init__.py +155 -0
- agentcore/cli/commands/traces/action.py +123 -0
- agentcore/cli/commands/update/__init__.py +63 -0
- agentcore/cli/commands/update/action.py +164 -0
- agentcore/cli/commands/validate.py +165 -0
- agentcore/cli/constants.py +96 -0
- agentcore/cli/errors.py +118 -0
- agentcore/cli/external_requirements/__init__.py +67 -0
- agentcore/cli/external_requirements/checks.py +280 -0
- agentcore/cli/external_requirements/detect.py +55 -0
- agentcore/cli/external_requirements/versions.py +57 -0
- agentcore/cli/groups.py +23 -0
- agentcore/cli/interactive.py +85 -0
- agentcore/cli/main.py +137 -0
- agentcore/cli/naming.py +11 -0
- agentcore/cli/notices.py +42 -0
- agentcore/cli/project.py +34 -0
- agentcore/cli/run_command.py +27 -0
- agentcore/cli/tree.py +135 -0
- agentcore/cli/update_notifier.py +123 -0
- agentcore/cli/variadic.py +50 -0
- agentcore/cloudformation/__init__.py +84 -0
- agentcore/cloudformation/bootstrap.py +64 -0
- agentcore/cloudformation/logical_ids.py +37 -0
- agentcore/cloudformation/outputs.py +566 -0
- agentcore/cloudformation/stack_discovery.py +75 -0
- agentcore/cloudformation/stack_events.py +107 -0
- agentcore/cloudformation/stack_status.py +98 -0
- agentcore/cloudformation/types.py +44 -0
- agentcore/lib/__init__.py +68 -0
- agentcore/lib/constants.py +77 -0
- agentcore/lib/dependency_management/__init__.py +23 -0
- agentcore/lib/dependency_management/messages.py +92 -0
- agentcore/lib/dependency_management/plan.py +98 -0
- agentcore/lib/dependency_management/semver.py +137 -0
- agentcore/lib/dependency_management/sync.py +139 -0
- agentcore/lib/dependency_management/types.py +56 -0
- agentcore/lib/errors/__init__.py +5 -0
- agentcore/lib/errors/types.py +352 -0
- agentcore/lib/errors/validation.py +121 -0
- agentcore/lib/packaging/__init__.py +81 -0
- agentcore/lib/packaging/build_args.py +23 -0
- agentcore/lib/packaging/build_context.py +26 -0
- agentcore/lib/packaging/build_context_dockerignore.py +45 -0
- agentcore/lib/packaging/container.py +95 -0
- agentcore/lib/packaging/helpers.py +289 -0
- agentcore/lib/packaging/node.py +153 -0
- agentcore/lib/packaging/python.py +135 -0
- agentcore/lib/packaging/types.py +69 -0
- agentcore/lib/packaging/uv.py +45 -0
- agentcore/lib/result.py +104 -0
- agentcore/lib/schemas/__init__.py +1 -0
- agentcore/lib/schemas/io/__init__.py +27 -0
- agentcore/lib/schemas/io/config_io.py +458 -0
- agentcore/lib/schemas/io/global_config.py +239 -0
- agentcore/lib/schemas/io/path_resolver.py +132 -0
- agentcore/lib/secrets/__init__.py +18 -0
- agentcore/lib/secrets/cipher.py +70 -0
- agentcore/lib/secrets/key_provider.py +163 -0
- agentcore/lib/secrets/sensitive_keys.py +55 -0
- agentcore/lib/time_constants.py +4 -0
- agentcore/lib/utils/__init__.py +47 -0
- agentcore/lib/utils/aws_account.py +15 -0
- agentcore/lib/utils/credentials.py +68 -0
- agentcore/lib/utils/dotenv.py +32 -0
- agentcore/lib/utils/env.py +105 -0
- agentcore/lib/utils/json_format.py +29 -0
- agentcore/lib/utils/json_rpc.py +22 -0
- agentcore/lib/utils/platform.py +33 -0
- agentcore/lib/utils/polling.py +86 -0
- agentcore/lib/utils/subprocess.py +135 -0
- agentcore/lib/utils/time_parser.py +48 -0
- agentcore/lib/utils/validation.py +106 -0
- agentcore/logging/__init__.py +10 -0
- agentcore/logging/_common.py +77 -0
- agentcore/logging/cdk_io_host.py +36 -0
- agentcore/logging/create_logger.py +107 -0
- agentcore/logging/dev_logger.py +63 -0
- agentcore/logging/exec_logger.py +159 -0
- agentcore/logging/invoke_logger.py +154 -0
- agentcore/logging/remove_logger.py +95 -0
- agentcore/operations/__init__.py +0 -0
- agentcore/operations/agent/__init__.py +0 -0
- agentcore/operations/agent/config_bundle_defaults.py +31 -0
- agentcore/operations/agent/generate/__init__.py +28 -0
- agentcore/operations/agent/generate/schema_mapper.py +319 -0
- agentcore/operations/agent/generate/types.py +172 -0
- agentcore/operations/agent/generate/write_agent_to_project.py +67 -0
- agentcore/operations/agent/import_/__init__.py +31 -0
- agentcore/operations/agent/import_/base_translator.py +449 -0
- agentcore/operations/agent/import_/constants.py +23 -0
- agentcore/operations/agent/import_/execute.py +127 -0
- agentcore/operations/agent/import_/langgraph_translator.py +294 -0
- agentcore/operations/agent/import_/pyproject_generator.py +73 -0
- agentcore/operations/agent/import_/strands_translator.py +289 -0
- agentcore/operations/config_bundle/__init__.py +1 -0
- agentcore/operations/config_bundle/bundle_name_variants.py +9 -0
- agentcore/operations/config_bundle/diff_versions.py +52 -0
- agentcore/operations/config_bundle/resolve_bundle.py +65 -0
- agentcore/operations/connectors/__init__.py +3 -0
- agentcore/operations/connectors/translators.py +63 -0
- agentcore/operations/dataset/__init__.py +21 -0
- agentcore/operations/dataset/delete.py +7 -0
- agentcore/operations/dataset/publish.py +16 -0
- agentcore/operations/dataset/pull.py +21 -0
- agentcore/operations/dataset/push.py +201 -0
- agentcore/operations/dataset/resolve_dataset.py +65 -0
- agentcore/operations/dataset/status.py +18 -0
- agentcore/operations/dataset/wait.py +25 -0
- agentcore/operations/deploy/__init__.py +152 -0
- agentcore/operations/deploy/backfill_vpc_id.py +90 -0
- agentcore/operations/deploy/change_detection.py +84 -0
- agentcore/operations/deploy/constants.py +58 -0
- agentcore/operations/deploy/dependency_sync.py +105 -0
- agentcore/operations/deploy/ensure_target.py +30 -0
- agentcore/operations/deploy/gateway_status.py +44 -0
- agentcore/operations/deploy/managed_memory_notice.py +36 -0
- agentcore/operations/deploy/payment_connector_authorization.py +106 -0
- agentcore/operations/deploy/post_deploy_datasets.py +96 -0
- agentcore/operations/deploy/post_deploy_knowledge_bases.py +104 -0
- agentcore/operations/deploy/post_deploy_observability.py +28 -0
- agentcore/operations/deploy/post_deploy_online_evals.py +60 -0
- agentcore/operations/deploy/pre_deploy_identity.py +505 -0
- agentcore/operations/deploy/preflight.py +385 -0
- agentcore/operations/deploy/region_override.py +45 -0
- agentcore/operations/deploy/teardown.py +153 -0
- agentcore/operations/dev/__init__.py +61 -0
- agentcore/operations/dev/_http.py +79 -0
- agentcore/operations/dev/_server.py +165 -0
- agentcore/operations/dev/codezip_dev_server.py +151 -0
- agentcore/operations/dev/config.py +128 -0
- agentcore/operations/dev/constants.py +9 -0
- agentcore/operations/dev/container_dev_server.py +237 -0
- agentcore/operations/dev/container_runtime.py +37 -0
- agentcore/operations/dev/dev_server.py +244 -0
- agentcore/operations/dev/gateway_env.py +25 -0
- agentcore/operations/dev/invoke.py +214 -0
- agentcore/operations/dev/invoke_a2a.py +204 -0
- agentcore/operations/dev/invoke_agui.py +88 -0
- agentcore/operations/dev/invoke_mcp.py +145 -0
- agentcore/operations/dev/invoke_types.py +30 -0
- agentcore/operations/dev/load_dev_env.py +26 -0
- agentcore/operations/dev/memory_env.py +19 -0
- agentcore/operations/dev/otel/__init__.py +5 -0
- agentcore/operations/dev/otel/collector.py +234 -0
- agentcore/operations/dev/otel/transforms.py +331 -0
- agentcore/operations/dev/payment_env.py +50 -0
- agentcore/operations/dev/server.py +28 -0
- agentcore/operations/dev/sse_transform.py +30 -0
- agentcore/operations/dev/utils.py +152 -0
- agentcore/operations/dev/web_ui/__init__.py +31 -0
- agentcore/operations/dev/web_ui/constants.py +25 -0
- agentcore/operations/dev/web_ui/handlers/__init__.py +36 -0
- agentcore/operations/dev/web_ui/handlers/_common.py +36 -0
- agentcore/operations/dev/web_ui/handlers/_deployed.py +37 -0
- agentcore/operations/dev/web_ui/handlers/a2a_proxy.py +37 -0
- agentcore/operations/dev/web_ui/handlers/cloudwatch_traces.py +71 -0
- agentcore/operations/dev/web_ui/handlers/harness_invocation.py +89 -0
- agentcore/operations/dev/web_ui/handlers/harness_utils.py +95 -0
- agentcore/operations/dev/web_ui/handlers/invocations.py +327 -0
- agentcore/operations/dev/web_ui/handlers/mcp_proxy.py +119 -0
- agentcore/operations/dev/web_ui/handlers/memory.py +84 -0
- agentcore/operations/dev/web_ui/handlers/resources.py +351 -0
- agentcore/operations/dev/web_ui/handlers/start.py +182 -0
- agentcore/operations/dev/web_ui/handlers/status.py +25 -0
- agentcore/operations/dev/web_ui/handlers/traces.py +50 -0
- agentcore/operations/dev/web_ui/run_web_ui.py +60 -0
- agentcore/operations/dev/web_ui/web_server.py +297 -0
- agentcore/operations/eval/__init__.py +8 -0
- agentcore/operations/eval/list_eval_runs.py +18 -0
- agentcore/operations/eval/logs_eval.py +119 -0
- agentcore/operations/eval/pause_resume.py +90 -0
- agentcore/operations/eval/run_eval.py +354 -0
- agentcore/operations/eval/shared/__init__.py +0 -0
- agentcore/operations/eval/shared/dataset_loader.py +68 -0
- agentcore/operations/eval/shared/dataset_session_provider.py +122 -0
- agentcore/operations/eval/shared/evaluator_runner.py +89 -0
- agentcore/operations/eval/shared/scenario_executor.py +71 -0
- agentcore/operations/eval/shared/span_collector.py +255 -0
- agentcore/operations/eval/storage.py +67 -0
- agentcore/operations/feedback/__init__.py +21 -0
- agentcore/operations/feedback/aperture_client.py +98 -0
- agentcore/operations/feedback/build_payload.py +97 -0
- agentcore/operations/feedback/constants.py +26 -0
- agentcore/operations/feedback/submit_feedback.py +150 -0
- agentcore/operations/fetch_access/__init__.py +36 -0
- agentcore/operations/fetch_access/_target.py +17 -0
- agentcore/operations/fetch_access/fetch_gateway_token.py +75 -0
- agentcore/operations/fetch_access/fetch_harness_token.py +72 -0
- agentcore/operations/fetch_access/fetch_runtime_token.py +76 -0
- agentcore/operations/fetch_access/list_agents.py +18 -0
- agentcore/operations/fetch_access/list_gateways.py +26 -0
- agentcore/operations/fetch_access/list_harnesses.py +23 -0
- agentcore/operations/fetch_access/oauth_token.py +162 -0
- agentcore/operations/fetch_access/types.py +34 -0
- agentcore/operations/harness/__init__.py +1 -0
- agentcore/operations/harness/orphan.py +84 -0
- agentcore/operations/harness/skill_utils.py +56 -0
- agentcore/operations/identity/__init__.py +33 -0
- agentcore/operations/identity/api_key_credential_provider.py +68 -0
- agentcore/operations/identity/oauth2_credential_provider.py +95 -0
- agentcore/operations/ingest/__init__.py +184 -0
- agentcore/operations/init/__init__.py +3 -0
- agentcore/operations/init/files.py +59 -0
- agentcore/operations/insights/__init__.py +21 -0
- agentcore/operations/insights/insights_storage.py +67 -0
- agentcore/operations/insights/run_insights.py +148 -0
- agentcore/operations/invoke/__init__.py +5 -0
- agentcore/operations/invoke/resolve_agent_context.py +85 -0
- agentcore/operations/jobs/__init__.py +38 -0
- agentcore/operations/jobs/ab_test/__init__.py +0 -0
- agentcore/operations/jobs/ab_test/build_options.py +126 -0
- agentcore/operations/jobs/ab_test/format.py +121 -0
- agentcore/operations/jobs/ab_test/handler.py +412 -0
- agentcore/operations/jobs/ab_test/promote.py +142 -0
- agentcore/operations/jobs/ab_test/resolve.py +221 -0
- agentcore/operations/jobs/batch_evaluation/__init__.py +0 -0
- agentcore/operations/jobs/batch_evaluation/build_source.py +68 -0
- agentcore/operations/jobs/batch_evaluation/dataset_phase1.py +95 -0
- agentcore/operations/jobs/batch_evaluation/format.py +58 -0
- agentcore/operations/jobs/batch_evaluation/handler.py +203 -0
- agentcore/operations/jobs/insights/__init__.py +0 -0
- agentcore/operations/jobs/insights/format.py +65 -0
- agentcore/operations/jobs/insights/handler.py +178 -0
- agentcore/operations/jobs/recommendation/__init__.py +0 -0
- agentcore/operations/jobs/recommendation/apply_to_bundle.py +76 -0
- agentcore/operations/jobs/recommendation/build_config.py +271 -0
- agentcore/operations/jobs/recommendation/fetch_session_spans.py +93 -0
- agentcore/operations/jobs/recommendation/format.py +77 -0
- agentcore/operations/jobs/recommendation/handler.py +331 -0
- agentcore/operations/jobs/shared/__init__.py +0 -0
- agentcore/operations/jobs/shared/constants.py +85 -0
- agentcore/operations/jobs/shared/engine.py +187 -0
- agentcore/operations/jobs/shared/format.py +104 -0
- agentcore/operations/jobs/shared/region.py +34 -0
- agentcore/operations/jobs/shared/resolve_agent_state.py +30 -0
- agentcore/operations/jobs/shared/storage.py +117 -0
- agentcore/operations/jobs/shared/wait.py +30 -0
- agentcore/operations/knowledge_base/__init__.py +33 -0
- agentcore/operations/knowledge_base/connector_config.py +105 -0
- agentcore/operations/knowledge_base/hydrate_data_sources.py +59 -0
- agentcore/operations/mcp/__init__.py +4 -0
- agentcore/operations/memory/__init__.py +13 -0
- agentcore/operations/memory/_records.py +72 -0
- agentcore/operations/memory/list_memory_records.py +45 -0
- agentcore/operations/memory/retrieve_memory_records.py +52 -0
- agentcore/operations/node/__init__.py +15 -0
- agentcore/operations/node/setup.py +36 -0
- agentcore/operations/python/__init__.py +17 -0
- agentcore/operations/python/setup.py +47 -0
- agentcore/operations/resolve_agent.py +159 -0
- agentcore/operations/session/__init__.py +86 -0
- agentcore/operations/traces/__init__.py +29 -0
- agentcore/operations/traces/get_trace.py +202 -0
- agentcore/operations/traces/insights_query.py +74 -0
- agentcore/operations/traces/list_traces.py +46 -0
- agentcore/operations/traces/trace_url.py +25 -0
- agentcore/operations/traces/types.py +55 -0
- agentcore/primitives/__init__.py +0 -0
- agentcore/primitives/_gateway_target_wizard.py +293 -0
- agentcore/primitives/_gateway_validation.py +442 -0
- agentcore/primitives/_harness_cli.py +557 -0
- agentcore/primitives/_harness_validation.py +163 -0
- agentcore/primitives/_harness_wizard.py +726 -0
- agentcore/primitives/_knowledge_base_wizard.py +259 -0
- agentcore/primitives/_payment_wizard.py +331 -0
- agentcore/primitives/_policy_form.py +160 -0
- agentcore/primitives/agent.py +817 -0
- agentcore/primitives/auth_utils.py +77 -0
- agentcore/primitives/base.py +178 -0
- agentcore/primitives/capacity_provider.py +501 -0
- agentcore/primitives/config_bundle.py +329 -0
- agentcore/primitives/credential.py +387 -0
- agentcore/primitives/credential_utils.py +45 -0
- agentcore/primitives/dataset.py +211 -0
- agentcore/primitives/deployed_gateways.py +34 -0
- agentcore/primitives/evaluator.py +909 -0
- agentcore/primitives/gateway.py +496 -0
- agentcore/primitives/gateway_target.py +988 -0
- agentcore/primitives/harness.py +706 -0
- agentcore/primitives/knowledge_base.py +477 -0
- agentcore/primitives/memory.py +387 -0
- agentcore/primitives/online_eval_config.py +365 -0
- agentcore/primitives/online_insights.py +275 -0
- agentcore/primitives/payment_connector.py +427 -0
- agentcore/primitives/payment_eligible.py +20 -0
- agentcore/primitives/payment_manager.py +605 -0
- agentcore/primitives/payment_validation.py +68 -0
- agentcore/primitives/policy.py +622 -0
- agentcore/primitives/policy_engine.py +358 -0
- agentcore/primitives/registry.py +56 -0
- agentcore/primitives/runtime_endpoint.py +361 -0
- agentcore/primitives/secret_flag_warning.py +17 -0
- agentcore/schema/__init__.py +14 -0
- agentcore/schema/_zod.py +395 -0
- agentcore/schema/constants.py +306 -0
- agentcore/schema/schemas/__init__.py +40 -0
- agentcore/schema/schemas/agent_env.py +545 -0
- agentcore/schema/schemas/agentcore_project.py +530 -0
- agentcore/schema/schemas/auth.py +286 -0
- agentcore/schema/schemas/aws_targets.py +72 -0
- agentcore/schema/schemas/connections.py +187 -0
- agentcore/schema/schemas/deployed_state.py +322 -0
- agentcore/schema/schemas/mcp.py +650 -0
- agentcore/schema/schemas/mcp_defs.py +54 -0
- agentcore/schema/schemas/primitives/__init__.py +52 -0
- agentcore/schema/schemas/primitives/ab_test.py +162 -0
- agentcore/schema/schemas/primitives/capacity_provider.py +214 -0
- agentcore/schema/schemas/primitives/config_bundle.py +48 -0
- agentcore/schema/schemas/primitives/dataset.py +55 -0
- agentcore/schema/schemas/primitives/evaluator.py +171 -0
- agentcore/schema/schemas/primitives/harness.py +710 -0
- agentcore/schema/schemas/primitives/knowledge_base.py +90 -0
- agentcore/schema/schemas/primitives/memory.py +113 -0
- agentcore/schema/schemas/primitives/online_eval_config.py +77 -0
- agentcore/schema/schemas/primitives/payment.py +114 -0
- agentcore/schema/schemas/primitives/policy.py +83 -0
- agentcore/schema/schemas/primitives/tags.py +50 -0
- agentcore/schema/schemas/zod_util.py +5 -0
- agentcore/schema/types/__init__.py +3 -0
- agentcore/schema/types/path.py +16 -0
- agentcore/templates/__init__.py +115 -0
- agentcore/templates/_handlebars.py +782 -0
- agentcore/templates/_js.py +164 -0
- agentcore/templates/base_renderer.py +107 -0
- agentcore/templates/cdk_renderer.py +129 -0
- agentcore/templates/evaluator_renderer.py +39 -0
- agentcore/templates/gateway_target_renderer.py +69 -0
- agentcore/templates/llm_context/README.md +16 -0
- agentcore/templates/llm_context/agentcore.ts +685 -0
- agentcore/templates/llm_context/aws-targets.ts +51 -0
- agentcore/templates/profiles.py +151 -0
- agentcore/templates/render.py +92 -0
- agentcore/templates/renderers.py +83 -0
- agentcore/templates/schema_assets.py +23 -0
- agentcore/templates/template_root.py +28 -0
- agentcore/templates/types.py +192 -0
- agentcore_cli-0.31.1.dist-info/METADATA +84 -0
- agentcore_cli-0.31.1.dist-info/RECORD +655 -0
- agentcore_cli-0.31.1.dist-info/WHEEL +4 -0
- agentcore_cli-0.31.1.dist-info/entry_points.txt +2 -0
- agentcore_cli-0.31.1.dist-info/licenses/LICENSE +175 -0
- agentcore_cli-0.31.1.dist-info/licenses/NOTICE +88 -0
agentcore/cdk/toolkit.py
ADDED
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
"""CDK toolkit driver (port of upstream ``src/cli/cdk/toolkit-lib``).
|
|
2
|
+
|
|
3
|
+
Upstream drives ``@aws-cdk/toolkit-lib`` in-process. The Python port keeps the vended TypeScript
|
|
4
|
+
CDK app and shells out to the CDK CLI the project already depends on
|
|
5
|
+
(``agentcore/cdk/node_modules/.bin/cdk``, falling back to ``npx cdk``):
|
|
6
|
+
|
|
7
|
+
===================================== ==========================================================
|
|
8
|
+
toolkit-lib call (upstream) cdk CLI invocation (here)
|
|
9
|
+
===================================== ==========================================================
|
|
10
|
+
``fromCdkApp('node dist/bin/cdk.js')`` ``--app "node dist/bin/cdk.js"`` with ``cwd`` = project dir
|
|
11
|
+
``synth(source, {validateStacks})`` ``cdk synth --app ... --output cdk.out --quiet --validation``
|
|
12
|
+
``fromAssemblyDirectory(dir)`` ``--app <assembly dir>`` for every later operation
|
|
13
|
+
``deploy(src, {stacks})`` ``cdk deploy <patterns> --require-approval never --ci``
|
|
14
|
+
``diff(src, {stacks})`` ``cdk diff <patterns>``
|
|
15
|
+
``destroy(src, {stacks})`` ``cdk destroy <patterns> --force``
|
|
16
|
+
``list(src, {stacks})`` ``cdk list <patterns>``
|
|
17
|
+
``bootstrap(envs, {parameters})`` ``cdk bootstrap <envs> --bootstrap-customer-key`` (or
|
|
18
|
+
``--bootstrap-kms-key-id <id>``)
|
|
19
|
+
===================================== ==========================================================
|
|
20
|
+
|
|
21
|
+
The region override is forwarded through the child environment (``AWS_REGION`` /
|
|
22
|
+
``AWS_DEFAULT_REGION``) exactly as upstream does for the app subprocess; a profile goes through
|
|
23
|
+
``AWS_PROFILE``. Context comes from the project's ``cdk.json`` (the CLI reads it from ``cwd``),
|
|
24
|
+
just as toolkit-lib reads it for ``fromCdkApp``. No hotswap: deploys are full change-set deploys,
|
|
25
|
+
which is toolkit-lib's default.
|
|
26
|
+
|
|
27
|
+
Progress: toolkit-lib streams stack activity through its IoHost (``CDK_TOOLKIT_I5501..I5901``).
|
|
28
|
+
Here, while ``cdk deploy`` runs, CloudFormation stack events are polled with boto3 and turned into
|
|
29
|
+
the same message codes, so the IoHost contract (``SwitchableIoHost``) is unchanged.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import json
|
|
35
|
+
import os
|
|
36
|
+
import re
|
|
37
|
+
import threading
|
|
38
|
+
import time
|
|
39
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
40
|
+
from dataclasses import dataclass, field
|
|
41
|
+
from datetime import UTC, datetime
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
from typing import Any, Protocol
|
|
44
|
+
|
|
45
|
+
from agentcore.cdk.constants import CDK_APP_ENTRY, CDK_PROJECT_DIR
|
|
46
|
+
from agentcore.cli.errors import is_changeset_in_progress_error
|
|
47
|
+
from agentcore.cloudformation.stack_events import StackEventPoller, describe_failures, format_stack_event
|
|
48
|
+
from agentcore.lib.constants import CONFIG_DIR
|
|
49
|
+
from agentcore.lib.utils.platform import is_windows
|
|
50
|
+
from agentcore.lib.utils.subprocess import SubprocessResult, run_subprocess_capture
|
|
51
|
+
|
|
52
|
+
# Command used to execute the CDK app (matches "app" in cdk.json; requires `tsc` build first).
|
|
53
|
+
CDK_APP_COMMAND = "node"
|
|
54
|
+
|
|
55
|
+
# Default assembly output directory (relative to the CDK project directory).
|
|
56
|
+
CDK_OUT_DIR = "cdk.out"
|
|
57
|
+
|
|
58
|
+
# toolkit-lib message codes relevant for deploy progress display.
|
|
59
|
+
DEPLOY_MESSAGE_CODES = frozenset(
|
|
60
|
+
{
|
|
61
|
+
"CDK_TOOLKIT_I5501", # Stack monitoring start
|
|
62
|
+
"CDK_TOOLKIT_I5502", # Stack activity event (resource events with progress)
|
|
63
|
+
"CDK_TOOLKIT_I5503", # Stack monitoring end
|
|
64
|
+
"CDK_TOOLKIT_I5900", # Deployment results
|
|
65
|
+
"CDK_TOOLKIT_I5901", # Deployment success
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Retry policy for transient changeset conflicts (identical to upstream).
|
|
70
|
+
DEPLOY_MAX_RETRIES = 3
|
|
71
|
+
DEPLOY_BASE_DELAY_S = 5.0
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class StackSelectionStrategy:
|
|
75
|
+
"""String constants mirroring toolkit-lib's ``StackSelectionStrategy`` enum."""
|
|
76
|
+
|
|
77
|
+
ALL_STACKS = "all-stacks"
|
|
78
|
+
MAIN_ASSEMBLY = "main-assembly"
|
|
79
|
+
ONLY_SINGLE = "only-single"
|
|
80
|
+
PATTERN_MATCH = "pattern-match"
|
|
81
|
+
PATTERN_MUST_MATCH = "pattern-must-match"
|
|
82
|
+
PATTERN_MUST_MATCH_SINGLE = "pattern-must-match-single"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass(frozen=True)
|
|
86
|
+
class StackSelector:
|
|
87
|
+
strategy: str = StackSelectionStrategy.PATTERN_MUST_MATCH
|
|
88
|
+
patterns: tuple[str, ...] = ()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def pattern_must_match(*patterns: str) -> StackSelector:
|
|
92
|
+
return StackSelector(StackSelectionStrategy.PATTERN_MUST_MATCH, tuple(patterns))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# IoHost (port of toolkit-lib/types.ts)
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class IoMessage:
|
|
102
|
+
code: str
|
|
103
|
+
message: str
|
|
104
|
+
level: str = "info"
|
|
105
|
+
time: datetime = field(default_factory=lambda: datetime.now(UTC))
|
|
106
|
+
action: str | None = None
|
|
107
|
+
data: Any = None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class IoHost(Protocol):
|
|
111
|
+
def notify(self, msg: IoMessage) -> None: ...
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class _SilentIoHost:
|
|
115
|
+
"""Suppresses all CDK output."""
|
|
116
|
+
|
|
117
|
+
def notify(self, msg: IoMessage) -> None:
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
silent_io_host: IoHost = _SilentIoHost()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
class ResourceProgress:
|
|
126
|
+
completed: int
|
|
127
|
+
total: int
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class DeployMessage:
|
|
132
|
+
"""CDK message filtered for deploy progress display (data pre-extracted)."""
|
|
133
|
+
|
|
134
|
+
code: str
|
|
135
|
+
message: str
|
|
136
|
+
level: str
|
|
137
|
+
timestamp: datetime
|
|
138
|
+
progress: ResourceProgress | None = None
|
|
139
|
+
outputs: dict[str, str] | None = None
|
|
140
|
+
stack_arn: str | None = None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
_PROGRESS_RE = re.compile(r"\|\s*(\d+)/(\d+)\s*\|")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def extract_progress_from_message(message: str) -> ResourceProgress | None:
|
|
147
|
+
"""I5502 lines look like ``StackName | X/Y | timestamp | STATUS | ...``."""
|
|
148
|
+
match = _PROGRESS_RE.search(message)
|
|
149
|
+
return ResourceProgress(int(match.group(1)), int(match.group(2))) if match else None
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
RawMessageCallback = Callable[[str, str, str, Any], None]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@dataclass
|
|
156
|
+
class SwitchableIoHost:
|
|
157
|
+
"""IoHost that starts silent; ``set_verbose(True)`` enables message capture."""
|
|
158
|
+
|
|
159
|
+
verbose: bool = False
|
|
160
|
+
on_message: Callable[[DeployMessage], None] | None = None
|
|
161
|
+
on_raw_message: RawMessageCallback | None = None
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def io_host(self) -> IoHost:
|
|
165
|
+
return self
|
|
166
|
+
|
|
167
|
+
def set_verbose(self, verbose: bool) -> None:
|
|
168
|
+
self.verbose = verbose
|
|
169
|
+
|
|
170
|
+
def set_on_message(self, callback: Callable[[DeployMessage], None] | None) -> None:
|
|
171
|
+
self.on_message = callback
|
|
172
|
+
|
|
173
|
+
def set_on_raw_message(self, callback: RawMessageCallback | None) -> None:
|
|
174
|
+
self.on_raw_message = callback
|
|
175
|
+
|
|
176
|
+
def notify(self, msg: IoMessage) -> None:
|
|
177
|
+
if not self.verbose:
|
|
178
|
+
return
|
|
179
|
+
code = msg.code or "UNKNOWN"
|
|
180
|
+
text = msg.message if isinstance(msg.message, str) else ""
|
|
181
|
+
if self.on_raw_message:
|
|
182
|
+
self.on_raw_message(code, msg.level or "info", text, msg.data)
|
|
183
|
+
if self.on_message and msg.code in DEPLOY_MESSAGE_CODES:
|
|
184
|
+
deploy_message = DeployMessage(
|
|
185
|
+
code=msg.code,
|
|
186
|
+
message=text,
|
|
187
|
+
level=msg.level or "info",
|
|
188
|
+
timestamp=msg.time,
|
|
189
|
+
progress=extract_progress_from_message(text),
|
|
190
|
+
)
|
|
191
|
+
if msg.code == "CDK_TOOLKIT_I5900" and isinstance(msg.data, Mapping):
|
|
192
|
+
if msg.data.get("outputs"):
|
|
193
|
+
deploy_message.outputs = dict(msg.data["outputs"])
|
|
194
|
+
if msg.data.get("stackArn"):
|
|
195
|
+
deploy_message.stack_arn = msg.data["stackArn"]
|
|
196
|
+
self.on_message(deploy_message)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def create_switchable_io_host() -> SwitchableIoHost:
|
|
200
|
+
return SwitchableIoHost()
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# ---------------------------------------------------------------------------
|
|
204
|
+
# Driver
|
|
205
|
+
# ---------------------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@dataclass(frozen=True)
|
|
209
|
+
class SynthOutput:
|
|
210
|
+
stack_names: list[str]
|
|
211
|
+
assembly_directory: str
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class CdkCommandError(RuntimeError):
|
|
215
|
+
"""A cdk subprocess failed. The message carries the CLI's own error text for classification."""
|
|
216
|
+
|
|
217
|
+
def __init__(self, message: str, result: SubprocessResult | None = None):
|
|
218
|
+
super().__init__(message)
|
|
219
|
+
self.result = result
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _with_error_context(context: str, err: BaseException) -> BaseException:
|
|
223
|
+
"""Prefix the error message with ``CDK <context> failed:`` (upstream mutates ``err.message``)."""
|
|
224
|
+
message = f"CDK {context} failed: {err}"
|
|
225
|
+
if isinstance(err, CdkCommandError):
|
|
226
|
+
return CdkCommandError(message, err.result)
|
|
227
|
+
wrapped = RuntimeError(message)
|
|
228
|
+
wrapped.__cause__ = err
|
|
229
|
+
return wrapped
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
_ANSI_RE = re.compile(r"\x1b\[[0-9;]*[a-zA-Z]")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _strip_ansi(text: str) -> str:
|
|
236
|
+
return _ANSI_RE.sub("", text)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _failure_text(result: SubprocessResult) -> str:
|
|
240
|
+
"""The meaningful part of a failed cdk run's output."""
|
|
241
|
+
text = _strip_ansi((result.stderr or "").strip() or (result.stdout or "").strip())
|
|
242
|
+
if not text:
|
|
243
|
+
reason = f"code {result.code}" if result.code is not None else f"signal {result.signal}"
|
|
244
|
+
return f"cdk exited with {reason}"
|
|
245
|
+
# Prefer the CLI's explicit error lines ("❌ ...", "Error: ...") when present.
|
|
246
|
+
lines = [line for line in text.splitlines() if line.strip()]
|
|
247
|
+
errorish = [line for line in lines if line.lstrip().startswith(("❌", "Error", "error"))]
|
|
248
|
+
if errorish:
|
|
249
|
+
tail = lines[lines.index(errorish[0]) :]
|
|
250
|
+
return "\n".join(tail[-40:])
|
|
251
|
+
return "\n".join(lines[-40:])
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def resolve_cdk_command(project_dir: str | os.PathLike[str]) -> tuple[str, list[str]]:
|
|
255
|
+
"""The project's own CDK CLI (``node_modules/.bin/cdk``), else ``npx cdk``."""
|
|
256
|
+
bin_dir = Path(project_dir) / "node_modules" / ".bin"
|
|
257
|
+
candidates = ["cdk.cmd", "cdk"] if is_windows else ["cdk"]
|
|
258
|
+
for name in candidates:
|
|
259
|
+
path = bin_dir / name
|
|
260
|
+
if path.exists():
|
|
261
|
+
return str(path), []
|
|
262
|
+
return "npx", ["cdk"]
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
Runner = Callable[..., SubprocessResult]
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class CdkToolkitWrapper:
|
|
269
|
+
"""Thin driver around the CDK CLI, pre-configured for the vended AgentCore CDK project."""
|
|
270
|
+
|
|
271
|
+
def __init__(
|
|
272
|
+
self,
|
|
273
|
+
project_dir: str | os.PathLike[str] | None = None,
|
|
274
|
+
*,
|
|
275
|
+
io_host: IoHost | None = None,
|
|
276
|
+
profile: str | None = None,
|
|
277
|
+
region: str | None = None,
|
|
278
|
+
runner: Runner | None = None,
|
|
279
|
+
poll_interval: float = 2.0,
|
|
280
|
+
sleep: Callable[[float], None] = time.sleep,
|
|
281
|
+
) -> None:
|
|
282
|
+
self.project_dir = str(project_dir or Path(os.getcwd()) / CONFIG_DIR / CDK_PROJECT_DIR)
|
|
283
|
+
self.io_host: IoHost = io_host or silent_io_host
|
|
284
|
+
self.profile = profile
|
|
285
|
+
self.region = region
|
|
286
|
+
self._runner = runner
|
|
287
|
+
self.poll_interval = poll_interval
|
|
288
|
+
self._sleep = sleep
|
|
289
|
+
self._command: tuple[str, list[str]] | None = None
|
|
290
|
+
self._env: dict[str, str] | None = None
|
|
291
|
+
self._assembly_dir: str | None = None
|
|
292
|
+
self._stack_names: list[str] = []
|
|
293
|
+
|
|
294
|
+
# -- setup -------------------------------------------------------------------------------
|
|
295
|
+
|
|
296
|
+
def get_project_dir(self) -> str:
|
|
297
|
+
return self.project_dir
|
|
298
|
+
|
|
299
|
+
def get_cdk_app_command(self) -> str:
|
|
300
|
+
return f"{CDK_APP_COMMAND} {CDK_APP_ENTRY}"
|
|
301
|
+
|
|
302
|
+
def initialize(self) -> None:
|
|
303
|
+
"""Resolve the CDK CLI and the child environment. Must be called before any operation."""
|
|
304
|
+
try:
|
|
305
|
+
profile = self.profile or os.environ.get("AWS_PROFILE")
|
|
306
|
+
region = self.region or os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION")
|
|
307
|
+
env = dict(os.environ)
|
|
308
|
+
if region:
|
|
309
|
+
env["AWS_REGION"] = region
|
|
310
|
+
env["AWS_DEFAULT_REGION"] = region
|
|
311
|
+
if profile:
|
|
312
|
+
env["AWS_PROFILE"] = profile
|
|
313
|
+
# The CLI is driven programmatically: no update notices, no interactive prompts.
|
|
314
|
+
env.setdefault("CDK_DISABLE_VERSION_CHECK", "1")
|
|
315
|
+
self._env = env
|
|
316
|
+
self._command = resolve_cdk_command(self.project_dir)
|
|
317
|
+
except Exception as exc: # pragma: no cover - defensive, mirrors withErrorContext
|
|
318
|
+
raise _with_error_context(f"initialize (project: {self.project_dir})", exc) from exc
|
|
319
|
+
|
|
320
|
+
def _ensure_initialized(self) -> tuple[tuple[str, list[str]], dict[str, str]]:
|
|
321
|
+
if self._command is None or self._env is None:
|
|
322
|
+
raise RuntimeError("CdkToolkitWrapper not initialized. Call initialize() first.")
|
|
323
|
+
return self._command, self._env
|
|
324
|
+
|
|
325
|
+
def _run(self, args: Sequence[str]) -> SubprocessResult:
|
|
326
|
+
(command, prefix), env = self._ensure_initialized()
|
|
327
|
+
runner = self._runner or run_subprocess_capture
|
|
328
|
+
return runner(command, [*prefix, *args], cwd=self.project_dir, env=env)
|
|
329
|
+
|
|
330
|
+
def _app_args(self) -> list[str]:
|
|
331
|
+
"""``--app`` for an operation: the synthesized assembly if any (no re-synth), else the app."""
|
|
332
|
+
return ["--app", self._assembly_dir or self.get_cdk_app_command()]
|
|
333
|
+
|
|
334
|
+
@staticmethod
|
|
335
|
+
def _selection_args(stacks: StackSelector | None) -> list[str]:
|
|
336
|
+
if stacks is None or stacks.strategy == StackSelectionStrategy.ALL_STACKS:
|
|
337
|
+
return ["--all"]
|
|
338
|
+
if stacks.patterns:
|
|
339
|
+
return list(stacks.patterns)
|
|
340
|
+
return ["--all"] if stacks.strategy == StackSelectionStrategy.MAIN_ASSEMBLY else []
|
|
341
|
+
|
|
342
|
+
def _notify(self, code: str, message: str, level: str = "info", data: Any = None, action: str | None = None):
|
|
343
|
+
self.io_host.notify(IoMessage(code=code, message=message, level=level, data=data, action=action))
|
|
344
|
+
|
|
345
|
+
def _forward_output(self, result: SubprocessResult, action: str) -> None:
|
|
346
|
+
"""Pass the CLI's own output lines to the IoHost (visible only to verbose/raw listeners)."""
|
|
347
|
+
for stream, level in ((result.stdout, "info"), (result.stderr, "info")):
|
|
348
|
+
for line in (stream or "").splitlines():
|
|
349
|
+
if line.strip():
|
|
350
|
+
self._notify("CDK_TOOLKIT_I0000", _strip_ansi(line), level, action=action)
|
|
351
|
+
|
|
352
|
+
# -- synth -------------------------------------------------------------------------------
|
|
353
|
+
|
|
354
|
+
def synth(self) -> SynthOutput:
|
|
355
|
+
"""Synthesize the app; later deploy/diff/destroy reuse the produced assembly."""
|
|
356
|
+
self._ensure_initialized()
|
|
357
|
+
self.dispose_synth_result()
|
|
358
|
+
out_dir = str(Path(self.project_dir) / CDK_OUT_DIR)
|
|
359
|
+
result = self._run(
|
|
360
|
+
[
|
|
361
|
+
"synth",
|
|
362
|
+
"--app",
|
|
363
|
+
self.get_cdk_app_command(),
|
|
364
|
+
"--output",
|
|
365
|
+
out_dir,
|
|
366
|
+
"--quiet",
|
|
367
|
+
"--validation",
|
|
368
|
+
"--no-notices",
|
|
369
|
+
]
|
|
370
|
+
)
|
|
371
|
+
self._forward_output(result, "synth")
|
|
372
|
+
if result.code != 0:
|
|
373
|
+
raise _with_error_context("synth", CdkCommandError(_failure_text(result), result))
|
|
374
|
+
self._assembly_dir = out_dir
|
|
375
|
+
self._stack_names = read_assembly_stack_names(out_dir)
|
|
376
|
+
return SynthOutput(stack_names=list(self._stack_names), assembly_directory=out_dir)
|
|
377
|
+
|
|
378
|
+
def dispose_synth_result(self) -> None:
|
|
379
|
+
self._assembly_dir = None
|
|
380
|
+
self._stack_names = []
|
|
381
|
+
|
|
382
|
+
def dispose(self) -> None:
|
|
383
|
+
"""Release state. The CLI holds no locks once it exits, so there is nothing else to free."""
|
|
384
|
+
self.dispose_synth_result()
|
|
385
|
+
self._command = None
|
|
386
|
+
self._env = None
|
|
387
|
+
|
|
388
|
+
# -- deploy ------------------------------------------------------------------------------
|
|
389
|
+
|
|
390
|
+
def deploy(self, stacks: StackSelector | None = None) -> None:
|
|
391
|
+
"""Deploy, retrying transient changeset conflicts (5s, 10s backoff, 3 attempts)."""
|
|
392
|
+
self._ensure_initialized()
|
|
393
|
+
last_error: BaseException | None = None
|
|
394
|
+
for attempt in range(DEPLOY_MAX_RETRIES):
|
|
395
|
+
try:
|
|
396
|
+
self._deploy_once(stacks)
|
|
397
|
+
return
|
|
398
|
+
except Exception as exc:
|
|
399
|
+
last_error = _with_error_context("deploy", exc)
|
|
400
|
+
if is_changeset_in_progress_error(last_error) and attempt < DEPLOY_MAX_RETRIES - 1:
|
|
401
|
+
delay = DEPLOY_BASE_DELAY_S * (2**attempt)
|
|
402
|
+
self._notify(
|
|
403
|
+
"CDK_TOOLKIT_W0001",
|
|
404
|
+
f"Changeset operation in progress, retrying in {delay:g}s "
|
|
405
|
+
f"(attempt {attempt + 1}/{DEPLOY_MAX_RETRIES})...",
|
|
406
|
+
level="warn",
|
|
407
|
+
action="deploy",
|
|
408
|
+
)
|
|
409
|
+
self._sleep(delay)
|
|
410
|
+
continue
|
|
411
|
+
raise last_error from exc
|
|
412
|
+
raise last_error or RuntimeError("Deploy failed after retries") # pragma: no cover
|
|
413
|
+
|
|
414
|
+
def _deploy_once(self, stacks: StackSelector | None) -> None:
|
|
415
|
+
outputs_file = str(Path(self._assembly_dir or Path(self.project_dir) / CDK_OUT_DIR) / "agentcore-outputs.json")
|
|
416
|
+
args = [
|
|
417
|
+
"deploy",
|
|
418
|
+
*self._selection_args(stacks),
|
|
419
|
+
*self._app_args(),
|
|
420
|
+
"--require-approval",
|
|
421
|
+
"never",
|
|
422
|
+
"--ci",
|
|
423
|
+
"--progress",
|
|
424
|
+
"events",
|
|
425
|
+
"--no-notices",
|
|
426
|
+
"--outputs-file",
|
|
427
|
+
outputs_file,
|
|
428
|
+
]
|
|
429
|
+
stack_names = self._target_stack_names(stacks)
|
|
430
|
+
region = (self._env or {}).get("AWS_REGION")
|
|
431
|
+
pollers = [StackEventPoller(region=region, stack_name=name) for name in stack_names] if region else []
|
|
432
|
+
totals = {name: self._template_resource_count(name) for name in stack_names}
|
|
433
|
+
completed: dict[str, set[str]] = {name: set() for name in stack_names}
|
|
434
|
+
|
|
435
|
+
holder: dict[str, Any] = {}
|
|
436
|
+
|
|
437
|
+
def run() -> None:
|
|
438
|
+
try:
|
|
439
|
+
holder["result"] = self._run(args)
|
|
440
|
+
except BaseException as exc: # noqa: BLE001 - re-raised on the calling thread
|
|
441
|
+
holder["error"] = exc
|
|
442
|
+
|
|
443
|
+
started = False
|
|
444
|
+
thread = threading.Thread(target=run, name="cdk-deploy", daemon=True)
|
|
445
|
+
thread.start()
|
|
446
|
+
|
|
447
|
+
def drain() -> None:
|
|
448
|
+
nonlocal started
|
|
449
|
+
for poller in pollers:
|
|
450
|
+
for event in poller.poll():
|
|
451
|
+
if not started:
|
|
452
|
+
started = True
|
|
453
|
+
self._notify("CDK_TOOLKIT_I5501", f"Deploying {poller.stack_name}", action="deploy")
|
|
454
|
+
logical = event.get("LogicalResourceId") or ""
|
|
455
|
+
status = event.get("ResourceStatus") or ""
|
|
456
|
+
if logical != poller.stack_name and status.endswith("_COMPLETE"):
|
|
457
|
+
completed[poller.stack_name].add(logical)
|
|
458
|
+
total = max(totals.get(poller.stack_name, 0), len(completed[poller.stack_name]))
|
|
459
|
+
line = format_stack_event(poller.stack_name, event, len(completed[poller.stack_name]), total)
|
|
460
|
+
level = "error" if status.endswith("FAILED") else "info"
|
|
461
|
+
self._notify("CDK_TOOLKIT_I5502", line, level, data=event, action="deploy")
|
|
462
|
+
|
|
463
|
+
while thread.is_alive():
|
|
464
|
+
thread.join(self.poll_interval)
|
|
465
|
+
drain()
|
|
466
|
+
drain()
|
|
467
|
+
|
|
468
|
+
if "error" in holder:
|
|
469
|
+
raise holder["error"]
|
|
470
|
+
result: SubprocessResult = holder["result"]
|
|
471
|
+
self._forward_output(result, "deploy")
|
|
472
|
+
if started:
|
|
473
|
+
self._notify("CDK_TOOLKIT_I5503", "Stack monitoring complete", action="deploy")
|
|
474
|
+
if result.code != 0:
|
|
475
|
+
message = _failure_text(result)
|
|
476
|
+
failures = describe_failures([f for p in pollers for f in p.failures])
|
|
477
|
+
if failures:
|
|
478
|
+
message = f"{message}\n\nResource failures:\n{failures}"
|
|
479
|
+
raise CdkCommandError(message, result)
|
|
480
|
+
|
|
481
|
+
stack_outputs = _read_outputs_file(outputs_file)
|
|
482
|
+
for name in stack_names or list(stack_outputs):
|
|
483
|
+
outputs = stack_outputs.get(name, {})
|
|
484
|
+
self._notify(
|
|
485
|
+
"CDK_TOOLKIT_I5900",
|
|
486
|
+
f"✅ {name}",
|
|
487
|
+
data={"outputs": outputs, "stackName": name},
|
|
488
|
+
action="deploy",
|
|
489
|
+
)
|
|
490
|
+
self._notify("CDK_TOOLKIT_I5901", "Deployment success", action="deploy")
|
|
491
|
+
|
|
492
|
+
def _target_stack_names(self, stacks: StackSelector | None) -> list[str]:
|
|
493
|
+
if stacks is not None and stacks.patterns and not any(ch in p for p in stacks.patterns for ch in "*?"):
|
|
494
|
+
return list(stacks.patterns)
|
|
495
|
+
return list(self._stack_names)
|
|
496
|
+
|
|
497
|
+
def _template_resource_count(self, stack_name: str) -> int:
|
|
498
|
+
if not self._assembly_dir:
|
|
499
|
+
return 0
|
|
500
|
+
template = read_assembly_template(self._assembly_dir, stack_name)
|
|
501
|
+
return len((template or {}).get("Resources") or {})
|
|
502
|
+
|
|
503
|
+
# -- destroy / diff / list / bootstrap ---------------------------------------------------
|
|
504
|
+
|
|
505
|
+
def destroy(self, stacks: StackSelector | None = None) -> None:
|
|
506
|
+
self._ensure_initialized()
|
|
507
|
+
result = self._run(["destroy", *self._selection_args(stacks), *self._app_args(), "--force", "--no-notices"])
|
|
508
|
+
self._forward_output(result, "destroy")
|
|
509
|
+
if result.code != 0:
|
|
510
|
+
raise _with_error_context("destroy", CdkCommandError(_failure_text(result), result))
|
|
511
|
+
|
|
512
|
+
def diff(self, stacks: StackSelector | None = None) -> None:
|
|
513
|
+
"""Run ``cdk diff``; per-stack blocks go out as I4002, the summary line as I4001."""
|
|
514
|
+
self._ensure_initialized()
|
|
515
|
+
# Without --fail, `cdk diff` exits 0 whether or not there are differences.
|
|
516
|
+
result = self._run(["diff", *self._selection_args(stacks), *self._app_args(), "--no-notices"])
|
|
517
|
+
if result.code != 0:
|
|
518
|
+
raise _with_error_context("diff", CdkCommandError(_failure_text(result), result))
|
|
519
|
+
for code, text in split_diff_output((result.stderr or "") + "\n" + (result.stdout or "")):
|
|
520
|
+
self._notify(code, text, action="diff")
|
|
521
|
+
|
|
522
|
+
def list(self, stacks: StackSelector | None = None) -> list[str]:
|
|
523
|
+
self._ensure_initialized()
|
|
524
|
+
result = self._run(["list", *self._selection_args(stacks), *self._app_args(), "--no-notices"])
|
|
525
|
+
if result.code != 0:
|
|
526
|
+
raise _with_error_context("list", CdkCommandError(_failure_text(result), result))
|
|
527
|
+
return [line.strip() for line in (result.stdout or "").splitlines() if line.strip()]
|
|
528
|
+
|
|
529
|
+
def bootstrap(self, environments: Sequence[str], kms_key_id: str | None = None) -> None:
|
|
530
|
+
"""Bootstrap the CDK toolkit stack; creates a KMS CMK for the staging bucket by default."""
|
|
531
|
+
self._ensure_initialized()
|
|
532
|
+
params = ["--bootstrap-kms-key-id", kms_key_id] if kms_key_id else ["--bootstrap-customer-key"]
|
|
533
|
+
result = self._run(["bootstrap", *environments, *self._app_args(), *params, "--ci", "--no-notices"])
|
|
534
|
+
self._forward_output(result, "bootstrap")
|
|
535
|
+
if result.code != 0:
|
|
536
|
+
message = _failure_text(result)
|
|
537
|
+
for env in environments:
|
|
538
|
+
message += explain_bootstrap_failure(env, self.profile)
|
|
539
|
+
raise _with_error_context("bootstrap", CdkCommandError(message, result))
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
BOOTSTRAP_STACK_NAME = "CDKToolkit"
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def explain_bootstrap_failure(environment: str, profile: str | None = None) -> str:
|
|
546
|
+
"""Why the CDKToolkit stack failed, from its CloudFormation events (best effort).
|
|
547
|
+
|
|
548
|
+
The cdk CLI often reports only "NoStack: CloudFormationStack object does not hold a stack";
|
|
549
|
+
the actionable reason (e.g. missing iam:CreateRole) is in the stack events.
|
|
550
|
+
"""
|
|
551
|
+
match = re.match(r"^aws://([^/]+)/(.+)$", environment)
|
|
552
|
+
if not match:
|
|
553
|
+
return ""
|
|
554
|
+
region = match.group(2)
|
|
555
|
+
try:
|
|
556
|
+
from agentcore.aws.account import create_aws_client
|
|
557
|
+
|
|
558
|
+
cfn = create_aws_client("cloudformation", region)
|
|
559
|
+
status = cfn.describe_stacks(StackName=BOOTSTRAP_STACK_NAME)["Stacks"][0]["StackStatus"]
|
|
560
|
+
events = cfn.describe_stack_events(StackName=BOOTSTRAP_STACK_NAME)["StackEvents"]
|
|
561
|
+
except Exception: # noqa: BLE001 - diagnostics only; the original error still surfaces
|
|
562
|
+
return ""
|
|
563
|
+
|
|
564
|
+
reasons: list[str] = []
|
|
565
|
+
for event in events: # newest first; stop at the start of the latest attempt
|
|
566
|
+
if (
|
|
567
|
+
event.get("LogicalResourceId") == BOOTSTRAP_STACK_NAME
|
|
568
|
+
and event.get("ResourceStatus", "").endswith("_IN_PROGRESS")
|
|
569
|
+
and event.get("ResourceStatusReason") == "User Initiated"
|
|
570
|
+
):
|
|
571
|
+
break
|
|
572
|
+
if event.get("ResourceStatus", "").endswith("_FAILED") and event.get("ResourceStatusReason"):
|
|
573
|
+
line = f"{event['LogicalResourceId']}: {event['ResourceStatusReason']}"
|
|
574
|
+
if line not in reasons:
|
|
575
|
+
reasons.append(line)
|
|
576
|
+
|
|
577
|
+
lines: list[str] = []
|
|
578
|
+
if reasons:
|
|
579
|
+
lines.append(f"\n\nCloudFormation reported for {BOOTSTRAP_STACK_NAME} in {region}:")
|
|
580
|
+
lines.extend(f" - {r}" for r in reasons[:3])
|
|
581
|
+
if any("iam:" in r and "not authorized" in r for r in reasons):
|
|
582
|
+
lines.append(
|
|
583
|
+
"\nYour AWS identity cannot create IAM roles, which CDK bootstrap (and deploy) requires. "
|
|
584
|
+
"Use a role with IAM permissions, e.g. AdministratorAccess in a sandbox account."
|
|
585
|
+
)
|
|
586
|
+
if status in ("ROLLBACK_COMPLETE", "ROLLBACK_FAILED", "DELETE_FAILED"):
|
|
587
|
+
profile_flag = f" --profile {profile}" if profile else ""
|
|
588
|
+
lines.append(
|
|
589
|
+
f"\nThe {BOOTSTRAP_STACK_NAME} stack is in {status} and must be deleted before retrying:\n"
|
|
590
|
+
f" aws cloudformation delete-stack --stack-name {BOOTSTRAP_STACK_NAME} --region {region}{profile_flag}"
|
|
591
|
+
)
|
|
592
|
+
return "\n".join(lines)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def create_cdk_toolkit_wrapper(
|
|
596
|
+
project_dir: str | os.PathLike[str] | None = None,
|
|
597
|
+
*,
|
|
598
|
+
io_host: IoHost | None = None,
|
|
599
|
+
profile: str | None = None,
|
|
600
|
+
region: str | None = None,
|
|
601
|
+
**kwargs: Any,
|
|
602
|
+
) -> CdkToolkitWrapper:
|
|
603
|
+
"""Create and initialize a :class:`CdkToolkitWrapper`."""
|
|
604
|
+
wrapper = CdkToolkitWrapper(project_dir, io_host=io_host, profile=profile, region=region, **kwargs)
|
|
605
|
+
wrapper.initialize()
|
|
606
|
+
return wrapper
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
# ---------------------------------------------------------------------------
|
|
610
|
+
# Cloud assembly helpers
|
|
611
|
+
# ---------------------------------------------------------------------------
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def _read_json(path: Path) -> Any:
|
|
615
|
+
try:
|
|
616
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
617
|
+
except (OSError, ValueError):
|
|
618
|
+
return None
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def _stack_artifacts(assembly_dir: str | os.PathLike[str]) -> list[tuple[str, dict[str, Any]]]:
|
|
622
|
+
manifest = _read_json(Path(assembly_dir) / "manifest.json") or {}
|
|
623
|
+
artifacts = manifest.get("artifacts") or {}
|
|
624
|
+
return [
|
|
625
|
+
(artifact_id, artifact)
|
|
626
|
+
for artifact_id, artifact in artifacts.items()
|
|
627
|
+
if isinstance(artifact, dict) and artifact.get("type") == "aws:cloudformation:stack"
|
|
628
|
+
]
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def read_assembly_stack_names(assembly_dir: str | os.PathLike[str]) -> list[str]:
|
|
632
|
+
"""Stack names in a synthesized cloud assembly (``manifest.json``), in manifest order."""
|
|
633
|
+
names = []
|
|
634
|
+
for artifact_id, artifact in _stack_artifacts(assembly_dir):
|
|
635
|
+
props = artifact.get("properties") or {}
|
|
636
|
+
names.append(props.get("stackName") or artifact.get("displayName") or artifact_id)
|
|
637
|
+
return names
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def read_assembly_template(assembly_dir: str | os.PathLike[str], stack_name: str) -> dict[str, Any] | None:
|
|
641
|
+
for artifact_id, artifact in _stack_artifacts(assembly_dir):
|
|
642
|
+
props = artifact.get("properties") or {}
|
|
643
|
+
if (props.get("stackName") or artifact_id) == stack_name:
|
|
644
|
+
template_file = props.get("templateFile") or f"{artifact_id}.template.json"
|
|
645
|
+
return _read_json(Path(assembly_dir) / template_file)
|
|
646
|
+
return None
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def _read_outputs_file(path: str) -> dict[str, dict[str, str]]:
|
|
650
|
+
data = _read_json(Path(path))
|
|
651
|
+
if not isinstance(data, dict):
|
|
652
|
+
return {}
|
|
653
|
+
return {k: {str(ok): str(ov) for ok, ov in v.items()} for k, v in data.items() if isinstance(v, dict)}
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
_DIFF_SUMMARY_RE = re.compile(r"Number of stacks with differences:\s*\d+")
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def split_diff_output(text: str) -> list[tuple[str, str]]:
|
|
660
|
+
"""Split ``cdk diff`` output into per-stack blocks (I4002) and the summary line (I4001)."""
|
|
661
|
+
clean = _strip_ansi(text)
|
|
662
|
+
messages: list[tuple[str, str]] = []
|
|
663
|
+
current: list[str] = []
|
|
664
|
+
summary: str | None = None
|
|
665
|
+
for line in clean.splitlines():
|
|
666
|
+
if _DIFF_SUMMARY_RE.search(line):
|
|
667
|
+
summary = line.strip()
|
|
668
|
+
continue
|
|
669
|
+
if line.startswith("Stack "):
|
|
670
|
+
if any(item.strip() for item in current):
|
|
671
|
+
messages.append(("CDK_TOOLKIT_I4002", "\n".join(current).strip("\n")))
|
|
672
|
+
current = [line]
|
|
673
|
+
continue
|
|
674
|
+
if current:
|
|
675
|
+
current.append(line)
|
|
676
|
+
if any(item.strip() for item in current):
|
|
677
|
+
messages.append(("CDK_TOOLKIT_I4002", "\n".join(current).strip("\n")))
|
|
678
|
+
if summary:
|
|
679
|
+
messages.append(("CDK_TOOLKIT_I4001", summary))
|
|
680
|
+
return messages
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
__all__ = [
|
|
684
|
+
"CDK_APP_COMMAND",
|
|
685
|
+
"CDK_OUT_DIR",
|
|
686
|
+
"DEPLOY_MESSAGE_CODES",
|
|
687
|
+
"CdkCommandError",
|
|
688
|
+
"CdkToolkitWrapper",
|
|
689
|
+
"DeployMessage",
|
|
690
|
+
"IoHost",
|
|
691
|
+
"IoMessage",
|
|
692
|
+
"ResourceProgress",
|
|
693
|
+
"StackSelectionStrategy",
|
|
694
|
+
"StackSelector",
|
|
695
|
+
"SwitchableIoHost",
|
|
696
|
+
"SynthOutput",
|
|
697
|
+
"create_cdk_toolkit_wrapper",
|
|
698
|
+
"create_switchable_io_host",
|
|
699
|
+
"extract_progress_from_message",
|
|
700
|
+
"pattern_must_match",
|
|
701
|
+
"read_assembly_stack_names",
|
|
702
|
+
"read_assembly_template",
|
|
703
|
+
"resolve_cdk_command",
|
|
704
|
+
"silent_io_host",
|
|
705
|
+
"split_diff_output",
|
|
706
|
+
]
|