项目文件夹

文件
Magnus Müller 82a1f2f540 remote: stop_remote_daemon helper, skill updates from sub-agent testing (#96)
* remote: stop_remote_daemon helper, skill updates from sub-agent testing

Three fresh sub-agents ran typical-user prompts against the skill
("start a remote browser with my logged-in data" / "Stripe only, no
Google" / "refresh my existing profile"). All three completed
end-to-end — cookies made the round trip, filters scoped cleanly,
refresh was idempotent. But they hit a few doc/code seams worth
closing:

admin.py:
- Add stop_remote_daemon(name="remote"). Sub-agents kept reaching
  for restart_daemon() because there was no obvious way to end a
  remote session; the new alias pairs symmetrically with
  start_remote_daemon() and makes the intent explicit. Same
  underlying implementation — it's just naming for callers.

run.py:
- Pre-import stop_remote_daemon so it's usable from
  browser-harness <<'PY' ... PY without an extra import line.

interaction-skills/profile-sync.md:
- Drop the "close Chrome before syncing" trap at the top of the
  Traps section. Obsolete on profile-use v1.0.5+ — the tool copies
  the profile dir to a temp and syncs from the copy. Two sub-agents
  verified sync works with Chrome open on v1.0.5. Kept a small
  note at the bottom for anyone on older versions.
- Document the ♻️ / 📝 reuse-vs-create signal in sync_local_profile
  output, so agents can confirm cloud_profile_id was accepted
  without counting profiles.
- Clarify the API path convention for _browser_use and the raw
  examples: paths are relative to BU_API, not absolute
  /api/v3/... Sub-agents were copy-pasting the old doc form and
  getting 404s.
- Add a one-liner for looking up an existing cloud profile's UUID.
- Surface stop_remote_daemon in the Python API overview.

Housekeeping:
- Stopped 5 sub-agent-leftover remote browsers from today's testing
  burst (not committed; just an operations note).

* skill: UUID lookup pattern must handle 0 and >1 matches (cubic review)

cubic flagged the one-liner as unsafe: 'next(p["id"] for p in … if …)'
raises StopIteration on no match and silently picks the first duplicate
when names repeat. Profile names genuinely aren't unique — sub-3 in the
same testing round surfaced a duplicate on this account — so use a list
comprehension and require exactly one match before using the UUID.
2026-04-19 00:23:28 -07:00

91 行
4.9 KiB
Markdown

# Profile sync
Make a remote Browser Use browser start already logged in, by uploading cookies from a local Chrome profile.
## One-time install
```bash
curl -fsSL https://browser-use.com/profile.sh | sh
```
Downloads `profile-use` (macOS / Linux / Windows, x64 / arm64). The Python helpers shell out to it; you don't run `profile-use` directly.
## Python API (pre-imported in `browser-harness <<'PY'`)
```python
list_cloud_profiles()
# [{id, name, userId, cookieDomains, lastUsedAt}, ...] — every profile under this API key
list_local_profiles()
# [{BrowserName, ProfileName, DisplayName, ProfilePath, ...}, ...] — detected on this machine
sync_local_profile(local_profile_name, browser=None,
cloud_profile_id=None, # update an existing cloud profile instead of creating new
include_domains=None, # only these domains (and subdomains); leading dot optional
exclude_domains=None) # drop these domains; applied before include
# Shells out to `profile-use sync`. Returns the cloud profile UUID
# (the existing one if cloud_profile_id was passed, else the newly-created one).
start_remote_daemon("work", profileName="my-work") # name→id resolved client-side
start_remote_daemon("work", profileId="<uuid>") # or pass UUID directly
stop_remote_daemon("work") # shut the daemon and PATCH the cloud browser to stop — billing ends
```
`sync_local_profile` prints `♻️ Using existing cloud profile` when `cloud_profile_id` is accepted, or `📝 Creating remote profile...``✓ Profile created: <uuid>` when it creates a new one. Check that line if you want to confirm which path ran.
## Chat-driven flow (don't guess — ask the user)
Cookies are real auth. Don't sync or pick a profile unilaterally.
```python
# 1. Show what's already in the cloud.
for p in list_cloud_profiles():
print(f"{p['name']:25} {len(p['cookieDomains']):3} domains {p['id']}")
```
→ Agent: *"You have these cloud profiles (<N> domains each). Want to reuse one, sync a local profile, or start clean?"*
```python
# 2a. Reuse cloud → one call.
start_remote_daemon("work", profileName="browser-use.com")
# 2b. Sync local first. Show the options:
for lp in list_local_profiles():
print(lp["DisplayName"])
```
→ Agent: *"Which local profile?"* → user picks → before syncing, inspect domain-level cookie counts with `profile-use inspect --profile <name>` (or `--verbose` for individual cookies) and report the summary; never dump 500 cookies into chat.
```python
# 3. Sync + use. Returns the cloud UUID.
uuid = sync_local_profile("browser-use.com")
start_remote_daemon("work", profileId=uuid)
# 3b. Refresh that same cloud profile later (idempotent — no duplicate profiles).
sync_local_profile("browser-use.com", cloud_profile_id=uuid)
# 3c. Scoped: push *only* Stripe cookies into a dedicated cloud profile.
sync_local_profile("browser-use.com",
cloud_profile_id=uuid,
include_domains=["stripe.com"])
```
## What actually gets synced
**Cookies only.** No localStorage, no IndexedDB, no extensions. Enough for session-cookie sites (Google, GitHub, Stripe, most SaaS); not for sites that store auth in localStorage.
Cookies mutated during a remote session only persist on a clean `PATCH /browsers/{id} {"action":"stop"}` — the daemon does this on shutdown when `BU_BROWSER_ID` + `BROWSER_USE_API_KEY` are set (default for remote daemons). Sessions that hit the timeout lose in-session state.
## Cloud profile CRUD
- UI: https://cloud.browser-use.com/settings?tab=profiles
- API: `GET /profiles`, `GET/PATCH/DELETE /profiles/{id}` (paths are relative to `BU_API = "https://api.browser-use.com/api/v3"` in `admin.py`). Fields: `id`, `name`, `userId`, `lastUsedAt`, `cookieDomains[]`. `list_cloud_profiles()` wraps this.
- Name → UUID: `profileName=` on `start_remote_daemon` resolves client-side; no API change needed.
- Need the UUID for an existing profile? `matches = [p["id"] for p in list_cloud_profiles() if p["name"] == "<name>"]` — then verify `len(matches) == 1` before using it. Profile names are not unique; syncs create duplicates unless you pass `cloud_profile_id=`.
- Lower-level raw calls: `from admin import _browser_use; _browser_use("/profiles/<id>", "DELETE")`. Pass the path *without* the `/api/v3` prefix — it's already on `BU_API`.
## Traps
- **Default proxy (`proxyCountryCode="us"`) blocks some destinations** with `ERR_TUNNEL_CONNECTION_FAILED` (e.g. `cloud.browser-use.com` itself). `proxyCountryCode=None` disables the BU proxy; a different country code picks a different exit.
- **Prefer a dedicated work profile over your personal one.** Especially while testing.
- **Older than `profile-use` v1.0.5?** Pre-1.0.5 the sync needed the Chrome profile to be closed (exclusive SQLite lock on the `Cookies` DB). v1.0.5+ copies the profile dir to a temp and syncs from the copy — Chrome can stay open.