lizard-sdk 1.0.2__tar.gz → 1.0.4__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.
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/PKG-INFO +1 -1
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/sandbox.py +21 -4
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/pyproject.toml +1 -1
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_firecracker_sandbox.py +18 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/.gitignore +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/README.md +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/__init__.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/cli.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/client.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/code_interpreter/__init__.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/code_interpreter/_runner.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/code_interpreter/sandbox.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/code_interpreter/types.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/config.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/errors.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/__init__.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/addons.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/api_keys.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/billing.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/client.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/domains.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/git.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/github.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/metrics.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/projects.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/regions.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/secrets.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/services.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/storage.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/workspaces.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/project.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/__init__.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/desktop.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/fs.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/process.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/volume.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_cli_parity.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_contracts.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_desktop.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_git.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_snapshot_lifecycle.py +0 -0
- {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_transport.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: lizard-sdk
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: Python SDK for Lizard (lizard.build): cloud sandboxes for AI agents and code execution.
|
|
5
5
|
Project-URL: Homepage, https://lizard.build
|
|
6
6
|
Project-URL: Documentation, https://lizard.build/docs
|
|
@@ -70,6 +70,8 @@ class SandboxInfo:
|
|
|
70
70
|
price_per_hour: float | None = None
|
|
71
71
|
#: False when the sandbox was created with ``internet_access=False``; None from older servers.
|
|
72
72
|
internet_access: bool | None = None
|
|
73
|
+
#: With no internet: what is still reachable (``allowed_hosts`` at create).
|
|
74
|
+
allowed_hosts: list[str] | None = None
|
|
73
75
|
cpus: int | None = None
|
|
74
76
|
memory_mb: int | None = None
|
|
75
77
|
metadata: dict[str, str] | None = None
|
|
@@ -95,6 +97,7 @@ def _to_sandbox_info(s: dict) -> SandboxInfo:
|
|
|
95
97
|
size=s.get("size"),
|
|
96
98
|
price_per_hour=s.get("pricePerHour"),
|
|
97
99
|
internet_access=s.get("internetAccess"),
|
|
100
|
+
allowed_hosts=s.get("allowedHosts"),
|
|
98
101
|
cpus=s.get("cpus"),
|
|
99
102
|
memory_mb=s.get("memoryMb"),
|
|
100
103
|
metadata=s.get("metadata"),
|
|
@@ -200,6 +203,7 @@ class Sandbox:
|
|
|
200
203
|
volume_name: str | None = None,
|
|
201
204
|
lizard_token: str | None = None,
|
|
202
205
|
internet_access: bool = True,
|
|
206
|
+
allowed_hosts: list[str] | None = None,
|
|
203
207
|
) -> "Sandbox":
|
|
204
208
|
"""
|
|
205
209
|
Boot a new Lizard sandbox from the specified template.
|
|
@@ -256,8 +260,15 @@ class Sandbox:
|
|
|
256
260
|
key that escapes is bounded to that one workspace.
|
|
257
261
|
:param internet_access: ``False``: no internet for the sandbox's whole
|
|
258
262
|
life, its resumes and forks included (no outbound TCP, UDP, DNS or
|
|
259
|
-
ping)
|
|
263
|
+
ping) and no public ports (``expose_port`` is refused); commands
|
|
264
|
+
and files still work. Set at create
|
|
260
265
|
only. Not available with a container volume or snapshot.
|
|
266
|
+
:param allowed_hosts: With no internet, these stay reachable: a host
|
|
267
|
+
name (``"pypi.org"``, or ``"*.pythonhosted.org"`` for its subdomains)
|
|
268
|
+
on ports 80 and 443, or an IPv4 address or range on every port.
|
|
269
|
+
Implies ``internet_access=False``. Up to 50. With a host name in the
|
|
270
|
+
list DNS (8.8.8.8) is open too so names resolve; a list of addresses
|
|
271
|
+
only keeps DNS closed.
|
|
261
272
|
|
|
262
273
|
Example::
|
|
263
274
|
|
|
@@ -301,8 +312,10 @@ class Sandbox:
|
|
|
301
312
|
if lizard_token:
|
|
302
313
|
body["lizardToken"] = lizard_token
|
|
303
314
|
# Sent only when asked: every other create stays what it was.
|
|
304
|
-
if internet_access is False:
|
|
315
|
+
if internet_access is False or allowed_hosts:
|
|
305
316
|
body["internetAccess"] = False
|
|
317
|
+
if allowed_hosts:
|
|
318
|
+
body["allowedHosts"] = list(allowed_hosts)
|
|
306
319
|
|
|
307
320
|
res = httpx.post(
|
|
308
321
|
f"{config.api_url}/api/sandboxes",
|
|
@@ -327,14 +340,18 @@ class Sandbox:
|
|
|
327
340
|
data = res.json()
|
|
328
341
|
# A server that predates the option ignores it and starts a sandbox WITH internet:
|
|
329
342
|
# asked for none, anything but an explicit false is deleted, never handed over.
|
|
330
|
-
|
|
343
|
+
# An older server ignores allowed_hosts: that sandbox would have no internet at all.
|
|
344
|
+
unsupported = bool(allowed_hosts) and not isinstance(data.get("allowedHosts"), list)
|
|
345
|
+
if unsupported or ((internet_access is False or allowed_hosts) and data.get("internetAccess") is not False):
|
|
331
346
|
try:
|
|
332
347
|
httpx.delete(f"{config.api_url}/api/sandboxes/{data['sandboxId']}",
|
|
333
348
|
headers=config.headers, timeout=HTTP_TIMEOUT_S)
|
|
334
349
|
except Exception:
|
|
335
350
|
pass
|
|
336
351
|
from ..errors import LizardError
|
|
337
|
-
raise LizardError("This server does not support
|
|
352
|
+
raise LizardError("This server does not support allowed_hosts yet; the sandbox it started was deleted"
|
|
353
|
+
if unsupported else
|
|
354
|
+
"This server does not support internet_access=False yet; "
|
|
338
355
|
"the sandbox it started had internet and was deleted")
|
|
339
356
|
return cls(
|
|
340
357
|
data["sandboxId"],
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lizard-sdk"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.4"
|
|
8
8
|
description = "Python SDK for Lizard (lizard.build): cloud sandboxes for AI agents and code execution."
|
|
9
9
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
@@ -178,3 +178,21 @@ def test_internet_access_false_ignored_by_the_server_deletes_the_sandbox(monkeyp
|
|
|
178
178
|
with pytest.raises(LizardError):
|
|
179
179
|
Sandbox.create("base", project_id="p1", internet_access=False, **CONFIG)
|
|
180
180
|
assert deleted == ["https://test.invalid/api/sandboxes/s9"]
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_allowed_hosts_sent_with_no_internet(monkeypatch):
|
|
184
|
+
sent = []
|
|
185
|
+
monkeypatch.setattr(httpx, "post", lambda url, **kw: sent.append(kw["json"]) or httpx.Response(
|
|
186
|
+
201, json={"sandboxId": "s", "internetAccess": False, "allowedHosts": ["pypi.org"]}))
|
|
187
|
+
Sandbox.create("base", project_id="p1", allowed_hosts=["pypi.org"], **CONFIG)
|
|
188
|
+
assert sent[0]["internetAccess"] is False
|
|
189
|
+
assert sent[0]["allowedHosts"] == ["pypi.org"]
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_allowed_hosts_ignored_by_the_server_deletes_the_sandbox(monkeypatch):
|
|
193
|
+
deleted = []
|
|
194
|
+
monkeypatch.setattr(httpx, "post", lambda url, **kw: httpx.Response(201, json={"sandboxId": "s8", "internetAccess": False}))
|
|
195
|
+
monkeypatch.setattr(httpx, "delete", lambda url, **kw: deleted.append(url) or httpx.Response(200, json={}))
|
|
196
|
+
with pytest.raises(LizardError, match="allowed_hosts"):
|
|
197
|
+
Sandbox.create("base", project_id="p1", allowed_hosts=["pypi.org"], **CONFIG)
|
|
198
|
+
assert deleted == ["https://test.invalid/api/sandboxes/s8"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|