From db6cd838b1a2f7cd7cb799fd5085bb62024b9a6c Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Sun, 19 Jul 2026 19:28:13 +0200 Subject: [PATCH] feat(docker): one-click "Run in Docker" case creation + Export button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New POST /api/cases/docker-quickcreate: creates a normal case (folder in CASES_DIR, scaffolded CLAUDE.md + hooks) AND links it to a hardened container with default settings, auto-provisioning a shared `default` docker host — the user never touches host/image/network fields. - Create New tab gains a "Run in isolated Docker container" checkbox; on submit it calls docker-quickcreate then auto-starts a claude session inside the container. - Case Manage list gains an Export (full-image) button per docker case. - SSE listeners for docker:exportComplete/exportFailed toast + refresh the exports list. Verified end-to-end on the live instance: one-click create put the case in ~/codeman-cases/, auto-created the default host, launched claude in the container; export button produces a bundle; checkbox + button render (Playwright). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/web/public/app.js | 19 ++++++++ src/web/public/index.html | 4 ++ src/web/public/session-ui.js | 22 +++++++++- src/web/routes/case-routes.ts | 81 +++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/web/public/app.js b/src/web/public/app.js index c27c7f48..8036f532 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -1433,6 +1433,25 @@ class CodemanApp { for (const event of [SSE_EVENTS.SESSION_CREATED, SSE_EVENTS.SESSION_DELETED]) { addListener(event, () => this._onSessionListMaybeChanged()); } + + // Docker export/import: toast + refresh the Manage-tab exports list on completion. + addListener(SSE_EVENTS.DOCKER_EXPORT_COMPLETE, (e) => { + try { + const d = e.data ? JSON.parse(e.data) : {}; + this.showToast(`Docker export ready: ${d.bundle} (${Math.round((d.sizeBytes || 0) / 1e6)} MB)`, 'success'); + this.refreshDockerExports?.(); + } catch (err) { + console.error('[SSE] docker export complete:', err); + } + }); + addListener(SSE_EVENTS.DOCKER_EXPORT_FAILED, (e) => { + try { + const d = e.data ? JSON.parse(e.data) : {}; + this.showToast(`Docker export failed: ${d.error || 'unknown error'}`, 'error'); + } catch (err) { + console.error('[SSE] docker export failed:', err); + } + }); } // ═══════════════════════════════════════════════════════════════ diff --git a/src/web/public/index.html b/src/web/public/index.html index db780445..d61b2fc7 100644 --- a/src/web/public/index.html +++ b/src/web/public/index.html @@ -1864,6 +1864,10 @@ +
+ + One click: creates the case folder AND a hardened container with default settings, then starts the session inside it. Requires the base image (node scripts/build-agent-image.mjs). +
+ ${ + c.location === 'docker' + ? `` + : '' + }