Repository navigation
feat: add a sandbox image with desktops agents can start #298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| name: Sandbox image preview | ||
|
|
||
| # A pull request that changes the sandbox image gets it built and published as | ||
| # ghcr.io/<owner>/sugabots-sandbox:pr-<number>, to try before it's merged, and | ||
| # says how on the pull request. Closing the pull request deletes it. Pull | ||
| # requests from forks get none: their workflows can't publish packages. | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened, closed] | ||
| paths: | ||
| - "docker/sandbox/**" | ||
| - ".github/workflows/sandbox-preview.yml" | ||
|
|
||
| concurrency: | ||
| group: sandbox-preview-${{ github.event.pull_request.number }} | ||
| cancel-in-progress: true | ||
|
|
||
| env: | ||
| IMAGE: ghcr.io/${{ github.repository_owner }}/sugabots-sandbox | ||
| TAG: pr-${{ github.event.pull_request.number }} | ||
|
|
||
| jobs: | ||
| publish: | ||
| name: Publish the pull request's sandbox image | ||
| if: | | ||
| github.event.action != 'closed' && | ||
| github.event.pull_request.head.repo.full_name == github.repository | ||
| runs-on: ubuntu-latest | ||
|
|
||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| pull-requests: write | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v5 | ||
|
|
||
| - uses: docker/setup-qemu-action@v4 | ||
|
|
||
| - uses: docker/setup-buildx-action@v4 | ||
|
|
||
| - uses: docker/login-action@v4 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| # Both platforms, as a release has, so it runs on Apple silicon too. | ||
| - id: build | ||
| uses: docker/build-push-action@v7 | ||
| with: | ||
| context: docker/sandbox | ||
| platforms: linux/amd64,linux/arm64 | ||
| push: true | ||
| tags: ${{ env.IMAGE }}:${{ env.TAG }} | ||
| cache-from: type=gha,scope=sandbox-image | ||
| cache-to: type=gha,scope=sandbox-image,mode=max | ||
|
|
||
| # One comment per pull request, updated on each push, found by its marker. | ||
| - name: Say how to try it | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| PR: ${{ github.event.pull_request.number }} | ||
| DIGEST: ${{ steps.build.outputs.digest }} | ||
| SHA: ${{ github.event.pull_request.head.sha }} | ||
| run: | | ||
| set -euo pipefail | ||
| marker="<!-- sandbox-image-preview -->" | ||
| body="$(cat <<EOF | ||
| $marker | ||
| **Sandbox image preview**, built from ${SHA:0:7}: | ||
|
|
||
| \`\`\` | ||
| $IMAGE:$TAG | ||
| \`\`\` | ||
|
|
||
| To try it, set a sandbox provider's image to that under Sandboxes in the workspace's settings, then Upgrade the pod's sandbox; on E2B, prepare the template again first. The tag moves with each push to this pull request, so a machine that pulled it before may need \`docker pull\` again; for this exact build, use \`$IMAGE@$DIGEST\`. | ||
|
|
||
| It's deleted when the pull request closes. | ||
| EOF | ||
| )" | ||
| comment_id=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR/comments" --paginate \ | ||
| --jq ".[] | select(.body | startswith(\"$marker\")) | .id" | head -n1) | ||
| if [ -n "$comment_id" ]; then | ||
| gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$comment_id" -f body="$body" >/dev/null | ||
| else | ||
| gh api --method POST "repos/$GITHUB_REPOSITORY/issues/$PR/comments" -f body="$body" >/dev/null | ||
| fi | ||
|
|
||
| cleanup: | ||
| name: Delete the pull request's sandbox image | ||
| if: | | ||
| github.event.action == 'closed' && | ||
| github.event.pull_request.head.repo.full_name == github.repository | ||
| runs-on: ubuntu-latest | ||
|
|
||
| permissions: | ||
| packages: write | ||
|
|
||
| steps: | ||
| - name: Delete the image version tagged for the pull request | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| PACKAGE: orgs/${{ github.repository_owner }}/packages/container/sugabots-sandbox | ||
| run: | | ||
| set -euo pipefail | ||
| version_id=$(gh api "/$PACKAGE/versions?per_page=100" --paginate \ | ||
| --jq ".[] | select(.metadata.container.tags[]? == \"$TAG\") | .id" | head -n1) | ||
| if [ -z "$version_id" ]; then | ||
| echo "No image version is tagged $TAG; nothing to delete." | ||
| exit 0 | ||
| fi | ||
| echo "Deleting package version $version_id ($TAG)" | ||
| gh api --method DELETE "/$PACKAGE/versions/$version_id" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| # The default sandbox image: Debian with what agents reach for when they work | ||
| # on code, and desktops with a browser they drive through Playwright MCP. | ||
| # Commands run without a screen; `sugabots-desktop` starts a desktop, each on | ||
| # its own display. | ||
| # | ||
| # bun run build:sandbox | ||
| # | ||
| # Builds ghcr.io/nitrictech/sugabots-sandbox:latest, where releases publish it | ||
| # too, and to docker.io/nitrictech/sugabots-sandbox: the image OpenSandbox | ||
| # providers use unless a workspace sets another, and the one E2B templates are | ||
| # built from. The agents' user is the provider's to | ||
| # make: OpenSandbox's when it makes a sandbox, and E2B's own `user`, uid 1000, | ||
| # so this image has none of its own to clash with them. | ||
|
|
||
| ARG PLAYWRIGHT_MCP_VERSION=0.0.83 | ||
|
|
||
| # The desktop's wallpaper, rendered at the display's size, so the renderer | ||
| # doesn't ship in the image. | ||
| FROM debian:trixie-slim AS wallpaper | ||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends librsvg2-bin \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
| COPY wallpaper.svg /wallpaper.svg | ||
| RUN rsvg-convert --width 1280 --height 800 /wallpaper.svg --output /wallpaper.png | ||
|
|
||
| # The dock's icons, from Papirus, without the rest of its 380 MB. | ||
| FROM debian:trixie-slim AS dock-icons | ||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends papirus-icon-theme \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
| RUN mkdir /icons \ | ||
| && cd /usr/share/icons/Papirus/64x64/apps \ | ||
| && cp -L chromium.svg /icons/browser.svg \ | ||
| && cp -L utilities-terminal.svg /icons/terminal.svg \ | ||
| && cp -L system-file-manager.svg /icons/files.svg | ||
|
|
||
| FROM debian:trixie-slim | ||
|
|
||
| ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 | ||
|
|
||
| # The tools agents use on a repository, then the desktop: a virtual display, | ||
| # a window manager and dock, a browser, a file manager, and what computer-use | ||
| # tools drive them with. | ||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends \ | ||
| bash ca-certificates curl wget git openssh-client less file procps \ | ||
| unzip zip xz-utils jq ripgrep fd-find tree \ | ||
| build-essential pkg-config \ | ||
| python3 python3-pip python3-venv \ | ||
| nodejs npm \ | ||
| xvfb x11vnc x11-utils openbox plank dconf-gsettings-backend dconf-cli xcompmgr xterm \ | ||
| xfonts-base dbus-x11 hsetroot \ | ||
| lxterminal pcmanfm adwaita-icon-theme librsvg2-common \ | ||
| nix-bin \ | ||
| chromium xdotool scrot xclip fonts-dejavu fonts-noto-color-emoji \ | ||
| && ln -s /usr/bin/fdfind /usr/local/bin/fd \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # The browser agents drive: Playwright MCP, on Debian's Chromium rather than | ||
| # a browser Playwright downloads. | ||
| ARG PLAYWRIGHT_MCP_VERSION | ||
| RUN npm install --global --omit=dev "@playwright/mcp@${PLAYWRIGHT_MCP_VERSION}" \ | ||
| && npm cache clean --force | ||
|
|
||
| COPY --from=wallpaper /wallpaper.png /usr/share/sugabots-desktop/wallpaper.png | ||
| COPY --from=dock-icons /icons /usr/share/sugabots-desktop/icons | ||
| # The dock's launchers stand in for the apps' own, so the dock matches their | ||
| # windows to them, and its settings are the system's dconf defaults. | ||
| COPY dock/*.desktop /usr/share/applications/ | ||
| COPY dock/plank/ /usr/share/sugabots-desktop/plank/ | ||
| COPY dock/plank-theme/dock.theme /usr/share/plank/themes/Sugabots/dock.theme | ||
| COPY dock/plank.dconf /etc/dconf/db/local.d/00-plank | ||
| RUN mkdir -p /etc/dconf/profile \ | ||
| && printf 'user-db:user\nsystem-db:local\n' >/etc/dconf/profile/user \ | ||
| && dconf update | ||
| # Nix, for software beyond this image: `nix profile install nixpkgs#ffmpeg`, | ||
| # or `nix shell nixpkgs#ffmpeg` for one command. The agents' user owns the | ||
| # store, so it installs without root. `nixpkgs` follows a NixOS release and | ||
| # `nixpkgs-unstable` its newer versions, by branch, so the image never needs | ||
| # moving forward; whatever pins exact versions records the commit it used. | ||
| COPY nix/nix.conf nix/registry.json /etc/nix/ | ||
| # Nix looks for its state here, and without it falls back to a store in the | ||
| # user's home that needs namespaces the agents' user can't make. | ||
| RUN mkdir -p /nix/store /nix/var/nix && chown -R 1000:1000 /nix | ||
|
|
||
| COPY sugabots-desktop /usr/local/bin/sugabots-desktop | ||
|
|
||
| RUN mkdir -p /workspace && chmod 755 /usr/local/bin/sugabots-desktop | ||
|
|
||
| WORKDIR /workspace |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [Desktop Entry] | ||
| Type=Application | ||
| Name=Browser | ||
| Comment=The agent's browser | ||
| Exec=sugabots-desktop open browser | ||
| Icon=/usr/share/sugabots-desktop/icons/browser.svg | ||
| StartupWMClass=sugabots-browser |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [Desktop Entry] | ||
| Type=Application | ||
| Name=Terminal | ||
| Comment=A shell in the workspace | ||
| Exec=sugabots-desktop open terminal | ||
| Icon=/usr/share/sugabots-desktop/icons/terminal.svg | ||
| StartupWMClass=lxterminal |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [Desktop Entry] | ||
| Type=Application | ||
| Name=Files | ||
| Comment=The workspace's files | ||
| Exec=sugabots-desktop open files | ||
| Icon=/usr/share/sugabots-desktop/icons/files.svg | ||
| StartupWMClass=pcmanfm |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # The dock's look: a rounded, light, see-through bar the icons sit inside, | ||
| # in the style of macOS's. Sizes are tenths of the icon size. | ||
| [PlankTheme] | ||
| TopRoundness=4 | ||
| BottomRoundness=4 | ||
| LineWidth=1 | ||
| OuterStrokeColor=255;;255;;255;;110 | ||
| FillStartColor=245;;245;;247;;150 | ||
| FillEndColor=235;;235;;240;;150 | ||
| InnerStrokeColor=255;;255;;255;;0 | ||
|
|
||
| [PlankDockTheme] | ||
| HorizPadding=1.5 | ||
| TopPadding=1.2 | ||
| BottomPadding=1.2 | ||
| ItemPadding=2.5 | ||
| IndicatorSize=4 | ||
| IconShadowSize=1 | ||
| UrgentBounceHeight=1.6666666666666667 | ||
| LaunchBounceHeight=0.625 | ||
| FadeOpacity=1 | ||
| ClickTime=300 | ||
| UrgentBounceTime=600 | ||
| LaunchBounceTime=600 | ||
| ActiveTime=300 | ||
| SlideTime=300 | ||
| FadeTime=250 | ||
| HideTime=250 | ||
| GlowSize=30 | ||
| GlowTime=10000 | ||
| GlowPulseTime=2000 | ||
| UrgentHueShift=150 | ||
| ItemMoveTime=450 | ||
| CascadeHide=true | ||
| BadgeColor=0;;0;;0;;0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # The dock's settings, as the system's defaults: Plank reads them from dconf, | ||
| # by path, so they need no write at runtime. | ||
| [net/launchpad/plank/docks/dock1] | ||
| dock-items=['chromium.dockitem', 'lxterminal.dockitem', 'pcmanfm.dockitem'] | ||
| position='bottom' | ||
| alignment='center' | ||
| hide-mode='none' | ||
| icon-size=48 | ||
| zoom-enabled=true | ||
| zoom-percent=140 | ||
| lock-items=true | ||
| show-dock-item=false | ||
| theme='Sugabots' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [PlankDockItemPreferences] | ||
| Launcher=file:///usr/share/applications/chromium.desktop |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [PlankDockItemPreferences] | ||
| Launcher=file:///usr/share/applications/lxterminal.desktop |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [PlankDockItemPreferences] | ||
| Launcher=file:///usr/share/applications/pcmanfm.desktop |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Nix for the agents' user alone: no daemon and no build users, since that | ||
| # user (uid 1000 on every provider) owns /nix and has no root. | ||
| build-users-group = | ||
| experimental-features = nix-command flakes | ||
| # Nix's build sandbox needs namespaces the agents' user can't make. Packages | ||
| # come built from the binary cache, so it only matters for one built here. | ||
| sandbox = false | ||
| substituters = https://cache.nixos.org | ||
| trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= | ||
| # Only the nixpkgs entries in /etc/nix/registry.json, not the global | ||
| # registry, which Nix would fetch from a host sandboxes don't reach. | ||
| flake-registry = |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "version": 2, | ||
| "flakes": [ | ||
| { | ||
| "from": { "type": "indirect", "id": "nixpkgs" }, | ||
| "to": { "type": "github", "owner": "NixOS", "repo": "nixpkgs", "ref": "nixos-26.05" } | ||
| }, | ||
| { | ||
| "from": { "type": "indirect", "id": "nixpkgs-unstable" }, | ||
| "to": { "type": "github", "owner": "NixOS", "repo": "nixpkgs", "ref": "nixos-unstable" } | ||
| } | ||
| ] | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.