| name | playwright |
| description | MUST USE for any browser-related tasks. Browser automation via agent-browser CLI - verification, browsing, information gathering, web scraping, testing, screenshots, and all browser interactions. (playwright - oh-my-opencode port) |
| allowed-tools | ["Bash"] |
Browser Automation with agent-browser
Quick Start
agent-browser open <url>
agent-browser snapshot -i
agent-browser click @e1
agent-browser fill @e2 "text"
agent-browser close
Core Workflow
- Navigate:
agent-browser open <url>
- Snapshot:
agent-browser snapshot -i (returns elements with refs like @e1, @e2)
- Interact using refs from the snapshot
- Re-snapshot after navigation or significant DOM changes
Commands
Navigation
agent-browser open <url>
agent-browser back
agent-browser forward
agent-browser reload
agent-browser close
Snapshot
agent-browser snapshot
agent-browser snapshot -i
agent-browser snapshot -c
agent-browser snapshot -d 3
agent-browser snapshot -s "#main"
Interactions (use @refs from snapshot)
agent-browser click @e1
agent-browser dblclick @e1
agent-browser fill @e2 "text"
agent-browser type @e2 "text"
agent-browser press Enter
agent-browser press Control+a
agent-browser hover @e1
agent-browser check @e1
agent-browser select @e1 "value"
agent-browser scroll down 500
agent-browser drag @e1 @e2
agent-browser upload @e1 file.pdf
Get Information
agent-browser get text @e1
agent-browser get html @e1
agent-browser get value @e1
agent-browser get attr @e1 href
agent-browser get title
agent-browser get url
agent-browser get count ".item"
Screenshots & PDF
agent-browser screenshot
agent-browser screenshot path.png
agent-browser screenshot --full
agent-browser pdf output.pdf
Wait
agent-browser wait @e1
agent-browser wait 2000
agent-browser wait --text "Success"
agent-browser wait --url "**/dashboard"
agent-browser wait --load networkidle
Cookies & Storage
agent-browser cookies
agent-browser cookies set name value
agent-browser cookies clear
agent-browser storage local
agent-browser storage local set k v
Network
agent-browser network route <url>
agent-browser network route <url> --abort
agent-browser network route <url> --body '{}'
agent-browser network requests
Tabs
agent-browser tab
agent-browser tab new [url]
agent-browser tab 2
agent-browser tab close
JavaScript
agent-browser eval "document.title"
Global Options
| Option | Description |
|---|
--session <name> | Isolated browser session |
--profile <path> | Persistent browser profile |
--headed | Show browser window |
--cdp <port> | Connect via Chrome DevTools Protocol |
--json | Machine-readable JSON output |
Example: Form Submission
agent-browser open https://example.com/form
agent-browser snapshot -i
agent-browser fill @e1 "user@example.com"
agent-browser fill @e2 "password123"
agent-browser click @e3
agent-browser wait --load networkidle
agent-browser snapshot -i
Example: Authentication with Saved State
agent-browser open https://app.example.com/login
agent-browser fill @e1 "username"
agent-browser fill @e2 "password"
agent-browser click @e3
agent-browser wait --url "**/dashboard"
agent-browser state save auth.json
agent-browser state load auth.json
agent-browser open https://app.example.com/dashboard
Install: bun add -g agent-browser && agent-browser install
Advanced: Google OAuth Stealth Login
์ ์ผ๋ฐ Playwright๊ฐ Google์ ์ํด ์ฐจ๋จ๋๋๊ฐ
Google์ ์๋ํ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ฌ๋ฌ ์ ํธ๋ก ๊ฐ์งํ์ฌ ๋ก๊ทธ์ธ์ ์ฐจ๋จํ๋ค:
| ๊ฐ์ง ์ ํธ | ์ผ๋ฐ Playwright ๊ฐ | ์ค์ Chrome ๊ฐ |
|---|
navigator.webdriver | true | undefined |
window.chrome | undefined | { runtime: {}, ... } |
| ํ์ดํ ์๋ | ์ฆ์ (0ms) | 30~100ms ๋๋ ์ด |
| User-Agent | headless ๋ฌธ์์ด ํฌํจ | ์ผ๋ฐ Chrome UA |
| Permission API | ์๋ํ ์๊ทธ๋์ฒ | ์ ์ ์๋ต |
์ด ์ค ํ๋๋ผ๋ ๊ฐ์ง๋๋ฉด "์ด ๋ธ๋ผ์ฐ์ ๋๋ ์ฑ์ ์์ ํ์ง ์์ ์ ์์ต๋๋ค" ์ค๋ฅ๋ก ๋ก๊ทธ์ธ์ด ์ฐจ๋จ๋๋ค.
3๊ฐ์ง ์ฐํ ๊ธฐ๋ฒ
๊ธฐ๋ฒ 1: navigator.webdriver ์จ๊ธฐ๊ธฐ (CDP via Page.addScriptToEvaluateOnNewDocument)
๊ฐ์ฅ ์ค์ํ ์ฐํ ๊ธฐ๋ฒ. ํ์ด์ง ๋ก๋ ์ ์ JS๋ฅผ ์ฃผ์
ํ์ฌ webdriver ํ๋๊ทธ๋ฅผ ์ ๊ฑฐํ๋ค.
await page.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined,
});
""")
โ ๏ธ evaluate()๋ก๋ ๋ถ์กฑํ๋ค โ ํ์ด์ง ๋ก๋ ํ ์คํ๋์ด ์ด๋ฏธ ๊ฐ์ง๋ ์ดํ๋ค.
add_init_script()๋ ๋ชจ๋ ํ์ด์งยทํ๋ ์ ๋ก๋ ์ด์ ์ ์ฃผ์
๋๋ค.
๊ธฐ๋ฒ 2: window.chrome ์ฃผ์
Google ๋ก๊ทธ์ธ ํ์ด์ง๋ window.chrome ๊ฐ์ฒด์ ์กด์ฌ ์ฌ๋ถ๋ก ์ค์ Chrome์ธ์ง ํ์ธํ๋ค.
await page.add_init_script("""
window.chrome = {
runtime: {
connect: () => {},
sendMessage: () => {},
},
};
""")
๊ธฐ๋ฒ 3: Human-like ํ์ดํ (slow typing with delay)
ํค ์
๋ ฅ ์๋๊ฐ 0ms์ด๋ฉด ๋ด์ผ๋ก ๊ฐ์ง๋๋ค. type()์ delay ์ต์
์ ์ฃผ์ด ์ฌ๋์ฒ๋ผ ํ์ดํํ๋ค.
await page.locator('input[type="email"]').type("user@example.com", delay=50)
await page.locator('input[type="password"]').type("password", delay=50)
์ค์ ๊ฒ์ฆ๋ Python ์คํฌ๋ฆฝํธ ์์
์๋๋ notebooklm.google.com Google OAuth ๋ก๊ทธ์ธ์ ์ค์ ์ฑ๊ณตํ ํจํด์ด๋ค (pseudocode ์์ค):
import asyncio
import json
from pathlib import Path
from playwright.async_api import async_playwright
COOKIE_DIR = Path.home() / ".notebooklm-mcp-cli" / "profiles" / "default"
COOKIE_FILE = COOKIE_DIR / "cookies.json"
STEALTH_SCRIPTS = [
"Object.defineProperty(navigator, 'webdriver', { get: () => undefined });",
"window.chrome = { runtime: { connect: ()=>{}, sendMessage: ()=>{} } };",
"""
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) =>
parameters.name === 'notifications'
? Promise.resolve({ state: Notification.permission })
: originalQuery(parameters);
""",
"Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3] });",
]
async def google_stealth_login(email: str, password: str) -> bool:
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=False,
args=["--disable-blink-features=AutomationControlled"],
)
context = await browser.new_context(
user_agent=(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/131.0.0.0 Safari/537.36"
),
)
page = await context.new_page()
for script in STEALTH_SCRIPTS:
await page.add_init_script(script)
await page.goto("https://accounts.google.com/signin")
await page.wait_for_load_state("networkidle")
await page.locator('input[type="email"]').type(email, delay=50)
await page.locator("#identifierNext").click()
await page.wait_for_timeout(2000)
await page.locator('input[type="password"]').type(password, delay=50)
await page.locator("#passwordNext").click()
await page.wait_for_url("**/myaccount**", timeout=15000)
await page.goto("https://notebooklm.google.com")
await page.wait_for_load_state("networkidle")
cookies = await context.cookies()
COOKIE_DIR.mkdir(parents=True, exist_ok=True)
COOKIE_FILE.write_text(json.dumps(cookies, indent=2))
print(f"โ ์ฟ ํค {len(cookies)}๊ฐ ์ ์ฅ โ {COOKIE_FILE}")
await browser.close()
return True
asyncio.run(google_stealth_login("shyeon0528@gmail.com", "YOUR_PASSWORD"))
์ค์ ์ฑ๊ณต ์ผ์ด์ค: ์ ํจํด์ผ๋ก notebooklm.google.com ๋ก๊ทธ์ธ ์ฑ๊ณต,
์ฟ ํค 27๊ฐ ์ ์ฅ ์๋ฃ.
playwright-stealth ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐฉ์ (๋์)
์ ์๋ ์ฃผ์
๋์ playwright-stealth ํจํค์ง๋ฅผ ์ฌ์ฉํ ์ ์๋ค:
pip install playwright-stealth
from playwright_stealth import stealth_async
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await stealth_async(page)
await page.goto("https://accounts.google.com/signin")
playwright-stealth๋ ๋ด๋ถ์ ์ผ๋ก ์์ ๊ธฐ๋ฒ๋ค์ ํฌํจํ 20+ ๊ฐ์ ํจ์น๋ฅผ ์ ์ฉํ๋ค.
๋จ์ ์ฌ์ฉ์๋ ํธ๋ฆฌํ๋, ์คํจ ์ ์๋ ๋ฐฉ์์ผ๋ก ๊ฐ๋ณ ๋๋ฒ๊น
์ด ํ์ํ๋ค.
CDP + ๊ธฐ์กด Chrome ์ธ์
์ฐ๊ฒฐ ๋ฐฉ์ (์ฃผ์: ์ ์ฑ
์ฐจ๋จ๋จ)
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug
browser = await p.chromium.connect_over_cdp("http://localhost:9222")
โ ๏ธ ์คํจ ์ฌ๋ก: Google์ 2024๋
๋ถํฐ CDP ์ฐ๊ฒฐ ์์ฒด๋ฅผ ๊ฐ์งํ์ฌ
๊ธฐ์
์ ์ฑ
(Enterprise policy)์ผ๋ก ์ฐจ๋จํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
์ด๋ฏธ ๋ก๊ทธ์ธ๋ ํ๋กํ์ ์ฌ์ฌ์ฉํ ๋๋ ๋์ํ๋, ์ ๊ท ๋ก๊ทธ์ธ์๋ ๋น์ถ์ฒ.
nlm ์ฟ ํค ์ ์ฅ ๊ฒฝ๋ก ๋ฐ ๊ฒ์ฆ
์ ์ฅ ๊ฒฝ๋ก
~/.notebooklm-mcp-cli/profiles/default/cookies.json
์ฟ ํค ํ์ผ ๊ตฌ์กฐ
[
{
"name": "__Secure-1PSID",
"value": "...",
"domain": ".google.com",
"path": "/",
"expires": 1234567890,
"httpOnly": true,
"secure": true,
"sameSite": "None"
},
...
]
์ฟ ํค ์ ํจ์ฑ ๊ฒ์ฆ ๋ฐฉ๋ฒ
nlm login --check
python3 -c "
import json
from pathlib import Path
cookies = json.loads(Path('~/.notebooklm-mcp-cli/profiles/default/cookies.json').expanduser().read_text())
names = {c['name'] for c in cookies}
required = {'__Secure-1PSID', '__Secure-3PSID', 'SID'}
missing = required - names
print('โ OK' if not missing else f'โ Missing: {missing}')
print(f' ์ด ์ฟ ํค: {len(cookies)}๊ฐ')
"
python3 -c "
import json, time
from pathlib import Path
cookies = json.loads(Path('~/.notebooklm-mcp-cli/profiles/default/cookies.json').expanduser().read_text())
expired = [c['name'] for c in cookies if c.get('expires', 0) > 0 and c['expires'] < time.time()]
print(f'๋ง๋ฃ๋ ์ฟ ํค: {expired if expired else \"์์\"}')
"
์ฟ ํค ๋ง๋ฃ ์ ์ฌ๋ก๊ทธ์ธ
Google ์ธ์
์ฟ ํค๋ ๋ณดํต 1๋
์ ํจํ๋, ๋นํ์ฑ ๋๋ ๋ณด์ ์ด๋ฒคํธ ์ ๋ฌดํจํ๋๋ค.
nlm login --check ์คํจ ์ ์ stealth ๋ก๊ทธ์ธ ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์คํํ๋ฉด ๋๋ค.