iac-code 0.4.0__tar.gz → 0.4.1__tar.gz
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.
- {iac_code-0.4.0 → iac_code-0.4.1}/PKG-INFO +1 -1
- iac_code-0.4.1/src/iac_code/__init__.py +2 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/executor.py +18 -2
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/session.py +15 -1
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/tools.py +39 -4
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/agent_loop.py +60 -8
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/agent_tool.py +79 -25
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/agent_types.py +11 -9
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/main.py +6 -0
- iac_code-0.4.1/src/iac_code/cli/update.py +38 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/auth.py +2 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/model.py +25 -10
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/status.py +8 -2
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/tasks.py +2 -1
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/config.py +3 -2
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/__init__.py +13 -1
- iac_code-0.4.1/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po +873 -676
- iac_code-0.4.1/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po +874 -676
- iac_code-0.4.1/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po +877 -676
- iac_code-0.4.1/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po +861 -676
- iac_code-0.4.1/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po +874 -676
- iac_code-0.4.1/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po +861 -676
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/memory/memory_tools.py +2 -2
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/manager.py +82 -27
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/openai_provider.py +9 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/openrouter_provider.py +12 -9
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/agent_factory.py +2 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/context_manager.py +51 -3
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/permissions/loader.py +1 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/permissions/pipeline.py +32 -2
- iac_code-0.4.1/src/iac_code/services/permissions/trusted_roots.py +21 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/providers/aliyun.py +48 -25
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/providers/aliyun_oauth.py +23 -7
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_index.py +26 -1
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/__init__.py +2 -1
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/identity.py +27 -1
- iac_code-0.4.1/src/iac_code/services/token_counter.py +149 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/update_checker.py +8 -3
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/renderer.py +119 -30
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/skill_tool.py +3 -1
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tasks/task_state.py +20 -5
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tasks/task_tools.py +2 -1
- iac_code-0.4.1/src/iac_code/tools/bash/argv_safety.py +1112 -0
- iac_code-0.4.1/src/iac_code/tools/bash/path_validation.py +249 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/permissions.py +33 -28
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/readonly_commands.py +9 -24
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/safety_checks.py +10 -53
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_stack.py +313 -139
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/base_stack.py +111 -49
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/types.py +10 -1
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/edit_file.py +24 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/glob.py +56 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/grep.py +127 -4
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/list_files.py +24 -5
- iac_code-0.4.1/src/iac_code/tools/path_safety.py +218 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/read_file.py +99 -19
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/tool_executor.py +1 -9
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/web_fetch.py +54 -6
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/write_file.py +24 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/types/permissions.py +1 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/types/stream_events.py +9 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/input_history.py +5 -5
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/prompt_input.py +10 -8
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/resume_picker.py +6 -6
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/renderer.py +246 -46
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/repl.py +91 -24
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/shell_history_provider.py +36 -2
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/file_security.py +19 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/PKG-INFO +1 -1
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/SOURCES.txt +4 -0
- iac_code-0.4.0/src/iac_code/__init__.py +0 -2
- iac_code-0.4.0/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.4.0/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.4.0/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.4.0/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.4.0/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.4.0/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.4.0/src/iac_code/services/token_counter.py +0 -81
- iac_code-0.4.0/src/iac_code/tools/bash/path_validation.py +0 -118
- {iac_code-0.4.0 → iac_code-0.4.1}/LICENSE +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/MANIFEST.in +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/README.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/pyproject.toml +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/setup.cfg +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/setup.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/agent_card.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/app.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/artifacts.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/client.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/events.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/exposure.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/metrics.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/parts.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/persistence.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/push.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/push_queue.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/push_secrets.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/push_worker.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/router.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/signing.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/task_store.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transport.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/base.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/dispatcher.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/grpc.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/grpc_jsonrpc.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/http.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/redis_streams.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/stdio.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/unix.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/websocket.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/types.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/convert.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/http_sse.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/mcp.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/metrics.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/server.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/slash_registry.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/state.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/types.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/version.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/message.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/system_prompt.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/headless.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/install_git_bash.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/output_formats.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/clear.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/compact.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/debug.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/effort.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/exit.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/help.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/memory.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/registry.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/rename.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/resume.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/skills.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/memory/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/memory/memory_manager.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/anthropic_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/azure_openai_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/base.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/dashscope_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/deepseek_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/gemini_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/kimi_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/lmstudio_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/minimax_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/modelscope_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/ollama_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/registry.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/retry.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/siliconflow_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/stream_watchdog.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/thinking.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/volcengine_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/zhipu_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/capabilities/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/capabilities/auto_detect.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/capabilities/multimodal.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/cloud_credentials.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/permissions/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/permissions/storage.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/providers/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/qwenpaw_source.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_metadata.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_resolver.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_storage.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_usage.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/attributes.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/client.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/config.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/constants.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/content_serializer.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/events.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/fallback.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/metrics.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/names.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/sanitize.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/sink.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/tracing.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/types.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/token_budget.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/auto_trigger.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/SKILL.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/auto_trigger.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/ecs.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/oss.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/rds.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/redis.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/slb.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/vpc.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/ros-template.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/template-parameter-recommendation.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/template-parameters.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/terraform-template.md +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/scripts/tf2ros.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/simplify.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/discovery.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/frontmatter.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/listing.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/loader.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/management.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/processor.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/settings.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/skill_definition.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/state/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/state/app_state.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tasks/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tasks/notification_queue.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/base.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/bash_tool.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/command_parser.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/mode_validation.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/rule_matching.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/aliyun_api.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/aliyun_doc_search.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/api_hooks.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/endpoints.yml +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/hooks/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_parameters.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_validate.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_client.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_stack_instances.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_yaml.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/user_agent.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/base_api.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/registry.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/result_storage.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/types/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/types/skill_source.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/banner.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/dialog.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/divider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/fuzzy_picker.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/progress_bar.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/search_box.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/select.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/status_icon.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/tabs.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/in_place_render.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/key_event.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/raw_input.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/raw_input_win.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/screen.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/global_search.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/history_search.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/model_picker.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/quick_open.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/skills_picker.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/keybindings/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/keybindings/manager.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/spinner.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/aggregator.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/command_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/directory_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/file_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/skill_provider.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/token_extractor.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/types.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/transcript_view.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/background_housekeeping.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/cleanup.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/console.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/__init__.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/clipboard.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/format_detect.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/pasted_content.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/processor.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/resizer.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/store.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/json_utils.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/log.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/platform.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/project_paths.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/signals.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/tool_input_parser.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/windows_paths.py +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/dependency_links.txt +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/entry_points.txt +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/requires.txt +0 -0
- {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import contextlib
|
|
4
5
|
import logging
|
|
5
6
|
import os
|
|
6
7
|
import uuid
|
|
@@ -27,7 +28,7 @@ from iac_code.a2a.types import (
|
|
|
27
28
|
)
|
|
28
29
|
from iac_code.services.agent_factory import AgentFactoryOptions, create_agent_runtime
|
|
29
30
|
from iac_code.services.session_storage import SessionStorage
|
|
30
|
-
from iac_code.services.telemetry import use_session_id
|
|
31
|
+
from iac_code.services.telemetry import use_session_id, use_user_id
|
|
31
32
|
|
|
32
33
|
logger = logging.getLogger(__name__)
|
|
33
34
|
_CONTEXT_LOCK_ACQUIRE_TIMEOUT_SECONDS = 1
|
|
@@ -87,6 +88,7 @@ class IacCodeA2AExecutor(AgentExecutor):
|
|
|
87
88
|
getattr(context, "message", None), "metadata", None
|
|
88
89
|
)
|
|
89
90
|
cwd = self._resolve_cwd(metadata)
|
|
91
|
+
user_id = self._resolve_user_id(metadata)
|
|
90
92
|
prompt = self._prompt_from_context(context, cwd=cwd)
|
|
91
93
|
except Exception as exc:
|
|
92
94
|
if _is_retryable_executor_error(exc):
|
|
@@ -222,7 +224,8 @@ class IacCodeA2AExecutor(AgentExecutor):
|
|
|
222
224
|
context_id=context_id,
|
|
223
225
|
state=TaskState.TASK_STATE_WORKING,
|
|
224
226
|
)
|
|
225
|
-
|
|
227
|
+
user_id_ctx = use_user_id(user_id) if user_id else contextlib.nullcontext()
|
|
228
|
+
with use_session_id(ctx.session_id), user_id_ctx:
|
|
226
229
|
async for event in runtime.agent_loop.run_streaming(prompt):
|
|
227
230
|
text_chunk = await publish_stream_event(
|
|
228
231
|
event_queue,
|
|
@@ -348,6 +351,19 @@ class IacCodeA2AExecutor(AgentExecutor):
|
|
|
348
351
|
resolved_cwd.mkdir(parents=True, exist_ok=True)
|
|
349
352
|
return str(resolved_cwd)
|
|
350
353
|
|
|
354
|
+
def _resolve_user_id(self, metadata: Any | None) -> str | None:
|
|
355
|
+
if metadata is not None and hasattr(metadata, "DESCRIPTOR"):
|
|
356
|
+
metadata = MessageToDict(metadata, preserving_proto_field_name=False)
|
|
357
|
+
if not isinstance(metadata, Mapping):
|
|
358
|
+
return None
|
|
359
|
+
raw_iac_meta = metadata.get("iac_code")
|
|
360
|
+
if not isinstance(raw_iac_meta, Mapping):
|
|
361
|
+
return None
|
|
362
|
+
raw_user_id = raw_iac_meta.get("user_id")
|
|
363
|
+
if isinstance(raw_user_id, str) and raw_user_id.strip():
|
|
364
|
+
return raw_user_id.strip()
|
|
365
|
+
return None
|
|
366
|
+
|
|
351
367
|
def _prompt_from_context(self, context: RequestContext, *, cwd: str) -> str:
|
|
352
368
|
message = getattr(context, "message", None)
|
|
353
369
|
if not isinstance(message, Message):
|
|
@@ -157,6 +157,20 @@ _PREFIX_ALLOW_RULE = "allow_rule:"
|
|
|
157
157
|
_PREFIX_DENY_RULE = "deny_rule:"
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
def _tool_supports_blanket_allow(agent_loop, tool_name: str) -> bool:
|
|
161
|
+
"""Return False only when the registered tool explicitly disables blanket allow."""
|
|
162
|
+
registry = getattr(agent_loop, "tool_registry", None)
|
|
163
|
+
get_tool = getattr(registry, "get", None)
|
|
164
|
+
if get_tool is None:
|
|
165
|
+
return True
|
|
166
|
+
|
|
167
|
+
tool = get_tool(tool_name)
|
|
168
|
+
if tool is None:
|
|
169
|
+
return True
|
|
170
|
+
|
|
171
|
+
return bool(getattr(tool, "supports_blanket_allow", True))
|
|
172
|
+
|
|
173
|
+
|
|
160
174
|
class ACPSession:
|
|
161
175
|
def __init__(
|
|
162
176
|
self,
|
|
@@ -457,7 +471,7 @@ class ACPSession:
|
|
|
457
471
|
kind="allow_always",
|
|
458
472
|
)
|
|
459
473
|
)
|
|
460
|
-
|
|
474
|
+
elif _tool_supports_blanket_allow(self.agent_loop, tool_name):
|
|
461
475
|
options.append(
|
|
462
476
|
acp.schema.PermissionOption(
|
|
463
477
|
option_id=_OPTION_ALLOW_ALWAYS,
|
|
@@ -5,6 +5,7 @@ from contextlib import suppress
|
|
|
5
5
|
|
|
6
6
|
import acp
|
|
7
7
|
|
|
8
|
+
from iac_code.i18n import _
|
|
8
9
|
from iac_code.tools.base import Tool, ToolContext, ToolResult
|
|
9
10
|
|
|
10
11
|
TERMINAL_TIMEOUT = 300 # 5 minutes default timeout
|
|
@@ -32,9 +33,37 @@ class ACPTerminalBashTool(Tool):
|
|
|
32
33
|
def timeout(self) -> float | None:
|
|
33
34
|
return self._original.timeout
|
|
34
35
|
|
|
36
|
+
@property
|
|
37
|
+
def supports_blanket_allow(self) -> bool:
|
|
38
|
+
return self._original.supports_blanket_allow
|
|
39
|
+
|
|
40
|
+
def user_facing_name(self, input: dict | None = None) -> str:
|
|
41
|
+
return self._original.user_facing_name(input)
|
|
42
|
+
|
|
43
|
+
def get_activity_description(self, input: dict | None = None) -> str | None:
|
|
44
|
+
return self._original.get_activity_description(input)
|
|
45
|
+
|
|
46
|
+
def get_tool_use_summary(self, input: dict | None = None) -> str | None:
|
|
47
|
+
return self._original.get_tool_use_summary(input)
|
|
48
|
+
|
|
49
|
+
def render_tool_use_message(self, input: dict, *, verbose: bool = False) -> str | None:
|
|
50
|
+
return self._original.render_tool_use_message(input, verbose=verbose)
|
|
51
|
+
|
|
52
|
+
def render_tool_result_message(self, output: str, *, is_error: bool = False, verbose: bool = False) -> str | None:
|
|
53
|
+
return self._original.render_tool_result_message(output, is_error=is_error, verbose=verbose)
|
|
54
|
+
|
|
55
|
+
def render_tool_use_error_message(self, error: str) -> str | None:
|
|
56
|
+
return self._original.render_tool_use_error_message(error)
|
|
57
|
+
|
|
58
|
+
def streaming_preview_fields(self) -> list[str]:
|
|
59
|
+
return self._original.streaming_preview_fields()
|
|
60
|
+
|
|
35
61
|
def is_read_only(self, input: dict | None = None) -> bool:
|
|
36
62
|
return self._original.is_read_only(input)
|
|
37
63
|
|
|
64
|
+
def is_concurrency_safe(self, tool_input: dict) -> bool:
|
|
65
|
+
return self._original.is_concurrency_safe(tool_input)
|
|
66
|
+
|
|
38
67
|
def is_destructive(self, input: dict | None = None) -> bool:
|
|
39
68
|
return self._original.is_destructive(input)
|
|
40
69
|
|
|
@@ -44,7 +73,7 @@ class ACPTerminalBashTool(Tool):
|
|
|
44
73
|
async def execute(self, *, tool_input: dict, context: ToolContext) -> ToolResult:
|
|
45
74
|
command = tool_input.get("command")
|
|
46
75
|
if not command:
|
|
47
|
-
return ToolResult.error("Bash command is required.")
|
|
76
|
+
return ToolResult.error(_("Bash command is required."))
|
|
48
77
|
|
|
49
78
|
timeout = tool_input.get("timeout", TERMINAL_TIMEOUT)
|
|
50
79
|
terminal_id: str | None = None
|
|
@@ -74,7 +103,7 @@ class ACPTerminalBashTool(Tool):
|
|
|
74
103
|
except asyncio.TimeoutError:
|
|
75
104
|
with suppress(Exception):
|
|
76
105
|
await self._conn.kill_terminal(session_id=self._session_id, terminal_id=terminal_id)
|
|
77
|
-
return ToolResult.error(
|
|
106
|
+
return ToolResult.error(_("Command timed out after {timeout} seconds").format(timeout=timeout))
|
|
78
107
|
|
|
79
108
|
if output.exit_status:
|
|
80
109
|
exit_status = output.exit_status
|
|
@@ -84,9 +113,15 @@ class ACPTerminalBashTool(Tool):
|
|
|
84
113
|
# at the session layer in a future phase.
|
|
85
114
|
|
|
86
115
|
if exit_status.signal:
|
|
87
|
-
return ToolResult.error(
|
|
116
|
+
return ToolResult.error(
|
|
117
|
+
_("Command terminated by signal: {signal}").format(signal=exit_status.signal) + "\n" + output.output
|
|
118
|
+
)
|
|
88
119
|
if exit_status.exit_code not in (None, 0):
|
|
89
|
-
return ToolResult.error(
|
|
120
|
+
return ToolResult.error(
|
|
121
|
+
_("Command failed with exit code {exit_code}").format(exit_code=exit_status.exit_code)
|
|
122
|
+
+ "\n"
|
|
123
|
+
+ output.output
|
|
124
|
+
)
|
|
90
125
|
return ToolResult.success(output.output)
|
|
91
126
|
except asyncio.CancelledError:
|
|
92
127
|
if terminal_id is not None:
|
|
@@ -6,7 +6,7 @@ import asyncio
|
|
|
6
6
|
import os
|
|
7
7
|
import time
|
|
8
8
|
import uuid
|
|
9
|
-
from collections.abc import AsyncGenerator
|
|
9
|
+
from collections.abc import AsyncGenerator, Callable
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from typing import Any, Literal
|
|
12
12
|
|
|
@@ -23,6 +23,7 @@ from iac_code.types.stream_events import (
|
|
|
23
23
|
CompactionEvent,
|
|
24
24
|
MessageEndEvent,
|
|
25
25
|
PermissionRequestEvent,
|
|
26
|
+
QueuedInputSubmittedEvent,
|
|
26
27
|
StackInstancesProgressEvent,
|
|
27
28
|
StackProgressEvent,
|
|
28
29
|
StreamEvent,
|
|
@@ -94,6 +95,7 @@ class AgentLoop:
|
|
|
94
95
|
model_name = provider_manager.get_model_name()
|
|
95
96
|
|
|
96
97
|
self.context_manager = ContextManager(system_prompt=system_prompt, model=model_name)
|
|
98
|
+
self._sync_tool_definitions()
|
|
97
99
|
if resume_messages:
|
|
98
100
|
self.context_manager.load_messages(resume_messages)
|
|
99
101
|
self._tool_executor = ToolExecutor(registry=tool_registry)
|
|
@@ -116,6 +118,7 @@ class AgentLoop:
|
|
|
116
118
|
if system_prompt is not None:
|
|
117
119
|
self.system_prompt = system_prompt
|
|
118
120
|
self.context_manager.set_system_prompt(system_prompt)
|
|
121
|
+
self._sync_tool_definitions()
|
|
119
122
|
|
|
120
123
|
def set_auto_trigger_skills(self, skill_commands: list[Any] | None) -> None:
|
|
121
124
|
"""Refresh skills considered for automatic trigger injection."""
|
|
@@ -136,6 +139,12 @@ class AgentLoop:
|
|
|
136
139
|
)
|
|
137
140
|
return tools
|
|
138
141
|
|
|
142
|
+
def _sync_tool_definitions(self):
|
|
143
|
+
"""Refresh context token accounting from the current tool registry."""
|
|
144
|
+
tool_definitions = self._get_tool_definitions()
|
|
145
|
+
self.context_manager.set_tool_definitions(tool_definitions)
|
|
146
|
+
return tool_definitions
|
|
147
|
+
|
|
139
148
|
def _get_provider_messages(self):
|
|
140
149
|
"""Convert context manager messages to provider Message format."""
|
|
141
150
|
from iac_code.providers.base import ContentBlock
|
|
@@ -178,7 +187,11 @@ class AgentLoop:
|
|
|
178
187
|
final_text += event.text
|
|
179
188
|
return final_text
|
|
180
189
|
|
|
181
|
-
async def run_streaming(
|
|
190
|
+
async def run_streaming(
|
|
191
|
+
self,
|
|
192
|
+
user_input: str | list[ContentBlock],
|
|
193
|
+
queued_input_provider: Callable[[], list[str]] | None = None,
|
|
194
|
+
) -> AsyncGenerator[StreamEvent, None]:
|
|
182
195
|
"""Streaming execution yielding fine-grained StreamEvents.
|
|
183
196
|
|
|
184
197
|
Flow:
|
|
@@ -248,7 +261,10 @@ class AgentLoop:
|
|
|
248
261
|
git_branch=self._current_git_branch,
|
|
249
262
|
)
|
|
250
263
|
try:
|
|
251
|
-
async for event in self._run_streaming_inner(
|
|
264
|
+
async for event in self._run_streaming_inner(
|
|
265
|
+
user_input,
|
|
266
|
+
queued_input_provider=queued_input_provider,
|
|
267
|
+
):
|
|
252
268
|
if isinstance(event, TextDeltaEvent) and not first_token_received:
|
|
253
269
|
first_token_received = True
|
|
254
270
|
ttft_ns = int((time.monotonic() - interaction_started) * 1_000_000_000)
|
|
@@ -272,14 +288,18 @@ class AgentLoop:
|
|
|
272
288
|
serialize_output_messages("".join(final_text_chunks), final_stop_reason),
|
|
273
289
|
)
|
|
274
290
|
|
|
275
|
-
async def _run_streaming_inner(
|
|
291
|
+
async def _run_streaming_inner(
|
|
292
|
+
self,
|
|
293
|
+
user_input: str | list[ContentBlock],
|
|
294
|
+
queued_input_provider: Callable[[], list[str]] | None = None,
|
|
295
|
+
) -> AsyncGenerator[StreamEvent, None]:
|
|
276
296
|
"""Inner streaming loop (called from run_streaming inside the ENTRY span)."""
|
|
277
297
|
from iac_code.services.telemetry import start_span
|
|
278
298
|
from iac_code.services.telemetry.names import GenAiAttr, GenAiOperationName, GenAiSpanKind, Spans
|
|
279
299
|
|
|
280
|
-
tool_definitions = self._get_tool_definitions()
|
|
281
|
-
|
|
282
300
|
for _turn in range(self._max_turns):
|
|
301
|
+
tool_definitions = self._sync_tool_definitions()
|
|
302
|
+
|
|
283
303
|
# Auto-compact if needed
|
|
284
304
|
if self.context_manager.needs_compaction():
|
|
285
305
|
compact_event = await self._auto_compact()
|
|
@@ -303,7 +323,7 @@ class AgentLoop:
|
|
|
303
323
|
async for event in self._provider_manager.stream(
|
|
304
324
|
messages=self._get_provider_messages(),
|
|
305
325
|
system=self.system_prompt,
|
|
306
|
-
tools=tool_definitions if
|
|
326
|
+
tools=tool_definitions if tool_definitions else None,
|
|
307
327
|
):
|
|
308
328
|
yield event # Forward all provider events to UI
|
|
309
329
|
|
|
@@ -329,6 +349,7 @@ class AgentLoop:
|
|
|
329
349
|
|
|
330
350
|
if not message_ended:
|
|
331
351
|
step_span.set_attribute(GenAiAttr.REACT_FINISH_REASON, "error")
|
|
352
|
+
yield MessageEndEvent(stop_reason="stream_error", usage=Usage())
|
|
332
353
|
break
|
|
333
354
|
|
|
334
355
|
# Build assistant message for context
|
|
@@ -451,8 +472,13 @@ class AgentLoop:
|
|
|
451
472
|
|
|
452
473
|
denied_content: list[ContentBlock] = list(denied_blocks)
|
|
453
474
|
self._session_storage.append(
|
|
454
|
-
self._cwd,
|
|
475
|
+
self._cwd,
|
|
476
|
+
self._session_id,
|
|
477
|
+
Message(role="user", content=denied_content),
|
|
478
|
+
git_branch=self._current_git_branch,
|
|
455
479
|
)
|
|
480
|
+
async for event in self._submit_queued_inputs_after_tool_call(queued_input_provider):
|
|
481
|
+
yield event
|
|
456
482
|
continue
|
|
457
483
|
|
|
458
484
|
requests = allowed_requests
|
|
@@ -549,9 +575,35 @@ class AgentLoop:
|
|
|
549
575
|
self.context_manager.add_raw_message(msg)
|
|
550
576
|
if result.context_modifier is not None:
|
|
551
577
|
self._apply_context_modifier(result.context_modifier)
|
|
578
|
+
|
|
579
|
+
async for event in self._submit_queued_inputs_after_tool_call(queued_input_provider):
|
|
580
|
+
yield event
|
|
552
581
|
else:
|
|
553
582
|
yield MessageEndEvent(stop_reason="max_turns", usage=Usage())
|
|
554
583
|
|
|
584
|
+
async def _submit_queued_inputs_after_tool_call(
|
|
585
|
+
self,
|
|
586
|
+
queued_input_provider: Callable[[], list[str]] | None,
|
|
587
|
+
) -> AsyncGenerator[QueuedInputSubmittedEvent, None]:
|
|
588
|
+
if queued_input_provider is None:
|
|
589
|
+
return
|
|
590
|
+
|
|
591
|
+
queued_inputs = queued_input_provider()
|
|
592
|
+
for raw_input in queued_inputs:
|
|
593
|
+
text = raw_input.strip()
|
|
594
|
+
if not text:
|
|
595
|
+
continue
|
|
596
|
+
await self._apply_auto_triggers(text)
|
|
597
|
+
message = self.context_manager.add_user_message(text)
|
|
598
|
+
if self._session_storage:
|
|
599
|
+
self._session_storage.append(
|
|
600
|
+
self._cwd,
|
|
601
|
+
self._session_id,
|
|
602
|
+
message,
|
|
603
|
+
git_branch=self._current_git_branch,
|
|
604
|
+
)
|
|
605
|
+
yield QueuedInputSubmittedEvent(text=text)
|
|
606
|
+
|
|
555
607
|
async def _apply_auto_triggers(self, user_input: str | list[ContentBlock]) -> None:
|
|
556
608
|
if not self._auto_trigger_skills:
|
|
557
609
|
return
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
from contextlib import suppress
|
|
6
7
|
from dataclasses import dataclass
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
9
10
|
from iac_code.agent.agent_types import filter_tools, get_agent_definition, get_builtin_agents
|
|
10
|
-
from iac_code.i18n import _
|
|
11
|
+
from iac_code.i18n import _, ngettext
|
|
11
12
|
from iac_code.tools.base import Tool, ToolContext, ToolResult
|
|
12
13
|
|
|
13
14
|
|
|
@@ -21,6 +22,14 @@ class AgentProgress:
|
|
|
21
22
|
summary: str = ""
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
def _format_base_exception(error: BaseException) -> str:
|
|
26
|
+
detail = str(error)
|
|
27
|
+
error_type = type(error).__name__
|
|
28
|
+
if not detail:
|
|
29
|
+
return error_type
|
|
30
|
+
return "{error_type}: {detail}".format(error_type=error_type, detail=detail)
|
|
31
|
+
|
|
32
|
+
|
|
24
33
|
async def run_sub_agent(
|
|
25
34
|
*,
|
|
26
35
|
prompt: str,
|
|
@@ -45,7 +54,7 @@ async def run_sub_agent(
|
|
|
45
54
|
|
|
46
55
|
defn = get_agent_definition(agent_type)
|
|
47
56
|
if defn is None:
|
|
48
|
-
raise ValueError(
|
|
57
|
+
raise ValueError(_("Unknown agent type: {agent_type}").format(agent_type=agent_type))
|
|
49
58
|
|
|
50
59
|
sub_registry = filter_tools(parent_tool_registry, defn) if parent_tool_registry else parent_tool_registry
|
|
51
60
|
system_prompt = parent_system_prompt or build_system_prompt(cwd=cwd)
|
|
@@ -58,7 +67,7 @@ async def run_sub_agent(
|
|
|
58
67
|
permission_context=permission_context,
|
|
59
68
|
)
|
|
60
69
|
|
|
61
|
-
progress = AgentProgress(summary=
|
|
70
|
+
progress = AgentProgress(summary=_("Running {agent_type} agent").format(agent_type=agent_type))
|
|
62
71
|
text_chunks: list[str] = []
|
|
63
72
|
# Track tool inputs: tool_use_id -> (name, input)
|
|
64
73
|
pending_tool_inputs: dict[str, tuple[str, dict]] = {}
|
|
@@ -128,7 +137,6 @@ class AgentTool(Tool):
|
|
|
128
137
|
self._tool_registry = tool_registry
|
|
129
138
|
self._system_prompt = system_prompt
|
|
130
139
|
self._permission_context = permission_context
|
|
131
|
-
self._event_queue: asyncio.Queue | None = None # Set by ToolExecutor via ToolCallRequest
|
|
132
140
|
|
|
133
141
|
@property
|
|
134
142
|
def name(self) -> str:
|
|
@@ -137,8 +145,13 @@ class AgentTool(Tool):
|
|
|
137
145
|
@property
|
|
138
146
|
def description(self) -> str:
|
|
139
147
|
agents = get_builtin_agents()
|
|
140
|
-
agent_list = "\n".join(
|
|
141
|
-
|
|
148
|
+
agent_list = "\n".join(
|
|
149
|
+
" - {agent_type}: {when_to_use}".format(agent_type=agent.agent_type, when_to_use=agent.when_to_use)
|
|
150
|
+
for agent in agents
|
|
151
|
+
)
|
|
152
|
+
return _("Launch a sub-agent to handle complex tasks.\n\nAvailable agent types:\n{agent_list}").format(
|
|
153
|
+
agent_list=agent_list
|
|
154
|
+
)
|
|
142
155
|
|
|
143
156
|
@property
|
|
144
157
|
def input_schema(self) -> dict[str, Any]:
|
|
@@ -148,20 +161,20 @@ class AgentTool(Tool):
|
|
|
148
161
|
"properties": {
|
|
149
162
|
"prompt": {
|
|
150
163
|
"type": "string",
|
|
151
|
-
"description": "The task for the sub-agent to perform.",
|
|
164
|
+
"description": _("The task for the sub-agent to perform."),
|
|
152
165
|
},
|
|
153
166
|
"description": {
|
|
154
167
|
"type": "string",
|
|
155
|
-
"description": "Short (3-5 word) description of the task.",
|
|
168
|
+
"description": _("Short (3-5 word) description of the task."),
|
|
156
169
|
},
|
|
157
170
|
"subagent_type": {
|
|
158
171
|
"type": "string",
|
|
159
172
|
"enum": agent_types,
|
|
160
|
-
"description": "The type of specialized agent to use.",
|
|
173
|
+
"description": _("The type of specialized agent to use."),
|
|
161
174
|
},
|
|
162
175
|
"run_in_background": {
|
|
163
176
|
"type": "boolean",
|
|
164
|
-
"description": "Run agent in background, parent continues.",
|
|
177
|
+
"description": _("Run agent in background, parent continues."),
|
|
165
178
|
},
|
|
166
179
|
},
|
|
167
180
|
"required": ["prompt", "description"],
|
|
@@ -171,18 +184,30 @@ class AgentTool(Tool):
|
|
|
171
184
|
prompt = tool_input["prompt"]
|
|
172
185
|
agent_type = tool_input.get("subagent_type", tool_input.get("agent_type", "general-purpose"))
|
|
173
186
|
run_in_background = tool_input.get("run_in_background", False)
|
|
187
|
+
event_queue = context.event_queue
|
|
174
188
|
|
|
175
189
|
defn = get_agent_definition(agent_type)
|
|
176
190
|
if defn is None:
|
|
177
|
-
return ToolResult.error(
|
|
191
|
+
return ToolResult.error(_("Unknown agent type: '{agent_type}'").format(agent_type=agent_type))
|
|
178
192
|
|
|
179
193
|
if run_in_background and self._task_manager:
|
|
180
194
|
task_id = self._task_manager.register(
|
|
181
|
-
description=tool_input.get("description", "Sub-agent task"),
|
|
195
|
+
description=tool_input.get("description", _("Sub-agent task")),
|
|
182
196
|
agent_type=agent_type,
|
|
183
197
|
)
|
|
184
|
-
asyncio.create_task(self._run_background(task_id, prompt, agent_type, context))
|
|
185
|
-
|
|
198
|
+
background_task = asyncio.create_task(self._run_background(task_id, prompt, agent_type, context))
|
|
199
|
+
attach_task = getattr(self._task_manager, "attach_task", None)
|
|
200
|
+
if callable(attach_task):
|
|
201
|
+
attach_task(task_id, background_task)
|
|
202
|
+
background_task.add_done_callback(self._consume_background_task_exception)
|
|
203
|
+
if event_queue is not None:
|
|
204
|
+
await event_queue.put(None)
|
|
205
|
+
return ToolResult.success(
|
|
206
|
+
_("Background agent launched (task_id: {task_id}, type: {agent_type})").format(
|
|
207
|
+
task_id=task_id,
|
|
208
|
+
agent_type=agent_type,
|
|
209
|
+
)
|
|
210
|
+
)
|
|
186
211
|
|
|
187
212
|
try:
|
|
188
213
|
result_text, progress = await run_sub_agent(
|
|
@@ -192,18 +217,25 @@ class AgentTool(Tool):
|
|
|
192
217
|
parent_provider_manager=self._provider_manager,
|
|
193
218
|
parent_tool_registry=self._tool_registry,
|
|
194
219
|
parent_system_prompt=self._system_prompt,
|
|
195
|
-
event_queue=
|
|
220
|
+
event_queue=event_queue,
|
|
196
221
|
permission_context=self._permission_context,
|
|
197
222
|
)
|
|
198
|
-
if
|
|
199
|
-
await
|
|
223
|
+
if event_queue is not None:
|
|
224
|
+
await event_queue.put(None)
|
|
200
225
|
return ToolResult.success(
|
|
201
226
|
f"{result_text}\n\n[Agent stats: {progress.tool_use_count} tool calls, {progress.token_count} tokens]"
|
|
202
227
|
)
|
|
203
228
|
except Exception as e:
|
|
204
|
-
if
|
|
205
|
-
await
|
|
206
|
-
return ToolResult.error(
|
|
229
|
+
if event_queue is not None:
|
|
230
|
+
await event_queue.put(None)
|
|
231
|
+
return ToolResult.error(_("Sub-agent failed: {error}").format(error=e))
|
|
232
|
+
|
|
233
|
+
@staticmethod
|
|
234
|
+
def _consume_background_task_exception(task: asyncio.Task) -> None:
|
|
235
|
+
if task.cancelled():
|
|
236
|
+
return
|
|
237
|
+
with suppress(asyncio.CancelledError):
|
|
238
|
+
task.exception()
|
|
207
239
|
|
|
208
240
|
async def _run_background(
|
|
209
241
|
self,
|
|
@@ -231,15 +263,37 @@ class AgentTool(Tool):
|
|
|
231
263
|
if self._notification_queue:
|
|
232
264
|
self._notification_queue.enqueue(
|
|
233
265
|
task_id=task_id,
|
|
234
|
-
message=
|
|
266
|
+
message=ngettext(
|
|
267
|
+
"Agent completed: {tool_count} tool call",
|
|
268
|
+
"Agent completed: {tool_count} tool calls",
|
|
269
|
+
progress.tool_use_count,
|
|
270
|
+
).format(tool_count=progress.tool_use_count),
|
|
235
271
|
)
|
|
272
|
+
except asyncio.CancelledError:
|
|
273
|
+
self._task_manager.stop(task_id)
|
|
274
|
+
if self._notification_queue:
|
|
275
|
+
self._notification_queue.enqueue(
|
|
276
|
+
task_id=task_id,
|
|
277
|
+
message=_("Agent stopped"),
|
|
278
|
+
)
|
|
279
|
+
raise
|
|
236
280
|
except Exception as e:
|
|
237
|
-
|
|
281
|
+
error = str(e) or type(e).__name__
|
|
282
|
+
self._task_manager.fail(task_id, error=error)
|
|
283
|
+
if self._notification_queue:
|
|
284
|
+
self._notification_queue.enqueue(
|
|
285
|
+
task_id=task_id,
|
|
286
|
+
message=_("Agent failed: {error}").format(error=error),
|
|
287
|
+
)
|
|
288
|
+
except BaseException as e:
|
|
289
|
+
error = _format_base_exception(e)
|
|
290
|
+
self._task_manager.fail(task_id, error=error)
|
|
238
291
|
if self._notification_queue:
|
|
239
292
|
self._notification_queue.enqueue(
|
|
240
293
|
task_id=task_id,
|
|
241
|
-
message=
|
|
294
|
+
message=_("Agent failed: {error}").format(error=error),
|
|
242
295
|
)
|
|
296
|
+
raise
|
|
243
297
|
|
|
244
298
|
def is_read_only(self, input: dict | None = None) -> bool:
|
|
245
299
|
return False
|
|
@@ -252,7 +306,7 @@ class AgentTool(Tool):
|
|
|
252
306
|
|
|
253
307
|
def render_tool_result_message(self, output: str, *, is_error: bool = False, verbose: bool = False) -> str | None:
|
|
254
308
|
if is_error:
|
|
255
|
-
return
|
|
309
|
+
return _("Agent error: {error}").format(error=output[:200])
|
|
256
310
|
if verbose:
|
|
257
311
|
return output
|
|
258
312
|
# Extract stats from the end of the output
|
|
@@ -282,4 +336,4 @@ class AgentTool(Tool):
|
|
|
282
336
|
def get_activity_description(self, input: dict | None = None) -> str | None:
|
|
283
337
|
if input is None:
|
|
284
338
|
return None
|
|
285
|
-
return
|
|
339
|
+
return _("Running agent: {description}").format(description=input.get("description", _("sub-agent")))
|
|
@@ -5,6 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
|
+
from iac_code.i18n import _
|
|
9
|
+
|
|
8
10
|
if TYPE_CHECKING:
|
|
9
11
|
from iac_code.tools.base import ToolRegistry
|
|
10
12
|
|
|
@@ -49,9 +51,9 @@ def get_builtin_agents() -> list[AgentDefinition]:
|
|
|
49
51
|
return [
|
|
50
52
|
AgentDefinition(
|
|
51
53
|
agent_type="general-purpose",
|
|
52
|
-
when_to_use=(
|
|
53
|
-
"Use for complex, multi-step tasks that require research, "
|
|
54
|
-
"
|
|
54
|
+
when_to_use=_(
|
|
55
|
+
"Use for complex, multi-step tasks that require research, code changes, "
|
|
56
|
+
"or coordinating multiple operations."
|
|
55
57
|
),
|
|
56
58
|
tools=["*"],
|
|
57
59
|
disallowed_tools=["agent"],
|
|
@@ -59,9 +61,9 @@ def get_builtin_agents() -> list[AgentDefinition]:
|
|
|
59
61
|
),
|
|
60
62
|
AgentDefinition(
|
|
61
63
|
agent_type="explore",
|
|
62
|
-
when_to_use=(
|
|
63
|
-
"Use to quickly find files, search code, or answer questions "
|
|
64
|
-
"
|
|
64
|
+
when_to_use=_(
|
|
65
|
+
"Use to quickly find files, search code, or answer questions about the codebase. "
|
|
66
|
+
"Read-only; cannot modify files."
|
|
65
67
|
),
|
|
66
68
|
tools=["read_file", "glob", "grep", "list_files", "bash"],
|
|
67
69
|
disallowed_tools=["write_file", "edit_file", "agent"],
|
|
@@ -69,9 +71,9 @@ def get_builtin_agents() -> list[AgentDefinition]:
|
|
|
69
71
|
),
|
|
70
72
|
AgentDefinition(
|
|
71
73
|
agent_type="plan",
|
|
72
|
-
when_to_use=(
|
|
73
|
-
"Use to plan implementation strategy, review architecture, "
|
|
74
|
-
"
|
|
74
|
+
when_to_use=_(
|
|
75
|
+
"Use to plan implementation strategy, review architecture, or design solutions. "
|
|
76
|
+
"Read-only, no execution."
|
|
75
77
|
),
|
|
76
78
|
tools=["read_file", "glob", "grep", "list_files"],
|
|
77
79
|
disallowed_tools=["bash", "write_file", "edit_file", "agent"],
|
|
@@ -28,6 +28,10 @@ completion_init()
|
|
|
28
28
|
# this works regardless of where it's called relative to `import typer` / click.
|
|
29
29
|
setup_i18n()
|
|
30
30
|
|
|
31
|
+
# Import subcommands with Typer parameter help after i18n setup, because Typer
|
|
32
|
+
# captures custom option help text when the function signature is defined.
|
|
33
|
+
from iac_code.cli.update import update as _update_command # noqa: E402
|
|
34
|
+
|
|
31
35
|
app = typer.Typer(
|
|
32
36
|
name="iac-code",
|
|
33
37
|
help=_("AI-powered infrastructure orchestration tool"),
|
|
@@ -54,6 +58,8 @@ if sys.platform == "win32":
|
|
|
54
58
|
help=_("Install Git for Windows via the npmmirror mirror (Windows only)."),
|
|
55
59
|
)(_install_git_bash)
|
|
56
60
|
|
|
61
|
+
app.command(name="update", help=_("Update iac-code to the latest version."))(_update_command)
|
|
62
|
+
|
|
57
63
|
|
|
58
64
|
@a2a_client_app.callback()
|
|
59
65
|
def a2a_client(
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from iac_code import __version__
|
|
8
|
+
from iac_code.i18n import _
|
|
9
|
+
from iac_code.services.update_checker import check_for_updates_once, run_update_command
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def update(
|
|
13
|
+
check: bool = typer.Option(False, "--check", help=_("Check for updates without installing.")),
|
|
14
|
+
) -> None:
|
|
15
|
+
"""Update iac-code to the latest available version."""
|
|
16
|
+
state = check_for_updates_once(current_version=__version__, force=True)
|
|
17
|
+
pending = state.pending
|
|
18
|
+
if pending is None:
|
|
19
|
+
typer.echo(_("iac-code is already up to date (v{}).").format(__version__))
|
|
20
|
+
raise typer.Exit()
|
|
21
|
+
|
|
22
|
+
if check:
|
|
23
|
+
typer.echo(_("Update available: v{} -> v{}").format(pending.current_version, pending.version))
|
|
24
|
+
typer.echo(_("Run {} to update.").format(shlex.join(pending.update_command)))
|
|
25
|
+
raise typer.Exit()
|
|
26
|
+
|
|
27
|
+
typer.echo(_("Updating iac-code from v{} to v{}...").format(pending.current_version, pending.version))
|
|
28
|
+
try:
|
|
29
|
+
result = run_update_command(pending)
|
|
30
|
+
except OSError as exc:
|
|
31
|
+
typer.echo(_("Update command failed: {}").format(exc), err=True)
|
|
32
|
+
raise typer.Exit(1) from exc
|
|
33
|
+
|
|
34
|
+
if result.returncode != 0:
|
|
35
|
+
typer.echo(_("Update command failed with exit code {}.").format(result.returncode), err=True)
|
|
36
|
+
raise typer.Exit(result.returncode)
|
|
37
|
+
|
|
38
|
+
typer.echo(_("Successfully updated to v{}!").format(pending.version))
|
|
@@ -1400,6 +1400,8 @@ def _aliyun_oauth_login_flow(existing_cred: "AliyunCredential | None") -> str |
|
|
|
1400
1400
|
return _BACK
|
|
1401
1401
|
except AliyunOAuthError as exc:
|
|
1402
1402
|
return _("Alibaba Cloud OAuth login failed: {error}").format(error=str(exc))
|
|
1403
|
+
finally:
|
|
1404
|
+
client.close()
|
|
1403
1405
|
|
|
1404
1406
|
credential = AliyunCredential(
|
|
1405
1407
|
mode="OAuth",
|