opencomputer-sdk 0.6.4__tar.gz → 0.6.7__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 (39) hide show
  1. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/PKG-INFO +1 -1
  2. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/__init__.py +4 -1
  3. opencomputer_sdk-0.6.7/opencomputer/browser.py +260 -0
  4. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/sandbox.py +16 -9
  5. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/pyproject.toml +1 -1
  6. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/.gitignore +0 -0
  7. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/README.md +0 -0
  8. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/run_all_tests.py +0 -0
  9. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/stream_demo.py +0 -0
  10. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_commands.py +0 -0
  11. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_concurrent.py +0 -0
  12. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_declarative_images.py +0 -0
  13. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_default_template.py +0 -0
  14. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_disk_size.py +0 -0
  15. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_domain_tls.py +0 -0
  16. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_environment.py +0 -0
  17. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_exec.py +0 -0
  18. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_file_ops.py +0 -0
  19. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_large_files.py +0 -0
  20. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_multi_template.py +0 -0
  21. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_python_sdk.py +0 -0
  22. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_reconnect.py +0 -0
  23. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_secret_store_fork.py +0 -0
  24. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_secretstore.py +0 -0
  25. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_shell.py +0 -0
  26. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/examples/test_timeout.py +0 -0
  27. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/agent.py +0 -0
  28. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/commands.py +0 -0
  29. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/exec.py +0 -0
  30. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/filesystem.py +0 -0
  31. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/image.py +0 -0
  32. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/mounts.py +0 -0
  33. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/project.py +0 -0
  34. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/pty.py +0 -0
  35. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/shell.py +0 -0
  36. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/snapshot.py +0 -0
  37. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/sse.py +0 -0
  38. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/template.py +0 -0
  39. {opencomputer_sdk-0.6.4 → opencomputer_sdk-0.6.7}/opencomputer/usage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencomputer-sdk
3
- Version: 0.6.4
3
+ Version: 0.6.7
4
4
  Summary: Python SDK for OpenComputer - cloud sandbox platform
5
5
  Project-URL: Homepage, https://github.com/diggerhq/opensandbox
6
6
  Project-URL: Repository, https://github.com/diggerhq/opensandbox
@@ -1,6 +1,7 @@
1
1
  """OpenComputer Python SDK - cloud sandbox platform."""
2
2
 
3
3
  from opencomputer.sandbox import Sandbox, ScalingLockedError, PlanLimitError, SandboxFamilyLimitError
4
+ from opencomputer.browser import Browser, BrowserProfile
4
5
  from opencomputer.agent import Agent, AgentEvent, AgentSession, AgentSessionInfo
5
6
  from opencomputer.filesystem import Filesystem
6
7
  from opencomputer.exec import Exec, ProcessResult, ExecSession, ExecSessionInfo
@@ -28,6 +29,8 @@ from opencomputer.usage import (
28
29
 
29
30
  __all__ = [
30
31
  "Sandbox",
32
+ "Browser",
33
+ "BrowserProfile",
31
34
  "ScalingLockedError",
32
35
  "PlanLimitError",
33
36
  "SandboxFamilyLimitError",
@@ -65,4 +68,4 @@ __all__ = [
65
68
  "TagKeyInfo",
66
69
  ]
67
70
 
68
- __version__ = "0.6.3"
71
+ __version__ = "0.6.7"
@@ -0,0 +1,260 @@
1
+ """Browser entity for OpenComputer's browser API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from dataclasses import dataclass
7
+ from typing import Any
8
+
9
+ import httpx
10
+
11
+
12
+ DEFAULT_BROWSER_API_URL = "https://browser.opencomputer.dev"
13
+
14
+
15
+ def _resolve_browser_api_url(api_url: str | None) -> str:
16
+ return (api_url or os.environ.get("OPENCOMPUTER_BROWSER_API_URL") or DEFAULT_BROWSER_API_URL).rstrip("/")
17
+
18
+
19
+ @dataclass
20
+ class Browser:
21
+ """Browser session managed through OpenComputer."""
22
+
23
+ id: str
24
+ provider: str
25
+ provider_session_id: str
26
+ status: str
27
+ cdp_ws_url: str
28
+ webdriver_ws_url: str
29
+ live_view_url: str | None = None
30
+ base_url: str | None = None
31
+ headless: bool | None = None
32
+ stealth: bool | None = None
33
+ gpu: bool | None = None
34
+ timeout_seconds: int | None = None
35
+ created_at: str | None = None
36
+ updated_at: str | None = None
37
+ deleted_at: str | None = None
38
+ _api_url: str = ""
39
+ _api_key: str = ""
40
+ _client: httpx.AsyncClient | None = None
41
+
42
+ @classmethod
43
+ async def create(
44
+ cls,
45
+ *,
46
+ api_key: str | None = None,
47
+ api_url: str | None = None,
48
+ name: str | None = None,
49
+ tags: dict[str, str] | None = None,
50
+ stealth: bool | None = None,
51
+ headless: bool | None = None,
52
+ gpu: bool | None = None,
53
+ timeout_seconds: int | None = None,
54
+ profile: dict[str, Any] | None = None,
55
+ extensions: list[dict[str, str]] | None = None,
56
+ proxy_id: str | None = None,
57
+ viewport: dict[str, int] | None = None,
58
+ kiosk_mode: bool | None = None,
59
+ start_url: str | None = None,
60
+ chrome_policy: dict[str, Any] | None = None,
61
+ telemetry: dict[str, Any] | None = None,
62
+ ) -> Browser:
63
+ """Create a browser session and return direct connection URLs."""
64
+ url = _resolve_browser_api_url(api_url)
65
+ key = api_key or os.environ.get("OPENCOMPUTER_API_KEY", "")
66
+ client = httpx.AsyncClient(base_url=url, headers=_headers(key), timeout=30.0)
67
+
68
+ body = _create_body(
69
+ name=name,
70
+ tags=tags,
71
+ stealth=stealth,
72
+ headless=headless,
73
+ gpu=gpu,
74
+ timeout_seconds=timeout_seconds,
75
+ profile=profile,
76
+ extensions=extensions,
77
+ proxy_id=proxy_id,
78
+ viewport=viewport,
79
+ kiosk_mode=kiosk_mode,
80
+ start_url=start_url,
81
+ chrome_policy=chrome_policy,
82
+ telemetry=telemetry,
83
+ )
84
+ resp = await client.post("/v1/browsers", json=body)
85
+ resp.raise_for_status()
86
+ return cls._from_data(resp.json(), url, key, client)
87
+
88
+ @classmethod
89
+ async def connect(
90
+ cls,
91
+ browser_id: str,
92
+ *,
93
+ api_key: str | None = None,
94
+ api_url: str | None = None,
95
+ ) -> Browser:
96
+ """Load an existing browser session by OpenComputer browser ID."""
97
+ url = _resolve_browser_api_url(api_url)
98
+ key = api_key or os.environ.get("OPENCOMPUTER_API_KEY", "")
99
+ client = httpx.AsyncClient(base_url=url, headers=_headers(key), timeout=30.0)
100
+ resp = await client.get(f"/v1/browsers/{browser_id}")
101
+ resp.raise_for_status()
102
+ return cls._from_data(resp.json(), url, key, client)
103
+
104
+ async def delete(self) -> None:
105
+ """Delete the browser session."""
106
+ client = self._client or httpx.AsyncClient(base_url=self._api_url, headers=_headers(self._api_key), timeout=30.0)
107
+ resp = await client.delete(f"/v1/browsers/{self.id}")
108
+ resp.raise_for_status()
109
+
110
+ async def close(self) -> None:
111
+ """Close the SDK HTTP client. This does not delete the browser."""
112
+ if self._client is not None:
113
+ await self._client.aclose()
114
+
115
+ @classmethod
116
+ def _from_data(
117
+ cls,
118
+ data: dict[str, Any],
119
+ api_url: str,
120
+ api_key: str,
121
+ client: httpx.AsyncClient,
122
+ ) -> Browser:
123
+ return cls(
124
+ id=data["id"],
125
+ provider=data.get("provider", "kernel"),
126
+ provider_session_id=data["provider_session_id"],
127
+ status=data.get("status", ""),
128
+ cdp_ws_url=data["cdp_ws_url"],
129
+ webdriver_ws_url=data["webdriver_ws_url"],
130
+ live_view_url=data.get("live_view_url"),
131
+ base_url=data.get("base_url"),
132
+ headless=data.get("headless"),
133
+ stealth=data.get("stealth"),
134
+ gpu=data.get("gpu"),
135
+ timeout_seconds=data.get("timeout_seconds"),
136
+ created_at=data.get("created_at"),
137
+ updated_at=data.get("updated_at"),
138
+ deleted_at=data.get("deleted_at"),
139
+ _api_url=api_url,
140
+ _api_key=api_key,
141
+ _client=client,
142
+ )
143
+
144
+
145
+ @dataclass
146
+ class BrowserProfile:
147
+ """Browser profile metadata managed through OpenComputer."""
148
+
149
+ id: str
150
+ provider: str
151
+ provider_profile_id: str
152
+ name: str | None = None
153
+ created_at: str | None = None
154
+ updated_at: str | None = None
155
+ deleted_at: str | None = None
156
+ provider_created_at: str | None = None
157
+ provider_updated_at: str | None = None
158
+ provider_last_used_at: str | None = None
159
+ _api_url: str = ""
160
+ _api_key: str = ""
161
+ _client: httpx.AsyncClient | None = None
162
+
163
+ @classmethod
164
+ async def create(
165
+ cls,
166
+ *,
167
+ api_key: str | None = None,
168
+ api_url: str | None = None,
169
+ name: str | None = None,
170
+ ) -> BrowserProfile:
171
+ """Create a browser profile."""
172
+ url = _resolve_browser_api_url(api_url)
173
+ key = api_key or os.environ.get("OPENCOMPUTER_API_KEY", "")
174
+ client = httpx.AsyncClient(base_url=url, headers=_headers(key), timeout=30.0)
175
+ body = {"name": name} if name is not None else {}
176
+ resp = await client.post("/v1/profiles", json=body)
177
+ resp.raise_for_status()
178
+ return cls._from_data(resp.json(), url, key, client)
179
+
180
+ @classmethod
181
+ async def list(
182
+ cls,
183
+ *,
184
+ api_key: str | None = None,
185
+ api_url: str | None = None,
186
+ ) -> list[BrowserProfile]:
187
+ """List browser profiles for the authenticated org."""
188
+ url = _resolve_browser_api_url(api_url)
189
+ key = api_key or os.environ.get("OPENCOMPUTER_API_KEY", "")
190
+ client = httpx.AsyncClient(base_url=url, headers=_headers(key), timeout=30.0)
191
+ resp = await client.get("/v1/profiles")
192
+ resp.raise_for_status()
193
+ data = resp.json()
194
+ return [cls._from_data(item, url, key, client) for item in data.get("profiles", [])]
195
+
196
+ @classmethod
197
+ async def connect(
198
+ cls,
199
+ id_or_name: str,
200
+ *,
201
+ api_key: str | None = None,
202
+ api_url: str | None = None,
203
+ ) -> BrowserProfile:
204
+ """Load a browser profile by OpenComputer profile ID or name."""
205
+ url = _resolve_browser_api_url(api_url)
206
+ key = api_key or os.environ.get("OPENCOMPUTER_API_KEY", "")
207
+ client = httpx.AsyncClient(base_url=url, headers=_headers(key), timeout=30.0)
208
+ resp = await client.get(f"/v1/profiles/{id_or_name}")
209
+ resp.raise_for_status()
210
+ return cls._from_data(resp.json(), url, key, client)
211
+
212
+ async def delete(self) -> None:
213
+ """Delete the browser profile."""
214
+ client = self._client or httpx.AsyncClient(base_url=self._api_url, headers=_headers(self._api_key), timeout=30.0)
215
+ resp = await client.delete(f"/v1/profiles/{self.id}")
216
+ resp.raise_for_status()
217
+
218
+ async def close(self) -> None:
219
+ """Close the SDK HTTP client. This does not delete the profile."""
220
+ if self._client is not None:
221
+ await self._client.aclose()
222
+
223
+ @classmethod
224
+ def _from_data(
225
+ cls,
226
+ data: dict[str, Any],
227
+ api_url: str,
228
+ api_key: str,
229
+ client: httpx.AsyncClient,
230
+ ) -> BrowserProfile:
231
+ return cls(
232
+ id=data["id"],
233
+ provider=data.get("provider", "kernel"),
234
+ provider_profile_id=data["provider_profile_id"],
235
+ name=data.get("name"),
236
+ created_at=data.get("created_at"),
237
+ updated_at=data.get("updated_at"),
238
+ deleted_at=data.get("deleted_at"),
239
+ provider_created_at=data.get("provider_created_at"),
240
+ provider_updated_at=data.get("provider_updated_at"),
241
+ provider_last_used_at=data.get("provider_last_used_at"),
242
+ _api_url=api_url,
243
+ _api_key=api_key,
244
+ _client=client,
245
+ )
246
+
247
+
248
+ def _headers(api_key: str) -> dict[str, str]:
249
+ headers = {"Content-Type": "application/json"}
250
+ if api_key:
251
+ headers["X-API-Key"] = api_key
252
+ return headers
253
+
254
+
255
+ def _create_body(**kwargs: Any) -> dict[str, Any]:
256
+ body: dict[str, Any] = {}
257
+ for key, value in kwargs.items():
258
+ if value is not None:
259
+ body[key] = value
260
+ return body
@@ -288,9 +288,16 @@ class Sandbox:
288
288
  which handles readiness waiting and proxies to workers."""
289
289
  return self._client
290
290
 
291
- async def kill(self) -> None:
292
- """Kill and remove the sandbox."""
293
- resp = await self._client.delete(f"/sandboxes/{self.sandbox_id}")
291
+ async def kill(self, delete_secret_store: bool = False) -> None:
292
+ """Kill and remove the sandbox.
293
+
294
+ Args:
295
+ delete_secret_store: Also delete the secret store currently attached
296
+ to this sandbox. Defaults to ``False``. Only use this for
297
+ single-use stores that should not outlive the sandbox.
298
+ """
299
+ params = {"deleteSecretStore": "true"} if delete_secret_store else None
300
+ resp = await self._client.delete(f"/sandboxes/{self.sandbox_id}", params=params)
294
301
  resp.raise_for_status()
295
302
  self.status = "stopped"
296
303
 
@@ -606,7 +613,7 @@ class Sandbox:
606
613
  self,
607
614
  name: str,
608
615
  kind: str | None = None,
609
- promote_to_full: bool = False,
616
+ promote_to_full: bool | None = None,
610
617
  retention_policy: dict | None = None,
611
618
  ) -> dict:
612
619
  """Create a named checkpoint of the running sandbox.
@@ -615,9 +622,9 @@ class Sandbox:
615
622
  name: A unique name for this checkpoint.
616
623
  kind: Optional checkpoint kind. Use "full" for disk, memory, and
617
624
  device state, or "disk_only" for a disk-only checkpoint that
618
- restores with a cold boot.
619
- promote_to_full: For disk-only checkpoints, asynchronously create
620
- a derived full checkpoint so future forks can use warm restore.
625
+ automatically prepares a full artifact for faster future forks.
626
+ promote_to_full: Advanced override for disk-only checkpoints.
627
+ Defaults to server behavior.
621
628
  retention_policy: Optional policy such as
622
629
  {"mode": "delete_oldest", "maxCount": 10}. When set, the
623
630
  server may delete older eligible checkpoints before creating
@@ -629,8 +636,8 @@ class Sandbox:
629
636
  body = {"name": name}
630
637
  if kind is not None:
631
638
  body["kind"] = kind
632
- if promote_to_full:
633
- body["promoteToFull"] = True
639
+ if promote_to_full is not None:
640
+ body["promoteToFull"] = promote_to_full
634
641
  if retention_policy is not None:
635
642
  body["retentionPolicy"] = retention_policy
636
643
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "opencomputer-sdk"
7
- version = "0.6.4"
7
+ version = "0.6.7"
8
8
  description = "Python SDK for OpenComputer - cloud sandbox platform"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"