shellsim 0.1.22__tar.gz → 0.1.25__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 (308) hide show
  1. {shellsim-0.1.22 → shellsim-0.1.25}/Cargo.lock +1 -1
  2. {shellsim-0.1.22 → shellsim-0.1.25}/Cargo.toml +1 -1
  3. {shellsim-0.1.22 → shellsim-0.1.25}/PKG-INFO +14 -1
  4. {shellsim-0.1.22 → shellsim-0.1.25}/README.md +11 -0
  5. {shellsim-0.1.22 → shellsim-0.1.25}/pyproject.toml +4 -0
  6. {shellsim-0.1.22 → shellsim-0.1.25}/python/native/Cargo.lock +3 -2
  7. {shellsim-0.1.22 → shellsim-0.1.25}/python/native/Cargo.toml +2 -1
  8. {shellsim-0.1.22 → shellsim-0.1.25}/python/native/src/lib.rs +261 -2
  9. {shellsim-0.1.22 → shellsim-0.1.25}/python/shellsim/__init__.py +15 -0
  10. {shellsim-0.1.22 → shellsim-0.1.25}/python/shellsim/_api.py +277 -1
  11. {shellsim-0.1.22 → shellsim-0.1.25}/python/shellsim/_cli.py +68 -1
  12. shellsim-0.1.25/python/shellsim/display_host.py +205 -0
  13. shellsim-0.1.25/python/shellsim/package.py +620 -0
  14. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/net.rs +7 -6
  15. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/wasm.rs +6 -10
  16. {shellsim-0.1.22 → shellsim-0.1.25}/src/display.rs +7 -0
  17. {shellsim-0.1.22 → shellsim-0.1.25}/src/exec.rs +2 -1
  18. {shellsim-0.1.22 → shellsim-0.1.25}/src/harness.rs +351 -12
  19. {shellsim-0.1.22 → shellsim-0.1.25}/src/harness_manager.rs +2 -2
  20. {shellsim-0.1.22 → shellsim-0.1.25}/src/main.rs +18 -7
  21. {shellsim-0.1.22 → shellsim-0.1.25}/src/net.rs +248 -5
  22. {shellsim-0.1.22 → shellsim-0.1.25}/src/netcmd.rs +50 -22
  23. {shellsim-0.1.22 → shellsim-0.1.25}/src/program.rs +1 -1
  24. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/http.rs +52 -41
  25. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/asyncio.rs +3 -2
  26. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/host.rs +13 -2
  27. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/native_runtime.rs +2 -2
  28. {shellsim-0.1.22 → shellsim-0.1.25}/src/scheduler.rs +2 -0
  29. {shellsim-0.1.22 → shellsim-0.1.25}/src/syscalls.rs +7 -5
  30. {shellsim-0.1.22 → shellsim-0.1.25}/src/vfs.rs +22 -0
  31. {shellsim-0.1.22 → shellsim-0.1.25}/LICENSE +0 -0
  32. {shellsim-0.1.22 → shellsim-0.1.25}/python/shellsim/__main__.py +0 -0
  33. {shellsim-0.1.22 → shellsim-0.1.25}/python/shellsim/py.typed +0 -0
  34. {shellsim-0.1.22 → shellsim-0.1.25}/python/shellsim/python.py +0 -0
  35. {shellsim-0.1.22 → shellsim-0.1.25}/rust-toolchain.toml +0 -0
  36. {shellsim-0.1.22 → shellsim-0.1.25}/src/arith.rs +0 -0
  37. {shellsim-0.1.22 → shellsim-0.1.25}/src/bin/shellsim-python.rs +0 -0
  38. {shellsim-0.1.22 → shellsim-0.1.25}/src/clock.rs +0 -0
  39. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/archives.rs +0 -0
  40. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/arcmd.rs +0 -0
  41. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/awk/ast.rs +0 -0
  42. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/awk/eval.rs +0 -0
  43. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/awk/lexer.rs +0 -0
  44. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/awk/parser.rs +0 -0
  45. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/awk.rs +0 -0
  46. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/bc.rs +0 -0
  47. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/builtins.rs +0 -0
  48. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/dd.rs +0 -0
  49. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/diff.rs +0 -0
  50. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/echo.rs +0 -0
  51. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/expr.rs +0 -0
  52. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/find.rs +0 -0
  53. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/format.rs +0 -0
  54. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/fs.rs +0 -0
  55. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/apply.rs +0 -0
  56. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/commit.rs +0 -0
  57. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/compare.rs +0 -0
  58. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/config.rs +0 -0
  59. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/conflict.rs +0 -0
  60. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/diff.rs +0 -0
  61. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/ignore.rs +0 -0
  62. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/import.rs +0 -0
  63. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/log.rs +0 -0
  64. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/merge.rs +0 -0
  65. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/mod.rs +0 -0
  66. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/plumbing.rs +0 -0
  67. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/rebase.rs +0 -0
  68. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/refs.rs +0 -0
  69. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/repo.rs +0 -0
  70. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/stash.rs +0 -0
  71. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/switch.rs +0 -0
  72. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/git/worktree.rs +0 -0
  73. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/grep.rs +0 -0
  74. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/hashing.rs +0 -0
  75. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/makecmd.rs +0 -0
  76. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/mod.rs +0 -0
  77. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/options.rs +0 -0
  78. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/patch.rs +0 -0
  79. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/pkg.rs +0 -0
  80. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/printf.rs +0 -0
  81. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/proc.rs +0 -0
  82. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/regex_compat.rs +0 -0
  83. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/rgcmd.rs +0 -0
  84. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/sed.rs +0 -0
  85. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/sort.rs +0 -0
  86. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/streams.rs +0 -0
  87. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/system.rs +0 -0
  88. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/tarcmd.rs +0 -0
  89. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/text.rs +0 -0
  90. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/unavailable.rs +0 -0
  91. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/util.rs +0 -0
  92. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/xargs.rs +0 -0
  93. {shellsim-0.1.22 → shellsim-0.1.25}/src/commands/zipcmd.rs +0 -0
  94. {shellsim-0.1.22 → shellsim-0.1.25}/src/corpus.rs +0 -0
  95. {shellsim-0.1.22 → shellsim-0.1.25}/src/descriptors.rs +0 -0
  96. {shellsim-0.1.22 → shellsim-0.1.25}/src/expand.rs +0 -0
  97. {shellsim-0.1.22 → shellsim-0.1.25}/src/hashes.rs +0 -0
  98. {shellsim-0.1.22 → shellsim-0.1.25}/src/host_ingest.rs +0 -0
  99. {shellsim-0.1.22 → shellsim-0.1.25}/src/interp.rs +0 -0
  100. {shellsim-0.1.22 → shellsim-0.1.25}/src/jqcmd.rs +0 -0
  101. {shellsim-0.1.22 → shellsim-0.1.25}/src/lib.rs +0 -0
  102. {shellsim-0.1.22 → shellsim-0.1.25}/src/mcp.rs +0 -0
  103. {shellsim-0.1.22 → shellsim-0.1.25}/src/process.rs +0 -0
  104. {shellsim-0.1.22 → shellsim-0.1.25}/src/profile.rs +0 -0
  105. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/cat.rs +0 -0
  106. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/env.rs +0 -0
  107. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/find.rs +0 -0
  108. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/head.rs +0 -0
  109. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/make.rs +0 -0
  110. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/nice.rs +0 -0
  111. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/nohup.rs +0 -0
  112. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/sleep.rs +0 -0
  113. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/tee.rs +0 -0
  114. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/timeout.rs +0 -0
  115. {shellsim-0.1.22 → shellsim-0.1.25}/src/program/xargs.rs +0 -0
  116. {shellsim-0.1.22 → shellsim-0.1.25}/src/pseudo_fs.rs +0 -0
  117. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/ast.rs +0 -0
  118. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/bytecode.rs +0 -0
  119. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/compiler.rs +0 -0
  120. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/complex.rs +0 -0
  121. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/cpython_names.rs +0 -0
  122. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/exception_types.rs +0 -0
  123. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/filesystem.rs +0 -0
  124. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/float_text.rs +0 -0
  125. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/hash.rs +0 -0
  126. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/heap.rs +0 -0
  127. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/lexer.rs +0 -0
  128. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/mapping.rs +0 -0
  129. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/mod.rs +0 -0
  130. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/native.rs +0 -0
  131. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/number.rs +0 -0
  132. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/object_model.rs +0 -0
  133. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/parser.rs +0 -0
  134. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/process.rs +0 -0
  135. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/protocol.rs +0 -0
  136. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/slice.rs +0 -0
  137. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/source.rs +0 -0
  138. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/argparse.rs +0 -0
  139. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/base64.rs +0 -0
  140. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/bisect.rs +0 -0
  141. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/collections.rs +0 -0
  142. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/core.rs +0 -0
  143. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/dataclasses.rs +0 -0
  144. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/enum.rs +0 -0
  145. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/frozen.rs +0 -0
  146. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/functools.rs +0 -0
  147. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/hashlib.rs +0 -0
  148. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/heapq.rs +0 -0
  149. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/http.rs +0 -0
  150. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/importlib.rs +0 -0
  151. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/introspect.rs +0 -0
  152. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/itertools.rs +0 -0
  153. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/json.rs +0 -0
  154. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/mapping_views.rs +0 -0
  155. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/math.rs +0 -0
  156. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/mod.rs +0 -0
  157. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/args.rs +0 -0
  158. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/array.rs +0 -0
  159. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/construct.rs +0 -0
  160. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/convert.rs +0 -0
  161. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/dtype.rs +0 -0
  162. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/dtype_object.rs +0 -0
  163. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/element.rs +0 -0
  164. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/errstate.rs +0 -0
  165. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/format.rs +0 -0
  166. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/index.rs +0 -0
  167. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/io.rs +0 -0
  168. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/layout.rs +0 -0
  169. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/linalg/dense.rs +0 -0
  170. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/linalg.rs +0 -0
  171. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/math.rs +0 -0
  172. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/mod.rs +0 -0
  173. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/ndarray.rs +0 -0
  174. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/ops.rs +0 -0
  175. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/products.rs +0 -0
  176. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/random/bitgen.rs +0 -0
  177. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/random/discrete.rs +0 -0
  178. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/random/gamma.rs +0 -0
  179. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/random/integers.rs +0 -0
  180. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/random/normal.rs +0 -0
  181. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/random/sequence.rs +0 -0
  182. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/random.rs +0 -0
  183. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/reduce.rs +0 -0
  184. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/scalar.rs +0 -0
  185. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/select.rs +0 -0
  186. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/shape/broadcast.rs +0 -0
  187. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/shape/diagonal.rs +0 -0
  188. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/shape/join.rs +0 -0
  189. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/shape/repeat.rs +0 -0
  190. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/shape.rs +0 -0
  191. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/sort/bincount.rs +0 -0
  192. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/sort/search.rs +0 -0
  193. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/sort.rs +0 -0
  194. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/ufunc/masked.rs +0 -0
  195. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/ufunc.rs +0 -0
  196. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/numpy/underflow.rs +0 -0
  197. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/operator.rs +0 -0
  198. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/os.rs +0 -0
  199. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/pytest.rs +0 -0
  200. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/re.rs +0 -0
  201. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/scipy/mod.rs +0 -0
  202. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/scipy/special/erf.rs +0 -0
  203. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/scipy/special/gamma.rs +0 -0
  204. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/scipy/special/ibeta.rs +0 -0
  205. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/scipy/special/igam.rs +0 -0
  206. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/scipy/special/meter.rs +0 -0
  207. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/scipy/special/mod.rs +0 -0
  208. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/scipy/special/zeta.rs +0 -0
  209. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/abc.py +0 -0
  210. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/asyncio.py +0 -0
  211. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/base64.py +0 -0
  212. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/cmath.py +0 -0
  213. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/codecs.py +0 -0
  214. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/collections.py +0 -0
  215. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/contextlib.py +0 -0
  216. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/copy.py +0 -0
  217. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/csv.py +0 -0
  218. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/datetime.py +0 -0
  219. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/fractions.py +0 -0
  220. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/functools.py +0 -0
  221. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/glob.py +0 -0
  222. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/hashlib.py +0 -0
  223. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/http.py +0 -0
  224. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/http_client.py +0 -0
  225. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/importlib.py +0 -0
  226. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/inspect.py +0 -0
  227. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/io.py +0 -0
  228. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/json.py +0 -0
  229. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/keyword.py +0 -0
  230. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/logging.py +0 -0
  231. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/__init__.py +0 -0
  232. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/_creation.py +0 -0
  233. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/_errors.py +0 -0
  234. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/_io.py +0 -0
  235. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/_math.py +0 -0
  236. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/_printing.py +0 -0
  237. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/_products.py +0 -0
  238. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/_sets.py +0 -0
  239. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/_shapes.py +0 -0
  240. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/_stats.py +0 -0
  241. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/exceptions.py +0 -0
  242. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/fft.py +0 -0
  243. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/linalg.py +0 -0
  244. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/random.py +0 -0
  245. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/numpy/testing.py +0 -0
  246. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/operator.py +0 -0
  247. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/os.py +0 -0
  248. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/pathlib.py +0 -0
  249. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/pytest.py +0 -0
  250. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/random.py +0 -0
  251. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/__init__.py +0 -0
  252. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/integrate.py +0 -0
  253. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/interpolate.py +0 -0
  254. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/linalg.py +0 -0
  255. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/optimize.py +0 -0
  256. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/spatial/__init__.py +0 -0
  257. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/spatial/distance.py +0 -0
  258. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/special.py +0 -0
  259. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/stats/__init__.py +0 -0
  260. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/stats/_describe.py +0 -0
  261. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/stats/_distributions.py +0 -0
  262. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/scipy/stats/_tests.py +0 -0
  263. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/shutil.py +0 -0
  264. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/signal.py +0 -0
  265. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/statistics.py +0 -0
  266. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/struct.py +0 -0
  267. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/subprocess.py +0 -0
  268. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/tempfile.py +0 -0
  269. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/textwrap.py +0 -0
  270. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/urllib.py +0 -0
  271. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/urllib_error.py +0 -0
  272. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/urllib_request.py +0 -0
  273. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/uuid.py +0 -0
  274. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/warnings.py +0 -0
  275. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/zipfile.py +0 -0
  276. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/source/zlib.py +0 -0
  277. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/string.rs +0 -0
  278. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/struct.rs +0 -0
  279. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/subprocess.rs +0 -0
  280. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/sys.rs +0 -0
  281. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/time.rs +0 -0
  282. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/typing.rs +0 -0
  283. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/unittest.rs +0 -0
  284. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/vfs.rs +0 -0
  285. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/warnings.rs +0 -0
  286. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/stdlib/zlib.rs +0 -0
  287. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/string.rs +0 -0
  288. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/token.rs +0 -0
  289. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/unicode/tables.rs +0 -0
  290. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/unicode.rs +0 -0
  291. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/calls.rs +0 -0
  292. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/dispatch.rs +0 -0
  293. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/equality.rs +0 -0
  294. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/format.rs +0 -0
  295. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/hashing.rs +0 -0
  296. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/iteration.rs +0 -0
  297. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/mappings.rs +0 -0
  298. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/namespace.rs +0 -0
  299. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/objects.rs +0 -0
  300. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/operations.rs +0 -0
  301. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm/summation.rs +0 -0
  302. {shellsim-0.1.22 → shellsim-0.1.25}/src/python/vm.rs +0 -0
  303. {shellsim-0.1.22 → shellsim-0.1.25}/src/realtime.rs +0 -0
  304. {shellsim-0.1.22 → shellsim-0.1.25}/src/resources.rs +0 -0
  305. {shellsim-0.1.22 → shellsim-0.1.25}/src/sandbox.rs +0 -0
  306. {shellsim-0.1.22 → shellsim-0.1.25}/src/scenario.rs +0 -0
  307. {shellsim-0.1.22 → shellsim-0.1.25}/src/shell.rs +0 -0
  308. {shellsim-0.1.22 → shellsim-0.1.25}/src/telemetry.rs +0 -0
@@ -861,7 +861,7 @@ dependencies = [
861
861
 
862
862
  [[package]]
863
863
  name = "shellsim"
864
- version = "0.1.22"
864
+ version = "0.1.25"
865
865
  dependencies = [
866
866
  "base64",
867
867
  "crc32fast",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "shellsim"
3
- version = "0.1.22"
3
+ version = "0.1.25"
4
4
  edition = "2021"
5
5
  description = "In-process BusyBox-like environment for RL rollouts and agent tests"
6
6
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shellsim
3
- Version: 0.1.22
3
+ Version: 0.1.25
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Operating System :: MacOS :: MacOS X
6
6
  Classifier: Operating System :: Microsoft :: Windows
@@ -10,6 +10,8 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
10
10
  Classifier: Programming Language :: Rust
11
11
  Classifier: Topic :: Software Development :: Testing
12
12
  Classifier: Typing :: Typed
13
+ Requires-Dist: shellsim-c-toolchain==0.1.25 ; extra == 'c'
14
+ Provides-Extra: c
13
15
  License-File: LICENSE
14
16
  Summary: In-process BusyBox-like environment for RL rollouts and agent tests
15
17
  Keywords: agents,containers,reinforcement-learning,sandbox,simulation,testing
@@ -84,6 +86,10 @@ shellsim -c 'printf "b\na\n" | sort'
84
86
  shellsim --root ./project -c 'python3.14 test.py'
85
87
  ```
86
88
 
89
+ Install `shellsim[c]` instead when a `.shl` package needs the separately distributed C-to-Wasm
90
+ toolchain. The core wheel does not contain TinyCC or the WASI sysroot. See
91
+ [the C toolchain contract](docs/shl-packages.md#optional-c-toolchain).
92
+
87
93
  `--root` copies the selected host tree into a disposable `/work` snapshot. With no `-c` and a
88
94
  terminal attached, `shellsim` starts a persistent interactive session.
89
95
 
@@ -206,6 +212,13 @@ as a stdio MCP server. `shellsim replay scenario.ndjson` reruns checked action t
206
212
  `shellsim corpus manifest.json` runs frozen shell and Python workloads under a versioned stock
207
213
  environment and emits a classified JSON report. See [compatibility testing](docs/compatibility-testing.md).
208
214
 
215
+ `shellsim serve --host-tools` grants a virtual guest-to-host tool route at construction. The
216
+ trusted runner receives typed calls in `poll_action` and completes them with `respond_tool_call`;
217
+ ordinary sessions have no such route. See [`.shl` package design](docs/shl-packages.md).
218
+
219
+ Python hosts can instead use `shellsim.Container(tools={"name": handler})` to register handlers
220
+ at construction and call `container.run(...)` without a CLI subprocess or manual polling.
221
+
209
222
  ```sh
210
223
  printf '%s\n' \
211
224
  '{"id":1,"op":"execute","source":"printf hello > result"}' \
@@ -61,6 +61,10 @@ shellsim -c 'printf "b\na\n" | sort'
61
61
  shellsim --root ./project -c 'python3.14 test.py'
62
62
  ```
63
63
 
64
+ Install `shellsim[c]` instead when a `.shl` package needs the separately distributed C-to-Wasm
65
+ toolchain. The core wheel does not contain TinyCC or the WASI sysroot. See
66
+ [the C toolchain contract](docs/shl-packages.md#optional-c-toolchain).
67
+
64
68
  `--root` copies the selected host tree into a disposable `/work` snapshot. With no `-c` and a
65
69
  terminal attached, `shellsim` starts a persistent interactive session.
66
70
 
@@ -183,6 +187,13 @@ as a stdio MCP server. `shellsim replay scenario.ndjson` reruns checked action t
183
187
  `shellsim corpus manifest.json` runs frozen shell and Python workloads under a versioned stock
184
188
  environment and emits a classified JSON report. See [compatibility testing](docs/compatibility-testing.md).
185
189
 
190
+ `shellsim serve --host-tools` grants a virtual guest-to-host tool route at construction. The
191
+ trusted runner receives typed calls in `poll_action` and completes them with `respond_tool_call`;
192
+ ordinary sessions have no such route. See [`.shl` package design](docs/shl-packages.md).
193
+
194
+ Python hosts can instead use `shellsim.Container(tools={"name": handler})` to register handlers
195
+ at construction and call `container.run(...)` without a CLI subprocess or manual polling.
196
+
186
197
  ```sh
187
198
  printf '%s\n' \
188
199
  '{"id":1,"op":"execute","source":"printf hello > result"}' \
@@ -24,6 +24,9 @@ classifiers = [
24
24
  ]
25
25
  keywords = ["agents", "containers", "reinforcement-learning", "sandbox", "simulation", "testing"]
26
26
 
27
+ [project.optional-dependencies]
28
+ c = ["shellsim-c-toolchain==0.1.25"]
29
+
27
30
  [project.urls]
28
31
  Homepage = "https://github.com/rjpower/shellsim"
29
32
  Issues = "https://github.com/rjpower/shellsim/issues"
@@ -52,6 +55,7 @@ include = [
52
55
  "infra/*.py",
53
56
  "infra/**/*.py",
54
57
  "python/**/*.py",
58
+ "toolchain/**/*.py",
55
59
  "tests/python/test_*.py",
56
60
  "tests/python_package/**/*.py",
57
61
  "tests/tooling/**/*.py",
@@ -925,7 +925,7 @@ dependencies = [
925
925
 
926
926
  [[package]]
927
927
  name = "shellsim"
928
- version = "0.1.22"
928
+ version = "0.1.25"
929
929
  dependencies = [
930
930
  "base64",
931
931
  "crc32fast",
@@ -949,8 +949,9 @@ dependencies = [
949
949
 
950
950
  [[package]]
951
951
  name = "shellsim-python-bindings"
952
- version = "0.1.22"
952
+ version = "0.1.25"
953
953
  dependencies = [
954
+ "base64",
954
955
  "pyo3",
955
956
  "serde",
956
957
  "serde_json",
@@ -2,7 +2,7 @@
2
2
 
3
3
  [package]
4
4
  name = "shellsim-python-bindings"
5
- version = "0.1.22"
5
+ version = "0.1.25"
6
6
  edition = "2021"
7
7
  description = "Python bindings for shellsim"
8
8
  license = "Apache-2.0"
@@ -14,6 +14,7 @@ name = "shellsim_native"
14
14
  crate-type = ["cdylib"]
15
15
 
16
16
  [dependencies]
17
+ base64 = "0.22"
17
18
  pyo3 = { version = "0.28", features = ["abi3-py39", "extension-module"] }
18
19
  serde = { version = "1", features = ["derive"] }
19
20
  serde_json = "1"
@@ -12,15 +12,21 @@ use std::path::Path;
12
12
  use std::sync::Mutex;
13
13
  use std::thread;
14
14
 
15
+ use base64::engine::general_purpose::STANDARD;
16
+ use base64::Engine;
15
17
  use pyo3::create_exception;
16
18
  use pyo3::exceptions::PyException;
17
19
  use pyo3::prelude::*;
18
20
  use pyo3::types::PyBytes;
19
21
  use serde::Serialize;
22
+ use shellsim::display::KeyEvent;
23
+ use shellsim::harness::{HarnessOperation, HarnessRequest, HarnessSession};
20
24
  use shellsim::net::NetworkRequest;
25
+ use shellsim::realtime::ClockMode;
21
26
  use shellsim::{CommandTrust, InvocationEvent, Limits, RunOutcome};
22
27
 
23
28
  const ACTION_STACK_BYTES: usize = 8 * 1024 * 1024;
29
+ type PythonFrame = (u64, u32, u32, Py<PyBytes>);
24
30
 
25
31
  create_exception!(
26
32
  shellsim._native,
@@ -67,6 +73,253 @@ struct NativeEnvironment {
67
73
  environment: Mutex<shellsim::Environment>,
68
74
  }
69
75
 
76
+ /// In-process host control for a machine granted the virtual tool endpoint.
77
+ #[pyclass(module = "shellsim._native")]
78
+ struct NativeContainer {
79
+ session: Mutex<HarnessSession>,
80
+ }
81
+
82
+ #[pymethods]
83
+ impl NativeContainer {
84
+ #[new]
85
+ fn new(cpu: u64, memory: u64, disk: u64, output: u64, real_time: bool) -> Self {
86
+ Self {
87
+ session: Mutex::new(HarnessSession::with_clock_and_host_tools(
88
+ Limits {
89
+ cpu,
90
+ memory,
91
+ disk,
92
+ output,
93
+ },
94
+ if real_time {
95
+ ClockMode::RealTime
96
+ } else {
97
+ ClockMode::Virtual
98
+ },
99
+ )),
100
+ }
101
+ }
102
+
103
+ fn write_file(&self, py: Python<'_>, path: String, data: Vec<u8>, mode: u32) -> PyResult<()> {
104
+ let path = container_vfs_path(&path).map_err(SimulationError::new_err)?;
105
+ if mode > 0o7777 {
106
+ return Err(SimulationError::new_err("invalid file mode"));
107
+ }
108
+ py.detach(|| {
109
+ let mut session = self
110
+ .session
111
+ .lock()
112
+ .map_err(|_| "shellsim container lock is poisoned".to_string())?;
113
+ on_worker(&mut session, move |session| {
114
+ session.environment.sync_vfs_time();
115
+ session
116
+ .environment
117
+ .vfs
118
+ .put_file(&path, data, mode)
119
+ .map_err(|error| error.to_string())
120
+ })
121
+ })
122
+ .map_err(SimulationError::new_err)
123
+ }
124
+
125
+ fn read_file(&self, py: Python<'_>, path: String) -> PyResult<Py<PyBytes>> {
126
+ let path = container_vfs_path(&path).map_err(SimulationError::new_err)?;
127
+ let bytes = py
128
+ .detach(|| {
129
+ let mut session = self
130
+ .session
131
+ .lock()
132
+ .map_err(|_| "shellsim container lock is poisoned".to_string())?;
133
+ on_worker(&mut session, move |session| {
134
+ session
135
+ .environment
136
+ .vfs
137
+ .read("/", &path)
138
+ .map_err(|error| error.to_string())
139
+ })
140
+ })
141
+ .map_err(SimulationError::new_err)?;
142
+ Ok(PyBytes::new(py, &bytes).unbind())
143
+ }
144
+
145
+ fn mkdir(&self, py: Python<'_>, path: String, mode: u32) -> PyResult<()> {
146
+ let path = container_vfs_path(&path).map_err(SimulationError::new_err)?;
147
+ if mode > 0o7777 {
148
+ return Err(SimulationError::new_err("invalid directory mode"));
149
+ }
150
+ py.detach(|| {
151
+ let mut session = self
152
+ .session
153
+ .lock()
154
+ .map_err(|_| "shellsim container lock is poisoned".to_string())?;
155
+ on_worker(&mut session, move |session| {
156
+ session.environment.sync_vfs_time();
157
+ session
158
+ .environment
159
+ .vfs
160
+ .put_dir(&path, mode)
161
+ .map_err(|error| error.to_string())
162
+ })
163
+ })
164
+ .map_err(SimulationError::new_err)
165
+ }
166
+
167
+ fn snapshot_workspace(&self, py: Python<'_>) -> PyResult<String> {
168
+ py.detach(|| {
169
+ let mut session = self
170
+ .session
171
+ .lock()
172
+ .map_err(|_| "shellsim container lock is poisoned".to_string())?;
173
+ on_worker(&mut session, |session| {
174
+ let entries = session.snapshot_workspace()?;
175
+ serde_json::to_string(&entries).map_err(|error| error.to_string())
176
+ })
177
+ })
178
+ .map_err(SimulationError::new_err)
179
+ }
180
+
181
+ fn start_execute(&self, py: Python<'_>, source: String, stdin: Vec<u8>) -> PyResult<String> {
182
+ self.apply(
183
+ py,
184
+ HarnessOperation::StartExecute {
185
+ source,
186
+ stdin_base64: STANDARD.encode(stdin),
187
+ stdin_closed: true,
188
+ },
189
+ )
190
+ .and_then(serialize_result)
191
+ }
192
+
193
+ fn poll_action(&self, py: Python<'_>, action_id: u64) -> PyResult<String> {
194
+ self.apply(
195
+ py,
196
+ HarnessOperation::PollAction {
197
+ action_id,
198
+ work_quanta: 100_000,
199
+ advance_time: true,
200
+ },
201
+ )
202
+ .and_then(serialize_result)
203
+ }
204
+
205
+ fn respond_tool_call(
206
+ &self,
207
+ py: Python<'_>,
208
+ request_id: u64,
209
+ result_json: Option<String>,
210
+ error: Option<String>,
211
+ ) -> PyResult<()> {
212
+ let result = result_json
213
+ .map(|json| serde_json::from_str(&json))
214
+ .transpose()
215
+ .map_err(|error| SimulationError::new_err(error.to_string()))?;
216
+ self.apply(
217
+ py,
218
+ HarnessOperation::RespondToolCall {
219
+ request_id,
220
+ result,
221
+ error,
222
+ },
223
+ )?;
224
+ Ok(())
225
+ }
226
+
227
+ fn read_action_output(&self, py: Python<'_>, action_id: u64) -> PyResult<String> {
228
+ self.apply(py, HarnessOperation::ReadActionOutput { action_id })
229
+ .and_then(serialize_result)
230
+ }
231
+
232
+ fn cancel_action(&self, py: Python<'_>, action_id: u64) -> PyResult<()> {
233
+ self.apply(py, HarnessOperation::CancelAction { action_id })?;
234
+ Ok(())
235
+ }
236
+
237
+ fn drop_action(&self, py: Python<'_>, action_id: u64) -> PyResult<()> {
238
+ self.apply(py, HarnessOperation::DropAction { action_id })?;
239
+ Ok(())
240
+ }
241
+
242
+ fn display_frame(&self, py: Python<'_>, action_id: u64) -> PyResult<Option<PythonFrame>> {
243
+ let frame = py
244
+ .detach(|| {
245
+ let mut session = self
246
+ .session
247
+ .lock()
248
+ .map_err(|_| "shellsim container lock is poisoned".to_string())?;
249
+ on_worker(&mut session, |session| session.display_frame(action_id))
250
+ })
251
+ .map_err(SimulationError::new_err)?;
252
+ Ok(frame.map(|(generation, frame)| {
253
+ (
254
+ generation,
255
+ frame.width,
256
+ frame.height,
257
+ PyBytes::new(py, &frame.pixels).unbind(),
258
+ )
259
+ }))
260
+ }
261
+
262
+ fn inject_key(&self, py: Python<'_>, action_id: u64, code: u32, pressed: bool) -> PyResult<()> {
263
+ py.detach(|| {
264
+ let mut session = self
265
+ .session
266
+ .lock()
267
+ .map_err(|_| "shellsim container lock is poisoned".to_string())?;
268
+ on_worker(&mut session, |session| {
269
+ session.inject_action_key(action_id, KeyEvent { code, pressed })
270
+ })
271
+ })
272
+ .map_err(SimulationError::new_err)
273
+ }
274
+
275
+ fn clock_mode(&self) -> PyResult<&'static str> {
276
+ let session = self
277
+ .session
278
+ .lock()
279
+ .map_err(|_| SimulationError::new_err("shellsim container lock is poisoned"))?;
280
+ Ok(match session.environment.clock_mode() {
281
+ ClockMode::Virtual => "virtual",
282
+ ClockMode::RealTime => "real_time",
283
+ })
284
+ }
285
+ }
286
+
287
+ impl NativeContainer {
288
+ fn apply(&self, py: Python<'_>, operation: HarnessOperation) -> PyResult<serde_json::Value> {
289
+ py.detach(|| {
290
+ let mut session = self
291
+ .session
292
+ .lock()
293
+ .map_err(|_| "shellsim container lock is poisoned".to_string())?;
294
+ on_worker(&mut session, move |session| {
295
+ let response = session.handle(HarnessRequest {
296
+ id: None,
297
+ session_id: None,
298
+ operation,
299
+ });
300
+ if !response.ok {
301
+ return Err(response
302
+ .error
303
+ .unwrap_or_else(|| "container operation failed".into()));
304
+ }
305
+ serde_json::to_value(response.result).map_err(|error| error.to_string())
306
+ })
307
+ })
308
+ .map_err(SimulationError::new_err)
309
+ }
310
+ }
311
+
312
+ fn container_vfs_path(path: &str) -> Result<String, String> {
313
+ if path.contains('\0') {
314
+ return Err("container path contains NUL".to_string());
315
+ }
316
+ Ok(shellsim::vfs::resolve_against("/work", path))
317
+ }
318
+
319
+ fn serialize_result(result: serde_json::Value) -> PyResult<String> {
320
+ serde_json::to_string(&result).map_err(|error| SimulationError::new_err(error.to_string()))
321
+ }
322
+
70
323
  #[pymethods]
71
324
  impl NativeEnvironment {
72
325
  #[new]
@@ -380,11 +633,16 @@ fn invocation_names(events: &[InvocationEvent], trust: CommandTrust) -> Vec<Stri
380
633
  }
381
634
 
382
635
  /// Run an environment operation with a predictable native stack and contain all Rust unwinds.
383
- fn on_worker<T, F>(environment: &mut shellsim::Environment, operation: F) -> Result<T, String>
636
+ fn on_worker<State, T, F>(
637
+ guard: &mut std::sync::MutexGuard<'_, State>,
638
+ operation: F,
639
+ ) -> Result<T, String>
384
640
  where
641
+ State: Send,
385
642
  T: Send,
386
- F: FnOnce(&mut shellsim::Environment) -> Result<T, String> + Send,
643
+ F: FnOnce(&mut State) -> Result<T, String> + Send,
387
644
  {
645
+ let environment = &mut **guard;
388
646
  thread::scope(|scope| {
389
647
  let worker = thread::Builder::new()
390
648
  .name("shellsim-python".to_string())
@@ -416,6 +674,7 @@ fn panic_suffix(payload: &(dyn Any + Send)) -> String {
416
674
  #[pymodule]
417
675
  fn _native(module: &Bound<'_, PyModule>) -> PyResult<()> {
418
676
  module.add_class::<NativeEnvironment>()?;
677
+ module.add_class::<NativeContainer>()?;
419
678
  module.add("SimulationError", module.py().get_type::<SimulationError>())?;
420
679
  Ok(())
421
680
  }
@@ -2,7 +2,11 @@
2
2
 
3
3
  from . import python as python
4
4
  from ._api import (
5
+ Action,
6
+ ActionPoll,
5
7
  CommandUsage,
8
+ Container,
9
+ DisplayFrame,
6
10
  Environment,
7
11
  HttpRequest,
8
12
  HttpResponse,
@@ -12,21 +16,32 @@ from ._api import (
12
16
  RunResult,
13
17
  ShellSession,
14
18
  SimulationError,
19
+ ToolError,
15
20
  Usage,
16
21
  run,
17
22
  )
23
+ from .display_host import DisplayHost
24
+ from .package import Package, PackageSpec
18
25
 
19
26
  __all__ = [
27
+ "Action",
28
+ "ActionPoll",
20
29
  "CommandUsage",
30
+ "Container",
31
+ "DisplayFrame",
32
+ "DisplayHost",
21
33
  "Environment",
22
34
  "HttpRequest",
23
35
  "HttpResponse",
24
36
  "Invocation",
25
37
  "Limits",
26
38
  "MountResult",
39
+ "Package",
40
+ "PackageSpec",
27
41
  "RunResult",
28
42
  "ShellSession",
29
43
  "SimulationError",
44
+ "ToolError",
30
45
  "Usage",
31
46
  "run",
32
47
  "python",