weijiafu14 / dsh-remote-sandbox

목록에 있음

Crash-resilient remote execution world for DeepSeek Harness: ctx.fs/ctx.subprocess over an E2B sandbox with heartbeat keep-alive, transparent recovery, and workspace sync.

master샌드박스 소스 보기

설치

npx -y @deepseek-ai/dsh plugin --profile web add github:weijiafu14/dsh-remote-sandbox

이 설치 명령은 GitHub 저장소 주소에서 생성된 확인되지 않은 시작점입니다.

README

유지 관리자가 작성한 문서 스냅샷입니다.

GitHub에서 보기 ↗
커밋 4da3dfd동기화 2026. 8. 18.

dsh-remote-sandbox

Production-grade remote execution world for DeepSeek Harness (dsh): move ctx.fs and ctx.subprocess into an E2B sandbox that survives crashes, stays alive under load, and keeps your workspace in sync — the pieces the official @deepseek-ai/dsh-e2b proof-of-concept explicitly leaves out.

Third-party plugin suite. Not affiliated with DeepSeek. MIT licensed.

Why

dsh's capability seams let one provider swap move the whole execution world to a remote sandbox — Bash, PTY, and LSP follow ctx.fs/ctx.subprocess with no forks. The official dsh-e2b package proves the seam works but is, by its own README, a POC: the sandbox is "deliberately ephemeral", dies after five minutes, keeps a process's complete output in host memory, and "neither uploads nor synchronizes the host workspace". This suite makes that execution world usable for real work.

official dsh-e2b (POC)dsh-remote-sandbox
Sandbox lifetimefixed 5 min, then deletedheartbeat keep-alive, lives as long as the task
Connection drop / pausenone — sandbox death is task deathtransparent recovery: resume the paused sandbox (disk retained)
Hard crashnonerecreate + restore workspace from the last synced snapshot
Host workspacenot uploaded or synced (path-spelling mirror only)tar IN at open, tar OUT on request/teardown, .gitignore-independent excludes
Process output memorycomplete stream retained in host memory (POC admits it breaks the seam's bound)bounded in-memory tail + optional host spill file — host memory is capped
Round trips per fs op3–8 SDK commands per mutation (realpath, stage, rename, xattr…)one — the sidecar does read-modify-publish atomically in the sandbox
glob/grep (tool-fs-search)broken in a remote world (see below)works — ripgrep ships with the sidecar
Custom sync backendn/asyncIn/syncOut overridable from cordis.yml (!!js)

Architecture

host (dsh process)                          sandbox (E2B)
┌───────────────────────────┐               ┌──────────────────────────┐
│ agent loop, LLM key,      │               │ sidecar (one pure-JS      │
│ session, tools            │               │ bundle, no native deps)   │
│                           │  one WSS      │  ├─ fs primitives          │
│ dsh-fs-remote  ───────────┼──────────────►│  ├─ process trees          │
│ dsh-subprocess-remote ────┤  structured   │  └─ ripgrep                │
│ dsh-sandbox-keeper        │  protocol     │                            │
│  ├─ E2B lifecycle         │               │ workspace ◄─ tar IN/OUT    │
│  ├─ heartbeat / recovery  │               └──────────────────────────┘
│  └─ tar sync              │
└───────────────────────────┘
  • The sidecar is one ~160 KB pure-JavaScript file (WebSocket server inlined, no native binding). The keeper uploads it and runs it with the sandbox's own Node. It executes primitives locally, so a filesystem mutation is one round trip instead of a chain of SDK shell commands.
  • The keeper owns the sandbox: it deploys the sidecar, pushes the pause deadline forward to keep the sandbox alive, and on a dropped connection resumes the paused sandbox (disk, and via E2B's memory snapshot the sidecar itself, retained) or — only when the sandbox is truly gone — recreates it and restores the workspace. LLM and git credentials never enter the sandbox.
  • The providers are thin adapters: every ctx.fs/ctx.subprocess call derives the current sidecar client through the keeper, so a recovery swaps the connection underneath without the provider noticing.

Install

dsh plugin --profile <name> add dsh-remote-sandbox
export E2B_API_KEY=e2b_...     # get one at https://e2b.dev/dashboard

The dsh-remote-sandbox bundle layers over dsh-base: it disables the local subprocess and fs-sandbox providers and inserts the keeper plus the remote providers. Bash, terminal, and LSP compose above them unchanged.

To wire it by hand instead, disable dsh-subprocess-local and dsh-fs-sandbox in your cordis.yml and insert:

- id: sandbox-keeper
  name: dsh-sandbox-keeper
  config:
    cwd: /home/user/workspace          # workspace path inside the sandbox
    hostWorkspace: !!js process.cwd()  # local project synced in/out
- id: subprocess-remote
  name: dsh-subprocess-remote
- id: fs-remote
  name: dsh-fs-remote

Configuration (dsh-sandbox-keeper)

KeyDefaultMeaning
apiKey$E2B_API_KEYE2B API key; never forwarded into the sandbox
templateE2B base imageE2B template id (the base image ships Node)
cwd/home/user/workspaceworkspace path inside the sandbox
hostWorkspaceprocess.cwd()host project synced in at open, out on request/teardown
timeoutMs300000pause deadline; the heartbeat pushes it forward
heartbeatMs30000keep-alive and liveness interval
excludesnode_modules, .pnpm-store, .dsh-remote-sidecarpath segments never synced (.git is synced)
pauseOnDisposetruepause (retain disk) rather than kill on teardown
syncIn / syncOuttar over the sidecaroverride with your own function (!!js) for git, object storage, a PVC, etc.

The two upstream bugs this works around

Found while building against deepseek-harness@0.1.0-rc.6:

  1. tool-fs-search hardcodes the host ripgrep path. runRipgrep spawns resolveRgPath() — the absolute path of the host's @vscode/ripgrep binary — as argv[0], without going through resolveExecutable. The plugin is in the shipped dsh-base bundle, but any remote execution world lacks that host path, so glob/grep fail on every call. The official e2b example sidesteps it by not mounting the plugin. This suite ships ripgrep with the sidecar so the tool works.
  2. Sandbox permission modes have no remote semantics. dsh-fs-sandbox extends LocalFileSystem and writableRoots() mixes in the host's /tmp/os.tmpdir(), so read-only and workspace-write silently do not apply in a remote world — the e2b example is forced to hardcode danger-full-access. The sidecar enforces the mode itself against the sandbox's own paths.

Known limitations and deferred work

  • Terminal (spawnTerminal) is experimental and not yet covered by tests. The remote sandbox targets non-interactive agent work — read/write/edit, build, test, grep — which never needs a pty; that path is fully tested. Interactive terminals are the rare case, so spawnTerminal ships as best-effort: it runs over E2B's native pty (output, input, resize, and kill are real), but that API exposes no foreground process-group or input-waiting facts, so inspectForeground reports the session pid and inputWaiting: false, and no test exercises it yet. Planned rework: move the pty into the sidecar via a prebuilt node-pty, giving it a real foreground group (tcgetpgrp) and input-wait detection — full parity with the local terminal, and it brings the pty onto the same sidecar channel as everything else. If your deployment does not open interactive terminals, this does not affect you.
  • Recovery is at-least-once for the moments around a drop. File writes or commands issued in the seconds before a crash may not have taken effect after recovery; the keeper injects a model-visible notice (via notify / consumeRecoveryNotice()) so the agent re-checks the tree.
  • OUT sync is low-frequency by design. Recovery relies first on the paused sandbox's retained disk; the tar snapshot is the fallback for a truly deleted sandbox, so there is no per-write sync tax. Enable a periodic OUT via a custom syncOut if your deployment needs it.
  • Pre-release upstream. dsh is in developer preview with no compatibility promise; peer versions are pinned loosely and may need bumping as the harness evolves.

Development

pnpm install
pnpm -r run build          # tsc + esbuild the sidecar bundle
pnpm test                  # 37 unit + provider tests (no sandbox needed)
E2B_API_KEY=... pnpm test:e2e   # 6 real-E2B killer scenarios

The unit suite spawns the real sidecar bundle as a local process and drives the wire protocol, the providers through the real dsh FileSystem/SubprocessRuntime base classes, and the keeper's lifecycle/recovery/sync against a fake backend. The E2E suite runs the six scenarios — keep-alive, resume, recreate, bounded grep, tar round-trip, custom sync — against real E2B sandboxes.

Packages

PackageRole
dsh-remote-protocolwire protocol + codec shared by sidecar and host
dsh-remote-sidecarthe in-sandbox executor (single bundled file)
dsh-sandbox-keeperctx.remoteSandbox: E2B lifecycle, heartbeat, recovery, sync
dsh-fs-remotectx.fs provider
dsh-subprocess-remotectx.subprocess provider
dsh-remote-sandboxone-line bundle that wires all three over dsh-base

License

MIT

프로젝트 파일 및 신호

표시된 항목은 디렉터리 스냅샷에서 감지된 공개 저장소 신호입니다.

테스트감지됨

저장소 정보

언어
TypeScript
라이선스
MIT
마지막 업데이트
2026. 8. 14. 오전 1:29

신중하게 설치하기

소스 코드, 권한, 수명 주기 스크립트, 의존성 및 네트워크 접근을 검토하고 신뢰하지 않는 플러그인은 격리 환경에서 테스트하세요.