democut
v1.0.31
Published
DemoCut 에이전트 CLI — device flow 로그인(RFC 8628) + 영상 생성
Maintainers
Readme
democut
DemoCut(democut) 에이전트 인증 CLI — OAuth2 Device Flow(RFC 8628). Higgsfield 의
auth login 과 같은 방식으로, 에이전트가 한 번 로그인하면 워크스페이스 스코프 토큰을 받아
이후 호출에 Bearer 가 자동으로 붙는다.
설치
npm install -g democut # 전역 설치 (선택)
npx -y democut@latest auth login # 설치 없이 바로 (@latest 필수 — 아래 MCP 절 참고)사용
아래 예시는 전역 설치한 경우다. npx 로 쓰고 있다면 democut 을
npx -y democut@latest 로 바꿔 읽으면 된다.
democut auth login # device flow 로 로그인 → 토큰 저장
democut auth status # 현재 정체/워크스페이스/티어
democut auth logout
# 헤드리스(서버/CI/에이전트): 브라우저 안 열고 코드+URL 만 출력
democut auth login --no-browser
# 기계 판독(래퍼가 코드를 Slack 등에 게시):
democut auth login --json--base-url <url> 또는 DEMOCUT_BASE_URL 로 서버를 지정한다(기본: prod).
동작
auth login이 서버에 device authorization 요청 →user_code+ 승인 URL 수신.- 데스크톱이면 브라우저 자동 오픈, 헤드리스면 코드+URL 출력 → 사람이 브라우저에서 로그인하고 워크스페이스(조직)를 선택해 승인.
- CLI 가 토큰 엔드포인트를 폴링 → 승인되면
access/refresh토큰을~/.democut/credentials.json(권한 600)에 저장. - 이후
access(1h)는 만료 시refresh(장수명)로 자동 갱신.
토큰은 승인한 사람의 워크스페이스와 플랜(tier/role) 으로 스코프된다 — 워크스페이스마다 각자의 에이전트를 붙일 수 있는 멀티테넌트 구조.
명령 — MCP 도구와 같은 이름
democut mcp 가 에이전트에게 주는 도구를 터미널·스크립트에서도 그대로 부른다. 이름은 도구 이름의
밑줄만 하이픈으로 바꾼 것이고, 밑줄·democut_ 접두사 형태도 그대로 받는다.
democut_estimate_cuts (MCP 도구)
democut estimate-cuts (CLI — 같은 인자, 같은 결과)명령·인자·설명은 손으로 쓰지 않는다. 서버의 도구 표(GET /api/meta/tools)를 빌드 때 받아
생성하므로 계약과 어긋날 자리가 없다.
democut --help # 묶음별 명령 목록
democut help export # 한 묶음만 자세히
democut estimate-cuts --help # 그 명령의 인자인자 주는 법
플래그로 주거나, MCP 호출 본문을 통째로 넘긴다.
# 플래그 — 사람이 치기 좋다
democut estimate-cuts --slug demo --items '[{"cut_id":"c1","mode":"ai_video"}]'
# 인자 객체 그대로 — 에이전트·스크립트가 MCP 호출을 그대로 옮길 때
democut estimate-cuts --args-json '{"slug":"demo","items":[…]}'
echo '{"slug":"demo","items":[…]}' | democut estimate-cuts --args-json @-
democut estimate-cuts --args-json @payload.json둘을 함께 주면 플래그가 이긴다(통째로 준 본문 위에 한 값만 덮어쓰는 것이 자연스럽다).
불리언은 값 없이 켜고(--transcribe) --no- 로 끈다. 배열·객체 인자는 JSON 문자열이다.
모르는 인자는 조용히 무시하지 않고 거부한다 — 삼키면 넣은 값이 오류 없이 사라진다.
출력
기본은 사람이 읽는 문장이고, --json 은 성공·실패를 같은 모양으로 stdout 에 낸다.
democut get-storyboard --slug demo --json
# {"ok":true,"text":"…","data":{…}} ← data 는 MCP 의 structuredContent 와 같은 값실패도 stdout 에 같은 모양으로 나온다(파이프로 읽는 쪽이 두 경로를 합치지 않아도 되게). 실패라는 사실은 종료 코드가 말한다.
비용 승인
돈이 나가는 명령은 승인 없이 진행하지 않는다. 대상은 계약이 정한다(도움말의 [과금]·[소액]).
# 대화형 — y/N 을 묻는다
democut make-cuts --slug demo --items '[…]' --confirmed-total-krw 1200 --rate-version 3
# 비대화형(CI·에이전트) — --yes 가 없으면 거부한다
democut make-cuts … --yes승인 전에는 요청을 보내지 않는다. 물어보기만 하고 이미 제출했으면 게이트가 무의미하다.
종료 코드
| 코드 | 뜻 | 대응 | |---|---|---| | 0 | 성공 | — | | 1 | 일반 오류 | 미로그인·사용자 취소·알 수 없음 | | 2 | 고쳐서 재시도 가능 | 인자 오류·승인 누락. 과금 없음 | | 3 | 일시적 오류 | 그대로 재시도해도 안전하다 | | 4 | 업스트림 거부 | 재시도하면 재과금될 수 있다 — 자동 재시도 금지 | | 5 | 대기 시한 초과 | 실패가 아니다. 작업은 서버에서 계속된다 — 재제출 금지 |
2~4 는 서버가 알려 준 재시도 안전성을 그대로 옮긴 것이다. 스크립트가 코드만 보고 「다시 걸어도 되는가」 를 판단할 수 있어야 재제출로 두 번 과금되지 않는다.
가이드
democut guide # 서버가 배포하는 에이전트 규약 원문(로그인 불필요)
democut init-agent # 그 규약을 레포의 CLAUDE.md 에 블록으로 써 준다(멱등)개발
npm install
npm run typecheck
npm test
npm run build # dist/cli.js (bin: democut) 번들MCP 서버 (에이전트에 도구로 붙이기)
democut mcp 는 stdio MCP 서버다. Claude Code·Cursor 등 MCP 클라이언트에 붙이면
에이전트가 도구로 직접 영상을 만든다.
claude mcp add democut -- npx -y democut@latest mcpnpx 를 쓰는 이유는 전역 설치가 연결의 선행 조건이 되지 않게 하기 위해서다.
설치가 빠지면 등록은 성립하는데(등록은 바이너리 존재를 검사하지 않는다) 연결만 실패하고,
그 실패는 클라이언트의 Failed to reconnect … 한 줄로만 드러난다 — 서버가 뜨지 못하므로
진단을 낼 주체가 없다. 자격증명은 패키지가 아니라 ~/.democut/credentials.json 에 있어
실행 방식을 바꿔도 로그인 상태는 그대로다. 전역 설치는 선택이다.
★ @latest 를 빼지 마라. -y 는 설치 확인 프롬프트를 자동 승인할 뿐 버전을 다시
해석하지 않는다 — 태그를 빼면 ~/.npm/_npx 캐시에 남은 옛 버전이 그대로 뜬다
(2026-08-31 실측: 게시본이 0.4.0 인데 캐시는 0.3.0). 그 스큐가 실제로 사고를 냈다. 서버가
project_slug 를 필수화한 뒤 게시본이 갱신되기까지 2시간 45분 동안 stdio 사용자의 영상
생성이 전부 422 였고, 원격 HTTP 사용자는 아무 영향이 없었다. 대가로 실행할 때마다
레지스트리를 한 번 보므로 npm 에 닿지 못하면 서버가 아예 뜨지 않는데, 조용히 옛 도구를
쓰는 것보다 큰 소리로 안 뜨는 편이 낫다(레지스트리가 막힌 사내망이면 아래 원격 HTTP 나
전역 설치를 쓴다).
도구 목록은 서버의 계약 표에서 생성된다(GET /api/meta/tools) — 수를 여기 적지 않는 이유는 표가 늘면 이 문장이 곧 거짓이 되기 때문이다. 목록과 인자는
democut --help 로 보는 것과 같다 — 같은 표에서 나오기 때문이다.
대부분은 조회·견적이라 부작용이 없고, 돈이 나가는 것은 계약이 charge·small 로 표시한
소수뿐이다(make_cuts·make_export·make_thumbnails·make_cut_image·make_character_photo·
apply_proposal·propose·generate_youtube_meta).
비용 승인이 프로토콜에 맞게 바뀐다
CLI 는 TTY 에 y/N 을 물어 사람을 막지만 MCP 에는 물어볼 화면이 없다. 대신 두 겹이다:
- 호스트의 도구 승인 — Claude Code 등이 도구 호출 전에 사용자에게 확인을 받는다. 우리가 제어할 수 없으므로 이것만 믿지 않는다.
confirmed_total_krw재확인 — 생성 도구는 호출자가 직접 본 견적 금액과rate_version을 다시 실어 보내야 하고, 서버가 방금 계산한 값과 대조해 어긋나면 409QUOTE_MISMATCH로 거부한다 (그때 과금은 0이고 새 견적을 준다). 모델이 견적을 건너뛰고 바로 만드는 것을 막고, 사람에게 보여준 금액과 실제 청구액이 갈라지지 않게 한다.
stdio 와 원격 HTTP — 어느 쪽을 언제 쓰나
둘은 대체가 아니라 병존이고, 도구 이름·인자도 같다. 고르는 기준은 취향이 아니라 클라이언트가 도는 자리다.
| | 사람이 쓰는 클라이언트(노트북의 Claude Code·Cursor…) | 헤드리스·자동화 호스트(게이트웨이·CI·서버 박스) |
|---|---|---|
| 권장 | 원격 HTTP | stdio (이 CLI) |
| 로그인 | 붙는 순간 브라우저가 열려 끝난다 | democut auth login --no-browser 1회 (device flow, RFC 8628) |
| 브라우저 없는 자리 | 불가 — 비대화형에서는 OAuth 흐름이 안 돈다 | 가능 — 코드를 다른 기기에서 승인 |
| 사전 준비 | 아래 두 옵션을 그대로 복사 | 없음 — redirect URI 등록도 브라우저도 불필요 |
| 버전 스큐 | 없다 — 띄울 로컬 바이너리가 없다 | @latest 로 막는다(위 참고) |
| 산출물 | 만료되는 링크로만 받는다 | --output 으로 디스크에 저장된다 |
헤드리스에서 갈리는 것은 편의가 아니라 가능/불가능이다. 원격 HTTP 의 OAuth 는 호스트가
브라우저를 띄워야 시작되는데, Claude Code 문서가 비대화형에서는 그 흐름을 돌릴 수 없다고
명시한다 — "In non-interactive mode there's no /mcp panel, so Claude Code can't run the
OAuth flow for you." 사람이 대화형 세션에서 미리 인증해 두는 것(claude mcp login)이
유일한 우회다. 거기에 Clerk 이 loopback redirect 의 포트를 정확 일치로 요구하므로 그 호스트가
쓸 포트의 redirect URI 도 OAuth 애플리케이션에 미리 등록해야 한다.
stdio 는 device flow(RFC 8628)라 redirect URI 자체가 없고, --no-browser 가 코드와 URL 을
출력하면 사람이 다른 기기에서 승인한다 — 그래서 브라우저가 없는 자리에서도 성립한다.
claude mcp add --transport http --client-id 7ovjLP6E85qLNAyq --callback-port 33418 \
democut https://democut.ai/mcp두 옵션은 생략할 수 없고 임의로 바꿀 수도 없다. --client-id 는 이 서버가 아무
클라이언트나 받지 않기 때문이고(동적 클라이언트 등록(DCR)을 일부러 꺼 두었다 —
켜면 아무나 등록해 통과하는 confused-deputy 표면이 열린다), --callback-port 는 그 포트의
redirect URI 가 사전 등록돼 있어야 하기 때문이다. 미등록이면 Clerk 이 400
redirect_uri … does not match … pre-registered redirect urls 로 거부한다. 위 두 값은
공개값이고 웹 문서(https://democut.ai/docs/agents)가 같은 쌍을 안내한다. 설계 근거의
SSOT 는 apps/api/app/mcp/auth.py docstring.
⚠️ "클라이언트 설정 파일에 토큰이 남지 않는다" 는 stdio 의 고유 장점이 아니다. 이 문서가 한동안 그렇게 적고 있었는데 사실이 아니다 — 원격 HTTP 등록도 설정에는 공개값 둘(
clientId·callbackPort)만 남고 액세스 토큰은 호스트의 자격증명 저장소에 있다(2026-08-31 실측). 두 전송이 같으므로 선택 근거가 되지 못한다. 위 표의 기준으로 골라라.
⚠️ 이 문단은 원래 "원격은 동적 클라이언트 등록을 요구해서 체인을 새로 쌓아야 한다" 고 적혀 있었는데 틀렸다. DCR 은 MCP 명세에서 MUST 가 아니라 SHOULD 이고, 무엇보다 우리 인증 공급자(Clerk)가 이미 완전한 OAuth 2.1 인가서버라 우리가 만든 AS 는 0줄이다. 오류의 모양이 재발 포인트라 남긴다 — 우리 코드만 재고 조사하고 이미 구입한 SaaS 기능은 세지 않았다.
게시 (npm publish)
CI 가 자동으로 한다. 사람이 순서를 기억할 필요가 없다.
promote(사람이 누르는 버튼) → npm-publish-cli → deploy-dgx게시는 trusted publishing(OIDC) 으로 한다 — 장수명 토큰 없이 CI 가 짧은 수명 자격증명으로 게시한다. npm 이 2027-01 부터 2FA 우회 토큰의 직접 publish 를 막기 때문이고, 그래서 CI 의 node 이미지가 24 다(OIDC 는 npm 11.5.1+ 요구).
⚠️ 게시본에는 sigstore provenance 서명이 붙지 않는다. npm 은 소스 저장소가 공개일
때만 서명을 받아 주는데(2026-09-03 실측: 422 … Unsupported GitLab CI source repository
visibility: "private") 이 레포는 private 이다. CI 설정으로 우회할 수 있는 문제가 아니라
배선을 걷어냈다 — 자세한 사정과 되살리는 절차는 scripts/ci-npm-publish.sh 머리말에 있다.
deploy-dgx 가 게시 성공을 needs 로 요구하므로, 게시가 실패하면 문서가 프로덕션에
나가지 않는다. /docs/agents 가 아직 없는 패키지를 안내하는 창이 구조적으로 열리지 않는다
(무스코프 이름이라 그 창에서 제3자가 선점하면 토큰 탈취로 이어질 수 있다 — CWE-829).
버전이 그대로면 잡은 "이미 게시됨" 으로 즉시 통과한다. 그러니 게시하려면 cli/package.json
의 version 을 올리기만 하면 된다 — VERSION ↔ package.json 정합은 테스트가 강제하므로
src/version.ts 도 같이 올린다.
순서를 강제하는 것은 .gitlab-ci.yml 의 deploy-dgx.needs 하나뿐이고, 그것이 지워지면
아무 신호 없이 보장이 사라진다 — scripts/tests/promote.sh 가 지킨다(make test-scripts).
선행 설정 (1회)
npm Automation token 을 발급해(2FA 를 우회하도록 설계된 종류다) GitLab CI/CD 변수
NPM_TOKEN 에 masked + protected 로 넣는다. 토큰이 없으면 잡이 명확한 메시지로 실패한다.
토큰을 코드나 대화에 넣지 않는다. 값은 GitLab 변수에만 두고, 레포에도 로컬 파일에도 남기지 않는다.
수동 게시 (긴급시)
cd cli
npm run build
npm publish # 무스코프는 기본 public — --access 불필요2FA 가 보안 키(WebAuthn)면 npm publish 가 브라우저 승인 URL 을 띄운다.
게시되는 것은 files 에 적힌 dist/ 뿐이다(소스·테스트 미포함).
npm pack --dry-run 으로 목록을 먼저 확인할 수 있다.
