SurfSense/surfsense_backend/scripts/create_sandbox_snapshot.py
2026-04-14 01:43:30 -07:00

105 lines
2.9 KiB
Python

"""Create the Daytona snapshot used by SurfSense code-execution sandboxes.
Run from the backend directory:
cd surfsense_backend
uv run python scripts/create_sandbox_snapshot.py
Prerequisites:
- DAYTONA_API_KEY set in surfsense_backend/.env (or exported in shell)
- DAYTONA_API_URL=https://app.daytona.io/api
- DAYTONA_TARGET=us (or eu)
After this script succeeds, add to surfsense_backend/.env:
DAYTONA_SNAPSHOT_ID=surfsense-sandbox
"""
import os
import sys
import time
from pathlib import Path
from dotenv import load_dotenv
_here = Path(__file__).parent
for candidate in [
_here / "../surfsense_backend/.env",
_here / ".env",
_here / "../.env",
]:
if candidate.exists():
load_dotenv(candidate)
break
from daytona import CreateSnapshotParams, Daytona, Image # noqa: E402
SNAPSHOT_NAME = "surfsense-sandbox"
PACKAGES = [
"pandas",
"numpy",
"matplotlib",
"scipy",
"scikit-learn",
]
def build_image() -> Image:
"""Build the sandbox image with data-science packages and a /documents symlink."""
return (
Image.debian_slim("3.12")
.pip_install(*PACKAGES)
# Symlink /documents → /home/daytona/documents so the LLM can use
# the same /documents/ path it sees in the virtual filesystem.
.run_commands(
"mkdir -p /home/daytona/documents",
"ln -sfn /home/daytona/documents /documents",
)
)
def main() -> None:
api_key = os.environ.get("DAYTONA_API_KEY")
if not api_key:
print("ERROR: DAYTONA_API_KEY is not set.", file=sys.stderr)
print(
"Add it to surfsense_backend/.env or export it in your shell.",
file=sys.stderr,
)
sys.exit(1)
daytona = Daytona()
try:
existing = daytona.snapshot.get(SNAPSHOT_NAME)
print(f"Deleting existing snapshot '{SNAPSHOT_NAME}'")
daytona.snapshot.delete(existing)
print(f"Deleted '{SNAPSHOT_NAME}'. Waiting for removal to propagate …")
for _attempt in range(30):
time.sleep(2)
try:
daytona.snapshot.get(SNAPSHOT_NAME)
except Exception:
print(f"Confirmed '{SNAPSHOT_NAME}' is gone.\n")
break
else:
print(
f"WARNING: '{SNAPSHOT_NAME}' may still exist after 60s. Proceeding anyway.\n"
)
except Exception:
pass
print(f"Building snapshot '{SNAPSHOT_NAME}'")
print(f"Packages: {', '.join(PACKAGES)}\n")
daytona.snapshot.create(
CreateSnapshotParams(name=SNAPSHOT_NAME, image=build_image()),
on_logs=lambda chunk: print(chunk, end="", flush=True),
)
print(f"\n\nSnapshot '{SNAPSHOT_NAME}' is ready.")
print("\nAdd this to surfsense_backend/.env:")
print(f" DAYTONA_SNAPSHOT_ID={SNAPSHOT_NAME}")
if __name__ == "__main__":
main()