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
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
"""Read/validate/write AgentCore config files (port of ``config-io.ts``).
|
|
2
|
+
|
|
3
|
+
Schema models come from ``agentcore.schema`` and are imported lazily. Reads return validated
|
|
4
|
+
Pydantic models; writes accept models or plain dicts, validate, and write
|
|
5
|
+
``JSON.stringify(data, null, 2)``-identical output (2-space indent, no trailing newline)
|
|
6
|
+
atomically through a sibling temp file + rename.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import time
|
|
14
|
+
import uuid
|
|
15
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
16
|
+
from contextlib import contextmanager
|
|
17
|
+
from typing import Any, Literal, TypeVar
|
|
18
|
+
|
|
19
|
+
from pydantic import BaseModel, TypeAdapter
|
|
20
|
+
from pydantic import ValidationError as PydanticValidationError
|
|
21
|
+
|
|
22
|
+
from agentcore.lib.errors.types import (
|
|
23
|
+
ConfigNotFoundError,
|
|
24
|
+
ConfigParseError,
|
|
25
|
+
ConfigReadError,
|
|
26
|
+
ConfigValidationError,
|
|
27
|
+
ConfigWriteError,
|
|
28
|
+
NoProjectError,
|
|
29
|
+
)
|
|
30
|
+
from agentcore.lib.schemas.io.path_resolver import PathConfig, PathResolver, find_config_root
|
|
31
|
+
from agentcore.lib.utils.aws_account import detect_aws_account
|
|
32
|
+
from agentcore.lib.utils.json_format import to_json_string
|
|
33
|
+
|
|
34
|
+
T = TypeVar("T")
|
|
35
|
+
|
|
36
|
+
SchemaVersion = Literal[1]
|
|
37
|
+
|
|
38
|
+
PROJECT_LOCK_RETRY_MS = 25
|
|
39
|
+
PROJECT_LOCK_TIMEOUT_MS = 45_000
|
|
40
|
+
PROJECT_LOCK_STALE_MS = 60_000
|
|
41
|
+
|
|
42
|
+
# Mirrors AgentCoreRegionSchema; used only if agentcore.schema doesn't expose the region type.
|
|
43
|
+
_FALLBACK_REGIONS = frozenset(
|
|
44
|
+
{
|
|
45
|
+
"ap-northeast-1", "ap-northeast-2", "ap-south-1", "ap-south-2", "ap-southeast-1", "ap-southeast-2",
|
|
46
|
+
"ap-southeast-5", "ap-southeast-7", "ca-central-1", "eu-central-1", "eu-north-1", "eu-south-1",
|
|
47
|
+
"eu-south-2", "eu-west-1", "eu-west-2", "eu-west-3", "sa-east-1", "us-east-1", "us-east-2",
|
|
48
|
+
"us-west-1", "us-west-2", "us-gov-west-1",
|
|
49
|
+
}
|
|
50
|
+
) # fmt: skip
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_schema_url_for_version(version: SchemaVersion) -> str:
|
|
54
|
+
return f"https://schema.agentcore.aws.dev/v{version}/agentcore.json"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# --- Schema bridge -----------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
Validator = type[BaseModel] | TypeAdapter[Any]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _schema_attr(name: str) -> Any:
|
|
63
|
+
import agentcore.schema as schema
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
return getattr(schema, name)
|
|
67
|
+
except AttributeError as exc:
|
|
68
|
+
raise ImportError(f"agentcore.schema does not define {name}") from exc
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _targets_validator() -> Validator:
|
|
72
|
+
try:
|
|
73
|
+
import agentcore.schema as schema
|
|
74
|
+
|
|
75
|
+
adapter = getattr(schema, "AwsDeploymentTargetsSchema", None)
|
|
76
|
+
except ImportError:
|
|
77
|
+
adapter = None
|
|
78
|
+
if isinstance(adapter, TypeAdapter) or (isinstance(adapter, type) and issubclass(adapter, BaseModel)):
|
|
79
|
+
return adapter
|
|
80
|
+
return TypeAdapter(list[_schema_attr("AwsDeploymentTarget")]) # type: ignore[misc]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def to_jsonable(value: Any) -> Any:
|
|
84
|
+
"""Models -> ``model_dump(mode="json", by_alias=True)``, recursively.
|
|
85
|
+
|
|
86
|
+
Not ``exclude_none``: the schema models already omit unset optionals, and ``exclude_none``
|
|
87
|
+
would also strip ``null``s inside free-form payloads that upstream preserves.
|
|
88
|
+
"""
|
|
89
|
+
if isinstance(value, BaseModel):
|
|
90
|
+
return value.model_dump(mode="json", by_alias=True)
|
|
91
|
+
if isinstance(value, Mapping):
|
|
92
|
+
return {k: to_jsonable(v) for k, v in value.items()}
|
|
93
|
+
if isinstance(value, (list, tuple)):
|
|
94
|
+
return [to_jsonable(v) for v in value]
|
|
95
|
+
return value
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _run_validator(validator: Validator, data: Any) -> Any:
|
|
99
|
+
if isinstance(validator, TypeAdapter):
|
|
100
|
+
return validator.validate_python(data)
|
|
101
|
+
return validator.model_validate(data)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _dump(validator: Validator, value: Any) -> Any:
|
|
105
|
+
if isinstance(validator, TypeAdapter):
|
|
106
|
+
return validator.dump_python(value, mode="json", by_alias=True)
|
|
107
|
+
return to_jsonable(value)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _unique_by_name(items: Any, message: Callable[[str], str]) -> list[dict[str, Any]]:
|
|
111
|
+
issues: list[dict[str, Any]] = []
|
|
112
|
+
seen: set[str] = set()
|
|
113
|
+
for idx, item in enumerate(items or []):
|
|
114
|
+
name = item.get("name") if isinstance(item, Mapping) else getattr(item, "name", None)
|
|
115
|
+
if name in seen:
|
|
116
|
+
issues.append({"type": "custom", "loc": (idx,), "msg": message(str(name))})
|
|
117
|
+
seen.add(name)
|
|
118
|
+
return issues
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class ConfigIO:
|
|
122
|
+
"""Reads, writes and validates AgentCore configuration files."""
|
|
123
|
+
|
|
124
|
+
def __init__(self, path_config: PathConfig | Mapping[str, str] | None = None, *, base_dir: str | None = None):
|
|
125
|
+
if isinstance(path_config, Mapping):
|
|
126
|
+
base_dir = base_dir or path_config.get("base_dir") or path_config.get("baseDir")
|
|
127
|
+
elif isinstance(path_config, PathConfig):
|
|
128
|
+
base_dir = base_dir or path_config.base_dir
|
|
129
|
+
|
|
130
|
+
if base_dir:
|
|
131
|
+
# Explicit base dir (e.g. during project creation).
|
|
132
|
+
self._project_discovered = True
|
|
133
|
+
else:
|
|
134
|
+
discovered = find_config_root()
|
|
135
|
+
base_dir = discovered
|
|
136
|
+
self._project_discovered = discovered is not None
|
|
137
|
+
|
|
138
|
+
self._path_resolver = PathResolver(base_dir=base_dir)
|
|
139
|
+
|
|
140
|
+
def has_project(self) -> bool:
|
|
141
|
+
"""False when no base dir was given and auto-discovery found no project."""
|
|
142
|
+
return self._project_discovered
|
|
143
|
+
|
|
144
|
+
def get_path_resolver(self) -> PathResolver:
|
|
145
|
+
return self._path_resolver
|
|
146
|
+
|
|
147
|
+
def set_base_dir(self, base_dir: str) -> None:
|
|
148
|
+
self._path_resolver.set_base_dir(base_dir)
|
|
149
|
+
|
|
150
|
+
def get_project_root(self) -> str:
|
|
151
|
+
return self._path_resolver.get_project_root()
|
|
152
|
+
|
|
153
|
+
def get_config_root(self) -> str:
|
|
154
|
+
return self._path_resolver.get_base_dir()
|
|
155
|
+
|
|
156
|
+
def get_agent_config_path(self) -> str:
|
|
157
|
+
return self._path_resolver.get_agent_config_path()
|
|
158
|
+
|
|
159
|
+
def get_harness_config_path(self, harness_name: str) -> str:
|
|
160
|
+
return self._path_resolver.get_harness_config_path(harness_name)
|
|
161
|
+
|
|
162
|
+
# --- Project spec ---
|
|
163
|
+
|
|
164
|
+
def read_project_spec(self) -> Any:
|
|
165
|
+
return self._read_and_validate(
|
|
166
|
+
self._path_resolver.get_agent_config_path(),
|
|
167
|
+
"AgentCore Project Config",
|
|
168
|
+
_schema_attr("AgentCoreProjectSpec"),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def write_project_spec(self, data: Any) -> None:
|
|
172
|
+
cleaned = to_jsonable(data)
|
|
173
|
+
# TODO (upstream): extend to all resource arrays so empty defaults never pollute agentcore.json
|
|
174
|
+
if isinstance(cleaned, dict):
|
|
175
|
+
for key in ("configBundles", "abTests"):
|
|
176
|
+
if cleaned.get(key) == []:
|
|
177
|
+
del cleaned[key]
|
|
178
|
+
self._validate_and_write(
|
|
179
|
+
self._path_resolver.get_agent_config_path(),
|
|
180
|
+
"AgentCore Project Config",
|
|
181
|
+
_schema_attr("AgentCoreProjectSpec"),
|
|
182
|
+
cleaned,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
def update_project_spec(self, mutate: Callable[[Any], Any]) -> Any:
|
|
186
|
+
"""Read, mutate, validate and write ``agentcore.json`` under a cross-process lock.
|
|
187
|
+
|
|
188
|
+
``mutate`` may modify the project in place (returning ``None``) or return a replacement.
|
|
189
|
+
"""
|
|
190
|
+
with self._file_lock(self._path_resolver.get_agent_config_path()):
|
|
191
|
+
project = self.read_project_spec()
|
|
192
|
+
result = mutate(project)
|
|
193
|
+
updated = project if result is None else result
|
|
194
|
+
self.write_project_spec(updated)
|
|
195
|
+
return updated
|
|
196
|
+
|
|
197
|
+
# --- AWS targets ---
|
|
198
|
+
|
|
199
|
+
def read_aws_deployment_targets(self) -> list[Any]:
|
|
200
|
+
"""Read ``aws-targets.json``. With ``AWS_PROFILE`` set, the account comes from the credentials."""
|
|
201
|
+
targets = self._read_targets()
|
|
202
|
+
if os.environ.get("AWS_PROFILE"):
|
|
203
|
+
account = detect_aws_account()
|
|
204
|
+
if account:
|
|
205
|
+
targets = [_with_field(t, "account", account) for t in targets]
|
|
206
|
+
return targets
|
|
207
|
+
|
|
208
|
+
def resolve_aws_deployment_targets(self) -> list[Any]:
|
|
209
|
+
"""Targets with region fallback: saved > AWS_REGION > AWS_DEFAULT_REGION > profile config."""
|
|
210
|
+
targets = self.read_aws_deployment_targets()
|
|
211
|
+
fallback_region = self._resolve_region_fallback()
|
|
212
|
+
if not fallback_region:
|
|
213
|
+
return targets
|
|
214
|
+
return [t if _get_field(t, "region") else _with_field(t, "region", fallback_region) for t in targets]
|
|
215
|
+
|
|
216
|
+
def _resolve_region_fallback(self) -> str | None:
|
|
217
|
+
env_region = os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION")
|
|
218
|
+
if env_region and _is_agentcore_region(env_region):
|
|
219
|
+
return env_region
|
|
220
|
+
try:
|
|
221
|
+
import botocore.session
|
|
222
|
+
|
|
223
|
+
profile = os.environ.get("AWS_PROFILE") or "default"
|
|
224
|
+
profiles = botocore.session.Session().full_config.get("profiles", {})
|
|
225
|
+
profile_region = profiles.get(profile, {}).get("region")
|
|
226
|
+
if profile_region and _is_agentcore_region(profile_region):
|
|
227
|
+
return profile_region
|
|
228
|
+
except Exception:
|
|
229
|
+
pass # config file not available
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
def write_aws_deployment_targets(self, data: Sequence[Any]) -> None:
|
|
233
|
+
file_path = self._path_resolver.get_aws_targets_config_path()
|
|
234
|
+
|
|
235
|
+
def check(targets: Any) -> None:
|
|
236
|
+
issues = _unique_by_name(_as_list(targets), lambda n: f"Duplicate deployment target name: {n}")
|
|
237
|
+
if issues:
|
|
238
|
+
raise ConfigValidationError(file_path, "AWS Targets", issues)
|
|
239
|
+
|
|
240
|
+
self._validate_and_write(file_path, "AWS Targets", _targets_validator(), list(data), check=check)
|
|
241
|
+
|
|
242
|
+
def _read_targets(self) -> list[Any]:
|
|
243
|
+
file_path = self._path_resolver.get_aws_targets_config_path()
|
|
244
|
+
targets = self._read_and_validate(file_path, "AWS Targets", _targets_validator())
|
|
245
|
+
targets = _as_list(targets)
|
|
246
|
+
issues = _unique_by_name(targets, lambda n: f"Duplicate deployment target name: {n}")
|
|
247
|
+
if issues:
|
|
248
|
+
raise ConfigValidationError(file_path, "AWS Targets", issues)
|
|
249
|
+
return targets
|
|
250
|
+
|
|
251
|
+
# --- Deployed state ---
|
|
252
|
+
|
|
253
|
+
def _target_names(self) -> set[str]:
|
|
254
|
+
return {str(_get_field(t, "name")) for t in self.read_aws_deployment_targets()}
|
|
255
|
+
|
|
256
|
+
def read_deployed_state(self) -> Any:
|
|
257
|
+
"""Read ``.cli/deployed-state.json``; every target key must exist in aws-targets."""
|
|
258
|
+
names = self._target_names()
|
|
259
|
+
file_path = self._path_resolver.get_state_path()
|
|
260
|
+
state = self._read_and_validate(file_path, "State", _schema_attr("DeployedState"))
|
|
261
|
+
self._check_state_targets(file_path, state, names)
|
|
262
|
+
return state
|
|
263
|
+
|
|
264
|
+
def write_deployed_state(self, data: Any) -> None:
|
|
265
|
+
names = self._target_names()
|
|
266
|
+
file_path = self._path_resolver.get_state_path()
|
|
267
|
+
self._validate_and_write(
|
|
268
|
+
file_path,
|
|
269
|
+
"State",
|
|
270
|
+
_schema_attr("DeployedState"),
|
|
271
|
+
data,
|
|
272
|
+
check=lambda state: self._check_state_targets(file_path, state, names),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def _check_state_targets(self, file_path: str, state: Any, names: set[str]) -> None:
|
|
276
|
+
targets = _get_field(state, "targets")
|
|
277
|
+
keys = list(targets.keys()) if isinstance(targets, Mapping) else []
|
|
278
|
+
if all(k in names for k in keys):
|
|
279
|
+
return
|
|
280
|
+
raise ConfigValidationError(
|
|
281
|
+
file_path,
|
|
282
|
+
"State",
|
|
283
|
+
[{"type": "custom", "loc": (), "msg": "Deployed state contains target names not present in aws-targets"}],
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
# --- MCP defs / harness ---
|
|
287
|
+
|
|
288
|
+
def read_mcp_defs(self) -> Any:
|
|
289
|
+
return self._read_and_validate(
|
|
290
|
+
self._path_resolver.get_mcp_defs_path(), "MCP Definitions", _schema_attr("AgentCoreCliMcpDefs")
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def write_mcp_defs(self, data: Any) -> None:
|
|
294
|
+
self._validate_and_write(
|
|
295
|
+
self._path_resolver.get_mcp_defs_path(), "MCP Definitions", _schema_attr("AgentCoreCliMcpDefs"), data
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
def read_harness_spec(self, harness_name: str) -> Any:
|
|
299
|
+
return self._read_and_validate(
|
|
300
|
+
self._path_resolver.get_harness_config_path(harness_name), "Harness Spec", _schema_attr("HarnessSpec")
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
def write_harness_spec(self, harness_name: str, data: Any) -> None:
|
|
304
|
+
self._validate_and_write(
|
|
305
|
+
self._path_resolver.get_harness_config_path(harness_name), "Harness Spec", _schema_attr("HarnessSpec"), data
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# --- Existence / init ---
|
|
309
|
+
|
|
310
|
+
def base_dir_exists(self) -> bool:
|
|
311
|
+
return os.path.exists(self._path_resolver.get_base_dir())
|
|
312
|
+
|
|
313
|
+
def config_exists(self, kind: Literal["project", "awsTargets", "state", "mcpDefs"]) -> bool:
|
|
314
|
+
paths = {
|
|
315
|
+
"project": self._path_resolver.get_agent_config_path,
|
|
316
|
+
"awsTargets": self._path_resolver.get_aws_targets_config_path,
|
|
317
|
+
"state": self._path_resolver.get_state_path,
|
|
318
|
+
"mcpDefs": self._path_resolver.get_mcp_defs_path,
|
|
319
|
+
}
|
|
320
|
+
return os.path.exists(paths[kind]())
|
|
321
|
+
|
|
322
|
+
def initialize_base_dir(self) -> None:
|
|
323
|
+
"""Create ``agentcore/`` and ``agentcore/.cli/``. Requires a configured/discovered project."""
|
|
324
|
+
self._require_project()
|
|
325
|
+
base_dir = self._path_resolver.get_base_dir()
|
|
326
|
+
try:
|
|
327
|
+
os.makedirs(base_dir, exist_ok=True)
|
|
328
|
+
os.makedirs(self._path_resolver.get_cli_system_dir(), exist_ok=True)
|
|
329
|
+
except OSError as exc:
|
|
330
|
+
raise ConfigWriteError(base_dir, exc) from exc
|
|
331
|
+
|
|
332
|
+
# --- Internals ---
|
|
333
|
+
|
|
334
|
+
def _require_project(self) -> None:
|
|
335
|
+
if not self._project_discovered:
|
|
336
|
+
raise NoProjectError()
|
|
337
|
+
|
|
338
|
+
def _read_and_validate(self, file_path: str, file_type: str, validator: Validator) -> Any:
|
|
339
|
+
if not os.path.exists(file_path):
|
|
340
|
+
raise ConfigNotFoundError(file_path, file_type)
|
|
341
|
+
try:
|
|
342
|
+
with open(file_path, encoding="utf-8") as fh:
|
|
343
|
+
content = fh.read()
|
|
344
|
+
except (OSError, UnicodeDecodeError) as exc:
|
|
345
|
+
raise ConfigReadError(file_path, exc) from exc
|
|
346
|
+
try:
|
|
347
|
+
parsed = json.loads(content)
|
|
348
|
+
except ValueError as exc:
|
|
349
|
+
raise ConfigParseError(file_path, exc) from exc
|
|
350
|
+
try:
|
|
351
|
+
return _run_validator(validator, parsed)
|
|
352
|
+
except PydanticValidationError as exc:
|
|
353
|
+
raise ConfigValidationError(file_path, file_type, exc) from exc
|
|
354
|
+
|
|
355
|
+
def _validate_and_write(
|
|
356
|
+
self,
|
|
357
|
+
file_path: str,
|
|
358
|
+
file_type: str,
|
|
359
|
+
validator: Validator,
|
|
360
|
+
data: Any,
|
|
361
|
+
check: Callable[[Any], None] | None = None,
|
|
362
|
+
) -> None:
|
|
363
|
+
self._require_project()
|
|
364
|
+
try:
|
|
365
|
+
validated = _run_validator(validator, to_jsonable(data))
|
|
366
|
+
except PydanticValidationError as exc:
|
|
367
|
+
raise ConfigValidationError(file_path, file_type, exc) from exc
|
|
368
|
+
if check is not None:
|
|
369
|
+
check(validated)
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
373
|
+
except OSError as exc:
|
|
374
|
+
raise ConfigWriteError(file_path, exc) from exc
|
|
375
|
+
|
|
376
|
+
# Write a sibling temp file and rename so readers never observe partial JSON.
|
|
377
|
+
temp_path = f"{file_path}.{os.getpid()}.{uuid.uuid4()}.tmp"
|
|
378
|
+
try:
|
|
379
|
+
content = to_json_string(_dump(validator, validated), 2)
|
|
380
|
+
with open(temp_path, "w", encoding="utf-8", newline="") as fh:
|
|
381
|
+
fh.write(content)
|
|
382
|
+
os.replace(temp_path, file_path)
|
|
383
|
+
except Exception as exc:
|
|
384
|
+
try:
|
|
385
|
+
os.unlink(temp_path)
|
|
386
|
+
except OSError:
|
|
387
|
+
pass
|
|
388
|
+
raise ConfigWriteError(file_path, exc) from exc
|
|
389
|
+
|
|
390
|
+
@contextmanager
|
|
391
|
+
def _file_lock(self, file_path: str): # type: ignore[no-untyped-def]
|
|
392
|
+
"""Exclusive ``<file>.lock`` via O_EXCL; stale locks (>60s) are broken, waits time out at 45s."""
|
|
393
|
+
lock_path = f"{file_path}.lock"
|
|
394
|
+
started = time.monotonic()
|
|
395
|
+
fd: int | None = None
|
|
396
|
+
while fd is None:
|
|
397
|
+
try:
|
|
398
|
+
fd = os.open(lock_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o666)
|
|
399
|
+
except FileExistsError:
|
|
400
|
+
try:
|
|
401
|
+
if time.time() - os.stat(lock_path).st_mtime > PROJECT_LOCK_STALE_MS / 1000:
|
|
402
|
+
os.unlink(lock_path)
|
|
403
|
+
continue
|
|
404
|
+
except FileNotFoundError:
|
|
405
|
+
continue
|
|
406
|
+
except OSError as exc:
|
|
407
|
+
raise ConfigWriteError(file_path, exc) from exc
|
|
408
|
+
if (time.monotonic() - started) * 1000 >= PROJECT_LOCK_TIMEOUT_MS:
|
|
409
|
+
raise ConfigWriteError(
|
|
410
|
+
file_path, RuntimeError("Timed out waiting for another configuration update")
|
|
411
|
+
) from None
|
|
412
|
+
time.sleep(PROJECT_LOCK_RETRY_MS / 1000)
|
|
413
|
+
except OSError as exc:
|
|
414
|
+
raise ConfigWriteError(file_path, exc) from exc
|
|
415
|
+
try:
|
|
416
|
+
yield
|
|
417
|
+
finally:
|
|
418
|
+
os.close(fd)
|
|
419
|
+
try:
|
|
420
|
+
os.unlink(lock_path)
|
|
421
|
+
except FileNotFoundError:
|
|
422
|
+
pass
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def _as_list(value: Any) -> list[Any]:
|
|
426
|
+
return list(value.root) if hasattr(value, "root") else list(value)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _get_field(obj: Any, key: str) -> Any:
|
|
430
|
+
if isinstance(obj, Mapping):
|
|
431
|
+
return obj.get(key)
|
|
432
|
+
return getattr(obj, key, None)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def _with_field(obj: Any, key: str, value: Any) -> Any:
|
|
436
|
+
if isinstance(obj, BaseModel):
|
|
437
|
+
return obj.model_copy(update={key: value})
|
|
438
|
+
if isinstance(obj, Mapping):
|
|
439
|
+
return {**obj, key: value}
|
|
440
|
+
return obj
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _is_agentcore_region(region: str) -> bool:
|
|
444
|
+
try:
|
|
445
|
+
region_type = _schema_attr("AgentCoreRegion")
|
|
446
|
+
except ImportError:
|
|
447
|
+
return region in _FALLBACK_REGIONS
|
|
448
|
+
try:
|
|
449
|
+
TypeAdapter(region_type).validate_python(region)
|
|
450
|
+
return True
|
|
451
|
+
except PydanticValidationError:
|
|
452
|
+
return False
|
|
453
|
+
except Exception:
|
|
454
|
+
return region in _FALLBACK_REGIONS
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def create_config_io(path_config: PathConfig | Mapping[str, str] | None = None, **kwargs: Any) -> ConfigIO:
|
|
458
|
+
return ConfigIO(path_config, **kwargs)
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""User-level CLI config at ``~/.agentcore/config.json`` (port of ``global-config.ts``).
|
|
2
|
+
|
|
3
|
+
The config is a plain ``dict`` keyed by the on-disk camelCase names. Reads are resilient
|
|
4
|
+
(invalid fields are dropped, unknown keys preserved); ``validate_global_config`` is strict.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
import uuid
|
|
13
|
+
from collections.abc import Callable, Mapping
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from typing import Any, NotRequired, TypedDict
|
|
16
|
+
|
|
17
|
+
from agentcore.lib.result import Err, Ok
|
|
18
|
+
from agentcore.lib.utils.json_format import to_json_string
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TelemetryConfig(TypedDict, total=False):
|
|
22
|
+
enabled: bool
|
|
23
|
+
endpoint: str
|
|
24
|
+
audit: bool
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class GlobalConfig(TypedDict, total=False):
|
|
28
|
+
installationId: str
|
|
29
|
+
uvDefaultIndex: str
|
|
30
|
+
uvIndex: str
|
|
31
|
+
disableDependencyManagement: bool
|
|
32
|
+
disableTransactionSearch: bool
|
|
33
|
+
transactionSearchIndexPercentage: int
|
|
34
|
+
telemetry: NotRequired[TelemetryConfig]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_global_config_dir() -> str:
|
|
38
|
+
env_dir = os.environ.get("AGENTCORE_CONFIG_DIR")
|
|
39
|
+
return env_dir if env_dir is not None else os.path.join(os.path.expanduser("~"), ".agentcore")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_global_config_file() -> str:
|
|
43
|
+
return os.path.join(get_global_config_dir(), "config.json")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Snapshot at import time, like upstream. Functions resolve the location at call time instead
|
|
47
|
+
# so AGENTCORE_CONFIG_DIR changes (e.g. in tests) take effect.
|
|
48
|
+
GLOBAL_CONFIG_DIR = get_global_config_dir()
|
|
49
|
+
GLOBAL_CONFIG_FILE = get_global_config_file()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# --- Schema (strict, mirrors the Zod object) --------------------------------------------------
|
|
53
|
+
|
|
54
|
+
# Zod 4's z.string().uuid() pattern.
|
|
55
|
+
_UUID_RE = re.compile(
|
|
56
|
+
r"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}"
|
|
57
|
+
r"|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class _Invalid(Exception):
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _string(value: Any) -> str:
|
|
66
|
+
if not isinstance(value, str):
|
|
67
|
+
raise _Invalid("expected string")
|
|
68
|
+
return value
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _uuid(value: Any) -> str:
|
|
72
|
+
if not isinstance(value, str) or not _UUID_RE.fullmatch(value):
|
|
73
|
+
raise _Invalid("Invalid UUID")
|
|
74
|
+
return value
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _boolean(value: Any) -> bool:
|
|
78
|
+
if not isinstance(value, bool):
|
|
79
|
+
raise _Invalid("expected boolean")
|
|
80
|
+
return value
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _percentage(value: Any) -> int:
|
|
84
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
85
|
+
raise _Invalid("expected number")
|
|
86
|
+
if isinstance(value, float) and not value.is_integer():
|
|
87
|
+
raise _Invalid("expected int, received number")
|
|
88
|
+
if not 0 <= value <= 100:
|
|
89
|
+
raise _Invalid("Too small: expected number to be >=0" if value < 0 else "Too big: expected number to be <=100")
|
|
90
|
+
return int(value)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
_Validator = Callable[[Any], Any]
|
|
94
|
+
|
|
95
|
+
_TELEMETRY_SHAPE: dict[str, _Validator] = {"enabled": _boolean, "endpoint": _string, "audit": _boolean}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _strict_object(shape: Mapping[str, Any], value: Any, path: list[Any], issues: list[dict[str, Any]]) -> None:
|
|
99
|
+
if not isinstance(value, Mapping):
|
|
100
|
+
issues.append({"type": "dict_type", "loc": tuple(path), "msg": "expected object"})
|
|
101
|
+
return
|
|
102
|
+
for key, item in value.items():
|
|
103
|
+
if key not in shape:
|
|
104
|
+
issues.append({"type": "extra_forbidden", "loc": (*path, key), "msg": "Unrecognized key"})
|
|
105
|
+
continue
|
|
106
|
+
validator = shape[key]
|
|
107
|
+
if isinstance(validator, Mapping):
|
|
108
|
+
_strict_object(validator, item, [*path, key], issues)
|
|
109
|
+
continue
|
|
110
|
+
try:
|
|
111
|
+
validator(item)
|
|
112
|
+
except _Invalid as exc:
|
|
113
|
+
issues.append({"type": "value_error", "loc": (*path, key), "msg": str(exc), "input": item})
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
_GLOBAL_SHAPE: dict[str, Any] = {
|
|
117
|
+
"installationId": _uuid,
|
|
118
|
+
"uvDefaultIndex": _string,
|
|
119
|
+
"uvIndex": _string,
|
|
120
|
+
"disableDependencyManagement": _boolean,
|
|
121
|
+
"disableTransactionSearch": _boolean,
|
|
122
|
+
"transactionSearchIndexPercentage": _percentage,
|
|
123
|
+
"telemetry": _TELEMETRY_SHAPE,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass(frozen=True, slots=True)
|
|
128
|
+
class GlobalConfigValidation:
|
|
129
|
+
success: bool
|
|
130
|
+
issues: list[dict[str, Any]] = field(default_factory=list)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def validate_global_config(data: Any) -> GlobalConfigValidation:
|
|
134
|
+
"""Strictly validate a (partial) config: unknown keys and wrong types fail."""
|
|
135
|
+
issues: list[dict[str, Any]] = []
|
|
136
|
+
_strict_object(_GLOBAL_SHAPE, data, [], issues)
|
|
137
|
+
return GlobalConfigValidation(success=not issues, issues=issues)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _resilient(shape: Mapping[str, Any], data: Any) -> dict[str, Any]:
|
|
141
|
+
"""Field-by-field parse: invalid known fields are dropped, unknown keys kept (upstream resilientParse)."""
|
|
142
|
+
if not isinstance(data, Mapping):
|
|
143
|
+
return {}
|
|
144
|
+
result: dict[str, Any] = {}
|
|
145
|
+
for key, validator in shape.items():
|
|
146
|
+
if key not in data:
|
|
147
|
+
continue
|
|
148
|
+
value = data[key]
|
|
149
|
+
if isinstance(validator, Mapping):
|
|
150
|
+
if isinstance(value, Mapping):
|
|
151
|
+
result[key] = _resilient(validator, value)
|
|
152
|
+
continue
|
|
153
|
+
try:
|
|
154
|
+
result[key] = validator(value)
|
|
155
|
+
except _Invalid:
|
|
156
|
+
pass
|
|
157
|
+
for key, value in data.items():
|
|
158
|
+
if key not in shape:
|
|
159
|
+
result[key] = value
|
|
160
|
+
return result
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# --- I/O ---------------------------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def read_global_config(config_file: str | None = None) -> Ok[GlobalConfig] | Err[Exception]:
|
|
167
|
+
"""Read the config. A missing file is ``Ok({})``; an unreadable/unparseable file is ``Err``."""
|
|
168
|
+
path = config_file or get_global_config_file()
|
|
169
|
+
if not os.path.exists(path):
|
|
170
|
+
return Ok(GlobalConfig())
|
|
171
|
+
try:
|
|
172
|
+
with open(path, encoding="utf-8") as fh:
|
|
173
|
+
parsed = json.load(fh)
|
|
174
|
+
if not isinstance(parsed, dict):
|
|
175
|
+
return Err(ValueError(f"Config at {path} is not a JSON object."))
|
|
176
|
+
return Ok(_resilient(_GLOBAL_SHAPE, parsed)) # type: ignore[arg-type]
|
|
177
|
+
except Exception as exc:
|
|
178
|
+
return Err(exc)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def read_global_config_sync(config_file: str | None = None) -> GlobalConfig:
|
|
182
|
+
"""Best-effort read; any failure yields ``{}``."""
|
|
183
|
+
try:
|
|
184
|
+
with open(config_file or get_global_config_file(), encoding="utf-8") as fh:
|
|
185
|
+
return _resilient(_GLOBAL_SHAPE, json.load(fh)) # type: ignore[return-value]
|
|
186
|
+
except Exception:
|
|
187
|
+
return GlobalConfig()
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _merge_config(target: Mapping[str, Any], source: Mapping[str, Any]) -> dict[str, Any]:
|
|
191
|
+
merged = {**target, **source}
|
|
192
|
+
if "telemetry" in source:
|
|
193
|
+
merged["telemetry"] = {**(target.get("telemetry") or {}), **(source["telemetry"] or {})}
|
|
194
|
+
return merged
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def update_global_config(
|
|
198
|
+
partial: Mapping[str, Any], config_dir: str | None = None, config_file: str | None = None
|
|
199
|
+
) -> bool:
|
|
200
|
+
"""Shallow-merge ``partial`` into the config (``telemetry`` merged one level deep)."""
|
|
201
|
+
config_dir = config_dir or get_global_config_dir()
|
|
202
|
+
config_file = config_file or get_global_config_file()
|
|
203
|
+
existing = read_global_config(config_file)
|
|
204
|
+
if not isinstance(existing, Ok):
|
|
205
|
+
return False
|
|
206
|
+
try:
|
|
207
|
+
merged = _merge_config(existing.value, partial)
|
|
208
|
+
os.makedirs(config_dir, exist_ok=True)
|
|
209
|
+
with open(config_file, "w", encoding="utf-8") as fh:
|
|
210
|
+
fh.write(to_json_string(merged, 2))
|
|
211
|
+
return True
|
|
212
|
+
except Exception:
|
|
213
|
+
return False
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@dataclass(frozen=True, slots=True)
|
|
217
|
+
class InstallationId:
|
|
218
|
+
id: str
|
|
219
|
+
created: bool
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def get_or_create_installation_id(
|
|
223
|
+
config_dir: str | None = None, config_file: str | None = None
|
|
224
|
+
) -> Ok[InstallationId] | Err[Exception]:
|
|
225
|
+
"""Return the installation id, generating and persisting one if absent or not a valid UUID.
|
|
226
|
+
|
|
227
|
+
Concurrent first runs may each generate an id; the last write wins.
|
|
228
|
+
"""
|
|
229
|
+
config_file = config_file or get_global_config_file()
|
|
230
|
+
read = read_global_config(config_file)
|
|
231
|
+
if not isinstance(read, Ok):
|
|
232
|
+
return read
|
|
233
|
+
existing = read.value.get("installationId")
|
|
234
|
+
if existing:
|
|
235
|
+
return Ok(InstallationId(id=existing, created=False))
|
|
236
|
+
new_id = str(uuid.uuid4())
|
|
237
|
+
if not update_global_config({"installationId": new_id}, config_dir, config_file):
|
|
238
|
+
return Err(RuntimeError(f"Failed to persist installation id to {config_file}"))
|
|
239
|
+
return Ok(InstallationId(id=new_id, created=True))
|