code-muse 0.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.
- code_muse/__init__.py +26 -0
- code_muse/__main__.py +10 -0
- code_muse/agents/__init__.py +31 -0
- code_muse/agents/_builder.py +214 -0
- code_muse/agents/_compaction.py +506 -0
- code_muse/agents/_diagnostics.py +171 -0
- code_muse/agents/_history.py +382 -0
- code_muse/agents/_key_listeners.py +148 -0
- code_muse/agents/_non_streaming_render.py +148 -0
- code_muse/agents/_runtime.py +596 -0
- code_muse/agents/agent_creator_agent.py +603 -0
- code_muse/agents/agent_helios.py +47 -0
- code_muse/agents/agent_manager.py +740 -0
- code_muse/agents/agent_muse.py +78 -0
- code_muse/agents/agent_planning.py +44 -0
- code_muse/agents/agent_qa_melpomene.py +207 -0
- code_muse/agents/base_agent.py +194 -0
- code_muse/agents/event_stream_handler.py +361 -0
- code_muse/agents/json_agent.py +201 -0
- code_muse/agents/prompt_v3.py +521 -0
- code_muse/agents/subagent_stream_handler.py +273 -0
- code_muse/callbacks.py +941 -0
- code_muse/chatgpt_codex_client.py +333 -0
- code_muse/claude_cache_client.py +853 -0
- code_muse/cli_runner/__init__.py +319 -0
- code_muse/cli_runner/args.py +63 -0
- code_muse/cli_runner/loop.py +510 -0
- code_muse/cli_runner/resume.py +72 -0
- code_muse/cli_runner/runner.py +161 -0
- code_muse/command_line/__init__.py +1 -0
- code_muse/command_line/add_model_menu.py +1331 -0
- code_muse/command_line/agent_menu.py +674 -0
- code_muse/command_line/attachments.py +397 -0
- code_muse/command_line/autosave_menu.py +709 -0
- code_muse/command_line/clipboard.py +528 -0
- code_muse/command_line/colors_menu.py +530 -0
- code_muse/command_line/command_handler.py +262 -0
- code_muse/command_line/command_registry.py +150 -0
- code_muse/command_line/config_commands.py +711 -0
- code_muse/command_line/core_commands.py +740 -0
- code_muse/command_line/diff_menu.py +865 -0
- code_muse/command_line/file_path_completion.py +73 -0
- code_muse/command_line/load_context_completion.py +57 -0
- code_muse/command_line/model_picker_completion.py +512 -0
- code_muse/command_line/model_settings_menu.py +983 -0
- code_muse/command_line/onboarding_slides.py +162 -0
- code_muse/command_line/onboarding_wizard.py +337 -0
- code_muse/command_line/pagination.py +41 -0
- code_muse/command_line/pin_command_completion.py +329 -0
- code_muse/command_line/prompt_toolkit_completion.py +886 -0
- code_muse/command_line/session_commands.py +304 -0
- code_muse/command_line/shell_passthrough.py +145 -0
- code_muse/command_line/skills_completion.py +158 -0
- code_muse/command_line/types.py +18 -0
- code_muse/command_line/uc_menu.py +908 -0
- code_muse/command_line/utils.py +105 -0
- code_muse/command_line/wiggum_state.py +77 -0
- code_muse/config.py +1138 -0
- code_muse/config_agent.py +168 -0
- code_muse/config_appearance.py +241 -0
- code_muse/config_model.py +357 -0
- code_muse/config_security.py +73 -0
- code_muse/error_logging.py +132 -0
- code_muse/evals/__init__.py +35 -0
- code_muse/evals/eval_helpers.py +81 -0
- code_muse/evals/eval_runner.py +299 -0
- code_muse/evals/sample_evals/__init__.py +1 -0
- code_muse/evals/sample_evals/eval_frugal_reads.py +59 -0
- code_muse/evals/sample_evals/eval_memory_planning.py +31 -0
- code_muse/evals/sample_evals/eval_shell_efficiency.py +39 -0
- code_muse/evals/sample_evals/eval_tool_masking.py +33 -0
- code_muse/fs_scan_cache/__init__.py +31 -0
- code_muse/fs_scan_cache/invalidation_hooks.py +89 -0
- code_muse/fs_scan_cache/scan_cache_core.cpython-314-darwin.so +0 -0
- code_muse/fs_scan_cache/scan_cache_core.pyx +203 -0
- code_muse/fs_scan_cache/tool_integration.py +309 -0
- code_muse/fs_scan_cache/ttl_policy.py +44 -0
- code_muse/gemini_code_assist.py +383 -0
- code_muse/gemini_model.py +838 -0
- code_muse/hook_engine/README.md +105 -0
- code_muse/hook_engine/__init__.py +21 -0
- code_muse/hook_engine/aliases.py +153 -0
- code_muse/hook_engine/engine.py +221 -0
- code_muse/hook_engine/executor.py +347 -0
- code_muse/hook_engine/matcher.py +154 -0
- code_muse/hook_engine/models.py +245 -0
- code_muse/hook_engine/registry.py +114 -0
- code_muse/hook_engine/trust.py +268 -0
- code_muse/hook_engine/validator.py +144 -0
- code_muse/http_utils.py +360 -0
- code_muse/keymap.py +128 -0
- code_muse/list_filtering.py +26 -0
- code_muse/main.py +10 -0
- code_muse/messaging/__init__.py +259 -0
- code_muse/messaging/bus.py +621 -0
- code_muse/messaging/commands.py +166 -0
- code_muse/messaging/markdown_patches.py +57 -0
- code_muse/messaging/message_queue.py +397 -0
- code_muse/messaging/messages.py +591 -0
- code_muse/messaging/queue_console.py +269 -0
- code_muse/messaging/renderers.py +308 -0
- code_muse/messaging/rich_renderer.py +1158 -0
- code_muse/messaging/shimmer.py +154 -0
- code_muse/messaging/spinner/__init__.py +87 -0
- code_muse/messaging/spinner/console_spinner.py +250 -0
- code_muse/messaging/spinner/spinner_base.py +82 -0
- code_muse/messaging/subagent_console.py +458 -0
- code_muse/model_factory.py +1203 -0
- code_muse/model_switching.py +59 -0
- code_muse/model_utils.py +156 -0
- code_muse/models.json +66 -0
- code_muse/models_cache/__init__.py +26 -0
- code_muse/models_cache/blocking_lru_cache.py +98 -0
- code_muse/models_cache/cache_writer.py +86 -0
- code_muse/models_cache/sha256_hash.cpython-314-darwin.so +0 -0
- code_muse/models_cache/sha256_hash.pyx +34 -0
- code_muse/models_cache/startup_integration.py +75 -0
- code_muse/models_dev_api.json +1 -0
- code_muse/models_dev_parser.py +590 -0
- code_muse/motion.py +126 -0
- code_muse/plugins/__init__.py +471 -0
- code_muse/plugins/agent_skills/__init__.py +32 -0
- code_muse/plugins/agent_skills/config.py +176 -0
- code_muse/plugins/agent_skills/discovery.py +309 -0
- code_muse/plugins/agent_skills/downloader.py +389 -0
- code_muse/plugins/agent_skills/installer.py +19 -0
- code_muse/plugins/agent_skills/metadata.py +293 -0
- code_muse/plugins/agent_skills/prompt_builder.py +66 -0
- code_muse/plugins/agent_skills/register_callbacks.py +298 -0
- code_muse/plugins/agent_skills/remote_catalog.py +320 -0
- code_muse/plugins/agent_skills/skill_catalog.py +254 -0
- code_muse/plugins/agent_skills/skills_install_menu.py +690 -0
- code_muse/plugins/agent_skills/skills_menu.py +791 -0
- code_muse/plugins/autonomous_memory/__init__.py +39 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-darwin.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.pyx +291 -0
- code_muse/plugins/autonomous_memory/consolidation.py +82 -0
- code_muse/plugins/autonomous_memory/extraction.py +382 -0
- code_muse/plugins/autonomous_memory/lease_lock.py +105 -0
- code_muse/plugins/autonomous_memory/memory_injection.py +59 -0
- code_muse/plugins/autonomous_memory/register_callbacks.py +268 -0
- code_muse/plugins/autonomous_memory/secret_scanner.py +62 -0
- code_muse/plugins/autonomous_memory/session_scanner.py +163 -0
- code_muse/plugins/aws_bedrock/__init__.py +14 -0
- code_muse/plugins/aws_bedrock/config.py +99 -0
- code_muse/plugins/aws_bedrock/register_callbacks.py +241 -0
- code_muse/plugins/aws_bedrock/utils.py +153 -0
- code_muse/plugins/azure_foundry/README.md +238 -0
- code_muse/plugins/azure_foundry/__init__.py +15 -0
- code_muse/plugins/azure_foundry/config.py +125 -0
- code_muse/plugins/azure_foundry/discovery.py +187 -0
- code_muse/plugins/azure_foundry/register_callbacks.py +495 -0
- code_muse/plugins/azure_foundry/token.py +180 -0
- code_muse/plugins/azure_foundry/utils.py +345 -0
- code_muse/plugins/build_filter/__init__.py +1 -0
- code_muse/plugins/build_filter/register_callbacks.py +201 -0
- code_muse/plugins/build_filter/strategies/__init__.py +1 -0
- code_muse/plugins/build_filter/strategies/build.py +397 -0
- code_muse/plugins/chatgpt_oauth/__init__.py +6 -0
- code_muse/plugins/chatgpt_oauth/config.py +52 -0
- code_muse/plugins/chatgpt_oauth/oauth_flow.py +338 -0
- code_muse/plugins/chatgpt_oauth/register_callbacks.py +172 -0
- code_muse/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_muse/plugins/chatgpt_oauth/utils.py +538 -0
- code_muse/plugins/checkpointing/__init__.py +29 -0
- code_muse/plugins/checkpointing/checkpoint_hook.py +51 -0
- code_muse/plugins/checkpointing/conversation_snapshots.py +117 -0
- code_muse/plugins/checkpointing/register_callbacks.py +51 -0
- code_muse/plugins/checkpointing/restore_command.py +263 -0
- code_muse/plugins/checkpointing/rewind_shortcut.py +88 -0
- code_muse/plugins/checkpointing/shadow_git.py +90 -0
- code_muse/plugins/claude_code_hooks/__init__.py +1 -0
- code_muse/plugins/claude_code_hooks/config.py +188 -0
- code_muse/plugins/claude_code_hooks/register_callbacks.py +208 -0
- code_muse/plugins/claude_code_oauth/README.md +167 -0
- code_muse/plugins/claude_code_oauth/SETUP.md +93 -0
- code_muse/plugins/claude_code_oauth/__init__.py +25 -0
- code_muse/plugins/claude_code_oauth/config.py +52 -0
- code_muse/plugins/claude_code_oauth/fast_mode.py +124 -0
- code_muse/plugins/claude_code_oauth/prompt_handler.py +63 -0
- code_muse/plugins/claude_code_oauth/register_callbacks.py +547 -0
- code_muse/plugins/claude_code_oauth/test_fast_mode.py +165 -0
- code_muse/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_muse/plugins/claude_code_oauth/token_refresh_heartbeat.py +237 -0
- code_muse/plugins/claude_code_oauth/utils.py +664 -0
- code_muse/plugins/copilot_auth/__init__.py +11 -0
- code_muse/plugins/copilot_auth/config.py +91 -0
- code_muse/plugins/copilot_auth/reasoning_client.py +409 -0
- code_muse/plugins/copilot_auth/register_callbacks.py +461 -0
- code_muse/plugins/copilot_auth/utils.py +584 -0
- code_muse/plugins/custom_commands/__init__.py +14 -0
- code_muse/plugins/custom_commands/args_injection.py +82 -0
- code_muse/plugins/custom_commands/command_discovery.py +89 -0
- code_muse/plugins/custom_commands/command_toml_schema.py +71 -0
- code_muse/plugins/custom_commands/register_callbacks.py +176 -0
- code_muse/plugins/customizable_commands/__init__.py +0 -0
- code_muse/plugins/customizable_commands/register_callbacks.py +136 -0
- code_muse/plugins/destructive_command_guard/__init__.py +14 -0
- code_muse/plugins/destructive_command_guard/detector.py +375 -0
- code_muse/plugins/destructive_command_guard/register_callbacks.py +148 -0
- code_muse/plugins/example_custom_command/README.md +280 -0
- code_muse/plugins/example_custom_command/register_callbacks.py +51 -0
- code_muse/plugins/file_permission_handler/__init__.py +4 -0
- code_muse/plugins/file_permission_handler/register_callbacks.py +441 -0
- code_muse/plugins/filter_engine/__init__.py +30 -0
- code_muse/plugins/filter_engine/classifier.py +153 -0
- code_muse/plugins/filter_engine/content_detector.py +184 -0
- code_muse/plugins/filter_engine/dispatcher.py +244 -0
- code_muse/plugins/filter_engine/register_callbacks.py +188 -0
- code_muse/plugins/filter_engine/registry.py +279 -0
- code_muse/plugins/filter_engine/strategies/__init__.py +8 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.pyx +348 -0
- code_muse/plugins/filter_engine/strategies/ast_parser.py +167 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.pyx +584 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.pyx +438 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.pyx +253 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.pyx +178 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.pyx +626 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.pyx +431 -0
- code_muse/plugins/filter_engine/verbosity.py +63 -0
- code_muse/plugins/force_push_guard/__init__.py +5 -0
- code_muse/plugins/force_push_guard/detector.py +96 -0
- code_muse/plugins/force_push_guard/register_callbacks.py +144 -0
- code_muse/plugins/force_push_guard/test_detector.py +143 -0
- code_muse/plugins/frontend_emitter/__init__.py +25 -0
- code_muse/plugins/frontend_emitter/emitter.py +121 -0
- code_muse/plugins/frontend_emitter/register_callbacks.py +259 -0
- code_muse/plugins/gac/__init__.py +4 -0
- code_muse/plugins/gac/git_ops.py +136 -0
- code_muse/plugins/gac/prompt.py +191 -0
- code_muse/plugins/gac/register_callbacks.py +82 -0
- code_muse/plugins/hook_creator/__init__.py +1 -0
- code_muse/plugins/hook_creator/register_callbacks.py +34 -0
- code_muse/plugins/hook_manager/__init__.py +1 -0
- code_muse/plugins/hook_manager/config.py +289 -0
- code_muse/plugins/hook_manager/hooks_menu.py +563 -0
- code_muse/plugins/hook_manager/register_callbacks.py +227 -0
- code_muse/plugins/hook_monitor/register_callbacks.py +36 -0
- code_muse/plugins/mindpack/__init__.py +0 -0
- code_muse/plugins/mindpack/factory.py +930 -0
- code_muse/plugins/mindpack/judge.py +573 -0
- code_muse/plugins/mindpack/memory.py +100 -0
- code_muse/plugins/mindpack/mindpack_menu.py +1552 -0
- code_muse/plugins/mindpack/orchestration.py +605 -0
- code_muse/plugins/mindpack/register_callbacks.py +175 -0
- code_muse/plugins/mindpack/schemas.py +358 -0
- code_muse/plugins/mindpack/tools.py +387 -0
- code_muse/plugins/oauth_muse_html.py +226 -0
- code_muse/plugins/ollama_setup/__init__.py +5 -0
- code_muse/plugins/ollama_setup/completer.py +36 -0
- code_muse/plugins/ollama_setup/register_callbacks.py +410 -0
- code_muse/plugins/plan_command/__init__.py +0 -0
- code_muse/plugins/plan_command/register_callbacks.py +206 -0
- code_muse/plugins/plan_mode/__init__.py +37 -0
- code_muse/plugins/plan_mode/mode_cycling.py +40 -0
- code_muse/plugins/plan_mode/plan_generation.py +68 -0
- code_muse/plugins/plan_mode/plan_hooks.py +74 -0
- code_muse/plugins/plan_mode/plan_mode_tools.py +138 -0
- code_muse/plugins/plan_mode/register_callbacks.py +121 -0
- code_muse/plugins/plugin_trust/register_callbacks.py +140 -0
- code_muse/plugins/policy_engine/__init__.py +46 -0
- code_muse/plugins/policy_engine/approval_flow_integration.py +59 -0
- code_muse/plugins/policy_engine/policy_evaluator.py +75 -0
- code_muse/plugins/policy_engine/policy_file_discovery.py +90 -0
- code_muse/plugins/policy_engine/policy_toml_schema.py +115 -0
- code_muse/plugins/policy_engine/register_callbacks.py +112 -0
- code_muse/plugins/pop_command/__init__.py +1 -0
- code_muse/plugins/pop_command/register_callbacks.py +189 -0
- code_muse/plugins/prompt_newline/__init__.py +13 -0
- code_muse/plugins/prompt_newline/config.py +19 -0
- code_muse/plugins/prompt_newline/register_callbacks.py +159 -0
- code_muse/plugins/safety_status/__init__.py +0 -0
- code_muse/plugins/safety_status/register_callbacks.py +113 -0
- code_muse/plugins/semantic_compression/__init__.py +6 -0
- code_muse/plugins/semantic_compression/compressor.py +295 -0
- code_muse/plugins/semantic_compression/config.py +123 -0
- code_muse/plugins/semantic_compression/register_callbacks.py +320 -0
- code_muse/plugins/shell_minimizer/__init__.py +50 -0
- code_muse/plugins/shell_minimizer/builtin_filters.toml +393 -0
- code_muse/plugins/shell_minimizer/pipeline.py +556 -0
- code_muse/plugins/shell_minimizer/primitives.py +482 -0
- code_muse/plugins/shell_minimizer/register_callbacks.py +276 -0
- code_muse/plugins/shell_safety/__init__.py +6 -0
- code_muse/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_muse/plugins/shell_safety/command_cache.py +149 -0
- code_muse/plugins/shell_safety/register_callbacks.py +202 -0
- code_muse/plugins/synthetic_status/__init__.py +1 -0
- code_muse/plugins/synthetic_status/register_callbacks.py +128 -0
- code_muse/plugins/synthetic_status/status_api.py +145 -0
- code_muse/plugins/token_caching/__init__.py +21 -0
- code_muse/plugins/token_caching/cache_hit_tracking.py +128 -0
- code_muse/plugins/token_caching/cacheable_prefix_detection.py +28 -0
- code_muse/plugins/token_caching/register_callbacks.py +54 -0
- code_muse/plugins/token_caching/stats_display.py +35 -0
- code_muse/plugins/token_tracking/__init__.py +26 -0
- code_muse/plugins/token_tracking/database.py +381 -0
- code_muse/plugins/token_tracking/edit_analyzer.py +97 -0
- code_muse/plugins/token_tracking/record.py +55 -0
- code_muse/plugins/token_tracking/register_callbacks.py +277 -0
- code_muse/plugins/token_tracking/reports.py +329 -0
- code_muse/plugins/universal_constructor/__init__.py +13 -0
- code_muse/plugins/universal_constructor/models.py +136 -0
- code_muse/plugins/universal_constructor/register_callbacks.py +47 -0
- code_muse/plugins/universal_constructor/registry.py +390 -0
- code_muse/plugins/universal_constructor/runner.py +474 -0
- code_muse/plugins/universal_constructor/safety.py +440 -0
- code_muse/plugins/universal_constructor/sandbox.py +584 -0
- code_muse/provider_identity.py +105 -0
- code_muse/pydantic_patches.py +410 -0
- code_muse/reopenable_async_client.py +233 -0
- code_muse/round_robin_model.py +151 -0
- code_muse/secret_storage.py +74 -0
- code_muse/security/__init__.py +1 -0
- code_muse/security/redaction.cpython-314-darwin.so +0 -0
- code_muse/security/redaction.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/security/redaction.pyx +135 -0
- code_muse/session_storage.py +565 -0
- code_muse/status_display.py +261 -0
- code_muse/stream_parser/__init__.py +76 -0
- code_muse/stream_parser/assistant_text_parser.py +90 -0
- code_muse/stream_parser/citation_parser.py +76 -0
- code_muse/stream_parser/inline_hidden_tag_parser.py +236 -0
- code_muse/stream_parser/proposed_plan_parser.py +158 -0
- code_muse/stream_parser/stream_text_chunk.py +23 -0
- code_muse/stream_parser/stream_text_parser.py +27 -0
- code_muse/stream_parser/tagged_line_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/tagged_line_parser.pyx +251 -0
- code_muse/stream_parser/utf8_stream_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/utf8_stream_parser.pyx +206 -0
- code_muse/summarization_agent.py +308 -0
- code_muse/terminal_utils.cpython-314-darwin.so +0 -0
- code_muse/terminal_utils.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/terminal_utils.pyx +483 -0
- code_muse/tools/__init__.py +459 -0
- code_muse/tools/agent_tools.py +613 -0
- code_muse/tools/ask_user_question/__init__.py +26 -0
- code_muse/tools/ask_user_question/constants.py +73 -0
- code_muse/tools/ask_user_question/demo_tui.py +55 -0
- code_muse/tools/ask_user_question/handler.py +232 -0
- code_muse/tools/ask_user_question/models.py +302 -0
- code_muse/tools/ask_user_question/registration.py +37 -0
- code_muse/tools/ask_user_question/renderers.py +336 -0
- code_muse/tools/ask_user_question/terminal_ui.py +327 -0
- code_muse/tools/ask_user_question/theme.py +156 -0
- code_muse/tools/ask_user_question/tui_loop.py +422 -0
- code_muse/tools/background_jobs.py +99 -0
- code_muse/tools/browser/__init__.py +37 -0
- code_muse/tools/browser/browser_control.py +289 -0
- code_muse/tools/browser/browser_interactions.py +545 -0
- code_muse/tools/browser/browser_locators.py +640 -0
- code_muse/tools/browser/browser_manager.py +376 -0
- code_muse/tools/browser/browser_navigation.py +251 -0
- code_muse/tools/browser/browser_screenshot.py +180 -0
- code_muse/tools/browser/browser_scripts.py +462 -0
- code_muse/tools/browser/browser_workflows.py +222 -0
- code_muse/tools/chrome_cdp/__init__.py +1070 -0
- code_muse/tools/chrome_cdp/register_callbacks.py +61 -0
- code_muse/tools/command_runner.py +1401 -0
- code_muse/tools/common.py +1407 -0
- code_muse/tools/display.py +87 -0
- code_muse/tools/file_modifications.py +1099 -0
- code_muse/tools/file_operations.py +860 -0
- code_muse/tools/image_tools.py +185 -0
- code_muse/tools/meetin_proxy/__init__.py +243 -0
- code_muse/tools/meetin_proxy/capture_addon.py +82 -0
- code_muse/tools/meetin_proxy/proxy_manager.py +326 -0
- code_muse/tools/meetin_proxy/register_callbacks.py +45 -0
- code_muse/tools/path_policy.py +219 -0
- code_muse/tools/skills_tools.py +586 -0
- code_muse/tools/subagent_context.py +158 -0
- code_muse/tools/tools_content.py +50 -0
- code_muse/tools/universal_constructor.py +965 -0
- code_muse/uvx_detection.py +241 -0
- code_muse/version_checker.py +86 -0
- code_muse-0.0.1.data/data/code_muse/models.json +66 -0
- code_muse-0.0.1.data/data/code_muse/models_dev_api.json +1 -0
- code_muse-0.0.1.dist-info/METADATA +845 -0
- code_muse-0.0.1.dist-info/RECORD +394 -0
- code_muse-0.0.1.dist-info/WHEEL +4 -0
- code_muse-0.0.1.dist-info/entry_points.txt +2 -0
- code_muse-0.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"""Universal Constructor tool runner using isolated subprocess workers.
|
|
2
|
+
|
|
3
|
+
Replaces thread-only timeout with a killable subprocess/multiprocessing
|
|
4
|
+
worker. Enforces wall-clock timeout, uses JSON-only serialization for
|
|
5
|
+
args and results, and caps stdout/stderr.
|
|
6
|
+
|
|
7
|
+
FREE-THREADED (PEP 734/779): This module now supports
|
|
8
|
+
concurrent.interpreters.InterpreterPoolExecutor as an alternative to
|
|
9
|
+
multiprocessing.Process for CPU-bound isolation in Python 3.14+ free-threaded
|
|
10
|
+
builds. The interpreter pool path shares memory and avoids serialization
|
|
11
|
+
overhead for simple types.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import contextlib
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import multiprocessing
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import tempfile
|
|
21
|
+
import time
|
|
22
|
+
import traceback
|
|
23
|
+
from collections.abc import Callable
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
# FREE-THREADED: Try to import InterpreterPoolExecutor (PEP 734, Python 3.14+)
|
|
29
|
+
# Falls back to None on older interpreters where it is not available.
|
|
30
|
+
try:
|
|
31
|
+
from concurrent.interpreters import InterpreterPoolExecutor
|
|
32
|
+
|
|
33
|
+
_INTERPRETER_POOL_AVAILABLE = True
|
|
34
|
+
except Exception:
|
|
35
|
+
InterpreterPoolExecutor = None # type: ignore[misc, assignment]
|
|
36
|
+
_INTERPRETER_POOL_AVAILABLE = False
|
|
37
|
+
|
|
38
|
+
# Cap output to prevent model context blowup
|
|
39
|
+
_MAX_STDOUT_LINES = 256
|
|
40
|
+
_MAX_STDERR_LINES = 128
|
|
41
|
+
_MAX_STDOUT_CHARS = 4096
|
|
42
|
+
_MAX_STDERR_CHARS = 2048
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _cap_output(text: str, max_chars: int, max_lines: int) -> str:
|
|
46
|
+
"""Cap output string to prevent unbounded growth."""
|
|
47
|
+
if not text:
|
|
48
|
+
return text
|
|
49
|
+
lines = text.splitlines()
|
|
50
|
+
if len(lines) > max_lines:
|
|
51
|
+
lines = lines[:max_lines]
|
|
52
|
+
truncated = True
|
|
53
|
+
else:
|
|
54
|
+
truncated = False
|
|
55
|
+
result = "\n".join(lines)
|
|
56
|
+
if len(result) > max_chars:
|
|
57
|
+
result = result[:max_chars]
|
|
58
|
+
truncated = True
|
|
59
|
+
if truncated:
|
|
60
|
+
result += "\n... [output truncated]"
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _run_in_subprocess(
|
|
65
|
+
module_path: str,
|
|
66
|
+
function_name: str,
|
|
67
|
+
args_json: str,
|
|
68
|
+
result_path: str,
|
|
69
|
+
stdout_path: str,
|
|
70
|
+
stderr_path: str,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Worker function executed in a subprocess.
|
|
73
|
+
|
|
74
|
+
Loads the module, calls the function with deserialized JSON args,
|
|
75
|
+
and writes the JSON result to result_path.
|
|
76
|
+
"""
|
|
77
|
+
stdout_buf = []
|
|
78
|
+
stderr_buf = []
|
|
79
|
+
|
|
80
|
+
class _CapStream:
|
|
81
|
+
def __init__(self, buf, max_lines):
|
|
82
|
+
self.buf = buf
|
|
83
|
+
self.max_lines = max_lines
|
|
84
|
+
|
|
85
|
+
def write(self, text: str) -> None:
|
|
86
|
+
self.buf.append(text)
|
|
87
|
+
if len(self.buf) > self.max_lines * 2:
|
|
88
|
+
self.buf = self.buf[-self.max_lines * 2 :]
|
|
89
|
+
|
|
90
|
+
def flush(self) -> None:
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
# Redirect stdout/stderr to capped buffers
|
|
94
|
+
cap_stdout = _CapStream(stdout_buf, _MAX_STDOUT_LINES)
|
|
95
|
+
cap_stderr = _CapStream(stderr_buf, _MAX_STDERR_LINES)
|
|
96
|
+
sys.stdout = cap_stdout # type: ignore[assignment]
|
|
97
|
+
sys.stderr = cap_stderr # type: ignore[assignment]
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
import importlib.util
|
|
101
|
+
|
|
102
|
+
spec = importlib.util.spec_from_file_location("uc_worker_module", module_path)
|
|
103
|
+
if spec is None or spec.loader is None:
|
|
104
|
+
raise ImportError(f"Cannot load module from {module_path}")
|
|
105
|
+
module = importlib.util.module_from_spec(spec)
|
|
106
|
+
spec.loader.exec_module(module)
|
|
107
|
+
|
|
108
|
+
func = getattr(module, function_name, None)
|
|
109
|
+
if func is None:
|
|
110
|
+
raise NameError(f"Function '{function_name}' not found in module")
|
|
111
|
+
if not callable(func):
|
|
112
|
+
raise TypeError(f"'{function_name}' is not callable")
|
|
113
|
+
|
|
114
|
+
args: dict[str, Any] = json.loads(args_json) if args_json else {}
|
|
115
|
+
if not isinstance(args, dict):
|
|
116
|
+
raise TypeError("tool_args must deserialize to a dict")
|
|
117
|
+
|
|
118
|
+
raw_result = func(**args)
|
|
119
|
+
|
|
120
|
+
# JSON-only serialization: reject non-serializable results
|
|
121
|
+
try:
|
|
122
|
+
result_json = json.dumps({"success": True, "result": raw_result})
|
|
123
|
+
except (TypeError, ValueError) as e:
|
|
124
|
+
raise TypeError(f"Tool result is not JSON-serializable: {e}") from e
|
|
125
|
+
|
|
126
|
+
with open(result_path, "w", encoding="utf-8") as f:
|
|
127
|
+
f.write(result_json)
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
error_info = {
|
|
131
|
+
"success": False,
|
|
132
|
+
"error": f"{type(e).__name__}: {e}",
|
|
133
|
+
"traceback": traceback.format_exc(),
|
|
134
|
+
}
|
|
135
|
+
with open(result_path, "w", encoding="utf-8") as f:
|
|
136
|
+
json.dump(error_info, f)
|
|
137
|
+
|
|
138
|
+
finally:
|
|
139
|
+
# Write captured stdout/stderr
|
|
140
|
+
with open(stdout_path, "w", encoding="utf-8") as f:
|
|
141
|
+
f.write("".join(stdout_buf))
|
|
142
|
+
with open(stderr_path, "w", encoding="utf-8") as f:
|
|
143
|
+
f.write("".join(stderr_buf))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _run_in_interpreter(
|
|
147
|
+
module_path: str,
|
|
148
|
+
function_name: str,
|
|
149
|
+
args: dict[str, Any],
|
|
150
|
+
result_path: str,
|
|
151
|
+
stdout_path: str,
|
|
152
|
+
stderr_path: str,
|
|
153
|
+
) -> None:
|
|
154
|
+
"""Worker function executed in a sub-interpreter via InterpreterPoolExecutor.
|
|
155
|
+
|
|
156
|
+
Mirrors _run_in_subprocess but is designed for PEP 734 interpreter pools.
|
|
157
|
+
Serializes args to JSON and writes JSON result to result_path.
|
|
158
|
+
"""
|
|
159
|
+
# Reuse the same subprocess worker logic — interpreter pools still
|
|
160
|
+
# need file-based I/O for stdout/stderr capture because sub-interpreters
|
|
161
|
+
# do not share sys.stdout with the main interpreter.
|
|
162
|
+
args_json = json.dumps(args) if args else "{}"
|
|
163
|
+
_run_in_subprocess(
|
|
164
|
+
module_path, function_name, args_json, result_path, stdout_path, stderr_path
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _should_use_interpreter_pool() -> bool:
|
|
169
|
+
"""Return True if the InterpreterPoolExecutor path should be used.
|
|
170
|
+
|
|
171
|
+
Controlled by the environment variable ``MUSE_USE_INTERPRETER_POOL``.
|
|
172
|
+
Default is False until PEP 734 is stable in production CPython builds.
|
|
173
|
+
"""
|
|
174
|
+
if not _INTERPRETER_POOL_AVAILABLE:
|
|
175
|
+
return False
|
|
176
|
+
return os.environ.get("MUSE_USE_INTERPRETER_POOL", "").lower() in (
|
|
177
|
+
"1",
|
|
178
|
+
"true",
|
|
179
|
+
"yes",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def run_tool_subprocess(
|
|
184
|
+
module_path: str,
|
|
185
|
+
function_name: str,
|
|
186
|
+
args: dict[str, Any | None] = None,
|
|
187
|
+
timeout: float = 30.0,
|
|
188
|
+
) -> dict[str, Any]:
|
|
189
|
+
"""Run a UC tool function in an isolated subprocess with a hard timeout.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
module_path: Absolute path to the Python module file.
|
|
193
|
+
function_name: Name of the callable function in the module.
|
|
194
|
+
args: Dictionary of arguments to pass to the function.
|
|
195
|
+
timeout: Maximum wall-clock seconds to allow.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Dict with keys:
|
|
199
|
+
- success: bool
|
|
200
|
+
- result: the JSON-deserialized return value (if success)
|
|
201
|
+
- error: error message string (if not success)
|
|
202
|
+
- stdout: capped stdout from the tool
|
|
203
|
+
- stderr: capped stderr from the tool
|
|
204
|
+
- execution_time: float seconds
|
|
205
|
+
"""
|
|
206
|
+
args = args or {}
|
|
207
|
+
args_json = json.dumps(args)
|
|
208
|
+
|
|
209
|
+
with (
|
|
210
|
+
tempfile.NamedTemporaryFile(
|
|
211
|
+
mode="w", suffix=".json", delete=False
|
|
212
|
+
) as result_file,
|
|
213
|
+
tempfile.NamedTemporaryFile(
|
|
214
|
+
mode="w", suffix=".txt", delete=False
|
|
215
|
+
) as stdout_file,
|
|
216
|
+
tempfile.NamedTemporaryFile(
|
|
217
|
+
mode="w", suffix=".txt", delete=False
|
|
218
|
+
) as stderr_file,
|
|
219
|
+
):
|
|
220
|
+
result_path = result_file.name
|
|
221
|
+
stdout_path = stdout_file.name
|
|
222
|
+
stderr_path = stderr_file.name
|
|
223
|
+
|
|
224
|
+
start_time = time.time()
|
|
225
|
+
process: multiprocessing.Process | None = None
|
|
226
|
+
|
|
227
|
+
# FREE-THREADED: Use InterpreterPoolExecutor when available and opted-in.
|
|
228
|
+
# This path avoids fork/spawn overhead and shares memory for simple types.
|
|
229
|
+
if _should_use_interpreter_pool() and InterpreterPoolExecutor is not None:
|
|
230
|
+
try:
|
|
231
|
+
with InterpreterPoolExecutor() as executor:
|
|
232
|
+
future = executor.submit(
|
|
233
|
+
_run_in_interpreter,
|
|
234
|
+
str(module_path),
|
|
235
|
+
function_name,
|
|
236
|
+
args,
|
|
237
|
+
result_path,
|
|
238
|
+
stdout_path,
|
|
239
|
+
stderr_path,
|
|
240
|
+
)
|
|
241
|
+
future.result(timeout=timeout)
|
|
242
|
+
except Exception as exc:
|
|
243
|
+
return {
|
|
244
|
+
"success": False,
|
|
245
|
+
"error": f"Interpreter pool execution failed: {type(exc).__name__}: {exc}",
|
|
246
|
+
"stdout": "",
|
|
247
|
+
"stderr": "",
|
|
248
|
+
"execution_time": time.time() - start_time,
|
|
249
|
+
}
|
|
250
|
+
finally:
|
|
251
|
+
for p in (result_path, stdout_path, stderr_path):
|
|
252
|
+
with contextlib.suppress(OSError):
|
|
253
|
+
os.unlink(p)
|
|
254
|
+
# Fall through to result reading (same as subprocess path)
|
|
255
|
+
execution_time = time.time() - start_time
|
|
256
|
+
try:
|
|
257
|
+
with open(result_path, encoding="utf-8") as f:
|
|
258
|
+
result_data = json.load(f)
|
|
259
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
260
|
+
return {
|
|
261
|
+
"success": False,
|
|
262
|
+
"error": f"Failed to read tool result: {e}",
|
|
263
|
+
"stdout": "",
|
|
264
|
+
"stderr": "",
|
|
265
|
+
"execution_time": execution_time,
|
|
266
|
+
}
|
|
267
|
+
try:
|
|
268
|
+
with open(stdout_path, encoding="utf-8") as f:
|
|
269
|
+
stdout_text = _cap_output(
|
|
270
|
+
f.read(), _MAX_STDOUT_CHARS, _MAX_STDOUT_LINES
|
|
271
|
+
)
|
|
272
|
+
except OSError:
|
|
273
|
+
stdout_text = ""
|
|
274
|
+
try:
|
|
275
|
+
with open(stderr_path, encoding="utf-8") as f:
|
|
276
|
+
stderr_text = _cap_output(
|
|
277
|
+
f.read(), _MAX_STDERR_CHARS, _MAX_STDERR_LINES
|
|
278
|
+
)
|
|
279
|
+
except OSError:
|
|
280
|
+
stderr_text = ""
|
|
281
|
+
return {
|
|
282
|
+
"success": result_data.get("success", False),
|
|
283
|
+
"result": result_data.get("result") if result_data.get("success") else None,
|
|
284
|
+
"error": result_data.get("error")
|
|
285
|
+
if not result_data.get("success")
|
|
286
|
+
else None,
|
|
287
|
+
"stdout": stdout_text,
|
|
288
|
+
"stderr": stderr_text,
|
|
289
|
+
"execution_time": execution_time,
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
# TODO: PEP 734 — replace multiprocessing with InterpreterPoolExecutor when stable
|
|
293
|
+
try:
|
|
294
|
+
ctx = multiprocessing.get_context("spawn")
|
|
295
|
+
process = ctx.Process(
|
|
296
|
+
target=_run_in_subprocess,
|
|
297
|
+
args=(
|
|
298
|
+
str(module_path),
|
|
299
|
+
function_name,
|
|
300
|
+
args_json,
|
|
301
|
+
result_path,
|
|
302
|
+
stdout_path,
|
|
303
|
+
stderr_path,
|
|
304
|
+
),
|
|
305
|
+
)
|
|
306
|
+
process.start()
|
|
307
|
+
process.join(timeout=timeout)
|
|
308
|
+
|
|
309
|
+
if process.is_alive():
|
|
310
|
+
# Timeout: kill the worker
|
|
311
|
+
process.terminate()
|
|
312
|
+
process.join(timeout=2.0)
|
|
313
|
+
if process.is_alive():
|
|
314
|
+
process.kill()
|
|
315
|
+
process.join(timeout=1.0)
|
|
316
|
+
return {
|
|
317
|
+
"success": False,
|
|
318
|
+
"error": f"Tool timed out after {timeout}s",
|
|
319
|
+
"stdout": "",
|
|
320
|
+
"stderr": "",
|
|
321
|
+
"execution_time": time.time() - start_time,
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
# Read result
|
|
325
|
+
try:
|
|
326
|
+
with open(result_path, encoding="utf-8") as f:
|
|
327
|
+
result_data = json.load(f)
|
|
328
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
329
|
+
return {
|
|
330
|
+
"success": False,
|
|
331
|
+
"error": f"Failed to read tool result: {e}",
|
|
332
|
+
"stdout": "",
|
|
333
|
+
"stderr": "",
|
|
334
|
+
"execution_time": time.time() - start_time,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
# Read and cap stdout/stderr
|
|
338
|
+
try:
|
|
339
|
+
with open(stdout_path, encoding="utf-8") as f:
|
|
340
|
+
stdout_text = _cap_output(
|
|
341
|
+
f.read(), _MAX_STDOUT_CHARS, _MAX_STDOUT_LINES
|
|
342
|
+
)
|
|
343
|
+
except OSError:
|
|
344
|
+
stdout_text = ""
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
with open(stderr_path, encoding="utf-8") as f:
|
|
348
|
+
stderr_text = _cap_output(
|
|
349
|
+
f.read(), _MAX_STDERR_CHARS, _MAX_STDERR_LINES
|
|
350
|
+
)
|
|
351
|
+
except OSError:
|
|
352
|
+
stderr_text = ""
|
|
353
|
+
|
|
354
|
+
execution_time = time.time() - start_time
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
"success": result_data.get("success", False),
|
|
358
|
+
"result": result_data.get("result") if result_data.get("success") else None,
|
|
359
|
+
"error": result_data.get("error")
|
|
360
|
+
if not result_data.get("success")
|
|
361
|
+
else None,
|
|
362
|
+
"stdout": stdout_text,
|
|
363
|
+
"stderr": stderr_text,
|
|
364
|
+
"execution_time": execution_time,
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
finally:
|
|
368
|
+
# Cleanup temp files
|
|
369
|
+
for p in (result_path, stdout_path, stderr_path):
|
|
370
|
+
with contextlib.suppress(OSError):
|
|
371
|
+
os.unlink(p)
|
|
372
|
+
if process is not None and process.is_alive():
|
|
373
|
+
try:
|
|
374
|
+
process.kill()
|
|
375
|
+
process.join(timeout=1.0)
|
|
376
|
+
except Exception:
|
|
377
|
+
pass
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def run_tool_callable(
|
|
381
|
+
func: Callable,
|
|
382
|
+
args: dict[str, Any | None] = None,
|
|
383
|
+
timeout: float = 30.0,
|
|
384
|
+
) -> dict[str, Any]:
|
|
385
|
+
"""Run a callable in an isolated subprocess by writing a temp module.
|
|
386
|
+
|
|
387
|
+
This is a fallback for when we only have a callable object (not a
|
|
388
|
+
file path). The callable must be picklable or the caller should
|
|
389
|
+
prefer run_tool_subprocess with a module_path.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
func: The callable to execute.
|
|
393
|
+
args: Dictionary of arguments.
|
|
394
|
+
timeout: Maximum seconds.
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
Dict with success/result/error/stdout/stderr/execution_time.
|
|
398
|
+
"""
|
|
399
|
+
import inspect
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
module = inspect.getmodule(func)
|
|
403
|
+
if module is not None and hasattr(module, "__file__") and module.__file__:
|
|
404
|
+
module_path = module.__file__
|
|
405
|
+
function_name = func.__name__
|
|
406
|
+
return run_tool_subprocess(module_path, function_name, args, timeout)
|
|
407
|
+
except Exception:
|
|
408
|
+
pass
|
|
409
|
+
|
|
410
|
+
# Fallback: write a temporary wrapper module
|
|
411
|
+
try:
|
|
412
|
+
import cloudpickle
|
|
413
|
+
|
|
414
|
+
with tempfile.NamedTemporaryFile(mode="wb", suffix=".pkl", delete=False) as f:
|
|
415
|
+
cloudpickle.dump(func, f)
|
|
416
|
+
pickle_path = f.name
|
|
417
|
+
except ImportError:
|
|
418
|
+
return {
|
|
419
|
+
"success": False,
|
|
420
|
+
"error": "Cannot serialize callable for subprocess execution (cloudpickle not available)",
|
|
421
|
+
"stdout": "",
|
|
422
|
+
"stderr": "",
|
|
423
|
+
"execution_time": 0.0,
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
wrapper_code = f"""
|
|
427
|
+
import cloudpickle, json, sys, traceback
|
|
428
|
+
|
|
429
|
+
with open({repr(pickle_path)}, "rb") as f:
|
|
430
|
+
func = cloudpickle.load(f)
|
|
431
|
+
|
|
432
|
+
args_json = sys.argv[1] if len(sys.argv) > 1 else "{{}}"
|
|
433
|
+
args = json.loads(args_json)
|
|
434
|
+
|
|
435
|
+
try:
|
|
436
|
+
result = func(**args)
|
|
437
|
+
print(json.dumps({{"success": True, "result": result}}))
|
|
438
|
+
except Exception as e:
|
|
439
|
+
print(json.dumps({{"success": False, "error": str(e), "traceback": traceback.format_exc()}}))
|
|
440
|
+
"""
|
|
441
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
|
|
442
|
+
f.write(wrapper_code)
|
|
443
|
+
wrapper_path = f.name
|
|
444
|
+
|
|
445
|
+
try:
|
|
446
|
+
args_json = json.dumps(args or {})
|
|
447
|
+
import subprocess
|
|
448
|
+
|
|
449
|
+
result = subprocess.run(
|
|
450
|
+
[sys.executable, wrapper_path, args_json],
|
|
451
|
+
capture_output=True,
|
|
452
|
+
text=True,
|
|
453
|
+
timeout=timeout,
|
|
454
|
+
)
|
|
455
|
+
try:
|
|
456
|
+
data = json.loads(result.stdout.splitlines()[-1] if result.stdout else "{}")
|
|
457
|
+
except json.JSONDecodeError:
|
|
458
|
+
data = {
|
|
459
|
+
"success": False,
|
|
460
|
+
"error": result.stdout or result.stderr or "Unknown error",
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
"success": data.get("success", False),
|
|
465
|
+
"result": data.get("result") if data.get("success") else None,
|
|
466
|
+
"error": data.get("error") if not data.get("success") else None,
|
|
467
|
+
"stdout": _cap_output(result.stdout, _MAX_STDOUT_CHARS, _MAX_STDOUT_LINES),
|
|
468
|
+
"stderr": _cap_output(result.stderr, _MAX_STDERR_CHARS, _MAX_STDERR_LINES),
|
|
469
|
+
"execution_time": timeout, # approximate
|
|
470
|
+
}
|
|
471
|
+
finally:
|
|
472
|
+
for p in (pickle_path, wrapper_path):
|
|
473
|
+
with contextlib.suppress(OSError):
|
|
474
|
+
os.unlink(p)
|