feat(phase21): real Docker Swarm management — live nodes, services, tasks, host shell, agent deployment
## What's implemented
### Go Gateway — New /api/swarm/* endpoints (handlers.go + docker/client.go + db.go)
- GET /api/swarm/info — swarm state, manager address, join tokens
- GET /api/swarm/nodes — live node list (hostname, IP, CPU, RAM, role, labels)
- POST /api/swarm/nodes/{id}/label — add/update node label
- POST /api/swarm/nodes/{id}/availability — set node availability (active|pause|drain)
- GET /api/swarm/services — all swarm services with replica counts
- POST /api/swarm/services/create — deploy a new agent as a swarm service
- GET /api/swarm/services/{id}/tasks — tasks per service (which node runs which replica)
- POST /api/swarm/services/{id}/scale — scale replicas
- GET /api/swarm/join-token — worker/manager join command with token + manager addr
- POST /api/swarm/shell — execute commands on the HOST via nsenter PID 1
### Docker client (client.go)
- ListServices, GetService, ScaleService, ListServiceTasks, CreateAgentService
- AddNodeLabel, UpdateNodeAvailability (patch node spec via Docker API)
- ExecOnHost (nsenter -t 1 → falls back to container scope)
### DB persistence (db.go)
- UpsertSwarmNodes — stores live node state to swarmNodes table
- UpsertSwarmTokens / GetSwarmTokens — persist join tokens
- Startup goroutine in main.go syncs tokens to DB on gateway start
### Node.js tRPC wrappers (routers.ts + gateway-proxy.ts)
- nodes.swarmInfo, nodes.list, nodes.services, nodes.serviceTasks
- nodes.scaleService, nodes.joinToken, nodes.execShell
- nodes.addNodeLabel, nodes.setAvailability, nodes.deployAgentService
### Frontend — Nodes.tsx (complete rewrite)
- Real swarm overview cards (nodes, managers, services, running tasks)
- Join token cards with copy button for worker & manager tokens
- Node cards with inline availability selector (active/pause/drain) + add-label form
- Services table with Scale dialog + Tasks drawer (replica → node mapping)
- Deploy Agent dialog (image, replicas, env vars, published port)
- Host Shell tab with command history and quick-command buttons
### docker-compose.yml
- gateway now runs with privileged: true + pid: host
→ nsenter can access the host PID namespace for real host-level shell execution
## Verified end-to-end
- GET /api/swarm/info returns manager addr + join tokens ✓
- GET /api/swarm/nodes returns node wsm (2 cores, 3.9 GB) ✓
- POST /api/swarm/services/create → deployed goclaw-test-agent (2 replicas) ✓
- GET /api/swarm/services/{id}/tasks returns task list with nodeId ✓
- POST /api/swarm/services/{id}/scale → scale to 0 ✓
- POST /api/swarm/shell {command:'docker node ls'} → real host output ✓
- tRPC chain: browser → control-center → gateway → docker.sock ✓