로컬에서 실행 중인 Antigravity CLI(agy) 세션을 디스코드 DM으로 원격 조작하는 셀프 호스팅 브릿지입니다.
agy를 의사 터미널(PTY) 위에서 실행하기 때문에, 로컬 터미널에서는 평소와 완전히 동일한 TUI를 그대로 사용하면서 동시에 디스코드에서도 같은 세션을 조작할 수 있습니다.
로컬 터미널 (raw mode) ─┬─► PTY master ─► agy (TUI, TTY 인식)
│ │
디스코드 DM ────────────┘ ▼
출력 바이트
│
┌───────────────────┴────────────────┐
▼ ▼
로컬 터미널에 그대로 헤드리스 터미널 에뮬레이터(pyte)
│
화면 스냅샷 → DM 메시지를 계속 edit
- PTY 기반:
agy는 stdin/stdout이 TTY가 아니면 TUI를 띄우지 않고 1회성 print 모드로 떨어집니다. 파이프 대신 PTY를 물려 실제 터미널로 인식시킵니다. 창 크기(TIOCSWINSZ)와SIGWINCH리사이즈도 그대로 전달됩니다. - 화면 스냅샷 전송: TUI는 커서를 옮겨 화면을 계속 덧그리기 때문에 출력 바이트를 그대로 보내면 제어문자 범벅이 됩니다. 출력을
pyte터미널 에뮬레이터에 재생해 현재 화면 상태만 뽑아내고, DM 메시지 하나를 계속 edit 해서 갱신합니다. - 상태 기반 알림: 디스코드는 메시지 edit에 푸시 알림을 보내지 않습니다. 그래서 화면을 계속 edit 하기만 하면 폰에는 아무것도 울리지 않습니다. 화면을 감시해 승인 대기 진입과 작업 완료 시점에만 새 메시지를 보내 알림이 뜨게 합니다.
- 로컬 명령 가로채기: 로컬 터미널은 raw 모드라 줄 단위 입력이 없습니다. 입력 바이트가
/rc ...의 접두사인 동안만 붙잡아 두었다가, 명령이 완성되면 통째로 소비하고 아니면 즉시agy로 흘려보냅니다. 덕분에agy자체 슬래시 명령(/model등)은 아무 영향 없이 그대로 동작합니다.
- Python 3.10 이상
- Antigravity CLI(
agy)가 PATH에 설치되어 있을 것 - 디스코드 봇 토큰 및 본인 User ID (개발자 포털에서 Message Content Intent 활성화 필수)
git clone https://github.com/seohuda/discord-agy-remote.git
cd discord-agy-remote
./install.sh
source ~/.zshrc # 또는 ~/.bashrcinstall.sh는 .venv 가상환경 생성, 의존성 설치, .env 생성, 셸 alias 등록까지 처리합니다.
이후 .env를 열어 값을 채웁니다.
DISCORD_TOKEN=봇_토큰
DISCORD_USER_ID=본인_디스코드_유저_ID
선택 항목입니다.
| 변수 | 기본값 | 설명 |
|---|---|---|
DISHELL_COLS |
60 |
원격 제어 중 agy가 그릴 화면 너비. 디스코드 코드블록이 60칸 근처에서 접히기 때문에, 넓은 터미널을 그대로 보내면 구분선과 푸터가 두 줄로 밀립니다. /rc on 때 좁혔다가 /rc off 때 원래 너비로 되돌립니다. 0 이면 터미널 너비를 그대로 씁니다. 폰에서 주로 본다면 50 정도가 낫습니다. |
DISHELL_AGY_BIN |
agy |
실행할 Antigravity CLI 실행 파일 이름. PATH 에서 찾습니다. |
DISHELL_DEBUG |
(없음) | 지정한 파일에 stdin 바이트와 필터 판정을 기록합니다. 문제 진단용입니다. |
봇을 처음 만든다면 아래 순서대로 진행합니다.
- 앱 생성: Discord Developer Portal 에서
New Application을 누릅니다. - 토큰 발급: 좌측
Bot메뉴에서Reset Token을 눌러 토큰을 복사합니다. 이 값이DISCORD_TOKEN입니다. - Intent 켜기: 같은
Bot메뉴의Privileged Gateway Intents에서 Message Content Intent 를 켭니다. 이걸 켜지 않으면 봇이 DM 내용을 읽지 못해 아무것도 동작하지 않습니다. - 서버에 초대:
OAuth2→URL Generator에서SCOPES의bot과applications.commands를 함께 체크합니다. 후자가 없으면 슬래시 명령이 등록되지 않습니다. 생성된 URL 을 열어 본인 서버에 초대합니다.
슬래시 명령이 안 보일 때: 방금 초대했다면 디스코드 앱 캐시 때문입니다.
Ctrl + R(맥은Cmd + R)로 새로고침하면 자동완성 목록에 나타납니다.
/ 를 치면 뜨는 자동완성 목록에서 골라야 슬래시 명령으로 동작합니다. /key, /rc, /help 는 위 사진처럼 일반 텍스트로 쳐도 같게 동작하지만, /say 는 자동완성으로 고를 때만 명령으로 인식됩니다.
평소처럼 agy를 실행합니다. 인자도 그대로 전달됩니다 (agy -c, agy --model ...).
agy실행 직후 아래 한 줄이 보이면 래퍼를 거치고 있는 것입니다.
[Dishell] 원격 제어 대기 중 · 세션에서 /rc on
/rc 로 시작하는 입력은 agy로 넘기지 않고 래퍼가 붙잡기 때문에, agy 입력창 대신 화면 맨 아래 상태줄에 표시됩니다.
[Dishell] /rc on
여기서 백스페이스로 고치거나 Ctrl-U 로 취소할 수 있고, 엔터를 치면 명령이 실행됩니다. 이 글자들은 agy에 전혀 전달되지 않습니다.
래퍼가 붙었는지 헷갈릴 때:
/rc를 쳐보세요. 아래 상태줄에 나타나면 래퍼가 붙은 것이고, agy 입력창에 그대로 들어가면서 자동완성 메뉴가 뜨면 안 붙은 것입니다.
세션 안에서 원격 제어를 켜고 끕니다. 이 명령은 agy에 전달되지 않고 래퍼가 가로챕니다.
| 로컬 터미널 | 동작 |
|---|---|
/rc on |
디스코드 봇 연결, 원격 제어 시작 |
/rc off |
원격 제어 종료, 봇 연결 해제 |
/rc status |
현재 상태 확인 |
디스코드 DM에서 사용할 수 있는 명령입니다. /say 를 뺀 나머지는 자동완성에서 고르든 그냥 텍스트로 치든 동일하게 동작합니다.
| 디스코드 DM | 동작 |
|---|---|
| 아무 텍스트 | agy 입력창에 입력 후 전송 (여러 줄은 bracketed paste로 한 번에 입력) |
/say <텍스트> |
위와 같지만 /model 처럼 슬래시로 시작하는 내용도 그대로 입력 (자동완성 전용) |
/key esc |
특수키 전송 |
/key down down enter |
여러 키 순서대로 전송 (메뉴·승인 프롬프트 조작용) |
/rc off |
원격에서 원격 제어 종료 |
/rc status |
세션 상태 확인 |
/help |
사용법과 키 목록 |
//텍스트 |
앞 슬래시 하나를 벗겨 그대로 전송 |
/model 처럼 agy 자신의 슬래시 명령은 가로채지 않고 그대로 전달됩니다.
사용 가능한 키: enter esc tab shift-tab space backspace delete up down left right home end pageup pagedown ctrl-a~ctrl-w y n
모델 선택이나 권한 승인처럼 화면에 목록이 뜨면, 커서(>)를 옮긴 뒤 확정하는 식으로 조작합니다. 화면 하단 푸터가 어떤 키를 쓰는지 알려줍니다.
> Gemini 3.1 Pro
Claude Opus 4.6
GPT-5.2
↑/↓ Navigate · enter Select · esc Go Back
두 칸 아래 항목을 고르려면 이동과 확정을 한 번에 보냅니다.
/key down down enter
승인 프롬프트도 같은 방식입니다. /key down enter 로 두 번째 선택지를, /key esc 로 취소를 보냅니다.
봇이 먼저 DM을 보냅니다. 말을 걸어야 응답하는 구조가 아닙니다.
진행 중인 화면은 메시지 하나를 계속 edit 해서 조용히 갱신하고, 아래 두 순간에만 새 메시지를 보내 푸시 알림이 뜨게 합니다.
| 상황 | 알림 |
|---|---|
| agy가 승인/선택 프롬프트를 띄움 | ⏸️ 입력 대기 중 |
| 작업이 끝나고 화면이 3초 이상 조용함 | ✅ 작업 완료 |
덕분에 노트북을 켜둔 채 나가 있어도, 승인이 필요한 순간에만 폰이 울립니다.
감지는 agy가 실제로 출력하는 문구를 기준으로 합니다. Do you want to proceed?, Allow access to this file?, Do you trust the contents of this project? 같은 알려진 문구에 더해, 선택지 하단의 ↑/↓ Navigate · enter Confirm 푸터를 일반 신호로 사용하기 때문에 목록에 없는 새 프롬프트도 잡힙니다. 작업 중 여부는 esc to interrupt 표시로 판단합니다.
- 원격 입력은 DM이면서
DISCORD_USER_ID와 정확히 일치하는 발신자인 경우에만 받아들입니다. 그 외 모든 메시지는 무시합니다. DISCORD_USER_ID가 비어 있으면0이 되어 아무도 통과하지 못합니다(fail-closed).- 원격 제어는 사실상 로컬 셸 접근과 동등한 권한입니다.
--dangerously-skip-permissions와 함께 쓰면 승인 없이 도구가 실행되므로 주의하세요. - 봇 토큰은
.env에 두며.gitignore에 포함되어 있습니다.
봇과 같은 서버에 한 번은 함께 있어야 합니다. 디스코드는 상호 서버가 없는 상대에게 봇이 DM 보내는 것을 막습니다(403). 본인만 있는 서버를 하나 만들어 봇을 초대해두면 됩니다.
/rc on은 연결 직후 실제로 DM을 한 번 보내보고, 실패하면 원인을 로컬 터미널에 출력한 뒤 원격 제어를 켜지 않습니다.
agy에는 자체 원격 제어 모드(--remote-control)가 내장되어 있으며, 이는 Antigravity 자체 프록시 서버를 경유하는 클라우드 방식입니다. 이 프로젝트는 그 경로를 쓰지 않고 본인 디스코드 봇만으로 자체 호스팅한다는 점이 다릅니다.
.venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest푸시와 PR 마다 파이썬 3.10 / 3.12 에서 같은 테스트가 돕니다(.github/workflows/tests.yml).
| 모듈 | 역할 |
|---|---|
dishell/terminal.py |
PTY 생성, 로컬 터미널 raw 모드, 창 크기 전달 |
dishell/screen.py |
pyte 기반 화면 에뮬레이션 및 디스코드 렌더링 |
dishell/alerts.py |
승인 대기 / 작업 완료 전이 감지 |
dishell/launch.py |
실제 agy 실행 파일 확인 및 자기 참조 차단 |
dishell/localcmd.py |
로컬 raw 입력에서 /rc 명령 가로채기 |
dishell/commands.py |
디스코드 메시지 → 동작 파싱 |
dishell/bot.py |
디스코드 클라이언트 및 발신자 인증 |
dishell/manager.py |
전체 배선, 봇 수명주기, 화면 발행 루프 |
- 긴 출력은 화면 위로 흘러간 스크롤 히스토리까지 모아 최대 4개 메시지로 나눠 보냅니다. 그보다 많으면 가장 오래된 상단이 생략됩니다.
- 색상은 전달되지 않습니다. 코드블록 안 단색 텍스트로만 표시됩니다.
- 화면 갱신은 1.5초 간격입니다. 디스코드 API Rate Limit 제한을 피하기 위한 값입니다.
- 봇 수명이 agy 프로세스에 묶여 있습니다. agy가 떠 있지 않으면 디스코드에서 새 세션을 시작할 수 없습니다. 반드시 로컬에서
agy실행 후/rc on을 입력해야 합니다. - 알림 감지는 화면 문구 기반이므로, agy가 UI 문구를 변경하면
dishell/alerts.py의 마커를 갱신해야 할 수 있습니다.
MIT

