| name | boss-auto-job |
| description | BOSS直聘自动求职: Camoufox隐身搜索→多Agent匹配→生成打招呼→自动投递。4层反爬绕过(TLS/行为/网关/风控),AST解密zp_stoken,Code 36/32安全停止机制。 |
BOSS Auto Job
Overview
General-purpose web platform automation framework demonstrated on BOSS Zhipin. Pattern: login session persistence → data scraping → multi-agent analysis → automated action execution. Adaptable to any web platform requiring cookie-based auth and batch AI processing.
When to Use
- Automating any web platform requiring login/session persistence
- Scraping structured data from authenticated pages
- Using multi-agent parallel analysis on scraped content
- Generating tailored content per-item based on AI analysis
- Executing browser automation actions (click, type, send) at scale
- Job hunting, social outreach, e-commerce monitoring, data collection
Core Pattern
1. Login & Session Persistence
└─ Browser login → Extract cookies → Save to ~/.hermes/credentials/
└─ Subsequent runs: load cookies → validate → refresh if needed
2. Data Scraping
└─ Authenticated requests with persisted cookies
└─ HTML parsing + API fallback strategy
└─ Output structured JSON with delimiters (===JSON_START/END===)
3. Multi-Agent Analysis
└─ Delegate each item to subagent with structured prompt
└─ Parallel scoring/matching/generation
└─ Collect results, filter by threshold
4. Automated Action Execution
└─ Generate browser action sequences (navigate, click, type, verify)
└─ Agent executes via browser_* tools
└─ Rate limiting between actions
Prerequisites
- Resume file at
~/.hermes/credentials/resume.txt (plain text)
- BOSS account (phone/password or QR code login)
- Python 3 with
requests, beautifulsoup4 installed
Workflow (BOSS Zhipin Example)
1. Check/Restore Login
└─ Cookie exists? → Valid? → Proceed / Re-login
2. Search Jobs
└─ Input job name → Search → Scrape JD list
3. Multi-Agent Match
└─ Delegate each JD to subagent → Score 0-100
4. Filter & Rank
└─ Sort by score → Keep top N (default 10)
5. Generate Introductions
└─ Per JD: analyze requirements → generate tailored intro
6. Send Applications
└─ Per target JD: send with generated intro
Adaptation Guide
To adapt this pattern to another platform:
- Replace login URL in
login.py → target platform login page
- Replace search endpoint in
search.py → target platform search API/HTML
- Adjust prompt template in
match.py → your analysis criteria
- Adjust generation prompt in
generate.py → your output format
- Adjust selectors in
send.py → target platform DOM selectors
Step 1: Login & Session
First run:
python scripts/login.py
- Opens browser to BOSS login page
- User completes login manually
- Saves cookies to
~/.hermes/credentials/boss_cookies.json
Subsequent runs:
- Script loads cookies automatically
- Validates session with a profile page check
- If expired, prompts re-login
Step 2: Search Jobs
Camoufox Mode (PRIMARY — Recommended)
Uses Camoufox (C++ level Firefox fingerprint spoofing) to bypass ALL 4 layers of BOSS anti-bot detection.
python scripts/search_camoufox.py "产品经理" --city=101010100 --pages=3
Why Camoufox wins:
- C++ level Canvas/WebGL/Audio/Font fingerprint spoofing (not JS injection)
- humanize=True: C++ HumanCursor mouse/keyboard/scroll simulation
- geoip=True: auto timezone/locale from IP
- BrowserForge: real-world device fingerprint distribution
- Passes: FingerprintJS, Cloudflare, DataDome, BrowserScan, CreepJS
- No login cookies required — verified: Code 0 without any cookies
Tested result:
普通 Playwright + Cookie → Code 0 (but gets Code 36 after heavy use)
Camoufox without Cookie → Code 0 (passes all 4 layers automatically)
Legacy: Playwright Mode (Fallback)
If Camoufox is not installed, use Playwright with user cookies:
python scripts/search_playwright.py "产品经理" 101010100 1
Requires: valid cookies in ~/.agent-browser/auth/boss-zhipin.json
Legacy: Chrome CDP Mode (Fallback)
Controls user's real Chrome browser via remote debugging port:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --remote-allow-origins='*'
python scripts/search_chrome_cdp.py "产品经理" --city=101010100 --pages=3
Legacy: curl_cffi Mode (HTTP only)
TLS fingerprint bypass without browser:
python scripts/search_curl_cffi.py "产品经理" --city=101010100
Note: curl_cffi bypasses TLS fingerprint but cannot pass behavior detection (code 37).
BOSS renders job cards on <canvas> to prevent DOM scraping. Always use the API (/wapi/zpgeek/search/joblist.json) instead of parsing HTML.
Output format:
[
"https://www.zhipin.com/web/geek/job?query=产品经理&city=101010100&page=1",
"https://www.zhipin.com/web/geek/job?query=产品经理&city=101010100&page=2"
]
**Agent workflow per URL:**
1. `browser_navigate(url)` — open search page
2. `browser_snapshot(full=true)` — get page HTML
3. Pass HTML to `extract_jobs_from_page()` (built into script)
4. Wait 2-3 seconds between pages
**Output format:**
```json
[
{
"job_id": "123456",
"title": "高级产品经理",
"company": "某某科技",
"salary": "25-40K",
"location": "北京·朝阳区",
"requirements": ["3-5年经验", "本科", "电商经验"],
"jd_text": "负责...",
"boss_name": "张先生",
"boss_title": "HR"
}
]
Step 3: Multi-Agent Resume Matching
Use delegate_task with parallel subagents. Each subagent receives:
- One JD (title, requirements, jd_text)
- Resume content from
resume.txt
Subagent prompt:
You are a professional HR recruiter. Compare this JD against the candidate's resume.
JD: {jd_text}
Resume: {resume_content}
Score 0-100 based on:
- Skills match (40%)
- Experience relevance (30%)
- Education fit (10%)
- Industry alignment (20%)
Return ONLY JSON: {"score": 85, "reason": "brief explanation"}
Parallel execution (max 3 concurrent — batch accordingly):
tasks = [
{"goal": "Score these 3 jobs", "context": f"JDs: {batch1}\nResume: {resume}", "toolsets": []},
{"goal": "Score these 3 jobs", "context": f"JDs: {batch2}\nResume: {resume}", "toolsets": []},
{"goal": "Score these 4 jobs", "context": f"JDs: {batch3}\nResume: {resume}", "toolsets": []},
]
delegate_task(tasks=tasks)
Step 4: Filter & Rank
Collect all scores, sort descending. Default keep top 10.
Step 5: Generate Introduction (Agent-Powered)
不再使用固定模板,也不调外部 API。 招呼语由 Agent 自己根据 JD 和简历的内容直接生成。
Agent 就是 LLM,它读取搜索结果中的 JD 信息和简历内容后,直接写出个性化招呼语。
无需额外的 Python 脚本或 API 配置。
生成规则(Agent 内部遵循)
- 基于 JD 和简历的实际内容分析 2-3 个匹配亮点(技能/经验/项目)
- 不使用固定模板,每条消息都独特
- 语气自然真诚,像在和招聘者对话
- 不以"您好"千篇一律开头,用更自然的方式切入
- 体现对该具体公司/岗位的兴趣(从 JD 中提取线索)
- 控制在 80-150 个中文字符
- 结尾礼貌表达沟通意愿
Agent 工作流
1. 搜索结果返回后,Agent 读取每个职位的 title、company、requirements、jd_text
2. 读取 resume.txt
3. 对每个职位,Agent 直接生成个性化招呼语(这一步不需要调任何工具)
4. 用 send_camoufox.py <job_id> "招呼语" 发送
Step 6: Send Application
Critical: encryptUserId is NOT returned by the search API.
You must fetch it from the job detail API:
/wapi/zpgeek/job/card.json?encryptJobId={job_id}
Or use search_playwright.py which runs inside a browser context where you can call both APIs.
python scripts/send_v2.py --job-id=123456 --intro="生成的介绍"
- Uses browser automation to open chat
- Pastes introduction
- Sends message
File Structure
boss-auto-job/
SKILL.md # Main docs
REVERSE_ENGINEERING.md # BOSS anti-bot reverse engineering report
BYPASS_SOLUTION.md # Bypass strategy with GitHub projects
scripts/
boss_apply.py # 🎯 One-click pipeline (Camoufox)
search_camoufox.py # 🏆 Search via Camoufox (PRIMARY)
send_camoufox.py # 🏆 Send via Camoufox (PRIMARY)
search_playwright.py # Search via Playwright (legacy)
search_chrome_cdp.py # Search via Chrome CDP (legacy)
search_curl_cffi.py # Search via curl_cffi (legacy)
search_browser.py # Search via browser automation (legacy)
search.py # requests-based search (deprecated)
match.py # Multi-agent matching orchestrator
generate.py # Introduction prompt builder (reference)
send_final.py # Message sender (legacy)
refresh_cookies.py # Cookie refresh tool
login.py # Login helper
orchestrator.py # Full pipeline orchestrator
Script Priority
| Priority | Script | Command | Status |
|---|
| 🥇 | search_camoufox.py | python search_camoufox.py "产品经理" | ✅ Best stealth |
| 🥇 | send_camoufox.py | python send_camoufox.py <job_id> "msg" | ✅ Best stealth |
| 🥇 | boss_apply.py | python boss_apply.py "产品经理" --send | ✅ One-click |
| 🥈 | search_playwright.py | python search_playwright.py "产品经理" 101010100 1 | ⚠️ Legacy |
| 🥉 | search_chrome_cdp.py | python search_chrome_cdp.py "产品经理" | ⚠️ Legacy |
Anti-Bot Research Notes
Reusable Pattern: AST Deobfuscation + Token Bypass
This pattern applies to any web platform using dynamically loaded obfuscated JS for bot detection:
- Capture the redirect — Note the 302 location and extract parameters (seed, ts, name)
- Fetch the security JS —
security-js/{name}.js (or equivalent endpoint)
- AST parse to find the encryption entry point — Look for
window.ABC or similar global constructor
- Execute in Node.js VM — Replicate minimal browser environment (
window, document, navigator, location)
- Generate the token — Call the exposed method with correct parameters
- Inject via CDP — Use
Network.setCookie to inject into real browser
- Navigate to target — Bypass complete
AST Deobfuscation Results (BOSS Zhipin)
BOSS security-check uses dynamically named JS files (security-js/{hash}.js) with the following structure:
(function() {
window.md5 = ...
window.s = ...
window.ABC = function() {
this.z = function(seed, ts) {
}
}
})();
Key parameters:
seed: Random string from redirect URL (URL-encoded base64)
ts: Timestamp from redirect URL
name: JS file identifier (e.g., b23b7024)
offset: new Date().getTimezoneOffset() (China = -480)
- Formula:
parseInt(ts) + 60 * (480 + offset) * 1000
Security check page flow:
- Browser hits 302 redirect to
security-check.html
- Page creates iframe, loads
security-js/{name}.js
- JS executes,
window.ABC becomes available
- Page calls
new ABC().z(seed, adjusted_ts) → generates __zp_stoken__
- Sets cookie:
__zp_stoken__={token}; domain=.zhipin.com; path=/; expires={now+2304e5}
- Redirects to
callbackUrl
Why requests alone fails:
Even with correct zp_stoken, BOSS detects non-browser TLS fingerprint and missing browser APIs. The token alone is necessary but not sufficient.
Why CDP works:
Chrome CDP controls a real browser instance. When we inject the zp_stoken cookie via Network.setCookie, the browser presents a legitimate environment (correct TLS, canvas, WebGL, etc.), passing all checks.
API Error Codes
| Code | Meaning | Action |
|---|
| 0 | Success | Process job data |
| 37 | Environment abnormal | Auto-generate zp_stoken (see below) |
| 36 | Account flagged | User must manually verify in browser, then run refresh_cookies.py |
| 32 | Account banned temporarily | More severe than 36. Stop all automation. User must manually verify AND send a message in their browser to restore full access. |
| 1006 | Rate limited | Wait 10s and retry |
curl_cffi TLS Fingerprint Bypass
curl_cffi with impersonate='chrome120' mimics Chrome's TLS/JA3 fingerprint. This alone is enough to pass Layer 2 (environment fingerprinting) — the page loads as 200 instead of 302. However, if the account itself is flagged (code 36), even correct TLS fingerprint won't help. The user must manually verify first.
Tested flow:
curl_cffi.get(page_url, impersonate='chrome120') → 200 ✅ (not 302)
curl_cffi.get(api_url, impersonate='chrome120') → code 36 or 37
- If code 37: generate
zp_stoken via Node.js VM, inject cookie, retry → code 0 or 36
- If code 36: must run
refresh_cookies.py for manual verification
Cookie Refresh Workflow
When cookies are expired or account is flagged (code 36):
cd ~/.hermes/skills/productivity/boss-auto-job/scripts
python refresh_cookies.py
This opens a visible Chromium window with existing cookies loaded. If verification appears, user completes it manually. Script detects successful login, exports updated cookies to ~/.agent-browser/auth/boss-zhipin.json, and validates the API.
Cookie source file: ~/.agent-browser/auth/boss-zhipin.json (Playwright CDP format with cookies array containing name, value, domain, path, expires, httpOnly, secure fields)
Adapting to Other Platforms
To adapt this bypass pattern to another platform:
- Trigger the security check — Visit a protected page, capture the 302 redirect URL
- Identify the JS source — Check the redirect page HTML for
<script src="..."> tags
- Fetch and AST parse — Use Node.js
esprima or manual regex to find the global constructor
- Replicate the browser env — Create a
vm.Context with window, document, navigator, location
- Call the encryption method — Pass the extracted parameters (seed, ts, etc.)
- Inject the resulting cookie — Via CDP
Network.setCookie or browser automation
- Navigate to the original target — The security check should now pass
One-Click Pipeline: boss_apply.py (Camoufox Edition)
cd ~/.hermes/skills/productivity/boss-auto-job/scripts
python boss_apply.py "产品经理"
python boss_apply.py "产品经理" --send
python boss_apply.py "产品经理" --city=101020100 --pages=2 --top=5
python boss_apply.py "产品经理" --send --dry-run
python search_camoufox.py "Python" --city=101010100 --pages=3
python send_camoufox.py <job_id> "你好,我对这个职位很感兴趣"
python send_camoufox.py <job_id> "你好,我对这个职位很感兴趣"
底层引擎: Camoufox (C++级Firefox指纹修改)
pip install "camoufox[geoip]"
- humanize=True: C++ HumanCursor 鼠标/键盘/滚动模拟
- geoip=True: 自动根据IP设置时区/语言
- BrowserForge: 统计分布的真实设备指纹
- 安装:
camoufox fetch (下载~100MB Firefox二进制)
登录认证说明:
- BOSS 直聘的 Cookie 绑定浏览器指纹(TLS/JA3/Canvas)
- 外部导入的 Cookie(如从 Chrome 导出)在 Camoufox(Firefox 内核)中会触发 Code 17
send_camoufox.py 检测到 Code 17 时自动弹出扫码登录页,用户在 Camoufox 内登录后 Cookie 与指纹一致
- 登录成功后 Cookie 保存到
~/.agent-browser/auth/boss-zhipin.json,后续发送自动复用
Agent Workflow (使用 boss_apply.py)
- 运行搜索:
python boss_apply.py "关键词" --send
- 解析输出: 从
===SEARCH_RESULT=== 块提取 jobs 列表(含 JD 详情)
- 并行匹配: 用
delegate_task(tasks=batches) 对每批3个职位打分
- 排名筛选: 按分数排序,取 Top N
- 生成招呼语: Agent 直接根据每个职位的 JD + 简历生成个性化招呼语(不调工具)
- 发送消息:
python send_camoufox.py <job_id> "招呼语",间隔 5 秒
完整 Agent 代码模板
# 1. 搜索
terminal("cd scripts && python boss_apply.py '产品经理' --send")
# 解析 SEARCH_RESULT 块中的 first_5、jobs_file 等
# 2. 匹配(每批3个,delegate_task max 3 concurrent)
delegate_task(tasks=[
{"goal": "Score these 3 jobs...", "context": "Resume: ...\nJob 1: ...\nJob 2: ...\nJob 3: ...", "toolsets": []},
{"goal": "Score these 3 jobs...", "context": "...", "toolsets": []},
{"goal": "Score these 4 jobs...", "context": "...", "toolsets": []},
])
# 3. 排名+生成招呼语
# 取 score >= 60 的职位,delegate_task 生成打招呼语
# 4. 发送(间隔 3 秒,每次发完检查返回码)
⚠️ CRITICAL SAFETY RULES
API 错误码处理(必须遵守)
| Code | 含义 | 正确操作 | ❌ 错误操作 |
|---|
| 0 | 成功 | 处理数据 | — |
| 37 | 环境异常 | 自动生成 zp_stoken 重试一次 | 反复重试 |
| 36 | 账户异常 | 立即停止,告诉用户手动验证 | 换脚本重试 |
| 32 | 账户封禁 | 立即停止,告诉用户手动发消息恢复 | 继续搜索 |
遇到 Code 36/32 时的 Agent 操作
1. 停止所有自动化操作
2. 告诉用户:
- Code 36: "BOSS 检测到异常,请在 Chrome 中打开 zhipin.com 完成验证后告诉我"
- Code 32: "BOSS 账户被临时封禁。请:① 在 Chrome 中完成验证 ② 随便找一个职位点'立即沟通'发一条消息"
3. 等待用户确认恢复
4. 确认后用 AppleScript 导出 Cookie:
osascript -e 'tell application "Google Chrome" to tell active tab of front window to return execute javascript "document.cookie"'
5. 合并 Cookie 并保存到 ~/.agent-browser/auth/boss-zhipin.json
6. 再恢复自动化
频率限制规则
- 搜索 API:每次间隔 3-5 秒
- 发消息 API:每次间隔 5 秒
- 单次运行最多搜索 3 页
- 单次运行最多发送 5 条消息
- 收到 code 1006(限速)时等待 10 秒
Agent Workflow: When Code 36 Appears
When the agent encounters code 36 ("您的账户存在异常行为") during any API call:
- Stop all automated operations immediately — don't keep retrying
- Tell the user: "BOSS 检测到异常行为,请在你的 Chrome 中打开 https://www.zhipin.com ,完成安全验证后告诉我"
- Wait for user confirmation — they will manually complete verification
- After confirmation: use AppleScript to export cookies (see Cookie Export below)
- Then resume the automation
Never try to auto-solve code 36 — it requires human verification (Geetest CAPTCHA).
Agent Workflow: When Code 32 Appears
Code 32 ("已暂时被禁止使用") is more severe than 36. The user's account is temporarily banned from detail/send APIs.
- Stop immediately — same as code 36
- Tell the user: "BOSS 账户被临时封禁。请在你的 Chrome 中完成两步:① 打开 zhipin.com 完成验证 ② 随便找一个职位点'立即沟通'发一条消息"
- The manual message is critical — BOSS's risk engine uses it as a trust signal to restore API access
- After confirmation: export cookies via AppleScript and retry
- Search API may still work (code 0) even when detail/send return code 32 — this is expected
Cookie Export from User's Chrome
When refresh_cookies.py can't launch a visible browser (missing Playwright browser install), ask the user to export cookies manually:
- User opens https://www.zhipin.com in their Chrome (must be logged in)
- User presses
Cmd+Option+I → Console tab
- User runs:
copy(JSON.stringify({cookies: document.cookie.split('; ').map(c => {const [n,...v]=c.split('='); return {name:n, value:v.join('='), domain:'.zhipin.com', path:'/', expires:-1, size:v.join('=').length, httpOnly:false, secure:false, session:true};}), origins:[]}, null, 2))
- Content is now on clipboard. Agent saves it with:
pbpaste > ~/.agent-browser/auth/boss-zhipin.json
Note: This only gets non-HttpOnly cookies. Key cookies (wt2, zp_at, bst) ARE accessible via document.cookie and are sufficient for the search API to work.
AppleScript fallback (when DevTools is blocked): BOSS auto-closes the page when DevTools opens (anti-debug). If user can't open Console, use AppleScript to read cookies directly from their running Chrome:
osascript -e 'tell application "Google Chrome" to tell active tab of front window to return execute javascript "document.cookie"'
This returns all non-HttpOnly cookies as a single string. Parse and save:
raw = "lastCity=101280600; __g=-; bst=xxx; ..."
cookies = {k: v for k, v in (p.split('=', 1) for p in raw.split('; ') if '=' in p)}
The merged result (fresh non-HttpOnly + stale httpOnly) works for the search API.
Important: After any verification/code-36 recovery, user MUST also send a manual message to any BOSS recruiter in their browser. This signals to BOSS's risk engine that the account is active and human-operated, restoring full API access for detail/send endpoints.
Reverse Engineering Report
Full reverse engineering analysis: REVERSE_ENGINEERING.md in skill directory.
Bypass Solution
Full bypass strategy based on GitHub open source research: BYPASS_SOLUTION.md in skill directory.
Recommended approach (ranked):
- Camoufox (
pip install camoufox) — C++ level fingerprint spoofing, humanize=True, geoip=True, passes all 30+ detection sites
- CloakBrowser (
pip install cloakbrowser) — Custom Chromium with 49 C++ patches, passes Cloudflare/reCAPTCHA/FingerprintJS
- rebrowser-playwright (
pip install rebrowser-playwright) — Drop-in Playwright replacement fixing Runtime.Enable leak + navigator.webdriver
- curl_cffi (already installed) — TLS/JA3 fingerprint for pure HTTP requests (no browser)
Key insight: JS-level fingerprint spoofing (Object.defineProperty) is ALWAYS detectable via toString()/getOwnPropertyDescriptor/Worker context mismatch. C++ level modification (Camoufox/CloakBrowser) is the only truly undetectable approach.
Key findings:
- 853 anti-bot patterns in app.js (2.4MB bundle)
- 4-layer defense: Environment → Behavior (Warlock) → Gateway (zp_stoken) → Risk Engine (Backend)
- Warlockdata.js (70KB) collects: click, input, focus, scroll, visibility, ajaxError events
- Patas.js (113KB) APM: LCP/FID/CLS/FP/FCP, resource timing, JS errors
- XOR-encoded risk constants (2539 strings)
- Proxy detection (64 occurrences) catches Playwright/Puppeteer
- Canvas/WebGL fingerprinting (16+14 occurrences)
- Geetest CAPTCHA integration via captcha-sdk@5.1.2.min.js
- Error code cascade: 0 → 37 (env) → 36 (account) → 32 (banned)
- API endpoints: shink.zhipin.com, warlock.zhipin.com, logapi.zhipin.com
Common Mistakes
- Resume too long: Keep resume.txt under 1000 words for better matching
- Cookie expired: Always validate session before search
- Rate limiting: Add 2-3 second delays between sends
- JD changes: Re-scrape JDs if search is >1 day old
- delegate_task max is 3 concurrent: Batch jobs into groups of 3-4 for parallel matching
- Navigation timeout: Use
wait_until="networkidle" and increase timeout to 30s for BOSS pages
- evaluate after navigation fails: Page.evaluate() throws "Execution context was destroyed" if page navigated during evaluation. Always wait for
networkidle before evaluating.
- About:blank fallback: If page.goto() lands on about:blank, the cookies are likely expired or session is invalid. Re-run
refresh_cookies.py or ask user to re-export cookies.
- Camoufox contexts is empty:
Camoufox() returns a standard Playwright Browser object, but browser.contexts is an empty list at start. Use browser.new_page() directly — NOT browser.contexts[0].new_page(). The latter throws IndexError: list index out of range.
- Camoufox page timeout on second query: When calling multiple
page.goto() in sequence, the second one may fail with TargetClosedError. Fix: create a new page per query with page = browser.new_page() and page.close() after each, instead of reusing one page.
- Camoufox
wait_until safety: Use wait_until="domcontentloaded" instead of wait_until="networkidle" for BOSS pages. The networkidle timeout is unreliable with BOSS's continuous polling scripts (warlock, patas, heartbeat).
- GitHub repo published: The skill is published at https://github.com/xiarongwen/boss-auto-job
Security Notes
- Cookies stored in plain JSON (consider encrypting)
- Don't share credential directory
- BOSS may detect automation; use reasonable delays
Known Limitations
- Geetest CAPTCHA — BOSS Zhipin uses Geetest (极验) point-select verification for suspicious accounts. Cannot be automated.
- Solution: Run
refresh_cookies.py to open a visible browser, complete verification manually, script auto-saves new cookies
- Chrome CDP mode: Use
search_chrome_cdp.py to control your real Chrome browser (completely bypasses detection)
- Account-level flag (code 36) — When BOSS flags an account (not just environment), manual verification is required. No programmatic bypass exists.
- Detection: API returns
{"code": 36, "message": "您的账户存在异常行为"}
- Fix:
python refresh_cookies.py → complete verification → cookies auto-exported
- Canvas-rendered job cards — BOSS renders job listing cards on
<canvas> elements to prevent text scraping from the DOM. Even when the page loads successfully, document.querySelectorAll('.job-card') returns empty.
- Workaround: Use the API (
/wapi/zpgeek/search/joblist.json) instead of DOM scraping
- The API requires: valid auth cookies +
__zp_stoken__ + correct TLS fingerprint
- Environment fingerprinting — BOSS detects non-browser TLS/JA3 fingerprints.
requests alone will always fail with "您的环境存在异常" (code: 37). Use curl_cffi with chrome120 impersonation or real browser.
- Playwright Chromium install —
playwright install chromium requires ~100MB disk space and may fail with ENOSPC. Use Chrome CDP mode instead if disk is tight.
- Rate limiting — Search and send APIs have undisclosed rate limits. Add 2-3 second delays between requests.
- DOM changes — BOSS frontend updates frequently.
send.py selectors may need adjustment after major UI updates.
- DevTools anti-debug — BOSS detects and auto-closes pages when DevTools (F12 / Cmd+Option+I) opens. This makes Console-based cookie export impossible. Use AppleScript instead (see Cookie Export section).
- Partial cookie set —
document.cookie returns ~16 cookies (non-HttpOnly). HttpOnly cookies (wt2, zp_at, wbg) must come from the existing auth file. Merge fresh non-HttpOnly + stale HttpOnly cookies for best results.
- Code 32 vs 36 cascade — When automation is heavy, BOSS escalates: code 0 (normal) → 37 (env check) → 36 (account flagged) → 32 (temporarily banned). Stop at the first sign of 36. Don't push through to 32.
- Respect robots.txt and terms of service
Cross-Agent CLI
The project also has a standalone CLI (boss) and cross-agent config files:
boss-auto-job/
boss # CLI entry point (chmod +x, Python shebang)
AGENTS.md # Agent-agnostic skill instructions
CLAUDE.md # = AGENTS.md (Claude Code auto-reads)
.cursorrules # = AGENTS.md (Cursor auto-reads)
Usage from any Agent CLI:
./boss search "Agent应用开发" --city 深圳 --area 南山 --match --top 5
./boss search "AI前端" --city 深圳 --match --send
./boss send <job_id> "你好"
GitHub repo: https://github.com/xiarongwen/boss-auto-job
The CLI wraps search_camoufox.py and send_camoufox.py with a unified interface.
AGENTS.md teaches any Agent CLI how to use the boss command.
Testing Notes
From testing this pattern:
execute_code may cache old module versions — use subprocess for reliable CLI testing
- JSON output from scripts should use delimiters (
===JSON_START/END===) to avoid regex matching issues with log prefixes like [match]
- Browser action sequences should handle both direct URLs (with entity ID) and indirect flows (click to open interaction)
- Cookie validation endpoint should be lightweight (profile/status API, not full page load)
- Bot detection fallback pattern: When requests fail with environment errors, immediately pivot to browser-based navigation + HTML extraction instead of trying to fix headers
- Subagent containment: Always include "ABSOLUTE RULES" in prompts forbidding external file searches — subagents will otherwise search the filesystem and use unrelated resumes
- Cookie format compatibility:
.agent-browser/auth/*.json uses Puppeteer/Playwright format with cookies array — convert to requests dict by filtering on domain and extracting name/value pairs
- JS reverse engineering works: BOSS's
__zp_stoken__ can be generated server-side by fetching security-js/{name}.js and executing in Node.js VM. See search_chrome_cdp.py::bypass_security_check() and search_curl_cffi.py::generate_zp_stoken(). However, the token alone is not sufficient — TLS fingerprint must also match.