modal 1.5.6.dev17__tar.gz → 1.5.6.dev19__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 (239) hide show
  1. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/PKG-INFO +1 -1
  2. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_workspace.py +8 -3
  3. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/app.py +76 -19
  4. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/entry_point.py +1 -3
  5. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/workspace.py +52 -8
  6. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/client.pyi +2 -2
  7. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/config.py +1 -0
  8. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/sandbox.py +57 -16
  9. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/sandbox.pyi +8 -0
  10. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/types.py +2 -0
  11. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/workspace.pyi +15 -5
  12. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/PKG-INFO +1 -1
  13. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/SOURCES.txt +0 -1
  14. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_pb2.py +1403 -1384
  15. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_pb2.pyi +57 -4
  16. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_version/__init__.py +1 -1
  17. modal-1.5.6.dev17/modal/cli/bootstrap.py +0 -139
  18. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/LICENSE +0 -0
  19. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/README.md +0 -0
  20. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/__init__.py +0 -0
  21. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/__main__.py +0 -0
  22. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_billing.py +0 -0
  23. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_clustered_functions.py +0 -0
  24. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_clustered_functions.pyi +0 -0
  25. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_container_entrypoint.py +0 -0
  26. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_environments.py +0 -0
  27. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_function_variants.py +0 -0
  28. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_functions.py +0 -0
  29. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_grpc_client.py +0 -0
  30. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_image.py +0 -0
  31. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_ipython.py +0 -0
  32. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_load_context.py +0 -0
  33. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_location.py +0 -0
  34. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_logs.py +0 -0
  35. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_logs_manager.py +0 -0
  36. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_logs_manager.pyi +0 -0
  37. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_object.py +0 -0
  38. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/__init__.py +0 -0
  39. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/manager.py +0 -0
  40. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/pty.py +0 -0
  41. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/rich.py +0 -0
  42. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_output/status.py +0 -0
  43. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_partial_function.py +0 -0
  44. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_resolver.py +0 -0
  45. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_resources.py +0 -0
  46. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/__init__.py +0 -0
  47. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/asgi.py +0 -0
  48. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/container_io_manager.py +0 -0
  49. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/container_io_manager.pyi +0 -0
  50. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/execution_context.py +0 -0
  51. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/execution_context.pyi +0 -0
  52. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  53. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/task_lifecycle_manager.py +0 -0
  54. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  55. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/telemetry.py +0 -0
  56. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/user_code_event_loop.py +0 -0
  57. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_runtime/user_code_imports.py +0 -0
  58. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_serialization.py +0 -0
  59. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_server.py +0 -0
  60. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_supports_logs.py +0 -0
  61. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_traceback.py +0 -0
  62. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_tunnel.py +0 -0
  63. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_tunnel.pyi +0 -0
  64. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_type_manager.py +0 -0
  65. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/__init__.py +0 -0
  66. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/app_utils.py +0 -0
  67. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/async_utils.py +0 -0
  68. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/auth_token_manager.py +0 -0
  69. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/blob_utils.py +0 -0
  70. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/browser_utils.py +0 -0
  71. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/bytes_io_segment_payload.py +0 -0
  72. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/curl_utils.py +0 -0
  73. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/deprecation.py +0 -0
  74. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/display_utils.py +0 -0
  75. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/docker_utils.py +0 -0
  76. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/function_utils.py +0 -0
  77. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/git_utils.py +0 -0
  78. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/grpc_testing.py +0 -0
  79. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/grpc_utils.py +0 -0
  80. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/hash_utils.py +0 -0
  81. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/http_utils.py +0 -0
  82. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/idle_countdown.py +0 -0
  83. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/jwt_utils.py +0 -0
  84. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/logger.py +0 -0
  85. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/mount_utils.py +0 -0
  86. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/name_utils.py +0 -0
  87. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/package_utils.py +0 -0
  88. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/pattern_utils.py +0 -0
  89. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/proxy_support.py +0 -0
  90. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/rand_pb_testing.py +0 -0
  91. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/sandbox_fs_utils.py +0 -0
  92. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/shell_utils.py +0 -0
  93. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/task_command_router_client.py +0 -0
  94. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_utils/time_utils.py +0 -0
  95. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/__init__.py +0 -0
  96. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  97. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/cloudpickle.py +0 -0
  98. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/tblib.py +0 -0
  99. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_vendor/version.py +0 -0
  100. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/_watcher.py +0 -0
  101. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/app.py +0 -0
  102. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/app.pyi +0 -0
  103. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/billing.py +0 -0
  104. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/billing.pyi +0 -0
  105. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2023.12.312.txt +0 -0
  106. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2023.12.txt +0 -0
  107. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2024.04.txt +0 -0
  108. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2024.10.txt +0 -0
  109. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/2025.06.txt +0 -0
  110. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/PREVIEW.txt +0 -0
  111. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/README.md +0 -0
  112. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/builder/base-images.json +0 -0
  113. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/call_graph.py +0 -0
  114. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/__init__.py +0 -0
  115. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_download.py +0 -0
  116. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_help.py +0 -0
  117. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_logs.py +0 -0
  118. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_stats.py +0 -0
  119. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/_traceback.py +0 -0
  120. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/billing.py +0 -0
  121. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/changelog.py +0 -0
  122. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/cluster.py +0 -0
  123. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/config.py +0 -0
  124. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/container.py +0 -0
  125. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/curl.py +0 -0
  126. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/dashboard.py +0 -0
  127. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/dict.py +0 -0
  128. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/endpoint.py +0 -0
  129. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/environment.py +0 -0
  130. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/function.py +0 -0
  131. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/image.py +0 -0
  132. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/import_refs.py +0 -0
  133. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/launch.py +0 -0
  134. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/logo.py +0 -0
  135. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/network_file_system.py +0 -0
  136. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/profile.py +0 -0
  137. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/programs/__init__.py +0 -0
  138. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/programs/run_jupyter.py +0 -0
  139. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/programs/vscode.py +0 -0
  140. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/queues.py +0 -0
  141. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/run.py +0 -0
  142. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/secret.py +0 -0
  143. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/selector.py +0 -0
  144. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/server.py +0 -0
  145. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/shell.py +0 -0
  146. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/skills.py +0 -0
  147. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/token.py +0 -0
  148. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/utils.py +0 -0
  149. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cli/volume.py +0 -0
  150. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/client.py +0 -0
  151. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cloud_bucket_mount.py +0 -0
  152. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cloud_bucket_mount.pyi +0 -0
  153. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cls.py +0 -0
  154. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/cls.pyi +0 -0
  155. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/container_process.py +0 -0
  156. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/container_process.pyi +0 -0
  157. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/dict.py +0 -0
  158. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/dict.pyi +0 -0
  159. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/environments.py +0 -0
  160. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/environments.pyi +0 -0
  161. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/exception.py +0 -0
  162. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/experimental/__init__.py +0 -0
  163. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/experimental/flash.py +0 -0
  164. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/experimental/flash.pyi +0 -0
  165. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/experimental/ipython.py +0 -0
  166. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/file_pattern_matcher.py +0 -0
  167. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/functions.py +0 -0
  168. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/functions.pyi +0 -0
  169. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/image.py +0 -0
  170. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/image.pyi +0 -0
  171. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/io_streams.py +0 -0
  172. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/io_streams.pyi +0 -0
  173. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/mount.py +0 -0
  174. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/mount.pyi +0 -0
  175. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/network_file_system.py +0 -0
  176. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/network_file_system.pyi +0 -0
  177. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/object.py +0 -0
  178. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/object.pyi +0 -0
  179. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/output.py +0 -0
  180. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/parallel_map.py +0 -0
  181. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/parallel_map.pyi +0 -0
  182. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/partial_function.py +0 -0
  183. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/partial_function.pyi +0 -0
  184. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/proxy.py +0 -0
  185. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/proxy.pyi +0 -0
  186. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/py.typed +0 -0
  187. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/queue.py +0 -0
  188. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/queue.pyi +0 -0
  189. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/retries.py +0 -0
  190. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/runner.py +0 -0
  191. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/runner.pyi +0 -0
  192. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/running_app.py +0 -0
  193. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/sandbox_fs.py +0 -0
  194. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/sandbox_fs.pyi +0 -0
  195. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/schedule.py +0 -0
  196. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/scheduler_placement.py +0 -0
  197. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/secret.py +0 -0
  198. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/secret.pyi +0 -0
  199. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/server.py +0 -0
  200. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/server.pyi +0 -0
  201. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/serving.py +0 -0
  202. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/serving.pyi +0 -0
  203. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/skills/modal/SKILL.md +0 -0
  204. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/snapshot.py +0 -0
  205. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/snapshot.pyi +0 -0
  206. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/stream_type.py +0 -0
  207. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/token_flow.py +0 -0
  208. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/token_flow.pyi +0 -0
  209. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/volume.py +0 -0
  210. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/volume.pyi +0 -0
  211. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal/workspace.py +0 -0
  212. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/dependency_links.txt +0 -0
  213. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/entry_points.txt +0 -0
  214. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/requires.txt +0 -0
  215. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal.egg-info/top_level.txt +0 -0
  216. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/__init__.py +0 -0
  217. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/gen_cli_docs.py +0 -0
  218. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/gen_cli_docs_main.py +0 -0
  219. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/gen_reference_docs.py +0 -0
  220. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/gen_reference_docs_main.py +0 -0
  221. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/mdmd/__init__.py +0 -0
  222. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/mdmd/mdmd.py +0 -0
  223. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/mdmd/signatures.py +0 -0
  224. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/mdmd/types.py +0 -0
  225. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_docs/reference_intro.md +0 -0
  226. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/__init__.py +0 -0
  227. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_grpc.py +0 -0
  228. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_pb2_grpc.py +0 -0
  229. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/api_pb2_grpc.pyi +0 -0
  230. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/modal_api_grpc.py +0 -0
  231. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/py.typed +0 -0
  232. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_grpc.py +0 -0
  233. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2.py +0 -0
  234. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2.pyi +0 -0
  235. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  236. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  237. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/modal_version/__main__.py +0 -0
  238. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/pyproject.toml +0 -0
  239. {modal-1.5.6.dev17 → modal-1.5.6.dev19}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.6.dev17
3
+ Version: 1.5.6.dev19
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -137,17 +137,20 @@ class _WorkspaceProxyTokenManager:
137
137
  """mdmd:hidden"""
138
138
  self._workspace = workspace
139
139
 
140
- async def create(self) -> TokenData:
140
+ async def create(self, name: str = "") -> TokenData:
141
141
  """Create a new proxy token for the Workspace.
142
142
 
143
+ Args:
144
+ name: An optional name to help identify the token.
145
+
143
146
  Examples:
144
147
  ```python notest
145
- token = modal.Workspace.from_context().proxy_tokens.create()
148
+ token = modal.Workspace.from_context().proxy_tokens.create(name="production-webhooks")
146
149
  print(token.token_id, token.token_secret)
147
150
  ```
148
151
  """
149
152
  await self._workspace.hydrate()
150
- resp = await self._workspace.client.stub.WebhookTokenCreate(api_pb2.WebhookTokenCreateRequest())
153
+ resp = await self._workspace.client.stub.WebhookTokenCreate(api_pb2.WebhookTokenCreateRequest(name=name))
151
154
  return TokenData(token_id=resp.token_id, token_secret=resp.token_secret)
152
155
 
153
156
  async def list(self, environment_name: Optional[str] = None) -> builtins.list[ProxyTokenInfo]:
@@ -180,6 +183,8 @@ class _WorkspaceProxyTokenManager:
180
183
  token_id=token.token_id,
181
184
  created_at=timestamp_to_localized_dt(token.created_at),
182
185
  scoped=token.scoped,
186
+ name=token.name,
187
+ created_by=token.created_by.username,
183
188
  )
184
189
  for token in resp.tokens
185
190
  ]
@@ -3,13 +3,15 @@ import re
3
3
  import sys
4
4
  import time
5
5
  import warnings
6
+ from collections.abc import Mapping
6
7
  from json import dumps
7
8
  from typing import Literal, get_args
8
9
 
9
10
  import click
10
11
  import rich
11
12
  from click import UsageError
12
- from rich.table import Column
13
+ from google.protobuf.json_format import MessageToDict
14
+ from rich.table import Column, Table
13
15
  from rich.text import Text
14
16
 
15
17
  from modal._environments import ensure_env
@@ -620,8 +622,9 @@ async def dashboard(
620
622
  @click.argument("app_identifier")
621
623
  @env_option
622
624
  @click.option("--json", is_flag=True, default=False)
625
+ @click.option("--no-color", is_flag=True, default=False, help="Disable colors in the output.")
623
626
  @synchronizer.create_blocking
624
- async def info(app_identifier: str, *, env: str | None = None, json: bool = False):
627
+ async def info(app_identifier: str, *, env: str | None = None, json: bool = False, no_color: bool = False):
625
628
  """Show an App's lifecycle, Functions, and Servers.
626
629
 
627
630
  Examples:
@@ -650,6 +653,17 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
650
653
  state = APP_STATE_TO_MESSAGE.get(lifecycle.app_state, Text("unknown", style="gray"))
651
654
 
652
655
  if json:
656
+ summaries = {
657
+ function_id: MessageToDict(summary, preserving_proto_field_name=True)
658
+ for function_id, summary in resp.function_info_summaries.items()
659
+ }
660
+
661
+ def entries_with_info(entries: Mapping[str, str]) -> dict[str, dict]:
662
+ return {
663
+ name: {"id": function_id, "summary": summaries.get(function_id, {})}
664
+ for name, function_id in sorted(entries.items())
665
+ }
666
+
653
667
  output.print_json(
654
668
  dumps(
655
669
  {
@@ -665,28 +679,71 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
665
679
  "stopped_at": timestamp_to_localized_str(lifecycle.stopped_at, json),
666
680
  "stopped_by": lifecycle.stopped_by or None,
667
681
  },
668
- "functions": dict(sorted(app_info.functions.items())),
669
- "servers": dict(sorted(app_info.servers.items())),
682
+ "functions": entries_with_info(app_info.functions),
683
+ "servers": entries_with_info(app_info.servers),
670
684
  }
671
685
  )
672
686
  )
673
687
  return
674
688
 
675
- output.print(
676
- Text.assemble("App: ", (app_info.app_id, "bold"), " - ", (app_info.description, "bold"), " (", state, ")")
677
- )
689
+ header = Table(show_header=False, box=None, pad_edge=False, padding=(0, 1))
690
+ header.add_row(Text("App:"), Text(app_info.description))
691
+ header.add_row(Text("App ID:"), Text(app_info.app_id))
692
+ header.add_row(Text("State:"), Text(state.plain))
678
693
 
679
- if app_info.functions:
680
- display_table(["Name", "Function ID"], sorted(app_info.functions.items()), title="Functions")
681
- if app_info.servers:
682
- display_table(["Name", "Function ID"], sorted(app_info.servers.items()), title="Servers")
694
+ def event(label: str, timestamp: float, actor: str, version: int = 0) -> None:
695
+ parts = [timestamp_to_localized_str(timestamp, isotz=False), actor]
696
+ if version:
697
+ parts.insert(0, f"v{version}")
698
+ header.add_row(Text(label + ":"), Text(" · ".join(p for p in parts if p)))
683
699
 
684
- events: list[list[Text | str | None]] = [
685
- ["Created", timestamp_to_localized_str(lifecycle.created_at, json), lifecycle.created_by]
686
- ]
687
- if lifecycle.deployed_at:
688
- label = f"Deployed (v{lifecycle.version})" if lifecycle.version else "Deployed"
689
- events.append([label, timestamp_to_localized_str(lifecycle.deployed_at, json), lifecycle.deployed_by])
690
700
  if lifecycle.stopped_at:
691
- events.append(["Stopped", timestamp_to_localized_str(lifecycle.stopped_at, json), lifecycle.stopped_by])
692
- display_table(["Event", "Time", "By"], events, title="Lifecycle")
701
+ event("Stopped", lifecycle.stopped_at, lifecycle.stopped_by)
702
+ if lifecycle.deployed_at:
703
+ event("Deployment", lifecycle.deployed_at, lifecycle.deployed_by, lifecycle.version)
704
+ event("Created", lifecycle.created_at, lifecycle.created_by)
705
+ output.print(header)
706
+
707
+ name_width = max((len(name) for name in [*app_info.functions, *app_info.servers]), default=0)
708
+ for title, entries in (("Functions", app_info.functions), ("Servers", app_info.servers)):
709
+ if not entries:
710
+ continue
711
+ output.print("")
712
+ output.print(Text(f"{title} ({len(entries)}):"))
713
+ for index, (name, function_id) in enumerate(sorted(entries.items())):
714
+ if index:
715
+ output.print("")
716
+ identity = Table(show_header=False, box=None, pad_edge=False, padding=(0, 1))
717
+ identity.add_column(width=name_width + 2)
718
+ identity.add_column(no_wrap=True)
719
+ identity.add_row(Text(f" {name}"), Text(function_id))
720
+ output.print(identity)
721
+ if function_id not in resp.function_info_summaries:
722
+ continue
723
+ summary = resp.function_info_summaries[function_id]
724
+ hardware = [f"{gpu.count} × {gpu.gpu_type} GPU" for gpu in summary.gpu_config]
725
+ metadata = Text(f" {hardware[0] if hardware else 'CPU'}")
726
+ if len(hardware) > 1:
727
+ metadata.append(f" ({', '.join(hardware[1:])})", style=None if no_color else "bright_black")
728
+ if summary.web_function:
729
+ metadata.append(" · Web function")
730
+ if schedule := _app_function_schedule(summary.schedule):
731
+ metadata.append(" · ")
732
+ metadata.append(schedule)
733
+ if summary.HasField("requires_proxy_auth") and not summary.requires_proxy_auth:
734
+ metadata.append(" · ")
735
+ metadata.append("Unauthenticated", style=None if no_color else "yellow")
736
+ output.print(metadata)
737
+
738
+
739
+ def _app_function_schedule(schedule: api_pb2.Schedule) -> str | None:
740
+ if schedule.HasField("cron"):
741
+ return f"{schedule.cron.cron_string} ({schedule.cron.timezone or 'UTC'})"
742
+ if schedule.HasField("period"):
743
+ parts = [
744
+ f"{value:g} {field.name.removesuffix('s') if value == 1 else field.name}"
745
+ for field, value in schedule.period.ListFields()
746
+ if value
747
+ ]
748
+ return "Every " + ", ".join(parts)
749
+ return None
@@ -11,7 +11,6 @@ from . import run, shell as shell_module
11
11
  from ._help import ModalCommand, ModalGroup, ModalProfileOption
12
12
  from .app import app_cli
13
13
  from .billing import billing_cli
14
- from .bootstrap import bootstrap
15
14
  from .changelog import changelog
16
15
  from .cluster import cluster_cli
17
16
  from .config import config_cli
@@ -131,8 +130,6 @@ entrypoint_cli.add_command(secret_cli, panel="Storage")
131
130
  entrypoint_cli.add_command(queue_cli, panel="Storage")
132
131
  entrypoint_cli.add_command(volume_cli, panel="Storage")
133
132
 
134
- entrypoint_cli.add_command(setup, panel="Onboarding")
135
- entrypoint_cli.add_command(bootstrap, panel="Onboarding")
136
133
 
137
134
  entrypoint_cli.add_command(workspace_cli, panel="Configuration")
138
135
  entrypoint_cli.add_command(environment_cli, panel="Configuration")
@@ -140,6 +137,7 @@ entrypoint_cli.add_command(profile_cli, panel="Configuration")
140
137
  entrypoint_cli.add_command(config_cli, panel="Configuration")
141
138
  entrypoint_cli.add_command(token_cli, panel="Configuration")
142
139
  entrypoint_cli.add_command(skills_cli, panel="Configuration")
140
+ entrypoint_cli.add_command(setup, panel="Configuration")
143
141
 
144
142
  entrypoint_cli.add_command(billing_cli, panel="Observability")
145
143
  entrypoint_cli.add_command(changelog, panel="Observability")
@@ -76,15 +76,23 @@ proxy_tokens_cli = ModalGroup(name="proxy-tokens", help=PROXY_TOKENS_HELP_TEXT)
76
76
  workspace_cli.add_command(proxy_tokens_cli)
77
77
 
78
78
 
79
- @proxy_tokens_cli.command("create", help="Create a proxy token in the current Workspace.")
79
+ @proxy_tokens_cli.command("create")
80
+ @click.option("--name", default="", help="Name to help identify the token.")
80
81
  @click.option("--json", is_flag=True, default=False)
81
- def proxy_tokens_create(*, json: bool = False):
82
+ def proxy_tokens_create(*, name: str = "", json: bool = False):
82
83
  """Create a proxy token in the current Workspace.
83
84
 
84
85
  The new token's ID and secret will be printed to stdout. The secret is only
85
86
  shown at creation time and cannot be retrieved later.
87
+
88
+ Examples:
89
+
90
+ ```
91
+ modal workspace proxy-tokens create --name production-webhooks
92
+ modal workspace proxy-tokens create --json
93
+ ```
86
94
  """
87
- token = Workspace.from_context().proxy_tokens.create()
95
+ token = Workspace.from_context().proxy_tokens.create(name=name)
88
96
  output_manager = OutputManager.get()
89
97
  bearer = f"Bearer {token.token_id}.{token.token_secret}"
90
98
  if json:
@@ -104,7 +112,7 @@ def proxy_tokens_create(*, json: bool = False):
104
112
  output_manager.print(table)
105
113
 
106
114
 
107
- @proxy_tokens_cli.command("list", help="List the proxy tokens of the current Workspace.")
115
+ @proxy_tokens_cli.command("list")
108
116
  @click.option(
109
117
  "-e",
110
118
  "--environment",
@@ -113,39 +121,75 @@ def proxy_tokens_create(*, json: bool = False):
113
121
  )
114
122
  @click.option("--json", is_flag=True, default=False)
115
123
  def proxy_tokens_list(environment: str | None = None, json: bool = False):
124
+ """List the proxy tokens of the current Workspace.
125
+
126
+ Examples:
127
+
128
+ ```
129
+ modal workspace proxy-tokens list
130
+ modal workspace proxy-tokens list --environment prod
131
+ modal workspace proxy-tokens list --json
132
+ ```
133
+ """
116
134
  tokens = Workspace.from_context().proxy_tokens.list(environment_name=environment)
117
135
  # Emit a real boolean for JSON output, but a string for the rich table (which can't render a bare bool).
118
136
  rows = [
119
137
  [
138
+ token.name,
120
139
  token.token_id,
121
140
  timestamp_to_localized_str(token.created_at.timestamp(), json),
141
+ token.created_by,
122
142
  token.scoped if json else str(token.scoped),
123
143
  ]
124
144
  for token in tokens
125
145
  ]
126
- display_table(["Token ID", "Created at", "Scoped"], rows, json=json)
146
+ display_table(["Name", "Token ID", "Created at", "Created by", "Scoped"], rows, json=json)
127
147
 
128
148
 
129
- @proxy_tokens_cli.command("allow", help="Allow a proxy token to authenticate to an environment.", no_args_is_help=True)
149
+ @proxy_tokens_cli.command("allow", no_args_is_help=True)
130
150
  @click.argument("token_id")
131
151
  @click.argument("environment_name")
132
152
  def proxy_tokens_allow(token_id: str, environment_name: str):
153
+ """Allow a proxy token to authenticate to an environment.
154
+
155
+ Example:
156
+
157
+ ```
158
+ modal workspace proxy-tokens allow wk-123 prod
159
+ ```
160
+ """
133
161
  Workspace.from_context().proxy_tokens.allow(token_id, environment_name)
134
162
  rich.print(f"[green]✓[/green] Allowed proxy token {token_id!r} to authenticate to environment {environment_name!r}")
135
163
 
136
164
 
137
- @proxy_tokens_cli.command("revoke", help="Revoke a proxy token's access to an environment.", no_args_is_help=True)
165
+ @proxy_tokens_cli.command("revoke", no_args_is_help=True)
138
166
  @click.argument("token_id")
139
167
  @click.argument("environment_name")
140
168
  def proxy_tokens_revoke(token_id: str, environment_name: str):
169
+ """Revoke a proxy token's access to an environment.
170
+
171
+ Example:
172
+
173
+ ```
174
+ modal workspace proxy-tokens revoke wk-123 prod
175
+ ```
176
+ """
141
177
  Workspace.from_context().proxy_tokens.revoke(token_id, environment_name)
142
178
  rich.print(f"[green]✓[/green] Revoked proxy token {token_id!r} access to environment {environment_name!r}")
143
179
 
144
180
 
145
- @proxy_tokens_cli.command("delete", help="Delete a proxy token from the current Workspace.", no_args_is_help=True)
181
+ @proxy_tokens_cli.command("delete", no_args_is_help=True)
146
182
  @click.argument("token_id")
147
183
  @yes_option
148
184
  def proxy_tokens_delete(token_id: str, yes: bool = False):
185
+ """Delete a proxy token from the current Workspace.
186
+
187
+ Example:
188
+
189
+ ```
190
+ modal workspace proxy-tokens delete wk-123
191
+ ```
192
+ """
149
193
  if not yes:
150
194
  message = (
151
195
  f"Are you sure you want to delete proxy token {token_id!r}? Requests using this token will be rejected."
@@ -77,7 +77,7 @@ class _Client:
77
77
  server_url: str,
78
78
  client_type: int,
79
79
  credentials: typing.Optional[tuple[str, str]],
80
- version: str = "1.5.6.dev17",
80
+ version: str = "1.5.6.dev19",
81
81
  *,
82
82
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
83
83
  ):
@@ -277,7 +277,7 @@ class Client:
277
277
  server_url: str,
278
278
  client_type: int,
279
279
  credentials: typing.Optional[tuple[str, str]],
280
- version: str = "1.5.6.dev17",
280
+ version: str = "1.5.6.dev19",
281
281
  *,
282
282
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
283
283
  ):
@@ -377,6 +377,7 @@ _SETTINGS = {
377
377
  "image_builder_version": _Setting(),
378
378
  "strict_parameters": _Setting(False, transform=_to_boolean), # For internal/experimental use
379
379
  "sandbox_v2": _Setting(None, transform=_to_boolean),
380
+ "use_control_plane_sidecar_create": _Setting(False, transform=_to_boolean), # For internal/experimental use
380
381
  "snapshot_debug": _Setting(False, transform=_to_boolean),
381
382
  "cuda_checkpoint_path": _Setting("/__modal/.bin/cuda-checkpoint"), # Used for snapshotting GPU memory.
382
383
  # Used to indicate gVisor cuda-checkpoint automation.
@@ -2979,6 +2979,15 @@ class _SidecarContainer:
2979
2979
  _MAIN_CONTAINER_NAME: str = "main"
2980
2980
 
2981
2981
 
2982
+ def _use_control_plane_sidecar_create(is_v2: bool) -> bool:
2983
+ """Whether a sidecar create request goes to the Modal server rather than over the Sandbox connection.
2984
+
2985
+ Opt-in via the `use_control_plane_sidecar_create` config setting. Only V2
2986
+ Sandboxes can; V1 Sandboxes always create sidecars over the Sandbox connection.
2987
+ """
2988
+ return is_v2 and config.get("use_control_plane_sidecar_create")
2989
+
2990
+
2982
2991
  class _SidecarManager:
2983
2992
  """Creates and manages sidecar containers in a Sandbox."""
2984
2993
 
@@ -3060,8 +3069,13 @@ class _SidecarManager:
3060
3069
  "or `.snapshot_filesystem()`\n"
3061
3070
  )
3062
3071
 
3063
- secrets = secrets or []
3064
- hydrate_coros = [secret.hydrate(client=self._sandbox._client) for secret in secrets] + [
3072
+ env_dict, resolvable_secrets = _split_env_dict_and_resolvable_secrets(list(secrets or []))
3073
+ if env:
3074
+ # `env` takes precedence over environment variables from secrets
3075
+ env_dict |= env
3076
+ _validate_sandbox_env(env_dict)
3077
+
3078
+ hydrate_coros = [secret.hydrate(client=self._sandbox._client) for secret in resolvable_secrets] + [
3065
3079
  volume.hydrate(client=self._sandbox._client) for _, volume in validated_volumes
3066
3080
  ]
3067
3081
  await TaskContext.gather(*hydrate_coros)
@@ -3073,21 +3087,48 @@ class _SidecarManager:
3073
3087
  # Relies on dicts being ordered (true as of Python 3.6).
3074
3088
  volume_mounts = [_volume_to_mount_proto(path, volume) for path, volume in validated_volumes]
3075
3089
 
3076
- task_id, command_router_client = await self._get_command_router()
3090
+ network_access = _build_outbound_network_access(False, outbound_cidr_allowlist, outbound_domain_allowlist)
3091
+ pty_info = _Sandbox._default_pty_info() if pty else None
3092
+
3093
+ if _use_control_plane_sidecar_create(self._sandbox._is_v2):
3094
+ definition = api_pb2.Sandbox(
3095
+ entrypoint_args=list(args),
3096
+ image_id=image.object_id,
3097
+ secret_ids=[secret.object_id for secret in resolvable_secrets],
3098
+ workdir=workdir,
3099
+ volume_mounts=volume_mounts,
3100
+ network_access=network_access,
3101
+ pty_info=pty_info,
3102
+ )
3103
+ create_req = api_pb2.SandboxContainerCreateV2Request(
3104
+ sandbox_id=self._sandbox.object_id,
3105
+ container_name=name,
3106
+ definition=definition,
3107
+ ephemeral_secrets=api_pb2.StringMap(contents=env_dict) if env_dict else None,
3108
+ )
3109
+ client = self._sandbox._client
3110
+ assert client._auth_token_manager
3111
+ auth_token = await client._auth_token_manager.get_token()
3112
+ create_resp = await client.stub.SandboxContainerCreateV2(
3113
+ create_req, metadata=[("x-modal-auth-token", auth_token)]
3114
+ )
3115
+ else:
3116
+ task_id, command_router_client = await self._get_command_router()
3117
+ create_resp = await command_router_client.container_create(
3118
+ sr_pb2.TaskContainerCreateRequest(
3119
+ task_id=task_id,
3120
+ container_name=name,
3121
+ image_id=image.object_id,
3122
+ args=list(args),
3123
+ env=env_dict,
3124
+ workdir=workdir or "",
3125
+ secret_ids=[secret.object_id for secret in resolvable_secrets],
3126
+ volume_mounts=volume_mounts,
3127
+ network_access=network_access,
3128
+ pty_info=pty_info,
3129
+ )
3130
+ )
3077
3131
 
3078
- create_req = sr_pb2.TaskContainerCreateRequest(
3079
- task_id=task_id,
3080
- container_name=name,
3081
- image_id=image.object_id,
3082
- args=list(args),
3083
- env=env or {},
3084
- workdir=workdir or "",
3085
- secret_ids=[secret.object_id for secret in secrets],
3086
- volume_mounts=volume_mounts,
3087
- network_access=_build_outbound_network_access(False, outbound_cidr_allowlist, outbound_domain_allowlist),
3088
- pty_info=_Sandbox._default_pty_info() if pty else None,
3089
- )
3090
- create_resp = await command_router_client.container_create(create_req)
3091
3132
  container_id = create_resp.container_id
3092
3133
  container_name = create_resp.container_name or name
3093
3134
  return _SidecarContainer(self._sandbox, container_id, container_name)
@@ -1222,6 +1222,14 @@ class _SidecarContainer:
1222
1222
  """
1223
1223
  ...
1224
1224
 
1225
+ def _use_control_plane_sidecar_create(is_v2: bool) -> bool:
1226
+ """Whether a sidecar create request goes to the Modal server rather than over the Sandbox connection.
1227
+
1228
+ Opt-in via the `use_control_plane_sidecar_create` config setting. Only V2
1229
+ Sandboxes can; V1 Sandboxes always create sidecars over the Sandbox connection.
1230
+ """
1231
+ ...
1232
+
1225
1233
  class _SidecarManager:
1226
1234
  """Creates and manages sidecar containers in a Sandbox."""
1227
1235
  def __init__(self, sandbox: _Sandbox) -> None:
@@ -310,6 +310,8 @@ class ProxyTokenInfo:
310
310
  token_id: str
311
311
  created_at: datetime
312
312
  scoped: bool
313
+ name: str = ""
314
+ created_by: str = ""
313
315
 
314
316
 
315
317
  @dataclass(frozen=True)
@@ -12,8 +12,12 @@ class ProxyTokenInfo:
12
12
  token_id: str
13
13
  created_at: datetime.datetime
14
14
  scoped: bool
15
+ name: str
16
+ created_by: str
15
17
 
16
- def __init__(self, token_id: str, created_at: datetime.datetime, scoped: bool) -> None:
18
+ def __init__(
19
+ self, token_id: str, created_at: datetime.datetime, scoped: bool, name: str = "", created_by: str = ""
20
+ ) -> None:
17
21
  """Initialize self. See help(type(self)) for accurate signature."""
18
22
  ...
19
23
 
@@ -395,23 +399,29 @@ class WorkspaceProxyTokenManager:
395
399
  ...
396
400
 
397
401
  class __create_spec(typing_extensions.Protocol):
398
- def __call__(self, /) -> modal.types.TokenData:
402
+ def __call__(self, /, name: str = "") -> modal.types.TokenData:
399
403
  """Create a new proxy token for the Workspace.
400
404
 
405
+ Args:
406
+ name: An optional name to help identify the token.
407
+
401
408
  Examples:
402
409
  ```python notest
403
- token = modal.Workspace.from_context().proxy_tokens.create()
410
+ token = modal.Workspace.from_context().proxy_tokens.create(name="production-webhooks")
404
411
  print(token.token_id, token.token_secret)
405
412
  ```
406
413
  """
407
414
  ...
408
415
 
409
- async def aio(self, /) -> modal.types.TokenData:
416
+ async def aio(self, /, name: str = "") -> modal.types.TokenData:
410
417
  """Create a new proxy token for the Workspace.
411
418
 
419
+ Args:
420
+ name: An optional name to help identify the token.
421
+
412
422
  Examples:
413
423
  ```python notest
414
- token = modal.Workspace.from_context().proxy_tokens.create()
424
+ token = modal.Workspace.from_context().proxy_tokens.create(name="production-webhooks")
415
425
  print(token.token_id, token.token_secret)
416
426
  ```
417
427
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.6.dev17
3
+ Version: 1.5.6.dev19
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -173,7 +173,6 @@ modal/cli/_stats.py
173
173
  modal/cli/_traceback.py
174
174
  modal/cli/app.py
175
175
  modal/cli/billing.py
176
- modal/cli/bootstrap.py
177
176
  modal/cli/changelog.py
178
177
  modal/cli/cluster.py
179
178
  modal/cli/config.py