auto-coder 1.0.0__py3-none-any.whl → 2.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of auto-coder might be problematic. Click here for more details.
- auto_coder-2.0.1.dist-info/LICENSE +158 -0
- auto_coder-2.0.1.dist-info/METADATA +558 -0
- auto_coder-2.0.1.dist-info/RECORD +795 -0
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.1.dist-info}/WHEEL +1 -1
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.1.dist-info}/entry_points.txt +3 -3
- autocoder/__init__.py +31 -0
- autocoder/agent/auto_filegroup.py +32 -13
- autocoder/agent/auto_learn_from_commit.py +9 -1
- autocoder/agent/base_agentic/__init__.py +3 -0
- autocoder/agent/base_agentic/agent_hub.py +1 -1
- autocoder/agent/base_agentic/base_agent.py +235 -136
- autocoder/agent/base_agentic/default_tools.py +119 -118
- autocoder/agent/base_agentic/test_base_agent.py +1 -1
- autocoder/agent/base_agentic/tool_registry.py +32 -20
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +24 -3
- autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
- autocoder/agent/base_agentic/types.py +42 -0
- autocoder/agent/entry_command_agent/chat.py +77 -73
- autocoder/auto_coder.py +31 -40
- autocoder/auto_coder_rag.py +11 -1084
- autocoder/auto_coder_runner.py +962 -2345
- autocoder/auto_coder_terminal.py +26 -0
- autocoder/auto_coder_terminal_v3.py +190 -0
- autocoder/chat/conf_command.py +224 -124
- autocoder/chat/models_command.py +361 -299
- autocoder/chat/rules_command.py +79 -31
- autocoder/chat_auto_coder.py +988 -398
- autocoder/chat_auto_coder_lang.py +23 -732
- autocoder/commands/auto_command.py +25 -8
- autocoder/commands/auto_web.py +1 -1
- autocoder/commands/tools.py +44 -44
- autocoder/common/__init__.py +150 -128
- autocoder/common/ac_style_command_parser/__init__.py +39 -2
- autocoder/common/ac_style_command_parser/config.py +422 -0
- autocoder/common/ac_style_command_parser/parser.py +292 -78
- autocoder/common/ac_style_command_parser/test_parser.py +241 -16
- autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
- autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
- autocoder/common/action_yml_file_manager.py +25 -13
- autocoder/common/agent_events/__init__.py +52 -0
- autocoder/common/agent_events/agent_event_emitter.py +193 -0
- autocoder/common/agent_events/event_factory.py +177 -0
- autocoder/common/agent_events/examples.py +307 -0
- autocoder/common/agent_events/types.py +113 -0
- autocoder/common/agent_events/utils.py +68 -0
- autocoder/common/agent_hooks/__init__.py +44 -0
- autocoder/common/agent_hooks/examples.py +582 -0
- autocoder/common/agent_hooks/hook_executor.py +217 -0
- autocoder/common/agent_hooks/hook_manager.py +288 -0
- autocoder/common/agent_hooks/types.py +133 -0
- autocoder/common/agent_hooks/utils.py +99 -0
- autocoder/common/agent_query_queue/queue_executor.py +324 -0
- autocoder/common/agent_query_queue/queue_manager.py +325 -0
- autocoder/common/agents/__init__.py +11 -0
- autocoder/common/agents/agent_manager.py +323 -0
- autocoder/common/agents/agent_parser.py +189 -0
- autocoder/common/agents/example_usage.py +344 -0
- autocoder/common/agents/integration_example.py +330 -0
- autocoder/common/agents/test_agent_parser.py +545 -0
- autocoder/common/async_utils.py +101 -0
- autocoder/common/auto_coder_lang.py +23 -972
- autocoder/common/autocoderargs_parser/__init__.py +14 -0
- autocoder/common/autocoderargs_parser/parser.py +184 -0
- autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
- autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
- autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
- autocoder/common/autocoderargs_parser/token_parser.py +290 -0
- autocoder/common/buildin_tokenizer.py +2 -4
- autocoder/common/code_auto_generate.py +149 -74
- autocoder/common/code_auto_generate_diff.py +163 -70
- autocoder/common/code_auto_generate_editblock.py +179 -89
- autocoder/common/code_auto_generate_strict_diff.py +167 -72
- autocoder/common/code_auto_merge_editblock.py +13 -6
- autocoder/common/code_modification_ranker.py +1 -1
- autocoder/common/command_completer.py +3 -3
- autocoder/common/command_file_manager/manager.py +183 -47
- autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
- autocoder/common/command_templates.py +1 -1
- autocoder/common/conf_utils.py +2 -4
- autocoder/common/conversations/config.py +11 -3
- autocoder/common/conversations/get_conversation_manager.py +100 -2
- autocoder/common/conversations/llm_stats_models.py +264 -0
- autocoder/common/conversations/manager.py +112 -28
- autocoder/common/conversations/models.py +16 -2
- autocoder/common/conversations/storage/index_manager.py +134 -10
- autocoder/common/core_config/__init__.py +63 -0
- autocoder/common/core_config/agentic_mode_manager.py +109 -0
- autocoder/common/core_config/base_manager.py +123 -0
- autocoder/common/core_config/compatibility.py +151 -0
- autocoder/common/core_config/config_manager.py +156 -0
- autocoder/common/core_config/conversation_manager.py +31 -0
- autocoder/common/core_config/exclude_manager.py +72 -0
- autocoder/common/core_config/file_manager.py +177 -0
- autocoder/common/core_config/human_as_model_manager.py +129 -0
- autocoder/common/core_config/lib_manager.py +54 -0
- autocoder/common/core_config/main_manager.py +81 -0
- autocoder/common/core_config/mode_manager.py +126 -0
- autocoder/common/core_config/models.py +70 -0
- autocoder/common/core_config/test_memory_manager.py +1056 -0
- autocoder/common/env_manager.py +282 -0
- autocoder/common/env_manager_usage_example.py +211 -0
- autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
- autocoder/common/file_checkpoint/manager.py +264 -48
- autocoder/common/file_checkpoint/test_backup.py +1 -18
- autocoder/common/file_checkpoint/test_manager.py +270 -1
- autocoder/common/file_checkpoint/test_store.py +1 -17
- autocoder/common/file_handler/__init__.py +23 -0
- autocoder/common/file_handler/active_context_handler.py +159 -0
- autocoder/common/file_handler/add_files_handler.py +409 -0
- autocoder/common/file_handler/chat_handler.py +180 -0
- autocoder/common/file_handler/coding_handler.py +409 -0
- autocoder/common/file_handler/commit_handler.py +200 -0
- autocoder/common/file_handler/lib_handler.py +156 -0
- autocoder/common/file_handler/list_files_handler.py +111 -0
- autocoder/common/file_handler/mcp_handler.py +268 -0
- autocoder/common/file_handler/models_handler.py +493 -0
- autocoder/common/file_handler/remove_files_handler.py +172 -0
- autocoder/common/git_utils.py +44 -8
- autocoder/common/global_cancel.py +15 -6
- autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
- autocoder/common/international/__init__.py +31 -0
- autocoder/common/international/demo_international.py +92 -0
- autocoder/common/international/message_manager.py +157 -0
- autocoder/common/international/messages/__init__.py +56 -0
- autocoder/common/international/messages/async_command_messages.py +507 -0
- autocoder/common/international/messages/auto_coder_messages.py +2208 -0
- autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
- autocoder/common/international/messages/command_help_messages.py +986 -0
- autocoder/common/international/messages/conversation_command_messages.py +191 -0
- autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
- autocoder/common/international/messages/queue_command_messages.py +751 -0
- autocoder/common/international/messages/rules_command_messages.py +77 -0
- autocoder/common/international/messages/sdk_messages.py +1707 -0
- autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
- autocoder/common/international/messages/tool_display_messages.py +1212 -0
- autocoder/common/international/messages/workflow_exception_messages.py +473 -0
- autocoder/common/international/test_international.py +612 -0
- autocoder/common/linter_core/__init__.py +28 -0
- autocoder/common/linter_core/base_linter.py +61 -0
- autocoder/common/linter_core/config_loader.py +271 -0
- autocoder/common/linter_core/formatters/__init__.py +0 -0
- autocoder/common/linter_core/formatters/base_formatter.py +38 -0
- autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
- autocoder/common/linter_core/linter.py +166 -0
- autocoder/common/linter_core/linter_factory.py +216 -0
- autocoder/common/linter_core/linter_manager.py +333 -0
- autocoder/common/linter_core/linters/__init__.py +9 -0
- autocoder/common/linter_core/linters/java_linter.py +342 -0
- autocoder/common/linter_core/linters/python_linter.py +115 -0
- autocoder/common/linter_core/linters/typescript_linter.py +119 -0
- autocoder/common/linter_core/models/__init__.py +7 -0
- autocoder/common/linter_core/models/lint_result.py +91 -0
- autocoder/common/linter_core/models.py +33 -0
- autocoder/common/linter_core/tests/__init__.py +3 -0
- autocoder/common/linter_core/tests/test_config_loader.py +323 -0
- autocoder/common/linter_core/tests/test_config_loading.py +308 -0
- autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
- autocoder/common/linter_core/tests/test_formatters.py +147 -0
- autocoder/common/linter_core/tests/test_integration.py +317 -0
- autocoder/common/linter_core/tests/test_java_linter.py +496 -0
- autocoder/common/linter_core/tests/test_linters.py +265 -0
- autocoder/common/linter_core/tests/test_models.py +81 -0
- autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
- autocoder/common/linter_core/tests/verify_fixes.py +183 -0
- autocoder/common/llm_friendly_package/__init__.py +31 -0
- autocoder/common/llm_friendly_package/base_manager.py +102 -0
- autocoder/common/llm_friendly_package/docs_manager.py +121 -0
- autocoder/common/llm_friendly_package/library_manager.py +171 -0
- autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
- autocoder/common/llm_friendly_package/models.py +40 -0
- autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
- autocoder/common/llms/__init__.py +15 -0
- autocoder/common/llms/demo_error_handling.py +85 -0
- autocoder/common/llms/factory.py +142 -0
- autocoder/common/llms/manager.py +264 -0
- autocoder/common/llms/pricing.py +121 -0
- autocoder/common/llms/registry.py +316 -0
- autocoder/common/llms/schema.py +77 -0
- autocoder/common/llms/simple_demo.py +45 -0
- autocoder/common/llms/test_quick_model.py +116 -0
- autocoder/common/llms/test_remove_functionality.py +182 -0
- autocoder/common/llms/tests/__init__.py +1 -0
- autocoder/common/llms/tests/test_manager.py +330 -0
- autocoder/common/llms/tests/test_registry.py +364 -0
- autocoder/common/mcp_tools/__init__.py +62 -0
- autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
- autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
- autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
- autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
- autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
- autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
- autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
- autocoder/common/mcp_tools/verify_functionality.py +202 -0
- autocoder/common/model_speed_tester.py +32 -26
- autocoder/common/priority_directory_finder/__init__.py +142 -0
- autocoder/common/priority_directory_finder/examples.py +230 -0
- autocoder/common/priority_directory_finder/finder.py +283 -0
- autocoder/common/priority_directory_finder/models.py +236 -0
- autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
- autocoder/common/project_scanner/__init__.py +18 -0
- autocoder/common/project_scanner/compat.py +77 -0
- autocoder/common/project_scanner/scanner.py +436 -0
- autocoder/common/project_tracker/__init__.py +27 -0
- autocoder/common/project_tracker/api.py +228 -0
- autocoder/common/project_tracker/demo.py +272 -0
- autocoder/common/project_tracker/tracker.py +487 -0
- autocoder/common/project_tracker/types.py +53 -0
- autocoder/common/pruner/__init__.py +67 -0
- autocoder/common/pruner/agentic_conversation_pruner.py +651 -102
- autocoder/common/pruner/conversation_message_ids_api.py +386 -0
- autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
- autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
- autocoder/common/pruner/conversation_normalizer.py +347 -0
- autocoder/common/pruner/conversation_pruner.py +26 -6
- autocoder/common/pruner/test_agentic_conversation_pruner.py +554 -112
- autocoder/common/pruner/test_conversation_normalizer.py +502 -0
- autocoder/common/pruner/test_tool_content_detector.py +324 -0
- autocoder/common/pruner/tool_content_detector.py +227 -0
- autocoder/common/pruner/tools/__init__.py +18 -0
- autocoder/common/pruner/tools/query_message_ids.py +264 -0
- autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
- autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
- autocoder/common/pull_requests/__init__.py +9 -1
- autocoder/common/pull_requests/utils.py +122 -1
- autocoder/common/rag_manager/rag_manager.py +36 -40
- autocoder/common/rulefiles/__init__.py +53 -1
- autocoder/common/rulefiles/api.py +250 -0
- autocoder/common/rulefiles/core/__init__.py +14 -0
- autocoder/common/rulefiles/core/manager.py +241 -0
- autocoder/common/rulefiles/core/selector.py +805 -0
- autocoder/common/rulefiles/models/__init__.py +20 -0
- autocoder/common/rulefiles/models/index.py +16 -0
- autocoder/common/rulefiles/models/init_rule.py +18 -0
- autocoder/common/rulefiles/models/rule_file.py +18 -0
- autocoder/common/rulefiles/models/rule_relevance.py +14 -0
- autocoder/common/rulefiles/models/summary.py +16 -0
- autocoder/common/rulefiles/test_rulefiles.py +776 -0
- autocoder/common/rulefiles/utils/__init__.py +34 -0
- autocoder/common/rulefiles/utils/monitor.py +86 -0
- autocoder/common/rulefiles/utils/parser.py +230 -0
- autocoder/common/save_formatted_log.py +67 -10
- autocoder/common/search_replace.py +8 -1
- autocoder/common/search_replace_patch/__init__.py +24 -0
- autocoder/common/search_replace_patch/base.py +115 -0
- autocoder/common/search_replace_patch/manager.py +248 -0
- autocoder/common/search_replace_patch/patch_replacer.py +304 -0
- autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
- autocoder/common/search_replace_patch/string_replacer.py +181 -0
- autocoder/common/search_replace_patch/tests/__init__.py +3 -0
- autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
- autocoder/common/search_replace_patch/tests/test_base.py +188 -0
- autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
- autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
- autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
- autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
- autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
- autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
- autocoder/common/shell_commands/__init__.py +197 -0
- autocoder/common/shell_commands/background_process_notifier.py +346 -0
- autocoder/common/shell_commands/command_executor.py +1127 -0
- autocoder/common/shell_commands/error_recovery.py +541 -0
- autocoder/common/shell_commands/exceptions.py +120 -0
- autocoder/common/shell_commands/interactive_executor.py +476 -0
- autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
- autocoder/common/shell_commands/interactive_process.py +744 -0
- autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
- autocoder/common/shell_commands/monitoring.py +529 -0
- autocoder/common/shell_commands/process_cleanup.py +386 -0
- autocoder/common/shell_commands/process_manager.py +606 -0
- autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
- autocoder/common/shell_commands/tests/__init__.py +6 -0
- autocoder/common/shell_commands/tests/conftest.py +118 -0
- autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
- autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
- autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
- autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
- autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
- autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
- autocoder/common/shell_commands/tests/test_integration.py +664 -0
- autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
- autocoder/common/shell_commands/tests/test_performance.py +632 -0
- autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
- autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
- autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
- autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
- autocoder/common/shell_commands/timeout_config.py +315 -0
- autocoder/common/shell_commands/timeout_manager.py +352 -0
- autocoder/common/terminal_paste/__init__.py +14 -0
- autocoder/common/terminal_paste/demo.py +145 -0
- autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
- autocoder/common/terminal_paste/paste_handler.py +200 -0
- autocoder/common/terminal_paste/paste_manager.py +118 -0
- autocoder/common/terminal_paste/tests/__init__.py +1 -0
- autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
- autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
- autocoder/common/terminal_paste/utils.py +163 -0
- autocoder/common/test_autocoder_args.py +232 -0
- autocoder/common/test_env_manager.py +173 -0
- autocoder/common/test_env_manager_integration.py +159 -0
- autocoder/common/text_similarity/__init__.py +9 -0
- autocoder/common/text_similarity/demo.py +216 -0
- autocoder/common/text_similarity/examples.py +266 -0
- autocoder/common/text_similarity/test_text_similarity.py +306 -0
- autocoder/common/text_similarity/text_similarity.py +194 -0
- autocoder/common/text_similarity/utils.py +125 -0
- autocoder/common/todos/__init__.py +61 -0
- autocoder/common/todos/cache/__init__.py +16 -0
- autocoder/common/todos/cache/base_cache.py +89 -0
- autocoder/common/todos/cache/cache_manager.py +228 -0
- autocoder/common/todos/cache/memory_cache.py +225 -0
- autocoder/common/todos/config.py +155 -0
- autocoder/common/todos/exceptions.py +35 -0
- autocoder/common/todos/get_todo_manager.py +161 -0
- autocoder/common/todos/manager.py +537 -0
- autocoder/common/todos/models.py +239 -0
- autocoder/common/todos/storage/__init__.py +14 -0
- autocoder/common/todos/storage/base_storage.py +76 -0
- autocoder/common/todos/storage/file_storage.py +278 -0
- autocoder/common/tokens/counter.py +24 -2
- autocoder/common/tools_manager/__init__.py +17 -0
- autocoder/common/tools_manager/examples.py +162 -0
- autocoder/common/tools_manager/manager.py +385 -0
- autocoder/common/tools_manager/models.py +39 -0
- autocoder/common/tools_manager/test_tools_manager.py +303 -0
- autocoder/common/tools_manager/utils.py +191 -0
- autocoder/common/v2/agent/agentic_callbacks.py +270 -0
- autocoder/common/v2/agent/agentic_edit.py +2699 -1856
- autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
- autocoder/common/v2/agent/agentic_edit_tools/__init__.py +35 -1
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +10 -1
- autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
- autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
- autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
- autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
- autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +564 -29
- autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
- autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
- autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
- autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
- autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
- autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
- autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +356 -0
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +243 -50
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
- autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +410 -86
- autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
- autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
- autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
- autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +207 -192
- autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +80 -63
- autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +237 -233
- autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
- autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
- autocoder/common/v2/agent/agentic_edit_types.py +343 -9
- autocoder/common/v2/agent/runner/__init__.py +3 -3
- autocoder/common/v2/agent/runner/base_runner.py +12 -26
- autocoder/common/v2/agent/runner/{event_runner.py → file_based_event_runner.py} +3 -2
- autocoder/common/v2/agent/runner/sdk_runner.py +150 -8
- autocoder/common/v2/agent/runner/terminal_runner.py +170 -57
- autocoder/common/v2/agent/runner/tool_display.py +557 -159
- autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
- autocoder/common/v2/agent/test_agentic_edit.py +194 -0
- autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
- autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
- autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
- autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
- autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
- autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
- autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
- autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
- autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
- autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
- autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
- autocoder/common/v2/code_auto_generate.py +136 -78
- autocoder/common/v2/code_auto_generate_diff.py +135 -79
- autocoder/common/v2/code_auto_generate_editblock.py +174 -99
- autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
- autocoder/common/v2/code_auto_merge.py +1 -1
- autocoder/common/v2/code_auto_merge_editblock.py +13 -1
- autocoder/common/v2/code_diff_manager.py +3 -3
- autocoder/common/v2/code_editblock_manager.py +4 -14
- autocoder/common/v2/code_manager.py +1 -1
- autocoder/common/v2/code_strict_diff_manager.py +2 -2
- autocoder/common/wrap_llm_hint/__init__.py +10 -0
- autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
- autocoder/common/wrap_llm_hint/utils.py +432 -0
- autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
- autocoder/completer/__init__.py +8 -0
- autocoder/completer/command_completer_v2.py +1094 -0
- autocoder/default_project/__init__.py +501 -0
- autocoder/dispacher/__init__.py +4 -12
- autocoder/dispacher/actions/action.py +400 -129
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
- autocoder/index/entry.py +117 -125
- autocoder/{agent → index/filter}/agentic_filter.py +322 -333
- autocoder/index/filter/normal_filter.py +5 -11
- autocoder/index/filter/quick_filter.py +1 -1
- autocoder/index/index.py +36 -9
- autocoder/index/tests/__init__.py +1 -0
- autocoder/index/tests/run_tests.py +195 -0
- autocoder/index/tests/test_entry.py +303 -0
- autocoder/index/tests/test_index_manager.py +314 -0
- autocoder/index/tests/test_module_integration.py +300 -0
- autocoder/index/tests/test_symbols_utils.py +183 -0
- autocoder/inner/__init__.py +4 -0
- autocoder/inner/agentic.py +923 -0
- autocoder/inner/async_command_handler.py +992 -0
- autocoder/inner/conversation_command_handlers.py +623 -0
- autocoder/inner/merge_command_handler.py +213 -0
- autocoder/inner/queue_command_handler.py +684 -0
- autocoder/models.py +95 -266
- autocoder/plugins/git_helper_plugin.py +31 -29
- autocoder/plugins/token_helper_plugin.py +65 -46
- autocoder/pyproject/__init__.py +32 -29
- autocoder/rag/agentic_rag.py +215 -75
- autocoder/rag/cache/simple_cache.py +1 -2
- autocoder/rag/loaders/image_loader.py +1 -1
- autocoder/rag/long_context_rag.py +42 -26
- autocoder/rag/qa_conversation_strategy.py +1 -1
- autocoder/rag/terminal/__init__.py +17 -0
- autocoder/rag/terminal/args.py +581 -0
- autocoder/rag/terminal/bootstrap.py +61 -0
- autocoder/rag/terminal/command_handlers.py +653 -0
- autocoder/rag/terminal/formatters/__init__.py +20 -0
- autocoder/rag/terminal/formatters/base.py +70 -0
- autocoder/rag/terminal/formatters/json_format.py +66 -0
- autocoder/rag/terminal/formatters/stream_json.py +95 -0
- autocoder/rag/terminal/formatters/text.py +28 -0
- autocoder/rag/terminal/init.py +120 -0
- autocoder/rag/terminal/utils.py +106 -0
- autocoder/rag/test_agentic_rag.py +389 -0
- autocoder/rag/test_doc_filter.py +3 -3
- autocoder/rag/test_long_context_rag.py +1 -1
- autocoder/rag/test_token_limiter.py +517 -10
- autocoder/rag/token_counter.py +3 -0
- autocoder/rag/token_limiter.py +19 -15
- autocoder/rag/tools/__init__.py +26 -2
- autocoder/rag/tools/bochaai_example.py +343 -0
- autocoder/rag/tools/bochaai_sdk.py +541 -0
- autocoder/rag/tools/metaso_example.py +268 -0
- autocoder/rag/tools/metaso_sdk.py +417 -0
- autocoder/rag/tools/recall_tool.py +28 -7
- autocoder/rag/tools/run_integration_tests.py +204 -0
- autocoder/rag/tools/test_all_providers.py +318 -0
- autocoder/rag/tools/test_bochaai_integration.py +482 -0
- autocoder/rag/tools/test_final_integration.py +215 -0
- autocoder/rag/tools/test_metaso_integration.py +424 -0
- autocoder/rag/tools/test_metaso_real.py +171 -0
- autocoder/rag/tools/test_web_crawl_tool.py +639 -0
- autocoder/rag/tools/test_web_search_tool.py +509 -0
- autocoder/rag/tools/todo_read_tool.py +202 -0
- autocoder/rag/tools/todo_write_tool.py +412 -0
- autocoder/rag/tools/web_crawl_tool.py +634 -0
- autocoder/rag/tools/web_search_tool.py +558 -0
- autocoder/rag/tools/web_tools_example.py +119 -0
- autocoder/rag/types.py +16 -0
- autocoder/rag/variable_holder.py +4 -2
- autocoder/rags.py +86 -79
- autocoder/regexproject/__init__.py +23 -21
- autocoder/sdk/__init__.py +46 -190
- autocoder/sdk/api.py +370 -0
- autocoder/sdk/async_runner/__init__.py +26 -0
- autocoder/sdk/async_runner/async_executor.py +650 -0
- autocoder/sdk/async_runner/async_handler.py +356 -0
- autocoder/sdk/async_runner/markdown_processor.py +595 -0
- autocoder/sdk/async_runner/task_metadata.py +284 -0
- autocoder/sdk/async_runner/worktree_manager.py +438 -0
- autocoder/sdk/cli/__init__.py +2 -5
- autocoder/sdk/cli/formatters.py +28 -204
- autocoder/sdk/cli/handlers.py +77 -44
- autocoder/sdk/cli/main.py +154 -171
- autocoder/sdk/cli/options.py +95 -22
- autocoder/sdk/constants.py +139 -51
- autocoder/sdk/core/auto_coder_core.py +484 -109
- autocoder/sdk/core/bridge.py +297 -115
- autocoder/sdk/exceptions.py +18 -12
- autocoder/sdk/formatters/__init__.py +19 -0
- autocoder/sdk/formatters/input.py +64 -0
- autocoder/sdk/formatters/output.py +247 -0
- autocoder/sdk/formatters/stream.py +54 -0
- autocoder/sdk/models/__init__.py +6 -5
- autocoder/sdk/models/options.py +55 -18
- autocoder/sdk/utils/formatters.py +27 -195
- autocoder/suffixproject/__init__.py +28 -25
- autocoder/terminal/__init__.py +14 -0
- autocoder/terminal/app.py +454 -0
- autocoder/terminal/args.py +32 -0
- autocoder/terminal/bootstrap.py +178 -0
- autocoder/terminal/command_processor.py +521 -0
- autocoder/terminal/command_registry.py +57 -0
- autocoder/terminal/help.py +97 -0
- autocoder/terminal/tasks/__init__.py +5 -0
- autocoder/terminal/tasks/background.py +77 -0
- autocoder/terminal/tasks/task_event.py +70 -0
- autocoder/terminal/ui/__init__.py +13 -0
- autocoder/terminal/ui/completer.py +268 -0
- autocoder/terminal/ui/keybindings.py +75 -0
- autocoder/terminal/ui/session.py +41 -0
- autocoder/terminal/ui/toolbar.py +64 -0
- autocoder/terminal/utils/__init__.py +13 -0
- autocoder/terminal/utils/errors.py +18 -0
- autocoder/terminal/utils/paths.py +19 -0
- autocoder/terminal/utils/shell.py +43 -0
- autocoder/terminal_v3/__init__.py +10 -0
- autocoder/terminal_v3/app.py +201 -0
- autocoder/terminal_v3/handlers/__init__.py +5 -0
- autocoder/terminal_v3/handlers/command_handler.py +131 -0
- autocoder/terminal_v3/models/__init__.py +6 -0
- autocoder/terminal_v3/models/conversation_buffer.py +214 -0
- autocoder/terminal_v3/models/message.py +50 -0
- autocoder/terminal_v3/models/tool_display.py +247 -0
- autocoder/terminal_v3/ui/__init__.py +7 -0
- autocoder/terminal_v3/ui/keybindings.py +56 -0
- autocoder/terminal_v3/ui/layout.py +141 -0
- autocoder/terminal_v3/ui/styles.py +43 -0
- autocoder/tsproject/__init__.py +23 -23
- autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
- autocoder/utils/llms.py +88 -80
- autocoder/utils/math_utils.py +101 -0
- autocoder/utils/model_provider_selector.py +16 -4
- autocoder/utils/operate_config_api.py +33 -5
- autocoder/utils/thread_utils.py +2 -2
- autocoder/version.py +4 -2
- autocoder/workflow_agents/__init__.py +84 -0
- autocoder/workflow_agents/agent.py +143 -0
- autocoder/workflow_agents/exceptions.py +573 -0
- autocoder/workflow_agents/executor.py +665 -0
- autocoder/workflow_agents/loader.py +749 -0
- autocoder/workflow_agents/runner.py +267 -0
- autocoder/workflow_agents/types.py +173 -0
- autocoder/workflow_agents/utils.py +434 -0
- autocoder/workflow_agents/workflow_manager.py +211 -0
- auto_coder-1.0.0.dist-info/METADATA +0 -396
- auto_coder-1.0.0.dist-info/RECORD +0 -442
- auto_coder-1.0.0.dist-info/licenses/LICENSE +0 -201
- autocoder/auto_coder_server.py +0 -672
- autocoder/benchmark.py +0 -138
- autocoder/common/ac_style_command_parser/example.py +0 -7
- autocoder/common/cleaner.py +0 -31
- autocoder/common/command_completer_v2.py +0 -615
- autocoder/common/context_pruner.py +0 -477
- autocoder/common/conversation_pruner.py +0 -132
- autocoder/common/directory_cache/__init__.py +0 -1
- autocoder/common/directory_cache/cache.py +0 -192
- autocoder/common/directory_cache/test_cache.py +0 -190
- autocoder/common/file_checkpoint/examples.py +0 -217
- autocoder/common/llm_friendly_package_example.py +0 -138
- autocoder/common/llm_friendly_package_test.py +0 -63
- autocoder/common/pull_requests/test_module.py +0 -1
- autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
- autocoder/common/text.py +0 -30
- autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
- autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
- autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
- autocoder/common/v2/agent/agentic_tool_display.py +0 -183
- autocoder/plugins/dynamic_completion_example.py +0 -148
- autocoder/plugins/sample_plugin.py +0 -160
- autocoder/sdk/cli/__main__.py +0 -26
- autocoder/sdk/cli/completion_wrapper.py +0 -38
- autocoder/sdk/cli/install_completion.py +0 -301
- autocoder/sdk/models/messages.py +0 -209
- autocoder/sdk/session/__init__.py +0 -32
- autocoder/sdk/session/session.py +0 -106
- autocoder/sdk/session/session_manager.py +0 -56
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.1.dist-info}/top_level.txt +0 -0
- /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Process manager module for shell command execution.
|
|
3
|
+
|
|
4
|
+
This module provides process management functionality including:
|
|
5
|
+
- Process creation with proper configuration
|
|
6
|
+
- Process monitoring and lifecycle management
|
|
7
|
+
- Process group management
|
|
8
|
+
- Integration with timeout and cleanup systems
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import subprocess
|
|
13
|
+
import platform
|
|
14
|
+
import threading
|
|
15
|
+
import time
|
|
16
|
+
from typing import Dict, List, Optional, Tuple, Any, Union
|
|
17
|
+
from loguru import logger
|
|
18
|
+
|
|
19
|
+
from .timeout_config import TimeoutConfig
|
|
20
|
+
from .timeout_manager import TimeoutManager
|
|
21
|
+
from .process_cleanup import cleanup_process_tree, get_process_children
|
|
22
|
+
from .exceptions import CommandExecutionError, ProcessCleanupError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _command_to_string(command: Union[str, List[str]]) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Convert command to string format for timeout configuration lookup.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
command: Command as string or list
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Command as string
|
|
34
|
+
"""
|
|
35
|
+
if isinstance(command, list):
|
|
36
|
+
if not command:
|
|
37
|
+
return ""
|
|
38
|
+
# Join list elements into a single string
|
|
39
|
+
return " ".join(str(arg) for arg in command)
|
|
40
|
+
return command
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ProcessManager:
|
|
44
|
+
"""
|
|
45
|
+
Manager for process creation and lifecycle management.
|
|
46
|
+
|
|
47
|
+
This class handles process creation, monitoring, and cleanup with
|
|
48
|
+
proper timeout management and cross-platform support.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, config: TimeoutConfig):
|
|
52
|
+
"""
|
|
53
|
+
Initialize process manager.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
config: Timeout configuration
|
|
57
|
+
"""
|
|
58
|
+
self.config = config
|
|
59
|
+
self.timeout_manager = TimeoutManager(config)
|
|
60
|
+
self.active_processes: Dict[int, subprocess.Popen] = {}
|
|
61
|
+
self.process_groups: Dict[int, List[int]] = {}
|
|
62
|
+
self.background_processes: Dict[int, Dict[str, Any]] = {}
|
|
63
|
+
self._lock = threading.Lock()
|
|
64
|
+
|
|
65
|
+
logger.debug(f"ProcessManager initialized with config: {config}")
|
|
66
|
+
|
|
67
|
+
def create_process(
|
|
68
|
+
self,
|
|
69
|
+
command: Union[str, List[str]],
|
|
70
|
+
timeout: Optional[float] = None,
|
|
71
|
+
cwd: Optional[str] = None,
|
|
72
|
+
env: Optional[Dict[str, str]] = None,
|
|
73
|
+
shell: bool = True,
|
|
74
|
+
capture_output: bool = True,
|
|
75
|
+
text: bool = True,
|
|
76
|
+
encoding: str = 'utf-8',
|
|
77
|
+
**kwargs
|
|
78
|
+
) -> subprocess.Popen:
|
|
79
|
+
"""
|
|
80
|
+
Create a new process with proper configuration.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
command: Command to execute
|
|
84
|
+
timeout: Timeout in seconds
|
|
85
|
+
cwd: Working directory
|
|
86
|
+
env: Environment variables
|
|
87
|
+
shell: Whether to use shell
|
|
88
|
+
capture_output: Whether to capture stdout/stderr
|
|
89
|
+
text: Whether to use text mode
|
|
90
|
+
encoding: Text encoding
|
|
91
|
+
**kwargs: Additional arguments for subprocess.Popen
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Created subprocess.Popen object
|
|
95
|
+
"""
|
|
96
|
+
try:
|
|
97
|
+
# Determine timeout
|
|
98
|
+
if timeout is None:
|
|
99
|
+
timeout = self.config.get_timeout_for_command(
|
|
100
|
+
_command_to_string(command))
|
|
101
|
+
|
|
102
|
+
# Prepare process arguments
|
|
103
|
+
popen_args = {
|
|
104
|
+
'shell': shell,
|
|
105
|
+
'cwd': cwd,
|
|
106
|
+
'env': env,
|
|
107
|
+
'text': text,
|
|
108
|
+
'encoding': encoding,
|
|
109
|
+
'errors': 'replace',
|
|
110
|
+
**kwargs
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Configure output capturing
|
|
114
|
+
if capture_output:
|
|
115
|
+
popen_args.update({
|
|
116
|
+
'stdout': subprocess.PIPE,
|
|
117
|
+
'stderr': subprocess.STDOUT,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
# Configure process group creation
|
|
121
|
+
if platform.system() == "Windows":
|
|
122
|
+
popen_args['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
|
|
123
|
+
else:
|
|
124
|
+
popen_args['preexec_fn'] = os.setsid
|
|
125
|
+
|
|
126
|
+
logger.debug(f"Creating process for command: {command}")
|
|
127
|
+
|
|
128
|
+
# Create process
|
|
129
|
+
process = subprocess.Popen(command, **popen_args)
|
|
130
|
+
|
|
131
|
+
# Register process
|
|
132
|
+
self._register_process(process)
|
|
133
|
+
|
|
134
|
+
# Note: Timeout will be handled by subprocess.communicate in the executor
|
|
135
|
+
# No need to start separate timeout manager here
|
|
136
|
+
|
|
137
|
+
logger.debug(f"Created process with PID {process.pid}")
|
|
138
|
+
return process
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.error(
|
|
142
|
+
f"Failed to create process for command '{command}': {e}")
|
|
143
|
+
raise CommandExecutionError(f"Failed to create process: {e}")
|
|
144
|
+
|
|
145
|
+
def _register_process(self, process: subprocess.Popen) -> None:
|
|
146
|
+
"""Register a process for management."""
|
|
147
|
+
pid = process.pid
|
|
148
|
+
|
|
149
|
+
with self._lock:
|
|
150
|
+
self.active_processes[pid] = process
|
|
151
|
+
|
|
152
|
+
# Create process group entry
|
|
153
|
+
try:
|
|
154
|
+
if platform.system() != "Windows":
|
|
155
|
+
pgid = os.getpgid(pid)
|
|
156
|
+
if pgid not in self.process_groups:
|
|
157
|
+
self.process_groups[pgid] = []
|
|
158
|
+
self.process_groups[pgid].append(pid)
|
|
159
|
+
else:
|
|
160
|
+
# On Windows, each process is its own group
|
|
161
|
+
self.process_groups[pid] = [pid]
|
|
162
|
+
except OSError:
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
logger.debug(f"Registered process {pid} for management")
|
|
166
|
+
|
|
167
|
+
def unregister_process(self, process: subprocess.Popen) -> None:
|
|
168
|
+
"""Unregister a process from management."""
|
|
169
|
+
pid = process.pid
|
|
170
|
+
|
|
171
|
+
with self._lock:
|
|
172
|
+
# Remove from active processes
|
|
173
|
+
self.active_processes.pop(pid, None)
|
|
174
|
+
|
|
175
|
+
# Remove from background processes if it's a background process
|
|
176
|
+
self.background_processes.pop(pid, None)
|
|
177
|
+
|
|
178
|
+
# Remove from process groups
|
|
179
|
+
for pgid, pids in list(self.process_groups.items()):
|
|
180
|
+
if pid in pids:
|
|
181
|
+
pids.remove(pid)
|
|
182
|
+
if not pids: # Remove empty groups
|
|
183
|
+
del self.process_groups[pgid]
|
|
184
|
+
break
|
|
185
|
+
|
|
186
|
+
# Cancel timeout
|
|
187
|
+
self.timeout_manager.cancel_timeout(process)
|
|
188
|
+
|
|
189
|
+
logger.debug(f"Unregistered process {pid}")
|
|
190
|
+
|
|
191
|
+
def wait_for_process(
|
|
192
|
+
self,
|
|
193
|
+
process: subprocess.Popen,
|
|
194
|
+
timeout: Optional[float] = None
|
|
195
|
+
) -> int:
|
|
196
|
+
"""
|
|
197
|
+
Wait for a process to complete.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
process: Process to wait for
|
|
201
|
+
timeout: Timeout in seconds
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Process exit code
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
if timeout:
|
|
208
|
+
exit_code = process.wait(timeout=timeout)
|
|
209
|
+
else:
|
|
210
|
+
exit_code = process.wait()
|
|
211
|
+
|
|
212
|
+
# Unregister process
|
|
213
|
+
self.unregister_process(process)
|
|
214
|
+
|
|
215
|
+
logger.debug(
|
|
216
|
+
f"Process {process.pid} completed with exit code {exit_code}")
|
|
217
|
+
return exit_code
|
|
218
|
+
|
|
219
|
+
except subprocess.TimeoutExpired:
|
|
220
|
+
logger.warning(f"Process {process.pid} timed out during wait")
|
|
221
|
+
raise
|
|
222
|
+
except Exception as e:
|
|
223
|
+
logger.error(f"Error waiting for process {process.pid}: {e}")
|
|
224
|
+
raise CommandExecutionError(f"Error waiting for process: {e}")
|
|
225
|
+
|
|
226
|
+
def cleanup_process_tree(
|
|
227
|
+
self,
|
|
228
|
+
process: subprocess.Popen,
|
|
229
|
+
timeout: Optional[float] = None
|
|
230
|
+
) -> bool:
|
|
231
|
+
"""
|
|
232
|
+
Clean up a process and its entire tree.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
process: Root process to cleanup
|
|
236
|
+
timeout: Timeout for cleanup
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
True if cleanup successful
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
if process.poll() is not None:
|
|
244
|
+
# 进程已经结束,直接注销
|
|
245
|
+
self.unregister_process(process)
|
|
246
|
+
return True
|
|
247
|
+
except Exception as e:
|
|
248
|
+
logger.error(f"Error checking process status: {e}")
|
|
249
|
+
pass
|
|
250
|
+
|
|
251
|
+
pid = process.pid
|
|
252
|
+
|
|
253
|
+
if timeout is None:
|
|
254
|
+
timeout = self.config.cleanup_timeout
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
logger.debug(f"Cleaning up process tree for PID {pid}")
|
|
258
|
+
|
|
259
|
+
# Cancel timeout first
|
|
260
|
+
self.timeout_manager.cancel_timeout(process)
|
|
261
|
+
|
|
262
|
+
# Use the process cleanup module
|
|
263
|
+
success = cleanup_process_tree(
|
|
264
|
+
pid,
|
|
265
|
+
timeout=self.config.grace_period,
|
|
266
|
+
force_timeout=timeout - self.config.grace_period
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Unregister process
|
|
270
|
+
self.unregister_process(process)
|
|
271
|
+
|
|
272
|
+
if success:
|
|
273
|
+
logger.debug(
|
|
274
|
+
f"Successfully cleaned up process tree for PID {pid}")
|
|
275
|
+
else:
|
|
276
|
+
logger.warning(
|
|
277
|
+
f"Failed to fully cleanup process tree for PID {pid}")
|
|
278
|
+
|
|
279
|
+
return success
|
|
280
|
+
|
|
281
|
+
except Exception as e:
|
|
282
|
+
logger.error(f"Error cleaning up process tree for PID {pid}: {e}")
|
|
283
|
+
return False
|
|
284
|
+
|
|
285
|
+
def get_all_processes(self) -> Dict[int, subprocess.Popen]:
|
|
286
|
+
"""Get all active processes."""
|
|
287
|
+
with self._lock:
|
|
288
|
+
return self.active_processes.copy()
|
|
289
|
+
|
|
290
|
+
def get_process_groups(self) -> Dict[int, List[int]]:
|
|
291
|
+
"""Get process groups mapping."""
|
|
292
|
+
with self._lock:
|
|
293
|
+
return {pgid: pids.copy() for pgid, pids in self.process_groups.items()}
|
|
294
|
+
|
|
295
|
+
def cleanup_all_processes(self, timeout: float = 10.0) -> List[int]:
|
|
296
|
+
"""
|
|
297
|
+
Clean up all managed processes.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
timeout: Timeout for each process cleanup
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
List of process IDs that failed to cleanup
|
|
304
|
+
"""
|
|
305
|
+
failed_pids = []
|
|
306
|
+
|
|
307
|
+
# Get copy of active processes
|
|
308
|
+
with self._lock:
|
|
309
|
+
processes = list(self.active_processes.values())
|
|
310
|
+
background_pids = list(self.background_processes.keys())
|
|
311
|
+
|
|
312
|
+
logger.debug(f"Cleaning up {len(processes)} processes (including {len(background_pids)} background processes)")
|
|
313
|
+
|
|
314
|
+
for process in processes:
|
|
315
|
+
try:
|
|
316
|
+
if not self.cleanup_process_tree(process, timeout):
|
|
317
|
+
failed_pids.append(process.pid)
|
|
318
|
+
except Exception as e:
|
|
319
|
+
logger.error(f"Error cleaning up process {process.pid}: {e}")
|
|
320
|
+
failed_pids.append(process.pid)
|
|
321
|
+
|
|
322
|
+
# Cleanup timeout manager
|
|
323
|
+
self.timeout_manager.cleanup_all_timeouts()
|
|
324
|
+
|
|
325
|
+
# Clear background processes records for successful cleanups
|
|
326
|
+
with self._lock:
|
|
327
|
+
for pid in background_pids:
|
|
328
|
+
if pid not in failed_pids:
|
|
329
|
+
self.background_processes.pop(pid, None)
|
|
330
|
+
|
|
331
|
+
logger.debug(f"Cleanup completed, {len(failed_pids)} processes failed")
|
|
332
|
+
return failed_pids
|
|
333
|
+
|
|
334
|
+
def __del__(self):
|
|
335
|
+
"""Cleanup when manager is destroyed."""
|
|
336
|
+
try:
|
|
337
|
+
self.cleanup_all_processes()
|
|
338
|
+
except Exception:
|
|
339
|
+
pass # Ignore errors during cleanup
|
|
340
|
+
|
|
341
|
+
def create_background_process(
|
|
342
|
+
self,
|
|
343
|
+
command: Union[str, List[str]],
|
|
344
|
+
cwd: Optional[str] = None,
|
|
345
|
+
env: Optional[Dict[str, str]] = None,
|
|
346
|
+
shell: bool = True,
|
|
347
|
+
process_uniq_id: Optional[str] = None,
|
|
348
|
+
**kwargs
|
|
349
|
+
) -> subprocess.Popen:
|
|
350
|
+
"""
|
|
351
|
+
Create a background process that runs independently.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
command: Command to execute
|
|
355
|
+
cwd: Working directory
|
|
356
|
+
env: Environment variables
|
|
357
|
+
shell: Whether to use shell
|
|
358
|
+
**kwargs: Additional arguments for subprocess.Popen
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
Created subprocess.Popen object for background process
|
|
362
|
+
"""
|
|
363
|
+
try:
|
|
364
|
+
# Prepare process arguments for background execution
|
|
365
|
+
popen_args = {
|
|
366
|
+
'shell': shell,
|
|
367
|
+
'cwd': cwd,
|
|
368
|
+
'env': env,
|
|
369
|
+
'stdout': subprocess.PIPE,
|
|
370
|
+
'stderr': subprocess.PIPE,
|
|
371
|
+
'text': True,
|
|
372
|
+
'encoding': 'utf-8',
|
|
373
|
+
'errors': 'replace',
|
|
374
|
+
**kwargs
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
# Configure process group creation for proper cleanup
|
|
378
|
+
if platform.system() == "Windows":
|
|
379
|
+
popen_args['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
|
|
380
|
+
else:
|
|
381
|
+
popen_args['preexec_fn'] = os.setsid
|
|
382
|
+
|
|
383
|
+
logger.debug(f"Creating background process for command: {command}")
|
|
384
|
+
|
|
385
|
+
# Create process
|
|
386
|
+
process = subprocess.Popen(command, **popen_args)
|
|
387
|
+
|
|
388
|
+
# Register as background process
|
|
389
|
+
self._register_background_process(process, command, cwd, process_uniq_id)
|
|
390
|
+
|
|
391
|
+
logger.debug(f"Created background process with PID {process.pid}")
|
|
392
|
+
return process
|
|
393
|
+
|
|
394
|
+
except Exception as e:
|
|
395
|
+
logger.error(
|
|
396
|
+
f"Failed to create background process for command '{command}': {e}")
|
|
397
|
+
raise CommandExecutionError(f"Failed to create background process: {e}")
|
|
398
|
+
|
|
399
|
+
def _register_background_process(
|
|
400
|
+
self,
|
|
401
|
+
process: subprocess.Popen,
|
|
402
|
+
command: Union[str, List[str]],
|
|
403
|
+
cwd: Optional[str],
|
|
404
|
+
process_uniq_id: Optional[str] = None
|
|
405
|
+
) -> None:
|
|
406
|
+
"""Register a background process for tracking."""
|
|
407
|
+
pid = process.pid
|
|
408
|
+
command_str = _command_to_string(command)
|
|
409
|
+
|
|
410
|
+
# Create backgrounds directory relative to target working directory
|
|
411
|
+
base_dir = cwd if isinstance(cwd, str) and len(cwd) > 0 else os.getcwd()
|
|
412
|
+
backgrounds_dir = os.path.join(base_dir, '.auto-coder', 'backgrounds')
|
|
413
|
+
os.makedirs(backgrounds_dir, exist_ok=True)
|
|
414
|
+
|
|
415
|
+
with self._lock:
|
|
416
|
+
# Register in active processes
|
|
417
|
+
self.active_processes[pid] = process
|
|
418
|
+
|
|
419
|
+
# Record background process metadata
|
|
420
|
+
self.background_processes[pid] = {
|
|
421
|
+
'command': command_str,
|
|
422
|
+
'cwd': cwd,
|
|
423
|
+
'start_time': time.time(),
|
|
424
|
+
'process': process,
|
|
425
|
+
'process_uniq_id': process_uniq_id
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
# Create process group entry
|
|
429
|
+
try:
|
|
430
|
+
if platform.system() != "Windows":
|
|
431
|
+
pgid = os.getpgid(pid)
|
|
432
|
+
if pgid not in self.process_groups:
|
|
433
|
+
self.process_groups[pgid] = []
|
|
434
|
+
self.process_groups[pgid].append(pid)
|
|
435
|
+
else:
|
|
436
|
+
# On Windows, each process is its own group
|
|
437
|
+
self.process_groups[pid] = [pid]
|
|
438
|
+
except OSError:
|
|
439
|
+
pass
|
|
440
|
+
|
|
441
|
+
# Start threads to capture stdout and stderr
|
|
442
|
+
self._start_output_capture_threads(process, process_uniq_id or str(pid), backgrounds_dir)
|
|
443
|
+
|
|
444
|
+
logger.debug(f"Registered background process {pid} for command: {command_str}")
|
|
445
|
+
|
|
446
|
+
def _start_output_capture_threads(
|
|
447
|
+
self,
|
|
448
|
+
process: subprocess.Popen,
|
|
449
|
+
process_id: str,
|
|
450
|
+
backgrounds_dir: str
|
|
451
|
+
) -> None:
|
|
452
|
+
"""Start threads to capture stdout and stderr to files."""
|
|
453
|
+
# Start stdout capture thread
|
|
454
|
+
if process.stdout:
|
|
455
|
+
stdout_file = os.path.join(backgrounds_dir, f"{process_id}.out")
|
|
456
|
+
stdout_thread = threading.Thread(
|
|
457
|
+
target=self._capture_output_to_file,
|
|
458
|
+
args=(process.stdout, stdout_file, process_id, "stdout"),
|
|
459
|
+
daemon=True
|
|
460
|
+
)
|
|
461
|
+
stdout_thread.start()
|
|
462
|
+
logger.debug(f"Started stdout capture thread for process {process_id} (PID {process.pid})")
|
|
463
|
+
|
|
464
|
+
# Start stderr capture thread
|
|
465
|
+
if process.stderr:
|
|
466
|
+
stderr_file = os.path.join(backgrounds_dir, f"{process_id}.err")
|
|
467
|
+
stderr_thread = threading.Thread(
|
|
468
|
+
target=self._capture_output_to_file,
|
|
469
|
+
args=(process.stderr, stderr_file, process_id, "stderr"),
|
|
470
|
+
daemon=True
|
|
471
|
+
)
|
|
472
|
+
stderr_thread.start()
|
|
473
|
+
logger.debug(f"Started stderr capture thread for process {process_id} (PID {process.pid})")
|
|
474
|
+
|
|
475
|
+
def _capture_output_to_file(
|
|
476
|
+
self,
|
|
477
|
+
stream,
|
|
478
|
+
filepath: str,
|
|
479
|
+
process_id: str,
|
|
480
|
+
stream_name: str
|
|
481
|
+
) -> None:
|
|
482
|
+
"""Capture output from a stream and write to file.
|
|
483
|
+
|
|
484
|
+
Handles both text and binary streams robustly by encoding text to UTF-8
|
|
485
|
+
before writing to the destination file.
|
|
486
|
+
"""
|
|
487
|
+
try:
|
|
488
|
+
with open(filepath, 'wb') as f:
|
|
489
|
+
while True:
|
|
490
|
+
# Read data from stream
|
|
491
|
+
data = stream.read(4096)
|
|
492
|
+
if not data:
|
|
493
|
+
break
|
|
494
|
+
|
|
495
|
+
# Normalize to bytes (stream may be text when Popen(text=True))
|
|
496
|
+
if isinstance(data, str):
|
|
497
|
+
data = data.encode('utf-8', errors='replace')
|
|
498
|
+
|
|
499
|
+
# Write to file
|
|
500
|
+
f.write(data)
|
|
501
|
+
f.flush()
|
|
502
|
+
|
|
503
|
+
logger.debug(f"Finished capturing {stream_name} for process {process_id}")
|
|
504
|
+
except Exception as e:
|
|
505
|
+
logger.error(f"Error capturing {stream_name} for process {process_id}: {e}")
|
|
506
|
+
finally:
|
|
507
|
+
try:
|
|
508
|
+
stream.close()
|
|
509
|
+
except Exception:
|
|
510
|
+
pass
|
|
511
|
+
|
|
512
|
+
def get_background_processes(self) -> Dict[int, Dict[str, Any]]:
|
|
513
|
+
"""Get all background processes information."""
|
|
514
|
+
with self._lock:
|
|
515
|
+
# Update status for each background process
|
|
516
|
+
updated_processes = {}
|
|
517
|
+
for pid, info in self.background_processes.items():
|
|
518
|
+
process = info['process']
|
|
519
|
+
updated_info = info.copy()
|
|
520
|
+
|
|
521
|
+
# Update process status
|
|
522
|
+
if process.poll() is None:
|
|
523
|
+
updated_info['status'] = 'running'
|
|
524
|
+
updated_info['exit_code'] = None
|
|
525
|
+
else:
|
|
526
|
+
updated_info['status'] = 'completed'
|
|
527
|
+
updated_info['exit_code'] = process.returncode
|
|
528
|
+
updated_info['end_time'] = time.time()
|
|
529
|
+
|
|
530
|
+
updated_processes[pid] = updated_info
|
|
531
|
+
|
|
532
|
+
return updated_processes
|
|
533
|
+
|
|
534
|
+
def is_background_process(self, pid: int) -> bool:
|
|
535
|
+
"""Check if a process is a background process."""
|
|
536
|
+
with self._lock:
|
|
537
|
+
return pid in self.background_processes
|
|
538
|
+
|
|
539
|
+
def get_background_process_info(self, pid: int) -> Optional[Dict[str, Any]]:
|
|
540
|
+
"""Get information about a specific background process."""
|
|
541
|
+
background_processes = self.get_background_processes()
|
|
542
|
+
return background_processes.get(pid)
|
|
543
|
+
|
|
544
|
+
def cleanup_background_process(self, pid: int, timeout: Optional[float] = None) -> bool:
|
|
545
|
+
"""
|
|
546
|
+
Clean up a specific background process.
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
pid: Process ID to cleanup
|
|
550
|
+
timeout: Timeout for cleanup
|
|
551
|
+
|
|
552
|
+
Returns:
|
|
553
|
+
True if cleanup successful
|
|
554
|
+
"""
|
|
555
|
+
with self._lock:
|
|
556
|
+
if pid not in self.background_processes:
|
|
557
|
+
logger.debug(f"Process {pid} is not a background process")
|
|
558
|
+
return True
|
|
559
|
+
|
|
560
|
+
process = self.background_processes[pid]['process']
|
|
561
|
+
|
|
562
|
+
# Use the existing cleanup logic
|
|
563
|
+
success = self.cleanup_process_tree(process, timeout)
|
|
564
|
+
|
|
565
|
+
if success:
|
|
566
|
+
with self._lock:
|
|
567
|
+
# Remove from background processes tracking
|
|
568
|
+
self.background_processes.pop(pid, None)
|
|
569
|
+
|
|
570
|
+
# Optionally clean up output files
|
|
571
|
+
# For now, we keep them for debugging/audit purposes
|
|
572
|
+
# self._cleanup_output_files(pid)
|
|
573
|
+
|
|
574
|
+
return success
|
|
575
|
+
|
|
576
|
+
def _cleanup_output_files(self, pid: int) -> None:
|
|
577
|
+
"""Clean up output files for a background process."""
|
|
578
|
+
# Prefer the background process working directory when available
|
|
579
|
+
with self._lock:
|
|
580
|
+
info = self.background_processes.get(pid)
|
|
581
|
+
if not info:
|
|
582
|
+
return
|
|
583
|
+
|
|
584
|
+
cwd = info.get('cwd')
|
|
585
|
+
process_uniq_id = info.get('process_uniq_id')
|
|
586
|
+
|
|
587
|
+
base_dir = cwd if isinstance(cwd, str) and len(cwd) > 0 else os.getcwd()
|
|
588
|
+
backgrounds_dir = os.path.join(base_dir, '.auto-coder', 'backgrounds')
|
|
589
|
+
|
|
590
|
+
# Use process_uniq_id if available, fallback to pid
|
|
591
|
+
file_prefix = process_uniq_id if process_uniq_id else str(pid)
|
|
592
|
+
stdout_file = os.path.join(backgrounds_dir, f"{file_prefix}.out")
|
|
593
|
+
stderr_file = os.path.join(backgrounds_dir, f"{file_prefix}.err")
|
|
594
|
+
|
|
595
|
+
for filepath in [stdout_file, stderr_file]:
|
|
596
|
+
try:
|
|
597
|
+
if os.path.exists(filepath):
|
|
598
|
+
os.remove(filepath)
|
|
599
|
+
logger.debug(f"Removed output file: {filepath}")
|
|
600
|
+
except Exception as e:
|
|
601
|
+
logger.warning(f"Failed to remove output file {filepath}: {e}")
|
|
602
|
+
|
|
603
|
+
def get_background_process_count(self) -> int:
|
|
604
|
+
"""Get the number of active background processes."""
|
|
605
|
+
with self._lock:
|
|
606
|
+
return len(self.background_processes)
|