microbots 0.0.7__tar.gz → 0.0.8__tar.gz

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.
Files changed (63) hide show
  1. {microbots-0.0.7 → microbots-0.0.8}/.vscode/launch.json +1 -1
  2. {microbots-0.0.7 → microbots-0.0.8}/PKG-INFO +10 -10
  3. {microbots-0.0.7 → microbots-0.0.8}/README.md +9 -9
  4. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/MicroBot.py +65 -12
  5. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/bot/LogAnalysisBot.py +4 -1
  6. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/bot/ReadingBot.py +3 -1
  7. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/bot/WritingBot.py +2 -1
  8. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/environment/local_docker/LocalDockerEnvironment.py +32 -39
  9. microbots-0.0.8/src/microbots/extras/mount.py +73 -0
  10. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/utils/path.py +9 -0
  11. {microbots-0.0.7 → microbots-0.0.8}/src/microbots.egg-info/PKG-INFO +10 -10
  12. {microbots-0.0.7 → microbots-0.0.8}/src/microbots.egg-info/SOURCES.txt +1 -0
  13. {microbots-0.0.7 → microbots-0.0.8}/test/bot/test_browsing_bot.py +15 -14
  14. {microbots-0.0.7 → microbots-0.0.8}/test/bot/test_log_analysis_bot.py +1 -1
  15. {microbots-0.0.7 → microbots-0.0.8}/test/bot/test_microbot.py +31 -11
  16. {microbots-0.0.7 → microbots-0.0.8}/test/environment/local_docker/test_local_docker_environment.py +15 -24
  17. microbots-0.0.8/test/extras/test_mount.py +37 -0
  18. {microbots-0.0.7 → microbots-0.0.8}/test/fixtures/fixture_test_repo.py +11 -4
  19. microbots-0.0.7/src/microbots/extras/mount.py +0 -27
  20. {microbots-0.0.7 → microbots-0.0.8}/.codecov.yml +0 -0
  21. {microbots-0.0.7 → microbots-0.0.8}/.github/workflows/dockerBuildPush.yml +0 -0
  22. {microbots-0.0.7 → microbots-0.0.8}/.github/workflows/publish.yml +0 -0
  23. {microbots-0.0.7 → microbots-0.0.8}/.github/workflows/test.yml +0 -0
  24. {microbots-0.0.7 → microbots-0.0.8}/.gitignore +0 -0
  25. {microbots-0.0.7 → microbots-0.0.8}/.vscode/settings.json +0 -0
  26. {microbots-0.0.7 → microbots-0.0.8}/LICENSE +0 -0
  27. {microbots-0.0.7 → microbots-0.0.8}/architechture.md +0 -0
  28. {microbots-0.0.7 → microbots-0.0.8}/docs/images/overall_architecture.png +0 -0
  29. {microbots-0.0.7 → microbots-0.0.8}/pyproject.toml +0 -0
  30. {microbots-0.0.7 → microbots-0.0.8}/pytest.ini +0 -0
  31. {microbots-0.0.7 → microbots-0.0.8}/requirements.txt +0 -0
  32. {microbots-0.0.7 → microbots-0.0.8}/setup.cfg +0 -0
  33. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/__init__.py +0 -0
  34. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/bot/BrowsingBot.py +0 -0
  35. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/bot/__init__.py +0 -0
  36. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/constants.py +0 -0
  37. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/environment/Environment.py +0 -0
  38. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/environment/local_docker/__init__.py +0 -0
  39. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/environment/local_docker/image_builder/Dockerfile +0 -0
  40. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/environment/local_docker/image_builder/ShellCommunicator.py +0 -0
  41. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/environment/local_docker/image_builder/dockerShell.py +0 -0
  42. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/environment/swe-rex/LocalDocker.py +0 -0
  43. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/extras/__init__.py +0 -0
  44. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/llm/__init__.py +0 -0
  45. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/llm/openai_api.py +0 -0
  46. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/tools/tool.py +0 -0
  47. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/tools/tool_definitions/browser-use/browser.py +0 -0
  48. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/tools/tool_definitions/browser-use.yaml +0 -0
  49. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/tools/tool_definitions/cscope.yaml +0 -0
  50. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/tools/tool_test.ipynb +0 -0
  51. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/utils/logger.py +0 -0
  52. {microbots-0.0.7 → microbots-0.0.8}/src/microbots/utils/network.py +0 -0
  53. {microbots-0.0.7 → microbots-0.0.8}/src/microbots.egg-info/dependency_links.txt +0 -0
  54. {microbots-0.0.7 → microbots-0.0.8}/src/microbots.egg-info/requires.txt +0 -0
  55. {microbots-0.0.7 → microbots-0.0.8}/src/microbots.egg-info/top_level.txt +0 -0
  56. {microbots-0.0.7 → microbots-0.0.8}/test/bot/calculator/calculator.log +0 -0
  57. {microbots-0.0.7 → microbots-0.0.8}/test/bot/calculator/code/calculator.py +0 -0
  58. {microbots-0.0.7 → microbots-0.0.8}/test/bot/test_reading_bot.py +0 -0
  59. {microbots-0.0.7 → microbots-0.0.8}/test/bot/test_writing_bot.py +0 -0
  60. {microbots-0.0.7 → microbots-0.0.8}/test/conftest.py +0 -0
  61. {microbots-0.0.7 → microbots-0.0.8}/test/environment/swe-rex/test_local_docker.py.disabled +0 -0
  62. {microbots-0.0.7 → microbots-0.0.8}/test/fixtures/fixture_issue_1.py +0 -0
  63. {microbots-0.0.7 → microbots-0.0.8}/test/fixtures/fixture_issue_22.py +0 -0
@@ -5,7 +5,7 @@
5
5
  "version": "0.2.0",
6
6
  "configurations": [
7
7
  {
8
- "name": "Python Debugger: Module",
8
+ "name": "PyTest: test_microbot_ro_mount",
9
9
  "type": "debugpy",
10
10
  "request": "launch",
11
11
  "module": "pytest",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microbots
3
- Version: 0.0.7
3
+ Version: 0.0.8
4
4
  Summary: container-based autonomous agent framework
5
5
  Author-email: xxx <xxx@example.com>
6
6
  License: MIT License
@@ -48,8 +48,8 @@ Dynamic: license-file
48
48
 
49
49
  # 🤖 Microbots
50
50
 
51
- MicroBots is a lightweight, extensible AI agent for code comprehension and controlled file edits. It integrates cleanly
52
- into automation pipelines, mounting a target directory with explicit read-only or read/write modes so LLMs can safely
51
+ MicroBots is a lightweight, extensible AI agent for code comprehension and controlled file edits. It integrates cleanly
52
+ into automation pipelines, mounting a target directory with explicit read-only or read/write modes so LLMs can safely
53
53
  inspect, refactor, or generate files with least‑privilege access.
54
54
 
55
55
 
@@ -61,14 +61,14 @@ myWritingBot = WritingBot(
61
61
  folder_to_mount=str("myReactApp"),
62
62
  )
63
63
 
64
- data = myWritingBot.run("""when doing npm run build, I get an error.
64
+ data = myWritingBot.run("""when doing npm run build, I get an error.
65
65
  Fix the error and make sure the build is successful.""", timeout_in_seconds=600)
66
66
  print(data.results)
67
67
  ```
68
68
 
69
- ## ⚠️ Project Status: Highly Unstable
70
- This project is currently **under active development** and is considered **highly unstable**. Features, APIs, and internal structures are subject to change without notice, and unexpected behavior may occur.
71
- Please **use with caution** in production environments.
69
+ ## ⚠️ Project Status: Under Fast Development
70
+
71
+ This project is currently **under active development**. Features, APIs, and internal structures are subject to change across versions without notice, and unexpected behavior may occur. Please **use with caution** in production environments.
72
72
 
73
73
  ## 🚀 How to install
74
74
 
@@ -85,7 +85,7 @@ pip install microbots
85
85
 
86
86
 
87
87
  ## ✨LLM Support
88
-
88
+
89
89
  Azure OpenAI Models - Add the below environment variables in a `.env` file in the root of your application
90
90
 
91
91
  ```env
@@ -95,7 +95,7 @@ OPEN_AI_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
95
95
 
96
96
  ## 🤖 Bots & Usage Examples
97
97
 
98
- Pre-requisite for the below example code of Bots:
98
+ Pre-requisite for the below example code of Bots:
99
99
  From the root of your application, Create a folder called `code` inside which clone the repo `https://github.com/swe-agent/test-repo/`. Now run the code
100
100
 
101
101
 
@@ -120,7 +120,7 @@ The `ReadingBot` will read the files inside `code` folder and will extract infor
120
120
 
121
121
  ### ✍️ WritingBot
122
122
 
123
- Pre-requisite for the example code:
123
+ Pre-requisite for the example code:
124
124
  From the root the application, Create a folder called `code` inside which clone the repo `https://github.com/swe-agent/test-repo/`. Now run the code
125
125
 
126
126
  ```py
@@ -1,7 +1,7 @@
1
1
  # 🤖 Microbots
2
2
 
3
- MicroBots is a lightweight, extensible AI agent for code comprehension and controlled file edits. It integrates cleanly
4
- into automation pipelines, mounting a target directory with explicit read-only or read/write modes so LLMs can safely
3
+ MicroBots is a lightweight, extensible AI agent for code comprehension and controlled file edits. It integrates cleanly
4
+ into automation pipelines, mounting a target directory with explicit read-only or read/write modes so LLMs can safely
5
5
  inspect, refactor, or generate files with least‑privilege access.
6
6
 
7
7
 
@@ -13,14 +13,14 @@ myWritingBot = WritingBot(
13
13
  folder_to_mount=str("myReactApp"),
14
14
  )
15
15
 
16
- data = myWritingBot.run("""when doing npm run build, I get an error.
16
+ data = myWritingBot.run("""when doing npm run build, I get an error.
17
17
  Fix the error and make sure the build is successful.""", timeout_in_seconds=600)
18
18
  print(data.results)
19
19
  ```
20
20
 
21
- ## ⚠️ Project Status: Highly Unstable
22
- This project is currently **under active development** and is considered **highly unstable**. Features, APIs, and internal structures are subject to change without notice, and unexpected behavior may occur.
23
- Please **use with caution** in production environments.
21
+ ## ⚠️ Project Status: Under Fast Development
22
+
23
+ This project is currently **under active development**. Features, APIs, and internal structures are subject to change across versions without notice, and unexpected behavior may occur. Please **use with caution** in production environments.
24
24
 
25
25
  ## 🚀 How to install
26
26
 
@@ -37,7 +37,7 @@ pip install microbots
37
37
 
38
38
 
39
39
  ## ✨LLM Support
40
-
40
+
41
41
  Azure OpenAI Models - Add the below environment variables in a `.env` file in the root of your application
42
42
 
43
43
  ```env
@@ -47,7 +47,7 @@ OPEN_AI_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
47
47
 
48
48
  ## 🤖 Bots & Usage Examples
49
49
 
50
- Pre-requisite for the below example code of Bots:
50
+ Pre-requisite for the below example code of Bots:
51
51
  From the root of your application, Create a folder called `code` inside which clone the repo `https://github.com/swe-agent/test-repo/`. Now run the code
52
52
 
53
53
 
@@ -72,7 +72,7 @@ The `ReadingBot` will read the files inside `code` folder and will extract infor
72
72
 
73
73
  ### ✍️ WritingBot
74
74
 
75
- Pre-requisite for the example code:
75
+ Pre-requisite for the example code:
76
76
  From the root the application, Create a folder called `code` inside which clone the repo `https://github.com/swe-agent/test-repo/`. Now run the code
77
77
 
78
78
  ```py
@@ -51,6 +51,33 @@ class BotRunResult:
51
51
 
52
52
 
53
53
  class MicroBot:
54
+ """
55
+ The core Microbot class.
56
+
57
+ MicroBot class is the core class representing the autonomous agent. Other bots are extensions of this class.
58
+ If you want to create a custom bot, you can directly use this class or extend it into your own bot class.
59
+
60
+ Attributes
61
+ ----------
62
+ model : str
63
+ The model to use for the bot, in the format <provider>/<model_name>.
64
+ bot_type : BotType
65
+ The type of bot being created. It's unused. Will be removed soon.
66
+ system_prompt : Optional[str]
67
+ The system prompt to guide the bot's behavior.
68
+ environment : Optional[any]
69
+ The execution environment for the bot. If not provided, a default
70
+ LocalDockerEnvironment will be created.
71
+ additional_tools : Optional[list[Tool]]
72
+ A list of additional tools to install in the bot's environment.
73
+ folder_to_mount : Optional[Mount]
74
+ A folder to mount into the bot's environment. The bot will be given
75
+ access to this folder based on the specified permissions. This will
76
+ be the main code folder where the bot will work. Additional folders
77
+ can be mounted during the run() method. Refer to `Mount` class
78
+ regarding the directory structure and permission details. Defaults
79
+ to None.
80
+ """
54
81
 
55
82
  def __init__(
56
83
  self,
@@ -61,6 +88,33 @@ class MicroBot:
61
88
  additional_tools: Optional[list[Tool]] = [],
62
89
  folder_to_mount: Optional[Mount] = None,
63
90
  ):
91
+ """
92
+ Init function for MicroBot class.
93
+
94
+ Parameters
95
+ ----------
96
+ model :str
97
+ The model to use for the bot, in the format <provider>/<model_name>.
98
+ bot_type :BotType
99
+ The type of bot being created. It's unused. Will be removed soon.
100
+ system_prompt :Optional[str]
101
+ The system prompt to guide the bot's behavior. Defaults to None.
102
+ environment :Optional[any]
103
+ The execution environment for the bot. If not provided, a default
104
+ LocalDockerEnvironment will be created.
105
+ additional_tools :Optional[list[Tool]]
106
+ A list of additional tools to install in the bot's environment.
107
+ Defaults to [].
108
+ folder_to_mount :Optional[Mount]
109
+ A folder to mount into the bot's environment. The bot will be given
110
+ access to this folder based on the specified permissions. This will
111
+ be the main code folder where the bot will work. Additional folders
112
+ can be mounted using the run() method. Refer to `Mount` class
113
+ regarding the directory structure and permission details. Defaults
114
+ to None.
115
+
116
+ Note: Supports only mount type MountType.MOUNT for now.
117
+ """
64
118
 
65
119
  self.folder_to_mount = folder_to_mount
66
120
 
@@ -74,6 +128,7 @@ class MicroBot:
74
128
 
75
129
  self.mounted = []
76
130
  if folder_to_mount is not None:
131
+ self._validate_folder_to_mount(folder_to_mount)
77
132
  self.mounted.append(folder_to_mount)
78
133
 
79
134
  self.system_prompt = system_prompt
@@ -195,10 +250,7 @@ class MicroBot:
195
250
 
196
251
  self.environment = LocalDockerEnvironment(
197
252
  port=free_port,
198
- folder_to_mount=(
199
- folder_to_mount.host_path_info.abs_path if folder_to_mount else None
200
- ),
201
- permission=folder_to_mount.permission if folder_to_mount else None,
253
+ folder_to_mount=folder_to_mount,
202
254
  )
203
255
 
204
256
  def _create_llm(self):
@@ -215,11 +267,12 @@ class MicroBot:
215
267
  if provider not in [e.value for e in ModelProvider]:
216
268
  raise ValueError(f"Unsupported model provider: {provider}")
217
269
 
218
- # def __del__(self):
219
- # if self.environment:
220
- # try:
221
- # self.environment.stop()
222
- # except Exception as e:
223
- # logger.error(
224
- # "%s Error while stopping environment: %s", LogLevelEmoji.ERROR, e
225
- # )
270
+ def _validate_folder_to_mount(self, folder_to_mount: Mount):
271
+ if folder_to_mount.mount_type != MountType.MOUNT:
272
+ logger.error(
273
+ "%s Only MOUNT mount type is supported for folder_to_mount",
274
+ LogLevelEmoji.ERROR,
275
+ )
276
+ raise ValueError(
277
+ "Only MOUNT mount type is supported for folder_to_mount"
278
+ )
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import os
2
3
  from typing import Optional
3
4
 
4
5
  from microbots.constants import DOCKER_WORKING_DIR, LOG_FILE_DIR, PermissionLabels
@@ -22,7 +23,9 @@ class LogAnalysisBot(MicroBot):
22
23
  bot_type = BotType.LOG_ANALYSIS_BOT
23
24
 
24
25
  folder_mount_info = Mount(
25
- folder_to_mount, DOCKER_WORKING_DIR, PermissionLabels.READ_ONLY
26
+ folder_to_mount,
27
+ f"/{DOCKER_WORKING_DIR}/{os.path.basename(folder_to_mount)}",
28
+ PermissionLabels.READ_ONLY
26
29
  )
27
30
 
28
31
  system_prompt = f"""
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from typing import Optional
2
3
 
3
4
  from microbots.constants import DOCKER_WORKING_DIR, PermissionLabels
@@ -18,8 +19,9 @@ class ReadingBot(MicroBot):
18
19
  # validate init values before assigning
19
20
  bot_type = BotType.READING_BOT
20
21
 
22
+ base_name = os.path.basename(folder_to_mount)
21
23
  folder_mount_info = Mount(
22
- folder_to_mount, f"/{DOCKER_WORKING_DIR}", PermissionLabels.READ_ONLY
24
+ folder_to_mount, f"/{DOCKER_WORKING_DIR}/{base_name}", PermissionLabels.READ_ONLY
23
25
  )
24
26
 
25
27
  system_prompt = f"""
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from typing import Optional
2
3
 
3
4
  from microbots.constants import DOCKER_WORKING_DIR, PermissionLabels
@@ -19,7 +20,7 @@ class WritingBot(MicroBot):
19
20
  bot_type = BotType.WRITING_BOT
20
21
 
21
22
  folder_mount_info = Mount(
22
- folder_to_mount, DOCKER_WORKING_DIR, PermissionLabels.READ_WRITE
23
+ folder_to_mount, f"/{DOCKER_WORKING_DIR}/{os.path.basename(folder_to_mount)}", PermissionLabels.READ_WRITE
23
24
  )
24
25
 
25
26
  system_prompt = f"""
@@ -10,7 +10,8 @@ import docker
10
10
  import requests
11
11
 
12
12
  from microbots.environment.Environment import CmdReturn, Environment
13
- from microbots.constants import DOCKER_WORKING_DIR, WORKING_DIR
13
+ from microbots.constants import DOCKER_WORKING_DIR, WORKING_DIR, PermissionLabels
14
+ from microbots.extras.mount import Mount
14
15
 
15
16
  logger = logging.getLogger(__name__)
16
17
 
@@ -19,23 +20,13 @@ class LocalDockerEnvironment(Environment):
19
20
  def __init__(
20
21
  self,
21
22
  port: int,
22
- folder_to_mount: Optional[str] = None,
23
- permission: Optional[str] = None,
23
+ folder_to_mount: Optional[Mount] = None,
24
24
  image: str = "kavyasree261002/shell_server:latest",
25
25
  ):
26
- if folder_to_mount is None and permission is not None:
27
- raise ValueError("permission provided but folder_to_mount is None")
28
- elif permission is None and folder_to_mount is not None:
29
- raise ValueError("folder_to_mount provided but permission is None")
30
- if permission is not None and permission not in ["READ_ONLY", "READ_WRITE"]:
31
- raise ValueError(
32
- "permission must be 'READ_ONLY' or 'READ_WRITE' when provided"
33
- )
34
26
 
35
27
  self.image = image
36
28
  self.folder_to_mount = folder_to_mount
37
29
  self.overlay_mount = False
38
- self.permission = permission
39
30
  self.container = None
40
31
  self.client = docker.from_env()
41
32
  self.port = port # required host port
@@ -58,26 +49,26 @@ class LocalDockerEnvironment(Environment):
58
49
  def start(self):
59
50
  mode_map = {"READ_ONLY": "ro", "READ_WRITE": "rw"}
60
51
  volumes_config = {WORKING_DIR: {"bind": DOCKER_WORKING_DIR, "mode": "rw"}}
61
- if self.folder_to_mount and self.permission:
62
- if self.permission == "READ_ONLY":
63
- volumes_config[self.folder_to_mount] = {
64
- "bind": f"/ro/{os.path.basename(self.folder_to_mount)}",
65
- "mode": mode_map[self.permission],
52
+ if self.folder_to_mount:
53
+ if self.folder_to_mount.permission == PermissionLabels.READ_ONLY:
54
+ volumes_config[self.folder_to_mount.host_path_info.abs_path] = {
55
+ "bind": f"/ro/{os.path.basename(self.folder_to_mount.sandbox_path)}",
56
+ "mode": mode_map[self.folder_to_mount.permission],
66
57
  }
67
58
  logger.info(
68
59
  "📦 Volume mapping: %s → /ro/%s",
69
- self.folder_to_mount,
70
- os.path.basename(self.folder_to_mount),
60
+ self.folder_to_mount.host_path_info.abs_path,
61
+ os.path.basename(self.folder_to_mount.sandbox_path),
71
62
  )
72
63
  else:
73
- volumes_config[self.folder_to_mount] = {
74
- "bind": f"/{DOCKER_WORKING_DIR}/{os.path.basename(self.folder_to_mount)}",
75
- "mode": mode_map[self.permission],
64
+ volumes_config[self.folder_to_mount.host_path_info.abs_path] = {
65
+ "bind": self.folder_to_mount.sandbox_path,
66
+ "mode": mode_map[self.folder_to_mount.permission],
76
67
  }
77
68
  logger.debug(
78
- "📦 Volume mapping: %s → /{DOCKER_WORKING_DIR}/%s",
79
- self.folder_to_mount,
80
- os.path.basename(self.folder_to_mount),
69
+ "📦 Volume mapping: %s → %s",
70
+ self.folder_to_mount.host_path_info.abs_path,
71
+ self.folder_to_mount.sandbox_path,
81
72
  )
82
73
 
83
74
  # Port mapping
@@ -100,20 +91,22 @@ class LocalDockerEnvironment(Environment):
100
91
  )
101
92
  time.sleep(2) # Give some time for the server to start
102
93
 
103
- if self.permission == "READ_ONLY" and self.folder_to_mount:
104
- self._setup_overlay_mount(self.folder_to_mount)
94
+ if self.folder_to_mount and self.folder_to_mount.permission == PermissionLabels.READ_ONLY:
95
+ self._setup_overlay_mount()
105
96
 
106
97
  if self.folder_to_mount:
107
- self.execute(f"cd /{DOCKER_WORKING_DIR}/{os.path.basename(self.folder_to_mount)}")
98
+ self.execute(f"cd {self.folder_to_mount.sandbox_path}")
108
99
  else:
109
100
  self.execute("cd /")
110
101
 
111
- def _setup_overlay_mount(self, folder_to_mount: str):
112
- path_name = os.path.basename(os.path.abspath(folder_to_mount))
102
+ def _setup_overlay_mount(self):
103
+ # NOTE: Don't use this for any other read-only mounts except the main code folder.
104
+
105
+ path_name = os.path.basename(self.folder_to_mount.sandbox_path)
113
106
  # Mount /ro/path_name to /{WORKING_DIR}/path_name using overlayfs
114
107
  mount_command = (
115
- f"mkdir -p /{DOCKER_WORKING_DIR}/{path_name} /{DOCKER_WORKING_DIR}/overlay/{path_name}/upper /{DOCKER_WORKING_DIR}/overlay/{path_name}/work && "
116
- f"mount -t overlay overlay -o lowerdir=/ro/{path_name}/,upperdir={DOCKER_WORKING_DIR}/overlay/{path_name}/upper/,workdir={DOCKER_WORKING_DIR}/overlay/{path_name}/work/ {DOCKER_WORKING_DIR}/{path_name}/"
108
+ f"mkdir -p {self.folder_to_mount.sandbox_path} /{DOCKER_WORKING_DIR}/overlay/{path_name}/upper /{DOCKER_WORKING_DIR}/overlay/{path_name}/work && sleep 5 && "
109
+ f"mount -t overlay overlay -o lowerdir=/ro/{path_name}/,upperdir={DOCKER_WORKING_DIR}/overlay/{path_name}/upper/,workdir={DOCKER_WORKING_DIR}/overlay/{path_name}/work/ {self.folder_to_mount.sandbox_path}"
117
110
  )
118
111
  self.execute(mount_command)
119
112
  logger.info(
@@ -121,12 +114,12 @@ class LocalDockerEnvironment(Environment):
121
114
  )
122
115
  self.overlay_mount = True
123
116
 
124
- def _teardown_overlay_mount(self, folder_to_mount: str):
125
- path_name = os.path.basename(os.path.abspath(folder_to_mount))
117
+ def _teardown_overlay_mount(self):
118
+ path_name = os.path.basename(os.path.abspath(self.folder_to_mount.sandbox_path))
126
119
 
127
120
  try:
128
121
  logger.info("🛠️ Tearing down overlay mount for %s", path_name)
129
- unmount_command = f"umount -l {DOCKER_WORKING_DIR}/{path_name}/"
122
+ unmount_command = f"umount -l {self.folder_to_mount.sandbox_path}"
130
123
  ret: CmdReturn = self.execute(unmount_command)
131
124
  if ret.return_code != 0:
132
125
  logger.error("❌ Failed to unmount overlay: %s", ret.stderr)
@@ -134,10 +127,10 @@ class LocalDockerEnvironment(Environment):
134
127
  logger.info("✅ Unmounted overlay for %s", path_name)
135
128
 
136
129
  logger.info(
137
- f"🛑 Removing overlay dirs at {DOCKER_WORKING_DIR}/{path_name} and {DOCKER_WORKING_DIR}/overlay/"
130
+ f"🛑 Removing overlay dirs at {self.folder_to_mount.sandbox_path} and {DOCKER_WORKING_DIR}/overlay/"
138
131
  )
139
132
  remove_dir_command = (
140
- f"rm -rf {DOCKER_WORKING_DIR}/{path_name} && "
133
+ f"rm -rf {self.folder_to_mount.sandbox_path} && "
141
134
  f"rm -rf {DOCKER_WORKING_DIR}/overlay/"
142
135
  )
143
136
  ret: CmdReturn = self.execute(remove_dir_command)
@@ -156,7 +149,7 @@ class LocalDockerEnvironment(Environment):
156
149
  """Stop and remove the container"""
157
150
  if self.container:
158
151
  if self.overlay_mount:
159
- self._teardown_overlay_mount(self.folder_to_mount)
152
+ self._teardown_overlay_mount()
160
153
 
161
154
  self.container.stop()
162
155
  self.container.remove()
@@ -236,7 +229,7 @@ class LocalDockerEnvironment(Environment):
236
229
  e,
237
230
  )
238
231
  return CmdReturn(stdout="", stderr="Unexpected error", return_code=1)
239
-
232
+
240
233
  def copy_to_container(self, src_path: str, dest_path: str) -> bool:
241
234
  """
242
235
  Copy a file or folder from the host machine to the Docker container.
@@ -0,0 +1,73 @@
1
+ from dataclasses import dataclass, field
2
+ from enum import StrEnum
3
+ from pathlib import Path
4
+
5
+ from microbots.constants import PermissionLabels, PermissionMapping
6
+ from microbots.utils.path import PathInfo, get_path_info, ends_with_separator
7
+
8
+
9
+ class MountType(StrEnum):
10
+ """
11
+ Enum representing the type of mount operation.
12
+
13
+ MOUNT : Mount the folder from host to sandbox environment.
14
+ COPY : Copy the folder from host to sandbox environment.
15
+ """
16
+ MOUNT = "mount"
17
+ COPY = "copy"
18
+
19
+
20
+ @dataclass
21
+ class Mount:
22
+ """
23
+ Folder mount configuration for a microbot environment.
24
+
25
+ All the folders and files to be presented for the Bot should be
26
+ either mounted or copied to the Bot's sandbox environment using
27
+ this class.
28
+
29
+ Attributes
30
+ ----------
31
+ host_path : str
32
+ The absolute path on the host machine to be mounted or copied.
33
+ sandbox_path : str
34
+ The absolute path inside the Bot's sandbox environment where the
35
+ host_path will be mounted or copied. If the host_path is a file
36
+ and the sandbox_path ends with a path separator ("/"), then
37
+ the file will be placed inside the sandbox_path directory with
38
+ the same base name as the host file.
39
+ permission : PermissionLabels
40
+ The permission level for the mounted/copied folder. It should
41
+ be one of the values from PermissionLabels enum.
42
+ mount_type : MountType, optional
43
+ The type of mount operation. Mounting and copying have are the
44
+ options. Possible values are MountType enum values.
45
+ Default is MountType.MOUNT.
46
+ """
47
+ host_path: str
48
+ sandbox_path: str
49
+ permission: PermissionLabels
50
+ mount_type: MountType = MountType.MOUNT
51
+
52
+ # These will be set in __post_init__
53
+ permission_key: str = field(init=False)
54
+ host_path_info: PathInfo = field(init=False)
55
+
56
+ def __post_init__(self):
57
+ self.permission_key = PermissionMapping.MAPPING.get(self.permission)
58
+ self.host_path_info = get_path_info(self.host_path)
59
+
60
+ sandbox_path = Path(self.sandbox_path)
61
+
62
+ # Validate that sandbox_path is absolute
63
+ if not sandbox_path.is_absolute():
64
+ raise ValueError(
65
+ f"sandbox_path must be an absolute path. Given: {self.sandbox_path}"
66
+ )
67
+
68
+ if not ends_with_separator(self.host_path) and ends_with_separator(self.sandbox_path):
69
+ # If host_path is a file and sandbox_path ends with separator,
70
+ # place the file inside the sandbox_path directory with same base name
71
+ sandbox_path = str(sandbox_path / self.host_path_info.base_name)
72
+
73
+ self.sandbox_path = str(sandbox_path)
@@ -37,3 +37,12 @@ def get_path_info(file_or_folder: str) -> PathInfo:
37
37
  abs_path=os.path.abspath(file_or_folder),
38
38
  )
39
39
  return PathInfo(path_valid=False, base_name="", abs_path="")
40
+
41
+
42
+ def ends_with_separator(path_str: str) -> bool:
43
+ # Accept both separators on Windows; only '/' on POSIX
44
+ seps = {os.sep}
45
+ if os.altsep:
46
+ seps.add(os.altsep)
47
+ return len(path_str) > 0 and path_str[-1] in seps
48
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microbots
3
- Version: 0.0.7
3
+ Version: 0.0.8
4
4
  Summary: container-based autonomous agent framework
5
5
  Author-email: xxx <xxx@example.com>
6
6
  License: MIT License
@@ -48,8 +48,8 @@ Dynamic: license-file
48
48
 
49
49
  # 🤖 Microbots
50
50
 
51
- MicroBots is a lightweight, extensible AI agent for code comprehension and controlled file edits. It integrates cleanly
52
- into automation pipelines, mounting a target directory with explicit read-only or read/write modes so LLMs can safely
51
+ MicroBots is a lightweight, extensible AI agent for code comprehension and controlled file edits. It integrates cleanly
52
+ into automation pipelines, mounting a target directory with explicit read-only or read/write modes so LLMs can safely
53
53
  inspect, refactor, or generate files with least‑privilege access.
54
54
 
55
55
 
@@ -61,14 +61,14 @@ myWritingBot = WritingBot(
61
61
  folder_to_mount=str("myReactApp"),
62
62
  )
63
63
 
64
- data = myWritingBot.run("""when doing npm run build, I get an error.
64
+ data = myWritingBot.run("""when doing npm run build, I get an error.
65
65
  Fix the error and make sure the build is successful.""", timeout_in_seconds=600)
66
66
  print(data.results)
67
67
  ```
68
68
 
69
- ## ⚠️ Project Status: Highly Unstable
70
- This project is currently **under active development** and is considered **highly unstable**. Features, APIs, and internal structures are subject to change without notice, and unexpected behavior may occur.
71
- Please **use with caution** in production environments.
69
+ ## ⚠️ Project Status: Under Fast Development
70
+
71
+ This project is currently **under active development**. Features, APIs, and internal structures are subject to change across versions without notice, and unexpected behavior may occur. Please **use with caution** in production environments.
72
72
 
73
73
  ## 🚀 How to install
74
74
 
@@ -85,7 +85,7 @@ pip install microbots
85
85
 
86
86
 
87
87
  ## ✨LLM Support
88
-
88
+
89
89
  Azure OpenAI Models - Add the below environment variables in a `.env` file in the root of your application
90
90
 
91
91
  ```env
@@ -95,7 +95,7 @@ OPEN_AI_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
95
95
 
96
96
  ## 🤖 Bots & Usage Examples
97
97
 
98
- Pre-requisite for the below example code of Bots:
98
+ Pre-requisite for the below example code of Bots:
99
99
  From the root of your application, Create a folder called `code` inside which clone the repo `https://github.com/swe-agent/test-repo/`. Now run the code
100
100
 
101
101
 
@@ -120,7 +120,7 @@ The `ReadingBot` will read the files inside `code` folder and will extract infor
120
120
 
121
121
  ### ✍️ WritingBot
122
122
 
123
- Pre-requisite for the example code:
123
+ Pre-requisite for the example code:
124
124
  From the root the application, Create a folder called `code` inside which clone the repo `https://github.com/swe-agent/test-repo/`. Now run the code
125
125
 
126
126
  ```py
@@ -54,6 +54,7 @@ test/bot/calculator/calculator.log
54
54
  test/bot/calculator/code/calculator.py
55
55
  test/environment/local_docker/test_local_docker_environment.py
56
56
  test/environment/swe-rex/test_local_docker.py.disabled
57
+ test/extras/test_mount.py
57
58
  test/fixtures/fixture_issue_1.py
58
59
  test/fixtures/fixture_issue_22.py
59
60
  test/fixtures/fixture_test_repo.py
@@ -19,7 +19,7 @@ from microbots import BrowsingBot, BotRunResult
19
19
  @pytest.mark.slow
20
20
  class TestBrowsingBot:
21
21
  """Integration tests for BrowsingBot functionality."""
22
-
22
+
23
23
  @pytest.fixture(scope="function")
24
24
  def browsing_bot(self):
25
25
  """Create a BrowsingBot instance for testing."""
@@ -31,42 +31,43 @@ class TestBrowsingBot:
31
31
  bot.environment.stop()
32
32
  except Exception as e:
33
33
  logger.warning(f"Error stopping environment: {e}")
34
-
34
+
35
+ # Google search may fail due to captcha, so this test may be flaky in CI environments.
35
36
  def test_simple_question_response(self, browsing_bot):
36
37
  """Test that the bot can answer a simple factual question."""
37
38
  response: BotRunResult = browsing_bot.run(
38
- "What is the capital of France?",
39
+ "What is the capital of France? Get this info from https://en.wikipedia.org/wiki/France",
39
40
  timeout_in_seconds=300,
40
41
  )
41
-
42
+
42
43
  # Assert the response was successful
43
44
  assert response.status == True, f"Bot failed with error: {response.error}"
44
45
  assert response.result is not None, "Bot returned no result"
45
46
  assert isinstance(response.result, str), "Result should be a string"
46
-
47
+
47
48
  # Check that the result contains the expected answer
48
49
  result_lower = response.result.lower()
49
50
  assert "paris" in result_lower, f"Expected 'Paris' in result, got: {response.result}"
50
-
51
+
51
52
  logger.info(f"Test passed. Bot response: {response.result}")
52
-
53
-
53
+
54
+
55
+ # Google search may fail due to captcha, so this test may be flaky in CI environments.
54
56
  @pytest.mark.parametrize("query,expected_keywords", [
55
- ("What is the capital of Germany?", ["berlin"]),
57
+ ("Get capital of Germany from https://en.wikipedia.org/wiki/Germany", ["berlin"]),
56
58
  ("What is 2+2?", ["4", "four"]),
57
- ("What time is it in Tokyo?", ["am", "pm", "time"]),
58
- ("Who is the current President of the United States?", ["Trump"]),
59
+ ("Get the description of this CVE-2024-11738 from nvd.nist.gov website", ["Rustls"]),
59
60
  ])
60
61
  def test_multiple_queries(self, browsing_bot, query, expected_keywords):
61
62
  """Test the bot with multiple different queries."""
62
63
  response: BotRunResult = browsing_bot.run(query, timeout_in_seconds=300)
63
-
64
+
64
65
  assert response.status == True, f"Query '{query}' failed: {response.error}"
65
66
  assert response.result is not None, f"No result for query: {query}"
66
-
67
+
67
68
  result_lower = response.result.lower()
68
69
  # At least one expected keyword should be in the result
69
70
  keyword_found = any(keyword.lower() in result_lower for keyword in expected_keywords)
70
71
  assert keyword_found, f"None of {expected_keywords} found in result: {response.result}"
71
-
72
+
72
73
  logger.info(f"Query '{query}' passed with result: {response.result[:100]}...")
@@ -65,7 +65,7 @@ class TestLogAnalysisBot:
65
65
  log_file.write(result.stderr)
66
66
 
67
67
  response: BotRunResult = log_analysis_bot.run(
68
- log_file_path, timeout_in_seconds=300
68
+ str(log_file_path), timeout_in_seconds=300
69
69
  )
70
70
 
71
71
  print(f"Status: {response.status}, Result: {response.result}, Error: {response.error}")
@@ -47,17 +47,17 @@ You must send `task_done` as true only when you have completed the task. It mean
47
47
  class TestMicroBot:
48
48
 
49
49
  @pytest.fixture(scope="function")
50
- def log_file_path(self, tmpdir):
50
+ def log_file_path(self, tmpdir: Path):
51
51
  assert tmpdir.exists()
52
52
  yield tmpdir / "error.log"
53
53
  if tmpdir.exists():
54
54
  subprocess.run(["sudo", "rm", "-rf", str(tmpdir)])
55
55
 
56
56
  @pytest.fixture(scope="function")
57
- def ro_mount(self, test_repo):
57
+ def ro_mount(self, test_repo: Path):
58
58
  assert test_repo is not None
59
59
  return Mount(
60
- str(test_repo), DOCKER_WORKING_DIR, PermissionLabels.READ_ONLY
60
+ str(test_repo), f"{DOCKER_WORKING_DIR}/{test_repo.name}", PermissionLabels.READ_ONLY
61
61
  )
62
62
 
63
63
  @pytest.fixture(scope="function")
@@ -98,7 +98,9 @@ class TestMicroBot:
98
98
  verify_function = issue_1[1]
99
99
 
100
100
  test_repo_mount_ro = Mount(
101
- str(test_repo), DOCKER_WORKING_DIR, PermissionLabels.READ_ONLY
101
+ str(test_repo),
102
+ f"{DOCKER_WORKING_DIR}/{test_repo.name}",
103
+ PermissionLabels.READ_ONLY
102
104
  )
103
105
  testing_bot = MicroBot(
104
106
  model="azure-openai/mini-swe-agent-gpt5",
@@ -121,7 +123,7 @@ class TestMicroBot:
121
123
  log_file.write(response.result)
122
124
 
123
125
  test_repo_mount_rw = Mount(
124
- str(test_repo), DOCKER_WORKING_DIR, PermissionLabels.READ_WRITE
126
+ str(test_repo), f"{DOCKER_WORKING_DIR}/{test_repo.name}", PermissionLabels.READ_WRITE
125
127
  )
126
128
  coding_bot = MicroBot(
127
129
  model="azure-openai/mini-swe-agent-gpt5",
@@ -130,7 +132,7 @@ class TestMicroBot:
130
132
  )
131
133
 
132
134
  additional_mounts = Mount(
133
- log_file_path,
135
+ str(log_file_path),
134
136
  "/var/log",
135
137
  PermissionLabels.READ_ONLY,
136
138
  MountType.COPY,
@@ -148,12 +150,30 @@ class TestMicroBot:
148
150
 
149
151
  verify_function(test_repo)
150
152
 
151
- def test_incorrect_mount_type(self, log_file_path, test_repo):
153
+ def test_incorrect_code_mount_type(self, log_file_path, test_repo):
152
154
  assert test_repo is not None
153
155
  assert log_file_path is not None
154
156
 
155
157
  test_repo_mount_ro = Mount(
156
- str(test_repo), DOCKER_WORKING_DIR, PermissionLabels.READ_ONLY
158
+ str(test_repo),
159
+ f"{DOCKER_WORKING_DIR}/{test_repo.name}",
160
+ PermissionLabels.READ_ONLY,
161
+ MountType.COPY,
162
+ )
163
+
164
+ with pytest.raises(ValueError, match="Only MOUNT mount type is supported for folder_to_mount"):
165
+ testing_bot = MicroBot(
166
+ model="azure-openai/mini-swe-agent-gpt5",
167
+ system_prompt=SYSTEM_PROMPT,
168
+ folder_to_mount=test_repo_mount_ro,
169
+ )
170
+
171
+ def test_incorrect_copy_mount_type(self, log_file_path, test_repo):
172
+ assert test_repo is not None
173
+ assert log_file_path is not None
174
+
175
+ test_repo_mount_ro = Mount(
176
+ str(test_repo), f"{DOCKER_WORKING_DIR}/{test_repo.name}", PermissionLabels.READ_ONLY
157
177
  )
158
178
  testing_bot = MicroBot(
159
179
  model="azure-openai/mini-swe-agent-gpt5",
@@ -162,7 +182,7 @@ class TestMicroBot:
162
182
  )
163
183
 
164
184
  additional_mounts = Mount(
165
- log_file_path,
185
+ str(log_file_path),
166
186
  "/var/log",
167
187
  PermissionLabels.READ_ONLY,
168
188
  MountType.MOUNT, # MOUNT is not supported yet
@@ -178,7 +198,7 @@ class TestMicroBot:
178
198
  assert test_repo is not None
179
199
 
180
200
  test_repo_mount_ro = Mount(
181
- str(test_repo), DOCKER_WORKING_DIR, PermissionLabels.READ_ONLY
201
+ str(test_repo), f"{DOCKER_WORKING_DIR}/{test_repo.name}", PermissionLabels.READ_ONLY
182
202
  )
183
203
  with pytest.raises(ValueError, match="Unsupported model provider: provider"):
184
204
  MicroBot(
@@ -191,7 +211,7 @@ class TestMicroBot:
191
211
  assert test_repo is not None
192
212
 
193
213
  test_repo_mount_ro = Mount(
194
- str(test_repo), DOCKER_WORKING_DIR, PermissionLabels.READ_ONLY
214
+ str(test_repo), f"{DOCKER_WORKING_DIR}/{test_repo.name}", PermissionLabels.READ_ONLY
195
215
  )
196
216
  with pytest.raises(ValueError, match="Model should be in the format <provider>/<model_name>"):
197
217
  MicroBot(
@@ -2,19 +2,17 @@
2
2
  Integration tests for LocalDockerEnvironment
3
3
  """
4
4
  import pytest
5
- import shutil
6
5
  import os
7
- import time
8
6
  import socket
9
- from pathlib import Path
10
7
  import re
11
8
 
12
9
  # Add src to path for imports
13
10
  import sys
11
+
14
12
  sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../src")))
15
13
 
16
14
  from microbots.environment.local_docker.LocalDockerEnvironment import LocalDockerEnvironment
17
- from microbots.environment.Environment import CmdReturn
15
+ from microbots.extras.mount import Mount
18
16
 
19
17
  # Use the correct working directory path
20
18
  DOCKER_WORKING_DIR = "workdir"
@@ -82,22 +80,6 @@ class TestLocalDockerEnvironmentIntegration:
82
80
  assert "Hello World" in result.stdout
83
81
 
84
82
 
85
- @pytest.mark.integration
86
- @pytest.mark.docker
87
- def test_initialization_validation_errors(self):
88
- """Test that initialization properly validates parameters"""
89
- # Test permission without folder
90
- with pytest.raises(ValueError, match="permission provided but folder_to_mount is None"):
91
- LocalDockerEnvironment(port=8999, permission="READ_ONLY")
92
-
93
- # Test folder without permission
94
- with pytest.raises(ValueError, match="folder_to_mount provided but permission is None"):
95
- LocalDockerEnvironment(port=8999, folder_to_mount="/some/path")
96
-
97
- # Test invalid permission
98
- with pytest.raises(ValueError, match="permission must be 'READ_ONLY' or 'READ_WRITE' when provided"):
99
- LocalDockerEnvironment(port=8999, folder_to_mount="/some/path", permission="INVALID")
100
-
101
83
  @pytest.mark.integration
102
84
  @pytest.mark.docker
103
85
  def test_command_execution_basic(self, shared_env):
@@ -191,11 +173,15 @@ EOF"""
191
173
  mount_port = s.getsockname()[1]
192
174
 
193
175
  env = None
176
+ test_dir_mount = Mount(
177
+ host_path=test_dir,
178
+ sandbox_path=f"/{DOCKER_WORKING_DIR}/{os.path.basename(test_dir)}",
179
+ permission="READ_WRITE"
180
+ )
194
181
  try:
195
182
  env = LocalDockerEnvironment(
196
183
  port=mount_port,
197
- folder_to_mount=test_dir,
198
- permission="READ_WRITE"
184
+ folder_to_mount=test_dir_mount,
199
185
  )
200
186
 
201
187
  folder_name = os.path.basename(test_dir)
@@ -246,11 +232,16 @@ EOF"""
246
232
  mount_port = s.getsockname()[1]
247
233
 
248
234
  env = None
235
+ test_dir_mount = Mount(
236
+ host_path=test_dir,
237
+ sandbox_path=f"/{DOCKER_WORKING_DIR}/{os.path.basename(test_dir)}",
238
+ permission="READ_ONLY"
239
+ )
240
+
249
241
  try:
250
242
  env = LocalDockerEnvironment(
251
243
  port=mount_port,
252
- folder_to_mount=test_dir,
253
- permission="READ_ONLY"
244
+ folder_to_mount=test_dir_mount,
254
245
  )
255
246
 
256
247
  folder_name = os.path.basename(test_dir)
@@ -0,0 +1,37 @@
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ import pytest
5
+
6
+ sys.path.insert(
7
+ 0, f"{Path(__file__).resolve().parents[2].as_posix()}/src"
8
+ )
9
+
10
+ import logging
11
+ logging.basicConfig(level=logging.INFO)
12
+ logger = logging.getLogger(__name__)
13
+
14
+ from microbots.extras.mount import Mount
15
+
16
+ @pytest.mark.integration
17
+ def test_non_abs_sandbox_path(tmpdir: Path):
18
+ with pytest.raises(ValueError, match="sandbox_path must be an absolute path"):
19
+ Mount(
20
+ host_path=str(tmpdir),
21
+ sandbox_path="relative/path/in/sandbox",
22
+ permission="READ_ONLY",
23
+ )
24
+
25
+ @pytest.mark.integration
26
+ def test_host_file_sandbox_dir(tmpdir: Path):
27
+ host_file = tmpdir / "testfile.txt"
28
+ host_file.write_text("Sample content", encoding="utf-8")
29
+
30
+ mount = Mount(
31
+ host_path=str(host_file),
32
+ sandbox_path="/sandbox/dir/",
33
+ permission="READ_ONLY",
34
+ mount_type="copy",
35
+ )
36
+
37
+ assert mount.sandbox_path == "/sandbox/dir/testfile.txt"
@@ -17,12 +17,19 @@ def test_repo(tmpdir):
17
17
  capture_output=True,
18
18
  text=True
19
19
  )
20
+
21
+ if result.returncode != 0:
22
+ pytest.fail(
23
+ f"Failed to clone repository: {TEST_REPO}\n"
24
+ f"Return code: {result.returncode}\n"
25
+ f"Stdout: {result.stdout}\n"
26
+ f"Stderr: {result.stderr}"
27
+ )
28
+
20
29
  except subprocess.CalledProcessError as e:
21
30
  pytest.fail(
22
- f"Failed to clone repository: {TEST_REPO}\n"
23
- f"Return code: {e.returncode}\n"
24
- f"Stdout: {e.stdout}\n"
25
- f"Stderr: {e.stderr}"
31
+ f"Exception while cloning repository: {TEST_REPO}\n"
32
+ f"Exception: {str(e)}\n"
26
33
  )
27
34
 
28
35
  repo_path = Path(tmpdir / os.listdir(tmpdir)[0])
@@ -1,27 +0,0 @@
1
- from dataclasses import dataclass, field
2
- from enum import StrEnum
3
-
4
- from microbots.constants import PermissionLabels, PermissionMapping
5
- from microbots.utils.path import PathInfo, get_path_info
6
-
7
-
8
- class MountType(StrEnum):
9
- MOUNT = "mount"
10
- COPY = "copy"
11
-
12
-
13
- @dataclass
14
- class Mount:
15
- host_path: str
16
- sandbox_path: str
17
- permission: PermissionLabels
18
- mount_type: MountType = MountType.MOUNT
19
-
20
- # These will be set in __post_init__
21
- permission_key: str = field(init=False)
22
- host_path_info: PathInfo = field(init=False)
23
-
24
- def __post_init__(self):
25
- self.permission_key = PermissionMapping.MAPPING.get(self.permission)
26
- self.host_path_info = get_path_info(self.host_path)
27
- self.sandbox_path = f"{self.sandbox_path}/{self.host_path_info.base_name}"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes