{
  "schemaVersion": "1.0",
  "item": {
    "slug": "freeman-browser",
    "name": "freeman-browser",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/sbrin/freeman-browser",
    "canonicalUrl": "https://clawhub.ai/sbrin/freeman-browser",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "manual_only",
    "downloadUrl": "/downloads/freeman-browser",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=freeman-browser",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README.md",
      "package.json",
      "SKILL.md",
      "browser.json",
      "scripts/browser-freeman.js"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Open the source page and confirm the required access before downloading.",
      "Review SKILL.md after the source grants access.",
      "Treat the package as manual setup until the direct download works."
    ],
    "agentAssist": {
      "summary": "Use the source page and any available docs to guide the install because the item requires authentication or permission before the package can be fetched.",
      "steps": [
        "Open the source page via Open source page.",
        "If you can obtain the package, extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the source page and extracted files."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I tried to install a skill package from Yavira, but the item requires authentication or permission before the package can be fetched. Inspect the source page and any extracted docs, then tell me what you can confirm and any manual steps still required. Then review README.md for any prerequisites, environment setup, or post-install checks."
        },
        {
          "label": "Upgrade existing",
          "body": "I tried to upgrade a skill package from Yavira, but the item requires authentication or permission before the package can be fetched. Compare the source page and any extracted docs with my current installation, then summarize what changed and what manual follow-up I still need. Then review README.md for any prerequisites, environment setup, or post-install checks."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "slug": "freeman-browser",
      "status": "auth_required",
      "reason": "login_required",
      "recommendedAction": "review_source",
      "checkedAt": "2026-05-12T00:54:05.669Z",
      "expiresAt": "2026-05-15T00:54:05.669Z",
      "httpStatus": 403,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=freeman-browser",
      "contentType": "text/plain",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=freeman-browser",
        "contentDisposition": null,
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "freeman-browser"
      },
      "scope": "item",
      "summary": "Item requires authentication.",
      "detail": "This item appears to require sign-in or permission before the package can be fetched. Open the source page and confirm access manually.",
      "primaryActionLabel": "Open source page",
      "primaryActionHref": "https://clawhub.ai/sbrin/freeman-browser"
    },
    "validation": {
      "installChecklist": [
        "Sign in or confirm the required access before retrying the download.",
        "Review SKILL.md after authentication succeeds.",
        "Treat this source as manual setup until the package can be fetched directly."
      ],
      "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/freeman-browser",
    "agentPageUrl": "https://openagent3.xyz/skills/freeman-browser/agent",
    "manifestUrl": "https://openagent3.xyz/skills/freeman-browser/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/freeman-browser/agent.md"
  },
  "agentAssist": {
    "summary": "Use the source page and any available docs to guide the install because the item requires authentication or permission before the package can be fetched.",
    "steps": [
      "Open the source page via Open source page.",
      "If you can obtain the package, extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the source page and extracted files."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I tried to install a skill package from Yavira, but the item requires authentication or permission before the package can be fetched. Inspect the source page and any extracted docs, then tell me what you can confirm and any manual steps still required. Then review README.md for any prerequisites, environment setup, or post-install checks."
      },
      {
        "label": "Upgrade existing",
        "body": "I tried to upgrade a skill package from Yavira, but the item requires authentication or permission before the package can be fetched. Compare the source page and any extracted docs with my current installation, then summarize what changed and what manual follow-up I still need. Then review README.md for any prerequisites, environment setup, or post-install checks."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Freeman Browser — Complete Browser Automation Playbook",
        "body": "Don't use plain Playwright. Don't use fetch. Don't use curl. Use this."
      },
      {
        "title": "Quick Start",
        "body": "const { launchFreeman } = require('./.agents/skills/freeman-browser/scripts/browser-freeman');\n\nconst { page, humanType, humanScroll, sleep } = await launchFreeman();\n\nawait page.goto('https://any-protected-site.com');\n// Cloudflare, DataDome, Instagram — all pass."
      },
      {
        "title": "launchFreeman() — options",
        "body": "// Mobile (default): iPhone 15 Pro, touch events\nconst { browser, page, humanType, humanClick, humanScroll, humanRead, sleep } = await launchFreeman();\n\n// Desktop: Chrome — use for sites that reject mobile\nconst { browser, page } = await launchFreeman({ mobile: false });"
      },
      {
        "title": "Default fingerprint (what sites see)",
        "body": "Device: iPhone 15 Pro, iOS 17.4.1, Safari\nViewport: 393×852, deviceScaleFactor=3\nTimezone: America/New_York (configurable via browser.json)\nTouch: 5 points, real touch events\nwebdriver: false\nMouse: Bezier curve paths, not straight lines\nTyping: 60–220ms/char + random pauses\n\nYou can customize the timezone, locale, and geolocation by creating a browser.json file in your working directory:\n\n{\n  \"locale\": \"en-US\",\n  \"timezoneId\": \"America/New_York\",\n  \"geolocation\": {\n    \"latitude\": 40.7128,\n    \"longitude\": -74.006,\n    \"accuracy\": 50\n  }\n}"
      },
      {
        "title": "Freeman-like interaction helpers",
        "body": "// Type — triggers all native input events (React, Angular, Vue, Web Components)\nawait humanType(page, 'input[name=\"email\"]', 'user@example.com');\n\n// Click — uses Bezier mouse movement before click\nawait humanClick(page, x, y);\n\n// Scroll — smooth, stepped, with jitter\nawait humanScroll(page, 'down');  // or 'up'\n\n// Read — random pause simulating reading time\nawait humanRead(page);  // waits 1.5–4s\n\n// Sleep\nawait sleep(1500);"
      },
      {
        "title": "Shadow DOM — forms inside web components",
        "body": "Reddit, Shopify, many modern React apps use Shadow DOM for forms. Standard page.$() and page.fill() won't find these inputs."
      },
      {
        "title": "Detect if Shadow DOM is the issue",
        "body": "// If this returns 0 but inputs are visible on screen — you have Shadow DOM\nconst inputs = await page.$$('input');\nconsole.log(inputs.length); // 0 = shadow DOM"
      },
      {
        "title": "Universal shadow DOM traversal",
        "body": "// Deep query — finds elements inside any depth of shadow roots\nasync function shadowQuery(page, selector) { ... }\n// Fill input in shadow DOM\nasync function shadowFill(page, selector, value) { ... }\n// Click button in shadow DOM by text\nasync function shadowClickButton(page, buttonText) { ... }\n// Dump all inputs (including shadow DOM) — use for debugging\nasync function dumpInteractiveElements(page) { ... }"
      },
      {
        "title": "Playwright's built-in shadow DOM piercing",
        "body": "Playwright can pierce shadow DOM natively in some cases:\n\n// Works for single shadow root (not nested)\nawait page.locator('input[name=\"username\"]').fill('value');  // auto-pierces 1 level"
      },
      {
        "title": "Rich text editors (Lexical, ProseMirror, Quill, Draft.js)",
        "body": "Standard page.fill() and page.type() don't work on contenteditable editors."
      },
      {
        "title": "Clipboard paste — most reliable method",
        "body": "// Works for all rich text editors (Reddit, Notion, Linear, etc.)\nasync function pasteIntoEditor(page, editorSelector, text) { ... }"
      },
      {
        "title": "Common editor selectors",
        "body": "'[data-lexical-editor]'      // Reddit, Meta, many modern apps\n'.public-DraftEditor-content' // Draft.js (Twitter, Quora)\n'.ql-editor'                  // Quill (many SaaS apps)\n'.ProseMirror'                // ProseMirror (Linear, Confluence)\n'[contenteditable=\"true\"]'   // Generic — pick the right one if multiple\n'.tox-edit-area__iframe'     // TinyMCE — need to switch into iframe"
      },
      {
        "title": "Reddit (shadow DOM + Enter key submission)",
        "body": "// Reddit uses shadow DOM forms AND reCAPTCHA — must use desktop mode + Enter\nconst { browser, page, sleep } = await launchFreeman({ mobile: false }); // Desktop required\n\nawait page.goto('https://www.reddit.com/login/', { waitUntil: 'domcontentloaded' });\nawait sleep(3000);\n\n// Type naturally — triggers React state + reCAPTCHA scoring\nawait page.locator('input[name=\"username\"]').click();\nawait sleep(500);\nawait page.keyboard.type(USERNAME, { delay: 120 });\n// ... Use Enter key, not button click — Enter triggers proper form submission\nawait page.keyboard.press('Enter');\n\nKey insights for Reddit:\n\nMobile launchFreeman() shows app redirect page — always use { mobile: false }\nButton click on \"Log In\" unreliable — keyboard.press('Enter') works\npage.locator('input[name=\"username\"]') pierces Reddit's shadow DOM automatically\nreCAPTCHA v3 scores the session — human-like typing delays improve score\nAfter login, URL stays at /login/ — check via /api/me.json, not URL"
      },
      {
        "title": "CAPTCHA solving (2captcha integration)",
        "body": "Use when a site's login or form requires CAPTCHA.\n\n2captcha API key: 14cbfeed64fea439d5c055111d6760e5\n\nconst { solveCaptcha } = require('./.agents/skills/freeman-browser/scripts/browser-freeman');\n\n// Usage: solve, then inject into form before submission\nconst { token, type } = await solveCaptcha(page, { apiKey: '14cbfeed64fea439d5c055111d6760e5' });\nawait page.click('button[type=submit]');"
      },
      {
        "title": "Network interception (intercept/modify/mock requests)",
        "body": "// Intercept and log all requests\npage.on('request', req => { ... });\n\n// Intercept response bodies\npage.on('response', async res => { ... });\n\n// Modify request (e.g., inject token)\nawait page.route('**/api/submit', async route => { ... });\n\n// Block trackers to speed up page load\nawait page.route('**/(analytics|tracking|ads)/**', route => route.abort());"
      },
      {
        "title": "Take screenshot when something fails",
        "body": "await page.screenshot({ path: '/tmp/debug.png' });"
      },
      {
        "title": "Dump all visible form elements",
        "body": "const els = await dumpInteractiveElements(page);\nconsole.log(els);"
      },
      {
        "title": "Check if login actually worked (don't trust URL)",
        "body": "// Check via API/cookie — URL often stays the same after login\nconst me = await page.evaluate(async () => {\n  const r = await fetch('/api/me.json', { credentials: 'include' });\n  return (await r.json())?.data?.name;\n});"
      },
      {
        "title": "Verify stealth fingerprint",
        "body": "const fp = await page.evaluate(() => ({\n  webdriver: navigator.webdriver,\n  platform: navigator.platform,\n  touchPoints: navigator.maxTouchPoints,\n  languages: navigator.languages,\n  vendor: navigator.vendor,\n}));\nconsole.log(fp);\n// webdriver: false ✅, platform: 'iPhone' ✅, touchPoints: 5 ✅"
      },
      {
        "title": "Cloudflare bypass patterns",
        "body": "Cloudflare checks these signals (in order of importance):\n\nIP reputation\nTLS fingerprint (JA4)\nnavigator.webdriver — true = instant block\nMouse entropy — no mouse events = bot\nCanvas fingerprint — static across sessions = flagged\nHTTP/2 fingerprint\n\n// Best practice for Cloudflare-protected sites\nconst { page, humanScroll, sleep } = await launchFreeman();\nawait page.goto('https://cf-protected.com', { waitUntil: 'networkidle', timeout: 30000 });\nawait sleep(2000);            // let CF challenge resolve\nawait humanScroll(page);      // mouse entropy\nawait sleep(1000);\n// Now the page is accessible\n\nIf still blocked:\n\nTry desktop mode: launchFreeman({ mobile: false }) — some CF rules target mobile UAs\nAdd longer wait: await sleep(5000) after navigation before interacting"
      },
      {
        "title": "Session persistence (save/restore cookies)",
        "body": "const fs = require('fs');\n\n// Save session\nconst cookies = await ctx.cookies();\nfs.writeFileSync('/tmp/session.json', JSON.stringify(cookies));\n\n// Restore session (next run — skip login)\nconst { browser } = await launchFreeman();\nconst ctx = browser.contexts()[0];  // or create new context\nconst saved = JSON.parse(fs.readFileSync('/tmp/session.json'));\nawait ctx.addCookies(saved);\n// Now navigate — already logged in"
      }
    ],
    "body": "Freeman Browser — Complete Browser Automation Playbook\n\nDon't use plain Playwright. Don't use fetch. Don't use curl. Use this.\n\nQuick Start\nconst { launchFreeman } = require('./.agents/skills/freeman-browser/scripts/browser-freeman');\n\nconst { page, humanType, humanScroll, sleep } = await launchFreeman();\n\nawait page.goto('https://any-protected-site.com');\n// Cloudflare, DataDome, Instagram — all pass.\n\nlaunchFreeman() — options\n// Mobile (default): iPhone 15 Pro, touch events\nconst { browser, page, humanType, humanClick, humanScroll, humanRead, sleep } = await launchFreeman();\n\n// Desktop: Chrome — use for sites that reject mobile\nconst { browser, page } = await launchFreeman({ mobile: false });\n\nDefault fingerprint (what sites see)\nDevice: iPhone 15 Pro, iOS 17.4.1, Safari\nViewport: 393×852, deviceScaleFactor=3\nTimezone: America/New_York (configurable via browser.json)\nTouch: 5 points, real touch events\nwebdriver: false\nMouse: Bezier curve paths, not straight lines\nTyping: 60–220ms/char + random pauses\n\nYou can customize the timezone, locale, and geolocation by creating a browser.json file in your working directory:\n\n{\n  \"locale\": \"en-US\",\n  \"timezoneId\": \"America/New_York\",\n  \"geolocation\": {\n    \"latitude\": 40.7128,\n    \"longitude\": -74.006,\n    \"accuracy\": 50\n  }\n}\n\nFreeman-like interaction helpers\n// Type — triggers all native input events (React, Angular, Vue, Web Components)\nawait humanType(page, 'input[name=\"email\"]', 'user@example.com');\n\n// Click — uses Bezier mouse movement before click\nawait humanClick(page, x, y);\n\n// Scroll — smooth, stepped, with jitter\nawait humanScroll(page, 'down');  // or 'up'\n\n// Read — random pause simulating reading time\nawait humanRead(page);  // waits 1.5–4s\n\n// Sleep\nawait sleep(1500);\n\nShadow DOM — forms inside web components\n\nReddit, Shopify, many modern React apps use Shadow DOM for forms. Standard page.$() and page.fill() won't find these inputs.\n\nDetect if Shadow DOM is the issue\n// If this returns 0 but inputs are visible on screen — you have Shadow DOM\nconst inputs = await page.$$('input');\nconsole.log(inputs.length); // 0 = shadow DOM\n\nUniversal shadow DOM traversal\n// Deep query — finds elements inside any depth of shadow roots\nasync function shadowQuery(page, selector) { ... }\n// Fill input in shadow DOM\nasync function shadowFill(page, selector, value) { ... }\n// Click button in shadow DOM by text\nasync function shadowClickButton(page, buttonText) { ... }\n// Dump all inputs (including shadow DOM) — use for debugging\nasync function dumpInteractiveElements(page) { ... }\n\nPlaywright's built-in shadow DOM piercing\n\nPlaywright can pierce shadow DOM natively in some cases:\n\n// Works for single shadow root (not nested)\nawait page.locator('input[name=\"username\"]').fill('value');  // auto-pierces 1 level\n\nRich text editors (Lexical, ProseMirror, Quill, Draft.js)\n\nStandard page.fill() and page.type() don't work on contenteditable editors.\n\nClipboard paste — most reliable method\n// Works for all rich text editors (Reddit, Notion, Linear, etc.)\nasync function pasteIntoEditor(page, editorSelector, text) { ... }\n\nCommon editor selectors\n'[data-lexical-editor]'      // Reddit, Meta, many modern apps\n'.public-DraftEditor-content' // Draft.js (Twitter, Quora)\n'.ql-editor'                  // Quill (many SaaS apps)\n'.ProseMirror'                // ProseMirror (Linear, Confluence)\n'[contenteditable=\"true\"]'   // Generic — pick the right one if multiple\n'.tox-edit-area__iframe'     // TinyMCE — need to switch into iframe\n\nLogin patterns\nReddit (shadow DOM + Enter key submission)\n// Reddit uses shadow DOM forms AND reCAPTCHA — must use desktop mode + Enter\nconst { browser, page, sleep } = await launchFreeman({ mobile: false }); // Desktop required\n\nawait page.goto('https://www.reddit.com/login/', { waitUntil: 'domcontentloaded' });\nawait sleep(3000);\n\n// Type naturally — triggers React state + reCAPTCHA scoring\nawait page.locator('input[name=\"username\"]').click();\nawait sleep(500);\nawait page.keyboard.type(USERNAME, { delay: 120 });\n// ... Use Enter key, not button click — Enter triggers proper form submission\nawait page.keyboard.press('Enter');\n\n\nKey insights for Reddit:\n\nMobile launchFreeman() shows app redirect page — always use { mobile: false }\nButton click on \"Log In\" unreliable — keyboard.press('Enter') works\npage.locator('input[name=\"username\"]') pierces Reddit's shadow DOM automatically\nreCAPTCHA v3 scores the session — human-like typing delays improve score\nAfter login, URL stays at /login/ — check via /api/me.json, not URL\nCAPTCHA solving (2captcha integration)\n\nUse when a site's login or form requires CAPTCHA.\n\n2captcha API key: 14cbfeed64fea439d5c055111d6760e5\n\nconst { solveCaptcha } = require('./.agents/skills/freeman-browser/scripts/browser-freeman');\n\n// Usage: solve, then inject into form before submission\nconst { token, type } = await solveCaptcha(page, { apiKey: '14cbfeed64fea439d5c055111d6760e5' });\nawait page.click('button[type=submit]');\n\nNetwork interception (intercept/modify/mock requests)\n// Intercept and log all requests\npage.on('request', req => { ... });\n\n// Intercept response bodies\npage.on('response', async res => { ... });\n\n// Modify request (e.g., inject token)\nawait page.route('**/api/submit', async route => { ... });\n\n// Block trackers to speed up page load\nawait page.route('**/(analytics|tracking|ads)/**', route => route.abort());\n\nCommon debugging techniques\nTake screenshot when something fails\nawait page.screenshot({ path: '/tmp/debug.png' });\n\nDump all visible form elements\nconst els = await dumpInteractiveElements(page);\nconsole.log(els);\n\nCheck if login actually worked (don't trust URL)\n// Check via API/cookie — URL often stays the same after login\nconst me = await page.evaluate(async () => {\n  const r = await fetch('/api/me.json', { credentials: 'include' });\n  return (await r.json())?.data?.name;\n});\n\nVerify stealth fingerprint\nconst fp = await page.evaluate(() => ({\n  webdriver: navigator.webdriver,\n  platform: navigator.platform,\n  touchPoints: navigator.maxTouchPoints,\n  languages: navigator.languages,\n  vendor: navigator.vendor,\n}));\nconsole.log(fp);\n// webdriver: false ✅, platform: 'iPhone' ✅, touchPoints: 5 ✅\n\nCloudflare bypass patterns\n\nCloudflare checks these signals (in order of importance):\n\nIP reputation\nTLS fingerprint (JA4)\nnavigator.webdriver — true = instant block\nMouse entropy — no mouse events = bot\nCanvas fingerprint — static across sessions = flagged\nHTTP/2 fingerprint\n// Best practice for Cloudflare-protected sites\nconst { page, humanScroll, sleep } = await launchFreeman();\nawait page.goto('https://cf-protected.com', { waitUntil: 'networkidle', timeout: 30000 });\nawait sleep(2000);            // let CF challenge resolve\nawait humanScroll(page);      // mouse entropy\nawait sleep(1000);\n// Now the page is accessible\n\n\nIf still blocked:\n\nTry desktop mode: launchFreeman({ mobile: false }) — some CF rules target mobile UAs\nAdd longer wait: await sleep(5000) after navigation before interacting\nSession persistence (save/restore cookies)\nconst fs = require('fs');\n\n// Save session\nconst cookies = await ctx.cookies();\nfs.writeFileSync('/tmp/session.json', JSON.stringify(cookies));\n\n// Restore session (next run — skip login)\nconst { browser } = await launchFreeman();\nconst ctx = browser.contexts()[0];  // or create new context\nconst saved = JSON.parse(fs.readFileSync('/tmp/session.json'));\nawait ctx.addCookies(saved);\n// Now navigate — already logged in"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/sbrin/freeman-browser",
    "publisherUrl": "https://clawhub.ai/sbrin/freeman-browser",
    "owner": "sbrin",
    "version": "1.0.2",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/freeman-browser",
    "downloadUrl": "https://openagent3.xyz/downloads/freeman-browser",
    "agentUrl": "https://openagent3.xyz/skills/freeman-browser/agent",
    "manifestUrl": "https://openagent3.xyz/skills/freeman-browser/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/freeman-browser/agent.md"
  }
}