auto-coder 1.0.0__py3-none-any.whl → 2.0.0__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.0.dist-info/LICENSE +158 -0
- auto_coder-2.0.0.dist-info/METADATA +558 -0
- auto_coder-2.0.0.dist-info/RECORD +795 -0
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.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 +73 -59
- autocoder/auto_coder.py +31 -40
- autocoder/auto_coder_rag.py +11 -1084
- autocoder/auto_coder_runner.py +970 -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 +401 -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 +288 -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 +349 -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 +1051 -0
- autocoder/default_project/__init__.py +501 -0
- autocoder/dispacher/__init__.py +4 -12
- autocoder/dispacher/actions/action.py +165 -7
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
- autocoder/index/entry.py +116 -124
- 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 +932 -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 +489 -0
- autocoder/workflow_agents/loader.py +737 -0
- autocoder/workflow_agents/runner.py +267 -0
- autocoder/workflow_agents/types.py +172 -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.0.dist-info}/top_level.txt +0 -0
- /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interactive process wrapper using pexpect for shell command execution.
|
|
3
|
+
|
|
4
|
+
This module provides pexpect-based interactive process functionality including:
|
|
5
|
+
- Real-time input/output streaming with pexpect
|
|
6
|
+
- Cross-platform support (Unix-like systems)
|
|
7
|
+
- Signal handling (Ctrl-C, etc.)
|
|
8
|
+
- Thread-safe operations
|
|
9
|
+
- Pattern matching and expect functionality
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
import threading
|
|
15
|
+
import time
|
|
16
|
+
import queue
|
|
17
|
+
import signal
|
|
18
|
+
from typing import Optional, Dict, Any, Generator, Union, List, Tuple
|
|
19
|
+
from loguru import logger
|
|
20
|
+
|
|
21
|
+
from .exceptions import CommandExecutionError, ProcessCleanupError
|
|
22
|
+
from .process_cleanup import cleanup_process_tree
|
|
23
|
+
|
|
24
|
+
# Platform check
|
|
25
|
+
PLATFORM = platform.system()
|
|
26
|
+
PEXPECT_AVAILABLE = False
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
import pexpect
|
|
30
|
+
if PLATFORM != "Windows":
|
|
31
|
+
PEXPECT_AVAILABLE = True
|
|
32
|
+
else:
|
|
33
|
+
# Try to import pexpect for Windows (pexpect-windows)
|
|
34
|
+
try:
|
|
35
|
+
import pexpect.popen_spawn
|
|
36
|
+
PEXPECT_AVAILABLE = True
|
|
37
|
+
except ImportError:
|
|
38
|
+
logger.debug("pexpect not fully available on Windows")
|
|
39
|
+
except ImportError:
|
|
40
|
+
logger.debug("pexpect not available")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class InteractivePexpectProcess:
|
|
44
|
+
"""
|
|
45
|
+
Interactive process wrapper using pexpect for real-time command execution.
|
|
46
|
+
|
|
47
|
+
This class provides a pexpect-based interface for executing commands that
|
|
48
|
+
require interactive input/output, with built-in pattern matching and
|
|
49
|
+
expect functionality.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
command: Union[str, List[str]],
|
|
55
|
+
cwd: Optional[str] = None,
|
|
56
|
+
env: Optional[Dict[str, str]] = None,
|
|
57
|
+
shell: bool = True,
|
|
58
|
+
encoding: str = 'utf-8',
|
|
59
|
+
timeout: Optional[float] = None,
|
|
60
|
+
maxread: int = 2000,
|
|
61
|
+
searchwindowsize: Optional[int] = None,
|
|
62
|
+
**kwargs
|
|
63
|
+
):
|
|
64
|
+
"""
|
|
65
|
+
Initialize interactive pexpect process.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
command: Command to execute
|
|
69
|
+
cwd: Working directory
|
|
70
|
+
env: Environment variables
|
|
71
|
+
shell: Whether to use shell
|
|
72
|
+
encoding: Text encoding
|
|
73
|
+
timeout: Default timeout for expect operations
|
|
74
|
+
maxread: Maximum bytes to read at once
|
|
75
|
+
searchwindowsize: Size of search window for pattern matching
|
|
76
|
+
**kwargs: Additional pexpect arguments
|
|
77
|
+
"""
|
|
78
|
+
self.command = command
|
|
79
|
+
self.cwd = cwd
|
|
80
|
+
self.env = env
|
|
81
|
+
self.encoding = encoding
|
|
82
|
+
self.shell = shell
|
|
83
|
+
self.timeout = timeout
|
|
84
|
+
self.maxread = maxread
|
|
85
|
+
self.searchwindowsize = searchwindowsize
|
|
86
|
+
self.pexpect_kwargs = kwargs
|
|
87
|
+
|
|
88
|
+
# Check availability
|
|
89
|
+
if not PEXPECT_AVAILABLE:
|
|
90
|
+
raise CommandExecutionError("pexpect not available on this platform")
|
|
91
|
+
|
|
92
|
+
# State management
|
|
93
|
+
self.child: Optional[pexpect.spawn] = None
|
|
94
|
+
self.logfile_read = None
|
|
95
|
+
self.logfile_send = None
|
|
96
|
+
|
|
97
|
+
# Threading
|
|
98
|
+
self.output_queue: queue.Queue = queue.Queue()
|
|
99
|
+
self.error_queue: queue.Queue = queue.Queue()
|
|
100
|
+
self.io_thread: Optional[threading.Thread] = None
|
|
101
|
+
self.alive_event = threading.Event()
|
|
102
|
+
self.started_event = threading.Event()
|
|
103
|
+
|
|
104
|
+
# Monitoring
|
|
105
|
+
self.start_time: Optional[float] = None
|
|
106
|
+
self.end_time: Optional[float] = None
|
|
107
|
+
self.bytes_written = 0
|
|
108
|
+
self.bytes_read = 0
|
|
109
|
+
|
|
110
|
+
# Buffer for continuous reading
|
|
111
|
+
self._output_buffer = ""
|
|
112
|
+
self._read_lock = threading.Lock()
|
|
113
|
+
|
|
114
|
+
logger.debug(f"InteractivePexpectProcess initialized: platform={PLATFORM}")
|
|
115
|
+
|
|
116
|
+
def start(self) -> None:
|
|
117
|
+
"""Start the interactive pexpect process."""
|
|
118
|
+
if self.is_alive():
|
|
119
|
+
raise CommandExecutionError("Process is already running")
|
|
120
|
+
|
|
121
|
+
self.start_time = time.time()
|
|
122
|
+
self.alive_event.set()
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
# Prepare command
|
|
126
|
+
if isinstance(self.command, list):
|
|
127
|
+
if self.shell:
|
|
128
|
+
# Join command parts for shell execution
|
|
129
|
+
cmd = ' '.join(self.command)
|
|
130
|
+
else:
|
|
131
|
+
# Use first element as command, rest as args
|
|
132
|
+
cmd = self.command[0]
|
|
133
|
+
args = self.command[1:] if len(self.command) > 1 else []
|
|
134
|
+
else:
|
|
135
|
+
cmd = self.command
|
|
136
|
+
args = []
|
|
137
|
+
|
|
138
|
+
# Create pexpect spawn
|
|
139
|
+
if PLATFORM == "Windows":
|
|
140
|
+
# Use popen_spawn for Windows
|
|
141
|
+
self.child = pexpect.popen_spawn.PopenSpawn(
|
|
142
|
+
cmd,
|
|
143
|
+
timeout=self.timeout,
|
|
144
|
+
maxread=self.maxread,
|
|
145
|
+
searchwindowsize=self.searchwindowsize,
|
|
146
|
+
encoding=self.encoding,
|
|
147
|
+
cwd=self.cwd,
|
|
148
|
+
env=self.env,
|
|
149
|
+
**self.pexpect_kwargs
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
# Use regular spawn for Unix-like systems
|
|
153
|
+
if self.shell:
|
|
154
|
+
# Use shell to execute command
|
|
155
|
+
shell_cmd = os.environ.get("SHELL", "/bin/sh")
|
|
156
|
+
self.child = pexpect.spawn(
|
|
157
|
+
shell_cmd,
|
|
158
|
+
args=["-c", cmd],
|
|
159
|
+
timeout=self.timeout,
|
|
160
|
+
maxread=self.maxread,
|
|
161
|
+
searchwindowsize=self.searchwindowsize,
|
|
162
|
+
encoding=self.encoding,
|
|
163
|
+
cwd=self.cwd,
|
|
164
|
+
env=self.env,
|
|
165
|
+
**self.pexpect_kwargs
|
|
166
|
+
)
|
|
167
|
+
else:
|
|
168
|
+
# Direct command execution
|
|
169
|
+
self.child = pexpect.spawn(
|
|
170
|
+
cmd,
|
|
171
|
+
args=args,
|
|
172
|
+
timeout=self.timeout,
|
|
173
|
+
maxread=self.maxread,
|
|
174
|
+
searchwindowsize=self.searchwindowsize,
|
|
175
|
+
encoding=self.encoding,
|
|
176
|
+
cwd=self.cwd,
|
|
177
|
+
env=self.env,
|
|
178
|
+
**self.pexpect_kwargs
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Configure logging if needed
|
|
182
|
+
if hasattr(self, 'logfile_read') and self.logfile_read:
|
|
183
|
+
self.child.logfile_read = self.logfile_read
|
|
184
|
+
if hasattr(self, 'logfile_send') and self.logfile_send:
|
|
185
|
+
self.child.logfile_send = self.logfile_send
|
|
186
|
+
|
|
187
|
+
# Start I/O thread for continuous reading
|
|
188
|
+
self.io_thread = threading.Thread(target=self._io_worker, daemon=True)
|
|
189
|
+
self.io_thread.start()
|
|
190
|
+
|
|
191
|
+
# Give process a moment to start
|
|
192
|
+
time.sleep(0.1)
|
|
193
|
+
|
|
194
|
+
# Check if process is alive
|
|
195
|
+
if not self.is_alive():
|
|
196
|
+
self._cleanup()
|
|
197
|
+
raise CommandExecutionError("Pexpect process failed to start")
|
|
198
|
+
|
|
199
|
+
self.started_event.set()
|
|
200
|
+
logger.info(f"Interactive pexpect process started: PID {self.pid}")
|
|
201
|
+
|
|
202
|
+
except Exception as e:
|
|
203
|
+
self.alive_event.clear()
|
|
204
|
+
self._cleanup()
|
|
205
|
+
raise CommandExecutionError(f"Failed to start pexpect process: {e}")
|
|
206
|
+
|
|
207
|
+
def _io_worker(self) -> None:
|
|
208
|
+
"""I/O worker thread for continuous reading."""
|
|
209
|
+
try:
|
|
210
|
+
while self.alive_event.is_set() and self.child and self.child.isalive():
|
|
211
|
+
try:
|
|
212
|
+
# Read data with timeout
|
|
213
|
+
data = self.child.read_nonblocking(size=1024, timeout=0.1)
|
|
214
|
+
if data:
|
|
215
|
+
with self._read_lock:
|
|
216
|
+
self._output_buffer += data
|
|
217
|
+
self.bytes_read += len(data.encode(self.encoding))
|
|
218
|
+
self.output_queue.put(data)
|
|
219
|
+
|
|
220
|
+
except pexpect.TIMEOUT:
|
|
221
|
+
# No data available, continue
|
|
222
|
+
continue
|
|
223
|
+
except pexpect.EOF:
|
|
224
|
+
# Process terminated
|
|
225
|
+
self.alive_event.clear()
|
|
226
|
+
break
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.debug(f"I/O worker error: {e}")
|
|
229
|
+
break
|
|
230
|
+
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logger.error(f"I/O worker fatal error: {e}")
|
|
233
|
+
finally:
|
|
234
|
+
self.alive_event.clear()
|
|
235
|
+
|
|
236
|
+
def write(self, data: str) -> None:
|
|
237
|
+
"""
|
|
238
|
+
Write data to process stdin.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
data: Data to write
|
|
242
|
+
|
|
243
|
+
Raises:
|
|
244
|
+
CommandExecutionError: If process is not running or write fails
|
|
245
|
+
"""
|
|
246
|
+
if not self.is_alive():
|
|
247
|
+
raise CommandExecutionError("Process is not running")
|
|
248
|
+
|
|
249
|
+
if not self.started_event.is_set():
|
|
250
|
+
raise CommandExecutionError("Process has not started yet")
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
if self.child:
|
|
254
|
+
self.child.send(data)
|
|
255
|
+
self.bytes_written += len(data.encode(self.encoding))
|
|
256
|
+
else:
|
|
257
|
+
raise CommandExecutionError("No pexpect child process available")
|
|
258
|
+
|
|
259
|
+
except Exception as e:
|
|
260
|
+
raise CommandExecutionError(f"Failed to write to process: {e}")
|
|
261
|
+
|
|
262
|
+
def sendline(self, line: str = "") -> None:
|
|
263
|
+
"""
|
|
264
|
+
Send a line to the process (adds newline).
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
line: Line to send
|
|
268
|
+
"""
|
|
269
|
+
if self.child:
|
|
270
|
+
self.child.sendline(line)
|
|
271
|
+
self.bytes_written += len(line.encode(self.encoding)) + 1 # +1 for newline
|
|
272
|
+
|
|
273
|
+
def sendcontrol(self, char: str) -> None:
|
|
274
|
+
"""
|
|
275
|
+
Send a control character to the process.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
char: Control character (e.g., 'c' for Ctrl-C)
|
|
279
|
+
"""
|
|
280
|
+
if self.child:
|
|
281
|
+
self.child.sendcontrol(char)
|
|
282
|
+
self.bytes_written += 1
|
|
283
|
+
|
|
284
|
+
def read_output(self, timeout: Optional[float] = None) -> Optional[str]:
|
|
285
|
+
"""
|
|
286
|
+
Read output from process.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
timeout: Timeout in seconds (None for non-blocking)
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Output string or None if no data available
|
|
293
|
+
"""
|
|
294
|
+
try:
|
|
295
|
+
if timeout is None:
|
|
296
|
+
return self.output_queue.get_nowait()
|
|
297
|
+
else:
|
|
298
|
+
return self.output_queue.get(timeout=timeout)
|
|
299
|
+
except queue.Empty:
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
def read_error(self, timeout: Optional[float] = None) -> Optional[str]:
|
|
303
|
+
"""
|
|
304
|
+
Read error output from process.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
timeout: Timeout in seconds (None for non-blocking)
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Error string or None if no data available
|
|
311
|
+
"""
|
|
312
|
+
try:
|
|
313
|
+
if timeout is None:
|
|
314
|
+
return self.error_queue.get_nowait()
|
|
315
|
+
else:
|
|
316
|
+
return self.error_queue.get(timeout=timeout)
|
|
317
|
+
except queue.Empty:
|
|
318
|
+
return None
|
|
319
|
+
|
|
320
|
+
def expect(self, pattern: Union[str, List[str]], timeout: Optional[float] = None) -> int:
|
|
321
|
+
"""
|
|
322
|
+
Wait for a pattern to appear in the output.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
pattern: Pattern(s) to match
|
|
326
|
+
timeout: Timeout in seconds
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Index of matched pattern (0 if single pattern)
|
|
330
|
+
|
|
331
|
+
Raises:
|
|
332
|
+
CommandExecutionError: If expect fails
|
|
333
|
+
"""
|
|
334
|
+
if not self.child:
|
|
335
|
+
raise CommandExecutionError("No pexpect child process available")
|
|
336
|
+
|
|
337
|
+
try:
|
|
338
|
+
if timeout is None:
|
|
339
|
+
timeout = self.timeout
|
|
340
|
+
|
|
341
|
+
result = self.child.expect(pattern, timeout=timeout)
|
|
342
|
+
|
|
343
|
+
# Add matched text to output queue
|
|
344
|
+
if self.child.after:
|
|
345
|
+
self.output_queue.put(self.child.after)
|
|
346
|
+
|
|
347
|
+
return result
|
|
348
|
+
|
|
349
|
+
except pexpect.TIMEOUT as e:
|
|
350
|
+
raise CommandExecutionError(f"Expect timeout: {e}")
|
|
351
|
+
except pexpect.EOF as e:
|
|
352
|
+
raise CommandExecutionError(f"Expect EOF: {e}")
|
|
353
|
+
except Exception as e:
|
|
354
|
+
raise CommandExecutionError(f"Expect error: {e}")
|
|
355
|
+
|
|
356
|
+
def expect_exact(self, pattern: Union[str, List[str]], timeout: Optional[float] = None) -> int:
|
|
357
|
+
"""
|
|
358
|
+
Wait for exact string(s) to appear in the output.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
pattern: Exact string(s) to match
|
|
362
|
+
timeout: Timeout in seconds
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
Index of matched pattern (0 if single pattern)
|
|
366
|
+
"""
|
|
367
|
+
if not self.child:
|
|
368
|
+
raise CommandExecutionError("No pexpect child process available")
|
|
369
|
+
|
|
370
|
+
try:
|
|
371
|
+
if timeout is None:
|
|
372
|
+
timeout = self.timeout
|
|
373
|
+
|
|
374
|
+
result = self.child.expect_exact(pattern, timeout=timeout)
|
|
375
|
+
|
|
376
|
+
# Add matched text to output queue
|
|
377
|
+
if self.child.after:
|
|
378
|
+
self.output_queue.put(self.child.after)
|
|
379
|
+
|
|
380
|
+
return result
|
|
381
|
+
|
|
382
|
+
except pexpect.TIMEOUT as e:
|
|
383
|
+
raise CommandExecutionError(f"Expect exact timeout: {e}")
|
|
384
|
+
except pexpect.EOF as e:
|
|
385
|
+
raise CommandExecutionError(f"Expect exact EOF: {e}")
|
|
386
|
+
except Exception as e:
|
|
387
|
+
raise CommandExecutionError(f"Expect exact error: {e}")
|
|
388
|
+
|
|
389
|
+
def read_lines(self, timeout: Optional[float] = 1.0) -> Generator[str, None, None]:
|
|
390
|
+
"""
|
|
391
|
+
Generator that yields output lines.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
timeout: Timeout for each read operation
|
|
395
|
+
|
|
396
|
+
Yields:
|
|
397
|
+
Output lines
|
|
398
|
+
"""
|
|
399
|
+
buffer = ""
|
|
400
|
+
|
|
401
|
+
while self.is_alive():
|
|
402
|
+
try:
|
|
403
|
+
data = self.read_output(timeout=timeout)
|
|
404
|
+
if data:
|
|
405
|
+
buffer += data
|
|
406
|
+
|
|
407
|
+
# Yield complete lines
|
|
408
|
+
while '\n' in buffer:
|
|
409
|
+
line, buffer = buffer.split('\n', 1)
|
|
410
|
+
yield line + '\n'
|
|
411
|
+
else:
|
|
412
|
+
# No data available, check if process is still alive
|
|
413
|
+
if not self.is_alive():
|
|
414
|
+
break
|
|
415
|
+
|
|
416
|
+
except Exception as e:
|
|
417
|
+
logger.debug(f"Error reading lines: {e}")
|
|
418
|
+
break
|
|
419
|
+
|
|
420
|
+
# Yield any remaining buffer content
|
|
421
|
+
if buffer:
|
|
422
|
+
yield buffer
|
|
423
|
+
|
|
424
|
+
def interact(self, escape_character: str = chr(29), input_filter=None, output_filter=None) -> None:
|
|
425
|
+
"""
|
|
426
|
+
Give control of the child process to the user.
|
|
427
|
+
|
|
428
|
+
Args:
|
|
429
|
+
escape_character: Character to exit interaction
|
|
430
|
+
input_filter: Filter for input
|
|
431
|
+
output_filter: Filter for output
|
|
432
|
+
"""
|
|
433
|
+
if not self.child:
|
|
434
|
+
raise CommandExecutionError("No pexpect child process available")
|
|
435
|
+
|
|
436
|
+
try:
|
|
437
|
+
self.child.interact(
|
|
438
|
+
escape_character=escape_character,
|
|
439
|
+
input_filter=input_filter,
|
|
440
|
+
output_filter=output_filter
|
|
441
|
+
)
|
|
442
|
+
except Exception as e:
|
|
443
|
+
raise CommandExecutionError(f"Interaction error: {e}")
|
|
444
|
+
|
|
445
|
+
def send_signal(self, sig: int) -> None:
|
|
446
|
+
"""
|
|
447
|
+
Send signal to process.
|
|
448
|
+
|
|
449
|
+
Args:
|
|
450
|
+
sig: Signal number
|
|
451
|
+
"""
|
|
452
|
+
if not self.is_alive():
|
|
453
|
+
return
|
|
454
|
+
|
|
455
|
+
try:
|
|
456
|
+
if self.child:
|
|
457
|
+
if PLATFORM == "Windows":
|
|
458
|
+
# Windows signal handling
|
|
459
|
+
if sig == signal.SIGINT:
|
|
460
|
+
self.child.sendcontrol('c')
|
|
461
|
+
else:
|
|
462
|
+
self.child.terminate()
|
|
463
|
+
else:
|
|
464
|
+
# Unix signal handling
|
|
465
|
+
self.child.kill(sig)
|
|
466
|
+
|
|
467
|
+
except Exception as e:
|
|
468
|
+
logger.debug(f"Error sending signal {sig}: {e}")
|
|
469
|
+
|
|
470
|
+
def terminate(self, grace_timeout: float = 5.0) -> bool:
|
|
471
|
+
"""
|
|
472
|
+
Terminate the process gracefully.
|
|
473
|
+
|
|
474
|
+
Args:
|
|
475
|
+
grace_timeout: Time to wait for graceful termination
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
True if terminated successfully
|
|
479
|
+
"""
|
|
480
|
+
if not self.is_alive():
|
|
481
|
+
return True
|
|
482
|
+
|
|
483
|
+
logger.debug(f"Terminating pexpect process PID {self.pid}")
|
|
484
|
+
|
|
485
|
+
try:
|
|
486
|
+
# Send SIGTERM (or equivalent)
|
|
487
|
+
if self.child:
|
|
488
|
+
self.child.terminate()
|
|
489
|
+
|
|
490
|
+
# Wait for graceful termination
|
|
491
|
+
start_time = time.time()
|
|
492
|
+
while time.time() - start_time < grace_timeout:
|
|
493
|
+
if not self.is_alive():
|
|
494
|
+
break
|
|
495
|
+
time.sleep(0.1)
|
|
496
|
+
|
|
497
|
+
# Force kill if still alive
|
|
498
|
+
if self.is_alive() and self.child:
|
|
499
|
+
logger.debug(f"Force killing pexpect process PID {self.pid}")
|
|
500
|
+
self.child.kill(signal.SIGKILL)
|
|
501
|
+
|
|
502
|
+
# Wait for I/O thread to finish
|
|
503
|
+
self.alive_event.clear()
|
|
504
|
+
if self.io_thread and self.io_thread.is_alive():
|
|
505
|
+
self.io_thread.join(timeout=2.0)
|
|
506
|
+
|
|
507
|
+
self._cleanup()
|
|
508
|
+
self.end_time = time.time()
|
|
509
|
+
|
|
510
|
+
return not self.is_alive()
|
|
511
|
+
|
|
512
|
+
except Exception as e:
|
|
513
|
+
logger.error(f"Error terminating pexpect process: {e}")
|
|
514
|
+
return False
|
|
515
|
+
|
|
516
|
+
def _cleanup(self) -> None:
|
|
517
|
+
"""Clean up resources."""
|
|
518
|
+
try:
|
|
519
|
+
if self.child:
|
|
520
|
+
try:
|
|
521
|
+
if self.child.isalive():
|
|
522
|
+
self.child.terminate(force=True)
|
|
523
|
+
except:
|
|
524
|
+
pass
|
|
525
|
+
|
|
526
|
+
# Close file descriptors
|
|
527
|
+
try:
|
|
528
|
+
self.child.close()
|
|
529
|
+
except:
|
|
530
|
+
pass
|
|
531
|
+
|
|
532
|
+
self.child = None
|
|
533
|
+
|
|
534
|
+
except Exception as e:
|
|
535
|
+
logger.debug(f"Cleanup error: {e}")
|
|
536
|
+
|
|
537
|
+
def is_alive(self) -> bool:
|
|
538
|
+
"""Check if process is alive."""
|
|
539
|
+
if self.child:
|
|
540
|
+
return self.child.isalive()
|
|
541
|
+
return False
|
|
542
|
+
|
|
543
|
+
@property
|
|
544
|
+
def pid(self) -> Optional[int]:
|
|
545
|
+
"""Get process PID."""
|
|
546
|
+
if self.child:
|
|
547
|
+
return self.child.pid
|
|
548
|
+
return None
|
|
549
|
+
|
|
550
|
+
@property
|
|
551
|
+
def exit_code(self) -> Optional[int]:
|
|
552
|
+
"""Get process exit code."""
|
|
553
|
+
if self.child:
|
|
554
|
+
return self.child.exitstatus
|
|
555
|
+
return None
|
|
556
|
+
|
|
557
|
+
@property
|
|
558
|
+
def signal_status(self) -> Optional[int]:
|
|
559
|
+
"""Get process signal status."""
|
|
560
|
+
if self.child:
|
|
561
|
+
return self.child.signalstatus
|
|
562
|
+
return None
|
|
563
|
+
|
|
564
|
+
@property
|
|
565
|
+
def duration(self) -> Optional[float]:
|
|
566
|
+
"""Get process duration."""
|
|
567
|
+
if self.start_time is None:
|
|
568
|
+
return None
|
|
569
|
+
|
|
570
|
+
end_time = self.end_time or time.time()
|
|
571
|
+
return end_time - self.start_time
|
|
572
|
+
|
|
573
|
+
@property
|
|
574
|
+
def before(self) -> Optional[str]:
|
|
575
|
+
"""Get text before the last match."""
|
|
576
|
+
if self.child:
|
|
577
|
+
return self.child.before
|
|
578
|
+
return None
|
|
579
|
+
|
|
580
|
+
@property
|
|
581
|
+
def after(self) -> Optional[str]:
|
|
582
|
+
"""Get text after the last match."""
|
|
583
|
+
if self.child:
|
|
584
|
+
return self.child.after
|
|
585
|
+
return None
|
|
586
|
+
|
|
587
|
+
@property
|
|
588
|
+
def match(self) -> Optional[str]:
|
|
589
|
+
"""Get the last match object."""
|
|
590
|
+
if self.child:
|
|
591
|
+
return self.child.match
|
|
592
|
+
return None
|
|
593
|
+
|
|
594
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
595
|
+
"""Get process statistics."""
|
|
596
|
+
return {
|
|
597
|
+
'pid': self.pid,
|
|
598
|
+
'exit_code': self.exit_code,
|
|
599
|
+
'signal_status': self.signal_status,
|
|
600
|
+
'duration': self.duration,
|
|
601
|
+
'bytes_written': self.bytes_written,
|
|
602
|
+
'bytes_read': self.bytes_read,
|
|
603
|
+
'is_alive': self.is_alive(),
|
|
604
|
+
'platform': PLATFORM,
|
|
605
|
+
'pexpect_available': PEXPECT_AVAILABLE
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
def __enter__(self) -> 'InteractivePexpectProcess':
|
|
609
|
+
"""Context manager entry."""
|
|
610
|
+
self.start()
|
|
611
|
+
return self
|
|
612
|
+
|
|
613
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
614
|
+
"""Context manager exit."""
|
|
615
|
+
self.terminate()
|
|
616
|
+
|
|
617
|
+
def __del__(self) -> None:
|
|
618
|
+
"""Destructor - cleanup resources."""
|
|
619
|
+
try:
|
|
620
|
+
if self.is_alive():
|
|
621
|
+
self.terminate(grace_timeout=1.0)
|
|
622
|
+
except Exception:
|
|
623
|
+
pass
|