iac-code 0.2.3__tar.gz → 0.3.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.2.3 → iac_code-0.3.1}/PKG-INFO +23 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/README.md +22 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/pyproject.toml +7 -2
- {iac_code-0.2.3 → iac_code-0.3.1}/setup.py +11 -10
- iac_code-0.3.1/src/iac_code/__init__.py +2 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/agent_card.py +16 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/app.py +8 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/client.py +46 -12
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/events.py +27 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/executor.py +18 -12
- iac_code-0.3.1/src/iac_code/a2a/exposure.py +66 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/parts.py +3 -2
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/push.py +6 -13
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/push_queue.py +3 -8
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/push_secrets.py +4 -9
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/base.py +22 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/dispatcher.py +5 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/http.py +1 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/stdio.py +62 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/__init__.py +3 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/session.py +11 -9
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/slash_registry.py +1 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/agent/agent_loop.py +47 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/agent/system_prompt.py +12 -23
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/cli/headless.py +54 -0
- iac_code-0.3.1/src/iac_code/cli/install_git_bash.py +85 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/cli/main.py +81 -11
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/cli/output_formats.py +6 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/auth.py +457 -177
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/clear.py +1 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/registry.py +8 -4
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/config.py +30 -11
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/i18n/__init__.py +33 -8
- iac_code-0.3.1/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po +510 -283
- iac_code-0.3.1/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po +510 -283
- iac_code-0.3.1/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po +510 -283
- iac_code-0.3.1/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po +495 -283
- iac_code-0.3.1/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po +509 -283
- iac_code-0.3.1/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po +491 -283
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/memory/memory_manager.py +51 -16
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/registry.py +1 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/agent_factory.py +1 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/capabilities/auto_detect.py +3 -2
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/capabilities/multimodal.py +1 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/permissions/loader.py +12 -6
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/providers/aliyun.py +3 -3
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/session_storage.py +8 -4
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/__init__.py +2 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/client.py +1 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/config.py +9 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/fallback.py +4 -2
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/identity.py +32 -1
- iac_code-0.3.1/src/iac_code/services/update_checker.py +567 -0
- iac_code-0.3.1/src/iac_code/skills/auto_trigger.py +115 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/__init__.py +2 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/SKILL.md +4 -2
- iac_code-0.3.1/src/iac_code/skills/bundled/iac_aliyun/__init__.py +20 -0
- iac_code-0.3.1/src/iac_code/skills/bundled/iac_aliyun/auto_trigger.py +88 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/scripts/tf2ros.py +5 -3
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/discovery.py +44 -27
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/frontmatter.py +4 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/renderer.py +33 -7
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/skill_definition.py +4 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/base.py +9 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/bash/bash_tool.py +25 -8
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/bash/path_validation.py +21 -2
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/bash/safety_checks.py +31 -8
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/aliyun_api.py +7 -2
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/ros_client.py +5 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/ros_stack.py +1 -1
- iac_code-0.3.1/src/iac_code/tools/cloud/aliyun/user_agent.py +23 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/edit_file.py +12 -3
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/glob.py +2 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/grep.py +2 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/list_files.py +2 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/read_file.py +5 -1
- iac_code-0.3.1/src/iac_code/tools/result_storage.py +64 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/write_file.py +9 -2
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/banner.py +38 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/core/input_history.py +36 -6
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/core/prompt_input.py +33 -8
- iac_code-0.3.1/src/iac_code/ui/core/raw_input.py +383 -0
- iac_code-0.3.1/src/iac_code/ui/core/raw_input_win.py +216 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/dialogs/global_search.py +6 -3
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/renderer.py +96 -35
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/repl.py +208 -9
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/spinner.py +0 -2
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/suggestions/shell_history_provider.py +16 -1
- iac_code-0.3.1/src/iac_code/ui/suggestions/skill_provider.py +47 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/suggestions/token_extractor.py +12 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/suggestions/types.py +3 -3
- iac_code-0.3.1/src/iac_code/utils/console.py +37 -0
- iac_code-0.3.1/src/iac_code/utils/file_security.py +64 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/image/store.py +4 -2
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/json_utils.py +38 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/log.py +26 -10
- iac_code-0.3.1/src/iac_code/utils/platform.py +183 -0
- iac_code-0.3.1/src/iac_code/utils/project_paths.py +132 -0
- iac_code-0.3.1/src/iac_code/utils/signals.py +63 -0
- iac_code-0.3.1/src/iac_code/utils/windows_paths.py +42 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code.egg-info/PKG-INFO +23 -1
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code.egg-info/SOURCES.txt +13 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code.egg-info/requires.txt +3 -2
- iac_code-0.2.3/src/iac_code/__init__.py +0 -2
- iac_code-0.2.3/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.2.3/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.2.3/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.2.3/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.2.3/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.2.3/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.2.3/src/iac_code/skills/bundled/iac_aliyun/__init__.py +0 -16
- iac_code-0.2.3/src/iac_code/tools/result_storage.py +0 -39
- iac_code-0.2.3/src/iac_code/ui/core/raw_input.py +0 -319
- iac_code-0.2.3/src/iac_code/utils/project_paths.py +0 -74
- {iac_code-0.2.3 → iac_code-0.3.1}/LICENSE +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/MANIFEST.in +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/setup.cfg +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/artifacts.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/metrics.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/persistence.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/push_worker.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/router.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/signing.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/task_store.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transport.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/grpc.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/grpc_jsonrpc.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/redis_streams.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/unix.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/transports/websocket.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/a2a/types.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/convert.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/http_sse.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/mcp.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/metrics.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/server.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/state.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/tools.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/types.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/acp/version.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/agent/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/agent/agent_tool.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/agent/agent_types.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/agent/message.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/cli/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/compact.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/debug.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/effort.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/exit.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/help.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/model.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/resume.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/commands/tasks.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/memory/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/memory/memory_tools.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/anthropic_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/azure_openai_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/base.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/dashscope_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/deepseek_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/gemini_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/kimi_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/lmstudio_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/manager.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/minimax_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/modelscope_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/ollama_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/openai_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/openrouter_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/retry.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/siliconflow_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/stream_watchdog.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/thinking.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/volcengine_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/providers/zhipu_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/capabilities/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/cloud_credentials.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/context_manager.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/permissions/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/permissions/pipeline.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/permissions/storage.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/providers/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/qwenpaw_source.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/session_index.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/attributes.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/constants.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/content_serializer.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/events.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/metrics.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/names.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/sanitize.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/sink.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/tracing.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/telemetry/types.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/token_budget.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/services/token_counter.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/ecs.md +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/oss.md +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/rds.md +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/redis.md +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/slb.md +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/vpc.md +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/ros-template.md +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/template-parameters.md +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/iac_aliyun/references/terraform-template.md +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/bundled/simplify.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/listing.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/loader.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/processor.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/skills/skill_tool.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/state/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/state/app_state.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tasks/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tasks/notification_queue.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tasks/task_state.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tasks/task_tools.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/bash/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/bash/command_parser.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/bash/mode_validation.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/bash/permissions.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/bash/readonly_commands.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/bash/rule_matching.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/aliyun_doc_search.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/api_hooks.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/endpoints.yml +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/hooks/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_parameters.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_validate.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/ros_stack_instances.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/aliyun/ros_yaml.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/base_api.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/base_stack.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/registry.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/cloud/types.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/tool_executor.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/tools/web_fetch.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/types/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/types/permissions.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/types/skill_source.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/types/stream_events.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/components/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/components/dialog.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/components/divider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/components/fuzzy_picker.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/components/progress_bar.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/components/search_box.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/components/select.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/components/status_icon.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/components/tabs.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/core/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/core/in_place_render.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/core/key_event.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/core/screen.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/dialogs/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/dialogs/history_search.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/dialogs/model_picker.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/dialogs/quick_open.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/dialogs/resume_picker.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/keybindings/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/keybindings/manager.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/suggestions/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/suggestions/aggregator.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/suggestions/command_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/suggestions/directory_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/suggestions/file_provider.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/ui/transcript_view.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/background_housekeeping.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/cleanup.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/image/__init__.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/image/clipboard.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/image/format_detect.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/image/pasted_content.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/image/processor.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/image/resizer.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code/utils/tool_input_parser.py +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code.egg-info/dependency_links.txt +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code.egg-info/entry_points.txt +0 -0
- {iac_code-0.2.3 → iac_code-0.3.1}/src/iac_code.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: iac_code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Your AI-powered Infrastructure as Code assistant
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -34,8 +34,16 @@ License-File: LICENSE
|
|
|
34
34
|
|
|
35
35
|
> **Documentation**: [https://aliyun.github.io/iac-code/](https://aliyun.github.io/iac-code/)
|
|
36
36
|
|
|
37
|
+
<p align="center">
|
|
38
|
+
<img src="website/static/img/demo_en.gif" alt="iac-code demo" width="100%">
|
|
39
|
+
</p>
|
|
40
|
+
|
|
37
41
|
## Installation
|
|
38
42
|
|
|
43
|
+
IaC Code requires Python 3.10 or later. It supports macOS, Linux, and Windows.
|
|
44
|
+
|
|
45
|
+
> **Windows note**: On Windows, [Git for Windows](https://gitforwindows.org/) must be installed to provide the bash shell used by the tool execution environment. If Git Bash is installed but not on PATH, set the `IAC_CODE_GIT_BASH_PATH` environment variable.
|
|
46
|
+
|
|
39
47
|
```bash
|
|
40
48
|
pip install iac-code
|
|
41
49
|
```
|
|
@@ -66,6 +74,20 @@ Reading from stdin is also supported:
|
|
|
66
74
|
echo "Create an OSS Bucket" | iac-code --prompt -
|
|
67
75
|
```
|
|
68
76
|
|
|
77
|
+
## Contributing
|
|
78
|
+
|
|
79
|
+
Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
make install # install dependencies and pre-commit hooks
|
|
83
|
+
make dev # run in debug mode
|
|
84
|
+
make test # run tests
|
|
85
|
+
make lint # run linters
|
|
86
|
+
make format # format code
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
See the [Contributing Guide](https://aliyun.github.io/iac-code/getting-started/contributing) for details.
|
|
90
|
+
|
|
69
91
|
## Contact Us
|
|
70
92
|
|
|
71
93
|
| [DingTalk](https://qr.dingtalk.com/action/joingroup?code=v1,k1,ubm/77U7qRh/STFZUNBP26X4PNg2z6+uhiPcLGtDNfU=&_dt_no_comment=1&origin=11) | [Discord](https://discord.gg/qECFuFBwF) |
|
|
@@ -15,8 +15,16 @@
|
|
|
15
15
|
|
|
16
16
|
> **Documentation**: [https://aliyun.github.io/iac-code/](https://aliyun.github.io/iac-code/)
|
|
17
17
|
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="website/static/img/demo_en.gif" alt="iac-code demo" width="100%">
|
|
20
|
+
</p>
|
|
21
|
+
|
|
18
22
|
## Installation
|
|
19
23
|
|
|
24
|
+
IaC Code requires Python 3.10 or later. It supports macOS, Linux, and Windows.
|
|
25
|
+
|
|
26
|
+
> **Windows note**: On Windows, [Git for Windows](https://gitforwindows.org/) must be installed to provide the bash shell used by the tool execution environment. If Git Bash is installed but not on PATH, set the `IAC_CODE_GIT_BASH_PATH` environment variable.
|
|
27
|
+
|
|
20
28
|
```bash
|
|
21
29
|
pip install iac-code
|
|
22
30
|
```
|
|
@@ -47,6 +55,20 @@ Reading from stdin is also supported:
|
|
|
47
55
|
echo "Create an OSS Bucket" | iac-code --prompt -
|
|
48
56
|
```
|
|
49
57
|
|
|
58
|
+
## Contributing
|
|
59
|
+
|
|
60
|
+
Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
make install # install dependencies and pre-commit hooks
|
|
64
|
+
make dev # run in debug mode
|
|
65
|
+
make test # run tests
|
|
66
|
+
make lint # run linters
|
|
67
|
+
make format # format code
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
See the [Contributing Guide](https://aliyun.github.io/iac-code/getting-started/contributing) for details.
|
|
71
|
+
|
|
50
72
|
## Contact Us
|
|
51
73
|
|
|
52
74
|
| [DingTalk](https://qr.dingtalk.com/action/joingroup?code=v1,k1,ubm/77U7qRh/STFZUNBP26X4PNg2z6+uhiPcLGtDNfU=&_dt_no_comment=1&origin=11) | [Discord](https://discord.gg/qECFuFBwF) |
|
|
@@ -21,6 +21,7 @@ dependencies = [
|
|
|
21
21
|
"pyperclip>=1.8.0",
|
|
22
22
|
"openai>=1.50",
|
|
23
23
|
"httpx>=0.27.0",
|
|
24
|
+
"packaging>=24.0",
|
|
24
25
|
"tiktoken>=0.7.0",
|
|
25
26
|
"jsonschema>=4.20",
|
|
26
27
|
"alibabacloud-ros20190910>=3.0.0",
|
|
@@ -32,8 +33,8 @@ dependencies = [
|
|
|
32
33
|
"pillow==12.2.0",
|
|
33
34
|
"cryptography>=42.0",
|
|
34
35
|
"keyring>=25.0",
|
|
35
|
-
"tree-sitter>=0.
|
|
36
|
-
"tree-sitter-bash>=0.
|
|
36
|
+
"tree-sitter>=0.25,<0.26",
|
|
37
|
+
"tree-sitter-bash>=0.25,<0.26",
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
[project.optional-dependencies]
|
|
@@ -68,6 +69,7 @@ dev = [
|
|
|
68
69
|
"pre-commit>=4.0",
|
|
69
70
|
"pytest-xdist>=3.0",
|
|
70
71
|
"pytest-cov>=5.0",
|
|
72
|
+
"pytest-timeout>=2.3.1",
|
|
71
73
|
"setuptools>=68.0",
|
|
72
74
|
"wheel",
|
|
73
75
|
"tomli>=2.0; python_version<\"3.11\"",
|
|
@@ -111,6 +113,9 @@ index-url = "https://mirrors.aliyun.com/pypi/simple/"
|
|
|
111
113
|
url = "https://mirrors.aliyun.com/pypi/simple/"
|
|
112
114
|
default = true
|
|
113
115
|
|
|
116
|
+
[tool.pytest.ini_options]
|
|
117
|
+
timeout = 30
|
|
118
|
+
|
|
114
119
|
[tool.coverage.run]
|
|
115
120
|
source_pkgs = ["iac_code"]
|
|
116
121
|
branch = true
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""Custom build hook to inject __release_date__ at build time."""
|
|
2
2
|
|
|
3
|
+
import platform
|
|
3
4
|
import re
|
|
4
5
|
import subprocess
|
|
5
6
|
import sys
|
|
6
7
|
from datetime import date
|
|
7
8
|
from pathlib import Path
|
|
8
|
-
import platform
|
|
9
9
|
|
|
10
10
|
from setuptools import setup
|
|
11
11
|
from setuptools.command.build_py import build_py
|
|
@@ -57,15 +57,16 @@ def _ensure_babel():
|
|
|
57
57
|
except Exception as exc:
|
|
58
58
|
attempts.append("ensurepip + pip -> %s" % exc)
|
|
59
59
|
|
|
60
|
-
# 3) apt-get install python3-babel (Debian/Ubuntu container with root)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
# 3) apt-get install python3-babel (Debian/Ubuntu container with root) — Linux only
|
|
61
|
+
if sys.platform == "linux":
|
|
62
|
+
try:
|
|
63
|
+
_run(["apt-get", "update", "-qq"])
|
|
64
|
+
_run(["apt-get", "install", "-y", "-qq", "python3-babel"])
|
|
65
|
+
result = _try_import_babel()
|
|
66
|
+
if result:
|
|
67
|
+
return result
|
|
68
|
+
except Exception as exc:
|
|
69
|
+
attempts.append("apt-get install python3-babel -> %s" % exc)
|
|
69
70
|
|
|
70
71
|
# 4) download get-pip.py via urllib, bootstrap pip, then pip install babel
|
|
71
72
|
try:
|
|
@@ -18,10 +18,12 @@ from a2a.types import (
|
|
|
18
18
|
from google.protobuf.json_format import ParseDict
|
|
19
19
|
|
|
20
20
|
from iac_code import __version__
|
|
21
|
+
from iac_code.a2a.exposure import format_a2a_exposure_types
|
|
21
22
|
from iac_code.a2a.parts import supported_input_mime_types
|
|
22
23
|
from iac_code.a2a.signing import sign_agent_card_dict
|
|
23
24
|
|
|
24
25
|
IAC_CODE_ARTIFACT_METADATA_EXTENSION_URI = "urn:iac-code:a2a:artifact-metadata:v1"
|
|
26
|
+
IAC_CODE_THINKING_EXPOSURE_EXTENSION_URI = "urn:iac-code:a2a:thinking-exposure:v1"
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
def _base_url(host: str, port: int) -> str:
|
|
@@ -66,6 +68,7 @@ def build_agent_card(
|
|
|
66
68
|
push_notifications: bool = False,
|
|
67
69
|
supported_interfaces: list[dict[str, str]] | None = None,
|
|
68
70
|
agent_extensions: Any = None,
|
|
71
|
+
thinking_exposure_types: Any = None,
|
|
69
72
|
) -> AgentCard:
|
|
70
73
|
url = _base_url(host, port)
|
|
71
74
|
description = "AI-powered Infrastructure as Code assistant for Alibaba Cloud ROS and Terraform workflows."
|
|
@@ -152,6 +155,19 @@ def build_agent_card(
|
|
|
152
155
|
required=False,
|
|
153
156
|
)
|
|
154
157
|
)
|
|
158
|
+
if thinking_exposure_types is not None:
|
|
159
|
+
enabled_types = format_a2a_exposure_types(thinking_exposure_types)
|
|
160
|
+
if enabled_types:
|
|
161
|
+
extension = AgentExtension(
|
|
162
|
+
uri=IAC_CODE_THINKING_EXPOSURE_EXTENSION_URI,
|
|
163
|
+
description=(
|
|
164
|
+
"Optional iac-code metadata namespace for selected thinking exposure signals. "
|
|
165
|
+
"Raw thinking is emitted only when raw_thinking is enabled."
|
|
166
|
+
),
|
|
167
|
+
required=False,
|
|
168
|
+
)
|
|
169
|
+
ParseDict({"enabledTypes": enabled_types}, extension.params)
|
|
170
|
+
card.capabilities.extensions.append(extension)
|
|
155
171
|
for item in _iter_agent_extensions(agent_extensions):
|
|
156
172
|
card.capabilities.extensions.append(_agent_extension_from_dict(item))
|
|
157
173
|
|
|
@@ -166,6 +166,7 @@ def create_app(
|
|
|
166
166
|
supported_interfaces: list[dict[str, str]] | None = None,
|
|
167
167
|
agent_extensions: object | None = None,
|
|
168
168
|
auto_approve_permissions: bool = False,
|
|
169
|
+
thinking_exposure: object | None = None,
|
|
169
170
|
) -> Starlette:
|
|
170
171
|
from iac_code.a2a.transports.dispatcher import create_runtime_components
|
|
171
172
|
|
|
@@ -194,6 +195,7 @@ def create_app(
|
|
|
194
195
|
supported_interfaces=supported_interfaces,
|
|
195
196
|
agent_extensions=agent_extensions,
|
|
196
197
|
auto_approve_permissions=auto_approve_permissions,
|
|
198
|
+
thinking_exposure=thinking_exposure,
|
|
197
199
|
)
|
|
198
200
|
|
|
199
201
|
@asynccontextmanager
|
|
@@ -287,8 +289,9 @@ def run_server(
|
|
|
287
289
|
push_consumer_name: str | None = None,
|
|
288
290
|
push_lease_timeout_ms: int = 300_000,
|
|
289
291
|
auto_approve_permissions: bool = False,
|
|
292
|
+
thinking_exposure: object | None = None,
|
|
290
293
|
) -> None:
|
|
291
|
-
from iac_code.a2a.transports.base import normalize_transport_name
|
|
294
|
+
from iac_code.a2a.transports.base import normalize_transport_name, validate_transport_for_platform
|
|
292
295
|
|
|
293
296
|
normalized_transport = normalize_transport_name(transport)
|
|
294
297
|
if persistence_dir is None:
|
|
@@ -305,6 +308,8 @@ def run_server(
|
|
|
305
308
|
if push_queue == "redis-streams" and not push_redis_url:
|
|
306
309
|
raise RuntimeError("--push-redis-url is required for --push-queue redis-streams.")
|
|
307
310
|
|
|
311
|
+
validate_transport_for_platform(normalized_transport)
|
|
312
|
+
|
|
308
313
|
supported_interfaces = _supported_interfaces(
|
|
309
314
|
transport=normalized_transport,
|
|
310
315
|
host=host,
|
|
@@ -345,6 +350,7 @@ def run_server(
|
|
|
345
350
|
"push_lease_timeout_ms": push_lease_timeout_ms,
|
|
346
351
|
"supported_interfaces": supported_interfaces,
|
|
347
352
|
"auto_approve_permissions": auto_approve_permissions,
|
|
353
|
+
"thinking_exposure": thinking_exposure,
|
|
348
354
|
}
|
|
349
355
|
|
|
350
356
|
if normalized_transport == "stdio":
|
|
@@ -442,6 +448,7 @@ def run_server(
|
|
|
442
448
|
push_lease_timeout_ms=push_lease_timeout_ms,
|
|
443
449
|
supported_interfaces=supported_interfaces,
|
|
444
450
|
auto_approve_permissions=auto_approve_permissions,
|
|
451
|
+
thinking_exposure=thinking_exposure,
|
|
445
452
|
)
|
|
446
453
|
|
|
447
454
|
try:
|
|
@@ -7,6 +7,7 @@ from time import monotonic
|
|
|
7
7
|
from typing import Any, AsyncIterator
|
|
8
8
|
|
|
9
9
|
import httpx
|
|
10
|
+
from a2a.types import Role
|
|
10
11
|
|
|
11
12
|
from iac_code.a2a.signing import AgentCardSignature, agent_card_signature_jwks_url, verify_agent_card_dict
|
|
12
13
|
from iac_code.a2a.transport import A2AAuthConfig, A2ATransportBinding, UnsupportedA2ATransportError, headers_for_auth
|
|
@@ -31,19 +32,52 @@ class A2AClientResponse:
|
|
|
31
32
|
if not isinstance(result, dict):
|
|
32
33
|
return ""
|
|
33
34
|
text = result.get("text")
|
|
34
|
-
if isinstance(text, str):
|
|
35
|
+
if isinstance(text, str) and text:
|
|
35
36
|
return text
|
|
36
37
|
status = result.get("status")
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
if isinstance(status, dict):
|
|
39
|
+
extracted = _extract_parts_text(status.get("message"))
|
|
40
|
+
if extracted:
|
|
41
|
+
return extracted
|
|
42
|
+
task = result.get("task")
|
|
43
|
+
if isinstance(task, dict):
|
|
44
|
+
task_status = task.get("status")
|
|
45
|
+
if isinstance(task_status, dict):
|
|
46
|
+
extracted = _extract_parts_text(task_status.get("message"))
|
|
47
|
+
if extracted:
|
|
48
|
+
return extracted
|
|
49
|
+
history = task.get("history")
|
|
50
|
+
if isinstance(history, list):
|
|
51
|
+
for entry in reversed(history):
|
|
52
|
+
extracted = _extract_agent_entry_text(entry)
|
|
53
|
+
if extracted:
|
|
54
|
+
return extracted
|
|
55
|
+
return ""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_AGENT_ROLE_NAME = Role.Name(Role.ROLE_AGENT)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _extract_agent_entry_text(entry: Any) -> str:
|
|
62
|
+
if not isinstance(entry, dict) or entry.get("role") != _AGENT_ROLE_NAME:
|
|
63
|
+
return ""
|
|
64
|
+
return _extract_parts_text(entry)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _extract_parts_text(message: Any) -> str:
|
|
68
|
+
if not isinstance(message, dict):
|
|
69
|
+
return ""
|
|
70
|
+
parts = message.get("parts")
|
|
71
|
+
if not isinstance(parts, list):
|
|
72
|
+
return ""
|
|
73
|
+
pieces: list[str] = []
|
|
74
|
+
for part in parts:
|
|
75
|
+
if not isinstance(part, dict):
|
|
76
|
+
continue
|
|
77
|
+
value = part.get("text")
|
|
78
|
+
if isinstance(value, str):
|
|
79
|
+
pieces.append(value)
|
|
80
|
+
return "".join(pieces)
|
|
47
81
|
|
|
48
82
|
|
|
49
83
|
class A2AClient:
|
|
@@ -302,7 +336,7 @@ class A2AClient:
|
|
|
302
336
|
message: dict[str, Any] = {
|
|
303
337
|
"messageId": str(uuid.uuid4()),
|
|
304
338
|
"role": "ROLE_USER",
|
|
305
|
-
"parts": [{"
|
|
339
|
+
"parts": [{"text": prompt}],
|
|
306
340
|
"metadata": {"iac_code": {"cwd": cwd}},
|
|
307
341
|
}
|
|
308
342
|
if context_id:
|
|
@@ -18,6 +18,7 @@ from a2a.types import (
|
|
|
18
18
|
)
|
|
19
19
|
from google.protobuf.json_format import ParseDict
|
|
20
20
|
|
|
21
|
+
from iac_code.a2a.exposure import A2AExposureType, normalize_a2a_exposure_types
|
|
21
22
|
from iac_code.types.stream_events import (
|
|
22
23
|
ErrorEvent,
|
|
23
24
|
MessageEndEvent,
|
|
@@ -170,7 +171,10 @@ async def publish_stream_event(
|
|
|
170
171
|
artifact_store: Any | None = None,
|
|
171
172
|
permission_resolver: A2APermissionResolver | None = None,
|
|
172
173
|
auto_approve_permissions: bool = False,
|
|
174
|
+
exposure_types: Any = None,
|
|
173
175
|
) -> str | None:
|
|
176
|
+
enabled_exposure_types = normalize_a2a_exposure_types(exposure_types)
|
|
177
|
+
|
|
174
178
|
if isinstance(event, TextDeltaEvent):
|
|
175
179
|
if not event.text:
|
|
176
180
|
return None
|
|
@@ -184,9 +188,20 @@ async def publish_stream_event(
|
|
|
184
188
|
return event.text
|
|
185
189
|
|
|
186
190
|
if isinstance(event, ThinkingDeltaEvent):
|
|
191
|
+
if A2AExposureType.RAW_THINKING not in enabled_exposure_types:
|
|
192
|
+
return None
|
|
193
|
+
await _enqueue_status(
|
|
194
|
+
event_queue,
|
|
195
|
+
task_id=task_id,
|
|
196
|
+
context_id=context_id,
|
|
197
|
+
state=TaskState.TASK_STATE_WORKING,
|
|
198
|
+
metadata={"iac_code": {"thinking": {"type": "raw_thinking", "text": _truncate(event.text)}}},
|
|
199
|
+
)
|
|
187
200
|
return None
|
|
188
201
|
|
|
189
202
|
if isinstance(event, ToolUseStartEvent):
|
|
203
|
+
if A2AExposureType.TOOL_TRACE not in enabled_exposure_types:
|
|
204
|
+
return None
|
|
190
205
|
await _enqueue_status(
|
|
191
206
|
event_queue,
|
|
192
207
|
task_id=task_id,
|
|
@@ -197,6 +212,8 @@ async def publish_stream_event(
|
|
|
197
212
|
return None
|
|
198
213
|
|
|
199
214
|
if isinstance(event, ToolInputDeltaEvent):
|
|
215
|
+
if A2AExposureType.TOOL_TRACE not in enabled_exposure_types:
|
|
216
|
+
return None
|
|
200
217
|
await _enqueue_status(
|
|
201
218
|
event_queue,
|
|
202
219
|
task_id=task_id,
|
|
@@ -215,6 +232,8 @@ async def publish_stream_event(
|
|
|
215
232
|
return None
|
|
216
233
|
|
|
217
234
|
if isinstance(event, ToolUseEndEvent):
|
|
235
|
+
if A2AExposureType.TOOL_TRACE not in enabled_exposure_types:
|
|
236
|
+
return None
|
|
218
237
|
await _enqueue_status(
|
|
219
238
|
event_queue,
|
|
220
239
|
task_id=task_id,
|
|
@@ -235,6 +254,12 @@ async def publish_stream_event(
|
|
|
235
254
|
|
|
236
255
|
if isinstance(event, ToolResultEvent):
|
|
237
256
|
artifact_metadata = _extract_artifact_metadata(event.result, artifact_store)
|
|
257
|
+
if A2AExposureType.TOOL_TRACE not in enabled_exposure_types:
|
|
258
|
+
if artifact_metadata is not None:
|
|
259
|
+
await event_queue.enqueue_event(
|
|
260
|
+
_artifact_update_event(task_id=task_id, context_id=context_id, metadata=artifact_metadata)
|
|
261
|
+
)
|
|
262
|
+
return None
|
|
238
263
|
tool_metadata = {
|
|
239
264
|
"status": "failed" if event.is_error else "completed",
|
|
240
265
|
"toolUseId": event.tool_use_id,
|
|
@@ -262,6 +287,8 @@ async def publish_stream_event(
|
|
|
262
287
|
approved = bool(await decision) if inspect.isawaitable(decision) else bool(decision)
|
|
263
288
|
if event.response_future is not None and not event.response_future.done():
|
|
264
289
|
event.response_future.set_result(approved)
|
|
290
|
+
if A2AExposureType.TOOL_TRACE not in enabled_exposure_types:
|
|
291
|
+
return None
|
|
265
292
|
await _enqueue_status(
|
|
266
293
|
event_queue,
|
|
267
294
|
task_id=task_id,
|
|
@@ -15,6 +15,7 @@ from a2a.types import Message, Role, Task, TaskState, TaskStatus, TaskStatusUpda
|
|
|
15
15
|
from google.protobuf.json_format import MessageToDict
|
|
16
16
|
|
|
17
17
|
from iac_code.a2a.events import make_text_part, publish_stream_event
|
|
18
|
+
from iac_code.a2a.exposure import normalize_a2a_exposure_types
|
|
18
19
|
from iac_code.a2a.metrics import A2AMetrics, NoOpA2AMetrics
|
|
19
20
|
from iac_code.a2a.parts import allowed_cwd_roots, is_relative_to, parts_to_prompt
|
|
20
21
|
from iac_code.a2a.task_store import A2ATaskStore
|
|
@@ -26,6 +27,7 @@ from iac_code.a2a.types import (
|
|
|
26
27
|
)
|
|
27
28
|
from iac_code.services.agent_factory import AgentFactoryOptions, create_agent_runtime
|
|
28
29
|
from iac_code.services.session_storage import SessionStorage
|
|
30
|
+
from iac_code.services.telemetry import use_session_id
|
|
29
31
|
|
|
30
32
|
logger = logging.getLogger(__name__)
|
|
31
33
|
_CONTEXT_LOCK_ACQUIRE_TIMEOUT_SECONDS = 1
|
|
@@ -61,6 +63,7 @@ class IacCodeA2AExecutor(AgentExecutor):
|
|
|
61
63
|
push_notifier: Any | None = None,
|
|
62
64
|
permission_resolver: A2APermissionResolver | None = None,
|
|
63
65
|
auto_approve_permissions: bool = False,
|
|
66
|
+
thinking_exposure_types: Any = None,
|
|
64
67
|
) -> None:
|
|
65
68
|
self._task_store = task_store
|
|
66
69
|
self._model = model
|
|
@@ -69,6 +72,7 @@ class IacCodeA2AExecutor(AgentExecutor):
|
|
|
69
72
|
self._push_notifier = push_notifier
|
|
70
73
|
self._permission_resolver = permission_resolver
|
|
71
74
|
self._auto_approve_permissions = auto_approve_permissions
|
|
75
|
+
self._thinking_exposure_types = normalize_a2a_exposure_types(thinking_exposure_types)
|
|
72
76
|
|
|
73
77
|
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
|
|
74
78
|
task_id = context.task_id or "task-" + uuid.uuid4().hex[:12]
|
|
@@ -218,18 +222,20 @@ class IacCodeA2AExecutor(AgentExecutor):
|
|
|
218
222
|
context_id=context_id,
|
|
219
223
|
state=TaskState.TASK_STATE_WORKING,
|
|
220
224
|
)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
225
|
+
with use_session_id(ctx.session_id):
|
|
226
|
+
async for event in runtime.agent_loop.run_streaming(prompt):
|
|
227
|
+
text_chunk = await publish_stream_event(
|
|
228
|
+
event_queue,
|
|
229
|
+
task_id=task_id,
|
|
230
|
+
context_id=context_id,
|
|
231
|
+
event=event,
|
|
232
|
+
artifact_store=self._artifact_store,
|
|
233
|
+
permission_resolver=self._permission_resolver,
|
|
234
|
+
auto_approve_permissions=self._auto_approve_permissions,
|
|
235
|
+
exposure_types=self._thinking_exposure_types,
|
|
236
|
+
)
|
|
237
|
+
if text_chunk:
|
|
238
|
+
task.output_text.append(text_chunk)
|
|
233
239
|
task.state = TASK_STATE_INPUT_REQUIRED
|
|
234
240
|
await self._publish_status(
|
|
235
241
|
event_queue,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class A2AExposureType(str, Enum):
|
|
9
|
+
RAW_THINKING = "raw_thinking"
|
|
10
|
+
TOOL_TRACE = "tool_trace"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_EXPOSURE_TYPE_ORDER = (
|
|
14
|
+
A2AExposureType.RAW_THINKING,
|
|
15
|
+
A2AExposureType.TOOL_TRACE,
|
|
16
|
+
)
|
|
17
|
+
_SUPPORTED_TYPE_NAMES = ", ".join(item.value.replace("_", "-") for item in _EXPOSURE_TYPE_ORDER)
|
|
18
|
+
_DISABLED_ALIASES = frozenset({"", "none", "off", "false", "0"})
|
|
19
|
+
_ALL_ALIASES = frozenset({"all", "*"})
|
|
20
|
+
DEFAULT_A2A_EXPOSURE_TYPES = frozenset({A2AExposureType.TOOL_TRACE})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def normalize_a2a_exposure_types(value: Any) -> frozenset[A2AExposureType]:
|
|
24
|
+
if value is None:
|
|
25
|
+
return DEFAULT_A2A_EXPOSURE_TYPES
|
|
26
|
+
|
|
27
|
+
tokens = list(_iter_exposure_tokens(value))
|
|
28
|
+
if not tokens:
|
|
29
|
+
return frozenset()
|
|
30
|
+
if any(token in _ALL_ALIASES for token in tokens):
|
|
31
|
+
return frozenset(_EXPOSURE_TYPE_ORDER)
|
|
32
|
+
tokens = [token for token in tokens if token not in _DISABLED_ALIASES]
|
|
33
|
+
return frozenset(_exposure_type_from_token(token) for token in tokens)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def format_a2a_exposure_types(value: Any) -> list[str]:
|
|
37
|
+
enabled = normalize_a2a_exposure_types(value)
|
|
38
|
+
return [item.value for item in _EXPOSURE_TYPE_ORDER if item in enabled]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _iter_exposure_tokens(value: Any) -> Iterable[str]:
|
|
42
|
+
if isinstance(value, A2AExposureType):
|
|
43
|
+
yield value.value
|
|
44
|
+
return
|
|
45
|
+
if isinstance(value, str):
|
|
46
|
+
for item in value.replace(";", ",").split(","):
|
|
47
|
+
token = item.strip().lower().replace("-", "_")
|
|
48
|
+
if token:
|
|
49
|
+
yield token
|
|
50
|
+
return
|
|
51
|
+
if isinstance(value, Iterable) and not isinstance(value, dict):
|
|
52
|
+
for item in value:
|
|
53
|
+
yield from _iter_exposure_tokens(item)
|
|
54
|
+
return
|
|
55
|
+
raise ValueError(
|
|
56
|
+
f"A2A thinking exposure must be a string or list of strings. Supported values: {_SUPPORTED_TYPE_NAMES}."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _exposure_type_from_token(token: str) -> A2AExposureType:
|
|
61
|
+
try:
|
|
62
|
+
return A2AExposureType(token)
|
|
63
|
+
except ValueError as exc:
|
|
64
|
+
raise ValueError(
|
|
65
|
+
f"Unsupported A2A thinking exposure type {token!r}. Supported values: {_SUPPORTED_TYPE_NAMES}."
|
|
66
|
+
) from exc
|
|
@@ -9,6 +9,7 @@ from collections.abc import Iterable
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Any
|
|
11
11
|
from urllib.parse import unquote, urlparse
|
|
12
|
+
from urllib.request import url2pathname
|
|
12
13
|
|
|
13
14
|
from google.protobuf.json_format import MessageToDict
|
|
14
15
|
|
|
@@ -129,7 +130,7 @@ def _read_file_url_part(url: str, *, cwd: Path) -> str:
|
|
|
129
130
|
raise ValueError("A2A file URL parts must use local file:// URLs.")
|
|
130
131
|
|
|
131
132
|
cwd_path = cwd.resolve()
|
|
132
|
-
path = Path(unquote(parsed.path)).resolve()
|
|
133
|
+
path = Path(url2pathname(unquote(parsed.path))).resolve()
|
|
133
134
|
if not is_relative_to(path, cwd_path) or not any(is_relative_to(path, root) for root in allowed_cwd_roots()):
|
|
134
135
|
raise ValueError("A2A file URL part is outside the allowed workspace.")
|
|
135
136
|
if not path.is_file():
|
|
@@ -199,7 +200,7 @@ def _safe_file_url_path(url: str, *, cwd: Path) -> Path:
|
|
|
199
200
|
raise ValueError("A2A file URL parts must use local file:// URLs.")
|
|
200
201
|
|
|
201
202
|
cwd_path = cwd.resolve()
|
|
202
|
-
path = Path(unquote(parsed.path)).resolve()
|
|
203
|
+
path = Path(url2pathname(unquote(parsed.path))).resolve()
|
|
203
204
|
if not is_relative_to(path, cwd_path) or not any(is_relative_to(path, root) for root in allowed_cwd_roots()):
|
|
204
205
|
raise ValueError("A2A file URL part is outside the allowed workspace.")
|
|
205
206
|
if not path.is_file():
|
|
@@ -4,7 +4,6 @@ import asyncio
|
|
|
4
4
|
import base64
|
|
5
5
|
import hashlib
|
|
6
6
|
import json
|
|
7
|
-
import os
|
|
8
7
|
import uuid
|
|
9
8
|
from dataclasses import asdict, dataclass
|
|
10
9
|
from ipaddress import ip_address
|
|
@@ -26,6 +25,7 @@ from iac_code.a2a.persistence import A2APersistenceStore
|
|
|
26
25
|
from iac_code.a2a.push_queue import A2APushJob, A2APushQueue, LocalFileA2APushQueue
|
|
27
26
|
from iac_code.a2a.push_secrets import A2APushSecretKeyring
|
|
28
27
|
from iac_code.a2a.types import validate_protocol_id
|
|
28
|
+
from iac_code.utils.file_security import restrict_file_permissions, safe_replace
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class InvalidPushNotificationConfigError(ValueError):
|
|
@@ -74,7 +74,7 @@ class A2APushConfigStore(PushNotificationConfigStore):
|
|
|
74
74
|
self._root = Path(persistence.root) / "push_configs"
|
|
75
75
|
self._secret_keyring = secret_keyring or A2APushSecretKeyring(Path(persistence.root) / "push_keys.json")
|
|
76
76
|
self._root.mkdir(parents=True, exist_ok=True)
|
|
77
|
-
|
|
77
|
+
restrict_file_permissions(self._root, directory=True)
|
|
78
78
|
|
|
79
79
|
async def set_info(
|
|
80
80
|
self,
|
|
@@ -95,7 +95,7 @@ class A2APushConfigStore(PushNotificationConfigStore):
|
|
|
95
95
|
|
|
96
96
|
path = self._config_path(_owner(context), task_id, config.id)
|
|
97
97
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
98
|
-
|
|
98
|
+
restrict_file_permissions(path.parent, directory=True)
|
|
99
99
|
data = self._config_to_storage(config)
|
|
100
100
|
_write_json_atomic(path, data)
|
|
101
101
|
|
|
@@ -304,19 +304,12 @@ def _owner(context: ServerCallContext) -> str:
|
|
|
304
304
|
return resolve_user_scope(context)
|
|
305
305
|
|
|
306
306
|
|
|
307
|
-
def _chmod_private(path: Path, *, directory: bool) -> None:
|
|
308
|
-
try:
|
|
309
|
-
os.chmod(path, 0o700 if directory else 0o600)
|
|
310
|
-
except OSError:
|
|
311
|
-
return
|
|
312
|
-
|
|
313
|
-
|
|
314
307
|
def _write_json_atomic(path: Path, data: dict[str, Any]) -> None:
|
|
315
308
|
tmp_path = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp")
|
|
316
309
|
try:
|
|
317
310
|
tmp_path.write_text(json.dumps(data, ensure_ascii=False, sort_keys=True), encoding="utf-8")
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
311
|
+
restrict_file_permissions(tmp_path, directory=False)
|
|
312
|
+
safe_replace(str(tmp_path), str(path))
|
|
313
|
+
restrict_file_permissions(path, directory=False)
|
|
321
314
|
finally:
|
|
322
315
|
tmp_path.unlink(missing_ok=True)
|
|
@@ -13,6 +13,7 @@ from pathlib import Path
|
|
|
13
13
|
from typing import Any, Protocol
|
|
14
14
|
|
|
15
15
|
from iac_code.a2a.push_secrets import A2APushSecretKeyring
|
|
16
|
+
from iac_code.utils.file_security import restrict_file_permissions
|
|
16
17
|
|
|
17
18
|
_REDACTED_HEADERS = {"authorization", "x-a2a-notification-token", "x-api-key", "api-key"}
|
|
18
19
|
_ENCRYPTED_JOB_FIELD = "iacCodeEncryptedPushJob"
|
|
@@ -127,7 +128,7 @@ class LocalFileA2APushQueue:
|
|
|
127
128
|
self.dead_dir = self.root / "dead"
|
|
128
129
|
for path in (self.pending_dir, self.inflight_dir, self.dead_dir):
|
|
129
130
|
path.mkdir(parents=True, exist_ok=True)
|
|
130
|
-
|
|
131
|
+
restrict_file_permissions(path, directory=True)
|
|
131
132
|
|
|
132
133
|
async def enqueue(self, job: A2APushJob) -> None:
|
|
133
134
|
self._write(self.pending_dir / f"{job.job_id}.json", job)
|
|
@@ -163,7 +164,7 @@ class LocalFileA2APushQueue:
|
|
|
163
164
|
|
|
164
165
|
def _write(self, path: Path, job: A2APushJob) -> None:
|
|
165
166
|
path.write_text(_serialize_push_job(job, secret_keyring=self._secret_keyring), encoding="utf-8")
|
|
166
|
-
|
|
167
|
+
restrict_file_permissions(path, directory=False)
|
|
167
168
|
|
|
168
169
|
def _read(self, path: Path) -> A2APushJob:
|
|
169
170
|
return _deserialize_push_job(path.read_text(encoding="utf-8"), secret_keyring=self._secret_keyring)
|
|
@@ -180,12 +181,6 @@ class LocalFileA2APushQueue:
|
|
|
180
181
|
job.with_attempt(attempt=job.attempt, next_attempt_at=now, last_error="Delivery lease expired."),
|
|
181
182
|
)
|
|
182
183
|
|
|
183
|
-
def _chmod_private(self, path: Path, *, directory: bool) -> None:
|
|
184
|
-
try:
|
|
185
|
-
os.chmod(path, 0o700 if directory else 0o600)
|
|
186
|
-
except OSError:
|
|
187
|
-
return
|
|
188
|
-
|
|
189
184
|
|
|
190
185
|
class RedisStreamsA2APushQueue:
|
|
191
186
|
def __init__(
|