code-sandboxes 1.8.2__tar.gz → 1.9.2__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.2/.github/workflows/environments-live.yml +181 -0
  2. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/CHANGELOG.md +87 -0
  3. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/Makefile +5 -0
  4. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/PKG-INFO +2 -2
  5. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/README.md +1 -1
  6. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/__version__.py +1 -1
  7. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/daytona_sandbox.py +14 -10
  8. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/e2b_sandbox.py +11 -7
  9. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/adapters/datalayer.py +64 -5
  10. code_sandboxes-1.9.2/code_sandboxes/environments/adapters/daytona.py +532 -0
  11. code_sandboxes-1.9.2/code_sandboxes/environments/adapters/e2b.py +533 -0
  12. code_sandboxes-1.9.2/code_sandboxes/environments/adapters/modal.py +565 -0
  13. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/resolve.py +27 -0
  14. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/modal_sandbox.py +76 -13
  15. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/index.mdx +1 -1
  16. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/daytona.mdx +32 -0
  17. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/e2b.mdx +31 -0
  18. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/modal.mdx +30 -0
  19. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docusaurus.config.js +1 -1
  20. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/README.md +1 -1
  21. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_daytona.py +20 -0
  22. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_e2b.py +20 -0
  23. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_datalayer_builder.py +52 -1
  24. code_sandboxes-1.9.2/tests/test_environment_daytona_builder.py +786 -0
  25. code_sandboxes-1.9.2/tests/test_environment_e2b_builder.py +498 -0
  26. code_sandboxes-1.9.2/tests/test_environment_live_matrix.py +312 -0
  27. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_managed_builders.py +59 -25
  28. code_sandboxes-1.9.2/tests/test_environment_modal_builder.py +746 -0
  29. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_resolve.py +63 -0
  30. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_jupyter_server_streaming.py +14 -1
  31. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/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.2}/.github/workflows/build.yml +0 -0
  36. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.github/workflows/py-code-style.yml +0 -0
  37. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.github/workflows/py-tests.yml +0 -0
  38. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.github/workflows/py-typing.yml +0 -0
  39. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.github/workflows/reusable-python.yml +0 -0
  40. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.gitignore +0 -0
  41. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.licenserc.yaml +0 -0
  42. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.pre-commit-config.yaml +0 -0
  43. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/.vscode/settings.json +0 -0
  44. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/Dockerfile +0 -0
  45. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/LICENSE +0 -0
  46. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/__init__.py +0 -0
  47. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/base.py +0 -0
  48. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/bridge_mount.py +0 -0
  49. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/builds.py +0 -0
  50. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/cli.py +0 -0
  51. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/client.py +0 -0
  52. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/cloudflare_sandbox.py +0 -0
  53. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/commands.py +0 -0
  54. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/console.py +0 -0
  55. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/contents.py +0 -0
  56. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/coreweave_sandbox.py +0 -0
  57. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/datalayer_sandbox.py +0 -0
  58. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/docker_sandbox.py +0 -0
  59. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/__init__.py +0 -0
  60. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/__main__.py +0 -0
  61. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/accounts.py +0 -0
  62. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/adapters/__init__.py +0 -0
  63. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/adapters/managed.py +0 -0
  64. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/attest.py +0 -0
  65. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/bases.py +0 -0
  66. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/breaker.py +0 -0
  67. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/build_secrets.py +0 -0
  68. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/builders.py +0 -0
  69. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/canonical.py +0 -0
  70. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/conformance.py +0 -0
  71. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/constraints/sandbox-contract-v1.txt +0 -0
  72. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/constraints/wheelhouse/README.md +0 -0
  73. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/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.2}/code_sandboxes/environments/contract.py +0 -0
  75. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/doctor/__init__.py +0 -0
  76. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/doctor/build.py +0 -0
  77. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/doctor/datalayer_sandbox.py +0 -0
  78. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/doctor/report.schema.json +0 -0
  79. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/errors.py +0 -0
  80. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/files.py +0 -0
  81. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/image_import.py +0 -0
  82. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/lifecycle.py +0 -0
  83. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/policy.py +0 -0
  84. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/redact.py +0 -0
  85. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/schema.py +0 -0
  86. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/environments/spec.py +0 -0
  87. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/eval_sandbox.py +0 -0
  88. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/exceptions.py +0 -0
  89. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/filesystem.py +0 -0
  90. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/google_colab.py +0 -0
  91. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/google_colab_sandbox.py +0 -0
  92. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/interfaces.py +0 -0
  93. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/jupyter_ingress.py +0 -0
  94. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/jupyter_server_sandbox.py +0 -0
  95. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/kaggle.py +0 -0
  96. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/kaggle_execute.py +0 -0
  97. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/kaggle_live.py +0 -0
  98. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/kaggle_sandbox.py +0 -0
  99. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/lifecycle.py +0 -0
  100. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/manage.py +0 -0
  101. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/models.py +0 -0
  102. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/monty_sandbox.py +0 -0
  103. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/provider_ingress.py +0 -0
  104. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/code_sandboxes/providers.py +0 -0
  105. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docker/Dockerfile +0 -0
  106. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/.gitignore +0 -0
  107. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/LICENSE +0 -0
  108. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/Makefile +0 -0
  109. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/README.md +0 -0
  110. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/babel.config.js +0 -0
  111. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/api-reference/_category_.yml +0 -0
  112. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/api-reference/index.mdx +0 -0
  113. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/cli/_category_.yml +0 -0
  114. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/cli/index.mdx +0 -0
  115. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/contribute/_category_.yml +0 -0
  116. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/contribute/index.mdx +0 -0
  117. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/_category_.yml +0 -0
  118. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/contract.mdx +0 -0
  119. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/index.mdx +0 -0
  120. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/specification.mdx +0 -0
  121. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/environments/spike-2026-09.mdx +0 -0
  122. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/examples/_category_.yml +0 -0
  123. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/examples/index.mdx +0 -0
  124. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/guide/_category_.yml +0 -0
  125. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/guide/index.mdx +0 -0
  126. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/install/_category_.yml +0 -0
  127. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/install/index.mdx +0 -0
  128. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/provider-ingress.mdx +0 -0
  129. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/_category_.yml +0 -0
  130. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/cloudflare.mdx +0 -0
  131. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/coreweave.mdx +0 -0
  132. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/datalayer.mdx +0 -0
  133. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/docker.mdx +0 -0
  134. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/eval.mdx +0 -0
  135. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/google-colab.mdx +0 -0
  136. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/index.mdx +0 -0
  137. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/jupyter-server.mdx +0 -0
  138. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/kaggle.mdx +0 -0
  139. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/docs/providers/monty.mdx +0 -0
  140. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/package.json +0 -0
  141. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/sidebars.js +0 -0
  142. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow1.js +0 -0
  143. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow1.module.css +0 -0
  144. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow2.js +0 -0
  145. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow2.module.css +0 -0
  146. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow3.js +0 -0
  147. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/components/HomepageRow3.module.css +0 -0
  148. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/css/custom.css +0 -0
  149. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/pages/index.module.css +0 -0
  150. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/pages/markdown-page.md +0 -0
  151. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/theme/CustomDocItem.tsx +0 -0
  152. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/src/theme/IconsTitle.tsx +0 -0
  153. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/bricks.svg +0 -0
  154. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/building-construction.svg +0 -0
  155. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/cloud-sun.svg +0 -0
  156. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/cloud.svg +0 -0
  157. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/collaboration.svg +0 -0
  158. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/copy-left.svg +0 -0
  159. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/logo.png +0 -0
  160. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/logo.svg +0 -0
  161. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/personas/business.svg +0 -0
  162. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/personas/data-scientist.svg +0 -0
  163. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/personas/devops.svg +0 -0
  164. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/datalayer/project/project-line.svg +0 -0
  165. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/favicon.ico +0 -0
  166. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/jupyter.svg +0 -0
  167. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/open-source.svg +0 -0
  168. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/react-js.svg +0 -0
  169. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/react-js2.svg +0 -0
  170. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/ringed-planet.svg +0 -0
  171. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/robot.svg +0 -0
  172. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/rocket.svg +0 -0
  173. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/shield.svg +0 -0
  174. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/sparkles.svg +0 -0
  175. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/streamlit.gif +0 -0
  176. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/super-hero.svg +0 -0
  177. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/target.svg +0 -0
  178. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/docs/static/img/thumbs-up.svg +0 -0
  179. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/Makefile +0 -0
  180. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/cloudflare_sandbox_example.py +0 -0
  181. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/coreweave_sandbox_example.py +0 -0
  182. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/datalayer_sandbox_example.py +0 -0
  183. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/daytona_sandbox_example.py +0 -0
  184. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/docker_sandbox_example.py +0 -0
  185. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/e2b_sandbox_example.py +0 -0
  186. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/eval_sandbox_example.py +0 -0
  187. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/google_colab_sandbox_example.py +0 -0
  188. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/jupyter_server_sandbox_example.py +0 -0
  189. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/kaggle_sandbox_example.py +0 -0
  190. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/modal_sandbox_example.py +0 -0
  191. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/exec/monty_sandbox_example.py +0 -0
  192. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/Makefile +0 -0
  193. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/cloudflare_sandbox_example.py +0 -0
  194. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/coreweave_sandbox_example.py +0 -0
  195. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/datalayer_sandbox_example.py +0 -0
  196. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/daytona_sandbox_example.py +0 -0
  197. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/docker_sandbox_example.py +0 -0
  198. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/e2b_sandbox_example.py +0 -0
  199. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/eval_sandbox_example.py +0 -0
  200. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/google_colab_sandbox_example.py +0 -0
  201. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/jupyter_server_sandbox_example.py +0 -0
  202. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/kaggle_sandbox_example.py +0 -0
  203. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/modal_sandbox_example.py +0 -0
  204. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/examples/repl/monty_sandbox_example.py +0 -0
  205. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/pyproject.toml +0 -0
  206. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/schemas/environment-v1alpha1.json +0 -0
  207. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/__init__.py +0 -0
  208. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/conftest.py +0 -0
  209. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_a_datalayer_sandbox_can_be_interrupted.py +0 -0
  210. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_bridge_mount.py +0 -0
  211. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_builds.py +0 -0
  212. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_cli_exec.py +0 -0
  213. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_cli_repl.py +0 -0
  214. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_client.py +0 -0
  215. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_client_contents.py +0 -0
  216. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_client_files.py +0 -0
  217. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_client_outcome_error_output.py +0 -0
  218. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_cloudflare.py +0 -0
  219. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_commands_envelope.py +0 -0
  220. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_console.py +0 -0
  221. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_contents_contract.py +0 -0
  222. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_contents_live_matrix.py +0 -0
  223. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_coreweave.py +0 -0
  224. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_datalayer_sandbox.py +0 -0
  225. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_accounts.py +0 -0
  226. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_attest.py +0 -0
  227. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_bases.py +0 -0
  228. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_breaker.py +0 -0
  229. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_build_secrets.py +0 -0
  230. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_builders.py +0 -0
  231. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_canonical.py +0 -0
  232. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_conformance.py +0 -0
  233. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_constraints.py +0 -0
  234. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_contract.py +0 -0
  235. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_cross_variant_report.py +0 -0
  236. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_doctor.py +0 -0
  237. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_errors.py +0 -0
  238. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_image_import.py +0 -0
  239. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_lifecycle.py +0 -0
  240. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_redact.py +0 -0
  241. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_environment_spec.py +0 -0
  242. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_eval.py +0 -0
  243. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_every_variant_has_a_real_interrupt.py +0 -0
  244. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_factory.py +0 -0
  245. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_google_colab.py +0 -0
  246. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_integration.py +0 -0
  247. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_jupyter_ingress.py +0 -0
  248. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_jupyter_server.py +0 -0
  249. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kaggle.py +0 -0
  250. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kaggle_execute.py +0 -0
  251. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kaggle_live.py +0 -0
  252. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kaggle_session.py +0 -0
  253. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_kernel_client_compatibility.py +0 -0
  254. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_lifecycle_vocabulary.py +0 -0
  255. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_manage.py +0 -0
  256. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_manage_datalayer.py +0 -0
  257. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_modal_session.py +0 -0
  258. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_models.py +0 -0
  259. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_output_line_fidelity.py +0 -0
  260. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_provider_catalog_names.py +0 -0
  261. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_provider_ingress.py +0 -0
  262. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_providers.py +0 -0
  263. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/tests/test_sandboxes.py +0 -0
  264. {code_sandboxes-1.8.2 → code_sandboxes-1.9.2}/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,93 @@
8
8
 
9
9
  ## Unreleased
10
10
 
11
+ ## 1.9.2
12
+
13
+ - **A real ECR repository name is lowercase; a real owner uid is not**
14
+ (`environments.adapters.datalayer.owner_repository`, new
15
+ `owner_cache_repository`; PLAN_ENV.md E1-07). Found live, 2026-09-14, the
16
+ first real Datalayer build ever run for a real account's own uid rather
17
+ than a lowercase test fixture: `DescribeRepositories` refused outright,
18
+ "Invalid parameter at 'repositoryName'" — this project's own uids are
19
+ ULIDs, conventionally uppercase, and ECR repository names match
20
+ `[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*` per path segment. Both the owner's own
21
+ repository and the owner's cache repository are lowered at the one place
22
+ each is built, so every caller (create, inspect, exists, delete, the
23
+ cache import/export) stays consistent with itself.
24
+
25
+ ## 1.9.1
26
+
27
+ - **mTLS to the build pool's `buildkitd`** (`environments.adapters.datalayer.Builder`,
28
+ `environments.resolve.BuildkitResolveRunner`; PLAN_ENV.md E1-06/E1-07). Both
29
+ drivers of `buildctl` took only `--addr`, which is what every prior live
30
+ drill needed against a plain-socket, ephemeral `buildkitd` — never a real
31
+ one. The build pool's real daemon on r1 takes mTLS connections only, so
32
+ reaching it needed three more flags neither driver had: `--tlscert`,
33
+ `--tlskey` and `--tlscacert`, each read from `DATALAYER_BUILDKIT_TLSCERT`,
34
+ `DATALAYER_BUILDKIT_TLSKEY` and `DATALAYER_BUILDKIT_TLSCACERT` when not
35
+ passed explicitly (`Builder`, matching how it already reads
36
+ `DATALAYER_BUILDKIT_ADDR`) or passed in by the caller (`BuildkitResolveRunner`,
37
+ constructed by durable's own `activities_environments.py`, which now reads
38
+ and forwards the same three). All three or none — a partial set is treated
39
+ as none, since a `buildkitd` requiring mTLS refuses a client carrying only
40
+ some of them at the daemon, with a less useful error than refusing here.
41
+
42
+ ## 1.9.0
43
+
44
+ - **The Daytona builder** (`environments.adapters.daytona`, PLAN_ENV.md
45
+ E2-04). A snapshot built from the approved Datalayer base — `uv pip sync --require-hashes` against the resolved lock, your files, `postInstall`
46
+ commands, and an explicit `tini -- sleep infinity` entrypoint — with the
47
+ owner's own Daytona organization, never this package's ambient
48
+ credentials. Confirmed live against a real, hash-verified lock: a real
49
+ snapshot reaches `Active` and a sandbox launched from it passes the full
50
+ sandbox contract, doctor included — the one managed variant, of the
51
+ three landing here, whose build-time state and launched state fully
52
+ agree.
53
+ - **The E2B builder** (`environments.adapters.e2b`, PLAN_ENV.md E2-03). A
54
+ template built from `code-interpreter-v1` — E2B's own proprietary
55
+ code-interpreter server ships baked into it only, so the build starts
56
+ there rather than from the Datalayer base and reconciles whatever it
57
+ shipped to exactly what the lock pins. Two live-found identity bugs are
58
+ fixed: `code-interpreter-v1` already holds an account at uid 1000 and a
59
+ group at gid 100 of its own, so the old `useradd` was silently landing
60
+ elsewhere — the build now renames the existing account instead — and the
61
+ base sets no locale at all, now set explicitly. A real build passes the
62
+ contract's doctor check in full. **Still open:** a launched sandbox
63
+ currently runs as root, not the contract's `1000:100` — the two
64
+ `systemd` services that actually execute a launched sandbox's code have
65
+ no `User=` of their own, and E2B's private server hardcodes `/home/user`
66
+ as the working directory, independent of anything the build sets.
67
+ - **The Modal builder** (`environments.adapters.modal`, PLAN_ENV.md E2-05).
68
+ An image built `from_aws_ecr` off the approved base, with a per-build
69
+ Secret made and torn down around it. The contract's own identity is put
70
+ back at launch: `modal_sandbox.py`'s session driver now drops itself to
71
+ `1000:100` for a sandbox launched from a built Environments artifact
72
+ specifically (gated on `image_id`, so general Modal sandbox usage
73
+ elsewhere is unaffected) — Modal ignores the image's own `USER`, so this
74
+ is the only place it can be restored. A separate, previously
75
+ silently-fatal bug is also fixed: the real launcher creates the sandbox
76
+ with no command arguments at all, so an entrypoint relying on `exec "$@"` alone was a no-op and every launched environment exited within
77
+ seconds; the entrypoint now falls back to `sleep infinity` when given
78
+ none. **Still open:** two of the core tier's checks (imports, filesystem)
79
+ still fail on a launched sandbox, for a reason not yet root-caused —
80
+ confirmed specific to a contract-built image under repeated `exec`, not
81
+ the identity fix.
82
+ - **A real, shared bug in all three managed launchers, found in review**:
83
+ `start()` creates the remote sandbox well before it marks itself
84
+ started, while `stop()` was guarded on that flag rather than on the
85
+ resource itself — a failure in between left a real, running sandbox
86
+ that `stop()` skipped entirely, orphaned for good. Fixed identically in
87
+ `daytona_sandbox.py`, `modal_sandbox.py` and `e2b_sandbox.py`.
88
+ - **A nightly live matrix for the three managed builders**
89
+ (`.github/workflows/environments-live.yml`, PLAN_ENV.md E2-11). Builds a
90
+ real artifact on each real provider from a real, hash-verified lock,
91
+ launches a sandbox from it, and runs the formal core tier — opening (or
92
+ commenting on) an issue naming the provider and the check on a genuine
93
+ failure. E2B and Modal run `xfail(strict=False)` for their own
94
+ already-documented gaps above; Daytona carries no such marker. Needs
95
+ provider secrets added to the repository before it does anything for
96
+ real.
97
+
11
98
  ## 1.8.2
12
99
 
13
100
  - **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.2
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.2"
@@ -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()
@@ -71,7 +71,12 @@ from ..files import files_step
71
71
  from ..resolve import WHEELHOUSE_IMAGE_PATH, WHEELHOUSE_PATH, apt_pins_in, locked_versions
72
72
  from ..spec import BuildSecret, Environment
73
73
 
74
- __all__ = ["ECR_ENVIRONMENT_PREFIX", "Builder", "owner_repository"]
74
+ __all__ = [
75
+ "ECR_ENVIRONMENT_PREFIX",
76
+ "Builder",
77
+ "owner_cache_repository",
78
+ "owner_repository",
79
+ ]
75
80
 
76
81
  #: Where an owner's environments live in ECR (D-10).
77
82
  ECR_ENVIRONMENT_PREFIX = "environments/u/"
@@ -89,10 +94,32 @@ DEFAULT_MAX_ARTIFACT_BYTES = 20 * 1024**3
89
94
 
90
95
 
91
96
  def owner_repository(owner_uid: str, environment_name: str) -> str:
92
- """The owner's repository for one environment: ``environments/u/<owner>/<name>``."""
97
+ """The owner's repository for one environment: ``environments/u/<owner>/<name>``.
98
+
99
+ Lowercased: an ECR repository name matches
100
+ ``[a-z0-9]+((\\.|_|__|-+)[a-z0-9]+)*`` per path segment, and this
101
+ project's own uids are ULIDs, conventionally uppercase — found live,
102
+ 2026-09-14, the first real build ever run for a real account's own
103
+ uid rather than a lowercase test fixture: ``DescribeRepositories``
104
+ refused it outright, "Invalid parameter at 'repositoryName'". Lowering
105
+ both segments here, the one place this reference is built, keeps every
106
+ caller (create, inspect, exists, delete) consistent with itself without
107
+ each needing to remember to.
108
+ """
93
109
  if not owner_uid or not environment_name:
94
110
  raise ValueError("an owner and an environment name make the repository")
95
- return f"{ECR_ENVIRONMENT_PREFIX}{owner_uid}/{environment_name}"
111
+ return f"{ECR_ENVIRONMENT_PREFIX}{owner_uid}/{environment_name}".lower()
112
+
113
+
114
+ def owner_cache_repository(owner_uid: str) -> str:
115
+ """The owner's own BuildKit cache repository: ``environments/cache/u/<owner>``.
116
+
117
+ Lowercased for the same reason ``owner_repository`` is (a ULID, not a
118
+ lowercase test fixture, is what a real build actually names).
119
+ """
120
+ if not owner_uid:
121
+ raise ValueError("an owner makes the cache repository")
122
+ return f"environments/cache/u/{owner_uid}".lower()
96
123
 
97
124
 
98
125
  def _secret_mount(secret: BuildSecret) -> str:
@@ -127,6 +154,15 @@ class Builder:
127
154
  looks like (E1-06).
128
155
  address
129
156
  ``buildkitd``'s address, when it is not the default socket.
157
+ tlscert, tlskey, tlscacert
158
+ Paths to the client certificate, its key, and the CA that signed
159
+ ``buildkitd``'s own server certificate — the build pool's daemon
160
+ takes mTLS connections only (PLAN_ENV.md E1-06), so a ``tcp://``
161
+ ``address`` with none of these set fails the handshake, not the
162
+ build. Unset (the default) is right for a plain-socket
163
+ ``buildkitd``, such as ``plane local``'s own ephemeral one, which
164
+ was every ``buildkitd`` this adapter had ever actually built
165
+ against before the pool on r1 existed.
130
166
  region
131
167
  The registry's region, for the ECR API calls.
132
168
  ecr
@@ -145,6 +181,9 @@ class Builder:
145
181
  credential: Any = None,
146
182
  buildctl: str | None = None,
147
183
  address: str | None = None,
184
+ tlscert: str | None = None,
185
+ tlskey: str | None = None,
186
+ tlscacert: str | None = None,
148
187
  region: str | None = None,
149
188
  ecr: Any = None,
150
189
  run: Callable[..., subprocess.CompletedProcess[str]] | None = None,
@@ -155,6 +194,9 @@ class Builder:
155
194
  self._credential = credential
156
195
  self._buildctl = (shutil.which("buildctl") or "") if buildctl is None else buildctl
157
196
  self._address = address or os.environ.get("DATALAYER_BUILDKIT_ADDR", "").strip()
197
+ self._tlscert = tlscert or os.environ.get("DATALAYER_BUILDKIT_TLSCERT", "").strip()
198
+ self._tlskey = tlskey or os.environ.get("DATALAYER_BUILDKIT_TLSKEY", "").strip()
199
+ self._tlscacert = tlscacert or os.environ.get("DATALAYER_BUILDKIT_TLSCACERT", "").strip()
158
200
  self._region = region or os.environ.get("AWS_REGION", "us-east-1")
159
201
  self._ecr = ecr
160
202
  self._run = run or subprocess.run
@@ -359,7 +401,7 @@ class Builder:
359
401
  repository = owner_repository(request.owner_uid, request.environment.metadata.name)
360
402
  registry = self._registry_host()
361
403
  self._ensure_repository(repository)
362
- self._ensure_cache_repository(f"environments/cache/u/{request.owner_uid}")
404
+ self._ensure_cache_repository(owner_cache_repository(request.owner_uid))
363
405
  tag = f"v{request.version}-{request.build_uid}"
364
406
  reference = f"{registry}/{repository}:{tag}"
365
407
  with tempfile.TemporaryDirectory(prefix="dl-build-") as directory:
@@ -384,6 +426,7 @@ class Builder:
384
426
  command = [
385
427
  self._buildctl,
386
428
  *(["--addr", self._address] if self._address else []),
429
+ *self._tls_options(),
387
430
  "build",
388
431
  "--frontend",
389
432
  "dockerfile.v0",
@@ -534,6 +577,22 @@ class Builder:
534
577
  self._ecr = boto3.client("ecr", region_name=self._region)
535
578
  return self._ecr
536
579
 
580
+ def _tls_options(self) -> list[str]:
581
+ """The ``buildctl`` global flags for the build pool's mTLS (E1-06).
582
+
583
+ All three or none: a `buildkitd` that takes mTLS refuses a client
584
+ with only some of them, so a partial set here would fail at the
585
+ daemon with a confusing error rather than a clear one about the
586
+ deployment's own configuration.
587
+ """
588
+ if not (self._tlscert and self._tlskey and self._tlscacert):
589
+ return []
590
+ return [
591
+ f"--tlscert={self._tlscert}",
592
+ f"--tlskey={self._tlskey}",
593
+ f"--tlscacert={self._tlscacert}",
594
+ ]
595
+
537
596
  def _registry_host(self) -> str:
538
597
  registry = str(getattr(self._credential, "registry", "") or "")
539
598
  if not registry:
@@ -605,7 +664,7 @@ class Builder:
605
664
  def _cache_options(self, request: BuildRequest) -> list[str]:
606
665
  """The owner's cache, imported and exported: never another owner's (D-12)."""
607
666
  registry = self._registry_host()
608
- cache = f"{registry}/environments/cache/u/{request.owner_uid}:{self.variant}"
667
+ cache = f"{registry}/{owner_cache_repository(request.owner_uid)}:{self.variant}"
609
668
  return [
610
669
  "--import-cache",
611
670
  f"type=registry,ref={cache}",