code-sandboxes 1.8.2__tar.gz → 1.9.1__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 (264) hide show
  1. code_sandboxes-1.9.1/.github/workflows/environments-live.yml +181 -0
  2. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/CHANGELOG.md +73 -0
  3. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/Makefile +5 -0
  4. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/PKG-INFO +2 -2
  5. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/README.md +1 -1
  6. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/__version__.py +1 -1
  7. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/daytona_sandbox.py +14 -10
  8. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/e2b_sandbox.py +11 -7
  9. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/adapters/datalayer.py +32 -0
  10. code_sandboxes-1.9.1/code_sandboxes/environments/adapters/daytona.py +532 -0
  11. code_sandboxes-1.9.1/code_sandboxes/environments/adapters/e2b.py +533 -0
  12. code_sandboxes-1.9.1/code_sandboxes/environments/adapters/modal.py +565 -0
  13. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/resolve.py +27 -0
  14. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/modal_sandbox.py +76 -13
  15. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/index.mdx +1 -1
  16. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/daytona.mdx +32 -0
  17. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/e2b.mdx +31 -0
  18. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/modal.mdx +30 -0
  19. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docusaurus.config.js +1 -1
  20. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/README.md +1 -1
  21. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_daytona.py +20 -0
  22. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_e2b.py +20 -0
  23. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_datalayer_builder.py +29 -0
  24. code_sandboxes-1.9.1/tests/test_environment_daytona_builder.py +786 -0
  25. code_sandboxes-1.9.1/tests/test_environment_e2b_builder.py +498 -0
  26. code_sandboxes-1.9.1/tests/test_environment_live_matrix.py +312 -0
  27. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_managed_builders.py +59 -25
  28. code_sandboxes-1.9.1/tests/test_environment_modal_builder.py +746 -0
  29. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_resolve.py +63 -0
  30. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_jupyter_server_streaming.py +14 -1
  31. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_modal_google_colab_sandbox.py +88 -0
  32. code_sandboxes-1.8.2/code_sandboxes/environments/adapters/daytona.py +0 -90
  33. code_sandboxes-1.8.2/code_sandboxes/environments/adapters/e2b.py +0 -74
  34. code_sandboxes-1.8.2/code_sandboxes/environments/adapters/modal.py +0 -75
  35. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/.github/workflows/build.yml +0 -0
  36. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/.github/workflows/py-code-style.yml +0 -0
  37. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/.github/workflows/py-tests.yml +0 -0
  38. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/.github/workflows/py-typing.yml +0 -0
  39. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/.github/workflows/reusable-python.yml +0 -0
  40. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/.gitignore +0 -0
  41. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/.licenserc.yaml +0 -0
  42. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/.pre-commit-config.yaml +0 -0
  43. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/.vscode/settings.json +0 -0
  44. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/Dockerfile +0 -0
  45. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/LICENSE +0 -0
  46. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/__init__.py +0 -0
  47. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/base.py +0 -0
  48. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/bridge_mount.py +0 -0
  49. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/builds.py +0 -0
  50. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/cli.py +0 -0
  51. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/client.py +0 -0
  52. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/cloudflare_sandbox.py +0 -0
  53. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/commands.py +0 -0
  54. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/console.py +0 -0
  55. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/contents.py +0 -0
  56. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/coreweave_sandbox.py +0 -0
  57. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/datalayer_sandbox.py +0 -0
  58. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/docker_sandbox.py +0 -0
  59. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/__init__.py +0 -0
  60. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/__main__.py +0 -0
  61. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/accounts.py +0 -0
  62. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/adapters/__init__.py +0 -0
  63. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/adapters/managed.py +0 -0
  64. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/attest.py +0 -0
  65. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/bases.py +0 -0
  66. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/breaker.py +0 -0
  67. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/build_secrets.py +0 -0
  68. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/builders.py +0 -0
  69. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/canonical.py +0 -0
  70. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/conformance.py +0 -0
  71. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/constraints/sandbox-contract-v1.txt +0 -0
  72. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/constraints/wheelhouse/README.md +0 -0
  73. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/constraints/wheelhouse/jupyter_server-2.21.0+datalayer.1-py3-none-any.whl +0 -0
  74. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/contract.py +0 -0
  75. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/doctor/__init__.py +0 -0
  76. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/doctor/build.py +0 -0
  77. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/doctor/datalayer_sandbox.py +0 -0
  78. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/doctor/report.schema.json +0 -0
  79. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/errors.py +0 -0
  80. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/files.py +0 -0
  81. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/image_import.py +0 -0
  82. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/lifecycle.py +0 -0
  83. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/policy.py +0 -0
  84. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/redact.py +0 -0
  85. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/schema.py +0 -0
  86. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/environments/spec.py +0 -0
  87. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/eval_sandbox.py +0 -0
  88. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/exceptions.py +0 -0
  89. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/filesystem.py +0 -0
  90. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/google_colab.py +0 -0
  91. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/google_colab_sandbox.py +0 -0
  92. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/interfaces.py +0 -0
  93. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/jupyter_ingress.py +0 -0
  94. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/jupyter_server_sandbox.py +0 -0
  95. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/kaggle.py +0 -0
  96. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/kaggle_execute.py +0 -0
  97. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/kaggle_live.py +0 -0
  98. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/kaggle_sandbox.py +0 -0
  99. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/lifecycle.py +0 -0
  100. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/manage.py +0 -0
  101. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/models.py +0 -0
  102. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/monty_sandbox.py +0 -0
  103. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/provider_ingress.py +0 -0
  104. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/code_sandboxes/providers.py +0 -0
  105. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docker/Dockerfile +0 -0
  106. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/.gitignore +0 -0
  107. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/LICENSE +0 -0
  108. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/Makefile +0 -0
  109. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/README.md +0 -0
  110. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/babel.config.js +0 -0
  111. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/api-reference/_category_.yml +0 -0
  112. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/api-reference/index.mdx +0 -0
  113. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/cli/_category_.yml +0 -0
  114. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/cli/index.mdx +0 -0
  115. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/contribute/_category_.yml +0 -0
  116. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/contribute/index.mdx +0 -0
  117. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/environments/_category_.yml +0 -0
  118. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/environments/contract.mdx +0 -0
  119. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/environments/index.mdx +0 -0
  120. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/environments/specification.mdx +0 -0
  121. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/environments/spike-2026-09.mdx +0 -0
  122. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/examples/_category_.yml +0 -0
  123. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/examples/index.mdx +0 -0
  124. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/guide/_category_.yml +0 -0
  125. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/guide/index.mdx +0 -0
  126. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/install/_category_.yml +0 -0
  127. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/install/index.mdx +0 -0
  128. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/provider-ingress.mdx +0 -0
  129. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/_category_.yml +0 -0
  130. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/cloudflare.mdx +0 -0
  131. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/coreweave.mdx +0 -0
  132. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/datalayer.mdx +0 -0
  133. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/docker.mdx +0 -0
  134. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/eval.mdx +0 -0
  135. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/google-colab.mdx +0 -0
  136. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/index.mdx +0 -0
  137. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/jupyter-server.mdx +0 -0
  138. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/kaggle.mdx +0 -0
  139. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/docs/providers/monty.mdx +0 -0
  140. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/package.json +0 -0
  141. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/sidebars.js +0 -0
  142. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/components/HomepageRow1.js +0 -0
  143. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/components/HomepageRow1.module.css +0 -0
  144. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/components/HomepageRow2.js +0 -0
  145. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/components/HomepageRow2.module.css +0 -0
  146. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/components/HomepageRow3.js +0 -0
  147. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/components/HomepageRow3.module.css +0 -0
  148. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/css/custom.css +0 -0
  149. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/pages/index.module.css +0 -0
  150. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/pages/markdown-page.md +0 -0
  151. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/theme/CustomDocItem.tsx +0 -0
  152. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/src/theme/IconsTitle.tsx +0 -0
  153. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/bricks.svg +0 -0
  154. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/building-construction.svg +0 -0
  155. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/cloud-sun.svg +0 -0
  156. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/cloud.svg +0 -0
  157. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/collaboration.svg +0 -0
  158. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/copy-left.svg +0 -0
  159. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/datalayer/logo.png +0 -0
  160. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/datalayer/logo.svg +0 -0
  161. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/datalayer/personas/business.svg +0 -0
  162. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/datalayer/personas/data-scientist.svg +0 -0
  163. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/datalayer/personas/devops.svg +0 -0
  164. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/datalayer/project/project-line.svg +0 -0
  165. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/favicon.ico +0 -0
  166. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/jupyter.svg +0 -0
  167. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/open-source.svg +0 -0
  168. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/react-js.svg +0 -0
  169. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/react-js2.svg +0 -0
  170. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/ringed-planet.svg +0 -0
  171. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/robot.svg +0 -0
  172. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/rocket.svg +0 -0
  173. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/shield.svg +0 -0
  174. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/sparkles.svg +0 -0
  175. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/streamlit.gif +0 -0
  176. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/super-hero.svg +0 -0
  177. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/target.svg +0 -0
  178. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/docs/static/img/thumbs-up.svg +0 -0
  179. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/Makefile +0 -0
  180. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/cloudflare_sandbox_example.py +0 -0
  181. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/coreweave_sandbox_example.py +0 -0
  182. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/datalayer_sandbox_example.py +0 -0
  183. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/daytona_sandbox_example.py +0 -0
  184. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/docker_sandbox_example.py +0 -0
  185. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/e2b_sandbox_example.py +0 -0
  186. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/eval_sandbox_example.py +0 -0
  187. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/google_colab_sandbox_example.py +0 -0
  188. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/jupyter_server_sandbox_example.py +0 -0
  189. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/kaggle_sandbox_example.py +0 -0
  190. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/modal_sandbox_example.py +0 -0
  191. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/exec/monty_sandbox_example.py +0 -0
  192. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/Makefile +0 -0
  193. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/cloudflare_sandbox_example.py +0 -0
  194. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/coreweave_sandbox_example.py +0 -0
  195. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/datalayer_sandbox_example.py +0 -0
  196. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/daytona_sandbox_example.py +0 -0
  197. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/docker_sandbox_example.py +0 -0
  198. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/e2b_sandbox_example.py +0 -0
  199. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/eval_sandbox_example.py +0 -0
  200. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/google_colab_sandbox_example.py +0 -0
  201. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/jupyter_server_sandbox_example.py +0 -0
  202. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/kaggle_sandbox_example.py +0 -0
  203. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/modal_sandbox_example.py +0 -0
  204. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/examples/repl/monty_sandbox_example.py +0 -0
  205. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/pyproject.toml +0 -0
  206. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/schemas/environment-v1alpha1.json +0 -0
  207. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/__init__.py +0 -0
  208. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/conftest.py +0 -0
  209. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_a_datalayer_sandbox_can_be_interrupted.py +0 -0
  210. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_bridge_mount.py +0 -0
  211. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_builds.py +0 -0
  212. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_cli_exec.py +0 -0
  213. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_cli_repl.py +0 -0
  214. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_client.py +0 -0
  215. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_client_contents.py +0 -0
  216. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_client_files.py +0 -0
  217. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_client_outcome_error_output.py +0 -0
  218. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_cloudflare.py +0 -0
  219. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_commands_envelope.py +0 -0
  220. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_console.py +0 -0
  221. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_contents_contract.py +0 -0
  222. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_contents_live_matrix.py +0 -0
  223. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_coreweave.py +0 -0
  224. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_datalayer_sandbox.py +0 -0
  225. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_accounts.py +0 -0
  226. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_attest.py +0 -0
  227. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_bases.py +0 -0
  228. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_breaker.py +0 -0
  229. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_build_secrets.py +0 -0
  230. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_builders.py +0 -0
  231. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_canonical.py +0 -0
  232. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_conformance.py +0 -0
  233. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_constraints.py +0 -0
  234. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_contract.py +0 -0
  235. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_cross_variant_report.py +0 -0
  236. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_doctor.py +0 -0
  237. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_errors.py +0 -0
  238. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_image_import.py +0 -0
  239. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_lifecycle.py +0 -0
  240. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_redact.py +0 -0
  241. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_environment_spec.py +0 -0
  242. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_eval.py +0 -0
  243. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_every_variant_has_a_real_interrupt.py +0 -0
  244. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_factory.py +0 -0
  245. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_google_colab.py +0 -0
  246. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_integration.py +0 -0
  247. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_jupyter_ingress.py +0 -0
  248. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_jupyter_server.py +0 -0
  249. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_kaggle.py +0 -0
  250. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_kaggle_execute.py +0 -0
  251. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_kaggle_live.py +0 -0
  252. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_kaggle_session.py +0 -0
  253. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_kernel_client_compatibility.py +0 -0
  254. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_lifecycle_vocabulary.py +0 -0
  255. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_manage.py +0 -0
  256. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_manage_datalayer.py +0 -0
  257. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_modal_session.py +0 -0
  258. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_models.py +0 -0
  259. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_output_line_fidelity.py +0 -0
  260. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_provider_catalog_names.py +0 -0
  261. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_provider_ingress.py +0 -0
  262. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_providers.py +0 -0
  263. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_sandboxes.py +0 -0
  264. {code_sandboxes-1.8.2 → code_sandboxes-1.9.1}/tests/test_the_kill_relaunch_drill.py +0 -0
@@ -0,0 +1,181 @@
1
+ name: Environments Live Matrix
2
+
3
+ # The managed Environment builders (E2-03, E2-04, E2-05) against the real
4
+ # providers, nightly (PLAN_ENV.md E2-11): a real, hash-verified lock, a real
5
+ # build, a real launch, and the formal core tier — tests/test_environment_live_matrix.py.
6
+ #
7
+ # E2B and Modal carry their own already-documented, unticked gaps there
8
+ # (E2-03's uid/gid mismatch; modal_sandbox.py's missing setpriv) and are
9
+ # `xfail(strict=False)` in that file, so an ordinary failure there is
10
+ # expected and does not fail this workflow. Daytona carries no such marker:
11
+ # a failure there is a real regression, and this workflow opens (or
12
+ # comments on) an issue naming it.
13
+ #
14
+ # Needs, as repository secrets: E2B_API_KEY, DAYTONA_API_KEY, MODAL_TOKEN_ID,
15
+ # MODAL_TOKEN_SECRET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY — the last two
16
+ # scoped to read the approved base from ECR only (D-18). A secret that is
17
+ # not set is not a workflow failure: the test each secret belongs to skips,
18
+ # by name, and says so in the job summary.
19
+
20
+ on:
21
+ schedule:
22
+ # 04:17 UTC: off-peak for every provider this reaches, and away from the
23
+ # hour boundary every other scheduled workflow in this org tends to pick.
24
+ - cron: '17 4 * * *'
25
+ workflow_dispatch: {}
26
+
27
+ concurrency:
28
+ group: ${{ github.workflow }}
29
+ cancel-in-progress: false
30
+
31
+ # The default token on this repository is read-only: without this, a genuine
32
+ # matrix failure would still 403 trying to label and open the issue that is
33
+ # meant to report it (found in review).
34
+ permissions:
35
+ contents: read
36
+ issues: write
37
+
38
+ jobs:
39
+ live-matrix:
40
+ runs-on: ubuntu-latest
41
+ timeout-minutes: 45
42
+ steps:
43
+ - name: Checkout
44
+ uses: actions/checkout@v6
45
+
46
+ - name: Setup UV
47
+ uses: astral-sh/setup-uv@v7
48
+ with:
49
+ version: latest
50
+ python-version: '3.13'
51
+ activate-environment: true
52
+
53
+ - name: Install dependencies
54
+ run: |
55
+ uv pip install ".[e2b,daytona,modal,test]"
56
+ uv pip install boto3
57
+
58
+ - name: Configure AWS credentials
59
+ run: |
60
+ mkdir -p ~/.aws
61
+ {
62
+ echo "[default]"
63
+ echo "aws_access_key_id=${AWS_ACCESS_KEY_ID}"
64
+ echo "aws_secret_access_key=${AWS_SECRET_ACCESS_KEY}"
65
+ } > ~/.aws/credentials
66
+ env:
67
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
68
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
69
+
70
+ - name: Run the live matrix
71
+ id: live
72
+ continue-on-error: true
73
+ run: |
74
+ CODE_SANDBOXES_LIVE=1 pytest -q -m live \
75
+ tests/test_environment_live_matrix.py \
76
+ --junitxml=live-matrix-results.xml
77
+ env:
78
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
79
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
80
+ AWS_REGION: us-east-1
81
+ E2B_API_KEY: ${{ secrets.E2B_API_KEY }}
82
+ DAYTONA_API_KEY: ${{ secrets.DAYTONA_API_KEY }}
83
+ MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
84
+ MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
85
+
86
+ - name: Upload the JUnit report
87
+ if: always()
88
+ uses: actions/upload-artifact@v4
89
+ with:
90
+ name: environments-live-results
91
+ path: live-matrix-results.xml
92
+ retention-days: 14
93
+
94
+ - name: Summarize genuine failures (not xfail) naming provider and check
95
+ if: steps.live.outcome == 'failure'
96
+ run: |
97
+ python - <<'PY'
98
+ import xml.etree.ElementTree as ET
99
+
100
+ tree = ET.parse("live-matrix-results.xml")
101
+ lines = []
102
+ for testcase in tree.getroot().iter("testcase"):
103
+ # An expected-fail (E2B/Modal's own documented gaps) is reported
104
+ # by pytest's junit writer as its own case with neither
105
+ # <failure> nor <error> — only a genuine, un-marked failure
106
+ # (Daytona, or a real regression anywhere else) has one.
107
+ node = testcase.find("failure")
108
+ if node is None:
109
+ node = testcase.find("error")
110
+ if node is None:
111
+ continue
112
+ name = f"{testcase.get('classname')}::{testcase.get('name')}"
113
+ detail = (node.get("message") or node.text or "").strip()
114
+ short = "\n".join(detail.splitlines()[:20])
115
+ lines.append(f"### `{name}`\n\n```\n{short}\n```")
116
+
117
+ with open("failures.md", "w") as handle:
118
+ handle.write("\n\n".join(lines))
119
+ print(f"{len(lines)} genuine failure(s) found")
120
+ PY
121
+
122
+ - name: Ensure the environments-live label exists
123
+ if: steps.live.outcome == 'failure'
124
+ continue-on-error: true # already existing is not an error
125
+ uses: actions/github-script@v7
126
+ with:
127
+ script: |
128
+ await github.rest.issues.createLabel({
129
+ owner: context.repo.owner,
130
+ repo: context.repo.repo,
131
+ name: 'environments-live',
132
+ color: 'b60205',
133
+ description: 'The nightly Environments live matrix (E2-11)',
134
+ });
135
+
136
+ - name: Open or update an issue naming the provider and the check
137
+ if: steps.live.outcome == 'failure'
138
+ uses: actions/github-script@v7
139
+ with:
140
+ script: |
141
+ const fs = require('fs');
142
+ const detail = fs.readFileSync('failures.md', 'utf8').trim();
143
+ if (!detail) {
144
+ // Every failure was inside an `xfail` — nothing genuinely
145
+ // broke (see the module docstring's own reasoning).
146
+ return;
147
+ }
148
+ const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
149
+ const body = [
150
+ `The nightly [environments live matrix](${runUrl}) failed for real — not the two already-documented \`xfail\` gaps (E2-03's uid/gid, Modal's missing \`setpriv\`).`,
151
+ '',
152
+ detail,
153
+ ].join('\n');
154
+ const title = 'environments-live: nightly failure';
155
+ const existing = await github.rest.issues.listForRepo({
156
+ owner: context.repo.owner,
157
+ repo: context.repo.repo,
158
+ state: 'open',
159
+ labels: 'environments-live',
160
+ });
161
+ const found = existing.data.find((issue) => issue.title === title);
162
+ if (found) {
163
+ await github.rest.issues.createComment({
164
+ owner: context.repo.owner,
165
+ repo: context.repo.repo,
166
+ issue_number: found.number,
167
+ body,
168
+ });
169
+ } else {
170
+ await github.rest.issues.create({
171
+ owner: context.repo.owner,
172
+ repo: context.repo.repo,
173
+ title,
174
+ body,
175
+ labels: ['environments-live'],
176
+ });
177
+ }
178
+
179
+ - name: Fail the job if something genuinely broke
180
+ if: steps.live.outcome == 'failure'
181
+ run: exit 1
@@ -8,6 +8,79 @@
8
8
 
9
9
  ## Unreleased
10
10
 
11
+ ## 1.9.1
12
+
13
+ - **mTLS to the build pool's `buildkitd`** (`environments.adapters.datalayer.Builder`,
14
+ `environments.resolve.BuildkitResolveRunner`; PLAN_ENV.md E1-06/E1-07). Both
15
+ drivers of `buildctl` took only `--addr`, which is what every prior live
16
+ drill needed against a plain-socket, ephemeral `buildkitd` — never a real
17
+ one. The build pool's real daemon on r1 takes mTLS connections only, so
18
+ reaching it needed three more flags neither driver had: `--tlscert`,
19
+ `--tlskey` and `--tlscacert`, each read from `DATALAYER_BUILDKIT_TLSCERT`,
20
+ `DATALAYER_BUILDKIT_TLSKEY` and `DATALAYER_BUILDKIT_TLSCACERT` when not
21
+ passed explicitly (`Builder`, matching how it already reads
22
+ `DATALAYER_BUILDKIT_ADDR`) or passed in by the caller (`BuildkitResolveRunner`,
23
+ constructed by durable's own `activities_environments.py`, which now reads
24
+ and forwards the same three). All three or none — a partial set is treated
25
+ as none, since a `buildkitd` requiring mTLS refuses a client carrying only
26
+ some of them at the daemon, with a less useful error than refusing here.
27
+
28
+ ## 1.9.0
29
+
30
+ - **The Daytona builder** (`environments.adapters.daytona`, PLAN_ENV.md
31
+ E2-04). A snapshot built from the approved Datalayer base — `uv pip sync --require-hashes` against the resolved lock, your files, `postInstall`
32
+ commands, and an explicit `tini -- sleep infinity` entrypoint — with the
33
+ owner's own Daytona organization, never this package's ambient
34
+ credentials. Confirmed live against a real, hash-verified lock: a real
35
+ snapshot reaches `Active` and a sandbox launched from it passes the full
36
+ sandbox contract, doctor included — the one managed variant, of the
37
+ three landing here, whose build-time state and launched state fully
38
+ agree.
39
+ - **The E2B builder** (`environments.adapters.e2b`, PLAN_ENV.md E2-03). A
40
+ template built from `code-interpreter-v1` — E2B's own proprietary
41
+ code-interpreter server ships baked into it only, so the build starts
42
+ there rather than from the Datalayer base and reconciles whatever it
43
+ shipped to exactly what the lock pins. Two live-found identity bugs are
44
+ fixed: `code-interpreter-v1` already holds an account at uid 1000 and a
45
+ group at gid 100 of its own, so the old `useradd` was silently landing
46
+ elsewhere — the build now renames the existing account instead — and the
47
+ base sets no locale at all, now set explicitly. A real build passes the
48
+ contract's doctor check in full. **Still open:** a launched sandbox
49
+ currently runs as root, not the contract's `1000:100` — the two
50
+ `systemd` services that actually execute a launched sandbox's code have
51
+ no `User=` of their own, and E2B's private server hardcodes `/home/user`
52
+ as the working directory, independent of anything the build sets.
53
+ - **The Modal builder** (`environments.adapters.modal`, PLAN_ENV.md E2-05).
54
+ An image built `from_aws_ecr` off the approved base, with a per-build
55
+ Secret made and torn down around it. The contract's own identity is put
56
+ back at launch: `modal_sandbox.py`'s session driver now drops itself to
57
+ `1000:100` for a sandbox launched from a built Environments artifact
58
+ specifically (gated on `image_id`, so general Modal sandbox usage
59
+ elsewhere is unaffected) — Modal ignores the image's own `USER`, so this
60
+ is the only place it can be restored. A separate, previously
61
+ silently-fatal bug is also fixed: the real launcher creates the sandbox
62
+ with no command arguments at all, so an entrypoint relying on `exec "$@"` alone was a no-op and every launched environment exited within
63
+ seconds; the entrypoint now falls back to `sleep infinity` when given
64
+ none. **Still open:** two of the core tier's checks (imports, filesystem)
65
+ still fail on a launched sandbox, for a reason not yet root-caused —
66
+ confirmed specific to a contract-built image under repeated `exec`, not
67
+ the identity fix.
68
+ - **A real, shared bug in all three managed launchers, found in review**:
69
+ `start()` creates the remote sandbox well before it marks itself
70
+ started, while `stop()` was guarded on that flag rather than on the
71
+ resource itself — a failure in between left a real, running sandbox
72
+ that `stop()` skipped entirely, orphaned for good. Fixed identically in
73
+ `daytona_sandbox.py`, `modal_sandbox.py` and `e2b_sandbox.py`.
74
+ - **A nightly live matrix for the three managed builders**
75
+ (`.github/workflows/environments-live.yml`, PLAN_ENV.md E2-11). Builds a
76
+ real artifact on each real provider from a real, hash-verified lock,
77
+ launches a sandbox from it, and runs the formal core tier — opening (or
78
+ commenting on) an issue naming the provider and the check on a genuine
79
+ failure. E2B and Modal run `xfail(strict=False)` for their own
80
+ already-documented gaps above; Daytona carries no such marker. Needs
81
+ provider secrets added to the repository before it does anything for
82
+ real.
83
+
11
84
  ## 1.8.2
12
85
 
13
86
  - **A sandbox can be asked whether it is still alive, and the answer no longer
@@ -54,6 +54,11 @@ live-matrix:
54
54
  kill-relaunch:
55
55
  CODE_SANDBOXES_LIVE=1 pytest -q -m live tests/test_the_kill_relaunch_drill.py
56
56
 
57
+ ## The managed Environment builders against the real providers: a real
58
+ ## resolved lock, build, launch, and the formal core tier (E2-11).
59
+ environments-live:
60
+ CODE_SANDBOXES_LIVE=1 pytest -q -m live tests/test_environment_live_matrix.py
61
+
57
62
  clean: ## clean
58
63
  git clean -fdx
59
64
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: code_sandboxes
3
- Version: 1.8.2
3
+ Version: 1.9.1
4
4
  Project-URL: Home, https://github.com/datalayer/code-sandboxes
5
5
  Author-email: Datalayer <info@datalayer.io>
6
6
  License: BSD 3-Clause License
@@ -109,7 +109,7 @@ Description-Content-Type: text/markdown
109
109
  [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer)
110
110
  [![PyPI - Version](https://img.shields.io/pypi/v/code-sandboxes)](https://pypi.org/project/code-sandboxes)
111
111
 
112
- # { } 📦 Code Sandboxes
112
+ # 📦 Code Sandboxes
113
113
 
114
114
  Code Sandboxes (`code_sandboxes`) is a Python package for running code in isolated sandbox variants through a unified API.
115
115
 
@@ -9,7 +9,7 @@
9
9
  [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer)
10
10
  [![PyPI - Version](https://img.shields.io/pypi/v/code-sandboxes)](https://pypi.org/project/code-sandboxes)
11
11
 
12
- # { } 📦 Code Sandboxes
12
+ # 📦 Code Sandboxes
13
13
 
14
14
  Code Sandboxes (`code_sandboxes`) is a Python package for running code in isolated sandbox variants through a unified API.
15
15
 
@@ -3,4 +3,4 @@
3
3
 
4
4
  """Code Sandboxes."""
5
5
 
6
- __version__ = "1.8.2"
6
+ __version__ = "1.9.1"
@@ -655,17 +655,21 @@ class DaytonaSandbox(Sandbox):
655
655
  )
656
656
 
657
657
  def stop(self) -> None:
658
- if not self._started:
658
+ # Guarded on the resource itself, not `_started` (found in review):
659
+ # `start()` creates the remote sandbox well before it marks itself
660
+ # started, so a failure in between — `create_context`, building
661
+ # `SandboxInfo` — would otherwise leave a real, running sandbox that
662
+ # `stop()` skips entirely and this object can never clean up again.
663
+ if self._sandbox is None:
659
664
  return
660
- if self._sandbox is not None:
661
- try:
662
- if self._delete_on_stop:
663
- self._sandbox.delete()
664
- else:
665
- self._sandbox.stop()
666
- except Exception:
667
- logger.debug("Ignoring error while stopping the Daytona sandbox", exc_info=True)
668
- self._sandbox = None
665
+ try:
666
+ if self._delete_on_stop:
667
+ self._sandbox.delete()
668
+ else:
669
+ self._sandbox.stop()
670
+ except Exception:
671
+ logger.debug("Ignoring error while stopping the Daytona sandbox", exc_info=True)
672
+ self._sandbox = None
669
673
  self._daytona = None
670
674
  self._jupyter_endpoint = None
671
675
  self._contexts.clear()
@@ -419,14 +419,18 @@ class E2BSandbox(Sandbox):
419
419
  return self._allow_internet_access
420
420
 
421
421
  def stop(self) -> None:
422
- if not self._started:
422
+ # Guarded on the resource itself, not `_started` (found in review):
423
+ # `start()` creates the remote sandbox well before it marks itself
424
+ # started, so a failure in between — `create_context`, building
425
+ # `SandboxInfo` — would otherwise leave a real, running sandbox that
426
+ # `stop()` skips entirely and this object can never clean up again.
427
+ if self._sandbox is None:
423
428
  return
424
- if self._sandbox is not None:
425
- try:
426
- self._sandbox.kill()
427
- except Exception:
428
- logger.debug("Ignoring error while killing the E2B sandbox", exc_info=True)
429
- self._sandbox = None
429
+ try:
430
+ self._sandbox.kill()
431
+ except Exception:
432
+ logger.debug("Ignoring error while killing the E2B sandbox", exc_info=True)
433
+ self._sandbox = None
430
434
  self._contexts.clear()
431
435
  self._jupyter_endpoint = None
432
436
  self._volume_mounts.stopped()
@@ -127,6 +127,15 @@ class Builder:
127
127
  looks like (E1-06).
128
128
  address
129
129
  ``buildkitd``'s address, when it is not the default socket.
130
+ tlscert, tlskey, tlscacert
131
+ Paths to the client certificate, its key, and the CA that signed
132
+ ``buildkitd``'s own server certificate — the build pool's daemon
133
+ takes mTLS connections only (PLAN_ENV.md E1-06), so a ``tcp://``
134
+ ``address`` with none of these set fails the handshake, not the
135
+ build. Unset (the default) is right for a plain-socket
136
+ ``buildkitd``, such as ``plane local``'s own ephemeral one, which
137
+ was every ``buildkitd`` this adapter had ever actually built
138
+ against before the pool on r1 existed.
130
139
  region
131
140
  The registry's region, for the ECR API calls.
132
141
  ecr
@@ -145,6 +154,9 @@ class Builder:
145
154
  credential: Any = None,
146
155
  buildctl: str | None = None,
147
156
  address: str | None = None,
157
+ tlscert: str | None = None,
158
+ tlskey: str | None = None,
159
+ tlscacert: str | None = None,
148
160
  region: str | None = None,
149
161
  ecr: Any = None,
150
162
  run: Callable[..., subprocess.CompletedProcess[str]] | None = None,
@@ -155,6 +167,9 @@ class Builder:
155
167
  self._credential = credential
156
168
  self._buildctl = (shutil.which("buildctl") or "") if buildctl is None else buildctl
157
169
  self._address = address or os.environ.get("DATALAYER_BUILDKIT_ADDR", "").strip()
170
+ self._tlscert = tlscert or os.environ.get("DATALAYER_BUILDKIT_TLSCERT", "").strip()
171
+ self._tlskey = tlskey or os.environ.get("DATALAYER_BUILDKIT_TLSKEY", "").strip()
172
+ self._tlscacert = tlscacert or os.environ.get("DATALAYER_BUILDKIT_TLSCACERT", "").strip()
158
173
  self._region = region or os.environ.get("AWS_REGION", "us-east-1")
159
174
  self._ecr = ecr
160
175
  self._run = run or subprocess.run
@@ -384,6 +399,7 @@ class Builder:
384
399
  command = [
385
400
  self._buildctl,
386
401
  *(["--addr", self._address] if self._address else []),
402
+ *self._tls_options(),
387
403
  "build",
388
404
  "--frontend",
389
405
  "dockerfile.v0",
@@ -534,6 +550,22 @@ class Builder:
534
550
  self._ecr = boto3.client("ecr", region_name=self._region)
535
551
  return self._ecr
536
552
 
553
+ def _tls_options(self) -> list[str]:
554
+ """The ``buildctl`` global flags for the build pool's mTLS (E1-06).
555
+
556
+ All three or none: a `buildkitd` that takes mTLS refuses a client
557
+ with only some of them, so a partial set here would fail at the
558
+ daemon with a confusing error rather than a clear one about the
559
+ deployment's own configuration.
560
+ """
561
+ if not (self._tlscert and self._tlskey and self._tlscacert):
562
+ return []
563
+ return [
564
+ f"--tlscert={self._tlscert}",
565
+ f"--tlskey={self._tlskey}",
566
+ f"--tlscacert={self._tlscacert}",
567
+ ]
568
+
537
569
  def _registry_host(self) -> str:
538
570
  registry = str(getattr(self._credential, "registry", "") or "")
539
571
  if not registry: