Skip to content

Commit 4bf55c4

Browse files
Remote Agentclaude
andcommitted
refactor: replace editor proxy with on-demand code-server on direct port
Remove the 305-line HTTP/WebSocket reverse proxy and per-session code-server management. Instead, expose a single code-server instance on its own port (default 8080) with on-demand lifecycle: - Starts only when a user requests the editor (zero memory when idle) - Auto-shuts down after configurable idle timeout (default 10 min) - Opens directly in a new browser tab — no iframe, no proxy Config: CODE_SERVER_URL (Tailscale IP), CODE_SERVER_PORT, CODE_SERVER_IDLE_TIMEOUT Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a5e0b12 commit 4bf55c4

16 files changed

Lines changed: 236 additions & 825 deletions

docker/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,9 @@ LABEL org.opencontainers.image.version=$VERSION
170170
LABEL org.opencontainers.image.source="https://github.com/mrtcmn/remote-agent"
171171
LABEL org.opencontainers.image.licenses="MIT"
172172

173-
# Expose port
173+
# Expose ports (API + code-server)
174174
EXPOSE 5100
175+
EXPOSE 8080
175176

176177
# Switch back to root for entrypoint (entrypoint drops to agent after fixing permissions)
177178
# Entrypoint (--chmod eliminates ghost layer from separate chmod)

docker/docker-compose.dev.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ services:
2626
ports:
2727
- "127.0.0.1:${PORT:-5100}:5100"
2828
- "127.0.0.1:${DEBUG_PORT:-6499}:6499" # Bun inspector port
29+
- "0.0.0.0:${CODE_SERVER_PORT:-8080}:8080"
2930
depends_on:
3031
db:
3132
condition: service_healthy
@@ -71,6 +72,10 @@ services:
7172
- CORS_ORIGIN=*
7273
- PORT=5100
7374

75+
# Code Server
76+
- CODE_SERVER_PORT=${CODE_SERVER_PORT:-8080}
77+
- VITE_CODE_SERVER_URL=${CODE_SERVER_URL:-}
78+
7479
# Debug settings
7580
- BUN_DEBUG_QUIET_LOGS=0
7681
stdin_open: true

docker/docker-compose.prod.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ services:
2828
container_name: remote-agent
2929
ports:
3030
- "127.0.0.1:${PORT:-5100}:5100"
31+
- "0.0.0.0:${CODE_SERVER_PORT:-8080}:8080"
3132
depends_on:
3233
db:
3334
condition: service_healthy
@@ -70,6 +71,10 @@ services:
7071
- APP_VERSION=${IMAGE_TAG:-latest}
7172
- CORS_ORIGIN=${CORS_ORIGIN:-*}
7273
- NODE_ENV=production
74+
75+
# Code Server (VS Code in the browser)
76+
- CODE_SERVER_PORT=${CODE_SERVER_PORT:-8080}
77+
- VITE_CODE_SERVER_URL=${CODE_SERVER_URL:-}
7378
restart: unless-stopped
7479
healthcheck:
7580
test: ["CMD", "curl", "-f", "http://localhost:5100/health"]

docker/docker-compose.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ services:
2626
container_name: remote-agent
2727
ports:
2828
- "127.0.0.1:${PORT:-5100}:5100"
29+
- "0.0.0.0:${CODE_SERVER_PORT:-8080}:8080"
2930
depends_on:
3031
db:
3132
condition: service_healthy
@@ -69,6 +70,10 @@ services:
6970
- APP_VERSION=${IMAGE_TAG:-dev}
7071
- CORS_ORIGIN=${CORS_ORIGIN:-*}
7172
- NODE_ENV=${NODE_ENV:-production}
73+
74+
# Code Server (VS Code in the browser)
75+
- CODE_SERVER_PORT=${CODE_SERVER_PORT:-8080}
76+
- VITE_CODE_SERVER_URL=${CODE_SERVER_URL:-}
7277
restart: unless-stopped
7378
healthcheck:
7479
test: ["CMD", "curl", "-f", "http://localhost:5100/health"]

docker/entrypoint.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,6 @@ fi
8383
echo "Environment ready"
8484
echo ""
8585

86+
8687
# Drop privileges and execute command as agent user
8788
exec gosu agent "$@"

packages/api/src/index.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { Elysia } from 'elysia';
22
import { cors } from '@elysiajs/cors';
33
import { staticPlugin } from '@elysiajs/static';
4-
import { api, internalRoutes, editorProxyRoutes } from './routes';
4+
import { api, internalRoutes } from './routes';
55
import { terminalWebsocketRoutes } from './routes/terminal-websocket';
66
import { previewWebsocketRoutes } from './routes/preview-websocket';
77
import { notificationService } from './services/notification';
88
import { terminalService } from './services/terminal';
99
import { browserPreviewService } from './services/browser-preview';
10-
import { codeEditorService } from './services/code-editor';
10+
import { codeServerManager } from './services/code-server/code-server.service';
1111
import { originsService } from './services/origins';
1212
import { seedTestUser } from './auth/seed';
1313

@@ -27,6 +27,7 @@ const CLIENT_ENV_KEYS = [
2727
'VITE_FIREBASE_APP_ID',
2828
'VITE_FIREBASE_MEASUREMENT_ID',
2929
'VITE_FIREBASE_VAPID_KEY',
30+
'VITE_CODE_SERVER_URL',
3031
];
3132

3233
function buildEnvScript(): string {
@@ -44,7 +45,6 @@ const indexHtml = rawHtml.replace('<head>', `<head>\n ${buildEnvScript()}`);
4445
await originsService.initialize();
4546
await notificationService.initialize();
4647
await terminalService.initialize();
47-
await codeEditorService.initialize();
4848

4949
// Seed test user
5050
await seedTestUser();
@@ -73,9 +73,6 @@ const app = new Elysia()
7373
// Browser preview WebSocket routes
7474
.use(previewWebsocketRoutes)
7575

76-
// Code editor proxy (outside /api prefix)
77-
.use(editorProxyRoutes)
78-
7976
// Health check
8077
.get('/health', () => ({
8178
status: 'ok',
@@ -128,23 +125,22 @@ Endpoints:
128125
- API: http://localhost:${PORT}/api
129126
- Terminal: ws://localhost:${PORT}/ws/terminal/:terminalId
130127
- Preview: ws://localhost:${PORT}/ws/preview/:previewId
131-
- Editor: http://localhost:${PORT}/editor-proxy/:editorId/
132128
- Health: http://localhost:${PORT}/health
133129
- UI: http://localhost:${PORT}
134130
`);
135131

136132
// Graceful shutdown
137133
process.on('SIGTERM', async () => {
138134
console.log('Shutting down...');
139-
await codeEditorService.shutdown();
135+
await codeServerManager.shutdown();
140136
await browserPreviewService.shutdown();
141137
await notificationService.shutdown();
142138
process.exit(0);
143139
});
144140

145141
process.on('SIGINT', async () => {
146142
console.log('Shutting down...');
147-
await codeEditorService.shutdown();
143+
await codeServerManager.shutdown();
148144
await browserPreviewService.shutdown();
149145
await notificationService.shutdown();
150146
process.exit(0);

0 commit comments

Comments
 (0)