daedalus
Daedalus
Chrome 확장 프로그램을 통한 원격 브라우저 제어. Eval 브리지 + 영구 핫픽스 + 탭별 제어 + 스크린샷, CDP, 쿠키, Chrome이 확장 프로그램을 실행하는 페이지에서의 네트워크 캡처.
설치
설치 전에 알아두세요: 일반 eval은 배너 없는 MAIN-world 주입을 사용합니다. 소스 없는 CSP 프로브가 동적 컴파일이 가능하다는 것을 확인할 수 없을 때 Daedalus는 CDP 폴백을 시도합니다. 성공적으로 연결되면 해당 폴백이 실행되는 동안 Chrome의 "Daedalus started debugging this browser" 배너가 표시됩니다. 유지된 CDP 세션 또는 네트워크 캡처는 연결을 더 오래 유지할 수 있습니다. 배너는 디버거 연결을 식별할 뿐, 값 무결성 보장은 아닙니다.
Chrome에서 압축 해제된 확장 프로그램(extension/)을 로드하세요:
chrome://extensions방문개발자 모드 활성화
압축 해제된 확장 프로그램 로드 클릭,
extension/선택
최초 설치 시 고유 토큰이 자동 생성되어 chrome.storage.local에 저장됩니다. 확장 프로그램 옵션 페이지(퍼즐 아이콘 → Daedalus → 옵션)에서 확인/변경할 수 있습니다.
다중 탭 병렬 스크래핑을 위해 Chrome의 백그라운드 탭 제한을 비활성화하세요:
chrome --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-renderer-backgrounding브리지 자체(server.py)는 표준 라이브러리만 사용하며 설치가 필요 없습니다. CLI와 프로세스 내 MCP 엔드포인트는 이 저장소의 Python 패키지입니다:
pip install . # the `daedalus` CLI
pip install ".[mcp]" # ... and the MCP endpoint's dependenciesmcp 추가 기능 없이도 브리지는 시작되어 다른 모든 것을 제공합니다. 시작 시 실패한 MCP 부트스트랩을 보고하며 /mcp는 제공되지 않습니다.
Related MCP server: Real Browser MCP
작동 방식
토큰: 설치 시
crypto.randomUUID()로 한 번 생성되어chrome.storage.local에 저장됩니다.탭 ID: Chrome의 기본
tabsAPI — 각 탭은 ChrometabId로 식별되며, 생성/업데이트 시 30초chrome.alarms하트비트로 등록됩니다.단일 SSE 스트림:
background.js는 하나의 지속적인fetchSSE 연결(tab=extension)을 열고 들어오는 명령을 올바른 탭으로 전달합니다.페이지 브리지:
content.js(ISOLATED world)는window.GM메시지를 background와page.js(MAIN world) 사이에서 중계합니다. Eval은 먼저chrome.scriptingMAIN-world 주입을 사용합니다. 소스 없는 동적 컴파일 프로브는 CSP 차단 페이지를 CDP로 라우팅합니다. 거기 연결 실패 시 페이지 릴레이에 도달합니다. 모든 채널은 페이지MAIN-world 의미론으로 실행됩니다.핫픽스: 확장 프로그램 전체
chrome.storage.local키daedalus-hotfixes(토큰별 아님)에 저장되며, background가 각 적격 최상위 페이지 로드 시 재생합니다 — MAIN-world 주입 또는 페이지 CSP가 동적 컴파일을 금지하는 경우 CDP — 비영구 수정은 버전 게이트가 적용됩니다. 토큰을 회전시켜도 이 저장소는 격리되거나 지워지지 않습니다.
명령 보내기
Daedalus는 확장 프로그램 명령 표면을 <your-bridge>/mcp(streamable-HTTP 전송)에서 MCP 서버로 노출합니다. 요청을 전달하기 전에 Bearer 값이 CLI의 기존 구성 경로를 통해 해석된 브리지 토큰과 정확히 일치해야 합니다: TOKEN은 DAEDALUS_TOKEN을 재정의하며, 선택적 _settings 공급자를 포함합니다. 구성된 토큰이 없으면 MCP 표면은 401로 실패 폐쇄됩니다. 엔드포인트는 mcp 추가 기능이 설치된 경우에만 존재합니다(위 "설치" 참조). Claude Code(또는 모든 MCP 클라이언트)에 추가하세요:
{
"mcpServers": {
"daedalus": {
"url": "https://daedalus.example.com/mcp",
"headers": { "Authorization": "Bearer <your-bridge-token>" }
}
}
}브리지 토큰은 확장 프로그램이 설치 시 생성하는 토큰입니다 — 확장 프로그램 옵션 페이지(퍼즐 아이콘 → Daedalus → 옵션)에서 볼 수 있습니다.
그 예제는 MCP 서버를 공개 호스트 이름으로 앞에 두지만, 전송의 기본 허용 호스트는 루프백 전용입니다(127.0.0.1:*,localhost:*): DAEDALUS_MCP_ALLOWED_HOSTS에 공개 호스트 이름을 지정하지 않으면 프록시된 요청이 거부됩니다. 세 가지 MCP 설정은 아래 "서버"를 참조하세요.
7개 그룹에 40개 도구 — 탭, eval/디버그, 미디어, 쿠키, CSS/차단, 핫픽스, 네트워크/CDP. 전체 목록은 CLAUDE.md의 <mcp> 섹션을 참조하거나 MCP 엔드포인트에서 tools/list를 호출하세요.
수동 검증 도우미(MCP 클라이언트 불필요):
TOKEN=<tok> python3 scripts/mcp_probe.py list
TOKEN=<tok> python3 scripts/mcp_probe.py call title '{"tab_id":"<tabId>"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call screenshot '{"include_image":true}'CLI
셸 CLI는 이 저장소의 daedalus-cli 휠(daedalus_cli/)로 제공되며 daedalus 명령으로 설치됩니다. 환경에서 DAEDALUS_URL과 DAEDALUS_TOKEN을 읽고, TOKEN은 일회성 재정의, ID=<tabId>는 탭별 타겟팅(생략 시 브로드캐스트)입니다:
DAEDALUS_TOKEN=<tok> daedalus tabs
DAEDALUS_TOKEN=<tok> ID=<tabId> daedalus title
DAEDALUS_TOKEN=<tok> daedalus exec myid 'document.title'daedalus --help는 모든 하위 명령을 나열하며, 각각 자체 --help를 지원합니다. 보내는 exec 코드는 표현식 또는 함수 본문이며 반환 값이 결과로 돌아옵니다 — 계약은 위 "명령 보내기"를 참조하세요.
선택적 가져오기 시임도 사용할 수 있습니다: sys.path에서 _settings라는 모듈을 가져올 수 있으면 CLI는 DAEDALUS_URL과 DAEDALUS_TOKEN에 대해 내장 환경 폴백 대신 setting(name, default) 및 required(name) 함수를 사용합니다. TOKEN 환경 변수는 일회성 토큰 재정의로 남아 우선합니다. _settings가 없으면 DAEDALUS_URL은 기본값을 사용하고 DAEDALUS_TOKEN은 필수입니다. ID는 탭별 타겟팅을 위한 환경 변수로 유지됩니다.
또는 원시 명령 파일을 원자적으로 게시합니다(MCP, CLI 불필요). 최종 .json 이름으로 직접 리디렉션은 스트림이 작성자가 끝내기 전에 파일을 관찰할 수 있으므로 지원되지 않습니다. .tmp로 끝나는 형제 이름을 쓴 다음 같은 디렉터리 내에서 이름을 바꾸세요:
# Broadcast to all tabs
commands_dir="$DAEDALUS_DIR/commands"
final="$commands_dir/<token>.json"
tmp="$(mktemp "$commands_dir/.<token>.XXXXXX.tmp")"
printf '%s\n' '{"id":"test1","code":"document.title"}' > "$tmp" &&
mv "$tmp" "$final"
# Target a specific tab
final="$commands_dir/<token>_<tabId>.json"
tmp="$(mktemp "$commands_dir/.<token>_<tabId>.XXXXXX.tmp")"
printf '%s\n' '{"id":"test1","code":"document.title"}' > "$tmp" &&
mv "$tmp" "$final"리더는 형제 .tmp 이름을 무시합니다. 이전 작성자가 최종 이름에 잘못된 JSON을 노출하면 리더는 진행 중인 쓰기를 삭제하는 대신 그대로 두고 재시도합니다. 원자적 이름 변경 후 SSE 스트림이 명령을 전달하고 소비합니다. 결과는 $DAEDALUS_DIR/results/<token>_<tabId>.json(탭별) 및 $DAEDALUS_DIR/results/<token>.json(마지막 작성자 우선)에 저장됩니다. page-main 주입 및 페이지 릴레이 eval 완료는 exec_ms 필드를 포함할 수 있습니다 — 페이지 컨텍스트 실행 시간(밀리초). 페이지는 두 채널 모두에서 이 페이지 시간 필드를 위조하거나 생략할 수 있습니다. CDP 완료는 exec_ms를 포함하지 않습니다. 대기 중인 전달과 관련된 결과는 roundtrip_ms도 포함합니다 — 명령 대기열(PUT /command)에서 결과 도착(POST /result)까지 서버가 관찰한 전체 왕복 시간(밀리초). 대기열 대기 + SSE 전달 + 클라이언트 릴레이 + 실행 + 복귀 여정을 포함하므로, 두 필드가 모두 있을 때 roundtrip_ms − exec_ms는 전송/대기열 오버헤드를 근사합니다. 이 측정은 서로 다른 클록을 사용합니다: exec_ms는 페이지의 performance.now()를 사용하고 roundtrip_ms는 서버 벽시계 밀리초를 사용하므로 차이는 정확한 뺄셈이 아닌 근사치입니다. _did가 없는 레거시 프레임에는 roundtrip_ms가 없습니다.
비동기 지원
기본 page-main 채널은 표현식을 페이지 소유 eval로, 함수 본문을 페이지 소유 Function으로 실행합니다. 비동기 래퍼가 최상위 await를 제공합니다. 소스를 제출하기 전에 background는 상수 Function 프로브만 주입합니다. 해당 프로브가 true를 반환하면 소스 주입이 한 번 시도되고 모든 결과는 종결적입니다: 값, 예외 또는 주입 전송 오류가 CDP에서 소스를 재시도하지 않고 보고됩니다. 페이지가 Function을 소유하고 이 라우팅 힌트에 영향을 줄 수 있지만 프로브에는 제출된 소스가 포함되지 않으므로 다른 채널을 선택해도 부작용이 중복되지 않습니다.
소스 없는 프로브가 true를 반환하지 않으면(대부분 페이지 CSP가 동적 컴파일을 차단하기 때문) Daedalus는 CDP를 시도합니다. 성공적인 연결은 Chrome의 디버거 배너를 표시합니다. Runtime.evaluate는 최상위 await에 REPL 모드를 사용합니다. return을 포함하는 소스는 래퍼 프로브가 표현식으로 구문 분석하지 않는 한 함수 본문으로 처리됩니다. 해당 래퍼 프로브는 파서 휴리스틱이지 비실행 보안 경계가 아닙니다: 연산자가 조작한 소스는 이를 벗어나 실행될 수 있습니다. 최종 CDP 평가가 전달된 후 모든 결과는 종결적입니다. 제출된 소스가 실행되기 전의 연결 또는 형태 실패만 페이지 릴레이에 도달합니다. CDP 약속 해결은 10초로 제한되며, 유지된 세션이나 캡처가 디버거 연결을 유지하더라도 결과 및 예외 객체 핸들은 해제됩니다.
제어하지 않는 페이지에서 평가된 JavaScript는 해당 페이지가 선택할 수 있는 값을 반환하며, 어떤 채널이 실행했는지와 무관합니다. world 필드는 제출된 소스가 실행된 실행 채널만 기록합니다. 이는 CSP 및 디버거 동작에 대한 진단 메타데이터이지 신뢰 신호가 아닙니다. eval 값은 일반 주입의 경우 page-main, 검사기 폴백의 경우 cdp, 최종 릴레이의 경우 page:<hostname>이며, 여기서 <hostname>은 콘텐츠 스크립트의 location.hostname입니다. background가 page: 접두사를 추가하므로 릴레이 호스트 이름은 cdp 또는 page-main과 충돌할 수 없습니다. 이 네임스페이스 사실은 반환된 값에 대해 아무것도 말하지 않습니다. CLI는 필드를 channel=...로 렌더링하고 대시보드도 동일하며 MCP exec, put, result, ping은 정확한 world 값을 보존합니다. 어떤 것도 신뢰 클래스를 할당하지 않습니다.
기본 주입 및 CDP 폴백은 문서화된 느슨한 모드 사례에 대해 클래식 스크립트 동작을 보존합니다: with는 컴파일되고, 레거시 8진수 리터럴은 허용되며, 선언되지 않은 할당은 전역을 생성합니다. CDP REPL 모드는 반복된 let 또는 const 선언도 허용합니다. CLI 및 MCP 결과 대기는 기본 15초입니다. 호출자 시간 초과는 페이지에서 이미 실행 중인 코드를 취소하지 않습니다. Blob 릴레이는 await를 포함하는 코드에 대해 최대 10초, 그 외에는 3초를 별도로 기다린 후 폴백 시간 초과를 보고합니다.
릴레이 상관 관계는 설명적이고 제한적입니다: 보낸 탭이 일치해야 하고, 수락된 메시지 하나가 무작위 릴레이 ID를 소비하며, 소스 없는 주입 프로브와 사전 전달 CDP 폴백이 완료될 때까지 릴레이 ID가 등록되지 않습니다. 이러한 제어는 교차 탭 또는 중복 릴레이 완료를 방지합니다. 값 무결성을 설정하지는 않습니다.
최대 1,000개의 페이지 릴레이 항목이 활성화될 수 있습니다. 용량이 가득 찬 상태에서 새 폴백은 활성 작업을 축출하지 않고 종결 오류 하나를 받습니다. 각 등록 항목은 300,000ms 후 만료되며, 같은 탭 완료 또는 이전 전송 실패가 제거하지 않으면 종결 시간 초과 오류 하나를 받습니다.
대시보드
<your-bridge>/dashboard의 브라우저 기반 제어 표면으로 CLI 없이 전체 확장 프로그램 명령 세트를 구동합니다 — 실시간 탭 목록, eval REPL, 스크린샷, 쿠키, 핫픽스, 차단 규칙, 네트워크 캡처, CDP, CSS 주입, fetch 타이밍, 업로드 브라우저.
확장 프로그램이 제어하는 탭에서 URL을 엽니다.
§12 설정으로 스크롤하여 확장 프로그램 옵션 페이지(퍼즐 아이콘 → Daedalus → 옵션)에서 토큰을 붙여넣습니다. 저장.
대시보드가 라이브 이벤트를 구독하면 상단 바의 SSE 상태 점이 청록색으로 바뀝니다.
server.py가 저장소의 dashboard/ 디렉터리에서 직접 제공합니다(바닐라 JS + ES 모듈, 빌드 없음). 라이브 업데이트는 기존 /stream 엔드포인트를 통해 제공됩니다: server.py는 /register가 기존 탭을 업데이트할 때와 /sync-tabs, /unregister, /result의 성공 경로에서 이벤트를 commands/<token>_dashboard/<ts>_<uuid>.json에 대기열에 넣습니다. /unregister는 탭이 없어도 이벤트를 내보냅니다. 대시보드는 이를 kind:'event' 프레임으로 소비합니다.
주의: 확장 프로그램의 콘텐츠 + 페이지 스크립트는 대시보드 탭을 포함한 일치하는 페이지에 주입됩니다. 브로드캐스트 eval 명령(exec -b)은 대시보드 내에서도 실행됩니다 — 파괴적인 코드를 브로드캐스트하기 전에 탭별 타겟팅을 선호하거나 대시보드를 닫으세요.
자체 패치
핫픽스
각 적격 최상위 페이지 로드 시 재생되는 작은 패치를 유지합니다. MCP 도구를 통해: store_hotfix, list_hotfixes, clear_hotfix, clear_hotfixes, set_permanent. 예시(프로브 스크립트를 통해):
TOKEN=<tok> python3 scripts/mcp_probe.py call store_hotfix '{"fix_id":"my-fix","code":"console.log(\"patched\")"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call store_hotfix '{"fix_id":"always-on","code":"console.log(\"baseline\")","permanent":true}'
TOKEN=<tok> python3 scripts/mcp_probe.py call set_permanent '{"fix_id":"my-fix","permanent":true}'
TOKEN=<tok> python3 scripts/mcp_probe.py call list_hotfixes
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfix '{"fix_id":"my-fix"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfixes
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfixes '{"include_permanent":true}'핫픽스는 기본적으로 버전 게이트가 적용됩니다. 확장 프로그램 버전 변경 후 유지된 비영구 수정은 삭제되지 않고 건너뜁니다. 수정을 저장하면 레코드가 현재 버전으로 업데이트되고 유지된 비영구 수정이 다시 적격해집니다. 수정을 영구로 표시하면(store_hotfix에서 permanent: true 또는 set_permanent) 버전 변경에도 재생됩니다. clear_hotfixes는 기본적으로 비영구 수정을 제거하고 영구 수정을 유지합니다. include_permanent: true를 전달하면 전체 저장소를 제거합니다.
확장 프로그램 명령
백그라운드 서비스 워커는 입력된 명령을 수락합니다(MCP 도구를 통해 발행하거나 $DAEDALUS_DIR/commands/에 "type": "..."이 포함된 JSON을 작성하여 발행):
명령 | 용도 |
| 보이는 탭을 PNG로 캡처 |
| 원시 Chrome DevTools Protocol 호출 발행 |
| CDP를 통한 전체 요청/응답 가로채기 |
| 쿠키 저장소 접근 |
| 탭 제어 |
| 탭별 CSS 주입 |
| declarativeNetRequest 차단 |
| 핫픽스 관리(영구 수정은 버전 업데이트 후에도 유지됨) |
| 디스크에서 확장 프로그램 자체를 다시 로드 |
| fetch 릴레이용 진단 링 버퍼 |
GM 브리지
window.GM(page.js의 MAIN world에 있음)은 다음 Tampermonkey 스타일 하위 집합을 제공합니다:
메서드 | 설명 |
| 확장 전역 |
| 확장 전역 저장소에 예약되지 않은 문자열 키를 씀 |
| 확장 전역 저장소에서 예약되지 않은 문자열 키를 삭제함 |
| 예약되지 않은 저장소 키를 나열함 |
| 백그라운드 중계 HTTP 요청(CSP 면역) |
| CSS 주입 |
| 클립보드에 쓰기 |
| 데스크톱 알림 |
| 새 탭 열기 |
| 다운로드 트리거 |
| 스크립트 메타데이터 |
쿠키 접근은 페이지 기능이 아닌 운영자 기능입니다. 위의 토큰 인증된 cookies / set-cookie / remove-cookie / clear-cookies 명령을 통해 이루어지며 의도적으로 페이지 컨텍스트에 노출되지 않습니다. page.js는 일치하는 각 최상위 페이지에서 실행되며, 그렇지 않으면 자체 document.cookie로는 볼 수 없는 쿠키를 읽을 수 있기 때문입니다.
아키텍처
Browser (matching tab) Server (your bridge host)
┌────────────────────────────────────────────┐ ┌──────────────────────┐
│ MAIN world │ │ bridge (server.py) │
│ ├─ page-main: default injection channel │ │ /stream?token │
│ └─ page.js: GM + relay channel │ │ watches commands/ │
│ ▲ │ │ │
│ │ window.postMessage │ │ /result writes │
│ content.js (ISOLATED) │ │ results/ │
│ ▲ │ │ │
│ │ chrome.runtime │ │ │
│ background.js (service worker) │ │ │
│ ├─ CDP: CSP fallback channel │ │ │
│ ├─ single SSE stream ◄────────────────────┼───┤ │
│ └─ POST result, fetch ────────────────────┼──►│ │
└────────────────────────────────────────────┘ └──────────────────────┘단일 SSE 스트림: 백그라운드는 단일
fetchSSE 연결(tab=extension)을 열고chrome.tabs.sendMessage를 통해 명령을 대상 탭으로 라우팅합니다. 30초 감시 타이머가 오래된 스트림에서 강제로 재연결합니다.탭별 라우팅: 명령은 특정 Chrome
tabId를 대상으로 하거나 모든 탭에 브로드캐스트합니다.CSP 동작:
GM.xmlhttpRequest는 HTTP 작업을 백그라운드 서비스 워커의fetch로 보냅니다. Eval은 일반적으로 배너 없는 MAIN-world 주입을 사용합니다. 소스 없는 프로브가 동적 컴파일을 사용할 수 없다고 보고하면 CDP가 CSP 폴백을 제공하고 연결되면 Chrome의 디버거 배너를 표시합니다. 연결 실패는 페이지/Blob 릴레이에 도달합니다. 결과world값은 해당 채널을 설명하며 무결성을 주장하지 않습니다.
서버
세 가지 필수 설정과 함께 브리지를 시작합니다. DAEDALUS_DIR 또는 DAEDALUS_PORT가 없으면 시작 시 종료되며, 구성된 토큰이 없으면 모든 브리지 제어 경로가 실패 시 닫힙니다(fail closed):
DAEDALUS_DIR=<data-dir> DAEDALUS_PORT=<port> DAEDALUS_TOKEN=<bridge-token> python3 server.pyserver.py는 사용하는 모든 슈퍼바이저(여기서는 systemd 유닛)에서 실행됩니다. localhost 이상으로 노출하는 경우 앞에 TLS 종료 리버스 프록시를 두십시오. 브리지 자체는 일반 HTTP를 사용합니다. 브리지 제어 및 저장소 경로는 CLI 구성 경로를 통해 해석된 하나의 구성된 비밀과 토큰을 비교합니다. TOKEN은 일회성 재정의이고, 그렇지 않으면 DAEDALUS_TOKEN이 필요합니다(임베딩 _settings 모듈이 이를 제공할 수 있음). 구성 누락과 불일치는 실패 시 닫힙니다. 페이지 대상 POST /segment 및 GET /segment-status 경로만 작업 범위(job-scoped) 기능을 대신 사용합니다.
프로세스 내 MCP 프런트 엔드는 세 가지 선택적 설정을 사용하며, 하나의 리스너를 설명하므로 함께 문서화합니다. DAEDALUS_MCP_PORT(기본값 8086)는 바인딩하는 루프백 포트입니다. 해당 도구 핸들러는 DAEDALUS_PORT=0인 경우를 포함하여 브리지의 실제 바인딩된 루프백 URL을 사용합니다. DAEDALUS_LOCAL_URL은 다른 곳에서 실행되는 브리지를 프런트하는 독립형 MCP 배포를 위해 해당 URL을 명시적으로 재정의합니다. DAEDALUS_MCP_ALLOWED_HOSTS(기본값 127.0.0.1:*,localhost:*)는 DNS 리바인딩 보호가 수락하는 쉼표로 구분된 호스트 허용 목록이므로 공개 호스트 이름으로 /mcp를 프런트하려면 해당 호스트 이름을 여기에 지정해야 합니다.
엔드포인트: GET /stream, GET /tabs, GET /health, GET /dashboard[/<asset>], POST /register, POST /sync-tabs, POST /unregister, POST /poll, POST /result, PUT /command, GET /result, POST/GET/DELETE /upload, GET /screenshot, POST /segment-job, POST /segment + GET /segment-status. POST /segment-job에는 구성된 브리지 토큰이 필요합니다. POST /segment 및 GET /segment-status만 작업 범위 sig를 사용합니다. 페이로드 및 엔드포인트 참고 사항은 CLAUDE.md를 참조하십시오.
POST /poll은 레거시 브로드캐스트 명령 파일이 있으면 이를 소비하고 삭제합니다.
PUT /command는 대상별 FIFO 디렉터리 큐에 인큐합니다(한 탭에 대한 연속 명령이 더 이상 덮어쓰지 않음). 각 명령에 전달 ID(_did)를 스탬프하여 확장 프로그램이 재전달된 프레임을 중복 제거할 수 있게 하고, DAEDALUS_CMD_TTL초(기본값 90) 후에 청구되지 않은 명령을 TTL로 삭제합니다. 백그라운드 수집기가 해당 TTL을 적용하고 SSE 소비자가 연결되지 않은 경우에도 빈 큐 디렉터리를 제거합니다. GET /health는 조용히 죽은 브리지를 감지하기 위해 스트림/레지스트리/마지막 전달 활성 상태를 보고합니다.
브리지는 반복된 권한 전달자를 하나의 값을 선택하는 대신 거부합니다. 여기에는 쿼리 문자열 또는 JSON 본문의 token과 세그먼트 기능 경로의 job / sig가 포함되며, 반복된 값이 같거나 비어 있어도 거부합니다. MCP 전송도 반복된 Authorization, Mcp-Session-Id, Host, Origin 헤더와 세그먼트 도구의 반복된 job 인수를 거부합니다.
확장 프로그램이 결과를 게시하면 서버는 명령의 _did를 deliveryId로 노출하고 새 resultGeneration을 할당합니다. CLI 및 MCP 대기자는 먼저 공유 결과 슬롯을 살펴보고 명령 ID와 deliveryId를 모두 일치시킨 다음 GET /result?...&consume=1&expected=<resultGeneration>으로 해당 세대를 조건부로 소비합니다. 해당 요청 사이에 다른 결과가 슬롯을 대체하면 조건부 소비는 새 결과를 그대로 두고 소비되지 않았음을 보고합니다. expected 없이 단순한 consume=1은 현재 슬롯의 파괴적 호환 읽기로 유지됩니다.
GET /stream은 SSE 연결을 무기한 유지하며 타이머로 연결을 순환하는 대신 DAEDALUS_STREAM_KEEPALIVE초(기본값 15)마다 keepalive 주석으로 활성 상태를 증명합니다. DAEDALUS_STREAM_MAX_AGE(기본값 3600)는 최후의 수단 상한일 뿐입니다. 닫기 경로는 tests/test_stream_lifecycle.py에 고정되어 있습니다.
DAEDALUS_MAX_BODY_SIZE(기본값 64 * 1024 * 1024바이트, 64MiB)는 POST, PUT, DELETE 핸들러가 읽는 요청 본문을 제한합니다. 한도보다 큰 선언된 본문은 413을 받고, Content-Length를 선언하지 않은 요청은 빈 본문으로 읽히는 대신 411을 받습니다. 더 큰 세그먼트나 다른 페이로드를 릴레이할 때 이 값을 늘리십시오.
파일 시스템 기반 호출자 값은 하나의 경로 구성 요소 정책을 사용합니다. .., C0/C1 제어 및 서로게이트 문자, Windows에서 유효하지 않은 경로 문자 및 장치 이름, 끝의 점 또는 공백, 240바이트보다 긴 UTF-8 인코딩을 거부합니다. 브리지 토큰은 더 엄격하며 점과 밑줄도 거부합니다. 다른 UTF-8 작업 이름은 허용되므로 클라이언트는 쿼리 문자열에서 URL 인코딩해야 합니다.
POST /segment-job은 세 가지 고정 할당량을 각 새 작업 레코드에 복사합니다. DAEDALUS_MAX_SEGMENT_INDEX(기본값 99999), DAEDALUS_MAX_SEGMENTS_PER_JOB(기본값 10000), DAEDALUS_MAX_SEGMENT_JOB_SIZE(기본값 4 * 1024 * 1024 * 1024바이트, 4GiB). 이러한 설정을 변경하면 이후에 생성되는 작업에 영향을 줍니다. 저장된 할당량이 있는 작업은 기록된 값을 계속 사용합니다. DAEDALUS_MAX_BODY_SIZE는 각 개별 세그먼트 요청도 제한합니다.
보안
확장 프로그램을 설치하기 전에 이 내용을 읽으십시오.
일치하는 모든 최상위 페이지(<all_urls>, MAIN world)에 window.GM 심을 주입합니다.
이것이 put으로 전송된 스크립트가 교차 출처 요청을 할 수 있게 하는 이유입니다.
결과는 신중해야 할 부분입니다. 방문하는 모든 일치 사이트가 해당 심을 호출할 수 있으므로 해당 사이트는 확장 프로그램의 권한을 통해 교차 출처 요청을 발행할 수 있습니다. 와일드카드 @match가 있는 유저스크립트 관리자와 정확히 동일합니다. 브라우징에 허용할 수 없는 경우 extension/manifest.json의 matches를 실제로 구동하는 호스트로 좁히고 브리지가 다른 페이지에서는 아무것도 하지 않는다는 점을 수락하십시오.
브리지 토큰과 서버 URL은 페이지의 GM 저장소 메서드를 통해 접근할 수 없습니다. 이들은 chrome.storage.local의 daedalus- 접두사 아래에 있으며, 릴레이는 페이지 스크립트에 대해 해당 네임스페이스의 읽기, 쓰기, 열거를 거부합니다. 이 규칙이 없으면 방문한 모든 사이트가 GM.getValue('daedalus-token')으로 브리지 토큰을 읽거나 GM.setValue('daedalus-server', ...)로 브리지를 조용히 다시 지정할 수 있습니다. tests/test_repo_contract.py가 이 규칙을 고정합니다.
핫픽스 소스는 의도적으로 페이지에서 관찰 가능한 상태이며 기밀 확장 상태가 아닙니다. 백그라운드는 영구 수정과 확장 버전과 일치하는 비영구 수정을 선택하고 각각을 일반 eval이 사용하는 것과 동일한 채널을 통해 페이지에서 실행합니다. 페이지가 동적 컴파일을 허용하는 곳에서는 MAIN-world 주입, 허용하지 않는 곳에서는 CDP를 사용하므로 페이지는 두 채널에서 실행하는 소스를 관찰할 수 있습니다.
MCP 베어러는 MCP 프로세스가 읽을 서버 호스트 파일을 지정할 수 없습니다. MCP put, CSS 주입/제거, 핫픽스 저장 도구는 인라인 소스만 수락합니다. 로컬 CLI는 파일 경로 편의성을 유지하지만 CLI 프로세스는 운영자가 선택한 파일을 읽고 내용을 인라인으로 제출합니다. 따라서 브리지 토큰을 보유하면 문서화된 브라우저 및 확장 제어 권한(저장된 핫픽스 소스 읽기 포함)이 부여되지만 이러한 도구를 통한 임의 호스트 파일 시스템 읽기가 추가되지는 않습니다.
브리지 제어 및 저장 라우트는 구성된 브리지 토큰을 요구하며,
/segment와 /segment-status만이 기능 예외입니다. 서버는 요청 토큰을
TOKEN 또는 DAEDALUS_TOKEN에서 해석된 비밀과 비교하고, 비밀이 구성되지
않으면 요청을 거부합니다. POST /segment-job은 작업 범위 기능을 발행하기
때문에 해당 브리지 토큰을 요구합니다. 신뢰할 수 없는 페이지 JavaScript는
해당 기능을 사용하여 그 작업을 게시하고 조회할 수만 있습니다. 브리지 토큰을
보유한 사람은 누구나 브라우저를 제어할 수 있습니다. TLS를 종료하는 리버스
프록시 없이 루프백 외부로 브리지 포트를 노출하지 말고, 토큰을 자격 증명으로
취급하십시오.
평가 결과는 값 무결성 보장을 제공하지 않습니다. 제어하지 않는 페이지에서
평가된 JavaScript는 어떤 채널이 실행했는지와 관계없이 해당 페이지가 선택한
값을 반환할 수 있습니다. world 필드는 제출된 소스가 어떻게 실행되었는지를
나타내며, 그 값을 신뢰할지 여부를 나타내지 않습니다. page-main은 일반적인
MAIN-world 주입이고, cdp는 검사기 CSP 대체이며, page:<hostname>은 릴레이
입니다. 필수 page: 접두사는 페이지 컨텍스트 외부에서 추가되므로 어떤
호스트 이름도 cdp 또는 page-main을 생성할 수 없습니다. 그 충돌 저항성은
설명적인 것에 불과합니다. page-main에서 페이지 소유의 eval 및 Function
바인딩은 제출된 소스를 읽을 수 있을 뿐만 아니라 반환된 값에도 영향을 줄 수
있습니다.
CDP 컴파일은 페이지의 eval 및 Function 바인딩을 해석하지 않으며, 구현은
직접 핸들을 직렬화하기 전에 참조로 검색합니다. 이러한 전송 메커니즘은 신뢰
경계를 변경하지 않습니다. 제출된 소스는 여전히 페이지 제어 상태를 읽고 CDP가
수신하기 전에 페이지 promise 메커니즘을 통해 프리미티브를 포함한 모든 값을
라우팅할 수 있습니다. 제출된 소스가 이러한 페이지 제어 경로를 사용할 때마다
페이지는 cdp에서도 반환된 값을 선택할 수 있습니다.
릴레이는 해당 임의 ID와 연결된 저장된 호출만 수락하고, 발신자의 탭이 일치해야 하며, 항목을 한 번만 소비합니다. 다운그레이드는 브리지 토큰, 서버 URL, 전달 ID 또는 결과 경로를 공개하지 않으며, 추가 확장 또는 브라우저 권한을 부여하지 않으며, 다른 탭의 호출에 영향을 줄 수 없습니다. 이러한 속성은 라우팅 및 브라우저 권한을 보호합니다. 릴레이 마커 또는 반환된 JavaScript 값을 신뢰 신호로 바꾸지 않습니다.
배포
브리지는 루프백에서 일반 HTTP를 사용합니다. 브리지 제어 및 저장 라우트는
구성된 토큰을 요구하고 토큰이 없으면 실패-폐쇄(fail closed)합니다. 오직
/segment와 /segment-status만 작업 범위 기능을 사용합니다. 의도적으로
수행하지 않는 두 가지가 있는데, 이는 앞에 있는 무엇이든지에 속하기 때문입니다:
TLS 및 CORS.
server.py는 CORS 헤더를 보내지 않습니다. 브리지가 교차 출처인 경우, 예제 HLS 릴레이의 페이지 측fetch호출은GET /segment-status와POST /segment모두에 접근해야 합니다. 프록시는 두 라우트 모두에서 페이지 출처를 허용하고 POST의 사전 요청에 필요한 메서드/헤더를 처리해야 합니다. 상태 GET이 차단되거나, 사용 불가능하거나, 비-2xx이거나, 유효하지 않으면 예제는 작업을 새 것으로 간주하고 모든 세그먼트를 다시 POST합니다. 이러한 쓰기는 여전히 동일한 인덱스별 파일을 대체하지만 실행은 재개/건너뛰기 절약을 잃습니다. 배포가 해당 CORS 정책을 제공할 수 없는 경우 두 요청을 확장의 GM 브리지를 통해 라우팅하십시오.저장된 업로드 제공. 대시보드는
/uploads/<path>에서 다운로드를 링크하지만 브리지에는 해당 라우트가 없습니다. 호출자가 제공한 업로드 이름과 바이트를 대시보드 출처에서 직접 제공하지 마십시오. 실행 가능한 콘텐츠가 대시보드의 토큰 보유 저장소와 출처를 공유하게 됩니다. 해당 링크를 사용 불가능하게 두거나,/uploads/가 별도의 다운로드 전용 출처로 리디렉션하여Content-Disposition: attachment를 강제하고application/octet-stream을 사용하며X-Content-Type-Options: nosniff를 보내도록 하십시오.GET /upload(목록) 및GET /screenshot은 브리지 자체에서 제공되며 프록시 도움이 필요하지 않습니다.
구성된 토큰으로 직접 실행하면 이 두 가지가 모두 단순히 없습니다 — 다른 모든 것은 작동합니다.
파일
파일 | 설명 |
| MV3 매니페스트 |
| 서비스 워커 — SSE, 명령 디스패치, fetch 릴레이, 스크린샷, CDP, 쿠키, 다운로드 |
| 페이지와 백그라운드 간 메시지 릴레이 |
| MAIN-world 브리지 — |
| 토큰/서버 설정 UI |
| 디버그 서버 (127.0.0.1:8086에서 MCP 데몬 스레드도 호스팅) |
| MCP 서버 — 확장 명령 표면을 HTTP를 통해 |
| 수동 검증을 위한 최소 MCP 클라이언트 헬퍼 |
| 모든 버전 사이트에서 버전 일관성 검사/범프 |
| pre-commit + pre-push 버전 일관성 게이트 |
|
|
|
|
|
|
| 테스트 스위트; |
예제
examples/에는 put으로 페이지 내에서 실행하도록 설계된 스크립트가 있습니다.
여섯 개 중 다섯 개는 특정 사이트보다는 브리지의 일부를 보여줍니다. Discord
예제는 의도적으로 사이트별로 지정되어 있습니다. 가상화된 목록을 뒤로 스크롤하는
것은 실제 가상화된 목록 없이는 보여줄 수 없는 기술이기 때문입니다:
예제 | 보여주는 내용 |
|
|
|
|
| 재시도를 멈추지 않는 플레이어 인스턴스를 찾아 파괴하는 방법 |
| React 제어 입력을 채워 React 자체 상태가 실제로 변경되도록 하는 방법 |
| 가상화된 메시지 목록을 뒤로 스크롤하여 추출하는 방법 |
| 가능한 가장 작은 |
put은 함수를 호출하는 대신 스크립트를 전달하고 인수를 전달할 방법이 없기
때문에, 전송되기 전에 __PLACEHOLDER__ 치환을 통해 구성을 받습니다.
각각은 독립 스크립트가 아니라 async 함수의 BODY입니다. 브리지가 보내는 것을
감쌉니다. 그래서 대부분이 최상위 return으로 끝나고, 그 중 하나인
scrape-discord-messages.js는 최상위 await도 사용합니다. 둘 다 실제로
실행되는 곳에서는 유효합니다. node --check는 CommonJS 래퍼에서 각 파일을
구문 분석하는데, 이는 최상위 return을 허용하지만 최상위 await는 거부합니다
— 그래서 정확히 그 파일 하나가 구문 검사에 실패합니다.
개발
python3 run_tests.py는 모든 스위트를 실행하고 검증한 내용을 명명하는 하나의
집계 줄을 출력합니다: 실행된 스위트 수, 통과한 테스트 수, 건너뛴 수. 모든
테스트가 건너뛴 스위트는 아무것도 검증하지 않았으므로 통과가 아닌
OVERALL: INCOMPLETE로 0이 아닌 종료 코드로 보고됩니다. 일반적인 원인은
누락된 종속성이며, 그 위의 NO COVERAGE 줄이 스위트를 명명합니다. MCP
스위트가 필요한 것은 mcp 추가 기능 설치입니다("설치" 참조).
버전 문자열은 확장, 대시보드 및 CLI 패키지의 여러 위치에 있습니다.
python3 scripts/check_versions.py가 목록입니다 — 모든 사이트를 명명하고
발견한 수를 보고합니다. 그래서 이 단락은 다음에 추가될 때 오래될 수 있는 수를
다시 언급하지 않습니다. 함께 범프하십시오, 절대 수동으로 하지 마십시오:
python3 scripts/check_versions.py --set 0.18.0 # rewrite every site
python3 scripts/check_versions.py # verify the working tree.githooks/pre-commit는 인덱스를 확인하고 .githooks/pre-push는 푸시된 각
커밋을 확인하므로 절반 범프는 랜딩할 수 없습니다. 모든 클론은 한 번
옵트인해야 합니다 — 그렇지 않으면 git은 추적된 디렉토리에서 훅을 실행하지
않습니다:
git config core.hooksPath .githooks브라우저가 편집하는 체크아웃과 다른 체크아웃에서 확장을 로드하는 경우, 거기에도 훅을 설치하십시오 — 그리고 확장을 다시 로드하면 브라우저 측의 파일을 다시 읽는다는 것을 기억하십시오, 당신의 측이 아닙니다.
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Access Kernel's cloud-based browsers and app actions via MCP (remote HTTP + OAuth).
Browserless MCP — wraps the Browserless headless-Chromium REST API (browserless.io)
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables MCP clients to drive a real, logged-in Chrome browser for web automation tasks like navigation, clicking, typing, and screenshotting.2 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables browser automation over MCP using a real Chrome browser with existing profile, supporting real tabs, downloads, cookies, and RPA workflows.104 npmMIT
- FlicenseNot gradedqualityCmaintenanceEnables MCP clients to control real Chrome tabs in existing profiles, supporting navigation, interaction, screenshots, and console/network inspection across multiple Chrome instances.-
- AlicenseBqualityCmaintenanceEnables MCP clients to drive a real Chromium browser for automation, including navigation, JavaScript execution, CDP commands, network capture, and multi-tab control.7GPL 3.0