Issue #348 — Phase 1 Implementation

Mini setup server + multi-select backend onboarding

Issue #348 This walkthrough covers the concrete changes in this PR.

1 What Changed

✗ Before

  • npm run dev → runs Docker mode by default
  • Fails if Docker isn’t installed
  • Step 1: “Check Backend” — just verifies connection, single backend
  • No way to run Local + Docker simultaneously

✓ After

  • npm run dev → always works (local mode, no Docker needed)
  • Step 1: “Choose Backend” — multi-select Local + Docker
  • Mini setup server bridges frontend ↔ Docker lifecycle
  • Docker is started on-demand through the UI

2 Architecture

npm run dev ├─ Vite dev server :3001 ├─ Local agent-server :18000 (always started) ├─ Automation backend :18001 ├─ Ingress proxy :8000 (routes all traffic) ├─ Mini Setup Server :18099 ← NEW │ ├─ GET /setup/status → { dockerInstalled, dockerRunning, dockerBackendRunning } │ ├─ POST /setup/docker → Start Docker backend, returns { host, port } │ └─ DELETE /setup/docker → Stop Docker backend │ └─ [spawned on demand by setup server] Docker agent-server :18002 The ingress proxy routes /setup/* to the setup server, so the browser frontend accesses it via the same origin.

Why a setup server?

3 Onboarding Flow

Step count stays at 4. The key change is Step 1:

Step 0
Choose Agent
→
changed Step 1
Choose Backend
→
Step 2
Setup LLM
→
Step 3
Say Hello
Before (CheckBackendStep)After (ChooseBackendStep)
PurposeVerify single backend is reachablePick one or both backends (Local + Docker)
LocalAlways used (if running)Always selected, always running
DockerRequires npm run dev:dockerStarted on-demand from UI via setup server
Multi-backend✗✓ Both in dropdown simultaneously

4 UI Mockup — Choose Backend

Agent Canvas — Onboarding
Step 2 of 4

Choose your backend

Select one or both — you can always change this later in Settings.

✓
Default Local Backend
Runs the agent directly on your machine. No Docker needed.
✓ Zero-config, local credentials, local tools
✗ Unsandboxed — full filesystem access
Confirmation mode enabled by default.
✓
Recommended Docker Backend
Runs inside a Docker container for isolation.
✓ Sandboxed — can’t affect host environment
✗ Requires Docker Desktop
Workspace directory
/Users/you/projects
Start Docker Backend

5 What happens when the user clicks “Next”

If only Local is selected

Nothing to do — local backend is already running since npm run dev. Proceeds immediately.

If Docker is selected

  1. User enters workspace directory and clicks “Start Docker Backend”
  2. Frontend calls GET /setup/status to check Docker availability
  3. If Docker is available, calls POST /setup/docker with { projectPath }
  4. Setup server spawns Docker container on port 18002
  5. Shows “Docker backend started” confirmation
  6. “Next” button becomes enabled once Docker is running or Local is selected

6 Setup Server API

EndpointMethodResponse
/setup/status GET { dockerInstalled, dockerRunning, dockerBackendRunning, dockerBackendPort }
/setup/docker POST Body: { projectPath } → { status, host, port, url }
/setup/docker DELETE { status: "stopped" }

All endpoints return JSON with CORS headers. The ingress proxy routes /setup/* to the setup server, so the frontend uses the same origin.

7 Files Changed

FileStatusChange
scripts/setup-server.mjs new Mini HTTP server for Docker lifecycle (~110 lines)
scripts/dev-with-automation.mjs modified Import + start setup server; add /setup route to ingress
src/api/setup-service.ts new Frontend API client for setup server endpoints
src/.../steps/choose-backend-step.tsx new Multi-select backend UI (Local + Docker)
src/.../onboarding-modal.tsx modified Swap CheckBackendStep → ChooseBackendStep
src/i18n/translation.json modified 21 new i18n keys for backend selection UI
walkthrough.html new This document — visual walkthrough of the changes

8 Key Code Snippets

scripts/setup-server.mjs — Setup Server core
// Endpoints served on 127.0.0.1:18099 GET /setup/status → isDockerInstalled() + isDockerRunning() + isDockerBackendRunning() POST /setup/docker → startDockerBackend(projectPath, { sessionApiKey }) DEL /setup/docker → stopDockerBackend() // Docker lifecycle reuses the same image + mount logic as dev-docker.mjs: docker run --rm --name agent-canvas-dev-docker-backend \ -v $PROJECT_PATH:/projects \ -v ~/.openhands:/home/openhands/.openhands \ -v ~/.claude:/home/openhands/.claude \ -p 18002:8000 \ ghcr.io/openhands/agent-server:1.22.0-python
scripts/dev-with-automation.mjs — Wiring
+ import { startSetupServer, DEFAULT_PORT as SETUP_SERVER_PORT } from "./setup-server.mjs"; // In main(): + // 7. Start setup server (lets the frontend manage Docker lifecycle) + startSetupServer({ port: SETUP_SERVER_PORT, sessionApiKey: config.sessionApiKey }); // In startIngress(): + "--route", `/setup=http://127.0.0.1:${SETUP_SERVER_PORT}`,
src/components/.../onboarding-modal.tsx — Swap step
- import { CheckBackendStep } from "./steps/check-backend-step"; + import { ChooseBackendStep } from "./steps/choose-backend-step"; <Slide index={1} currentStep={currentStep}> - <CheckBackendStep onBack={goBack} onNext={goNext} /> + <ChooseBackendStep onBack={goBack} onNext={goNext} /> </Slide>

9 Backend Comparison

Local Docker
Sandbox✗ None✓ Container
Confirmation Mode✓ On by defaultOptional
File AccessEntire filesystemMounted dir only
PrerequisitesNone (uvx only)Docker Desktop
How StartedAlways (via npm run dev)On demand (via setup server)
Port:18000:18002

Summary of Changes

Not in scope (future work): Agent auto-discovery, ACP session listing, conversation agent tags. See agent-canvas #405 and software-agent-sdk #3236.