A DNS/timeout/socket error during PATCH /browsers/{id} was bubbling out
of the loop, leaving remaining zombies running and still billing.
HTTPError is a subclass of URLError, so the existing clause stays first
and the URLError catch handles only the transport-level failures.
Adds an operator-facing skill for cloud.browser-use.com / api.browser-use.com,
covering the same REST surface that the harness's own admin.py already
uses (X-Browser-Use-API-Key header, /browsers, /profiles), plus a
companion script for the most common automation -- stopping zombie
sessions older than N minutes.
Live-tested on 2026-05-05 with a real BROWSER_USE_API_KEY:
POST /browsers 201 - shape verified incl. liveUrl on live.browser-use.com
PATCH /browsers/{id} stop 200 - returns final cost
GET /browsers 200 - paginated {items, totalItems, ...}
GET /profiles 200 - same envelope
GET /profiles/{id} 200 - cookieDomains=None on fresh profiles
GET /usage 404 - no public endpoint, doc'd accordingly
GET / 404 - no root metadata
The cleanup-zombies.py companion is the regression artefact; running it
in dry-run mode is the cheapest smoke test, and a full E2E loop
(spawn -> list -> stop -> re-list) was confirmed end-to-end during
authoring.
Notable wire gotchas surfaced and documented:
- Cost / proxy fields (proxyCost, browserCost, proxyUsedMb) are returned
as JSON strings, not numbers; cast to float before arithmetic.
- liveUrl host is live.browser-use.com (different from cloud.browser-use.com),
with the cdp WebSocket encoded as a ?wss= query parameter.
- cookieDomains can be null on a freshly-created profile despite
list_cloud_profiles' docstring describing it as an array.
- GET /usage returns 404 -- per-session cost lives on each browser
record; aggregate billing only on the dashboard.
Style follows the claude-ai/share-export (#267) pattern of
markdown-skill-with-companion-py, sized at 222 + 161 LOC. Discovery
under helpers.py:163's current logic would resolve cloud.browser-use.com
to "cloud/" rather than "browser-use-cloud/" -- the skill folder name
mirrors the convention used by claude-ai/, vercel/, and tasksquad-ai/
which are also not auto-discoverable today; PR #165 is the broader fix
for that.
Refs: PR #300 (run.py precedence fix in the same area), PR #267
(claude-ai companion-script pattern), PR #288 (vercel dashboard skill
header style).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Move from top-level domain-skills/tasksquad.ai/ to the canonical
agent-workspace/domain-skills/tasksquad-ai/ location, switching to
kebab-case to match sibling dirs (booking-com, dev-to, archive-org).
- Replace find_text_coords(...) with self-contained inline Python
helpers (_coords_by_text, _coords_by_selector) that wrap querySelector
+ getBoundingClientRect. find_text_coords does not exist in the
harness or agent_helpers — every call site was a NameError waiting to
happen.
- Fix the compose-task snippet: drop the dead subject_input = js(...)
line, focus the subject input via _coords_by_selector before typing,
and add a placeholder click for the agent dropdown item.
- Fix the polling-interval contradiction: the inbox section now defers
to the gotchas, and the gotchas list the Free (5s) / Pro (2s) cadence
in one place.
- Move agent-workspace/domain-skills/x.com/ to .../x/ to match repo
convention (siblings use bare platform names like 'tiktok' / 'youtube'
or kebab-case like 'booking-com'; nothing else uses a literal dot in
a dir name).
- Guard the compose-textarea and post-button querySelector lookups so
the snippet raises a clear RuntimeError if the elements aren't there
yet, instead of throwing a confusing JS TypeError on
el.getBoundingClientRect().
If an agent runs the interceptor snippet before GTM has initialized,
window.dataLayer is undefined and .push.bind throws. Seeding with [] is
safe — GTM picks up a pre-existing array on init.
- ly-com: read Vue ref via stable .value with _rawValue fallback
rather than the brittle internal _rawValue path.
- wehotel: ancestor walk now identifies the hotel-name anchor by
identity/innerText instead of an href-based negation against text
(查看详情 lives on the link body, not in href), which previously
let the loop stop on the detail anchor itself.
- ctrip: raise an explicit error when the destination input or 搜索
button isn't found, instead of dereferencing None and crashing
with a confusing TypeError.
- write_text calls now pin encoding="utf-8" so non-ASCII transcripts
don't depend on the locale default.
- share-export.md schema now lists source_url, matching what the script
actually emits.
* domain-skills: add bilibili navigation and structure skill
Covers site architecture, URL patterns, content channels, video
interaction features (三连/投币/弹幕), and navigation quirks for
the Chinese video platform.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* domain-skills: fix PR review — correct history URL and ranking tab count
- Fix history URL from bilibili.com/history to https://www.bilibili.com/account/history
- Remove incorrect "also contains 稍后再看 tab" note (watch later is a separate SPA)
- Fix ranking category tab count from 23 to 24
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* domain-skills: add BOSS直聘 (zhipin.com) navigation and job search skills
Verified content only — navigation structure and job card extraction
confirmed via browser CDP.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* domain-skills: verify zhipin job search with API-first approach
Browser-verified: the /wapi/zpgeek/pc/recommend/job/list.json API
returns real salary numbers ("18-22K") unlike the DOM which uses
font-encoded PUA characters. Added full API reference for job list,
job detail, and filter conditions endpoints.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* domain-skills: verify zhipin chat with WebSocket + REST API
Browser-verified: conversation list loads via WebSocket (ws6.zhipin.com),
message history via /wapi/zpchat/geek/historyMsg API. Documented message
body types (text=1, job card=8, system=16), DOM selectors, pagination,
and bossId extraction.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* domain-skills: add zhipin chat filter tabs with clickable UI control
Browser-verified: 全部/未读/新招呼/更多/有交换/有面试/不感兴趣/AI筛选
filters with click_filter() and click_more_filter() functions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* domain-skills: browser-verify job-search.md and fix API field names
Verified all functions against live zhipin.com. Fixed brandInfo→brandComInfo
with correct sub-fields (brandName/logo/industryName), corrected city codes
(101280100=Guangzhou not Shenzhen), documented page-based pagination as
unreliable, added encryptExpectId default params, and noted anti-bot timing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* domain-skills: fix PR review — add API response validation, narrow bossId extraction, clarify city slug
- Guard all d.zpData accesses with d.code !== 0 checks across job-search.md and chat.md
- Filter bossId extraction to /wapi/zpchat/geek/historyMsg URLs only
- Correct "Chinese" to "pinyin" for city slug and add API code cross-reference
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Captures durable selectors and gotchas for exporting transcripts from
claude.ai/share/<uuid> URLs:
- [data-testid=user-message] for user turns, .font-claude-response for
assistant turns, [data-testid=page-header] for the chat title
- Container-walk pattern (no semantic wrapper — turns are alternating
siblings of a common ancestor)
- The H2.sr-only "Claude responded:" trap that pollutes innerText if you
select the outer turn div instead of .font-claude-response
- Auth requirement: share pages render the conversation only for
signed-in viewers; logged-out sessions get an empty app shell
Includes a working extract-share-transcript.py that emits both JSON and
LLM-friendly Markdown ({Human|Assistant} headers).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #247 added these under domain-skills/ at the repo root, but the
canonical location for domain skills is agent-workspace/domain-skills/
(referenced in README.md, SKILL.md, and helpers.py).
Field-tested 2026-04-29. First batch covering Chinese hotel-booking sites:
- ly-com (同程旅行): hotels.md — prices gated behind login; Nuxt SSR
state holds metadata. Documents the ¥? placeholder pattern, Nuxt data
key randomization, login flow via passport.ly.com.
- ctrip (携程): hotels.md — anonymous price extraction works *only*
with the canonical 6-param URL schema. Documents the schema gate,
homepage-form fallback, phantom-¥ regex trap, no-href-on-cards
reality.
- wehotel (锦江): hotels.md — fully anonymous; covers all Jin Jiang
sub-brands (J酒店/锦江/维也纳/7天/IU etc). Documents the
inconsistent checkinDate vs checkInDate capitalization between list
and detail URLs.
All three include URL patterns, list/detail extraction JS, traps, and
quick-start snippets per the SKILL.md "durable site map" guidance.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Agent-generated skill covering Big Bang (bigbang.hr) checkout:
Vue/Nuxt form filling quirks, jQuery UI location autocomplete,
GTM dataLayer event patterns, and payment step selectors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(tests): reorganize into tests/unit and tests/integration
Moves all root-level test_*.py files into a structured tests/ directory:
- tests/unit/ — admin, helpers (was test_screenshot), run
- tests/integration/ — js expression tests
- tests/conftest.py — shared fake_png pytest fixture, eliminating duplication
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor: move to src layout, agent-workspace, and fix SKILL.md invocation format
- Move package to src/browser_harness/ and domain-skills/interaction-skills to agent-workspace/
- Fix all browser-harness <<'PY' heredoc examples in SKILL.md and run.py HELP string to use the correct -c '...' flag format (heredoc was never supported by the CLI)
- Update SKILL.md path references from domain-skills/ to agent-workspace/domain-skills/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>