@@ -46,6 +46,7 @@ type Server struct {
4646 ecrConfig * ecr.Config // nil if ECR not configured
4747 cfClient * cloudflare.Client // nil if Cloudflare not configured
4848 pendingCreates sync.Map // map[sandboxID]*pendingCreate — async sandbox creation tracking
49+ sandboxAPIProxy * proxy.SandboxAPIProxy // nil except in server mode (proxies data-plane to workers)
4950}
5051
5152// pendingCreate tracks an async sandbox creation.
@@ -73,6 +74,7 @@ type ServerOpts struct {
7374 CheckpointStore * storage.CheckpointStore // nil if hibernation not configured
7475 ECRConfig * ecr.Config // nil if ECR not configured
7576 CFClient * cloudflare.Client // nil if Cloudflare not configured
77+ SandboxAPIProxy * proxy.SandboxAPIProxy // nil except in server mode (proxies data-plane to workers)
7678}
7779
7880// NewServer creates a new API server with all routes configured.
@@ -102,6 +104,7 @@ func NewServer(mgr sandbox.Manager, ptyMgr *sandbox.PTYManager, apiKey string, o
102104 s .sandboxDomain = opts .SandboxDomain
103105 s .ecrConfig = opts .ECRConfig
104106 s .cfClient = opts .CFClient
107+ s .sandboxAPIProxy = opts .SandboxAPIProxy
105108 }
106109
107110 // Global middleware
@@ -132,7 +135,6 @@ func NewServer(mgr sandbox.Manager, ptyMgr *sandbox.PTYManager, apiKey string, o
132135 api .GET ("/sandboxes" , s .listSandboxes )
133136 api .GET ("/sandboxes/:id" , s .getSandbox )
134137 api .DELETE ("/sandboxes/:id" , s .killSandbox )
135- api .POST ("/sandboxes/:id/timeout" , s .setTimeout )
136138
137139 // Hibernation
138140 api .POST ("/sandboxes/:id/hibernate" , s .hibernateSandbox )
@@ -155,32 +157,70 @@ func NewServer(mgr sandbox.Manager, ptyMgr *sandbox.PTYManager, apiKey string, o
155157 api .GET ("/sandboxes/:id/preview" , s .listPreviewURLs )
156158 api .DELETE ("/sandboxes/:id/preview/:port" , s .deletePreviewURL )
157159
158- // Exec sessions (replaces old /commands)
159- api .POST ("/sandboxes/:id/exec" , s .createExecSession )
160- api .GET ("/sandboxes/:id/exec" , s .listExecSessions )
161- api .GET ("/sandboxes/:id/exec/:sessionID" , s .execSessionWebSocket )
162- api .POST ("/sandboxes/:id/exec/:sessionID/kill" , s .killExecSession )
163- api .POST ("/sandboxes/:id/exec/run" , s .execRun )
164-
165- // Agent sessions (Claude Agent SDK)
166- api .POST ("/sandboxes/:id/agent" , s .createAgentSession )
167- api .GET ("/sandboxes/:id/agent" , s .listAgentSessions )
168- api .POST ("/sandboxes/:id/agent/:sid/prompt" , s .sendAgentPrompt )
169- api .POST ("/sandboxes/:id/agent/:sid/interrupt" , s .interruptAgent )
170- api .POST ("/sandboxes/:id/agent/:sid/kill" , s .killAgentSession )
171-
172- // Filesystem
173- api .GET ("/sandboxes/:id/files" , s .readFile )
174- api .PUT ("/sandboxes/:id/files" , s .writeFile )
175- api .GET ("/sandboxes/:id/files/list" , s .listDir )
176- api .POST ("/sandboxes/:id/files/mkdir" , s .makeDir )
177- api .DELETE ("/sandboxes/:id/files" , s .removeFile )
178-
179- // PTY
180- api .POST ("/sandboxes/:id/pty" , s .createPTY )
181- api .GET ("/sandboxes/:id/pty/:sessionID" , s .ptyWebSocket )
182- api .POST ("/sandboxes/:id/pty/:sessionID/resize" , s .resizePTY )
183- api .DELETE ("/sandboxes/:id/pty/:sessionID" , s .killPTY )
160+ // Data-plane routes: in server mode, proxy to workers; otherwise handle locally
161+ if s .sandboxAPIProxy != nil {
162+ // Server mode: proxy all data-plane requests to the worker that owns the sandbox
163+ pxy := s .sandboxAPIProxy .ProxyHandler
164+
165+ // Exec
166+ api .POST ("/sandboxes/:id/exec" , pxy )
167+ api .GET ("/sandboxes/:id/exec" , pxy )
168+ api .GET ("/sandboxes/:id/exec/:sessionID" , pxy )
169+ api .POST ("/sandboxes/:id/exec/:sessionID/kill" , pxy )
170+ api .POST ("/sandboxes/:id/exec/run" , pxy )
171+
172+ // Agent
173+ api .POST ("/sandboxes/:id/agent" , pxy )
174+ api .GET ("/sandboxes/:id/agent" , pxy )
175+ api .POST ("/sandboxes/:id/agent/:sid/prompt" , pxy )
176+ api .POST ("/sandboxes/:id/agent/:sid/interrupt" , pxy )
177+ api .POST ("/sandboxes/:id/agent/:sid/kill" , pxy )
178+
179+ // Filesystem
180+ api .GET ("/sandboxes/:id/files" , pxy )
181+ api .PUT ("/sandboxes/:id/files" , pxy )
182+ api .GET ("/sandboxes/:id/files/list" , pxy )
183+ api .POST ("/sandboxes/:id/files/mkdir" , pxy )
184+ api .DELETE ("/sandboxes/:id/files" , pxy )
185+
186+ // PTY
187+ api .POST ("/sandboxes/:id/pty" , pxy )
188+ api .GET ("/sandboxes/:id/pty/:sessionID" , pxy )
189+ api .POST ("/sandboxes/:id/pty/:sessionID/resize" , pxy )
190+ api .DELETE ("/sandboxes/:id/pty/:sessionID" , pxy )
191+
192+ // Timeout
193+ api .POST ("/sandboxes/:id/timeout" , pxy )
194+
195+ // Token refresh
196+ api .POST ("/sandboxes/:id/token/refresh" , pxy )
197+ } else {
198+ // Combined/worker mode: handle locally
199+ api .POST ("/sandboxes/:id/exec" , s .createExecSession )
200+ api .GET ("/sandboxes/:id/exec" , s .listExecSessions )
201+ api .GET ("/sandboxes/:id/exec/:sessionID" , s .execSessionWebSocket )
202+ api .POST ("/sandboxes/:id/exec/:sessionID/kill" , s .killExecSession )
203+ api .POST ("/sandboxes/:id/exec/run" , s .execRun )
204+
205+ api .POST ("/sandboxes/:id/agent" , s .createAgentSession )
206+ api .GET ("/sandboxes/:id/agent" , s .listAgentSessions )
207+ api .POST ("/sandboxes/:id/agent/:sid/prompt" , s .sendAgentPrompt )
208+ api .POST ("/sandboxes/:id/agent/:sid/interrupt" , s .interruptAgent )
209+ api .POST ("/sandboxes/:id/agent/:sid/kill" , s .killAgentSession )
210+
211+ api .GET ("/sandboxes/:id/files" , s .readFile )
212+ api .PUT ("/sandboxes/:id/files" , s .writeFile )
213+ api .GET ("/sandboxes/:id/files/list" , s .listDir )
214+ api .POST ("/sandboxes/:id/files/mkdir" , s .makeDir )
215+ api .DELETE ("/sandboxes/:id/files" , s .removeFile )
216+
217+ api .POST ("/sandboxes/:id/pty" , s .createPTY )
218+ api .GET ("/sandboxes/:id/pty/:sessionID" , s .ptyWebSocket )
219+ api .POST ("/sandboxes/:id/pty/:sessionID/resize" , s .resizePTY )
220+ api .DELETE ("/sandboxes/:id/pty/:sessionID" , s .killPTY )
221+
222+ api .POST ("/sandboxes/:id/timeout" , s .setTimeout )
223+ }
184224
185225 // Snapshots (pre-built declarative images)
186226 api .POST ("/snapshots" , s .createSnapshot )
0 commit comments