내 작업 공간 창
원천 · framework/evomind/web/workflow-project-evo-service기준 · 릴리스 v0.12.0커밋 · 2e32c6cc파일 · docs/operations/workspace-user-window.md기준선 · 2026-11-12
사용자가 계정 메뉴에서 내 작업 공간 을 열어 전체 사용량과 프로젝트(복제)·대화 worktree·파일을 보고, 필요 없는 항목을
지우거나 전체를 초기화한다. 파일은 읽기 전용으로만 본다 — 편집·업로드·다운로드는 이 창에 없다(의 나머지).
실행기는 workspace-runner.md 의 /v1/files/{list,read,delete}·/v1/usage·/v1/workspace/reset
을 쓴다. 관리자가 남의 공간을 보는 화면은 workspace-admin.md 다.
1. 보이는 조건
섹션 제목: “1. 보이는 조건”| 조건 | 설명 |
|---|---|
WORKSPACE_ENABLED=true |
기능이 꺼져 있으면 계정 메뉴에 내 작업 공간 항목 자체가 나오지 않는다. |
WORKSPACE_RUNNER_URL·WORKSPACE_RUNNER_TOKEN |
실행기 주소·서비스 토큰. 없으면 기능이 꺼진 것으로 본다. |
그 사용자의 workspace 기능 권한 |
PermissionService.isFeatureAllowed(username, Feature.WORKSPACE). 없으면 403. |
| 로컬 계정의 활성 상태·GitLab 토큰 | WorkspaceService.availableFor — 비활성이거나 토큰·호스트가 없으면 메뉴를 숨긴다. |
GET /api/workspace/self/status 는 위 조건을 모두 만족할 때 enabled:true 와 실행 인증 상태(GitLab 계정·만료·경고)를
돌려준다. 사이드바는 이 값을 미리 받아 메뉴를 보일지 정하고, 창을 열 때 다시 받아 만료된 토큰을 반영한다.
2. API (evo-api, 인증 필요)
섹션 제목: “2. API (evo-api, 인증 필요)”모든 경로는 로그인 사용자의 홈 기준 상대 경로다. 서버는 로그인 사용자의 runner 번호로만 부르고, runner 는 그 사용자의
홈 밖을 커널 수준(os.Root)에서 거절한다 — 다른 사용자의 공간은 조회·삭제 어느 쪽으로도 드러나지 않는다.
권한이 없으면 403, 기능·runner 가 꺼져 있으면 503, 실행 중 삭제·초기화는 409, runner 호출 실패는 502 다.
| 경로 | 하는 일 |
|---|---|
GET /api/workspace/self/status |
창을 보일 수 있는지·GitLab 인증 상태 |
GET /api/workspace/self/usage |
전체 사용량 + 프로젝트·worktree 별 크기 + 묶인 대화 제목 |
POST /api/workspace/self/files/list |
{"path":"repos/group__proj"} — 폴더 목록(비우면 홈) |
POST /api/workspace/self/files/read |
{"path":"…"} — 텍스트만 내용, 바이너리·256KiB 초과는 이유만 |
POST /api/workspace/self/impact |
{"path":"…"} — 지우기 전 영향(크기·묶인 대화·커밋하지 않은 변경·push 하지 않은 커밋) |
POST /api/workspace/self/files/delete |
{"path":"…"} — 파일·폴더(재귀)·worktree·프로젝트 삭제 |
POST /api/workspace/self/reset |
내 공간 전체 초기화(계정·토큰·에이전트 ID 등록은 유지) |
사용량 응답
섹션 제목: “사용량 응답”{"bytes":3221225472,"files":120,"dirs":14,"links":0,"truncated":false,"limitBytes":10737418240, "over":false,"groupsTruncated":false, "projects":[{"folder":"group__proj","project":"group/proj","bytes":3000000000,"files":118, "conversations":[{"conversationId":"…","title":"첫 대화"}]}], "worktrees":[{"path":"work/<대화 id>/group__proj","project":"group/proj","folder":"group__proj", "conversationId":"…","conversationTitle":"첫 대화","bytes":40000000,"files":2}]}- 실행기
usage.groups(프로젝트 clone·worktree 별 크기)를 대화 연결(conversation_workspaces)과 맞물려 이름·제목을 붙인다. 연결만 있고 파일이 없는 worktree 도 0 바이트로 보여 준다. - 프로젝트가 1000개를 넘으면
groupsTruncated:true(실행기가 크기 순 상위 1000개만 준다). 전체 합계는 그대로 정확하다. - 묶인 대화는 그 사용자의 대화만 조회한다(
conversation_workspaces+conversations.user_id).
삭제 전 영향(impact)
섹션 제목: “삭제 전 영향(impact)”kind는project(repos/<폴더>) ·worktree(work/<대화 id>/<폴더>) ·conversation(work/<대화 id>) ·item(그 밖의 파일·폴더).- 크기는 사용량 묶음에서 정확히 얻는다. 묶음에 없는 폴더는 세지 않으므로
sizeKnown:false(파일이면 목록에서 크기를 찾는다). uncommitted·unpushed는 그 폴더에서 서버가 만든 인자로git status --porcelain=v1과git rev-list --count HEAD --not --remotes를 실행해 센다. 그 사용자의 실행이 진행 중이면 알 수 없으므로 null 이고busy:true다. git 을 부를 수 없거나 저장소가 아니면 null(모름)이다.- 대화(
conversation)를 지울 때는 그 아래 worktree 들의 수를 모두 더한다 — 하나라도 모르면 null.
delete는 실행기/v1/files/delete를 그대로 부른다. 파일·폴더(재귀)·링크를 지우고, worktree 폴더면 git 등록 정보도 정리한다. 홈 자체와 최상위 관리 폴더(repos·work)는 실행기가 400 으로 거절한다.- 실행 중이면 409 이고 아무것도 지우지 않는다. 성공하면 사용량을 다시 세어 응답에 담는다(다시 세지 못하면
usage:null, 화면은GET /usage로 새로 고친다). 초기화 뒤에도 같은 규칙이다.
3. 화면 동작
섹션 제목: “3. 화면 동작”- 사용량 — 상한(기본 10GiB) 대비 막대와 파일·폴더·링크 수. 넘으면 빨간 경고, 일부만 셌으면 그 사실을 함께 보여 준다.
- 프로젝트·worktree — 크기·파일 수·묶인 대화 제목을 보여 주고 행마다 삭제 버튼이 있다.
- 파일 — 홈부터 경로를 따라 내려가는 목록. 폴더는 눌러 들어가고 파일은 눌러 읽는다. 내용은 React 텍스트로만 그리므로 HTML·스크립트가 실행되지 않는다. 바이너리·큰 파일은 내용 대신 이유만, 심볼릭 링크·장치 등은 이름만 보여 준다.
- 삭제 확인 — 파일·폴더·worktree·프로젝트 어느 쪽이든 확인 창을 거친다. 창에는 종류·지울 크기·영향받는 대화· 커밋하지 않은 변경·push 하지 않은 커밋 수가 나오고, 실행 중이면 경고가 함께 나온다(그래도 시도하면 409).
- 전체 초기화 — 현재 사용량과 “저장하지 않았거나 push 하지 않은 변경이 모두 사라진다”는 경고를 담은 확인 창을 거친다. 계정·GitLab 토큰·에이전트 ID 등록은 남는다.
- 키보드만으로 열고 조작할 수 있고(계정 메뉴 → 창, 포커스 트랩·Escape), 문구는 한국어·영어 i18n 키(
workspace.self.*)다.
4. 감사
섹션 제목: “4. 감사”| 이벤트 | 남기는 것 |
|---|---|
workspace.delete |
행위자·대상 username·kind·path(요약: repos/<폴더>·work/<대화 id>/<폴더> 까지만)·bytes·files. 그 아래 파일 이름·내용은 넣지 않는다. |
workspace.reset |
행위자·대상 username·bytes·files(초기화 전). 경로·파일 이름 없음. |
5. 시험
섹션 제목: “5. 시험”cd backend && ./gradlew :app:test --tests '*WorkspaceSelfServiceTest'cd frontend && npm run build && npm run check:a11ycd runner && go test ./... # groups 계산·경로 묶음 시험- 서비스 시험: 권한 없음 403(실행기 호출 없음), 기능 꺼짐 503, 사용량 묶음 매핑(프로젝트·worktree·제목·0바이트 worktree), 경로 이탈 400, 바이너리 통과, 영향(크기·대화·git 수·busy), 삭제 409·감사 없음, 감사에 종류·크기만, 사용량 갱신 실패에도 삭제 성공 유지, 초기화 감사.
답변은 문서를 근거로 만들지만 틀릴 수 있습니다. 출처 문서를 함께 확인하세요.