├── .devcontainer ├── README.MD ├── devcontainer.json └── on_create.sh ├── .dockerignore ├── .gitattributes ├── .github ├── .codecov.yml ├── ISSUE_TEMPLATE │ ├── bug_template.yml │ ├── feature_request.md │ └── technical_proposal.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── clean-up.yml │ ├── deploy-docs.yml │ ├── dummy-agent-test.yml │ ├── eval-runner.yml │ ├── fe-unit-tests.yml │ ├── ghcr-build.yml │ ├── lint-fix.yml │ ├── lint.yml │ ├── openhands-resolver.yml │ ├── py-unit-tests-mac.yml │ ├── py-unit-tests.yml │ ├── pypi-release.yml │ ├── review-pr.yml │ └── stale.yml ├── .gitignore ├── .openhands_instructions ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CREDITS.md ├── Development.md ├── ISSUE_TRIAGE.md ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── build.sh ├── compose.yml ├── config.template.toml ├── containers ├── README.md ├── app │ ├── Dockerfile │ ├── config.sh │ └── entrypoint.sh ├── build.sh ├── dev │ ├── Dockerfile │ ├── README.md │ ├── compose.yml │ └── dev.sh ├── e2b-sandbox │ ├── Dockerfile │ ├── README.md │ └── e2b.toml └── runtime │ ├── README.md │ └── config.sh ├── dev_config └── python │ ├── .pre-commit-config.yaml │ ├── mypy.ini │ └── ruff.toml ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── docusaurus.config.ts ├── i18n │ ├── fr │ │ ├── code.json │ │ ├── docusaurus-plugin-content-blog │ │ │ └── options.json │ │ ├── docusaurus-plugin-content-docs │ │ │ ├── current.json │ │ │ └── current │ │ │ │ ├── python │ │ │ │ ├── python.md │ │ │ │ └── sidebar.json │ │ │ │ └── usage │ │ │ │ ├── about.md │ │ │ │ ├── agents.md │ │ │ │ ├── architecture.mdx │ │ │ │ ├── architecture │ │ │ │ ├── backend.mdx │ │ │ │ └── runtime.md │ │ │ │ ├── custom_sandbox_guide.md │ │ │ │ ├── feedback.md │ │ │ │ ├── getting-started.mdx │ │ │ │ ├── how-to │ │ │ │ ├── cli-mode.md │ │ │ │ ├── custom-sandbox-guide.md │ │ │ │ ├── debugging.md │ │ │ │ ├── evaluation-harness.md │ │ │ │ ├── github-action.md │ │ │ │ ├── gui-mode.md │ │ │ │ ├── headless-mode.md │ │ │ │ └── openshift-example.md │ │ │ │ ├── installation.mdx │ │ │ │ ├── intro.mdx │ │ │ │ ├── llms │ │ │ │ ├── azure-llms.md │ │ │ │ ├── azureLLMs.md │ │ │ │ ├── google-llms.md │ │ │ │ ├── googleLLMs.md │ │ │ │ ├── groq.md │ │ │ │ ├── llms.md │ │ │ │ ├── local-llms.md │ │ │ │ ├── localLLMs.md │ │ │ │ ├── openai-llms.md │ │ │ │ └── openrouter.md │ │ │ │ ├── prompting-best-practices.md │ │ │ │ ├── troubleshooting │ │ │ │ ├── troubleshooting.md │ │ │ │ └── windows.md │ │ │ │ └── upgrade-guide.md │ │ └── docusaurus-theme-classic │ │ │ └── navbar.json │ └── zh-Hans │ │ ├── code.json │ │ ├── docusaurus-plugin-content-blog │ │ └── options.json │ │ ├── docusaurus-plugin-content-docs │ │ ├── current.json │ │ └── current │ │ │ ├── python │ │ │ ├── python.md │ │ │ └── sidebar.json │ │ │ └── usage │ │ │ ├── about.md │ │ │ ├── agents.md │ │ │ ├── architecture.mdx │ │ │ ├── architecture │ │ │ ├── backend.mdx │ │ │ └── runtime.md │ │ │ ├── custom_sandbox_guide.md │ │ │ ├── feedback.md │ │ │ ├── getting-started.mdx │ │ │ ├── how-to │ │ │ ├── cli-mode.md │ │ │ ├── custom-sandbox-guide.md │ │ │ ├── debugging.md │ │ │ ├── evaluation-harness.md │ │ │ ├── github-action.md │ │ │ ├── gui-mode.md │ │ │ ├── headless-mode.md │ │ │ └── openshift-example.md │ │ │ ├── installation.mdx │ │ │ ├── intro.mdx │ │ │ ├── llms │ │ │ ├── azure-llms.md │ │ │ ├── azureLLMs.md │ │ │ ├── google-llms.md │ │ │ ├── googleLLMs.md │ │ │ ├── groq.md │ │ │ ├── llms.md │ │ │ ├── local-llms.md │ │ │ ├── localLLMs.md │ │ │ ├── openai-llms.md │ │ │ └── openrouter.md │ │ │ ├── prompting-best-practices.md │ │ │ ├── troubleshooting │ │ │ ├── troubleshooting.md │ │ │ └── windows.md │ │ │ └── upgrade-guide.md │ │ └── docusaurus-theme-classic │ │ └── navbar.json ├── modules │ ├── python │ │ ├── python.md │ │ └── sidebar.json │ └── usage │ │ ├── about.md │ │ ├── agents.md │ │ ├── architecture │ │ ├── backend.mdx │ │ └── runtime.md │ │ ├── feedback.md │ │ ├── getting-started.mdx │ │ ├── how-to │ │ ├── cli-mode.md │ │ ├── custom-sandbox-guide.md │ │ ├── debugging.md │ │ ├── evaluation-harness.md │ │ ├── github-action.md │ │ ├── gui-mode.md │ │ ├── headless-mode.md │ │ └── openshift-example.md │ │ ├── installation.mdx │ │ ├── llms │ │ ├── azure-llms.md │ │ ├── google-llms.md │ │ ├── groq.md │ │ ├── litellm-proxy.md │ │ ├── llms.md │ │ ├── local-llms.md │ │ ├── openai-llms.md │ │ └── openrouter.md │ │ ├── prompting-best-practices.md │ │ ├── runtimes.md │ │ ├── troubleshooting │ │ ├── troubleshooting.md │ │ └── windows.md │ │ └── upgrade-guide.md ├── package-lock.json ├── package.json ├── plugins │ └── tailwind-config.cjs ├── sidebars.ts ├── src │ ├── components │ │ ├── CustomFooter.tsx │ │ ├── Demo │ │ │ ├── Demo.tsx │ │ │ └── index.module.css │ │ └── HomepageHeader │ │ │ └── HomepageHeader.tsx │ ├── css │ │ ├── custom.css │ │ ├── footer.css │ │ └── homepageHeader.css │ ├── pages │ │ ├── _footer.tsx │ │ └── index.tsx │ └── theme │ │ └── Layout │ │ └── index.tsx ├── static │ ├── .nojekyll │ └── img │ │ ├── backend_architecture.png │ │ ├── backend_architecture.puml │ │ ├── backend_architecture.svg │ │ ├── logo-square.png │ │ ├── logo.png │ │ ├── results.png │ │ ├── screenshot.png │ │ ├── settings-advanced.png │ │ ├── settings-screenshot.png │ │ ├── system_architecture.png │ │ ├── system_architecture.puml │ │ ├── system_architecture.svg │ │ ├── system_architecture_overview.png │ │ └── teaser.mp4 ├── translation_cache.json ├── translation_updater.py ├── tsconfig.json └── yarn.lock ├── evaluation ├── EDA │ ├── README.md │ ├── game.py │ ├── run_infer.py │ └── scripts │ │ └── run_infer.sh ├── README.md ├── __init__.py ├── agent_bench │ ├── README.md │ ├── __init__.py │ ├── helper.py │ ├── run_infer.py │ └── scripts │ │ ├── run_infer.sh │ │ └── summarise_results.py ├── aider_bench │ ├── README.md │ ├── create_dataset.py │ ├── helper.py │ ├── run_infer.py │ └── scripts │ │ ├── run_infer.sh │ │ └── summarize_results.py ├── biocoder │ ├── README.md │ ├── run_infer.py │ ├── scripts │ │ ├── run_infer.sh │ │ └── setup │ │ │ ├── copy_changed_code.py │ │ │ └── remove_code.py │ └── utils.py ├── bird │ ├── README.md │ ├── __init__.py │ ├── run_infer.py │ └── scripts │ │ └── run_infer.sh ├── browsing_delegation │ ├── README.md │ ├── run_infer.py │ └── scripts │ │ └── run_infer.sh ├── discoverybench │ ├── README.md │ ├── eval_utils │ │ ├── README.md │ │ ├── __init__.py │ │ ├── eval_w_subhypo_gen.py │ │ ├── lm_utils.py │ │ ├── openai_helpers.py │ │ ├── openai_semantic_gen_prompts.py │ │ └── response_parser.py │ ├── run_infer.py │ └── scripts │ │ └── run_infer.sh ├── gaia │ ├── README.md │ ├── get_score.py │ ├── run_infer.py │ ├── scorer.py │ └── scripts │ │ └── run_infer.sh ├── gorilla │ ├── README.md │ ├── ast_eval_hf.py │ ├── ast_eval_tf.py │ ├── ast_eval_th.py │ ├── run_infer.py │ ├── scripts │ │ └── run_infer.sh │ └── utils.py ├── gpqa │ ├── README.md │ ├── __init__.py │ ├── run_infer.py │ └── scripts │ │ └── run_infer.sh ├── humanevalfix │ ├── README.md │ ├── __init__.py │ ├── run_infer.py │ └── scripts │ │ └── run_infer.sh ├── integration_tests │ ├── README.md │ ├── __init__.py │ ├── run_infer.py │ ├── scripts │ │ └── run_infer.sh │ └── tests │ │ ├── __init__.py │ │ ├── base.py │ │ ├── t01_fix_simple_typo.py │ │ ├── t02_add_bash_hello.py │ │ ├── t03_jupyter_write_file.py │ │ ├── t04_git_staging.py │ │ ├── t05_simple_browsing.py │ │ └── t06_github_pr_browsing.py ├── logic_reasoning │ ├── .cache_program │ │ ├── facts.kfb │ │ └── rules.krb │ ├── Dockerfile │ ├── README.md │ ├── __init__.py │ ├── instruction.txt │ ├── logic_inference.py │ ├── run_infer.py │ └── scripts │ │ └── run_infer.sh ├── miniwob │ ├── Dockerfile │ ├── README.md │ ├── get_avg_reward.py │ ├── run_infer.py │ └── scripts │ │ └── run_infer.sh ├── mint │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── config_variables.py │ ├── datatypes.py │ ├── env.py │ ├── prompts │ │ ├── __init__.py │ │ └── template_with_tool.txt │ ├── requirements.txt │ ├── run_infer.py │ ├── scripts │ │ └── run_infer.sh │ ├── tasks │ │ ├── __init__.py │ │ ├── base.py │ │ ├── codegen.py │ │ ├── in_context_examples │ │ │ ├── humaneval │ │ │ │ └── with_tool.txt │ │ │ ├── mbpp │ │ │ │ └── with_tool.txt │ │ │ └── reasoning │ │ │ │ └── with_tool.txt │ │ └── reasoning.py │ └── utils.py ├── ml_bench │ ├── README.md │ ├── __init__.py │ ├── run_analysis.py │ ├── run_infer.py │ └── scripts │ │ ├── cleanup.sh │ │ ├── run_analysis.sh │ │ ├── run_infer.sh │ │ └── summarise_results.py ├── regression │ ├── .gitignore │ ├── README.md │ ├── cases │ │ ├── client-server │ │ │ └── task.txt │ │ ├── express │ │ │ └── task.txt │ │ ├── hello-name │ │ │ ├── start │ │ │ │ └── hello_world.sh │ │ │ └── task.txt │ │ ├── hello-world │ │ │ ├── task.txt │ │ │ └── test_hello_world.py │ │ ├── node-cli-rewrite │ │ │ ├── start │ │ │ │ ├── commands │ │ │ │ │ ├── length.py │ │ │ │ │ ├── lowercase.py │ │ │ │ │ ├── reverse.py │ │ │ │ │ ├── scramble.py │ │ │ │ │ ├── spongebob.py │ │ │ │ │ └── uppercase.py │ │ │ │ └── string_cli.py │ │ │ └── task.txt │ │ ├── python-cli-help │ │ │ ├── start │ │ │ │ ├── commands │ │ │ │ │ ├── length.py │ │ │ │ │ ├── lowercase.py │ │ │ │ │ ├── reverse.py │ │ │ │ │ ├── scramble.py │ │ │ │ │ ├── spongebob.py │ │ │ │ │ └── uppercase.py │ │ │ │ └── string_cli.py │ │ │ └── task.txt │ │ ├── python-cli │ │ │ └── task.txt │ │ ├── react-todo │ │ │ └── task.txt │ │ └── server-test │ │ │ ├── start │ │ │ └── server.py │ │ │ └── task.txt │ ├── conftest.py │ └── run_tests.py ├── scienceagentbench │ ├── Dockerfile │ ├── Dockerfile.evaluator │ ├── README.md │ ├── post_proc.py │ ├── run_infer.py │ └── scripts │ │ └── run_infer.sh ├── static │ └── example_task_1.png ├── swe_bench │ ├── README.md │ ├── __init__.py │ ├── eval_infer.py │ ├── examples │ │ ├── example_agent_output.jsonl │ │ └── example_model_output.json │ ├── prompt.py │ ├── run_infer.py │ └── scripts │ │ ├── cleanup_remote_runtime.sh │ │ ├── docker │ │ ├── all-swebench-full-instance-images.txt │ │ ├── all-swebench-lite-instance-images.txt │ │ ├── pull_all_eval_docker.sh │ │ ├── push_docker_instance_images.py │ │ └── push_eval_docker.sh │ │ ├── eval │ │ ├── compare_outputs.py │ │ ├── convert_oh_folder_to_swebench_submission.sh │ │ ├── convert_oh_output_to_md.py │ │ ├── convert_oh_output_to_swe_json.py │ │ ├── download_gold_patch.py │ │ ├── summarize_outputs.py │ │ └── update_output_with_eval.py │ │ ├── eval_infer.sh │ │ ├── eval_infer_remote.sh │ │ ├── run_infer.sh │ │ └── setup │ │ ├── compare_patch_filename.py │ │ ├── instance_swe_entry.sh │ │ ├── prepare_swe_utils.sh │ │ └── swe_entry.sh ├── toolqa │ ├── Dockerfile │ ├── README.md │ ├── run_infer.py │ ├── scripts │ │ └── run_infer.sh │ └── utils.py ├── utils │ ├── shared.py │ └── version_control.sh └── webarena │ ├── README.md │ ├── __init__.py │ ├── get_success_rate.py │ ├── run_infer.py │ └── scripts │ └── run_infer.sh ├── frontend ├── .env.sample ├── .eslintrc ├── .gitignore ├── .husky │ └── pre-commit ├── .npmrc ├── .prettierrc.json ├── README.md ├── __tests__ │ ├── clear-session.test.ts │ ├── components │ │ ├── Browser.test.tsx │ │ ├── chat-message.test.tsx │ │ ├── chat │ │ │ ├── chat-input.test.tsx │ │ │ └── chat-interface.test.tsx │ │ ├── context-menu │ │ │ ├── account-settings-context-menu.test.tsx │ │ │ └── context-menu-list-item.test.tsx │ │ ├── feedback-actions.test.tsx │ │ ├── feedback-form.test.tsx │ │ ├── file-explorer │ │ │ ├── ExplorerTree.test.tsx │ │ │ ├── FileExplorer.test.tsx │ │ │ └── TreeNode.test.tsx │ │ ├── image-preview.test.tsx │ │ ├── interactive-chat-box.test.tsx │ │ ├── modals │ │ │ ├── ConnectToGitHubByTokenModal.test.tsx │ │ │ ├── base-modal │ │ │ │ └── BaseModal.test.tsx │ │ │ └── settings │ │ │ │ └── ModelSelector.test.tsx │ │ ├── project-menu │ │ │ └── ProjectMenuCard.test.tsx │ │ ├── settings │ │ │ ├── ai-config-form.test.tsx │ │ │ ├── dropdown-input.test.tsx │ │ │ └── model-selector.test.tsx │ │ ├── suggestion-item.test.tsx │ │ ├── suggestions.test.tsx │ │ ├── terminal │ │ │ └── Terminal.test.tsx │ │ ├── upload-image-input.test.tsx │ │ ├── user-actions.test.tsx │ │ └── user-avatar.test.tsx │ ├── hooks │ │ ├── use-click-outside-element.test.tsx │ │ ├── use-rate.test.ts │ │ └── use-terminal.test.tsx │ ├── initial-query.test.tsx │ ├── routes │ │ ├── _oh.app.test.tsx │ │ └── _oh.test.tsx │ ├── services │ │ ├── auth.test.ts │ │ └── settings.test.ts │ └── utils │ │ ├── cache.test.ts │ │ ├── extractModelAndProvider.test.ts │ │ ├── format-time-delta.test.ts │ │ ├── formatMs.test.ts │ │ ├── isNumber.test.ts │ │ ├── mapProvider.test.ts │ │ ├── organizeModelsAndProviders.test.ts │ │ ├── parseGithubUrl.test.ts │ │ ├── parseTerminalOutput.test.ts │ │ ├── storage.test.ts │ │ └── utils.test.ts ├── global.d.ts ├── index.html ├── package-lock.json ├── package.json ├── playwright.config.ts ├── postcss.config.js ├── public │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── beep.wav │ ├── browserconfig.xml │ ├── config.json │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── mockServiceWorker.js │ ├── mstile-150x150.png │ ├── robots.txt │ ├── safari-pinned-tab.svg │ └── site.webmanifest ├── scripts │ ├── detect-node-version.js │ └── make-i18n-translations.cjs ├── src │ ├── api │ │ ├── github.ts │ │ ├── open-hands.ts │ │ ├── open-hands.types.ts │ │ └── open-hands.utils.ts │ ├── assets │ │ ├── arrow.tsx │ │ ├── branding │ │ │ ├── all-hands-logo-spark.svg │ │ │ ├── all-hands-logo.svg │ │ │ └── github-logo.svg │ │ ├── calendar.tsx │ │ ├── chevron-left.tsx │ │ ├── chevron-right.tsx │ │ ├── cmd-line.tsx │ │ ├── cog-tooth.tsx │ │ ├── confirm.tsx │ │ ├── earth.tsx │ │ ├── logo.png │ │ ├── pause.tsx │ │ ├── pencil.tsx │ │ ├── play.tsx │ │ ├── reject.tsx │ │ ├── stop.tsx │ │ └── vscode-alt.svg │ ├── components │ │ ├── AgentControlBar.tsx │ │ ├── AgentStatusBar.tsx │ │ ├── Browser.tsx │ │ ├── FileIcons.tsx │ │ ├── FolderIcon.tsx │ │ ├── IconButton.tsx │ │ ├── Jupyter.tsx │ │ ├── analytics-consent-form-modal.tsx │ │ ├── attach-image-label.tsx │ │ ├── buttons │ │ │ └── ModalButton.tsx │ │ ├── chat-input.tsx │ │ ├── chat-interface.tsx │ │ ├── chat-message.tsx │ │ ├── chat │ │ │ ├── ConfirmationButtons.tsx │ │ │ ├── TypingIndicator.tsx │ │ │ └── message.d.ts │ │ ├── container.tsx │ │ ├── context-menu │ │ │ ├── account-settings-context-menu.tsx │ │ │ ├── context-menu-list-item.tsx │ │ │ ├── context-menu-separator.tsx │ │ │ └── context-menu.tsx │ │ ├── continue-button.tsx │ │ ├── controls.tsx │ │ ├── editor-actions.tsx │ │ ├── error-message.tsx │ │ ├── error-toast.tsx │ │ ├── event-handler.tsx │ │ ├── feedback-actions.tsx │ │ ├── feedback-form.tsx │ │ ├── feedback-modal.tsx │ │ ├── file-explorer │ │ │ ├── ExplorerTree.tsx │ │ │ ├── FileExplorer.tsx │ │ │ └── TreeNode.tsx │ │ ├── form │ │ │ ├── FormFieldset.tsx │ │ │ ├── custom-input.tsx │ │ │ └── settings-form.tsx │ │ ├── github-repositories-suggestion-box.tsx │ │ ├── image-carousel.tsx │ │ ├── image-preview.tsx │ │ ├── interactive-chat-box.tsx │ │ ├── markdown │ │ │ ├── code.tsx │ │ │ └── list.tsx │ │ ├── modals │ │ │ ├── AccountSettingsModal.tsx │ │ │ ├── ConnectToGitHubByTokenModal.tsx │ │ │ ├── LoadingProject.tsx │ │ │ ├── ModalBody.tsx │ │ │ ├── base-modal │ │ │ │ ├── BaseModal.tsx │ │ │ │ ├── FooterContent.tsx │ │ │ │ └── HeaderContent.tsx │ │ │ ├── confirmation-modals │ │ │ │ ├── BaseModal.tsx │ │ │ │ └── danger-modal.tsx │ │ │ ├── connect-to-github-modal.tsx │ │ │ ├── modal-backdrop.tsx │ │ │ ├── security │ │ │ │ ├── Security.tsx │ │ │ │ └── invariant │ │ │ │ │ ├── Invariant.tsx │ │ │ │ │ └── assets │ │ │ │ │ └── logo.tsx │ │ │ └── settings │ │ │ │ └── ModelSelector.tsx │ │ ├── project-menu │ │ │ ├── ProjectMenuCard.tsx │ │ │ ├── project-menu-details-placeholder.tsx │ │ │ ├── project-menu-details.tsx │ │ │ └── project.menu-card-context-menu.tsx │ │ ├── scroll-button.tsx │ │ ├── scroll-to-bottom-button.tsx │ │ ├── suggestion-bubble.tsx │ │ ├── suggestion-item.tsx │ │ ├── suggestions.tsx │ │ ├── terminal │ │ │ └── Terminal.tsx │ │ ├── upload-image-input.tsx │ │ ├── user-actions.tsx │ │ ├── user-avatar.tsx │ │ └── waitlist-modal.tsx │ ├── context │ │ ├── files.tsx │ │ └── ws-client-provider.tsx │ ├── entry.client.tsx │ ├── hooks │ │ ├── useClickOutsideElement.ts │ │ ├── useScrollToBottom.ts │ │ └── useTerminal.ts │ ├── i18n │ │ ├── index.ts │ │ └── translation.json │ ├── icons │ │ ├── arrow-send.svg │ │ ├── build-it.svg │ │ ├── checkmark.svg │ │ ├── chevron-double-right.svg │ │ ├── clip.svg │ │ ├── clipboard.svg │ │ ├── close.svg │ │ ├── cloud-connection.svg │ │ ├── code.svg │ │ ├── copy.svg │ │ ├── default-user.svg │ │ ├── docs.svg │ │ ├── ellipsis-h.svg │ │ ├── external-link.svg │ │ ├── globe.svg │ │ ├── lightbulb.svg │ │ ├── list-type-number.svg │ │ ├── loading-outer.svg │ │ ├── message.svg │ │ ├── new-project.svg │ │ ├── play.svg │ │ ├── refresh.svg │ │ ├── send.svg │ │ ├── thumbs-down.svg │ │ └── thumbs-up.svg │ ├── index.css │ ├── mocks │ │ ├── browser.ts │ │ ├── handlers.ts │ │ ├── handlers.ws.ts │ │ └── node.ts │ ├── react-app-env.d.ts │ ├── root.tsx │ ├── routes │ │ ├── _oh._index │ │ │ ├── github-repo-selector.tsx │ │ │ ├── hero-heading.tsx │ │ │ ├── route.tsx │ │ │ ├── suggestion-box.tsx │ │ │ └── task-form.tsx │ │ ├── _oh.app._index │ │ │ ├── code-editor-component.tsx │ │ │ └── route.tsx │ │ ├── _oh.app.browser.tsx │ │ ├── _oh.app.jupyter.tsx │ │ ├── _oh.app.tsx │ │ ├── _oh.tsx │ │ ├── end-session.ts │ │ ├── login.ts │ │ ├── logout.ts │ │ ├── oauth.github.callback.tsx │ │ ├── set-consent.ts │ │ └── settings.ts │ ├── services │ │ ├── actions.ts │ │ ├── agentStateService.ts │ │ ├── api.ts │ │ ├── auth.ts │ │ ├── chatService.ts │ │ ├── observations.ts │ │ ├── options.ts │ │ ├── settings.ts │ │ └── terminalService.ts │ ├── sessions.ts │ ├── state │ │ ├── agentSlice.tsx │ │ ├── browserSlice.ts │ │ ├── chatSlice.ts │ │ ├── codeSlice.ts │ │ ├── commandSlice.ts │ │ ├── file-state-slice.ts │ │ ├── initial-query-slice.ts │ │ ├── jupyterSlice.ts │ │ ├── securityAnalyzerSlice.ts │ │ └── statusSlice.ts │ ├── store.ts │ ├── tailwind.css │ ├── types │ │ ├── ActionType.tsx │ │ ├── AgentState.tsx │ │ ├── ConfigType.tsx │ │ ├── Message.tsx │ │ ├── ObservationType.tsx │ │ ├── ResponseType.tsx │ │ ├── TabOption.tsx │ │ ├── core │ │ │ ├── actions.ts │ │ │ ├── base.ts │ │ │ ├── index.ts │ │ │ ├── observations.ts │ │ │ └── variances.ts │ │ └── github.d.ts │ └── utils │ │ ├── base64-to-blob.ts │ │ ├── beep.tsx │ │ ├── cache.ts │ │ ├── clear-session.ts │ │ ├── convert-image-to-base-64.ts │ │ ├── convert-zip-to-base64.ts │ │ ├── display-error-toast.tsx │ │ ├── download-workspace.ts │ │ ├── event-logger.ts │ │ ├── extractModelAndProvider.ts │ │ ├── format-time-delta.ts │ │ ├── formatMs.ts │ │ ├── generate-github-auth-url.ts │ │ ├── get-random-key.ts │ │ ├── isNumber.ts │ │ ├── mapProvider.ts │ │ ├── organizeModelsAndProviders.ts │ │ ├── parseGithubUrl.ts │ │ ├── parseTerminalOutput.ts │ │ ├── storage.tsx │ │ ├── suggestions │ │ ├── index.ts │ │ ├── non-repo-suggestions.ts │ │ └── repo-suggestions.ts │ │ ├── toast.tsx │ │ ├── use-effect-once.ts │ │ ├── use-rate.ts │ │ ├── user-is-authenticated.ts │ │ ├── utils.ts │ │ └── verified-models.ts ├── tailwind.config.js ├── test-utils.tsx ├── tests │ ├── fixtures │ │ └── project.zip │ └── redirect.spec.ts ├── tsconfig.json ├── vite-env.d.ts ├── vite.config.ts └── vitest.setup.ts ├── openhands ├── README.md ├── __init__.py ├── agenthub │ ├── README.md │ ├── __init__.py │ ├── browsing_agent │ │ ├── README.md │ │ ├── __init__.py │ │ ├── browsing_agent.py │ │ ├── prompt.py │ │ ├── response_parser.py │ │ └── utils.py │ ├── codeact_agent │ │ ├── README.md │ │ ├── __init__.py │ │ ├── codeact_agent.py │ │ ├── function_calling.py │ │ ├── micro │ │ │ ├── flarglebargle.md │ │ │ └── github.md │ │ └── prompts │ │ │ ├── system_prompt.j2 │ │ │ └── user_prompt.j2 │ ├── codeact_swe_agent │ │ ├── README.md │ │ ├── __init__.py │ │ ├── action_parser.py │ │ ├── codeact_swe_agent.py │ │ ├── prompt.py │ │ └── response_parser.py │ ├── delegator_agent │ │ ├── __init__.py │ │ └── agent.py │ ├── dummy_agent │ │ ├── __init__.py │ │ └── agent.py │ ├── micro │ │ ├── README.md │ │ ├── _instructions │ │ │ ├── actions │ │ │ │ ├── browse.md │ │ │ │ ├── delegate.md │ │ │ │ ├── finish.md │ │ │ │ ├── kill.md │ │ │ │ ├── message.md │ │ │ │ ├── read.md │ │ │ │ ├── reject.md │ │ │ │ ├── run.md │ │ │ │ └── write.md │ │ │ ├── format │ │ │ │ └── action.md │ │ │ └── history_truncated.md │ │ ├── agent.py │ │ ├── coder │ │ │ ├── agent.yaml │ │ │ └── prompt.md │ │ ├── commit_writer │ │ │ ├── README.md │ │ │ ├── agent.yaml │ │ │ └── prompt.md │ │ ├── instructions.py │ │ ├── manager │ │ │ ├── agent.yaml │ │ │ └── prompt.md │ │ ├── math_agent │ │ │ ├── agent.yaml │ │ │ └── prompt.md │ │ ├── postgres_agent │ │ │ ├── agent.yaml │ │ │ └── prompt.md │ │ ├── registry.py │ │ ├── repo_explorer │ │ │ ├── agent.yaml │ │ │ └── prompt.md │ │ ├── study_repo_for_task │ │ │ ├── agent.yaml │ │ │ └── prompt.md │ │ ├── typo_fixer_agent │ │ │ ├── agent.yaml │ │ │ └── prompt.md │ │ └── verifier │ │ │ ├── agent.yaml │ │ │ └── prompt.md │ └── planner_agent │ │ ├── __init__.py │ │ ├── agent.py │ │ ├── prompt.py │ │ └── response_parser.py ├── controller │ ├── __init__.py │ ├── action_parser.py │ ├── agent.py │ ├── agent_controller.py │ ├── state │ │ ├── state.py │ │ └── task.py │ └── stuck.py ├── core │ ├── cli.py │ ├── config │ │ ├── README.md │ │ ├── __init__.py │ │ ├── agent_config.py │ │ ├── app_config.py │ │ ├── config_utils.py │ │ ├── llm_config.py │ │ ├── sandbox_config.py │ │ ├── security_config.py │ │ └── utils.py │ ├── const │ │ └── guide_url.py │ ├── download.py │ ├── exceptions.py │ ├── logger.py │ ├── loop.py │ ├── main.py │ ├── message.py │ ├── schema │ │ ├── __init__.py │ │ ├── action.py │ │ ├── agent.py │ │ ├── config.py │ │ └── observation.py │ └── utils │ │ ├── __init__.py │ │ └── json.py ├── events │ ├── __init__.py │ ├── action │ │ ├── __init__.py │ │ ├── action.py │ │ ├── agent.py │ │ ├── browse.py │ │ ├── commands.py │ │ ├── empty.py │ │ ├── files.py │ │ ├── message.py │ │ └── tasks.py │ ├── event.py │ ├── observation │ │ ├── __init__.py │ │ ├── agent.py │ │ ├── browse.py │ │ ├── commands.py │ │ ├── delegate.py │ │ ├── empty.py │ │ ├── error.py │ │ ├── files.py │ │ ├── observation.py │ │ ├── reject.py │ │ └── success.py │ ├── serialization │ │ ├── __init__.py │ │ ├── action.py │ │ ├── event.py │ │ ├── observation.py │ │ └── utils.py │ ├── stream.py │ ├── tool.py │ └── utils.py ├── linter │ └── __init__.py ├── llm │ ├── __init__.py │ ├── async_llm.py │ ├── bedrock.py │ ├── debug_mixin.py │ ├── fn_call_converter.py │ ├── llm.py │ ├── metrics.py │ ├── retry_mixin.py │ └── streaming_llm.py ├── memory │ ├── README.md │ ├── __init__.py │ ├── condenser.py │ └── memory.py ├── py.typed ├── resolver │ ├── README.md │ ├── __init__.py │ ├── examples │ │ └── openhands-resolver.yml │ ├── github_issue.py │ ├── io_utils.py │ ├── issue_definitions.py │ ├── patching │ │ ├── README.md │ │ ├── __init__.py │ │ ├── apply.py │ │ ├── exceptions.py │ │ ├── patch.py │ │ └── snippets.py │ ├── prompts │ │ ├── guess_success │ │ │ ├── issue-success-check.jinja │ │ │ ├── pr-feedback-check.jinja │ │ │ ├── pr-review-check.jinja │ │ │ └── pr-thread-check.jinja │ │ ├── repo_instructions │ │ │ ├── all-hands-ai___openhands-resolver.txt │ │ │ ├── all-hands-ai___openhands.txt │ │ │ └── rbren___rss-parser.txt │ │ └── resolve │ │ │ ├── basic-followup.jinja │ │ │ ├── basic-with-tests.jinja │ │ │ ├── basic.jinja │ │ │ └── pr-changes-summary.jinja │ ├── resolve_all_issues.py │ ├── resolve_issue.py │ ├── resolver_output.py │ ├── send_pull_request.py │ ├── utils.py │ └── visualize_resolver_output.py ├── runtime │ ├── README.md │ ├── __init__.py │ ├── action_execution_server.py │ ├── base.py │ ├── browser │ │ ├── __init__.py │ │ ├── browser_env.py │ │ └── utils.py │ ├── builder │ │ ├── __init__.py │ │ ├── base.py │ │ ├── docker.py │ │ └── remote.py │ ├── impl │ │ ├── e2b │ │ │ ├── README.md │ │ │ ├── e2b_runtime.py │ │ │ ├── filestore.py │ │ │ └── sandbox.py │ │ ├── eventstream │ │ │ ├── containers.py │ │ │ └── eventstream_runtime.py │ │ ├── modal │ │ │ └── modal_runtime.py │ │ ├── remote │ │ │ └── remote_runtime.py │ │ └── runloop │ │ │ ├── README.md │ │ │ └── runloop_runtime.py │ ├── plugins │ │ ├── __init__.py │ │ ├── agent_skills │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── agentskills.py │ │ │ ├── file_editor │ │ │ │ ├── README.md │ │ │ │ └── __init__.py │ │ │ ├── file_ops │ │ │ │ ├── __init__.py │ │ │ │ └── file_ops.py │ │ │ ├── file_reader │ │ │ │ ├── __init__.py │ │ │ │ └── file_readers.py │ │ │ └── utils │ │ │ │ ├── config.py │ │ │ │ └── dependency.py │ │ ├── jupyter │ │ │ ├── __init__.py │ │ │ └── execute_server.py │ │ ├── requirement.py │ │ └── vscode │ │ │ └── __init__.py │ └── utils │ │ ├── __init__.py │ │ ├── bash.py │ │ ├── command.py │ │ ├── edit.py │ │ ├── files.py │ │ ├── request.py │ │ ├── runtime_build.py │ │ ├── runtime_init.py │ │ ├── runtime_templates │ │ └── Dockerfile.j2 │ │ ├── singleton.py │ │ ├── system.py │ │ └── tenacity_stop.py ├── security │ ├── README.md │ ├── __init__.py │ ├── analyzer.py │ ├── invariant │ │ ├── __init__.py │ │ ├── analyzer.py │ │ ├── client.py │ │ ├── nodes.py │ │ ├── parser.py │ │ └── policies.py │ └── options.py ├── server │ ├── README.md │ ├── __init__.py │ ├── auth │ │ ├── __init__.py │ │ └── auth.py │ ├── data_models │ │ └── feedback.py │ ├── github.py │ ├── listen.py │ ├── middleware.py │ ├── mock │ │ ├── README.md │ │ └── listen.py │ ├── session │ │ ├── __init__.py │ │ ├── agent_session.py │ │ ├── conversation.py │ │ ├── manager.py │ │ └── session.py │ └── sheets_client.py ├── storage │ ├── __init__.py │ ├── files.py │ ├── google_cloud.py │ ├── local.py │ ├── memory.py │ └── s3.py └── utils │ ├── async_utils.py │ ├── chunk_localizer.py │ ├── embeddings.py │ ├── microagent.py │ ├── prompt.py │ ├── shutdown_listener.py │ └── tenacity_stop.py ├── poetry.lock ├── pydoc-markdown.yml ├── pyproject.toml ├── pytest.ini └── tests ├── runtime ├── conftest.py ├── test_bash.py ├── test_browsing.py ├── test_edit.py ├── test_env_vars.py ├── test_images.py ├── test_ipython.py └── test_stress_remote_runtime.py ├── test_fileops.py └── unit ├── README.md ├── resolver ├── mock_output │ ├── output.jsonl │ └── repo │ │ └── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── PullRequestViewer.test.tsx │ │ └── PullRequestViewer.tsx ├── test_guess_success.py ├── test_issue_handler.py ├── test_issue_handler_error_handling.py ├── test_issue_references.py ├── test_pr_handler_guess_success.py ├── test_pr_title_escaping.py ├── test_resolve_issues.py └── test_send_pull_request.py ├── test_acompletion.py ├── test_action_serialization.py ├── test_agent_controller.py ├── test_agent_skill.py ├── test_arg_parser.py ├── test_async_utils.py ├── test_bash_parsing.py ├── test_browsing_agent_parser.py ├── test_chunk_localizer.py ├── test_codeact_agent.py ├── test_condenser.py ├── test_config.py ├── test_event_stream.py ├── test_is_stuck.py ├── test_json.py ├── test_listen.py ├── test_llm.py ├── test_llm_fncall_converter.py ├── test_logging.py ├── test_memory.py ├── test_message_serialization.py ├── test_micro_agents.py ├── test_microagent_utils.py ├── test_observation_serialization.py ├── test_prompt_caching.py ├── test_prompt_manager.py ├── test_response_parsing.py ├── test_runtime_build.py ├── test_security.py ├── test_storage.py └── test_truncation.py /.devcontainer/README.MD: -------------------------------------------------------------------------------- 1 | The files in this directory configure a development container for GitHub Codespaces. 2 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OpenHands Codespaces", 3 | "image": "mcr.microsoft.com/devcontainers/universal", 4 | "customizations":{ 5 | "vscode":{ 6 | "extensions": [ 7 | "ms-python.python" 8 | ] 9 | } 10 | }, 11 | "onCreateCommand": "sh ./.devcontainer/on_create.sh", 12 | "postCreateCommand": "make build", 13 | "postStartCommand": "USE_HOST_NETWORK=True nohup bash -c 'make run &'" 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.devcontainer/on_create.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | sudo apt update 3 | sudo apt install -y netcat 4 | sudo add-apt-repository -y ppa:deadsnakes/ppa 5 | sudo apt install -y python3.12 6 | curl -sSL https://install.python-poetry.org | python3.12 - 7 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | frontend/node_modules 2 | config.toml 3 | .envrc 4 | .env 5 | .git 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ipynb linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | wait_for_ci: true 4 | # our project is large, so 6 builds are typically uploaded. this waits till 5/6 5 | # See https://docs.codecov.com/docs/notifications#section-preventing-notifications-until-after-n-builds 6 | after_n_builds: 5 7 | 8 | coverage: 9 | status: 10 | patch: 11 | default: 12 | threshold: 100% # allow patch coverage to be lower than project coverage by any amount 13 | project: 14 | default: 15 | threshold: 5% # allow project coverage to drop at most 5% 16 | 17 | comment: false 18 | github_checks: 19 | annotations: false 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for OpenHands features 4 | title: '' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **What problem or use case are you trying to solve?** 11 | 12 | **Describe the UX of the solution you'd like** 13 | 14 | **Do you have thoughts on the technical implementation?** 15 | 16 | **Describe alternatives you've considered** 17 | 18 | **Additional context** 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/technical_proposal.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Technical Proposal 3 | about: Propose a new architecture or technology 4 | title: '' 5 | labels: 'proposal' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Summary** 11 | 12 | **Motivation** 13 | 14 | **Technical Design** 15 | 16 | **Alternatives to Consider** 17 | 18 | **Additional context** 19 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | **End-user friendly description of the problem this fixes or functionality that this introduces** 2 | 3 | - [ ] Include this change in the Release Notes. If checked, you must provide an **end-user friendly** description for your change below 4 | 5 | --- 6 | **Give a summary of what the PR does, explaining any non-trivial design decisions** 7 | 8 | 9 | 10 | --- 11 | **Link of any specific issues this addresses** 12 | -------------------------------------------------------------------------------- /.github/workflows/pypi-release.yml: -------------------------------------------------------------------------------- 1 | # Publishes the OpenHands PyPi package 2 | name: Publish PyPi Package 3 | 4 | # Triggered manually 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | reason: 9 | description: 'Reason for manual trigger' 10 | required: true 11 | default: '' 12 | 13 | jobs: 14 | release: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-python@v5 19 | with: 20 | python-version: 3.12 21 | - name: Install Poetry 22 | uses: snok/install-poetry@v1.4.1 23 | with: 24 | virtualenvs-in-project: true 25 | virtualenvs-path: ~/.virtualenvs 26 | - name: Install Poetry Dependencies 27 | run: poetry install --no-interaction --no-root 28 | - name: Build poetry project 29 | run: ./build.sh 30 | - name: publish 31 | run: poetry publish -u __token__ -p ${{ secrets.PYPI_TOKEN }} 32 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Exclude all Python bytecode files 2 | global-exclude *.pyc 3 | 4 | # Exclude Python cache directories 5 | global-exclude __pycache__ 6 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cp pyproject.toml poetry.lock openhands 5 | poetry build -v 6 | -------------------------------------------------------------------------------- /compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | services: 3 | openhands: 4 | build: 5 | context: ./ 6 | dockerfile: ./containers/app/Dockerfile 7 | image: openhands:latest 8 | container_name: openhands-app-${DATE:-} 9 | environment: 10 | - SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.14-nikolaik} 11 | - SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} 12 | - WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace} 13 | ports: 14 | - "3000:3000" 15 | extra_hosts: 16 | - "host.docker.internal:host-gateway" 17 | volumes: 18 | - /var/run/docker.sock:/var/run/docker.sock 19 | - ${WORKSPACE_BASE:-$PWD/workspace}:/opt/workspace_base 20 | pull_policy: build 21 | stdin_open: true 22 | tty: true 23 | -------------------------------------------------------------------------------- /containers/README.md: -------------------------------------------------------------------------------- 1 | # Docker Containers 2 | 3 | Each folder here contains a Dockerfile, and a config.sh describing how to build 4 | the images and where to push them. These images are built and pushed in GitHub Actions 5 | by the `ghcr.yml` workflow. 6 | 7 | ## Building Manually 8 | 9 | ```bash 10 | docker build -f containers/app/Dockerfile -t openhands . 11 | docker build -f containers/sandbox/Dockerfile -t sandbox . 12 | ``` 13 | -------------------------------------------------------------------------------- /containers/app/config.sh: -------------------------------------------------------------------------------- 1 | DOCKER_REGISTRY=ghcr.io 2 | DOCKER_ORG=all-hands-ai 3 | DOCKER_IMAGE=openhands 4 | DOCKER_BASE_DIR="." 5 | -------------------------------------------------------------------------------- /containers/dev/dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -o pipefail 3 | 4 | function get_docker() { 5 | echo "Docker is required to build and run OpenHands." 6 | echo "https://docs.docker.com/get-started/get-docker/" 7 | exit 1 8 | } 9 | 10 | function check_tools() { 11 | command -v docker &>/dev/null || get_docker 12 | } 13 | 14 | function exit_if_indocker() { 15 | if [ -f /.dockerenv ]; then 16 | echo "Running inside a Docker container. Exiting..." 17 | exit 1 18 | fi 19 | } 20 | 21 | # 22 | exit_if_indocker 23 | 24 | check_tools 25 | 26 | ## 27 | OPENHANDS_WORKSPACE=$(git rev-parse --show-toplevel) 28 | 29 | cd "$OPENHANDS_WORKSPACE/containers/dev/" || exit 1 30 | 31 | ## 32 | export BACKEND_HOST="0.0.0.0" 33 | # 34 | export SANDBOX_USER_ID=$(id -u) 35 | export WORKSPACE_BASE=${WORKSPACE_BASE:-$OPENHANDS_WORKSPACE/workspace} 36 | 37 | docker compose run --rm --service-ports "$@" dev 38 | 39 | ## 40 | -------------------------------------------------------------------------------- /containers/e2b-sandbox/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | # install basic packages 4 | RUN apt-get update && apt-get install -y \ 5 | curl \ 6 | wget \ 7 | git \ 8 | vim \ 9 | nano \ 10 | unzip \ 11 | zip \ 12 | python3 \ 13 | python3-pip \ 14 | python3-venv \ 15 | python3-dev \ 16 | build-essential \ 17 | openssh-server \ 18 | sudo \ 19 | && rm -rf /var/lib/apt/lists/* 20 | -------------------------------------------------------------------------------- /containers/e2b-sandbox/README.md: -------------------------------------------------------------------------------- 1 | # How to build custom E2B sandbox for OpenHands 2 | 3 | [E2B](https://e2b.dev) is an [open-source](https://github.com/e2b-dev/e2b) secure cloud environment (sandbox) made for running AI-generated code and agents. E2B offers [Python](https://pypi.org/project/e2b/) and [JS/TS](https://www.npmjs.com/package/e2b) SDK to spawn and control these sandboxes. 4 | 5 | 6 | 1. Install the CLI with NPM. 7 | ```sh 8 | npm install -g @e2b/cli@latest 9 | ``` 10 | Full CLI API is [here](https://e2b.dev/docs/cli/installation). 11 | 12 | 1. Build the sandbox 13 | ```sh 14 | e2b template build --dockerfile ./Dockerfile --name "openhands" 15 | ``` 16 | -------------------------------------------------------------------------------- /containers/e2b-sandbox/e2b.toml: -------------------------------------------------------------------------------- 1 | # This is a config for E2B sandbox template. 2 | # You can use 'template_id' (785n69crgahmz0lkdw9h) or 'template_name (openhands) from this config to spawn a sandbox: 3 | 4 | # Python SDK 5 | # from e2b import Sandbox 6 | # sandbox = Sandbox(template='openhands') 7 | 8 | # JS SDK 9 | # import { Sandbox } from 'e2b' 10 | # const sandbox = await Sandbox.create({ template: 'openhands' }) 11 | 12 | dockerfile = "Dockerfile" 13 | template_name = "openhands" 14 | template_id = "785n69crgahmz0lkdw9h" 15 | -------------------------------------------------------------------------------- /containers/runtime/README.md: -------------------------------------------------------------------------------- 1 | # Dynamically constructed Dockerfile 2 | 3 | This folder builds a runtime image (sandbox), which will use a dynamically generated `Dockerfile` 4 | that depends on the `base_image` **AND** a [Python source distribution](https://docs.python.org/3.10/distutils/sourcedist.html) that is based on the current commit of `openhands`. 5 | 6 | The following command will generate a `Dockerfile` file for `nikolaik/python-nodejs:python3.12-nodejs22` (the default base image), an updated `config.sh` and the runtime source distribution files/folders into `containers/runtime`: 7 | 8 | ```bash 9 | poetry run python3 openhands/runtime/utils/runtime_build.py \ 10 | --base_image nikolaik/python-nodejs:python3.12-nodejs22 \ 11 | --build_folder containers/runtime 12 | ``` 13 | -------------------------------------------------------------------------------- /containers/runtime/config.sh: -------------------------------------------------------------------------------- 1 | DOCKER_REGISTRY=ghcr.io 2 | DOCKER_ORG=all-hands-ai 3 | DOCKER_BASE_DIR="./containers/runtime" 4 | DOCKER_IMAGE=runtime 5 | # These variables will be appended by the runtime_build.py script 6 | # DOCKER_IMAGE_TAG= 7 | # DOCKER_IMAGE_SOURCE_TAG= 8 | -------------------------------------------------------------------------------- /dev_config/python/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | warn_unused_configs = True 3 | ignore_missing_imports = True 4 | check_untyped_defs = True 5 | explicit_package_bases = True 6 | warn_unreachable = True 7 | warn_redundant_casts = True 8 | no_implicit_optional = True 9 | strict_optional = True 10 | -------------------------------------------------------------------------------- /dev_config/python/ruff.toml: -------------------------------------------------------------------------------- 1 | [lint] 2 | select = [ 3 | "E", 4 | "W", 5 | "F", 6 | "I", 7 | "Q", 8 | "B", 9 | ] 10 | 11 | ignore = [ 12 | "E501", 13 | "B003", 14 | "B007", 15 | "B009", 16 | "B010", 17 | "B904", 18 | "B018", 19 | ] 20 | 21 | [lint.flake8-quotes] 22 | docstring-quotes = "double" 23 | inline-quotes = "single" 24 | 25 | [format] 26 | quote-style = "single" 27 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/i18n/fr/docusaurus-plugin-content-blog/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": { 3 | "message": "Blog", 4 | "description": "The title for the blog used in SEO" 5 | }, 6 | "description": { 7 | "message": "Blog", 8 | "description": "The description for the blog used in SEO" 9 | }, 10 | "sidebar.title": { 11 | "message": "Articles récents", 12 | "description": "The label for the left sidebar" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/i18n/fr/docusaurus-plugin-content-docs/current.json: -------------------------------------------------------------------------------- 1 | { 2 | "version.label": { 3 | "message": "Next", 4 | "description": "The label for version current" 5 | }, 6 | "sidebar.docsSidebar.category.🤖 Backends LLM": { 7 | "message": "🤖 Backends LLM", 8 | "description": "The label for category 🤖 Backends LLM in sidebar docsSidebar" 9 | }, 10 | "sidebar.docsSidebar.category.🚧 Dépannage": { 11 | "message": "🚧 Dépannage", 12 | "description": "The label for category 🚧 Dépannage in sidebar docsSidebar" 13 | }, 14 | "sidebar.apiSidebar.category.Backend": { 15 | "message": "Backend", 16 | "description": "The label for category Backend in sidebar apiSidebar" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/i18n/fr/docusaurus-plugin-content-docs/current/python/python.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Documentation Python 4 | 5 | La documentation apparaîtra ici après le déploiement. 6 | -------------------------------------------------------------------------------- /docs/i18n/fr/docusaurus-plugin-content-docs/current/python/sidebar.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": ["python/python"], 3 | "label": "Backend", 4 | "type": "categorie" 5 | } 6 | -------------------------------------------------------------------------------- /docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/how-to/github-action.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Utilisation de l'Action GitHub OpenHands 4 | 5 | Ce guide explique comment utiliser l'Action GitHub OpenHands, à la fois dans le dépôt OpenHands et dans vos propres projets. 6 | 7 | ## Utilisation de l'Action dans le dépôt OpenHands 8 | 9 | Pour utiliser l'Action GitHub OpenHands dans le dépôt OpenHands, un mainteneur OpenHands peut : 10 | 11 | 1. Créer une issue dans le dépôt. 12 | 2. Ajouter le label `fix-me` à l'issue. 13 | 3. L'action se déclenchera automatiquement et tentera de résoudre l'issue. 14 | 15 | ## Installation de l'Action dans un nouveau dépôt 16 | 17 | Pour installer l'Action GitHub OpenHands dans votre propre dépôt, suivez les [instructions dans le dépôt OpenHands Resolver](https://github.com/All-Hands-AI/OpenHands-resolver?tab=readme-ov-file#using-the-github-actions-workflow). 18 | -------------------------------------------------------------------------------- /docs/i18n/fr/docusaurus-plugin-content-docs/current/usage/llms/openrouter.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # OpenRouter 4 | 5 | OpenHands utilise LiteLLM pour effectuer des appels aux modèles de chat sur OpenRouter. Vous pouvez trouver leur documentation sur l'utilisation d'OpenRouter en tant que fournisseur [ici](https://docs.litellm.ai/docs/providers/openrouter). 6 | 7 | ## Configuration 8 | 9 | Lors de l'exécution d'OpenHands, vous devrez définir les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres : 10 | * `LLM Provider` à `OpenRouter` 11 | * `LLM Model` au modèle que vous utiliserez. 12 | [Visitez ici pour voir une liste complète des modèles OpenRouter](https://openrouter.ai/models). 13 | Si le modèle ne figure pas dans la liste, activez `Advanced Options`, et entrez-le dans `Custom Model` (par exemple openrouter/<model-name> comme `openrouter/anthropic/claude-3.5-sonnet`). 14 | * `API Key` à votre clé API OpenRouter. 15 | -------------------------------------------------------------------------------- /docs/i18n/fr/docusaurus-theme-classic/navbar.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": { 3 | "message": "OpenHands", 4 | "description": "The title in the navbar" 5 | }, 6 | "logo.alt": { 7 | "message": "OpenHands", 8 | "description": "The alt text of navbar logo" 9 | }, 10 | "item.label.Docs": { 11 | "message": "Docs", 12 | "description": "Navbar item with label Docs" 13 | }, 14 | "item.label.Codebase": { 15 | "message": "Codebase", 16 | "description": "Navbar item with label Codebase" 17 | }, 18 | "item.label.FAQ": { 19 | "message": "FAQ", 20 | "description": "Navbar item with label FAQ" 21 | }, 22 | "item.label.GitHub": { 23 | "message": "GitHub", 24 | "description": "Navbar item with label GitHub" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-plugin-content-blog/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": { 3 | "message": "博客", 4 | "description": "The title for the blog used in SEO" 5 | }, 6 | "description": { 7 | "message": "博客", 8 | "description": "The description for the blog used in SEO" 9 | }, 10 | "sidebar.title": { 11 | "message": "最近文章", 12 | "description": "The label for the left sidebar" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json: -------------------------------------------------------------------------------- 1 | { 2 | "version.label": { 3 | "message": "Next", 4 | "description": "The label for version current" 5 | }, 6 | "sidebar.docsSidebar.category.🤖 LLM 支持": { 7 | "message": "🤖 LLM 支持", 8 | "description": "The label for category 🤖 LLM 支持 in sidebar docsSidebar" 9 | }, 10 | "sidebar.docsSidebar.category.🚧 故障排除": { 11 | "message": "🚧 故障排除", 12 | "description": "The label for category 🚧 故障排除 in sidebar docsSidebar" 13 | }, 14 | "sidebar.apiSidebar.category.Backend": { 15 | "message": "Backend", 16 | "description": "The label for category Backend in sidebar apiSidebar" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/python/python.md: -------------------------------------------------------------------------------- 1 | # Python 文档 2 | 3 | 部署后文档将显示在此处。 4 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/python/sidebar.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": ["python/python"], 3 | "label": "后端", 4 | "type": "category" 5 | } 6 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/agents.md: -------------------------------------------------------------------------------- 1 | # 🧠 主代理和能力 2 | 3 | ## CodeActAgent 4 | 5 | ### 描述 6 | 7 | 这个代理实现了 CodeAct 的思想([论文](https://arxiv.org/abs/2402.01030),[推文](https://twitter.com/xingyaow_/status/1754556835703751087)),将 LLM 代理的**行动**整合到一个统一的**代码**行动空间中,以实现_简单性_和_性能_。 8 | 9 | 概念思想如下图所示。在每一轮中,代理可以: 10 | 11 | 1. **对话**:用自然语言与人类交流,以寻求澄清、确认等。 12 | 2. **CodeAct**:选择通过执行代码来执行任务 13 | 14 | - 执行任何有效的 Linux `bash` 命令 15 | - 使用 [交互式 Python 解释器](https://ipython.org/) 执行任何有效的 `Python` 代码。这是通过 `bash` 命令模拟的,有关更多详细信息,请参阅下面的插件系统。 16 | 17 | ![image](https://github.com/All-Hands-AI/OpenHands/assets/38853559/92b622e3-72ad-4a61-8f41-8c040b6d5fb3) 18 | 19 | ### 演示 20 | 21 | https://github.com/All-Hands-AI/OpenHands/assets/38853559/f592a192-e86c-4f48-ad31-d69282d5f6ac 22 | 23 | _使用 `gpt-4-turbo-2024-04-09` 的 CodeActAgent 执行数据科学任务(线性回归)的示例_。 24 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/how-to/github-action.md: -------------------------------------------------------------------------------- 1 | # 使用 OpenHands GitHub Action 2 | 3 | 本指南解释了如何在 OpenHands 仓库内以及你自己的项目中使用 OpenHands GitHub Action。 4 | 5 | ## 在 OpenHands 仓库中使用 Action 6 | 7 | 要在 OpenHands 仓库中使用 OpenHands GitHub Action,OpenHands 维护者可以: 8 | 9 | 1. 在仓库中创建一个 issue。 10 | 2. 为该 issue 添加 `fix-me` 标签。 11 | 3. Action 将自动触发并尝试解决该 issue。 12 | 13 | ## 在新仓库中安装 Action 14 | 15 | 要在你自己的仓库中安装 OpenHands GitHub Action,请按照 [OpenHands Resolver 仓库中的说明](https://github.com/All-Hands-AI/OpenHands-resolver?tab=readme-ov-file#using-the-github-actions-workflow) 进行操作。 16 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/llms/googleLLMs.md: -------------------------------------------------------------------------------- 1 | # Google Gemini/Vertex LLM 2 | 3 | ## Completion 4 | 5 | OpenHands 使用 LiteLLM 进行补全调用。以下资源与使用 OpenHands 和 Google 的 LLM 相关: 6 | 7 | - [Gemini - Google AI Studio](https://docs.litellm.ai/docs/providers/gemini) 8 | - [VertexAI - Google Cloud Platform](https://docs.litellm.ai/docs/providers/vertex) 9 | 10 | ### Gemini - Google AI Studio 配置 11 | 12 | 在运行 OpenHands Docker 镜像时,通过 Google AI Studio 使用 Gemini,你需要使用 `-e` 设置以下环境变量: 13 | 14 | ``` 15 | GEMINI_API_KEY="" 16 | LLM_MODEL="gemini/gemini-1.5-pro" 17 | ``` 18 | 19 | ### Vertex AI - Google Cloud Platform 配置 20 | 21 | 在运行 OpenHands Docker 镜像时,通过 Google Cloud Platform 使用 Vertex AI,你需要使用 `-e` 设置以下环境变量: 22 | 23 | ``` 24 | GOOGLE_APPLICATION_CREDENTIALS="" 25 | VERTEXAI_PROJECT="" 26 | VERTEXAI_LOCATION="" 27 | LLM_MODEL="vertex_ai/" 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/llms/groq.md: -------------------------------------------------------------------------------- 1 | # Groq 2 | 3 | OpenHands 使用 LiteLLM 在 Groq 上调用聊天模型。你可以在[这里](https://docs.litellm.ai/docs/providers/groq)找到他们关于使用 Groq 作为提供商的文档。 4 | 5 | ## 配置 6 | 7 | 在运行 OpenHands 时,你需要在设置中设置以下内容: 8 | * `LLM Provider` 设置为 `Groq` 9 | * `LLM Model` 设置为你将使用的模型。[访问此处查看 Groq 托管的模型列表](https://console.groq.com/docs/models)。如果模型不在列表中,切换 `Advanced Options`,并在 `Custom Model` 中输入它(例如 groq/<model-name> 如 `groq/llama3-70b-8192`)。 10 | * `API key` 设置为你的 Groq API 密钥。要查找或创建你的 Groq API 密钥,[请参见此处](https://console.groq.com/keys)。 11 | 12 | 13 | 14 | ## 使用 Groq 作为 OpenAI 兼容端点 15 | 16 | Groq 的聊天完成端点[大部分与 OpenAI 兼容](https://console.groq.com/docs/openai)。因此,你可以像访问任何 OpenAI 兼容端点一样访问 Groq 模型。你可以在设置中设置以下内容: 17 | * 启用 `Advanced Options` 18 | * `Custom Model` 设置为前缀 `openai/` + 你将使用的模型(例如 `openai/llama3-70b-8192`) 19 | * `Base URL` 设置为 `https://api.groq.com/openai/v1` 20 | * `API Key` 设置为你的 Groq API 密钥 21 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/usage/llms/openrouter.md: -------------------------------------------------------------------------------- 1 | 以下是翻译后的内容: 2 | 3 | # OpenRouter 4 | 5 | OpenHands 使用 LiteLLM 调用 OpenRouter 上的聊天模型。你可以在[这里](https://docs.litellm.ai/docs/providers/openrouter)找到他们关于使用 OpenRouter 作为提供者的文档。 6 | 7 | ## 配置 8 | 9 | 运行 OpenHands 时,你需要在设置中设置以下内容: 10 | * `LLM Provider` 设置为 `OpenRouter` 11 | * `LLM Model` 设置为你将使用的模型。 12 | [访问此处查看 OpenRouter 模型的完整列表](https://openrouter.ai/models)。 13 | 如果模型不在列表中,请切换 `Advanced Options`,并在 `Custom Model` 中输入(例如 openrouter/<model-name> 如 `openrouter/anthropic/claude-3.5-sonnet`)。 14 | * `API Key` 设置为你的 OpenRouter API 密钥。 15 | -------------------------------------------------------------------------------- /docs/i18n/zh-Hans/docusaurus-theme-classic/navbar.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": { 3 | "message": "OpenHands", 4 | "description": "The title in the navbar" 5 | }, 6 | "logo.alt": { 7 | "message": "OpenHands", 8 | "description": "The alt text of navbar logo" 9 | }, 10 | "item.label.Docs": { 11 | "message": "文档", 12 | "description": "Navbar item with label Docs" 13 | }, 14 | "item.label.Codebase": { 15 | "message": "代码库", 16 | "description": "Navbar item with label Codebase" 17 | }, 18 | "item.label.FAQ": { 19 | "message": "常见问题", 20 | "description": "Navbar item with label FAQ" 21 | }, 22 | "item.label.GitHub": { 23 | "message": "GitHub", 24 | "description": "Navbar item with label GitHub" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/modules/python/python.md: -------------------------------------------------------------------------------- 1 | # Python Docs 2 | 3 | Docs will appear here after deployment. 4 | -------------------------------------------------------------------------------- /docs/modules/python/sidebar.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": ["python/python"], 3 | "label": "Backend", 4 | "type": "category" 5 | } 6 | -------------------------------------------------------------------------------- /docs/modules/usage/how-to/github-action.md: -------------------------------------------------------------------------------- 1 | # Using the OpenHands GitHub Action 2 | 3 | This guide explains how to use the OpenHands GitHub Action, both within the OpenHands repository and in your own projects. 4 | 5 | ## Using the Action in the OpenHands Repository 6 | 7 | To use the OpenHands GitHub Action in the OpenHands repository, an OpenHands maintainer can: 8 | 9 | 1. Create an issue in the repository. 10 | 2. Add the `fix-me` label to the issue. 11 | 3. The action will automatically trigger and attempt to resolve the issue. 12 | 13 | ## Installing the Action in a New Repository 14 | 15 | To install the OpenHands GitHub Action in your own repository, follow 16 | the [README for the OpenHands Resolver](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/resolver/README.md). 17 | -------------------------------------------------------------------------------- /docs/modules/usage/llms/openrouter.md: -------------------------------------------------------------------------------- 1 | # OpenRouter 2 | 3 | OpenHands uses LiteLLM to make calls to chat models on OpenRouter. You can find their documentation on using OpenRouter as a provider [here](https://docs.litellm.ai/docs/providers/openrouter). 4 | 5 | ## Configuration 6 | 7 | When running OpenHands, you'll need to set the following in the OpenHands UI through the Settings: 8 | * `LLM Provider` to `OpenRouter` 9 | * `LLM Model` to the model you will be using. 10 | [Visit here to see a full list of OpenRouter models](https://openrouter.ai/models). 11 | If the model is not in the list, toggle `Advanced Options`, and enter it in `Custom Model` (e.g. openrouter/<model-name> like `openrouter/anthropic/claude-3.5-sonnet`). 12 | * `API Key` to your OpenRouter API key. 13 | -------------------------------------------------------------------------------- /docs/plugins/tailwind-config.cjs: -------------------------------------------------------------------------------- 1 | export default function tailwindPlugin(context, options) { 2 | return { 3 | name: 'tailwind-plugin', 4 | configurePostCss(postcssOptions) { 5 | postcssOptions.plugins = [ 6 | require('postcss-import'), 7 | require('tailwindcss'), 8 | require('autoprefixer'), 9 | ]; 10 | return postcssOptions; 11 | }, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /docs/src/components/Demo/Demo.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./index.module.css"; 3 | 4 | export function Demo() { 5 | const videoRef = React.useRef(null); 6 | 7 | return ( 8 |
11 | 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /docs/src/components/Demo/index.module.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | width: 100%; 3 | padding: 30px; 4 | max-width: 800px; 5 | text-align: center; 6 | border-radius: 40px; 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/css/homepageHeader.css: -------------------------------------------------------------------------------- 1 | /* homepageHeader.css */ 2 | 3 | .homepage-header { 4 | height: 800px; 5 | } 6 | 7 | .header-content { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | padding: 2rem; 12 | font-weight: 300; 13 | width: 100%; 14 | } 15 | 16 | .header-title { 17 | font-size: 3rem; 18 | } 19 | 20 | @media (min-width: 768px) { 21 | .header-title { 22 | font-size: 4rem; 23 | } 24 | } 25 | 26 | .header-subtitle { 27 | font-size: 1.5rem; 28 | } 29 | 30 | .header-links { 31 | display: flex; 32 | flex-wrap: wrap; 33 | justify-content: center; 34 | gap: 10px; 35 | max-width: 680px; 36 | } 37 | 38 | .header-links a { 39 | display: inline-block; 40 | transition: transform 0.2s ease-in-out; 41 | } 42 | 43 | .header-links a:hover { 44 | transform: translateY(-2px); 45 | } 46 | -------------------------------------------------------------------------------- /docs/src/pages/_footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CustomFooter from '../components/CustomFooter'; 3 | 4 | export default function Footer() { 5 | return ; 6 | } 7 | -------------------------------------------------------------------------------- /docs/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 2 | import Layout from "@theme/Layout"; 3 | import { HomepageHeader } from "../components/HomepageHeader/HomepageHeader"; 4 | import { Welcome } from "../components/Welcome/Welcome"; 5 | import { translate } from '@docusaurus/Translate'; 6 | 7 | export function Header({ title, summary }): JSX.Element { 8 | return ( 9 |
10 |

{title}

11 |

{summary}

12 |
13 | ); 14 | } 15 | 16 | export default function Home(): JSX.Element { 17 | const { siteConfig } = useDocusaurusContext(); 18 | return ( 19 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /docs/src/theme/Layout/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import OriginalLayout from '@theme-original/Layout'; 3 | import Footer from '@site/src/pages/_footer'; 4 | 5 | export default function Layout(props) { 6 | return ( 7 | <> 8 | 9 |