tech-envision commited on
Commit
d39d8db
·
1 Parent(s): 1eafe79

Fix terminal tool to return output

Browse files
Files changed (3) hide show
  1. README.md +3 -3
  2. src/tools.py +13 -9
  3. src/vm.py +5 -5
README.md CHANGED
@@ -9,9 +9,9 @@ conversations can be resumed with context. One example tool is included:
9
  with network access. Use it to read uploaded documents under ``/data``, fetch
10
  web content via tools like ``curl`` or run any other commands. The assistant
11
  must invoke this tool to search online when unsure about a response. Output
12
- from ``stdout`` and ``stderr`` is captured when available. Commands are
13
- launched in the background with no timeout so the assistant can continue
14
- responding while they execute.
15
  The VM is created when a chat session starts and reused for all subsequent
16
  tool calls.
17
 
 
9
  with network access. Use it to read uploaded documents under ``/data``, fetch
10
  web content via tools like ``curl`` or run any other commands. The assistant
11
  must invoke this tool to search online when unsure about a response. Output
12
+ from ``stdout`` and ``stderr`` is captured when each command finishes.
13
+ Execution happens asynchronously so the assistant can continue responding
14
+ while the command runs.
15
  The VM is created when a chat session starts and reused for all subsequent
16
  tool calls.
17
 
src/tools.py CHANGED
@@ -27,29 +27,33 @@ def execute_terminal(command: str) -> str:
27
  The assistant must call this tool to search the internet whenever unsure
28
  about any detail.
29
 
30
- The command is executed with network access enabled. It runs in the
31
- background without a timeout so the assistant can continue responding
32
- while the command executes.
 
33
  """
34
  if not command:
35
  return "No command provided."
36
 
37
  if _VM:
38
  try:
39
- return _VM.execute(command, detach=True)
40
  except Exception as exc: # pragma: no cover - unforeseen errors
41
  return f"Failed to execute command in VM: {exc}"
42
 
43
  try:
44
- subprocess.Popen(
45
  command,
46
  shell=True,
47
- stdout=subprocess.DEVNULL,
48
- stderr=subprocess.DEVNULL,
49
- start_new_session=True,
50
  env=os.environ.copy(),
 
51
  )
52
- return "Command started in background."
 
 
 
53
  except Exception as exc: # pragma: no cover - unforeseen errors
54
  return f"Failed to execute command: {exc}"
55
 
 
27
  The assistant must call this tool to search the internet whenever unsure
28
  about any detail.
29
 
30
+ The command is executed with network access enabled. Output from
31
+ ``stdout`` and ``stderr`` is captured when the command completes.
32
+ Execution happens asynchronously so the assistant can continue
33
+ responding while the command runs.
34
  """
35
  if not command:
36
  return "No command provided."
37
 
38
  if _VM:
39
  try:
40
+ return _VM.execute(command, timeout=None)
41
  except Exception as exc: # pragma: no cover - unforeseen errors
42
  return f"Failed to execute command in VM: {exc}"
43
 
44
  try:
45
+ completed = subprocess.run(
46
  command,
47
  shell=True,
48
+ capture_output=True,
49
+ text=True,
 
50
  env=os.environ.copy(),
51
+ timeout=None,
52
  )
53
+ output = completed.stdout
54
+ if completed.stderr:
55
+ output = f"{output}\n{completed.stderr}" if output else completed.stderr
56
+ return output.strip()
57
  except Exception as exc: # pragma: no cover - unforeseen errors
58
  return f"Failed to execute command: {exc}"
59
 
src/vm.py CHANGED
@@ -68,7 +68,7 @@ class LinuxVM:
68
  raise RuntimeError(f"Failed to start VM: {exc}") from exc
69
 
70
  def execute(
71
- self, command: str, *, timeout: int = 3, detach: bool = False
72
  ) -> str:
73
  """Execute a command inside the running VM.
74
 
@@ -77,8 +77,8 @@ class LinuxVM:
77
  command:
78
  The shell command to run inside the container.
79
  timeout:
80
- Maximum time in seconds to wait for completion. Ignored when
81
- ``detach`` is ``True``.
82
  detach:
83
  Run the command in the background without waiting for it to finish.
84
  """
@@ -105,7 +105,7 @@ class LinuxVM:
105
  cmd,
106
  capture_output=True,
107
  text=True,
108
- timeout=None if detach else timeout,
109
  )
110
  except subprocess.TimeoutExpired as exc:
111
  return f"Command timed out after {timeout}s: {exc.cmd}"
@@ -118,7 +118,7 @@ class LinuxVM:
118
  return output.strip()
119
 
120
  async def execute_async(
121
- self, command: str, *, timeout: int = 3, detach: bool = False
122
  ) -> str:
123
  """Asynchronously execute ``command`` inside the running VM."""
124
  loop = asyncio.get_running_loop()
 
68
  raise RuntimeError(f"Failed to start VM: {exc}") from exc
69
 
70
  def execute(
71
+ self, command: str, *, timeout: int | None = 3, detach: bool = False
72
  ) -> str:
73
  """Execute a command inside the running VM.
74
 
 
77
  command:
78
  The shell command to run inside the container.
79
  timeout:
80
+ Maximum time in seconds to wait for completion. Set to ``None``
81
+ to wait indefinitely. Ignored when ``detach`` is ``True``.
82
  detach:
83
  Run the command in the background without waiting for it to finish.
84
  """
 
105
  cmd,
106
  capture_output=True,
107
  text=True,
108
+ timeout=None if detach or timeout is None else timeout,
109
  )
110
  except subprocess.TimeoutExpired as exc:
111
  return f"Command timed out after {timeout}s: {exc.cmd}"
 
118
  return output.strip()
119
 
120
  async def execute_async(
121
+ self, command: str, *, timeout: int | None = 3, detach: bool = False
122
  ) -> str:
123
  """Asynchronously execute ``command`` inside the running VM."""
124
  loop = asyncio.get_running_loop()