{
  "schemaVersion": "1.0",
  "item": {
    "slug": "browser-playwright-bridge",
    "name": "Browser Playwright Bridge",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/swaylq/browser-playwright-bridge",
    "canonicalUrl": "https://clawhub.ai/swaylq/browser-playwright-bridge",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/browser-playwright-bridge",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=browser-playwright-bridge",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README.md",
      "SKILL.md",
      "scripts/browser-lock.sh",
      "scripts/playwright-template.js"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/browser-playwright-bridge"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/browser-playwright-bridge",
    "agentPageUrl": "https://openagent3.xyz/skills/browser-playwright-bridge/agent",
    "manifestUrl": "https://openagent3.xyz/skills/browser-playwright-bridge/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/browser-playwright-bridge/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Browser ↔ Playwright Bridge",
        "body": "OpenClaw's browser tool and external Playwright scripts cannot share the same CDP connection simultaneously. This skill provides a lock-based bridge: stop OpenClaw browser → run Playwright with the same Chrome profile (cookies/login intact) → release for OpenClaw to reconnect."
      },
      {
        "title": "Architecture",
        "body": "Chrome (CDP port)  ←  shared user-data-dir (~/.openclaw/browser/openclaw/user-data)\n       ↕ mutually exclusive\n┌──────────────┐    ┌──────────────────┐\n│ OpenClaw     │ OR │ Playwright script │\n│ browser tool │    │ (zero token cost) │\n└──────────────┘    └──────────────────┘\n       ↕ managed by browser-lock.sh"
      },
      {
        "title": "Setup",
        "body": "Install Playwright in the workspace (once):\n\ncd <workspace> && npm install playwright\n\nNote: npx playwright install is NOT needed. Playwright connects to the existing Chrome via CDP — no local browser download required.\n\nCopy scripts/browser-lock.sh to your workspace scripts/ directory:\n\nchmod +x scripts/browser-lock.sh"
      },
      {
        "title": "Discovering the CDP Port",
        "body": "The CDP port is dynamically assigned. Never hardcode it. Use discoverCdpUrl() (see below) or the shell equivalent in browser-lock.sh.\n\nShell one-liner:\n\nps aux | grep 'remote-debugging-port=' | grep -v grep | grep -o 'remote-debugging-port=[0-9]*' | head -1 | cut -d= -f2\n\nVerify CDP is responding:\n\ncurl -s --max-time 1 http://127.0.0.1:<port>/json/version"
      },
      {
        "title": "Run a Playwright script (recommended)",
        "body": "./scripts/browser-lock.sh run scripts/my-task.js [args...]\n./scripts/browser-lock.sh run --timeout 120 scripts/my-task.js  # custom timeout\n\nDefault timeout: 300s. If the script exceeds it, the watchdog kills it and releases the lock.\n\nThis automatically: checks lock → stops OpenClaw browser → starts Chrome with CDP → runs script → cleans up → releases lock."
      },
      {
        "title": "Manual acquire/release",
        "body": "./scripts/browser-lock.sh acquire    # stop OpenClaw browser, start Chrome\nnode scripts/my-task.js              # run script(s)\n./scripts/browser-lock.sh release    # kill Chrome, release lock"
      },
      {
        "title": "Check status",
        "body": "./scripts/browser-lock.sh status"
      },
      {
        "title": "Writing Playwright Scripts",
        "body": "Use scripts/playwright-template.js as starting point."
      },
      {
        "title": "CDP Discovery Helper",
        "body": "All scripts should use discoverCdpUrl() instead of hardcoding a port:\n\nconst { execSync } = require('child_process');\n\n/**\n * Discover the CDP URL by inspecting Chrome process args.\n * Falls back to CDP_PORT env var, then probes common ports.\n */\nfunction discoverCdpUrl() {\n  // Method 1: extract from running Chrome process\n  try {\n    const ps = execSync(\n      \"ps aux | grep 'remote-debugging-port=' | grep -v grep\",\n      { encoding: 'utf8', timeout: 3000 }\n    );\n    const match = ps.match(/remote-debugging-port=(\\d+)/);\n    if (match) return `http://127.0.0.1:${match[1]}`;\n  } catch {}\n\n  // Method 2: CDP_PORT env var\n  if (process.env.CDP_PORT) {\n    return `http://127.0.0.1:${process.env.CDP_PORT}`;\n  }\n\n  // Method 3: probe common ports\n  // 18800 is the typical OpenClaw default; others are common CDP conventions\n  const { execSync: probe } = require('child_process');\n  for (const port of [18800, 9222, 9229]) {\n    try {\n      probe(`curl -s --max-time 1 http://127.0.0.1:${port}/json/version`, {\n        encoding: 'utf8', timeout: 2000\n      });\n      return `http://127.0.0.1:${port}`;\n    } catch {}\n  }\n\n  throw new Error('CDP port not found. Is Chrome running with --remote-debugging-port?');\n}"
      },
      {
        "title": "Script Pattern",
        "body": "const { chromium } = require('playwright');\n\nasync function main() {\n  let browser;\n  try {\n    browser = await chromium.connectOverCDP(discoverCdpUrl());\n  } catch (e) {\n    console.error('❌ Cannot connect to Chrome CDP:', e.message);\n    console.error('   Ensure browser-lock.sh acquire was called, or Chrome is running with --remote-debugging-port');\n    process.exit(1);\n  }\n\n  const context = browser.contexts()[0]; // reuse existing context (cookies!)\n  const page = await context.newPage();\n\n  try {\n    // ====== Your automation here ======\n    await page.goto('https://example.com');\n    console.log('Title:', await page.title());\n    // ==================================\n  } catch (e) {\n    console.error('❌ Script error:', e.message);\n    throw e;\n  } finally {\n    await page.close();     // close only your tab\n    // NEVER call browser.close() — it kills the entire Chrome\n  }\n}\n\nmain().then(() => process.exit(0)).catch(e => {\n  console.error('❌', e.message);\n  process.exit(1);\n});\n\nCritical rules:\n\nbrowser.contexts()[0] — reuse the existing context to inherit cookies/login\npage.close() only — never browser.close()\nAlways process.exit(0) on success — Playwright keeps event loops alive otherwise\nWrap connectOverCDP in try-catch — fail fast with a clear message"
      },
      {
        "title": "Workflow: Explore → Record → Replay",
        "body": "Explore — Use OpenClaw browser tool (snapshot/act) to figure out a new workflow\nRecord — Ask the agent to convert the steps into a Playwright script\nReplay — Run via browser-lock.sh run — zero token cost, deterministic"
      },
      {
        "title": "Cron / Scheduled Tasks",
        "body": "In cron tasks, call browser-lock.sh directly:\n\ncd /path/to/workspace && ./scripts/browser-lock.sh run scripts/publish-task.js\n\nThe lock file (/tmp/openclaw-browser.lock) prevents concurrent browser access. If a lock is stale (owner process dead), it auto-recovers."
      },
      {
        "title": "Troubleshooting",
        "body": "ProblemFixLock held by PID xxx./scripts/browser-lock.sh release to force-releasePlaywright connectOverCDP timeoutEnsure OpenClaw browser is stopped first (acquire does this)CDP port not foundChrome isn't running; call browser-lock.sh acquire firstopenclaw browser stop doesn't kill ChromeKnown issue; browser-lock.sh kills the process directlyScript hangs after completionAdd process.exit(0) at the endLogin expiredUse OpenClaw browser tool to re-login, then run scripts again"
      },
      {
        "title": "Environment Variables",
        "body": "VarDefaultDescriptionCDP_PORTauto-discoverOverride CDP port (skips process detection)CHROME_BINauto-detectPath to Chrome/Chromium binaryHEADLESSautoSet true/1 to force headless; false/0 to force headed. Auto-detects on Linux without DISPLAY"
      }
    ],
    "body": "Browser ↔ Playwright Bridge\n\nOpenClaw's browser tool and external Playwright scripts cannot share the same CDP connection simultaneously. This skill provides a lock-based bridge: stop OpenClaw browser → run Playwright with the same Chrome profile (cookies/login intact) → release for OpenClaw to reconnect.\n\nArchitecture\nChrome (CDP port)  ←  shared user-data-dir (~/.openclaw/browser/openclaw/user-data)\n       ↕ mutually exclusive\n┌──────────────┐    ┌──────────────────┐\n│ OpenClaw     │ OR │ Playwright script │\n│ browser tool │    │ (zero token cost) │\n└──────────────┘    └──────────────────┘\n       ↕ managed by browser-lock.sh\n\nSetup\nInstall Playwright in the workspace (once):\ncd <workspace> && npm install playwright\n\n\nNote: npx playwright install is NOT needed. Playwright connects to the existing Chrome via CDP — no local browser download required.\n\nCopy scripts/browser-lock.sh to your workspace scripts/ directory:\nchmod +x scripts/browser-lock.sh\n\nDiscovering the CDP Port\n\nThe CDP port is dynamically assigned. Never hardcode it. Use discoverCdpUrl() (see below) or the shell equivalent in browser-lock.sh.\n\nShell one-liner:\n\nps aux | grep 'remote-debugging-port=' | grep -v grep | grep -o 'remote-debugging-port=[0-9]*' | head -1 | cut -d= -f2\n\n\nVerify CDP is responding:\n\ncurl -s --max-time 1 http://127.0.0.1:<port>/json/version\n\nUsage\nRun a Playwright script (recommended)\n./scripts/browser-lock.sh run scripts/my-task.js [args...]\n./scripts/browser-lock.sh run --timeout 120 scripts/my-task.js  # custom timeout\n\n\nDefault timeout: 300s. If the script exceeds it, the watchdog kills it and releases the lock.\n\nThis automatically: checks lock → stops OpenClaw browser → starts Chrome with CDP → runs script → cleans up → releases lock.\n\nManual acquire/release\n./scripts/browser-lock.sh acquire    # stop OpenClaw browser, start Chrome\nnode scripts/my-task.js              # run script(s)\n./scripts/browser-lock.sh release    # kill Chrome, release lock\n\nCheck status\n./scripts/browser-lock.sh status\n\nWriting Playwright Scripts\n\nUse scripts/playwright-template.js as starting point.\n\nCDP Discovery Helper\n\nAll scripts should use discoverCdpUrl() instead of hardcoding a port:\n\nconst { execSync } = require('child_process');\n\n/**\n * Discover the CDP URL by inspecting Chrome process args.\n * Falls back to CDP_PORT env var, then probes common ports.\n */\nfunction discoverCdpUrl() {\n  // Method 1: extract from running Chrome process\n  try {\n    const ps = execSync(\n      \"ps aux | grep 'remote-debugging-port=' | grep -v grep\",\n      { encoding: 'utf8', timeout: 3000 }\n    );\n    const match = ps.match(/remote-debugging-port=(\\d+)/);\n    if (match) return `http://127.0.0.1:${match[1]}`;\n  } catch {}\n\n  // Method 2: CDP_PORT env var\n  if (process.env.CDP_PORT) {\n    return `http://127.0.0.1:${process.env.CDP_PORT}`;\n  }\n\n  // Method 3: probe common ports\n  // 18800 is the typical OpenClaw default; others are common CDP conventions\n  const { execSync: probe } = require('child_process');\n  for (const port of [18800, 9222, 9229]) {\n    try {\n      probe(`curl -s --max-time 1 http://127.0.0.1:${port}/json/version`, {\n        encoding: 'utf8', timeout: 2000\n      });\n      return `http://127.0.0.1:${port}`;\n    } catch {}\n  }\n\n  throw new Error('CDP port not found. Is Chrome running with --remote-debugging-port?');\n}\n\nScript Pattern\nconst { chromium } = require('playwright');\n\nasync function main() {\n  let browser;\n  try {\n    browser = await chromium.connectOverCDP(discoverCdpUrl());\n  } catch (e) {\n    console.error('❌ Cannot connect to Chrome CDP:', e.message);\n    console.error('   Ensure browser-lock.sh acquire was called, or Chrome is running with --remote-debugging-port');\n    process.exit(1);\n  }\n\n  const context = browser.contexts()[0]; // reuse existing context (cookies!)\n  const page = await context.newPage();\n\n  try {\n    // ====== Your automation here ======\n    await page.goto('https://example.com');\n    console.log('Title:', await page.title());\n    // ==================================\n  } catch (e) {\n    console.error('❌ Script error:', e.message);\n    throw e;\n  } finally {\n    await page.close();     // close only your tab\n    // NEVER call browser.close() — it kills the entire Chrome\n  }\n}\n\nmain().then(() => process.exit(0)).catch(e => {\n  console.error('❌', e.message);\n  process.exit(1);\n});\n\n\nCritical rules:\n\nbrowser.contexts()[0] — reuse the existing context to inherit cookies/login\npage.close() only — never browser.close()\nAlways process.exit(0) on success — Playwright keeps event loops alive otherwise\nWrap connectOverCDP in try-catch — fail fast with a clear message\nWorkflow: Explore → Record → Replay\nExplore — Use OpenClaw browser tool (snapshot/act) to figure out a new workflow\nRecord — Ask the agent to convert the steps into a Playwright script\nReplay — Run via browser-lock.sh run — zero token cost, deterministic\nCron / Scheduled Tasks\n\nIn cron tasks, call browser-lock.sh directly:\n\ncd /path/to/workspace && ./scripts/browser-lock.sh run scripts/publish-task.js\n\n\nThe lock file (/tmp/openclaw-browser.lock) prevents concurrent browser access. If a lock is stale (owner process dead), it auto-recovers.\n\nTroubleshooting\nProblem\tFix\nLock held by PID xxx\t./scripts/browser-lock.sh release to force-release\nPlaywright connectOverCDP timeout\tEnsure OpenClaw browser is stopped first (acquire does this)\nCDP port not found\tChrome isn't running; call browser-lock.sh acquire first\nopenclaw browser stop doesn't kill Chrome\tKnown issue; browser-lock.sh kills the process directly\nScript hangs after completion\tAdd process.exit(0) at the end\nLogin expired\tUse OpenClaw browser tool to re-login, then run scripts again\nEnvironment Variables\nVar\tDefault\tDescription\nCDP_PORT\tauto-discover\tOverride CDP port (skips process detection)\nCHROME_BIN\tauto-detect\tPath to Chrome/Chromium binary\nHEADLESS\tauto\tSet true/1 to force headless; false/0 to force headed. Auto-detects on Linux without DISPLAY"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/swaylq/browser-playwright-bridge",
    "publisherUrl": "https://clawhub.ai/swaylq/browser-playwright-bridge",
    "owner": "swaylq",
    "version": "1.1.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/browser-playwright-bridge",
    "downloadUrl": "https://openagent3.xyz/downloads/browser-playwright-bridge",
    "agentUrl": "https://openagent3.xyz/skills/browser-playwright-bridge/agent",
    "manifestUrl": "https://openagent3.xyz/skills/browser-playwright-bridge/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/browser-playwright-bridge/agent.md"
  }
}