ego-browser
AI 에이전트가 ego (lite)의 실제 Chromium 세션을 제어하는 데 사용하는 브라우저 자동화 런타임입니다.
ego-browser는 ego (lite)가 AI 에이전트에 제공하는 브라우저 자동화 런타임입니다. Chrome DevTools Protocol을 통해 ego (lite) 내부의 실제 Chromium 세션과 통신하고, Node.js heredoc 스크립트를 진입점으로 사용합니다. 에이전트는 한 번의 stdin 전달로 전체 JS 흐름을 작성하며, 모든 helper는 스크립트 범위에 미리 주입되고 브라우저 상태는 Space 안에 유지됩니다.
ego-browser는 사람이 브라우저를 수동으로 조작하기 위한 도구가 아니며 Playwright 또는 Puppeteer를 대체하지도 않습니다. 대상 사용자는 LLM 에이전트입니다.
누구를 위한 것인가
- 브라우저를 구동해야 하는 AI 코딩 에이전트: Claude Code, Codex, Cursor, 맞춤형 SDK 에이전트.
- Lark, Google Docs, Salesforce와 같은 백오피스를 자동화하는 수직형 에이전트를 구축하는 팀.
- 고정된 웹 흐름 반복: 로그인, 채우기, 내보내기, 검색, 테이블 읽기.
- 전체 DOM이나 HTML 페이지를 LLM에 채우려고 시도하다가 토큰 벽에 부딪힌 사람.
설치
ego (lite)와 함께 제공됩니다. 빠른 시작을 참조하세요. 설치 후 임의의 디렉터리에서 ego-browser를 실행합니다.
Skill을 독립형으로 설치할 수도 있습니다.
npx skills add github:CitroLabs/ego-lite/skills/ego-browser
코어 루프
에이전트가 페이지를 조작하는 일반적인 흐름은 다음과 같습니다. 모든 작업이 하나의 heredoc 안에서 이루어집니다.
ego-browser nodejs <<'EOF'
const task = await useOrCreateTaskSpace('search github issues')
await openOrReuseTab('https://github.com/issues', { wait: true, timeout: 20 })
cliLog(await snapshotText())
EOF
- 작업 Space를 재사용하거나 생성합니다(모든 heredoc에서 선언 - Space 참조).
- 대상 페이지를 엽니다.
[ref=N, loc=..., url=...]이 포함된 의미 트리를 얻으려면 스냅샷(snapshotText())을 읽어보세요.@N참조 또는 CSS 선택기를 사용하여 페이지에서 작업합니다.cliLog(...)를 사용하여 최종 결과를 인쇄합니다.
heredoc 내부에서는 Node.js 프로세스를 진행하고 있습니다.
js(...)내부에는 페이지 컨텍스트가 있습니다. 그것들을 섞지 마십시오.
도우미 참조
CamelCase 이름으로 스크립트 범위 내에서 모든 도우미를 사용할 수 있습니다. import는 필요하지 않습니다.
작업 Space
await listTaskSpaces()
const task = await useOrCreateTaskSpace('describe task') // 재사용하거나 생성
await completeTaskSpace(task.name) // 작업 완료, 탭 유지
await closeTaskSpace(task.name) // Space 종료
name은 작업에 대한 3~6단어의 자연어 설명이어야 합니다. 자리표시자를 사용하지 마세요.
탐색 및 상태
await listTabs()
await openOrReuseTab(url, { wait: true, timeout: 20 })
await gotoAndWait(url, { timeout: 20, settle: 1 })
await newTab(url)
await switchTab(tabId)
await currentTab()
await pageInfo()
await ensureRealTab() // 새 Task Space에는 아직 탭이 없을 수 있음
관찰
await snapshotText() // 전체 페이지 시맨틱 Snapshot(기본값)
await snapshotText({ scope: 'only_within_viewport' })
await captureScreenshot('result.png')
await drainEvents() // 탐색 및 네트워크 이벤트 대기열 소비
마우스 및 스크롤
click, doubleClick, hover 및 dragMouse는 동일한 대상 형식(CSS 픽셀)을 허용합니다.
'string': CSS 선택기 또는@ref. 요소 중심을 클릭합니다.[x, y]또는{x, y}: 뷰포트 좌표.{selector, x, y}: 요소 왼쪽 상단의 상대 오프셋입니다.options.label: 3~6단어 설명. 그것을 전달하면 작업이 시각적 하이라이트를 트리거합니다.
await click('@21', { label: 'check the login state' })
await click('button.primary', { label: 'click the submit button' })
await click([420, 260])
await hover('@5', { label: 'hover to reveal the menu' })
await dragMouse([from, to], { label: 'drag the card' })
await scrollBy(900)
await scroll({ dy: 900 })
await scrollToBottomUntil(
async () => await js(String.raw`document.querySelectorAll('article').length`) >= 20,
{ step: 900, wait: 1, maxSteps: 20 },
)
키보드 및 입력
await typeText('hello world')
await fillInput('@2', 'user@test.com')
await pressKey('Enter')
await dispatchKey({ ... })
파일 및 네트워크
await uploadFile('input[type="file"]', '/absolute/path/to/file.pdf')
await httpGet('https://api.example.com/data') // 페이지 컨텍스트에서 GET 요청
대기 중
await wait(1) // 초 단위
await waitForLoad()
await waitForElement('@1')
await waitForNetworkIdle()
wait()및timeout는 초 단위입니다.Ms로 끝나는 매개변수만 밀리초입니다.
브라우저 실행
js(source)는 내부적으로 Runtime.evaluate이며 문자열을 사용합니다. Puppeteer 처럼 함수와 인수를 전달하지 마세요. 경고를 생성하고 .toString()에 래핑되며 클로저 변수와 인수 채널이 모두 사라집니다.
다단계 논리의 경우 한 번 반환되는 IIFE로 래핑합니다.
const data = await js(String.raw`(() => {
const items = [...document.querySelectorAll('article')]
return items.map(el => ({
text: el.innerText,
links: [...el.querySelectorAll('a')].map(a => a.href),
}))
})()`)
await elementEval('@1', el => el.getBoundingClientRect())
await cdp('Page.captureScreenshot', { format: 'png' })
출력 및 자기 발견
cliLog(value) // heredoc에서 사용하는 유일한 출력 채널
cliLog(help('click')) // helper 사용법 확인
권장 워크플로
snapshotText와 ref / loc로 시작하세요. 이는 의미를 그대로 유지하고 좌표의 취약성을 방지합니다.
- 작업 Space를 재사용하거나 생성합니다.
- 페이지(
openOrReuseTab/gotoAndWait)를 열거나 전환합니다. snapshotText()하여[ref=N, loc=..., url=...]트리를 가져옵니다. Ref는 자동으로 refMap에 등록됩니다.click/fillInput/elementEval를 사용하여@N에 대한 작업을 수행하거나js(...)내에서 원샷 DOM 추출을 수행합니다.cliLog(...)최종 결과입니다.
결합할 수 있는 다른 유용한 경로:
captureScreenshot+click([x, y]): 시각적 레이아웃, 캔버스 기반 UI, 가상 목록, 접근성이 불완전한 페이지.js/elementEval/cdp: DOM을 직접 추출하고, 브라우저 상태를 검사하거나, 표준 도우미에 완전히 맞지 않는 모든 항목을 검사합니다.
탐색, 관찰, 스크롤, 추출, 필터링, 집계 및 출력을 단일
ego-browser nodejsheredoc 내에 보관하세요. 두 번째 로컬node스크립트를 통해 데이터를 파이프하지 마십시오.
참조 범위
@N은 가장 최근 snapshotText의 refMap에서만 유효합니다. 모든 snapshotText()는 refMap을 다시 작성합니다. 참조 번호는 요소의 CDP backendNodeId에서 나오므로 같은 요소는 일반적으로 여러 Snapshot에서도 같은 번호를 유지합니다. 그러나 @N이 작동하려면 N이 가장 최근 Snapshot 출력에 나타나야 합니다.
Unknown ref의 일반적인 원인:
- 요소가 뷰포트 밖으로 스크롤되었습니다.
- DOM이 다시 렌더링되었습니다.
- 이전 라운드에서
scope: 'only_within_viewport'를 사용했지만 다음 라운드에서는 해당 요소가 범위에 포함되지 않았습니다.
여러 라운드에 걸쳐 동일한 요소에 대한 안정적인 참조가 필요한 경우 스냅샷의 loc=... 선택기를 사용하거나 CSS 선택기를 직접 작성하세요. 이는 축적된 경험의 기초이기도 합니다. Skill을 참조하세요.
Skill 작업 영역
ego-browser 자체에는 수정 가능한 에이전트 경험이 포함되지 않습니다. 기본적으로 저장소의 Skill 번들에서 helper 확장과 학습된 사이트 경험을 불러옵니다.
../../skills/ego-browser
env var를 통해 재정의:
EGO_BROWSER_AGENT_WORKSPACE=/path/to/ego-browser ego-browser nodejs <<'EOF'
cliLog(await siteSkills())
EOF
learnings/의 사이트 경험은 항상 활성화되어 있으며 모든 helper 호출이 이를 읽습니다. Experience의 기록 및 검색 방식은 Skill에 설명되어 있습니다.
학습된 경험을 검증합니다:
npm run validate:learnings
디렉토리 레이아웃
package/ego-browser/
├── src/ # browser-runtime / helpers / run.js
│ ├── browser-runtime.js # 브라우저 측 ego 런타임 브리지
│ ├── helpers.js # helpers exposed to the agent script
│ ├── run.js # CLI entry point (executes stdin)
│ └── learning/ # Experience 색인, 도메인 검사, 형식 검사
├── artifacts/ego-browser/ # 빌드 결과물, npm bin이 이 위치를 가리킴
└── test/ # unit tests
skills/ego-browser/
├── SKILL.md / SKILL.zh.md # entry point for the agent
└── learnings/ # 사이트 Experience 디렉터리
메모
snapshotText()의 기본값은scope: 'full_page'입니다. 보이는 영역만 정말로 필요한 경우에만'only_within_viewport'를 전달하세요.js()는 평가 결과를 직접 반환합니다. 다시는JSON.parse(...)하지 마세요.js()템플릿 문자열 내에 정규식을 작성할 때 백슬래시(\\d,\\s)를 두 배로 늘리거나String.raw로 전환하세요.- 최상위
return는 IIFE에 자동으로 래핑됩니다. 중첩된 콜백 내부의return도 이를 트리거할 수 있으므로 복잡한 표현식을(() => { ... })()로 먼저 작성하세요. - 사용자가 명시적으로 ego-browser를 요청하면 런타임이 준비된 것입니다.
which ego-browser/node -v/ 도움말 덤프를 사용하여 사전 실행하지 마세요. 첫 번째 실행에서 실제로 오류가 발생한 경우에만 수행하세요.