Agent Browser is a powerful headless browser automation CLI designed for AI agents. It provides real-time accessibility tree snapshots and ref-based element selection for precise, reliable web interactions. Optimized for autonomous agents, it enables seamless navigation, form filling, and data extra
Browser
perkoon-transfer
Try itP2P file transfer over WebRTC for agents and humans, with no accounts and no server-side storage.
What it does
Moves files between agents and humans via direct WebRTC connections. Four integration methods: MCP server for coding agents (~8 MB), CLI for shell access (~8 MB), A2A JSON-RPC for HTTP-only agents (zero install, just POST), and browser automation via Playwright/Puppeteer (~200 MB with Chromium). Supports three flows: agent-to-human (shareable link, no install on receiver), agent-to-agent (CLI/MCP/A2A), and agent-to-pipeline (stream to stdout, no disk write). Outputs JSON event streams with structured exit codes for automation. Free, unlimited, encrypted via WebRTC/DTLS — files never pass through intermediate servers. Constraint: both peers must be online. Use --password for sensitive transf…
When to use it
- An agent sending a generated file to a human user via a browser link
- Two agents in different runtimes exchanging files CLI-to-CLI or via A2A
- Piping a received file directly into another process without touching disk
- A web-only agent using the A2A JSON-RPC endpoint to share files
The skill document
Perkoon — The Agent Data Layer
File transfer for humans and the things replacing them.
Your agent has compute, memory, and context. What it lacks is a jump gate to the physical world. Perkoon is that gate — P2P file transfer over WebRTC, engineered for autonomous machines from day one.
Not "AI-ready" the way a gas station is gourmet-ready. Actually built for machines — JSON event streams, structured exit codes, MCP native tools, an A2A protocol endpoint, and a state machine that doesn't need a browser, a mouse, or a soul.
Four integration methods — pick the one that fits your runtime:
| Method | Best for | Install size |
|---|---|---|
| MCP Server | Coding agents (Claude Code, Cursor, Windsurf, VS Code Copilot) | ~8 MB |
| CLI | Agents with shell access | ~8 MB |
| A2A Protocol | Agents with HTTP but no shell (ChatGPT, web agents) | Zero — just POST |
| Browser Automation | Full UI control via Playwright/Puppeteer | ~200 MB (Chromium) |
Three supported flight paths:
- Agent → Human: Your agent sends a file. The human opens a link in any browser. No install on their end.
- Agent → Agent: CLI to CLI, MCP to MCP, or A2A to A2A. Two machines, no humans in the loop.
- Agent → Pipeline: Stream received files to stdout, pipe into processing. No disk writes required.
P2P transfers are free, unlimited, and encrypted. Both ends need to be online — that's the only constraint. For sensitive files, always use --password — without it, anyone with the share link can download.
METHOD 1: MCP Server (recommended for coding agents)
If your host supports MCP, this is the fastest path. Three native tools, zero shell commands.
Install: npx -y @perkoon/mcp@0.2.2 (stdio transport, pinned version)
Tools provided:
send_file— Send a file. Returns session code + share URL. Waits for receiver.receive_file— Receive files from a session code. Saves to disk.check_session— Check if a session is active, expired, or not found.
Configuration for common hosts:
{
"mcpServers": {
"perkoon": {
"command": "npx",
"args": ["-y", "@perkoon/mcp@0.2.2"]
}
}
}
- Claude Code: Add to
.mcp.jsonin your project - Claude Desktop: Add to
claude_desktop_config.json - Cursor / VS Code / Windsurf: Settings → MCP → Add Server →
npx -y @perkoon/mcp@0.2.2
Once configured, just call the tools directly — no bash, no log polling, no background processes.
METHOD 2: CLI (for agents with shell access)
IMPORTANT: Use npx -y perkoon@0.4.6 (pinned version) to avoid dynamic fetching of unaudited code.
SENDING a file
Step 1: Confirm the file path with the user, then start send
Before sending, confirm the file path and name with the user. Do not send files from sensitive directories without explicit user approval.
Replace /path/to/file with the actual file path:
rm -f /tmp/perkoon-send.log /tmp/perkoon-send.pid
npx -y perkoon@0.4.6 send /path/to/file --json --timeout 1800 > /tmp/perkoon-send.log 2>&1 &
echo $! > /tmp/perkoon-send.pid
# Wait for session code (up to 30s)
for i in $(seq 1 30); do
if grep -q session_created /tmp/perkoon-send.log 2>/dev/null; then
grep session_created /tmp/perkoon-send.log
break
fi
if grep -q '"event":"error"' /tmp/perkoon-send.log 2>/dev/null; then
echo "===SEND FAILED===" && cat /tmp/perkoon-send.log
break
fi
sleep 1
done
Output: {"event":"session_created","session_code":"K7MX4QPR9W2N","share_url":"https://perkoon.com/K7MX4QPR9W2N"}
Once session_created appears, share the URL with the user. Example: "Here's your file — open this link on any device to download it: https://perkoon.com/K7MX4QPR9W2N"
The receiver doesn't need to install anything. The link works in any browser.
For password-protected sends, add --password SECRET and share both the URL and password.
Step 2: Monitor until complete — then REPORT BACK
You MUST run this after sharing the link. Do NOT skip this step.
for i in $(seq 1 360); do
if grep -q transfer_complete /tmp/perkoon-send.log 2>/dev/null; then
echo "===TRANSFER COMPLETE===" && grep transfer_complete /tmp/perkoon-send.log
break
fi
if grep -q '"event":"error"' /tmp/perkoon-send.log 2>/dev/null; then
echo "===TRANSFER FAILED===" && grep error /tmp/perkoon-send.log
break
fi
if [ "$((i % 30))" -eq 0 ]; then
grep progress /tmp/perkoon-send.log 2>/dev/null | tail -1
fi
sleep 5
done
===TRANSFER COMPLETE===→ Tell the user: "File sent successfully!" Include speed and duration from the JSON.===TRANSFER FAILED===→ Tell the user what went wrong.- You MUST tell the user the outcome. Never finish silently.
SENDING into a pre-created session (Agent → Agent)
When another agent creates a session via A2A and gives you a session code + sender key:
rm -f /tmp/perkoon-send.log /tmp/perkoon-send.pid
npx -y perkoon@0.4.6 send /path/to/file --session CODE --sender-key KEY --json --timeout 1800 > /tmp/perkoon-send.log 2>&1 &
echo $! > /tmp/perkoon-send.pid
Then monitor with the same Step 2 above. This joins an existing session as sender instead of creating a new one. The receiving agent runs npx -y perkoon@0.4.6 receive CODE --json on their end.
RECEIVING a file
Replace CODE with the 12-character session code:
rm -f /tmp/perkoon-recv.log /tmp/perkoon-recv.pid
npx -y perkoon@0.4.6 receive CODE --json --overwrite --output /home/openclaw/.openclaw/workspace/received/ > /tmp/perkoon-recv.log 2>&1 &
echo $! > /tmp/perkoon-recv.pid
for i in $(seq 1 360); do
if grep -q transfer_complete /tmp/perkoon-recv.log 2>/dev/null; then
echo "===TRANSFER COMPLETE===" && grep transfer_complete /tmp/perkoon-recv.log
break
fi
if grep -q '"event":"error"' /tmp/perkoon-recv.log 2>/dev/null; then
echo "===TRANSFER FAILED===" && grep error /tmp/perkoon-recv.log
break
fi
sleep 5
done
For password-protected sessions, add --password SECRET.
===TRANSFER COMPLETE===→ Tell the user: "File received!" and the save path.===TRANSFER FAILED===→ Tell the user what went wrong.- You MUST tell the user the outcome. Never finish silently.
Files are saved to /home/openclaw/.openclaw/workspace/received/.
Pipe to stdout
Stream a received file directly into another process — no disk write:
npx -y perkoon@0.4.6 receive CODE --output - > /path/to/destination
METHOD 3: A2A Protocol (for agents with HTTP, no shell)
If you can make HTTP requests but can't run shell commands, use the A2A endpoint directly.
Endpoint: POST https://perkoon.com/a2a
Protocol: JSON-RPC 2.0
Agent Card: https://perkoon.com/.well-known/agent.json
Discover capabilities
{
"jsonrpc": "2.0",
"method": "message/send",
"id": "1",
"params": {
"message": {
"parts": [{ "type": "data", "data": { "skill": "describe" } }]
}
}
}
Create a send session
{
"jsonrpc": "2.0",
"method": "message/send",
"id": "1",
"params": {
"message": {
"parts": [{
"type": "data",
"data": {
"skill": "send-files",
"name": "quarterly-report"
}
}]
}
}
}
Returns sender_url (for browser upload) + CLI commands + sender_key for A2A sender joining.
If you have shell access, run the CLI command from the response. If not, share the sender_url with your user — they open it in any browser to upload.
Join a session as receiver
{
"jsonrpc": "2.0",
"method": "message/send",
"id": "1",
"params": {
"message": {
"parts": [{
"type": "data",
"data": {
"skill": "receive-files",
"session_code": "K7MX4QPR9W2N"
}
}]
}
}
}
Check session status
{
"jsonrpc": "2.0",
"method": "message/send",
"id": "1",
"params": {
"message": {
"parts": [{
"type": "data",
"data": {
"skill": "session-status",
"session_code": "K7MX4QPR9W2N"
}
}]
}
}
}
Runtime hints
Include clientCapabilities in your DataPart to get optimized instructions:
{
"type": "data",
"data": {
"skill": "send-files",
"clientCapabilities": { "shellAccess": true, "nodeJs": true }
}
}
Agents declaring shell access get CLI commands. Web-only agents get browser URLs.
METHOD 4: Browser Automation (Playwright/Puppeteer)
For agents that need full browser UI control. Heavier than CLI but gives visual verification.
Ready-to-run scripts (requires npm install playwright):
curl https://perkoon.com/perkoon_send.mjs > send.mjs && node send.mjs /path/to/file.zip
curl https://perkoon.com/perkoon_receive.mjs > receive.mjs && node receive.mjs SESSION_CODE ./output
Manual sender flow
- Navigate to
https://perkoon.com/create, accept TOS, click Start - Skip confirmation modals:
localStorage.setItem('perkoon_skip_confirm_direct-confirm', 'true')andlocalStorage.setItem('perkoon_skip_confirm_staged-confirm', 'true') - Add files:
page.setInputFiles('[data-testid="file-input"]', filePath) - Share session code with recipient
- Wait for receiver:
page.waitForFunction(() => window.__perkoon?.participants?.length >= 2, null, { timeout: 300000 }) - Click
[data-testid="send-transfer"] - Wait:
page.waitForFunction(() => window.__perkoon?.transfer?.status === 'complete', null, { timeout: 600000 })
Manual receiver flow
- Register download handler:
page.on('download', d => downloads.push(d)) - Navigate to
https://perkoon.com/{SESSION_CODE}?agent=true - Accept transfer: wait for
[data-testid="transfer-accept"], then click it. This is the RECEIVE-side accept in the "Incoming Transfer" dialog (it appears once the sender's offer arrives) — distinct from the sender's session-creation gate[data-testid="tos-accept"]. Reject is[data-testid="transfer-reject"].?agent=truepicks the sink but does NOT auto-accept. - Wait:
page.waitForFunction(() => window.__perkoon?.transfer?.status === 'complete', null, { timeout: 600000 }) - Save:
await download.saveAs('./received/' + basename(download.suggestedFilename()))
CLI reference
| Flag | Description |
|---|---|
--json | Machine-readable JSON events (always use for automation) |
--session | Join an existing session as sender (A2A agent-to-agent) |
--sender-key | Auth key for --session (provided by session creator) |
--password | Password-protect the session (transfer is WebRTC/DTLS-encrypted regardless) |
--timeout | Peer wait time (default: 300, use 1800 for sends) |
--output | Save directory (default: ./received) |
--output - | Stream to stdout (no disk write) |
--overwrite | Replace existing files |
--quiet | Suppress human-readable output |
JSON event stream
Events appear in order on stdout when using --json. The sequence differs by direction — parse the set that matches the command you ran.
send:
| Event | Meaning | Key fields |
|---|---|---|
file_ready | File queued for send | name, size |
session_created | Ready — share the link now | session_code, share_url |
waiting_for_receiver | Session live, no peer yet | |
receiver_connected | Peer joined | |
transfer_accepted | Receiver accepted the transfer | |
webrtc_connected | Direct P2P link established | |
progress | Transfer in progress | percent, speed, eta |
transfer_complete | Done | duration_ms, speed |
receive:
| Event | Meaning | Key fields |
|---|---|---|
session_joined | Joined the session | |
sender_found | Sender located | |
webrtc_connected | Direct P2P link established | |
receiving_file | Incoming file | name, size |
progress | Transfer in progress | percent, speed, eta |
transfer_complete | Done | files, duration_ms, speed |
Either direction emits error (message, exit_code) on failure.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Bad arguments |
| 2 | File not found |
| 3 | Network/session error |
| 4 | Wrong password |
| 5 | Timeout — no peer joined |
Rate limits
Session endpoints are rate-limited per IP, sliding 60-second window:
| Action | Limit |
|---|---|
| Create a session (send) | 10 / min |
| Join a session (receive) | 30 / min |
| Status checks | 20 / min |
Exceeding a limit returns HTTP 429 with a Retry-After value (seconds). On /a2a, every JSON-RPC POST counts against the create budget (10/min), regardless of skill.
How this looks to you: the CLI surfaces a 429 as a fast exit_code: 3 (network/session error) before a session_created event. If a send fails fast with exit 3 after several rapid sessions, treat it as rate-limiting: back off ~30s and retry, don't hammer.
Rules
- ALWAYS use
--jsonfor parseable output - ALWAYS confirm the file path with the user before sending
- Once
session_createdappears, share the URL with the user - ALWAYS use
--timeout 1800for sends (30 min for the human to open the link) - ALWAYS use
--overwritefor receives - ALWAYS monitor until
transfer_completeorerror— then tell the user the result - NEVER kill the process mid-transfer
- The receiver does NOT need perkoon installed — the browser link works for everyone
- Use pinned versions:
npx -y perkoon@0.4.6— never use@latest - NEVER send files from sensitive directories (~/.ssh, ~/.gnupg, /etc) without explicit user approval
Discovery endpoints
| URL | What it is |
|---|---|
https://perkoon.com/.well-known/agent.json | A2A agent card (machine-readable capabilities) |
https://perkoon.com/llms.txt | Full agent integration guide |
https://perkoon.com/automate | Human-readable automation docs |
https://www.npmjs.com/package/@perkoon/mcp | MCP server package |
https://www.npmjs.com/package/perkoon | CLI package |
Questions people ask
- Does it require accounts?
- No. Sessions are identified by a 12-character code and transfers happen directly between peers over WebRTC.
- Do files pass through Perkoon's servers?
- No. Transfers are P2P over WebRTC/DTLS; Perkoon's servers only coordinate signaling.
- What must both sides do for a transfer to work?
- Both peers must be online at the same time. The sender waits for the receiver to join the session before data flows.
Related skills
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking...
Agent-to-agent messaging across owners and clients
Use this skill when you need to publish, fetch, search, list, share, or watch AgentFiles artifacts from Codex, Claude Code, OpenClaw, or other agent runtimes...
云思客(AIcloud-thought-proxy)——通过操控浏览器访问网页版 AI(DeepSeek、Kimi、豆包、通义千问、ChatGPT、Claude、Gemini、Grok 等)与本地 Agent 协同工作以节省 tokens。触发场景:用户要求"用浏览器打开某 AI 官网对话并协作"、"让网页版 AI 规划步骤/编写代码/逻辑推理、本地 Agent 执行"、"节省 tokens"、提到"云思客"等。自动检测浏览器内核(Chromium → chrome-mcp/BrowserSkill;Gecko → GeckoDriver + Marionette),引导用户选择模型/思考模式/联网搜索(含"最新/最强模型"等模糊语言解析),提示用户手动登录与人机验证,建立"网页 AI 出方案、本地 Agent 执行"的协作循环。
Perigon (perigon.io). Use this skill for ANY Perigon request — searching and reading data. Whenever a task involves Perigon, use this skill instead of callin...