제가 기차에 앉아 있는 동안 서버에서 리팩토링(refactor)이 진행됩니다. 휴대폰으로 미리보기를 열어 에이전트가 방금 수정한 페이지를 보고, 수정 사항 한 가지를 입력한 뒤 휴대폰을 내려놓습니다. 두 시간이 지난 후 책상에서 노트북을 켜니 작업물이 제 작업 트리(working tree)에 있습니다: 새로운 파일들, 절반만 편집된 파일들, 제가 여전히 고쳐야 할 실패하는 테스트까지. 아무것도 커밋(commit), 푸시(push) 또는 풀(pull)되지 않았습니다.
두 개의 복사본이 존재하는 이유
코딩 에이전트는 계속 켜져 있는 기계를 필요로 하고, 저는 제 앞에 놓인 컴퓨터에 코드가 필요합니다. 다른 모든 답변들은 코드를 어딘가로 옮깁니다: 클라우드 에이전트는 저장소(repository)를 VM(Virtual Machine)으로 복제하고 사용자의 작업 복사본은 오래된 체크아웃(stale checkout)이 됩니다; 임대 서버의 tmux는 에이전트를 살아있게 유지하며 프로젝트를 서버에 남겨둡니다.
Corami는 양쪽 모두에서 프로젝트의 실제 사본을 유지합니다. 이는 서버와 노트북 모두에 있는 일반 디렉터리이며, Tailscale SSH를 통해 Mutagen으로 양방향 동기화됩니다.
빌드는 제가 있는 곳에서 실행됩니다. Mac에 연결된 휴대폰의 xcodebuild, flutter run 또는 책상 위의 브라우저가 통신하는 개발 서버: 이들은 로컬 파일, 로컬 캐시, 그리고 그들 사이에 디스크와 네트워크 파일 시스템이 없는 것을 원합니다. 마운트(mount)는 그것들을 느리게 만듭니다. 동기화된 디렉터리는 디렉터리이며 그것들이 정상적으로 작동하게 합니다.
커밋되지 않은 작업은 이동합니다. 동기화는 디스크에 있는 것을 운반합니다. 제가 절반만 작성한 마이그레이션(migration)과 파일 내의 디버그 프린트(debug print)를 남겨두고 뚜껑을 닫아도, 서버의 에이전트는 그 정확한 상태로 작업합니다. 그 수정 사항들은 같은 폴더로 돌아오며 여전히 커밋되지 않은 상태이고, 저는 어떤 결정을 내리기 전에 git diff로 그것들을 읽습니다.
제 편집기는 계속 작동합니다. 트리를 가로질러 심볼(symbol) 이름을 바꾸거나, grep으로 검색하거나, 언어 서버(language server)가 인덱싱하도록 둡니다. 이 모든 것은 로컬 파일을 가정하며, 여기서는 파일들이 로컬입니다.
서버의 에이전트, 노트북의 빌드
Harness는 사용자의 것입니다: opencode, Claude Code, Codex 등, 여러분이 이미 사용하는 것이 무엇이든 상관없이 서버에 설치됩니다. Corami가 호스트이며, harness는 서버에 설치한 모든 것입니다.
서버에서 실행되는 개발 서버는 corami serve 3000을 통해 안정적인 HTTPS 주소를 얻으며, 이 주소는 tailnet에서 접근 가능합니다. 기차 안에서 저는 그 주소를 브라우저에 열어 에이전트가 방금 만든 변경 사항을 확인하고 같은 대화창에 답장할 수 있습니다.
빌드는 더 어려운 부분인데, flutter build가 Mac이 필요하기 때문입니다. 이제 클라이언트는 프로그램과 프로젝트 이름을 지정하는 승인 다이얼로그 뒤에서 서버용 명령을 실행할 수 있습니다. 거부하면 조용히 실패하는 대신 다이얼로그에 메모를 남깁니다. 이것은 샌드박스(sandbox)가 아니라 권한 게이트(permission gate)이며, 저는 그것을 그렇게 취급하지 않을 것입니다.
설정하기
서버에서 root로 ssh 접속하여 (설정 과정에서 corami 계정이 생성됩니다):
curl -fsSL https://github.com/tapeo/corami/releases/latest/download/install.sh | sh
'server'라고 답변합니다. 노트북에서는 동일한 스크립트를 사용하고, 'client'라고 답변하면 설정이 tailnet에서 서버를 찾습니다. 그런 다음:
corami project add ~/dev/myapp
프로젝트는 서버의 ~/projects/myapp에 위치하게 되며; 노트북의 폴더는 원래 자리에 남아 동기화된 사본이 됩니다. 설정 파일은 없습니다. 의존성 및 빌드 출력물(node_modules, build, Pods)은 제외되며 각 기기가 이를 재생성합니다. 그 외 모든 것, 키와 환경(env) 파일을 포함하여 전송되므로 서버에서 수행하는 빌드나 미리보기는 노트북이 가진 것을 갖게 됩니다. 양쪽 모두가 같은 파일을 편집하고 병합을 안전하게 할 수 없을 때, 동기화는 중단되고 어떤 파일과 어느 두 경로에 문제가 있는지 보고합니다. 아무것도 덮어쓰지 않습니다.
작동하지 않는 경우
여기서 발생하는 실패는 작업 시간을 비용으로 지불하므로, 이 목록이 기능보다 더 중요합니다.
- git: 한쪽에서 rebase를 하고 다른 쪽에서 에이전트가 작업하는 경우
index.lock 충돌과 부분 적용 상태가 발생합니다. 한 번에 하나의 긴 git 작업을 수행해야 합니다.
- 대용량 바이너리: 백업이 아닌 소스 트리를 동기화합니다. 비디오나 데이터셋은 제외하세요.
- 양쪽에서 같은 파일을 삭제하는 것은 전형적인 동기화 함정입니다. Corami는 이를 충돌로 보고하며 추측하지 않습니다.
- 툴체인: Node, pnpm, Flutter 등은 실행되는 기기에 존재해야 합니다. Corami가 설치해주지는 않습니다.
현재 상태
초기 단계이며, macOS 및 Linux 클라이언트용입니다. CLI와 데몬 역할을 모두 하는 단일 바이너리이며, 모든 명령어는 JSON으로 출력되어 에이전트가 제어할 수 있습니다. 서버, 에이전트, 미리보기(previews)는 tailnet에 머무르며, 공용 인터넷에서는 아무것도 리스닝하지 않습니다.
라이선스는 FSL-1.1-ALv2입니다: 소스 코드는 이용 가능하지만 OSI 오픈 소스가 아니며, 각 릴리스는 2년 후 Apache 2.0이 됩니다.
이미 원격 기기에서 에이전트를 실행하고 있다면, 빌드 절반(build half)을 어떻게 처리하는지 궁금합니다: 마운트(mount), 두 번째 체크아웃(second checkout) 또는 이와 같은 동기화 방식 중 어떤 것을 사용하시나요?
제가 Corami를 만들었으니, 추천 사항을 고려해주시기 바랍니다.