@elizaos/sweagent-root 2.0.0-alpha
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.
- package/LICENSE +21 -0
- package/README.md +270 -0
- package/package.json +71 -0
- package/python/LICENSE +21 -0
- package/python/config/README.md +15 -0
- package/python/config/bash_only.yaml +222 -0
- package/python/config/benchmarks/250212_sweagent_heavy_sbl.yaml +188 -0
- package/python/config/benchmarks/250225_anthropic_filemap_simple_review.yaml +75 -0
- package/python/config/benchmarks/250522_anthropic_filemap_simple_review.yaml +92 -0
- package/python/config/benchmarks/250526_anthropic_filemap_simple_review_sbl.yaml +93 -0
- package/python/config/benchmarks/anthropic_filemap_multilingual.yaml +66 -0
- package/python/config/coding_challenge.yaml +104 -0
- package/python/config/default.yaml +69 -0
- package/python/config/default_backticks.yaml +69 -0
- package/python/config/default_mm_no_images.yaml +82 -0
- package/python/config/default_mm_with_images.yaml +83 -0
- package/python/config/demo/default.yaml +80 -0
- package/python/config/demo/no_instructions.yaml +69 -0
- package/python/config/demo/only_bash.yaml +60 -0
- package/python/config/exotic/default_shell.yaml +52 -0
- package/python/config/exotic/windowed_replace.yaml +125 -0
- package/python/config/exotic/windowed_replace_late_repro.yaml +127 -0
- package/python/config/human/human.yaml +24 -0
- package/python/config/human/human_demo.yaml +52 -0
- package/python/config/sweagent_0_7/07.yaml +101 -0
- package/python/config/sweagent_0_7/07_fcalling.yaml +100 -0
- package/python/config/sweagent_0_7/07_from_url.yaml +114 -0
- package/python/config/sweagent_0_7/07_thought_action.yaml +102 -0
- package/python/config/sweagent_0_7/07_thought_action_xml.yaml +96 -0
- package/python/mlc_config.json +44 -0
- package/python/pyproject.toml +262 -0
- package/python/sweagent/__init__.py +114 -0
- package/python/sweagent/__main__.py +4 -0
- package/python/sweagent/agent/__init__.py +0 -0
- package/python/sweagent/agent/action_sampler.py +317 -0
- package/python/sweagent/agent/agents.py +1294 -0
- package/python/sweagent/agent/extra/shell_agent.py +106 -0
- package/python/sweagent/agent/history_processors.py +399 -0
- package/python/sweagent/agent/hooks/__init__.py +0 -0
- package/python/sweagent/agent/hooks/abstract.py +139 -0
- package/python/sweagent/agent/hooks/status.py +34 -0
- package/python/sweagent/agent/models.py +896 -0
- package/python/sweagent/agent/problem_statement.py +312 -0
- package/python/sweagent/agent/reviewer.py +664 -0
- package/python/sweagent/environment/__init__.py +0 -0
- package/python/sweagent/environment/hooks/__init__.py +0 -0
- package/python/sweagent/environment/hooks/abstract.py +60 -0
- package/python/sweagent/environment/hooks/status.py +28 -0
- package/python/sweagent/environment/repo.py +219 -0
- package/python/sweagent/environment/swe_env.py +276 -0
- package/python/sweagent/exceptions.py +54 -0
- package/python/sweagent/inspector/README.md +6 -0
- package/python/sweagent/inspector/__init__.py +0 -0
- package/python/sweagent/inspector/favicon.ico +0 -0
- package/python/sweagent/inspector/fileViewer.js +354 -0
- package/python/sweagent/inspector/icons/computer.png +0 -0
- package/python/sweagent/inspector/icons/edit_icon.svg +11 -0
- package/python/sweagent/inspector/icons/swe-agent-logo-50.png +0 -0
- package/python/sweagent/inspector/icons/swellama_blue.png +0 -0
- package/python/sweagent/inspector/icons/swellama_brown.png +0 -0
- package/python/sweagent/inspector/icons/swellama_grey.png +0 -0
- package/python/sweagent/inspector/icons/swellama_tan.png +0 -0
- package/python/sweagent/inspector/index.html +25 -0
- package/python/sweagent/inspector/server.py +354 -0
- package/python/sweagent/inspector/static.py +169 -0
- package/python/sweagent/inspector/style.css +454 -0
- package/python/sweagent/run/__init__.py +0 -0
- package/python/sweagent/run/_progress.py +158 -0
- package/python/sweagent/run/batch_instances.py +419 -0
- package/python/sweagent/run/common.py +387 -0
- package/python/sweagent/run/compare_runs.py +123 -0
- package/python/sweagent/run/extract_pred.py +19 -0
- package/python/sweagent/run/hooks/__init__.py +0 -0
- package/python/sweagent/run/hooks/abstract.py +67 -0
- package/python/sweagent/run/hooks/apply_patch.py +106 -0
- package/python/sweagent/run/hooks/open_pr.py +244 -0
- package/python/sweagent/run/hooks/swe_bench_evaluate.py +113 -0
- package/python/sweagent/run/inspector_cli.py +493 -0
- package/python/sweagent/run/merge_predictions.py +64 -0
- package/python/sweagent/run/quick_stats.py +96 -0
- package/python/sweagent/run/remove_unfinished.py +63 -0
- package/python/sweagent/run/rich_test.py +91 -0
- package/python/sweagent/run/run.py +147 -0
- package/python/sweagent/run/run_batch.py +442 -0
- package/python/sweagent/run/run_replay.py +219 -0
- package/python/sweagent/run/run_shell.py +155 -0
- package/python/sweagent/run/run_single.py +225 -0
- package/python/sweagent/run/run_traj_to_demo.py +85 -0
- package/python/sweagent/tools/__init__.py +0 -0
- package/python/sweagent/tools/bundle.py +57 -0
- package/python/sweagent/tools/commands.py +220 -0
- package/python/sweagent/tools/parsing.py +619 -0
- package/python/sweagent/tools/tools.py +430 -0
- package/python/sweagent/tools/utils.py +108 -0
- package/python/sweagent/types.py +102 -0
- package/python/sweagent/utils/__init__.py +0 -0
- package/python/sweagent/utils/config.py +80 -0
- package/python/sweagent/utils/files.py +27 -0
- package/python/sweagent/utils/github.py +118 -0
- package/python/sweagent/utils/jinja_warnings.py +14 -0
- package/python/sweagent/utils/log.py +175 -0
- package/python/sweagent/utils/patch_formatter.py +152 -0
- package/python/sweagent/utils/serialization.py +45 -0
- package/python/tests/__init__.py +0 -0
- package/python/tests/conftest.py +191 -0
- package/python/tests/test_agent.py +258 -0
- package/python/tests/test_batch_instance.py +43 -0
- package/python/tests/test_commands/_interactive_dummy.py +35 -0
- package/python/tests/test_commands/interactive_dummy_wrapper.sh +29 -0
- package/python/tests/test_data/config_files/dummy_interactive.yaml +62 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/Dockerfile +20 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/README.md +13 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/challenge.json +12 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/customrandom.c +50 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/docker-compose.yml +14 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/release +0 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/server +0 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/solver.py +12 -0
- package/python/tests/test_data/data_sources/ctf/forensics/flash/README.md +16 -0
- package/python/tests/test_data/data_sources/ctf/forensics/flash/challenge.json +9 -0
- package/python/tests/test_data/data_sources/ctf/forensics/flash/flash_c8429a430278283c0e571baebca3d139.zip +0 -0
- package/python/tests/test_data/data_sources/ctf/misc/networking_1/README.md +15 -0
- package/python/tests/test_data/data_sources/ctf/misc/networking_1/challenge.json +10 -0
- package/python/tests/test_data/data_sources/ctf/misc/networking_1/networking.pcap +0 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/Dockerfile +28 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/README.md +14 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/challenge.json +14 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/docker-compose.yml +14 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/flag.txt +1 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/warmup +0 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/warmup.c +26 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/warmup.py +9 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/README.md +14 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/challenge.json +8 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/rock +0 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/rock.cpp +167 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/solution.cpp +24 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/test_solver/solution.py +6 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/test_solver/test.sh +10 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/000-default.conf +18 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/Dockerfile +20 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/cgi/file.pl +38 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/cgi/forms.pl +40 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/cgi/hello.pl +11 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/challenge.json +12 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/docker-compose.yml +14 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/flag +1 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/index.html +11 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/solution.txt +1 -0
- package/python/tests/test_data/data_sources/debug_20240322.json +1 -0
- package/python/tests/test_data/data_sources/expert_instances.yaml +16 -0
- package/python/tests/test_data/data_sources/human_eval.json +1 -0
- package/python/tests/test_data/data_sources/simple_instances.yaml +3 -0
- package/python/tests/test_data/data_sources/simple_instances_long.yaml +30 -0
- package/python/tests/test_data/data_sources/swe-bench-dev-easy.json +1 -0
- package/python/tests/test_data/data_sources/swe-bench-dev-easy_first_only.json +1 -0
- package/python/tests/test_data/data_sources/swe-bench-lite-test.json +1 -0
- package/python/tests/test_data/trajectories/gpt4__swe-agent-test-repo__default_from_url__t-0.00__p-0.95__c-3.00__install-1/6e44b9__sweagenttestrepo-1c2844.traj +342 -0
- package/python/tests/test_data/trajectories/gpt4__swe-agent-test-repo__default_from_url__t-0.00__p-0.95__c-3.00__install-1/solution_missing_colon.py +15 -0
- package/python/tests/test_data/trajectories/gpt4__swe-agent__test-repo__default_from_url__t-0.00__p-0.95__c-3.00__install-1/args.yaml +518 -0
- package/python/tests/test_data/trajectories/gpt4__swe-agent__test-repo__default_from_url__t-0.00__p-0.95__c-3.00__install-1/swe-agent__test-repo-i1.traj +124 -0
- package/python/tests/test_data/trajectories/gpt4__swe-bench-dev-easy_first_only__default__t-0.00__p-0.95__c-3.00__install-1/all_preds.jsonl +1 -0
- package/python/tests/test_data/trajectories/gpt4__swe-bench-dev-easy_first_only__default__t-0.00__p-0.95__c-3.00__install-1/args.yaml +520 -0
- package/python/tests/test_data/trajectories/gpt4__swe-bench-dev-easy_first_only__default__t-0.00__p-0.95__c-3.00__install-1/patches/pydicom__pydicom-1458.patch +18 -0
- package/python/tests/test_data/trajectories/gpt4__swe-bench-dev-easy_first_only__default__t-0.00__p-0.95__c-3.00__install-1/pydicom__pydicom-1458.traj +257 -0
- package/python/tests/test_env.py +66 -0
- package/python/tests/test_env_utils.py +129 -0
- package/python/tests/test_history_processors.py +40 -0
- package/python/tests/test_models.py +23 -0
- package/python/tests/test_openai_live.py +164 -0
- package/python/tests/test_packaging.py +7 -0
- package/python/tests/test_parsing.py +131 -0
- package/python/tests/test_problem_statement_multimodal.py +111 -0
- package/python/tests/test_quick_stats.py +42 -0
- package/python/tests/test_run.py +37 -0
- package/python/tests/test_run_batch.py +110 -0
- package/python/tests/test_run_hooks.py +114 -0
- package/python/tests/test_run_replay.py +33 -0
- package/python/tests/test_run_single.py +125 -0
- package/python/tests/test_tools_command_parsing.py +193 -0
- package/python/tests/test_utils.py +15 -0
- package/python/tests/tools/__init__.py +0 -0
- package/python/tests/tools/conftest.py +12 -0
- package/python/tests/tools/test_default_utils.py +153 -0
- package/python/tests/tools/test_edit_replace.py +0 -0
- package/python/tests/tools/test_split_string.py +82 -0
- package/python/tests/utils.py +29 -0
- package/python/tools/diff_state/bin/_state_diff_state +52 -0
- package/python/tools/diff_state/config.yaml +2 -0
- package/python/tools/edit_anthropic/bin/_state_anthropic +21 -0
- package/python/tools/edit_anthropic/bin/str_replace_editor +710 -0
- package/python/tools/edit_anthropic/config.yaml +56 -0
- package/python/tools/edit_anthropic/install.sh +3 -0
- package/python/tools/filemap/bin/filemap +45 -0
- package/python/tools/filemap/config.yaml +9 -0
- package/python/tools/filemap/install.sh +2 -0
- package/python/tools/forfeit/bin/exit_forfeit +5 -0
- package/python/tools/forfeit/config.yaml +5 -0
- package/python/tools/image_tools/bin/view_image +36 -0
- package/python/tools/image_tools/config.yaml +9 -0
- package/python/tools/multilingual_setup/bin/do_nothing +2 -0
- package/python/tools/multilingual_setup/config.yaml +1 -0
- package/python/tools/multilingual_setup/install.sh +45 -0
- package/python/tools/registry/bin/_read_env +10 -0
- package/python/tools/registry/bin/_write_env +10 -0
- package/python/tools/registry/config.yaml +1 -0
- package/python/tools/registry/install.sh +6 -0
- package/python/tools/registry/lib/__init__.py +0 -0
- package/python/tools/registry/lib/registry.py +56 -0
- package/python/tools/review_on_submit_m/README.md +6 -0
- package/python/tools/review_on_submit_m/bin/submit +54 -0
- package/python/tools/review_on_submit_m/config.yaml +6 -0
- package/python/tools/review_on_submit_m/install.sh +0 -0
- package/python/tools/search/bin/find_file +31 -0
- package/python/tools/search/bin/search_dir +39 -0
- package/python/tools/search/bin/search_file +55 -0
- package/python/tools/search/config.yaml +37 -0
- package/python/tools/search/install.sh +3 -0
- package/python/tools/submit/bin/submit +17 -0
- package/python/tools/submit/config.yaml +5 -0
- package/python/tools/web_browser/bin/click_mouse +41 -0
- package/python/tools/web_browser/bin/close_site +28 -0
- package/python/tools/web_browser/bin/double_click_mouse +37 -0
- package/python/tools/web_browser/bin/drag_mouse +46 -0
- package/python/tools/web_browser/bin/execute_script_on_page +39 -0
- package/python/tools/web_browser/bin/get_console_output +48 -0
- package/python/tools/web_browser/bin/move_mouse +35 -0
- package/python/tools/web_browser/bin/navigate_back +33 -0
- package/python/tools/web_browser/bin/navigate_forward +33 -0
- package/python/tools/web_browser/bin/open_site +36 -0
- package/python/tools/web_browser/bin/press_keys_on_page +51 -0
- package/python/tools/web_browser/bin/reload_page +33 -0
- package/python/tools/web_browser/bin/run_web_browser_server +394 -0
- package/python/tools/web_browser/bin/screenshot_site +38 -0
- package/python/tools/web_browser/bin/scroll_on_page +40 -0
- package/python/tools/web_browser/bin/set_browser_window_size +40 -0
- package/python/tools/web_browser/bin/type_text +34 -0
- package/python/tools/web_browser/bin/wait_time +39 -0
- package/python/tools/web_browser/config.yaml +155 -0
- package/python/tools/web_browser/install.sh +22 -0
- package/python/tools/web_browser/lib/browser_manager.py +404 -0
- package/python/tools/web_browser/lib/web_browser_config.py +33 -0
- package/python/tools/web_browser/lib/web_browser_utils.py +126 -0
- package/python/tools/web_browser/test_console.html +1 -0
- package/python/tools/windowed/bin/_state +25 -0
- package/python/tools/windowed/bin/create +29 -0
- package/python/tools/windowed/bin/goto +37 -0
- package/python/tools/windowed/bin/open +49 -0
- package/python/tools/windowed/bin/scroll_down +12 -0
- package/python/tools/windowed/bin/scroll_up +13 -0
- package/python/tools/windowed/config.yaml +38 -0
- package/python/tools/windowed/install.sh +15 -0
- package/python/tools/windowed/lib/__init__.py +0 -0
- package/python/tools/windowed/lib/flake8_utils.py +147 -0
- package/python/tools/windowed/lib/windowed_file.py +312 -0
- package/python/tools/windowed_edit_linting/bin/edit +128 -0
- package/python/tools/windowed_edit_linting/config.yaml +31 -0
- package/python/tools/windowed_edit_linting/install.sh +5 -0
- package/python/tools/windowed_edit_replace/bin/edit +172 -0
- package/python/tools/windowed_edit_replace/bin/insert +77 -0
- package/python/tools/windowed_edit_replace/config.yaml +60 -0
- package/python/tools/windowed_edit_replace/install.sh +5 -0
- package/python/tools/windowed_edit_rewrite/bin/edit +78 -0
- package/python/tools/windowed_edit_rewrite/config.yaml +11 -0
- package/python/tools/windowed_edit_rewrite/install.sh +5 -0
- package/python/trajectories/demonstrations/ctf/crypto/BabyEncryption.traj +318 -0
- package/python/trajectories/demonstrations/ctf/crypto/BabyTimeCapsule.traj +197 -0
- package/python/trajectories/demonstrations/ctf/crypto/eps.traj +289 -0
- package/python/trajectories/demonstrations/ctf/crypto/katy.traj +368 -0
- package/python/trajectories/demonstrations/ctf/forensics/flash.traj +102 -0
- package/python/trajectories/demonstrations/ctf/misc/networking_1.traj +102 -0
- package/python/trajectories/demonstrations/ctf/pwn/warmup.traj +159 -0
- package/python/trajectories/demonstrations/ctf/rev/rock.traj +251 -0
- package/python/trajectories/demonstrations/ctf/web/i_got_id_demo.traj +422 -0
- package/python/trajectories/demonstrations/function_calling_simple.traj +151 -0
- package/python/trajectories/demonstrations/human_thought__swe-bench-HumanEvalFix-python__lcb__t-0.00__p-0.95__c-4.00__install-0/humanevalfix-python-0.traj +129 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__default__t-0.20__p-0.95__c-2.00__install-1___install_from_source/marshmallow-code__marshmallow-1867.traj +318 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__default_sys-env_cursors_window100__t-0.20__p-0.95__c-2.00__install-1/marshmallow-code__marshmallow-1867.traj +251 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__default_sys-env_window100__t-0.20__p-0.95__c-2.00__install-1/marshmallow-code__marshmallow-1867.traj +399 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__function_calling__install-1/marshmallow-code__marshmallow-1867.traj +594 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__function_calling_replace__install-1/marshmallow-code__marshmallow-1867.traj +592 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__function_calling_replace_from_source/marshmallow-code__marshmallow-1867.traj +3316 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__xml_sys-env_cursors_window100__t-0.20__p-0.95__c-2.00__install-1/marshmallow-code__marshmallow-1867.traj +251 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__xml_sys-env_window100__t-0.20__p-0.95__c-2.00__install-1/marshmallow-code__marshmallow-1867.traj +399 -0
- package/python/trajectories/demonstrations/str_replace_anthropic_demo.yaml +432 -0
- package/rust/Cargo.toml +100 -0
- package/rust/README.md +49 -0
- package/rust/src/agent/action_sampler.rs +130 -0
- package/rust/src/agent/agents.rs +1029 -0
- package/rust/src/agent/history_processors.rs +277 -0
- package/rust/src/agent/hooks/mod.rs +208 -0
- package/rust/src/agent/mod.rs +24 -0
- package/rust/src/agent/models.rs +837 -0
- package/rust/src/agent/problem_statement.rs +355 -0
- package/rust/src/agent/reviewer.rs +505 -0
- package/rust/src/bin/sweagent.rs +784 -0
- package/rust/src/environment/deployment.rs +631 -0
- package/rust/src/environment/hooks/mod.rs +114 -0
- package/rust/src/environment/mod.rs +16 -0
- package/rust/src/environment/repo.rs +265 -0
- package/rust/src/environment/runtime.rs +237 -0
- package/rust/src/environment/swe_env.rs +248 -0
- package/rust/src/exceptions.rs +228 -0
- package/rust/src/lib.rs +68 -0
- package/rust/src/monitoring.rs +482 -0
- package/rust/src/run/hooks/mod.rs +134 -0
- package/rust/src/run/mod.rs +12 -0
- package/rust/src/run/run_batch.rs +563 -0
- package/rust/src/run/run_single.rs +196 -0
- package/rust/src/tools/bundle.rs +224 -0
- package/rust/src/tools/commands.rs +173 -0
- package/rust/src/tools/mod.rs +295 -0
- package/rust/src/tools/parsing.rs +354 -0
- package/rust/src/tools/registry.rs +143 -0
- package/rust/src/types.rs +554 -0
- package/rust/src/utils/config.rs +105 -0
- package/rust/src/utils/files.rs +137 -0
- package/rust/src/utils/github.rs +171 -0
- package/rust/src/utils/log.rs +65 -0
- package/rust/src/utils/mod.rs +17 -0
- package/rust/src/utils/serialization.rs +181 -0
- package/rust/src/utils/template.rs +173 -0
- package/typescript/README.md +335 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
//! Environment hooks for monitoring and extending environment behavior
|
|
2
|
+
|
|
3
|
+
use async_trait::async_trait;
|
|
4
|
+
|
|
5
|
+
/// Abstract hook for environment events
|
|
6
|
+
#[async_trait]
|
|
7
|
+
pub trait EnvironmentHook: Send + Sync {
|
|
8
|
+
/// Called when environment is initialized
|
|
9
|
+
fn on_init(&mut self) {}
|
|
10
|
+
|
|
11
|
+
/// Called when environment starts
|
|
12
|
+
fn on_start(&mut self) {}
|
|
13
|
+
|
|
14
|
+
/// Called when environment stops
|
|
15
|
+
fn on_stop(&mut self) {}
|
|
16
|
+
|
|
17
|
+
/// Called when a command is executed
|
|
18
|
+
fn on_command_executed(&mut self, _command: &str, _output: &str) {}
|
|
19
|
+
|
|
20
|
+
/// Called when environment is reset
|
|
21
|
+
fn on_reset(&mut self) {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// Combined hook that wraps multiple hooks
|
|
25
|
+
pub struct CombinedEnvironmentHook {
|
|
26
|
+
hooks: Vec<Box<dyn EnvironmentHook>>,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
impl CombinedEnvironmentHook {
|
|
30
|
+
pub fn new() -> Self {
|
|
31
|
+
Self { hooks: Vec::new() }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pub fn add_hook(&mut self, hook: Box<dyn EnvironmentHook>) {
|
|
35
|
+
self.hooks.push(hook);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
impl Default for CombinedEnvironmentHook {
|
|
40
|
+
fn default() -> Self {
|
|
41
|
+
Self::new()
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#[async_trait]
|
|
46
|
+
impl EnvironmentHook for CombinedEnvironmentHook {
|
|
47
|
+
fn on_init(&mut self) {
|
|
48
|
+
for hook in &mut self.hooks {
|
|
49
|
+
hook.on_init();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fn on_start(&mut self) {
|
|
54
|
+
for hook in &mut self.hooks {
|
|
55
|
+
hook.on_start();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fn on_stop(&mut self) {
|
|
60
|
+
for hook in &mut self.hooks {
|
|
61
|
+
hook.on_stop();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn on_command_executed(&mut self, command: &str, output: &str) {
|
|
66
|
+
for hook in &mut self.hooks {
|
|
67
|
+
hook.on_command_executed(command, output);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fn on_reset(&mut self) {
|
|
72
|
+
for hook in &mut self.hooks {
|
|
73
|
+
hook.on_reset();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Status hook for logging environment events
|
|
79
|
+
pub struct EnvironmentStatusHook {
|
|
80
|
+
pub show_commands: bool,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
impl EnvironmentStatusHook {
|
|
84
|
+
pub fn new(show_commands: bool) -> Self {
|
|
85
|
+
Self { show_commands }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
impl Default for EnvironmentStatusHook {
|
|
90
|
+
fn default() -> Self {
|
|
91
|
+
Self::new(false)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
#[async_trait]
|
|
96
|
+
impl EnvironmentHook for EnvironmentStatusHook {
|
|
97
|
+
fn on_start(&mut self) {
|
|
98
|
+
tracing::info!("Environment started");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fn on_stop(&mut self) {
|
|
102
|
+
tracing::info!("Environment stopped");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fn on_command_executed(&mut self, command: &str, output: &str) {
|
|
106
|
+
if self.show_commands {
|
|
107
|
+
tracing::debug!(
|
|
108
|
+
command = command,
|
|
109
|
+
output_len = output.len(),
|
|
110
|
+
"Command executed"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//! Environment module for SWE-agent
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides the runtime environment for agent execution,
|
|
4
|
+
//! including Docker deployment and repository management.
|
|
5
|
+
|
|
6
|
+
pub mod deployment;
|
|
7
|
+
pub mod hooks;
|
|
8
|
+
pub mod repo;
|
|
9
|
+
pub mod runtime;
|
|
10
|
+
pub mod swe_env;
|
|
11
|
+
|
|
12
|
+
pub use deployment::*;
|
|
13
|
+
pub use hooks::*;
|
|
14
|
+
pub use repo::*;
|
|
15
|
+
pub use runtime::*;
|
|
16
|
+
pub use swe_env::*;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
//! Repository management for SWE-agent
|
|
2
|
+
|
|
3
|
+
use crate::exceptions::{Result, SWEAgentError};
|
|
4
|
+
use crate::utils::github::{parse_github_repo_url, GithubRepoInfo};
|
|
5
|
+
use serde::{Deserialize, Serialize};
|
|
6
|
+
use std::path::{Path, PathBuf};
|
|
7
|
+
|
|
8
|
+
/// Trait for repository sources
|
|
9
|
+
pub trait Repo: Send + Sync {
|
|
10
|
+
/// Get the repository name
|
|
11
|
+
fn repo_name(&self) -> &str;
|
|
12
|
+
|
|
13
|
+
/// Get the clone URL if available
|
|
14
|
+
fn clone_url(&self) -> Option<&str>;
|
|
15
|
+
|
|
16
|
+
/// Get the local path if available
|
|
17
|
+
fn local_path(&self) -> Option<&Path>;
|
|
18
|
+
|
|
19
|
+
/// Get the base commit if specified
|
|
20
|
+
fn base_commit(&self) -> Option<&str>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Pre-existing repository already in the environment
|
|
24
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
25
|
+
pub struct PreExistingRepo {
|
|
26
|
+
pub repo_name: String,
|
|
27
|
+
pub path: PathBuf,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl PreExistingRepo {
|
|
31
|
+
pub fn new(repo_name: impl Into<String>, path: impl Into<PathBuf>) -> Self {
|
|
32
|
+
Self {
|
|
33
|
+
repo_name: repo_name.into(),
|
|
34
|
+
path: path.into(),
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
impl Repo for PreExistingRepo {
|
|
40
|
+
fn repo_name(&self) -> &str {
|
|
41
|
+
&self.repo_name
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn clone_url(&self) -> Option<&str> {
|
|
45
|
+
None
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fn local_path(&self) -> Option<&Path> {
|
|
49
|
+
Some(&self.path)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fn base_commit(&self) -> Option<&str> {
|
|
53
|
+
None
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// Local repository on the filesystem
|
|
58
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
59
|
+
pub struct LocalRepo {
|
|
60
|
+
pub path: PathBuf,
|
|
61
|
+
pub repo_name: String,
|
|
62
|
+
pub base_commit: Option<String>,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
impl LocalRepo {
|
|
66
|
+
pub fn new(path: impl Into<PathBuf>) -> Result<Self> {
|
|
67
|
+
let path = path.into();
|
|
68
|
+
let repo_name = path
|
|
69
|
+
.file_name()
|
|
70
|
+
.and_then(|n| n.to_str())
|
|
71
|
+
.unwrap_or("local-repo")
|
|
72
|
+
.to_string();
|
|
73
|
+
|
|
74
|
+
if !path.exists() {
|
|
75
|
+
return Err(SWEAgentError::FileNotFound(format!(
|
|
76
|
+
"Repository path does not exist: {}",
|
|
77
|
+
path.display()
|
|
78
|
+
)));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Ok(Self {
|
|
82
|
+
path,
|
|
83
|
+
repo_name,
|
|
84
|
+
base_commit: None,
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
pub fn with_base_commit(mut self, commit: impl Into<String>) -> Self {
|
|
89
|
+
self.base_commit = Some(commit.into());
|
|
90
|
+
self
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
impl Repo for LocalRepo {
|
|
95
|
+
fn repo_name(&self) -> &str {
|
|
96
|
+
&self.repo_name
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fn clone_url(&self) -> Option<&str> {
|
|
100
|
+
None
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fn local_path(&self) -> Option<&Path> {
|
|
104
|
+
Some(&self.path)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
fn base_commit(&self) -> Option<&str> {
|
|
108
|
+
self.base_commit.as_deref()
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// GitHub repository
|
|
113
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
114
|
+
pub struct GithubRepo {
|
|
115
|
+
pub github_url: String,
|
|
116
|
+
pub info: Option<GithubRepoInfo>,
|
|
117
|
+
pub base_commit: Option<String>,
|
|
118
|
+
pub clone_timeout: u64,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
impl GithubRepo {
|
|
122
|
+
pub fn new(github_url: impl Into<String>) -> Result<Self> {
|
|
123
|
+
let url = github_url.into();
|
|
124
|
+
let info = parse_github_repo_url(&url)?;
|
|
125
|
+
|
|
126
|
+
Ok(Self {
|
|
127
|
+
github_url: url,
|
|
128
|
+
info: Some(info),
|
|
129
|
+
base_commit: None,
|
|
130
|
+
clone_timeout: 300,
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pub fn with_base_commit(mut self, commit: impl Into<String>) -> Self {
|
|
135
|
+
self.base_commit = Some(commit.into());
|
|
136
|
+
self
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
pub fn with_clone_timeout(mut self, timeout: u64) -> Self {
|
|
140
|
+
self.clone_timeout = timeout;
|
|
141
|
+
self
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
impl Repo for GithubRepo {
|
|
146
|
+
fn repo_name(&self) -> &str {
|
|
147
|
+
self.info
|
|
148
|
+
.as_ref()
|
|
149
|
+
.map(|i| i.full_name.as_str())
|
|
150
|
+
.unwrap_or("unknown")
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fn clone_url(&self) -> Option<&str> {
|
|
154
|
+
Some(&self.github_url)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fn local_path(&self) -> Option<&Path> {
|
|
158
|
+
None
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fn base_commit(&self) -> Option<&str> {
|
|
162
|
+
self.base_commit.as_deref()
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// Configuration for pre-existing repository
|
|
167
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
168
|
+
pub struct PreExistingRepoConfig {
|
|
169
|
+
pub repo_name: String,
|
|
170
|
+
pub path: String,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// Configuration for local repository
|
|
174
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
175
|
+
pub struct LocalRepoConfig {
|
|
176
|
+
pub path: String,
|
|
177
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
178
|
+
pub base_commit: Option<String>,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/// Configuration for GitHub repository
|
|
182
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
183
|
+
pub struct GithubRepoConfig {
|
|
184
|
+
pub github_url: String,
|
|
185
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
186
|
+
pub base_commit: Option<String>,
|
|
187
|
+
#[serde(default = "default_clone_timeout")]
|
|
188
|
+
pub clone_timeout: u64,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
fn default_clone_timeout() -> u64 {
|
|
192
|
+
300
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/// Union type for repository configurations
|
|
196
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
197
|
+
#[serde(tag = "type", rename_all = "snake_case")]
|
|
198
|
+
pub enum RepoConfig {
|
|
199
|
+
PreExisting(PreExistingRepoConfig),
|
|
200
|
+
Local(LocalRepoConfig),
|
|
201
|
+
Github(GithubRepoConfig),
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// Create a repository from simplified input
|
|
205
|
+
pub fn repo_from_simplified_input(input: &str) -> Result<Box<dyn Repo>> {
|
|
206
|
+
// Check if it's a GitHub URL
|
|
207
|
+
if input.starts_with("https://github.com/") || input.starts_with("git@github.com:") {
|
|
208
|
+
return Ok(Box::new(GithubRepo::new(input)?));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Check if it's a local path
|
|
212
|
+
let path = Path::new(input);
|
|
213
|
+
if path.exists() {
|
|
214
|
+
return Ok(Box::new(LocalRepo::new(path)?));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
Err(SWEAgentError::InvalidConfiguration(format!(
|
|
218
|
+
"Could not parse repository input: {}",
|
|
219
|
+
input
|
|
220
|
+
)))
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/// Create a repository from configuration
|
|
224
|
+
pub fn create_repo(config: &RepoConfig) -> Result<Box<dyn Repo>> {
|
|
225
|
+
match config {
|
|
226
|
+
RepoConfig::PreExisting(cfg) => Ok(Box::new(PreExistingRepo::new(
|
|
227
|
+
cfg.repo_name.clone(),
|
|
228
|
+
&cfg.path,
|
|
229
|
+
))),
|
|
230
|
+
RepoConfig::Local(cfg) => {
|
|
231
|
+
let mut repo = LocalRepo::new(&cfg.path)?;
|
|
232
|
+
if let Some(ref commit) = cfg.base_commit {
|
|
233
|
+
repo = repo.with_base_commit(commit.clone());
|
|
234
|
+
}
|
|
235
|
+
Ok(Box::new(repo))
|
|
236
|
+
}
|
|
237
|
+
RepoConfig::Github(cfg) => {
|
|
238
|
+
let mut repo = GithubRepo::new(&cfg.github_url)?;
|
|
239
|
+
if let Some(ref commit) = cfg.base_commit {
|
|
240
|
+
repo = repo.with_base_commit(commit.clone());
|
|
241
|
+
}
|
|
242
|
+
repo = repo.with_clone_timeout(cfg.clone_timeout);
|
|
243
|
+
Ok(Box::new(repo))
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#[cfg(test)]
|
|
249
|
+
mod tests {
|
|
250
|
+
use super::*;
|
|
251
|
+
|
|
252
|
+
#[test]
|
|
253
|
+
fn test_github_repo() {
|
|
254
|
+
let repo = GithubRepo::new("https://github.com/elizaos/eliza").unwrap();
|
|
255
|
+
assert_eq!(repo.repo_name(), "elizaos/eliza");
|
|
256
|
+
assert!(repo.clone_url().is_some());
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
#[test]
|
|
260
|
+
fn test_pre_existing_repo() {
|
|
261
|
+
let repo = PreExistingRepo::new("test-repo", "/tmp/test");
|
|
262
|
+
assert_eq!(repo.repo_name(), "test-repo");
|
|
263
|
+
assert!(repo.local_path().is_some());
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
//! Runtime abstractions for command execution
|
|
2
|
+
|
|
3
|
+
use crate::exceptions::{Result, SWEAgentError};
|
|
4
|
+
use async_trait::async_trait;
|
|
5
|
+
use serde::{Deserialize, Serialize};
|
|
6
|
+
|
|
7
|
+
/// A bash action to execute
|
|
8
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
9
|
+
pub struct BashAction {
|
|
10
|
+
pub command: String,
|
|
11
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
12
|
+
pub timeout: Option<u64>,
|
|
13
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
14
|
+
pub cwd: Option<String>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl BashAction {
|
|
18
|
+
pub fn new(command: impl Into<String>) -> Self {
|
|
19
|
+
Self {
|
|
20
|
+
command: command.into(),
|
|
21
|
+
timeout: None,
|
|
22
|
+
cwd: None,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn with_timeout(mut self, timeout: u64) -> Self {
|
|
27
|
+
self.timeout = Some(timeout);
|
|
28
|
+
self
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pub fn with_cwd(mut self, cwd: impl Into<String>) -> Self {
|
|
32
|
+
self.cwd = Some(cwd.into());
|
|
33
|
+
self
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// Result of a bash action
|
|
38
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
39
|
+
pub struct BashActionResult {
|
|
40
|
+
pub output: String,
|
|
41
|
+
pub exit_code: i32,
|
|
42
|
+
pub timed_out: bool,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
impl BashActionResult {
|
|
46
|
+
pub fn success(output: impl Into<String>) -> Self {
|
|
47
|
+
Self {
|
|
48
|
+
output: output.into(),
|
|
49
|
+
exit_code: 0,
|
|
50
|
+
timed_out: false,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
pub fn failure(output: impl Into<String>, exit_code: i32) -> Self {
|
|
55
|
+
Self {
|
|
56
|
+
output: output.into(),
|
|
57
|
+
exit_code,
|
|
58
|
+
timed_out: false,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub fn timeout(output: impl Into<String>) -> Self {
|
|
63
|
+
Self {
|
|
64
|
+
output: output.into(),
|
|
65
|
+
exit_code: -1,
|
|
66
|
+
timed_out: true,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// Bash interrupt action
|
|
72
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
73
|
+
pub struct BashInterruptAction;
|
|
74
|
+
|
|
75
|
+
/// Request to create a bash session
|
|
76
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
77
|
+
pub struct CreateBashSessionRequest {
|
|
78
|
+
pub session_id: String,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// A command to execute
|
|
82
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
83
|
+
pub struct Command {
|
|
84
|
+
pub command: String,
|
|
85
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
86
|
+
pub args: Option<Vec<String>>,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
impl Command {
|
|
90
|
+
pub fn new(command: impl Into<String>) -> Self {
|
|
91
|
+
Self {
|
|
92
|
+
command: command.into(),
|
|
93
|
+
args: None,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
pub fn with_args(mut self, args: Vec<String>) -> Self {
|
|
98
|
+
self.args = Some(args);
|
|
99
|
+
self
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// Result of a command execution
|
|
104
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
105
|
+
pub struct CommandResult {
|
|
106
|
+
pub stdout: String,
|
|
107
|
+
pub stderr: String,
|
|
108
|
+
pub exit_code: i32,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Request to read a file
|
|
112
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
113
|
+
pub struct ReadFileRequest {
|
|
114
|
+
pub path: String,
|
|
115
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
116
|
+
pub encoding: Option<String>,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// Response from reading a file
|
|
120
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
121
|
+
pub struct ReadFileResponse {
|
|
122
|
+
pub content: String,
|
|
123
|
+
pub size: u64,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// Request to write a file
|
|
127
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
128
|
+
pub struct WriteFileRequest {
|
|
129
|
+
pub path: String,
|
|
130
|
+
pub content: String,
|
|
131
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
132
|
+
pub mode: Option<u32>,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// Request to upload a file
|
|
136
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
137
|
+
pub struct UploadRequest {
|
|
138
|
+
pub local_path: String,
|
|
139
|
+
pub remote_path: String,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// Abstract runtime trait
|
|
143
|
+
#[async_trait]
|
|
144
|
+
pub trait Runtime: Send + Sync {
|
|
145
|
+
/// Execute a bash command
|
|
146
|
+
async fn execute_bash(&self, action: BashAction) -> Result<BashActionResult>;
|
|
147
|
+
|
|
148
|
+
/// Interrupt the current bash command
|
|
149
|
+
async fn interrupt_bash(&self) -> Result<()>;
|
|
150
|
+
|
|
151
|
+
/// Read a file
|
|
152
|
+
async fn read_file(&self, path: &str) -> Result<String>;
|
|
153
|
+
|
|
154
|
+
/// Write a file
|
|
155
|
+
async fn write_file(&self, path: &str, content: &str) -> Result<()>;
|
|
156
|
+
|
|
157
|
+
/// Upload a file to the runtime
|
|
158
|
+
async fn upload_file(&self, local_path: &str, remote_path: &str) -> Result<()>;
|
|
159
|
+
|
|
160
|
+
/// Get current working directory
|
|
161
|
+
fn get_cwd(&self) -> Option<String>;
|
|
162
|
+
|
|
163
|
+
/// Check if runtime is alive
|
|
164
|
+
fn is_alive(&self) -> bool;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// Simple local runtime for testing
|
|
168
|
+
pub struct LocalRuntime {
|
|
169
|
+
cwd: String,
|
|
170
|
+
alive: bool,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
impl LocalRuntime {
|
|
174
|
+
pub fn new(cwd: impl Into<String>) -> Self {
|
|
175
|
+
Self {
|
|
176
|
+
cwd: cwd.into(),
|
|
177
|
+
alive: true,
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#[async_trait]
|
|
183
|
+
impl Runtime for LocalRuntime {
|
|
184
|
+
async fn execute_bash(&self, action: BashAction) -> Result<BashActionResult> {
|
|
185
|
+
use std::process::Command;
|
|
186
|
+
|
|
187
|
+
let output = Command::new("sh")
|
|
188
|
+
.arg("-c")
|
|
189
|
+
.arg(&action.command)
|
|
190
|
+
.current_dir(action.cwd.as_ref().unwrap_or(&self.cwd))
|
|
191
|
+
.output()
|
|
192
|
+
.map_err(|e| SWEAgentError::RuntimeError(e.to_string()))?;
|
|
193
|
+
|
|
194
|
+
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
|
195
|
+
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
|
196
|
+
|
|
197
|
+
Ok(BashActionResult {
|
|
198
|
+
output: if stderr.is_empty() {
|
|
199
|
+
stdout
|
|
200
|
+
} else {
|
|
201
|
+
format!("{}\n{}", stdout, stderr)
|
|
202
|
+
},
|
|
203
|
+
exit_code: output.status.code().unwrap_or(-1),
|
|
204
|
+
timed_out: false,
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async fn interrupt_bash(&self) -> Result<()> {
|
|
209
|
+
// Not applicable for local runtime
|
|
210
|
+
Ok(())
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async fn read_file(&self, path: &str) -> Result<String> {
|
|
214
|
+
std::fs::read_to_string(path).map_err(|e| SWEAgentError::IoError(e.to_string()))
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async fn write_file(&self, path: &str, content: &str) -> Result<()> {
|
|
218
|
+
std::fs::write(path, content).map_err(|e| SWEAgentError::IoError(e.to_string()))
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async fn upload_file(&self, local_path: &str, remote_path: &str) -> Result<()> {
|
|
222
|
+
std::fs::copy(local_path, remote_path)
|
|
223
|
+
.map(|_| ())
|
|
224
|
+
.map_err(|e| SWEAgentError::IoError(e.to_string()))
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
fn get_cwd(&self) -> Option<String> {
|
|
228
|
+
Some(self.cwd.clone())
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
fn is_alive(&self) -> bool {
|
|
232
|
+
self.alive
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Re-export
|
|
237
|
+
pub use LocalRuntime as AbstractRuntime;
|