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.
Files changed (42) hide show
  1. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/PKG-INFO +1 -1
  2. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/sandbox.py +21 -4
  3. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/pyproject.toml +1 -1
  4. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_firecracker_sandbox.py +18 -0
  5. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/.gitignore +0 -0
  6. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/README.md +0 -0
  7. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/__init__.py +0 -0
  8. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/cli.py +0 -0
  9. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/client.py +0 -0
  10. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/code_interpreter/__init__.py +0 -0
  11. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/code_interpreter/_runner.py +0 -0
  12. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/code_interpreter/sandbox.py +0 -0
  13. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/code_interpreter/types.py +0 -0
  14. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/config.py +0 -0
  15. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/errors.py +0 -0
  16. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/__init__.py +0 -0
  17. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/addons.py +0 -0
  18. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/api_keys.py +0 -0
  19. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/billing.py +0 -0
  20. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/client.py +0 -0
  21. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/domains.py +0 -0
  22. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/git.py +0 -0
  23. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/github.py +0 -0
  24. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/metrics.py +0 -0
  25. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/projects.py +0 -0
  26. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/regions.py +0 -0
  27. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/secrets.py +0 -0
  28. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/services.py +0 -0
  29. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/storage.py +0 -0
  30. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/platform/workspaces.py +0 -0
  31. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/project.py +0 -0
  32. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/__init__.py +0 -0
  33. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/desktop.py +0 -0
  34. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/fs.py +0 -0
  35. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/sandbox/process.py +0 -0
  36. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/lizard/volume.py +0 -0
  37. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_cli_parity.py +0 -0
  38. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_contracts.py +0 -0
  39. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_desktop.py +0 -0
  40. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_git.py +0 -0
  41. {lizard_sdk-1.0.2 → lizard_sdk-1.0.4}/tests/test_snapshot_lifecycle.py +0 -0
  42. {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.2
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); commands, files and exposed ports still work. Set at create
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
- if internet_access is False and data.get("internetAccess") is not False:
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 internet_access=False yet; "
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.2"
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