voidrun 0.0.7__tar.gz → 0.1.0__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 (85) hide show
  1. {voidrun-0.0.7 → voidrun-0.1.0}/PKG-INFO +4 -2
  2. {voidrun-0.0.7 → voidrun-0.1.0}/README.md +3 -1
  3. {voidrun-0.0.7 → voidrun-0.1.0}/pyproject.toml +1 -1
  4. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/__init__.py +2 -1
  5. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api/execution_api.py +3 -3
  6. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/create_sandbox_request.py +1 -3
  7. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/exec_request.py +4 -6
  8. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/sandbox.py +7 -13
  9. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/client.py +0 -8
  10. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/sandbox.py +78 -2
  11. {voidrun-0.0.7 → voidrun-0.1.0}/.gitignore +0 -0
  12. {voidrun-0.0.7 → voidrun-0.1.0}/scripts/run_all_examples.sh +0 -0
  13. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/__init__.py +0 -0
  14. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api/__init__.py +0 -0
  15. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api/file_system_api.py +0 -0
  16. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api/images_api.py +0 -0
  17. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api/organizations_api.py +0 -0
  18. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api/sandboxes_api.py +0 -0
  19. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api/system_api.py +0 -0
  20. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api/users_api.py +0 -0
  21. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api_client.py +0 -0
  22. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/api_response.py +0 -0
  23. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/configuration.py +0 -0
  24. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/exceptions.py +0 -0
  25. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/__init__.py +0 -0
  26. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/activate_api_key_request.py +0 -0
  27. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/api_key_response.py +0 -0
  28. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/api_response_sandbox.py +0 -0
  29. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/api_response_sandboxes_list.py +0 -0
  30. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/api_response_sandboxes_list_meta.py +0 -0
  31. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/command_kill_response.py +0 -0
  32. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/command_list_response.py +0 -0
  33. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/command_run_response.py +0 -0
  34. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/command_wait_response.py +0 -0
  35. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/compress_request.py +0 -0
  36. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/create_file_request.py +0 -0
  37. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/create_image_request.py +0 -0
  38. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/create_pty_session200_response.py +0 -0
  39. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/create_pty_session200_response_all_of_data.py +0 -0
  40. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/create_pty_session_request.py +0 -0
  41. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/create_sandbox201_response.py +0 -0
  42. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/disk_usage.py +0 -0
  43. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/error_response.py +0 -0
  44. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/exec_response.py +0 -0
  45. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/exec_response_data.py +0 -0
  46. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/execute_in_session_request.py +0 -0
  47. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/extract_request.py +0 -0
  48. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/file_event.py +0 -0
  49. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/file_info.py +0 -0
  50. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/file_stats.py +0 -0
  51. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/generate_api_key_request.py +0 -0
  52. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/generated_api_key_response.py +0 -0
  53. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/get_current_user200_response.py +0 -0
  54. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/get_current_user200_response_orgs_inner.py +0 -0
  55. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/get_org_users200_response.py +0 -0
  56. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/get_org_users200_response_all_of_data_inner.py +0 -0
  57. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/get_pty_buffer200_response.py +0 -0
  58. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/get_pty_buffer200_response_all_of_data.py +0 -0
  59. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/get_version200_response.py +0 -0
  60. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/image.py +0 -0
  61. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/kill_background_process_request.py +0 -0
  62. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/list_files200_response.py +0 -0
  63. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/list_files200_response_data.py +0 -0
  64. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/list_pty_sessions200_response.py +0 -0
  65. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/list_pty_sessions200_response_all_of_data.py +0 -0
  66. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/organization.py +0 -0
  67. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/process_info.py +0 -0
  68. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/pty_session_info.py +0 -0
  69. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/resize_terminal_request.py +0 -0
  70. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/run_background_command_request.py +0 -0
  71. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/session_exec_request.py +0 -0
  72. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/session_exec_response.py +0 -0
  73. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/session_exec_stream_request.py +0 -0
  74. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/start_watch200_response.py +0 -0
  75. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/start_watch200_response_data.py +0 -0
  76. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/start_watch_request.py +0 -0
  77. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/models/success_response.py +0 -0
  78. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/py.typed +0 -0
  79. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/api_client/rest.py +0 -0
  80. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/commands.py +0 -0
  81. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/constants.py +0 -0
  82. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/fs.py +0 -0
  83. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/interpreter.py +0 -0
  84. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/pty.py +0 -0
  85. {voidrun-0.0.7 → voidrun-0.1.0}/voidrun/response.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voidrun
3
- Version: 0.0.7
3
+ Version: 0.1.0
4
4
  Summary: Python SDK for VoidRun AI Sandbox
5
5
  Project-URL: Homepage, https://voidrun.io
6
6
  Project-URL: Documentation, https://docs.voidrun.io
@@ -56,7 +56,7 @@ The high-level API is aligned with the official **TypeScript SDK** (`create_sand
56
56
  ## Features
57
57
 
58
58
  - **Sandbox lifecycle**: Create, list, fetch, start/stop/pause/resume, and remove sandboxes (sync and async).
59
- - **Shell execution**: `exec` with optional `ExecRequest`; SSE streaming via `exec_stream`.
59
+ - **Shell execution**: synchronous `exec` with optional `ExecRequest` (no `background` flag); SSE streaming via `exec_stream`; detached processes via `.commands.run`.
60
60
  - **Code interpreter**: `run_code` / `interpreter.run` return a structured **`CodeExecutionResult`** (stdout, stderr, success, exit_code, results, logs).
61
61
  - **Files**: Create, upload, download, list, move, copy, compress, extract, permissions, search, disk usage.
62
62
  - **File watching**: WebSocket-backed directory watches (async).
@@ -244,6 +244,8 @@ Return type: **`VoidRunResponse[ExecResponseData]`**: the API’s outer **`ExecR
244
244
  print(r.data.stdout, r.data.stderr, r.data.exit_code)
245
245
  ```
246
246
 
247
+ **`sandbox.exec` is synchronous only** — it waits for the command. Do **not** use a `background` flag on `ExecRequest`; for a PID and long-running processes, use **`sandbox.commands.run`** ([Background commands](#background-commands)).
248
+
247
249
  ### Streaming (`exec_stream`)
248
250
 
249
251
  Provide callbacks for SSE events (`on_stdout`, `on_stderr`, `on_exit`, `on_error`).
@@ -32,7 +32,7 @@ The high-level API is aligned with the official **TypeScript SDK** (`create_sand
32
32
  ## Features
33
33
 
34
34
  - **Sandbox lifecycle**: Create, list, fetch, start/stop/pause/resume, and remove sandboxes (sync and async).
35
- - **Shell execution**: `exec` with optional `ExecRequest`; SSE streaming via `exec_stream`.
35
+ - **Shell execution**: synchronous `exec` with optional `ExecRequest` (no `background` flag); SSE streaming via `exec_stream`; detached processes via `.commands.run`.
36
36
  - **Code interpreter**: `run_code` / `interpreter.run` return a structured **`CodeExecutionResult`** (stdout, stderr, success, exit_code, results, logs).
37
37
  - **Files**: Create, upload, download, list, move, copy, compress, extract, permissions, search, disk usage.
38
38
  - **File watching**: WebSocket-backed directory watches (async).
@@ -220,6 +220,8 @@ Return type: **`VoidRunResponse[ExecResponseData]`**: the API’s outer **`ExecR
220
220
  print(r.data.stdout, r.data.stderr, r.data.exit_code)
221
221
  ```
222
222
 
223
+ **`sandbox.exec` is synchronous only** — it waits for the command. Do **not** use a `background` flag on `ExecRequest`; for a PID and long-running processes, use **`sandbox.commands.run`** ([Background commands](#background-commands)).
224
+
223
225
  ### Streaming (`exec_stream`)
224
226
 
225
227
  Provide callbacks for SSE events (`on_stdout`, `on_stderr`, `on_exit`, `on_error`).
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "voidrun"
7
- version = "0.0.7"
7
+ version = "0.1.0"
8
8
  description = "Python SDK for VoidRun AI Sandbox"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -11,7 +11,7 @@ from .constants import (
11
11
  DEFAULT_SANDBOX_MEM,
12
12
  )
13
13
  from .interpreter import CodeExecutionResult, CodeInterpreter, Interpreter
14
- from .sandbox import Sandbox
14
+ from .sandbox import Sandbox, SandboxPublicURL
15
15
 
16
16
  __all__ = [
17
17
  "AsyncVoidRun",
@@ -25,5 +25,6 @@ __all__ = [
25
25
  "ListSandboxesMeta",
26
26
  "ListSandboxesResult",
27
27
  "Sandbox",
28
+ "SandboxPublicURL",
28
29
  "VoidRun",
29
30
  ]
@@ -1500,7 +1500,7 @@ class ExecutionApi:
1500
1500
  ) -> ExecResponse:
1501
1501
  """Execute command (synchronous)
1502
1502
 
1503
- Execute a command in the sandbox and wait for the result
1503
+ Execute a command in the sandbox and wait for the result. For a non-blocking start with a PID, use `POST /sandboxes/{id}/commands/run`.
1504
1504
 
1505
1505
  :param id: (required)
1506
1506
  :type id: str
@@ -1574,7 +1574,7 @@ class ExecutionApi:
1574
1574
  ) -> ApiResponse[ExecResponse]:
1575
1575
  """Execute command (synchronous)
1576
1576
 
1577
- Execute a command in the sandbox and wait for the result
1577
+ Execute a command in the sandbox and wait for the result. For a non-blocking start with a PID, use `POST /sandboxes/{id}/commands/run`.
1578
1578
 
1579
1579
  :param id: (required)
1580
1580
  :type id: str
@@ -1648,7 +1648,7 @@ class ExecutionApi:
1648
1648
  ) -> RESTResponseType:
1649
1649
  """Execute command (synchronous)
1650
1650
 
1651
- Execute a command in the sandbox and wait for the result
1651
+ Execute a command in the sandbox and wait for the result. For a non-blocking start with a PID, use `POST /sandboxes/{id}/commands/run`.
1652
1652
 
1653
1653
  :param id: (required)
1654
1654
  :type id: str
@@ -37,8 +37,7 @@ class CreateSandboxRequest(BaseModel):
37
37
  env_vars: Optional[Dict[str, StrictStr]] = Field(default=None, description="Environment variables to set on the sandbox", alias="envVars")
38
38
  auto_sleep: Optional[StrictBool] = Field(default=None, description="If true, the sandbox will be auto-paused due to inactivity", alias="autoSleep")
39
39
  region: Optional[StrictStr] = Field(default=None, description="Target region for the sandbox")
40
- ref_id: Optional[StrictStr] = Field(default=None, description="External reference ID for tracking", alias="refId")
41
- __properties: ClassVar[List[str]] = ["name", "image", "cpu", "mem", "orgId", "userId", "sync", "envVars", "autoSleep", "region", "refId"]
40
+ __properties: ClassVar[List[str]] = ["name", "image", "cpu", "mem", "orgId", "userId", "sync", "envVars", "autoSleep", "region"]
42
41
 
43
42
  @field_validator('name')
44
43
  def name_validate_regular_expression(cls, value):
@@ -108,7 +107,6 @@ class CreateSandboxRequest(BaseModel):
108
107
  "envVars": obj.get("envVars"),
109
108
  "autoSleep": obj.get("autoSleep"),
110
109
  "region": obj.get("region"),
111
- "refId": obj.get("refId")
112
110
  })
113
111
  return _obj
114
112
 
@@ -17,7 +17,7 @@ import pprint
17
17
  import re # noqa: F401
18
18
  import json
19
19
 
20
- from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictInt, StrictStr
20
+ from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr
21
21
  from typing import Any, ClassVar, Dict, List, Optional
22
22
  from typing import Optional, Set
23
23
  from typing_extensions import Self
@@ -28,11 +28,10 @@ class ExecRequest(BaseModel):
28
28
  """ # noqa: E501
29
29
  command: StrictStr
30
30
  args: Optional[List[StrictStr]] = None
31
- timeout: Optional[StrictInt] = Field(default=30, description="Timeout in seconds (ignored if background=true)")
31
+ timeout: Optional[StrictInt] = Field(default=30, description="Timeout in seconds")
32
32
  env: Optional[Dict[str, StrictStr]] = None
33
33
  cwd: Optional[StrictStr] = None
34
- background: Optional[StrictBool] = Field(default=False, description="If true, starts process in background and returns PID immediately")
35
- __properties: ClassVar[List[str]] = ["command", "args", "timeout", "env", "cwd", "background"]
34
+ __properties: ClassVar[List[str]] = ["command", "args", "timeout", "env", "cwd"]
36
35
 
37
36
  model_config = ConfigDict(
38
37
  populate_by_name=True,
@@ -89,8 +88,7 @@ class ExecRequest(BaseModel):
89
88
  "args": obj.get("args"),
90
89
  "timeout": obj.get("timeout") if obj.get("timeout") is not None else 30,
91
90
  "env": obj.get("env"),
92
- "cwd": obj.get("cwd"),
93
- "background": obj.get("background") if obj.get("background") is not None else False
91
+ "cwd": obj.get("cwd")
94
92
  })
95
93
  return _obj
96
94
 
@@ -33,30 +33,24 @@ class Sandbox(BaseModel):
33
33
  cpu: Optional[StrictInt] = Field(default=None, description="Number of vCPUs allocated")
34
34
  mem: Optional[StrictInt] = Field(default=None, description="Memory in MiB")
35
35
  disk_mb: Optional[StrictInt] = Field(default=None, description="Disk size in MiB", alias="diskMB")
36
- status: Optional[StrictStr] = None
36
+ status: Optional[StrictStr] = Field(default=None, description="Lifecycle state. Terminal states `killed` and `deleted` may still appear in list responses for historical or cleanup rows.")
37
37
  created_at: Optional[datetime] = Field(default=None, alias="createdAt")
38
38
  created_by: Optional[StrictStr] = Field(default=None, description="User ID who created the sandbox", alias="createdBy")
39
39
  org_id: Optional[StrictStr] = Field(default=None, description="Organization ID the sandbox belongs to", alias="orgId")
40
40
  env_vars: Optional[Dict[str, StrictStr]] = Field(default=None, description="Environment variables set on the sandbox (optional, only present if configured)", alias="envVars")
41
41
  auto_sleep: Optional[StrictBool] = Field(default=None, description="Indicates if auto-sleep is enabled", alias="autoSleep")
42
42
  region: Optional[StrictStr] = Field(default=None, description="Region where the sandbox is hosted")
43
- ref_id: Optional[StrictStr] = Field(default=None, description="External reference ID", alias="refId")
44
- __properties: ClassVar[List[str]] = ["id", "name", "image", "cpu", "mem", "diskMB", "status", "createdAt", "createdBy", "orgId", "envVars", "autoSleep", "region", "refId"]
43
+ node_id: Optional[StrictStr] = Field(default=None, description="Voidrun host that created this sandbox", alias="nodeId")
44
+ __properties: ClassVar[List[str]] = ["id", "name", "image", "cpu", "mem", "diskMB", "status", "createdAt", "createdBy", "orgId", "envVars", "autoSleep", "region", "nodeId"]
45
45
 
46
46
  @field_validator('status')
47
47
  def status_validate_enum(cls, value):
48
- """Validates the enum; must match voidrun/openapi.yml Sandbox.status."""
48
+ """Validates the enum"""
49
49
  if value is None:
50
50
  return value
51
51
 
52
- allowed = frozenset(
53
- ('running', 'stopped', 'paused', 'error', 'killed', 'deleted'),
54
- )
55
- if value not in allowed:
56
- raise ValueError(
57
- "must be one of enum values "
58
- "('running', 'stopped', 'paused', 'error', 'killed', 'deleted')",
59
- )
52
+ if value not in set(['running', 'stopped', 'paused', 'error', 'killed', 'deleted']):
53
+ raise ValueError("must be one of enum values ('running', 'stopped', 'paused', 'error', 'killed', 'deleted')")
60
54
  return value
61
55
 
62
56
  model_config = ConfigDict(
@@ -123,7 +117,7 @@ class Sandbox(BaseModel):
123
117
  "envVars": obj.get("envVars"),
124
118
  "autoSleep": obj.get("autoSleep"),
125
119
  "region": obj.get("region"),
126
- "refId": obj.get("refId")
120
+ "nodeId": obj.get("nodeId")
127
121
  })
128
122
  return _obj
129
123
 
@@ -55,7 +55,6 @@ def _build_create_sandbox_request(
55
55
  env_vars: Optional[Dict[str, str]],
56
56
  auto_sleep: Optional[bool],
57
57
  region: Optional[str],
58
- ref_id: Optional[str],
59
58
  ) -> CreateSandboxRequest:
60
59
  default_name = f"sdbx-{int(time.time() * 1000)}"
61
60
  org_clean = (org_id or "").strip() or None
@@ -70,7 +69,6 @@ def _build_create_sandbox_request(
70
69
  env_vars=env_vars,
71
70
  auto_sleep=auto_sleep,
72
71
  region=region,
73
- ref_id=ref_id,
74
72
  )
75
73
 
76
74
 
@@ -118,8 +116,6 @@ class VoidRun:
118
116
  auto_sleep: Optional[bool] = None,
119
117
  autoSleep: Optional[bool] = None,
120
118
  region: Optional[str] = None,
121
- ref_id: Optional[str] = None,
122
- refId: Optional[str] = None,
123
119
  _owner: Optional[Any] = None,
124
120
  ) -> Sandbox:
125
121
  """_owner: internal — AsyncVoidRun passes itself so Sandbox uses the async client."""
@@ -133,7 +129,6 @@ class VoidRun:
133
129
  oid = (oid or "").strip() or None
134
130
  ev = env_vars if env_vars is not None else envVars
135
131
  asl = auto_sleep if auto_sleep is not None else autoSleep
136
- rid = ref_id if ref_id is not None else refId
137
132
  uid = user_id if user_id is not None else userId
138
133
 
139
134
  req = _build_create_sandbox_request(
@@ -147,7 +142,6 @@ class VoidRun:
147
142
  env_vars=ev,
148
143
  auto_sleep=asl,
149
144
  region=region,
150
- ref_id=rid,
151
145
  )
152
146
  response = self._sandboxes_api.create_sandbox_with_http_info(
153
147
  create_sandbox_request=req,
@@ -237,7 +231,6 @@ class SandboxesFacade:
237
231
  env_vars=kwargs.get("env_vars") or kwargs.get("envVars"),
238
232
  auto_sleep=kwargs.get("auto_sleep") or kwargs.get("autoSleep"),
239
233
  region=kwargs.get("region"),
240
- ref_id=kwargs.get("ref_id") or kwargs.get("refId"),
241
234
  )
242
235
  response = self._api.create_sandbox_with_http_info(
243
236
  create_sandbox_request=req,
@@ -372,7 +365,6 @@ class AsyncSandboxesFacade:
372
365
  env_vars=kwargs.get("env_vars") or kwargs.get("envVars"),
373
366
  auto_sleep=kwargs.get("auto_sleep") or kwargs.get("autoSleep"),
374
367
  region=kwargs.get("region"),
375
- ref_id=kwargs.get("ref_id") or kwargs.get("refId"),
376
368
  )
377
369
  response = await self._client._run_async(
378
370
  self._api.create_sandbox_with_http_info,
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
- from typing import Any, Optional, Union
4
+ from dataclasses import dataclass
5
+ from typing import Any, List, Optional, Union
5
6
 
6
7
  import httpx
7
8
 
@@ -16,6 +17,14 @@ from .pty import PTY
16
17
  from .response import VoidRunResponse
17
18
 
18
19
 
20
+ @dataclass
21
+ class SandboxPublicURL:
22
+ """One published sandbox port and its public URL."""
23
+
24
+ port: int
25
+ url: str
26
+
27
+
19
28
  class Sandbox:
20
29
  def __init__(self, client: Any, model: SandboxModel):
21
30
  self._client = client
@@ -36,7 +45,7 @@ class Sandbox:
36
45
  self.created_at = model.created_at
37
46
  self.created_by = model.created_by
38
47
  self.region = model.region
39
- self.ref_id = model.ref_id
48
+ self.node_id = model.node_id
40
49
  self.auto_sleep = model.auto_sleep
41
50
  self.image = model.image
42
51
  self.disk_mb = model.disk_mb
@@ -170,6 +179,9 @@ class Sandbox:
170
179
  ) -> VoidRunResponse[ExecResponseData]:
171
180
  """Exec: pass a string command or an `ExecRequest` (ts-sdk style).
172
181
 
182
+ Synchronous only (waits for the command). There is no ``background`` field on ``ExecRequest``;
183
+ use ``sandbox.commands.run`` for detached processes and PIDs.
184
+
173
185
  Returns ``VoidRunResponse`` whose ``.data`` is ``ExecResponseData`` (stdout/stderr/exit_code),
174
186
  not the outer ``ExecResponse`` envelope, so use ``result.data.stdout`` not ``result.data.data.stdout``.
175
187
  """
@@ -267,3 +279,67 @@ class Sandbox:
267
279
  timeout=timeout,
268
280
  **kwargs,
269
281
  )
282
+
283
+ def _public_urls_request(self) -> tuple[str, dict]:
284
+ """URL + auth headers for the public-urls endpoint (shared by sync/async)."""
285
+ cfg = self._exec_api.api_client.configuration
286
+ url = f"{cfg.host.rstrip('/')}/sandboxes/{self.id}/public-urls"
287
+ headers = {"Accept": "application/json"}
288
+ api_key = cfg.get_api_key_with_prefix("ApiKeyAuth")
289
+ if api_key:
290
+ headers["X-API-Key"] = api_key
291
+ return url, headers
292
+
293
+ def get_public_urls(self, timeout: float = 15.0) -> List[SandboxPublicURL]:
294
+ """Public URLs for every port this sandbox publishes. Raises on HTTP >= 400."""
295
+ url, headers = self._public_urls_request()
296
+ with httpx.Client(timeout=timeout) as client:
297
+ resp = client.get(url, headers=headers)
298
+ resp.raise_for_status()
299
+ return _parse_public_urls(resp.json())
300
+
301
+ async def get_public_urls_async(
302
+ self, timeout: float = 15.0
303
+ ) -> List[SandboxPublicURL]:
304
+ url, headers = self._public_urls_request()
305
+ async with httpx.AsyncClient(timeout=timeout) as client:
306
+ resp = await client.get(url, headers=headers)
307
+ resp.raise_for_status()
308
+ return _parse_public_urls(resp.json())
309
+
310
+ def get_public_url(self, port: int, timeout: float = 15.0) -> Optional[SandboxPublicURL]:
311
+ """Public URL for one published port, or ``None`` if not published."""
312
+ for entry in self.get_public_urls(timeout=timeout):
313
+ if entry.port == port:
314
+ return entry
315
+ return None
316
+
317
+ async def get_public_url_async(
318
+ self, port: int, timeout: float = 15.0
319
+ ) -> Optional[SandboxPublicURL]:
320
+ for entry in await self.get_public_urls_async(timeout=timeout):
321
+ if entry.port == port:
322
+ return entry
323
+ return None
324
+
325
+
326
+ def _parse_public_urls(body: Any) -> List[SandboxPublicURL]:
327
+ data = body.get("data") if isinstance(body, dict) else None
328
+ if not isinstance(data, list):
329
+ return []
330
+ out: List[SandboxPublicURL] = []
331
+ for item in data:
332
+ if not isinstance(item, dict):
333
+ continue
334
+ raw_port = item.get("port")
335
+ try:
336
+ port = int(raw_port) if isinstance(raw_port, (int, str)) else -1
337
+ except (TypeError, ValueError):
338
+ continue
339
+ if not (1 <= port <= 65535):
340
+ continue
341
+ url = item.get("url")
342
+ if not isinstance(url, str) or not url:
343
+ continue
344
+ out.append(SandboxPublicURL(port=port, url=url))
345
+ return out
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes