marin-shellbox 0.2.152.dev37849924057__tar.gz → 0.2.153.dev37866254792__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 (54) hide show
  1. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/PKG-INFO +2 -2
  2. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/pyproject.toml +2 -2
  3. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/shellsim/machine.py +22 -1
  4. marin_shellbox-0.2.153.dev37866254792/tests/test_shellsim_machine.py +106 -0
  5. marin_shellbox-0.2.152.dev37849924057/tests/test_shellsim_machine.py +0 -55
  6. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/.gitignore +0 -0
  7. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/README.md +0 -0
  8. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/benchmarks/tasktrove/compare.py +0 -0
  9. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/benchmarks/tasktrove/sample.py +0 -0
  10. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/__init__.py +0 -0
  11. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/agent.py +0 -0
  12. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/__init__.py +0 -0
  13. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/daytona/__init__.py +0 -0
  14. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/daytona/machine.py +0 -0
  15. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/docker/__init__.py +0 -0
  16. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/docker/machine.py +0 -0
  17. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/gvisor/__init__.py +0 -0
  18. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/gvisor/machine.py +0 -0
  19. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/iris/__init__.py +0 -0
  20. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/iris/machine.py +0 -0
  21. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/local/__init__.py +0 -0
  22. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/local/launch.py +0 -0
  23. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/local/machine.py +0 -0
  24. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/local/python_environment.py +0 -0
  25. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/qemu/__init__.py +0 -0
  26. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/qemu/bundle.py +0 -0
  27. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/qemu/environment.py +0 -0
  28. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/qemu/guest/disk-init +0 -0
  29. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/qemu/guest/init +0 -0
  30. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/qemu/guest/pty-agent.c +0 -0
  31. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/qemu/image.py +0 -0
  32. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/qemu/machine.py +0 -0
  33. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/shellsim/__init__.py +0 -0
  34. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/backends/shellsim/environment.py +0 -0
  35. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/image.py +0 -0
  36. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/src/shellbox/machine.py +0 -0
  37. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/harbor_smoke.py +0 -0
  38. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/manual/README.md +0 -0
  39. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/manual/check_machine.py +0 -0
  40. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/manual/check_prepared_image.py +0 -0
  41. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/manual/check_qemu_isolation.py +0 -0
  42. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/manual/check_shell.py +0 -0
  43. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/manual/task/instruction.md +0 -0
  44. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/manual/task/task.toml +0 -0
  45. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/manual/task/tests/test.sh +0 -0
  46. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/manual/tasktrove_nl2bash_command.sh +0 -0
  47. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/test_daytona_machine.py +0 -0
  48. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/test_docker_machine.py +0 -0
  49. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/test_gvisor_machine.py +0 -0
  50. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/test_harbor_shellsim.py +0 -0
  51. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/test_image_cache.py +0 -0
  52. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/test_iris_machine.py +0 -0
  53. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/test_local_machine.py +0 -0
  54. {marin_shellbox-0.2.152.dev37849924057 → marin_shellbox-0.2.153.dev37866254792}/tests/test_qemu_machine.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: marin-shellbox
3
- Version: 0.2.152.dev37849924057
3
+ Version: 0.2.153.dev37866254792
4
4
  Summary: Shell execution machines with local and remote backends
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: bubblewrap-bin==0.11.0; sys_platform == 'linux'
@@ -10,7 +10,7 @@ Requires-Dist: daytona==0.200.2; extra == 'daytona'
10
10
  Requires-Dist: marin-rigging>=0.2.0; extra == 'daytona'
11
11
  Provides-Extra: gvisor
12
12
  Provides-Extra: iris
13
- Requires-Dist: marin-iris==0.2.152.dev37849924057; extra == 'iris'
13
+ Requires-Dist: marin-iris==0.2.153.dev37866254792; extra == 'iris'
14
14
  Provides-Extra: qemu
15
15
  Requires-Dist: quicksand-qemu<0.6,>=0.5.12; extra == 'qemu'
16
16
  Provides-Extra: shellsim
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "marin-shellbox"
7
- version = "0.2.152.dev37849924057"
7
+ version = "0.2.153.dev37866254792"
8
8
  description = "Shell execution machines with local and remote backends"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -16,7 +16,7 @@ shellsim = ["shellsim>=0.1.29,<0.2", "marin-rigging>=0.2.0"]
16
16
  gvisor = []
17
17
  daytona = ["daytona==0.200.2", "marin-rigging>=0.2.0"]
18
18
  iris = [
19
- "marin-iris==0.2.152.dev37849924057",
19
+ "marin-iris==0.2.153.dev37866254792",
20
20
  ]
21
21
 
22
22
  [project.scripts]
@@ -31,6 +31,7 @@ DEFAULT_CPU_LIMIT = 10_000_000_000
31
31
  DEFAULT_DISK_LIMIT = 256 * 1024 * 1024
32
32
  DEFAULT_OUTPUT_LIMIT = 128 * 1024 * 1024
33
33
  DEFAULT_MEMORY_MB = 256
34
+ SIMULATOR_PANIC_EXIT_CODE = 1
34
35
  ENV_NAME = re.compile(r"[A-Za-z_][A-Za-z0-9_]*\Z")
35
36
 
36
37
 
@@ -45,7 +46,16 @@ class ShellSimShellSession:
45
46
  async def execute(
46
47
  self, command: str, *, wait: float = 120, output_limit_bytes: int = DEFAULT_MACHINE_OUTPUT_LIMIT_BYTES
47
48
  ) -> ShellUpdate:
48
- result = await self.machine._run_source(command)
49
+ try:
50
+ result = await self.machine._run_source(command)
51
+ except shellsim.SimulationError as error:
52
+ output = str(error).encode()
53
+ return ShellUpdate(
54
+ output[:output_limit_bytes],
55
+ ShellStatus.COMPLETED,
56
+ SIMULATOR_PANIC_EXIT_CODE,
57
+ len(output) > output_limit_bytes,
58
+ )
49
59
  output = result.stdout + result.stderr
50
60
  return ShellUpdate(
51
61
  output[:output_limit_bytes],
@@ -123,6 +133,17 @@ class ShellSimMachine:
123
133
  except TimeoutError:
124
134
  self._closed = True
125
135
  return Result(None, b"", b"", False, False, ExitReason.TIMED_OUT)
136
+ except shellsim.SimulationError as error:
137
+ stderr = str(error).encode()
138
+ limit = command.output_limit_bytes
139
+ return Result(
140
+ SIMULATOR_PANIC_EXIT_CODE,
141
+ b"",
142
+ stderr[:limit],
143
+ False,
144
+ len(stderr) > limit,
145
+ ExitReason.EXITED,
146
+ )
126
147
  limit = command.output_limit_bytes
127
148
  return Result(
128
149
  result.returncode,
@@ -0,0 +1,106 @@
1
+ # Copyright The Marin Authors
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Behavior checks for the simulated shell and verifier boundary."""
5
+
6
+ import asyncio
7
+ from pathlib import Path
8
+
9
+ from shellbox.backends.shellsim.machine import ShellSimMachineFactory
10
+ from shellbox.machine import Command, ExitReason, MachineSpec, ShellSimBuiltins, ShellStatus
11
+
12
+ PANIC_COMMAND = "cat /workspace/numbers.txt | python3 -c 'import sys; print(sum(int(line) for line in sys.stdin))'"
13
+ PANIC_OUTPUT_LIMIT_BYTES = 64
14
+
15
+
16
+ def test_shell_state_and_one_shot_command() -> None:
17
+ async def check() -> None:
18
+ machine = await ShellSimMachineFactory().create(MachineSpec(ShellSimBuiltins()))
19
+ try:
20
+ shell = await machine.open_shell()
21
+ first = await shell.execute(
22
+ 'cd /tmp; export ANSWER=hello; answer() { echo "$ANSWER"; }; echo file >/workspace/result'
23
+ )
24
+ assert first.status is ShellStatus.COMPLETED
25
+ second = await shell.execute("pwd; answer")
26
+ assert second.output == b"/tmp\nhello\n"
27
+
28
+ verifier = await machine.run(Command(("sh", "-c", "pwd; cat /workspace/result")))
29
+ assert verifier.stdout == b"/workspace\nfile\n"
30
+ after = await shell.execute("pwd; answer")
31
+ assert after.output == b"/tmp\nhello\n"
32
+ finally:
33
+ await machine.close()
34
+
35
+ asyncio.run(check())
36
+
37
+
38
+ def test_file_transfer_and_bounded_output(tmp_path: Path) -> None:
39
+ async def check() -> None:
40
+ machine = await ShellSimMachineFactory().create(MachineSpec(ShellSimBuiltins()))
41
+ try:
42
+ source = tmp_path / "source"
43
+ source.mkdir()
44
+ (source / "data.txt").write_text("payload")
45
+ await machine.upload(source, "/workspace/imported")
46
+ shell = await machine.open_shell()
47
+ result = await shell.execute("cat /workspace/imported/data.txt; printf 'x'", output_limit_bytes=4)
48
+ assert result.output == b"payl"
49
+ assert result.truncated
50
+
51
+ target = tmp_path / "downloaded"
52
+ target.mkdir()
53
+ await machine.download("/workspace/imported", target)
54
+ assert (target / "data.txt").read_text() == "payload"
55
+ finally:
56
+ await machine.close()
57
+
58
+ asyncio.run(check())
59
+
60
+
61
+ def test_run_simulator_panic_returns_failed_result(tmp_path: Path) -> None:
62
+ async def check() -> None:
63
+ machine = await ShellSimMachineFactory().create(MachineSpec(ShellSimBuiltins()))
64
+ try:
65
+ source = tmp_path / "numbers.txt"
66
+ source.write_text("1\n2\n")
67
+ await machine.upload(source, "/workspace/numbers.txt")
68
+
69
+ result = await machine.run(Command(("sh", "-c", PANIC_COMMAND), output_limit_bytes=PANIC_OUTPUT_LIMIT_BYTES))
70
+ assert result.reason is ExitReason.EXITED
71
+ assert result.exit_code == 1
72
+ assert result.stdout == b""
73
+ assert result.stderr.startswith(b"shellsim operation panicked")
74
+ assert len(result.stderr) == PANIC_OUTPUT_LIMIT_BYTES
75
+ assert result.stderr_truncated
76
+
77
+ followup = await machine.run(Command(("cat", "/workspace/numbers.txt")))
78
+ assert (followup.exit_code, followup.stdout) == (0, b"1\n2\n")
79
+ finally:
80
+ await machine.close()
81
+
82
+ asyncio.run(check())
83
+
84
+
85
+ def test_shell_session_simulator_panic_returns_failed_update(tmp_path: Path) -> None:
86
+ async def check() -> None:
87
+ machine = await ShellSimMachineFactory().create(MachineSpec(ShellSimBuiltins()))
88
+ try:
89
+ source = tmp_path / "numbers.txt"
90
+ source.write_text("1\n2\n")
91
+ await machine.upload(source, "/workspace/numbers.txt")
92
+ shell = await machine.open_shell()
93
+
94
+ update = await shell.execute(PANIC_COMMAND, output_limit_bytes=PANIC_OUTPUT_LIMIT_BYTES)
95
+ assert update.status is ShellStatus.COMPLETED
96
+ assert update.exit_code == 1
97
+ assert update.output.startswith(b"shellsim operation panicked")
98
+ assert len(update.output) == PANIC_OUTPUT_LIMIT_BYTES
99
+ assert update.truncated
100
+
101
+ followup = await shell.execute("cat /workspace/numbers.txt")
102
+ assert (followup.exit_code, followup.output) == (0, b"1\n2\n")
103
+ finally:
104
+ await machine.close()
105
+
106
+ asyncio.run(check())
@@ -1,55 +0,0 @@
1
- # Copyright The Marin Authors
2
- # SPDX-License-Identifier: Apache-2.0
3
-
4
- """Behavior checks for the simulated shell and verifier boundary."""
5
-
6
- import asyncio
7
- from pathlib import Path
8
-
9
- from shellbox.backends.shellsim.machine import ShellSimMachineFactory
10
- from shellbox.machine import Command, MachineSpec, ShellSimBuiltins, ShellStatus
11
-
12
-
13
- def test_shell_state_and_one_shot_command() -> None:
14
- async def check() -> None:
15
- machine = await ShellSimMachineFactory().create(MachineSpec(ShellSimBuiltins()))
16
- try:
17
- shell = await machine.open_shell()
18
- first = await shell.execute(
19
- 'cd /tmp; export ANSWER=hello; answer() { echo "$ANSWER"; }; echo file >/workspace/result'
20
- )
21
- assert first.status is ShellStatus.COMPLETED
22
- second = await shell.execute("pwd; answer")
23
- assert second.output == b"/tmp\nhello\n"
24
-
25
- verifier = await machine.run(Command(("sh", "-c", "pwd; cat /workspace/result")))
26
- assert verifier.stdout == b"/workspace\nfile\n"
27
- after = await shell.execute("pwd; answer")
28
- assert after.output == b"/tmp\nhello\n"
29
- finally:
30
- await machine.close()
31
-
32
- asyncio.run(check())
33
-
34
-
35
- def test_file_transfer_and_bounded_output(tmp_path: Path) -> None:
36
- async def check() -> None:
37
- machine = await ShellSimMachineFactory().create(MachineSpec(ShellSimBuiltins()))
38
- try:
39
- source = tmp_path / "source"
40
- source.mkdir()
41
- (source / "data.txt").write_text("payload")
42
- await machine.upload(source, "/workspace/imported")
43
- shell = await machine.open_shell()
44
- result = await shell.execute("cat /workspace/imported/data.txt; printf 'x'", output_limit_bytes=4)
45
- assert result.output == b"payl"
46
- assert result.truncated
47
-
48
- target = tmp_path / "downloaded"
49
- target.mkdir()
50
- await machine.download("/workspace/imported", target)
51
- assert (target / "data.txt").read_text() == "payload"
52
- finally:
53
- await machine.close()
54
-
55
- asyncio.run(check())