diff --git a/.github/workflows/template.yml b/.github/workflows/template.yml index aa9bc0d..e635b3c 100644 --- a/.github/workflows/template.yml +++ b/.github/workflows/template.yml @@ -3,8 +3,8 @@ name: Build and push Desktop template on: push: paths: - - "template/**" - - ".github/workflows/template.yml" + - 'template/**' + - '.github/workflows/template.yml' branches: - main @@ -23,6 +23,22 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12' + + - name: Install and configure Poetry + uses: snok/install-poetry@v1 + with: + version: 2.1.1 + virtualenvs-create: true + virtualenvs-in-project: true + installer-parallel: true + + - name: Install dependencies + run: poetry install + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -35,16 +51,12 @@ jobs: - name: Build and push to DockerHub run: | docker pull ${{ secrets.DOCKERHUB_USERNAME }}/desktop:latest || true - docker buildx build \ - --file Dockerfile \ + poetry run python build_docker.py | docker buildx build \ --platform linux/amd64 \ --push \ - --tag ${{ secrets.DOCKERHUB_USERNAME }}/desktop:latest . - - - name: Install E2B CLI - run: npm install -g @e2b/cli + --tag ${{ secrets.DOCKERHUB_USERNAME }}/desktop:latest -f - files - - name: Build e2b - run: e2b template build + - name: Build E2B template + run: poetry run python build_prod.py env: - E2B_ACCESS_TOKEN: ${{ secrets.E2B_ACCESS_TOKEN }} + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} diff --git a/.gitignore b/.gitignore index 4d709de..b8b821c 100644 --- a/.gitignore +++ b/.gitignore @@ -295,3 +295,5 @@ cython_debug/ # SDK reference artifacts sdk_ref/ + +.ruff_cache/ \ No newline at end of file diff --git a/template/.python-version b/template/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/template/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/template/Dockerfile b/template/Dockerfile deleted file mode 100644 index 89933a2..0000000 --- a/template/Dockerfile +++ /dev/null @@ -1,103 +0,0 @@ -# E2B Desktop Sandbox Template -# -# This Dockerfile contains the commands to create a computer use sandbox on E2B. -# If you want to make your own template based on this one, make your changes - -FROM ubuntu:22.04 - -# Environment variables: - -ENV \ - # Avoid system prompts: \ - DEBIAN_FRONTEND=noninteractive \ - DEBIAN_PRIORITY=high \ - # Pip settings: \ - PIP_DEFAULT_TIMEOUT=100 \ - PIP_DISABLE_PIP_VERSION_CHECK=1 \ - PIP_NO_CACHE_DIR=1 - -# Desktop environment: - -RUN yes | unminimize && \ - apt-get update && \ - # X window server: - apt-get install -y xserver-xorg xorg x11-xserver-utils xvfb x11-utils xauth && \ - # XFCE desktop environment: - apt-get install -y xfce4 xfce4-goodies && \ - # Basic system utilities: - apt-get install -y util-linux sudo curl git wget && \ - # Pip will be used to install Python packages: - apt-get install -y python3-pip && \ - # Tools used by the desktop SDK: - apt-get install -y xdotool scrot ffmpeg - -# Streaming server: - -RUN \ - # VNC: \ - apt-get install -y x11vnc && \ - # NoVNC: \ - git clone --branch e2b-desktop https://github.com/e2b-dev/noVNC.git /opt/noVNC && \ - ln -s /opt/noVNC/vnc.html /opt/noVNC/index.html && \ - # Websockify: \ - apt-get install -y net-tools netcat && \ - pip install numpy && \ - git clone --branch v0.12.0 https://github.com/novnc/websockify /opt/noVNC/utils/websockify - -# User applications: - -# ~ Make your changes to this template BELOW this line ~ - -# Set the default terminal -RUN ln -sf /usr/bin/xfce4-terminal.wrapper /etc/alternatives/x-terminal-emulator - -# Install standard apps -RUN apt-get install -y x11-apps \ - libreoffice \ - xpdf \ - gedit \ - xpaint \ - tint2 \ - galculator \ - pcmanfm - -# Install Firefox -RUN apt-get install -y software-properties-common && \ - add-apt-repository ppa:mozillateam/ppa && \ - apt-get install -y --no-install-recommends \ - firefox-esr - -# Install Chrome -RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \ - echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list && \ - apt-get update && \ - apt-get install -y google-chrome-stable - -# Set Firefox as default browser -RUN update-alternatives --set x-www-browser /usr/bin/firefox-esr - -# Copy Chrome desktop shortcut -COPY google-chrome.desktop /usr/share/applications/google-chrome.desktop - -# Install VS Code -RUN apt-get install apt-transport-https && \ - wget -qO- https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \ - add-apt-repository -y "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" && \ - apt-get update -y && \ - apt-get install -y code -RUN mkdir -p /home/user/.config/Code/User -COPY ./settings.json /home/user/.config/Code/User/settings.json - -# Copy desktop background for XFCE -COPY ./wallpaper.png /usr/share/backgrounds/xfce/wallpaper.png -RUN mkdir -p /home/user/.config/xfce4/xfconf/xfce-perchannel-xml/ -COPY ./xfce4-desktop.xml /home/user/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-desktop.xml - -# Install gtk-launch and update desktop database -RUN apt-get install -y libgtk-3-bin && \ - update-desktop-database /usr/share/applications/ - -# Copy firefox policies -COPY firefox-policies.json /usr/lib/firefox-esr/distribution/policies.json -COPY firefox-autoconfig.js /usr/lib/firefox-esr/defaults/pref/autoconfig.js -COPY firefox.cfg /usr/lib/firefox-esr/firefox.cfg diff --git a/template/README.md b/template/README.md new file mode 100644 index 0000000..d2db436 --- /dev/null +++ b/template/README.md @@ -0,0 +1,15 @@ +# E2B Desktop Template + +This is the template for the E2B Desktop Sandbox. + +## Building the template + +```bash +poetry run python build_dev.py +``` + +## Building the production image + +```bash +poetry run python build_prod.py +``` diff --git a/template/build_dev.py b/template/build_dev.py new file mode 100644 index 0000000..ed08c02 --- /dev/null +++ b/template/build_dev.py @@ -0,0 +1,13 @@ +from dotenv import load_dotenv +from e2b import Template, default_build_logger +from template import template_with_user_workdir + +load_dotenv() + +Template.build( + template=template_with_user_workdir, + alias="desktop-dev", + cpu_count=8, + memory_mb=8192, + on_build_logs=default_build_logger(), +) diff --git a/template/build_docker.py b/template/build_docker.py new file mode 100644 index 0000000..72a439b --- /dev/null +++ b/template/build_docker.py @@ -0,0 +1,5 @@ +from e2b import Template +from template import template + +# output the template to stdout to pipe into docker buildx +print(Template.to_dockerfile(template)) diff --git a/template/build_prod.py b/template/build_prod.py new file mode 100644 index 0000000..0652e64 --- /dev/null +++ b/template/build_prod.py @@ -0,0 +1,13 @@ +from dotenv import load_dotenv +from e2b import Template, default_build_logger +from template import template_with_user_workdir + +load_dotenv() + +Template.build( + template_with_user_workdir, + alias="desktop", + cpu_count=8, + memory_mb=8192, + on_build_logs=default_build_logger(), +) diff --git a/template/e2b.Dockerfile b/template/e2b.Dockerfile deleted file mode 100644 index ea7b048..0000000 --- a/template/e2b.Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM e2bdev/desktop:latest diff --git a/template/e2b.toml b/template/e2b.toml deleted file mode 100644 index 22884a5..0000000 --- a/template/e2b.toml +++ /dev/null @@ -1,18 +0,0 @@ -# This is a config for E2B sandbox template. -# You can use template ID (k0wmnzir0zuzye6dndlw) or template name (desktop) to create a sandbox: - -# Python SDK -# from e2b import Sandbox, AsyncSandbox -# sandbox = Sandbox.create("desktop") # Sync sandbox -# sandbox = await AsyncSandbox.create("desktop") # Async sandbox - -# JS SDK -# import { Sandbox } from 'e2b' -# const sandbox = await Sandbox.create('desktop') - -team_id = "460355b3-4f64-48f9-9a16-4442817f79f5" -memory_mb = 8_192 -cpu_count = 8 -dockerfile = "e2b.Dockerfile" -template_name = "desktop" -template_id = "k0wmnzir0zuzye6dndlw" diff --git a/template/firefox-autoconfig.js b/template/files/firefox-autoconfig.js similarity index 100% rename from template/firefox-autoconfig.js rename to template/files/firefox-autoconfig.js diff --git a/template/firefox-policies.json b/template/files/firefox-policies.json similarity index 100% rename from template/firefox-policies.json rename to template/files/firefox-policies.json diff --git a/template/firefox.cfg b/template/files/firefox.cfg similarity index 100% rename from template/firefox.cfg rename to template/files/firefox.cfg diff --git a/template/google-chrome.desktop b/template/files/google-chrome.desktop similarity index 100% rename from template/google-chrome.desktop rename to template/files/google-chrome.desktop diff --git a/template/settings.json b/template/files/settings.json similarity index 100% rename from template/settings.json rename to template/files/settings.json diff --git a/template/wallpaper.png b/template/files/wallpaper.png similarity index 100% rename from template/wallpaper.png rename to template/files/wallpaper.png diff --git a/template/xfce4-desktop.xml b/template/files/xfce4-desktop.xml similarity index 100% rename from template/xfce4-desktop.xml rename to template/files/xfce4-desktop.xml diff --git a/template/poetry.lock b/template/poetry.lock new file mode 100644 index 0000000..e2b47de --- /dev/null +++ b/template/poetry.lock @@ -0,0 +1,370 @@ +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. + +[[package]] +name = "anyio" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +trio = ["trio (>=0.31.0)"] + +[[package]] +name = "attrs" +version = "25.4.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, +] + +[[package]] +name = "certifi" +version = "2025.10.5" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, +] + +[[package]] +name = "dockerfile-parse" +version = "2.0.1" +description = "Python library for Dockerfile manipulation" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "dockerfile-parse-2.0.1.tar.gz", hash = "sha256:3184ccdc513221983e503ac00e1aa504a2aa8f84e5de673c46b0b6eee99ec7bc"}, + {file = "dockerfile_parse-2.0.1-py2.py3-none-any.whl", hash = "sha256:bdffd126d2eb26acf1066acb54cb2e336682e1d72b974a40894fac76a4df17f6"}, +] + +[[package]] +name = "dotenv" +version = "0.9.9" +description = "Deprecated package" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9"}, +] + +[package.dependencies] +python-dotenv = "*" + +[[package]] +name = "e2b" +version = "2.4.3" +description = "E2B SDK that give agents cloud environments" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["main"] +files = [ + {file = "e2b-2.4.3-py3-none-any.whl", hash = "sha256:7ebab1cb2b845c963cdb3fe150487a141f573b5966a58d85adbe3ab371207295"}, + {file = "e2b-2.4.3.tar.gz", hash = "sha256:99928b9cf57b3feb7f9e7f71bf01fcdf22682832fd5e750ed124bf5041147f0a"}, +] + +[package.dependencies] +attrs = ">=23.2.0" +dockerfile-parse = ">=2.0.1,<3.0.0" +httpcore = ">=1.0.5,<2.0.0" +httpx = ">=0.27.0,<1.0.0" +packaging = ">=24.1" +protobuf = ">=4.21.0" +python-dateutil = ">=2.8.2" +rich = ">=14.0.0" +typing-extensions = ">=4.1.0" + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.16" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + +[package.extras] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, + {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins (>=0.5.0)"] +profiling = ["gprof2dot"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "protobuf" +version = "6.33.0" +description = "" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035"}, + {file = "protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee"}, + {file = "protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455"}, + {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90"}, + {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298"}, + {file = "protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef"}, + {file = "protobuf-6.33.0-cp39-cp39-win32.whl", hash = "sha256:cd33a8e38ea3e39df66e1bbc462b076d6e5ba3a4ebbde58219d777223a7873d3"}, + {file = "protobuf-6.33.0-cp39-cp39-win_amd64.whl", hash = "sha256:c963e86c3655af3a917962c9619e1a6b9670540351d7af9439d06064e3317cc9"}, + {file = "protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995"}, + {file = "protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954"}, +] + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.2.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61"}, + {file = "python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "rich" +version = "14.2.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd"}, + {file = "rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "ruff" +version = "0.12.12" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc"}, + {file = "ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727"}, + {file = "ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5"}, + {file = "ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4"}, + {file = "ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23"}, + {file = "ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489"}, + {file = "ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee"}, + {file = "ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1"}, + {file = "ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d"}, + {file = "ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093"}, + {file = "ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[metadata] +lock-version = "2.1" +python-versions = "^3.12" +content-hash = "dc20c232a40b14cfe7f5203e0663df571bf0d995a9484aed6bcbf1a52d281ba3" diff --git a/template/pyproject.toml b/template/pyproject.toml new file mode 100644 index 0000000..9a90bb0 --- /dev/null +++ b/template/pyproject.toml @@ -0,0 +1,16 @@ +[project] +name = "e2b_desktop" +version = "0.1.0" +description = "E2B Desktop Template" +readme = "README.md" + +[tool.poetry] +package-mode = false + +[tool.poetry.dependencies] +python = "^3.12" +e2b = "^2.3.5" + +[tool.poetry.group.dev.dependencies] +dotenv = "^0.9.9" +ruff = "^0.12.5" diff --git a/template/template.py b/template/template.py new file mode 100644 index 0000000..866e39a --- /dev/null +++ b/template/template.py @@ -0,0 +1,121 @@ +from e2b import CopyItem, Template + +template = ( + Template(file_context_path="files") + .from_image("ubuntu:22.04") + .set_user("root") + .set_workdir("/") + .set_envs( + { + # Avoid system prompts + "DEBIAN_FRONTEND": "noninteractive", + "DEBIAN_PRIORITY": "high", + # Pip settings + "PIP_DEFAULT_TIMEOUT": "100", + "PIP_DISABLE_PIP_VERSION_CHECK": "1", + "PIP_NO_CACHE_DIR": "1", + } + ) + # Initial system setup and packages + .run_cmd("yes | unminimize") + .apt_install( + [ + "xserver-xorg", + "x11-xserver-utils", + "xvfb", + "x11-utils", + "xauth", + "xfce4", + "xfce4-goodies", + "util-linux", + "sudo", + "curl", + "git", + "wget", + "python3-pip", + "xdotool", + "scrot", + "ffmpeg", + "x11vnc", + "net-tools", + "netcat", + "x11-apps", + "libreoffice", + "xpdf", + "gedit", + "xpaint", + "tint2", + "galculator", + "pcmanfm", + "software-properties-common", + "apt-transport-https", + "libgtk-3-bin", + ] + ) + .pip_install("numpy") + # Setup NoVNC and websockify + .git_clone( + "https://github.com/e2b-dev/noVNC.git", "/opt/noVNC", branch="e2b-desktop" + ) + .make_symlink("/opt/noVNC/vnc.html", "/opt/noVNC/index.html") + .git_clone( + "https://github.com/novnc/websockify.git", + "/opt/noVNC/utils/websockify", + branch="v0.12.0", + ) + # Install browsers and set up repositories + .run_cmd( + [ + "add-apt-repository ppa:mozillateam/ppa", + "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -", + 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list', + "wget -qO- https://packages.microsoft.com/keys/microsoft.asc | apt-key add -", + 'add-apt-repository -y "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main"', + "apt-get update", + ], + ) + # Install browsers and VS Code + .apt_install(["firefox-esr", "google-chrome-stable", "code"]) + # Configure system settings + .make_symlink( + "/usr/bin/xfce4-terminal.wrapper", "/etc/alternatives/x-terminal-emulator", + force=True + ) + .run_cmd("update-alternatives --set x-www-browser /usr/bin/firefox-esr") + .make_dir("/home/user/.config/Code/User") + .make_dir("/home/user/.config/xfce4/xfconf/xfce-perchannel-xml/") + .run_cmd("update-desktop-database /usr/share/applications/") + # Copy all configuration files + .copy_items( + [ + CopyItem( + src="google-chrome.desktop", + dest="/usr/share/applications/google-chrome.desktop", + ), + CopyItem( + src="settings.json", + dest="/home/user/.config/Code/User/settings.json", + ), + CopyItem( + src="wallpaper.png", + dest="/usr/share/backgrounds/xfce/wallpaper.png", + ), + CopyItem( + src="xfce4-desktop.xml", + dest="/home/user/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-desktop.xml", + ), + CopyItem( + src="firefox-policies.json", + dest="/usr/lib/firefox-esr/distribution/policies.json", + ), + CopyItem( + src="firefox-autoconfig.js", + dest="/usr/lib/firefox-esr/defaults/pref/autoconfig.js", + ), + CopyItem(src="firefox.cfg", dest="/usr/lib/firefox-esr/firefox.cfg"), + ] + ) +) + +# Template with user and workdir set +template_with_user_workdir = template.set_user("user").set_workdir("/home/user")