From 5b08cca69b2681de24415e6248113386b7c26df7 Mon Sep 17 00:00:00 2001 From: iliya Date: Fri, 27 Feb 2026 21:04:11 +0200 Subject: [PATCH] refactor: move Docker files to docker/ and CHANGELOG to docs/ --- .github/SECURITY.md | 4 +- .dockerignore => docker/.dockerignore | 0 Dockerfile => docker/Dockerfile | 0 .../docker-compose.yml | 6 +- CHANGELOG.md => docs/CHANGELOG.md | 0 .../services/team/TeamAgentToolsInstaller.ts | 1 + src/main/services/team/TeamInboxWriter.ts | 1 + .../team/dialogs/CreateTeamDialog.tsx | 71 ++++++++++++++----- 8 files changed, 62 insertions(+), 21 deletions(-) rename .dockerignore => docker/.dockerignore (100%) rename Dockerfile => docker/Dockerfile (100%) rename docker-compose.yml => docker/docker-compose.yml (87%) rename CHANGELOG.md => docs/CHANGELOG.md (100%) diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 3ea3b06c..c23e4b37 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -26,11 +26,11 @@ In standalone mode (Docker or `node dist-standalone/index.cjs`), the auto-update For maximum trust, run the Docker container with `--network none`: ```bash -docker build -t claude-agent-teams-ui . +docker build -t claude-agent-teams-ui -f docker/Dockerfile . docker run --network none -p 3456:3456 -v ~/.claude:/data/.claude:ro claude-agent-teams-ui ``` -Or with Docker Compose, uncomment `network_mode: "none"` in `docker-compose.yml`. +Or with Docker Compose, uncomment `network_mode: "none"` in `docker/docker-compose.yml`. ## IPC & Input Validation diff --git a/.dockerignore b/docker/.dockerignore similarity index 100% rename from .dockerignore rename to docker/.dockerignore diff --git a/Dockerfile b/docker/Dockerfile similarity index 100% rename from Dockerfile rename to docker/Dockerfile diff --git a/docker-compose.yml b/docker/docker-compose.yml similarity index 87% rename from docker-compose.yml rename to docker/docker-compose.yml index 28891ad7..db820738 100644 --- a/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,7 +2,7 @@ # Claude Agent Teams UI — Docker Compose # # Quick start: -# docker compose up +# docker compose -f docker/docker-compose.yml up # # Then open http://localhost:3456 in your browser. # @@ -14,7 +14,9 @@ services: claude-agent-teams-ui: - build: . + build: + context: .. + dockerfile: docker/Dockerfile ports: - "3456:3456" volumes: diff --git a/CHANGELOG.md b/docs/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to docs/CHANGELOG.md diff --git a/src/main/services/team/TeamAgentToolsInstaller.ts b/src/main/services/team/TeamAgentToolsInstaller.ts index 60b3b767..74cb5f60 100644 --- a/src/main/services/team/TeamAgentToolsInstaller.ts +++ b/src/main/services/team/TeamAgentToolsInstaller.ts @@ -413,6 +413,7 @@ function sendInboxMessage(paths, teamName, flags) { : String(Date.now()) + '-' + String(Math.random()); const payload = { from, + to, text, timestamp: nowIso(), read: false, diff --git a/src/main/services/team/TeamInboxWriter.ts b/src/main/services/team/TeamInboxWriter.ts index 3fa5badf..cfb606be 100644 --- a/src/main/services/team/TeamInboxWriter.ts +++ b/src/main/services/team/TeamInboxWriter.ts @@ -15,6 +15,7 @@ export class TeamInboxWriter { const payload: InboxMessage = { from: request.from ?? 'user', + to: request.member, text: request.text, timestamp: new Date().toISOString(), read: false, diff --git a/src/renderer/components/team/dialogs/CreateTeamDialog.tsx b/src/renderer/components/team/dialogs/CreateTeamDialog.tsx index f60aaa07..9aea9e3e 100644 --- a/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +++ b/src/renderer/components/team/dialogs/CreateTeamDialog.tsx @@ -142,6 +142,28 @@ function buildMembers(members: MemberDraft[]): TeamCreateRequest['members'] { .filter((member): member is NonNullable => member !== null); } +// eslint-disable-next-line security/detect-unsafe-regex -- kebab-case pattern is linear, no ReDoS +const TEAM_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; +const MEMBER_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/; + +function validateTeamNameInline(name: string): string | null { + const trimmed = name.trim(); + if (!trimmed) return null; + if (!TEAM_NAME_RE.test(trimmed) || trimmed.length > 64) { + return 'Use kebab-case [a-z0-9-], max 64 chars'; + } + return null; +} + +function validateMemberNameInline(name: string): string | null { + const trimmed = name.trim(); + if (!trimmed) return null; + if (!MEMBER_NAME_RE.test(trimmed)) { + return 'Start with alphanumeric, use only [a-zA-Z0-9._-], max 128 chars'; + } + return null; +} + function validateRequest( request: TeamCreateRequest, options?: { requireCwd?: boolean } @@ -692,6 +714,8 @@ export const CreateTeamDialog = ({ /> {existingTeamNames.includes(teamName.trim()) ? (

Team name already exists

+ ) : validateTeamNameInline(teamName) ? ( +

{validateTeamNameInline(teamName)}

) : fieldErrors.teamName ? (

{fieldErrors.teamName}

) : null} @@ -727,20 +751,27 @@ export const CreateTeamDialog = ({ borderLeftColor: memberColorSet.border, }} > - updateMemberName(member.id, event.target.value)} - placeholder="member-name" - style={ - member.name.trim() - ? { - color: memberColorSet.text, - } - : undefined - } - /> +
+ updateMemberName(member.id, event.target.value)} + placeholder="member-name" + style={ + member.name.trim() + ? { + color: memberColorSet.text, + } + : undefined + } + /> + {validateMemberNameInline(member.name) ? ( +

+ {validateMemberNameInline(member.name)} +

+ ) : null} +