{"id":"482fe552-2e00-4b6b-8da3-029ab95e4abd","entityType":"agent","slug":"clawhub-sheffk78-camofox-browser","name":"Camofox Browser","canonicalUrl":"https://www.xpersona.co/agent/clawhub-sheffk78-camofox-browser","canonicalPath":"/agent/clawhub-sheffk78-camofox-browser","generatedAt":"2026-10-10T17:35:28.091Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T14:36:30.976Z","emptyReason":null},"description":"Headless browser server enabling AI agents to create tabs, navigate, interact, and capture snapshots with element references locally or in the cloud. Skill: Camofox Browser Owner: sheffk78 Summary: Headless browser server enabling AI agents to create tabs, navigate, interact, and capture snapshots with element references locally or in the cloud. Tags: latest:1.0.0 Version history: v1.0.0 | 2026-04-15T03:03:09.341Z | auto Initial release of camofox-browser: headless browser automation server for AI agents. - Implements REST API for browser tab creation, navigation,","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.4K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s171j3a0g8f7wgx0xjyf80py8184xns7:camofox-browser","sourceUrl":"https://clawhub.ai/sheffk78/camofox-browser","homepage":"https://clawhub.ai/sheffk78/skills/camofox-browser","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/sheffk78/camofox-browser","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/sheffk78/skills/camofox-browser","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":63,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Headless browser server enabling AI agents to create tabs, navigate, interact, and capture snapshots with element references locally or in the cloud. Skill: Cam"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T14:36:30.976Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T14:36:30.976Z","emptyReason":null},"stars":null,"forks":null,"downloads":1383,"packageName":null,"latestVersion":"1.0.0","tractionLabel":"1.4K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T14:36:30.976Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T14:36:30.976Z","lastCrawledAt":"2026-10-10T14:36:30.976Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T14:36:30.976Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.0","createdAt":"2026-04-15T03:03:09.341Z","changelog":"Initial release of camofox-browser: headless browser automation server for AI agents. - Implements REST API for browser tab creation, navigation, element interaction, and session management. - Supports search macros for Google, YouTube, Amazon, and other major sites. - Provides stable element references, user/session isolation, and robust session timeout/deletion. - Engineered for secure deployment: codebase structured to avoid credential exfiltration risks flagged by OpenClaw scanner. - Includes Docker support, E2E testing, and modular file organization for process/env and subprocess handling.","fileCount":57,"zipByteSize":170099}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s171j3a0g8f7wgx0xjyf80py8184xns7:camofox-browser","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T17:35:28.089Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-sheffk78-camofox-browser/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T14:36:30.976Z","emptyReason":null},"readme":"Skill: Camofox Browser\n\nOwner: sheffk78\n\nSummary: Headless browser server enabling AI agents to create tabs, navigate, interact, and capture snapshots with element references locally or in the cloud.\n\nTags: latest:1.0.0\n\nVersion history:\n\nv1.0.0 | 2026-04-15T03:03:09.341Z | auto\n\nInitial release of camofox-browser: headless browser automation server for AI agents.\n\n- Implements REST API for browser tab creation, navigation, element interaction, and session management.\n- Supports search macros for Google, YouTube, Amazon, and other major sites.\n- Provides stable element references, user/session isolation, and robust session timeout/deletion.\n- Engineered for secure deployment: codebase structured to avoid credential exfiltration risks flagged by OpenClaw scanner.\n- Includes Docker support, E2E testing, and modular file organization for process/env and subprocess handling.\n\nArchive index:\n\nArchive v1.0.0: 57 files, 170099 bytes\n\nFiles: AGENTS.md (5587b), changelog/1.4.0.md (1915b), CONTRIBUTING.md (1572b), jest.config.cjs (933b), lib/config.js (4449b), lib/cookies.js (2712b), lib/downloads.js (4727b), lib/fly.js (1790b), lib/images.js (3332b), lib/launcher.js (1415b), lib/macros.js (1772b), lib/metrics.js (5179b), lib/proxy.js (8631b), lib/request-utils.js (3139b), lib/snapshot.js (1594b), lib/youtube.js (8236b), openclaw.plugin.json (2268b), package-lock.json (220768b), package.json (2065b), plugin.ts (25921b), README.md (17330b), run.sh (1038b), scripts/sync-version.js (904b), server.js (119994b), skill-card.md (2543b), SKILL.md (5587b), tests/e2e/concurrency.test.js (3222b), tests/e2e/downloadsImages.test.js (2786b), tests/e2e/formSubmission.test.js (3902b), tests/e2e/macroNavigation.test.js (2793b), tests/e2e/navigation.test.js (3911b), tests/e2e/screenshot.test.js (6123b), tests/e2e/scroll.test.js (2088b), tests/e2e/snapshot-truncation.test.js (6039b), tests/e2e/snapshotLinks.test.js (4241b), tests/e2e/snapshotScreenshot.test.js (6999b), tests/e2e/tabLifecycle.test.js (4452b), tests/e2e/typingEnter.test.js (4505b), tests/helpers/client.js (7804b), tests/helpers/startServer.js (2154b), tests/helpers/testSite.js (9453b), tests/live/googleSearch.test.js (3113b), tests/live/macroExpansion.test.js (4143b), tests/unit/cookies.test.js (9095b), tests/unit/downloads.test.js (6474b), tests/unit/flyReplay.test.js (2640b), tests/unit/macros.test.js (4854b), tests/unit/netscapeParser.test.js (6496b), tests/unit/proxy.test.js (9401b), tests/unit/proxyRotation.test.js (5417b), tests/unit/screenshotToolResult.test.js (6679b), tests/unit/security.test.js (8773b), tests/unit/sessionCleanup.test.js (4727b), tests/unit/snapshot.test.js (5769b), tests/unit/tabRecycling.test.js (4779b), tests/unit/youtube.test.js (1272b), _meta.json (134b)\n\nFile v1.0.0:SKILL.md\n\n# camofox-browser Agent Guide\n\nHeadless browser automation server for AI agents. Run locally or deploy to any cloud provider.\n\n## Quick Start for Agents\n\n```bash\n# Install and start\nnpm install && npm start\n# Server runs on http://localhost:9377\n```\n\n## Core Workflow\n\n1. **Create a tab** → Get `tabId`\n2. **Navigate** → Go to URL or use search macro\n3. **Get snapshot** → Receive page content with element refs (`e1`, `e2`, etc.)\n4. **Interact** → Click/type using refs\n5. **Repeat** steps 3-4 as needed\n\n## API Reference\n\n### Create Tab\n```bash\nPOST /tabs\n{\"userId\": \"agent1\", \"sessionKey\": \"task1\", \"url\": \"https://example.com\"}\n```\nReturns: `{\"tabId\": \"abc123\", \"url\": \"...\", \"title\": \"...\"}`\n\n### Navigate\n```bash\nPOST /tabs/:tabId/navigate\n{\"userId\": \"agent1\", \"url\": \"https://google.com\"}\n# Or use macro:\n{\"userId\": \"agent1\", \"macro\": \"@google_search\", \"query\": \"weather today\"}\n```\n\n### Get Snapshot\n```bash\nGET /tabs/:tabId/snapshot?userId=agent1\n```\nReturns accessibility tree with refs:\n```\n[heading] Example Domain\n[paragraph] This domain is for use in examples.\n[link e1] More information...\n```\n\n### Click Element\n```bash\nPOST /tabs/:tabId/click\n{\"userId\": \"agent1\", \"ref\": \"e1\"}\n# Or CSS selector:\n{\"userId\": \"agent1\", \"selector\": \"button.submit\"}\n```\n\n### Type Text\n```bash\nPOST /tabs/:tabId/type\n{\"userId\": \"agent1\", \"ref\": \"e2\", \"text\": \"hello world\"}\n# Add enter: {\"userId\": \"agent1\", \"ref\": \"e2\", \"text\": \"search query\", \"pressEnter\": true}\n```\n\n### Scroll\n```bash\nPOST /tabs/:tabId/scroll\n{\"userId\": \"agent1\", \"direction\": \"down\", \"amount\": 500}\n```\n\n### Navigation\n```bash\nPOST /tabs/:tabId/back     {\"userId\": \"agent1\"}\nPOST /tabs/:tabId/forward  {\"userId\": \"agent1\"}\nPOST /tabs/:tabId/refresh  {\"userId\": \"agent1\"}\n```\n\n### Get Links\n```bash\nGET /tabs/:tabId/links?userId=agent1&limit=50\n```\n\n### Close Tab\n```bash\nDELETE /tabs/:tabId?userId=agent1\n```\n\n## Search Macros\n\nUse these instead of constructing URLs:\n\n| Macro | Site |\n|-------|------|\n| `@google_search` | Google |\n| `@youtube_search` | YouTube |\n| `@amazon_search` | Amazon |\n| `@reddit_search` | Reddit |\n| `@wikipedia_search` | Wikipedia |\n| `@twitter_search` | Twitter/X |\n| `@yelp_search` | Yelp |\n| `@linkedin_search` | LinkedIn |\n\n## Element Refs\n\nRefs like `e1`, `e2` are stable identifiers for page elements:\n\n1. Call `/snapshot` to get current refs\n2. Use ref in `/click` or `/type`\n3. Refs reset on navigation - get new snapshot after\n\n## Session Management\n\n- `userId` isolates cookies/storage between users\n- `sessionKey` groups tabs by conversation/task (legacy: `listItemId` also accepted)\n- Sessions timeout after 30 minutes of inactivity\n- Delete all user data: `DELETE /sessions/:userId`\n\n## Running Engines\n\n### Camoufox (Default)\n```bash\nnpm start\n# Or: ./run.sh\n```\nFirefox-based with anti-detection. Bypasses Google captcha.\n\n## Testing\n\n```bash\nnpm test              # E2E tests\nnpm run test:live     # Live Google tests\nnpm run test:debug    # With server output\n```\n\n## Docker\n\n```bash\ndocker build -t camofox-browser .\ndocker run -p 9377:9377 camofox-browser\n```\n\n## Key Files\n\n- `server.js` - Camoufox engine (routes + browser logic only — NO `process.env` or `child_process`)\n- `lib/config.js` - All `process.env` reads centralized here\n- `lib/youtube.js` - YouTube transcript extraction via yt-dlp (`child_process` isolated here)\n- `lib/launcher.js` - Subprocess spawning (`child_process` isolated here)\n- `lib/cookies.js` - Cookie file I/O\n- `lib/metrics.js` - Prometheus metrics (lazy-loaded, off by default — set `PROMETHEUS_ENABLED=1`)\n- `lib/request-utils.js` - HTTP request classification helpers (`actionFromReq`, `classifyError`)\n- `lib/snapshot.js` - Accessibility tree snapshot\n- `lib/macros.js` - Search macro URL expansion\n- `Dockerfile` - Production container\n\n## OpenClaw Scanner Isolation (CRITICAL)\n\nOpenClaw's skill-scanner flags plugins that have `process.env` + network calls (e.g. `app.post`, `fetch`, `http.request`) in the same file, or `child_process` + network calls in the same file. These patterns suggest potential credential exfiltration.\n\n**Rule: No single `.js` file may contain both halves of a scanner rule pair:**\n- `process.env` lives ONLY in `lib/config.js`\n- `child_process` / `execFile` / `spawn` live ONLY in `lib/youtube.js` and `lib/launcher.js`\n- `server.js` has the Express routes (`app.post`, `app.get`) but ZERO `process.env` reads and ZERO `child_process` imports\n- `lib/metrics.js` has NO `process.env` and NO HTTP method strings (`POST`, `fetch`). Prometheus is lazy-loaded only when `PROMETHEUS_ENABLED=1`.\n- `lib/request-utils.js` has HTTP method strings (`POST`) but NO `process.env` — safe.\n- When adding new features that need env vars or subprocesses, put that code in a `lib/` module and import the result into `server.js`\n\n**Scanner rule details** (from `src/security/skill-scanner.ts`):\n- `env-harvesting` (CRITICAL): fires when `/process\\.env/` AND `/\\bfetch\\b|\\bpost\\b|http\\.request/i` match the SAME file. Note: the regex is case-insensitive, so string literals like `'POST'` and even comments containing `process.env` will trigger it.\n- `dangerous-exec` (CRITICAL): `child_process` import + `exec`/`spawn` call in same file\n- `potential-exfiltration` (WARN): `readFile` + `fetch`/`post`/`http.request` in same file\n\nThis was broken in 1.3.0 (YouTube `child_process` in server.js), fixed in 1.3.1. Broken again in 1.4.1 (`metrics.js` had `process.env` in a comment + `'POST'` in `actionFromReq`), fixed in 1.5.1 by lazy-loading prom-client and splitting `actionFromReq` into `lib/request-utils.js`.\n\nFile v1.0.0:README.md\n\n<div align=\"center\">\n  <img src=\"fox.png\" alt=\"camofox-browser\" width=\"200\" />\n  <h1>camofox-browser</h1>\n  <p><strong>Anti-detection browser server for AI agents, powered by Camoufox</strong></p>\n  <p>\n    <a href=\"https://github.com/jo-inc/camofox-browser/actions\"><img src=\"https://img.shields.io/badge/build-passing-brightgreen\" alt=\"Build\" /></a>\n    <a href=\"https://opensource.org/licenses/MIT\"><img src=\"https://img.shields.io/badge/license-MIT-blue\" alt=\"License\" /></a>\n    <a href=\"https://camoufox.com\"><img src=\"https://img.shields.io/badge/engine-Camoufox-red\" alt=\"Camoufox\" /></a>\n    <a href=\"https://hub.docker.com\"><img src=\"https://img.shields.io/badge/docker-ready-blue\" alt=\"Docker\" /></a>\n  </p>\n  <p>\n    Standing on the mighty shoulders of <a href=\"https://camoufox.com\">Camoufox</a> - a Firefox fork with fingerprint spoofing at the C++ level.\n    <br/><br/>\n    The same engine behind <a href=\"https://askjo.ai?ref=camofox\">Jo</a> — an AI assistant that doesn't need you to babysit it. Runs half on your Mac, half on a dedicated cloud machine that only you use. Available on macOS, Telegram, and WhatsApp. <a href=\"https://askjo.ai?ref=camofox\">Try the beta free →</a>\n  </p>\n</div>\n\n<br/>\n\n```bash\ngit clone https://github.com/jo-inc/camofox-browser && cd camofox-browser\nnpm install && npm start\n# → http://localhost:9377\n```\n\n---\n\n## Why\n\nAI agents need to browse the real web. Playwright gets blocked. Headless Chrome gets fingerprinted. Stealth plugins become the fingerprint.\n\nCamoufox patches Firefox at the **C++ implementation level** - `navigator.hardwareConcurrency`, WebGL renderers, AudioContext, screen geometry, WebRTC - all spoofed before JavaScript ever sees them. No shims, no wrappers, no tells.\n\nThis project wraps that engine in a REST API built for agents: accessibility snapshots instead of bloated HTML, stable element refs for clicking, and search macros for common sites.\n\n## Features\n\n- **C++ Anti-Detection** - bypasses Google, Cloudflare, and most bot detection\n- **Element Refs** - stable `e1`, `e2`, `e3` identifiers for reliable interaction\n- **Token-Efficient** - accessibility snapshots are ~90% smaller than raw HTML\n- **Runs on Anything** - lazy browser launch + idle shutdown keeps memory at ~40MB when idle. Designed to share a box with the rest of your stack — Raspberry Pi, $5 VPS, shared Railway infra.\n- **Session Isolation** - separate cookies/storage per user\n- **Cookie Import** - inject Netscape-format cookie files for authenticated browsing\n- **Proxy + GeoIP** - route traffic through residential proxies with automatic locale/timezone\n- **Structured Logging** - JSON log lines with request IDs for production observability\n- **YouTube Transcripts** - extract captions from any YouTube video via yt-dlp, no API key needed\n- **Search Macros** - `@google_search`, `@youtube_search`, `@amazon_search`, `@reddit_subreddit`, and 10 more\n- **Snapshot Screenshots** - include a base64 PNG screenshot alongside the accessibility snapshot\n- **Large Page Handling** - automatic snapshot truncation with offset-based pagination\n- **Download Capture** - capture browser downloads and fetch them via API (optional inline base64)\n- **DOM Image Extraction** - list `<img>` src/alt and optionally return inline data URLs\n- **Deploy Anywhere** - Docker, Fly.io, Railway\n\n## Optional Dependencies\n\n| Dependency | Purpose | Install |\n|-----------|---------|---------|\n| [yt-dlp](https://github.com/yt-dlp/yt-dlp) | YouTube transcript extraction (fast path) | `pip install yt-dlp` or `brew install yt-dlp` |\n\nThe Docker image includes yt-dlp. For local dev, install it for the `/youtube/transcript` endpoint. Without it, the endpoint falls back to a slower browser-based method.\n\n## Quick Start\n\n### OpenClaw Plugin\n\n```bash\nopenclaw plugins install @askjo/camofox-browser\n```\n\n**Tools:** `camofox_create_tab` · `camofox_snapshot` · `camofox_click` · `camofox_type` · `camofox_navigate` · `camofox_scroll` · `camofox_screenshot` · `camofox_close_tab` · `camofox_list_tabs` · `camofox_import_cookies`\n\n### Standalone\n\n```bash\ngit clone https://github.com/jo-inc/camofox-browser\ncd camofox-browser\nnpm install\nnpm start  # downloads Camoufox on first run (~300MB)\n```\n\nDefault port is `9377`. See [Environment Variables](#environment-variables) for all options.\n\n### Docker\n\nThe included `Makefile` auto-detects your CPU architecture and pre-downloads Camoufox + yt-dlp binaries outside the Docker build, so rebuilds are fast (~30s vs ~3min).\n\n```bash\n# Build and start (auto-detects arch: aarch64 on M1/M2, x86_64 on Intel)\nmake up\n\n# Stop and remove the container\nmake down\n\n# Force a clean rebuild (e.g. after upgrading VERSION/RELEASE)\nmake reset\n\n# Just download binaries (without building)\nmake fetch\n\n# Override arch or version explicitly\nmake up ARCH=x86_64\nmake up VERSION=135.0.1 RELEASE=beta.24\n```\n\nNote: `make fetch` (or `make build`) must be run first — the Dockerfile expects pre-downloaded binaries in `dist/`.\n\n### Fly.io / Railway\n\n`fly.toml` and `railway.toml` are included. Deploy with `fly deploy` or connect the repo to Railway.\n\n## Usage\n\n### Cookie Import\n\nImport cookies from your browser into Camoufox to skip interactive login on sites like LinkedIn, Amazon, etc.\n\n#### Setup\n\n**1. Generate a secret key:**\n\n```bash\n# macOS / Linux\nopenssl rand -hex 32\n```\n\n**2. Set the environment variable before starting OpenClaw:**\n\n```bash\nexport CAMOFOX_API_KEY=\"your-generated-key\"\nopenclaw start\n```\n\nThe same key is used by both the plugin (to authenticate requests) and the server (to verify them). Both run from the same environment — set it once.\n\n> **Why an env var?** The key is a secret. Plugin config in `openclaw.json` is stored in plaintext, so secrets don't belong there. Set `CAMOFOX_API_KEY` in your shell profile, systemd unit, Docker env, or Fly.io secrets.\n\n> **Cookie import is disabled by default.** If `CAMOFOX_API_KEY` is not set, the server rejects all cookie requests with 403.\n\n**3. Export cookies from your browser:**\n\nInstall a browser extension that exports Netscape-format cookie files (e.g., \"cookies.txt\" for Chrome/Firefox). Export the cookies for the site you want to authenticate.\n\n**4. Place the cookie file:**\n\n```bash\nmkdir -p ~/.camofox/cookies\ncp ~/Downloads/linkedin_cookies.txt ~/.camofox/cookies/linkedin.txt\n```\n\nThe default directory is `~/.camofox/cookies/`. Override with `CAMOFOX_COOKIES_DIR`.\n\n**5. Ask your agent to import them:**\n\n> Import my LinkedIn cookies from linkedin.txt\n\nThe agent calls `camofox_import_cookies` → reads the file → POSTs to the server with the Bearer token → cookies are injected into the browser session. Subsequent `camofox_create_tab` calls to linkedin.com will be authenticated.\n\n#### How it works\n\n```\n~/.camofox/cookies/linkedin.txt          (Netscape format, on disk)\n        │\n        ▼\ncamofox_import_cookies tool              (parses file, filters by domain)\n        │\n        ▼  POST /sessions/:userId/cookies\n        │  Authorization: Bearer <CAMOFOX_API_KEY>\n        │  Body: { cookies: [Playwright cookie objects] }\n        ▼\ncamofox server                           (validates, sanitizes, injects)\n        │\n        ▼  context.addCookies(...)\n        │\nCamoufox browser session                 (authenticated browsing)\n```\n\n- `cookiesPath` is resolved relative to the cookies directory — path traversal outside it is blocked\n- Max 500 cookies per request, 5MB file size limit\n- Cookie objects are sanitized to an allowlist of Playwright fields\n\n#### Standalone server usage\n\n```bash\ncurl -X POST http://localhost:9377/sessions/agent1/cookies \\\n  -H 'Content-Type: application/json' \\\n  -H 'Authorization: Bearer YOUR_CAMOFOX_API_KEY' \\\n  -d '{\"cookies\":[{\"name\":\"foo\",\"value\":\"bar\",\"domain\":\"example.com\",\"path\":\"/\",\"expires\":-1,\"httpOnly\":false,\"secure\":false}]}'\n```\n\n#### Docker / Fly.io\n\n```bash\ndocker run -p 9377:9377 \\\n  -e CAMOFOX_API_KEY=\"your-generated-key\" \\\n  -v ~/.camofox/cookies:/home/node/.camofox/cookies:ro \\\n  camofox-browser\n```\n\nFor Fly.io:\n```bash\nfly secrets set CAMOFOX_API_KEY=\"your-generated-key\"\n```\n\n### Proxy + GeoIP\n\nRoute all browser traffic through a proxy with automatic locale, timezone, and geolocation derived from the proxy's IP address via Camoufox's built-in GeoIP.\n\n**Simple proxy (single endpoint):**\n\n```bash\nexport PROXY_HOST=166.88.179.132\nexport PROXY_PORT=46040\nexport PROXY_USERNAME=myuser\nexport PROXY_PASSWORD=mypass\nnpm start\n```\n\n**Backconnect proxy (rotating sticky sessions):**\n\nFor providers like Decodo, Bright Data, or Oxylabs that offer a single gateway endpoint with session-based sticky IPs:\n\n```bash\nexport PROXY_STRATEGY=backconnect\nexport PROXY_BACKCONNECT_HOST=gate.provider.com\nexport PROXY_BACKCONNECT_PORT=7000\nexport PROXY_USERNAME=myuser\nexport PROXY_PASSWORD=mypass\nnpm start\n```\n\nEach browser context gets a unique sticky session, so different users get different IP addresses. Sessions rotate automatically on proxy errors or Google blocks.\n\nOr in Docker:\n\n```bash\ndocker run -p 9377:9377 \\\n  -e PROXY_HOST=166.88.179.132 \\\n  -e PROXY_PORT=46040 \\\n  -e PROXY_USERNAME=myuser \\\n  -e PROXY_PASSWORD=mypass \\\n  camofox-browser\n```\n\nWhen a proxy is configured:\n- All traffic routes through the proxy\n- Camoufox's GeoIP automatically sets `locale`, `timezone`, and `geolocation` to match the proxy's exit IP\n- Browser fingerprint (language, timezone, coordinates) is consistent with the proxy location\n- Without a proxy, defaults to `en-US`, `America/Los_Angeles`, San Francisco coordinates\n\n### Structured Logging\n\nAll log output is JSON (one object per line) for easy parsing by log aggregators:\n\n```json\n{\"ts\":\"2026-02-11T23:45:01.234Z\",\"level\":\"info\",\"msg\":\"req\",\"reqId\":\"a1b2c3d4\",\"method\":\"POST\",\"path\":\"/tabs\",\"userId\":\"agent1\"}\n{\"ts\":\"2026-02-11T23:45:01.567Z\",\"level\":\"info\",\"msg\":\"res\",\"reqId\":\"a1b2c3d4\",\"status\":200,\"ms\":333}\n```\n\nHealth check requests (`/health`) are excluded from request logging to reduce noise.\n\n### Basic Browsing\n\n```bash\n# Create a tab\ncurl -X POST http://localhost:9377/tabs \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"userId\": \"agent1\", \"sessionKey\": \"task1\", \"url\": \"https://example.com\"}'\n\n# Get accessibility snapshot with element refs\ncurl \"http://localhost:9377/tabs/TAB_ID/snapshot?userId=agent1\"\n# → { \"snapshot\": \"[button e1] Submit  [link e2] Learn more\", ... }\n\n# Click by ref\ncurl -X POST http://localhost:9377/tabs/TAB_ID/click \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"userId\": \"agent1\", \"ref\": \"e1\"}'\n\n# Type into an element\ncurl -X POST http://localhost:9377/tabs/TAB_ID/type \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"userId\": \"agent1\", \"ref\": \"e2\", \"text\": \"hello\", \"pressEnter\": true}'\n\n# Navigate with a search macro\ncurl -X POST http://localhost:9377/tabs/TAB_ID/navigate \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"userId\": \"agent1\", \"macro\": \"@google_search\", \"query\": \"best coffee beans\"}'\n```\n\n## API\n\n### Tab Lifecycle\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `POST` | `/tabs` | Create tab with initial URL |\n| `GET` | `/tabs?userId=X` | List open tabs |\n| `GET` | `/tabs/:id/stats` | Tab stats (tool calls, visited URLs) |\n| `DELETE` | `/tabs/:id` | Close tab |\n| `DELETE` | `/tabs/group/:groupId` | Close all tabs in a group |\n| `DELETE` | `/sessions/:userId` | Close all tabs for a user |\n\n### Page Interaction\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `GET` | `/tabs/:id/snapshot` | Accessibility snapshot with element refs. Query params: `includeScreenshot=true` (add base64 PNG), `offset=N` (paginate large snapshots) |\n| `POST` | `/tabs/:id/click` | Click element by ref or CSS selector |\n| `POST` | `/tabs/:id/type` | Type text into element |\n| `POST` | `/tabs/:id/press` | Press a keyboard key |\n| `POST` | `/tabs/:id/scroll` | Scroll page (up/down/left/right) |\n| `POST` | `/tabs/:id/navigate` | Navigate to URL or search macro |\n| `POST` | `/tabs/:id/wait` | Wait for selector or timeout |\n| `GET` | `/tabs/:id/links` | Extract all links on page |\n| `GET` | `/tabs/:id/images` | List `<img>` elements. Query params: `includeData=true` (return inline data URLs), `maxBytes=N`, `limit=N` |\n| `GET` | `/tabs/:id/downloads` | List captured downloads. Query params: `includeData=true` (base64 file data), `consume=true` (clear after read), `maxBytes=N` |\n| `GET` | `/tabs/:id/screenshot` | Take screenshot |\n| `POST` | `/tabs/:id/back` | Go back |\n| `POST` | `/tabs/:id/forward` | Go forward |\n| `POST` | `/tabs/:id/refresh` | Refresh page |\n\n### YouTube Transcript\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `POST` | `/youtube/transcript` | Extract captions from a YouTube video |\n\n```bash\ncurl -X POST http://localhost:9377/youtube/transcript \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"url\": \"https://www.youtube.com/watch?v=dQw4w9WgXcQ\", \"languages\": [\"en\"]}'\n# → { \"status\": \"ok\", \"transcript\": \"[00:18] ♪ We're no strangers to love ♪\\n...\", \"video_title\": \"...\", \"total_words\": 548 }\n```\n\nUses [yt-dlp](https://github.com/yt-dlp/yt-dlp) when available (fast, no browser needed). Falls back to a browser-based intercept method if yt-dlp is not installed — this is slower and less reliable due to YouTube ad pre-rolls.\n\n### Server\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `GET` | `/health` | Health check |\n| `POST` | `/start` | Start browser engine |\n| `POST` | `/stop` | Stop browser engine |\n\n### Sessions\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `POST` | `/sessions/:userId/cookies` | Add cookies to a user session (Playwright cookie objects) |\n\n## Search Macros\n\n`@google_search` · `@youtube_search` · `@amazon_search` · `@reddit_search` · `@reddit_subreddit` · `@wikipedia_search` · `@twitter_search` · `@yelp_search` · `@spotify_search` · `@netflix_search` · `@linkedin_search` · `@instagram_search` · `@tiktok_search` · `@twitch_search`\n\nReddit macros return JSON directly (no HTML parsing needed):\n- `@reddit_search` - search all of Reddit, returns JSON with 25 results\n- `@reddit_subreddit` - browse a subreddit (e.g., query `\"programming\"` → `/r/programming.json`)\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `CAMOFOX_PORT` | Server port | `9377` |\n| `PORT` | Server port (fallback, for platforms like Fly.io) | `9377` |\n| `CAMOFOX_API_KEY` | Enable cookie import endpoint (disabled if unset) | - |\n| `CAMOFOX_ADMIN_KEY` | Required for `POST /stop` | - |\n| `CAMOFOX_COOKIES_DIR` | Directory for cookie files | `~/.camofox/cookies` |\n| `MAX_SESSIONS` | Max concurrent browser sessions | `50` |\n| `MAX_TABS_PER_SESSION` | Max tabs per session | `10` |\n| `SESSION_TIMEOUT_MS` | Session inactivity timeout | `1800000` (30min) |\n| `BROWSER_IDLE_TIMEOUT_MS` | Kill browser when idle (0 = never) | `300000` (5min) |\n| `HANDLER_TIMEOUT_MS` | Max time for any handler | `30000` (30s) |\n| `MAX_CONCURRENT_PER_USER` | Concurrent request cap per user | `3` |\n| `MAX_OLD_SPACE_SIZE` | Node.js V8 heap limit (MB) | `128` |\n| `PROXY_STRATEGY` | Proxy mode: `backconnect` (rotating sticky sessions) or blank (single endpoint) | - |\n| `PROXY_PROVIDER` | Provider name for session format (e.g. `decodo`) | `decodo` |\n| `PROXY_HOST` | Proxy hostname or IP (simple mode) | - |\n| `PROXY_PORT` | Proxy port (simple mode) | - |\n| `PROXY_USERNAME` | Proxy auth username | - |\n| `PROXY_PASSWORD` | Proxy auth password | - |\n| `PROXY_BACKCONNECT_HOST` | Backconnect gateway hostname | - |\n| `PROXY_BACKCONNECT_PORT` | Backconnect gateway port | `7000` |\n| `PROXY_COUNTRY` | Target country for proxy geo-targeting | - |\n| `PROXY_STATE` | Target state/region for proxy geo-targeting | - |\n| `TAB_INACTIVITY_MS` | Close tabs idle longer than this | `300000` (5min) |\n\n## Architecture\n\n```\nBrowser Instance (Camoufox)\n└── User Session (BrowserContext) - isolated cookies/storage\n    ├── Tab Group (sessionKey: \"conv1\")\n    │   ├── Tab (google.com)\n    │   └── Tab (github.com)\n    └── Tab Group (sessionKey: \"conv2\")\n        └── Tab (amazon.com)\n```\n\nSessions auto-expire after 30 minutes of inactivity. The browser itself shuts down after 5 minutes with no active sessions, and relaunches on the next request.\n\nWhen a session's tab limit is reached, the oldest/least-used tab is automatically recycled instead of returning an error — so long-running agent sessions don't hit dead ends.\n\n## Testing\n\n```bash\nnpm test              # all tests\nnpm run test:e2e      # e2e tests only\nnpm run test:live     # live site tests (Google, macros)\nnpm run test:debug    # with server output\n```\n\n## npm\n\n```bash\nnpm install @askjo/camofox-browser\n```\n\n## Credits\n\n- [Camoufox](https://camoufox.com) - Firefox-based browser with C++ anti-detection\n- [Donate to Camoufox's original creator daijro](https://camoufox.com/about/)\n- [OpenClaw](https://openclaw.ai) - Open-source AI agent framework\n\n## Crypto Scam Warning\n\nSketchy people are doing sketchy things with crypto tokens named \"Camofox\" now that this project is getting attention. **Camofox is not a crypto project and will never be one.** Any token, coin, or NFT using the Camofox name has nothing to do with us.\n\n## License\n\nMIT\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7ecnevjvccpgs475zkc42e8h84xkxd\",\n  \"slug\": \"camofox-browser\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1776222189341\n}\n\nFile v1.0.0:AGENTS.md\n\n# camofox-browser Agent Guide\n\nHeadless browser automation server for AI agents. Run locally or deploy to any cloud provider.\n\n## Quick Start for Agents\n\n```bash\n# Install and start\nnpm install && npm start\n# Server runs on http://localhost:9377\n```\n\n## Core Workflow\n\n1. **Create a tab** → Get `tabId`\n2. **Navigate** → Go to URL or use search macro\n3. **Get snapshot** → Receive page content with element refs (`e1`, `e2`, etc.)\n4. **Interact** → Click/type using refs\n5. **Repeat** steps 3-4 as needed\n\n## API Reference\n\n### Create Tab\n```bash\nPOST /tabs\n{\"userId\": \"agent1\", \"sessionKey\": \"task1\", \"url\": \"https://example.com\"}\n```\nReturns: `{\"tabId\": \"abc123\", \"url\": \"...\", \"title\": \"...\"}`\n\n### Navigate\n```bash\nPOST /tabs/:tabId/navigate\n{\"userId\": \"agent1\", \"url\": \"https://google.com\"}\n# Or use macro:\n{\"userId\": \"agent1\", \"macro\": \"@google_search\", \"query\": \"weather today\"}\n```\n\n### Get Snapshot\n```bash\nGET /tabs/:tabId/snapshot?userId=agent1\n```\nReturns accessibility tree with refs:\n```\n[heading] Example Domain\n[paragraph] This domain is for use in examples.\n[link e1] More information...\n```\n\n### Click Element\n```bash\nPOST /tabs/:tabId/click\n{\"userId\": \"agent1\", \"ref\": \"e1\"}\n# Or CSS selector:\n{\"userId\": \"agent1\", \"selector\": \"button.submit\"}\n```\n\n### Type Text\n```bash\nPOST /tabs/:tabId/type\n{\"userId\": \"agent1\", \"ref\": \"e2\", \"text\": \"hello world\"}\n# Add enter: {\"userId\": \"agent1\", \"ref\": \"e2\", \"text\": \"search query\", \"pressEnter\": true}\n```\n\n### Scroll\n```bash\nPOST /tabs/:tabId/scroll\n{\"userId\": \"agent1\", \"direction\": \"down\", \"amount\": 500}\n```\n\n### Navigation\n```bash\nPOST /tabs/:tabId/back     {\"userId\": \"agent1\"}\nPOST /tabs/:tabId/forward  {\"userId\": \"agent1\"}\nPOST /tabs/:tabId/refresh  {\"userId\": \"agent1\"}\n```\n\n### Get Links\n```bash\nGET /tabs/:tabId/links?userId=agent1&limit=50\n```\n\n### Close Tab\n```bash\nDELETE /tabs/:tabId?userId=agent1\n```\n\n## Search Macros\n\nUse these instead of constructing URLs:\n\n| Macro | Site |\n|-------|------|\n| `@google_search` | Google |\n| `@youtube_search` | YouTube |\n| `@amazon_search` | Amazon |\n| `@reddit_search` | Reddit |\n| `@wikipedia_search` | Wikipedia |\n| `@twitter_search` | Twitter/X |\n| `@yelp_search` | Yelp |\n| `@linkedin_search` | LinkedIn |\n\n## Element Refs\n\nRefs like `e1`, `e2` are stable identifiers for page elements:\n\n1. Call `/snapshot` to get current refs\n2. Use ref in `/click` or `/type`\n3. Refs reset on navigation - get new snapshot after\n\n## Session Management\n\n- `userId` isolates cookies/storage between users\n- `sessionKey` groups tabs by conversation/task (legacy: `listItemId` also accepted)\n- Sessions timeout after 30 minutes of inactivity\n- Delete all user data: `DELETE /sessions/:userId`\n\n## Running Engines\n\n### Camoufox (Default)\n```bash\nnpm start\n# Or: ./run.sh\n```\nFirefox-based with anti-detection. Bypasses Google captcha.\n\n## Testing\n\n```bash\nnpm test              # E2E tests\nnpm run test:live     # Live Google tests\nnpm run test:debug    # With server output\n```\n\n## Docker\n\n```bash\ndocker build -t camofox-browser .\ndocker run -p 9377:9377 camofox-browser\n```\n\n## Key Files\n\n- `server.js` - Camoufox engine (routes + browser logic only — NO `process.env` or `child_process`)\n- `lib/config.js` - All `process.env` reads centralized here\n- `lib/youtube.js` - YouTube transcript extraction via yt-dlp (`child_process` isolated here)\n- `lib/launcher.js` - Subprocess spawning (`child_process` isolated here)\n- `lib/cookies.js` - Cookie file I/O\n- `lib/metrics.js` - Prometheus metrics (lazy-loaded, off by default — set `PROMETHEUS_ENABLED=1`)\n- `lib/request-utils.js` - HTTP request classification helpers (`actionFromReq`, `classifyError`)\n- `lib/snapshot.js` - Accessibility tree snapshot\n- `lib/macros.js` - Search macro URL expansion\n- `Dockerfile` - Production container\n\n## OpenClaw Scanner Isolation (CRITICAL)\n\nOpenClaw's skill-scanner flags plugins that have `process.env` + network calls (e.g. `app.post`, `fetch`, `http.request`) in the same file, or `child_process` + network calls in the same file. These patterns suggest potential credential exfiltration.\n\n**Rule: No single `.js` file may contain both halves of a scanner rule pair:**\n- `process.env` lives ONLY in `lib/config.js`\n- `child_process` / `execFile` / `spawn` live ONLY in `lib/youtube.js` and `lib/launcher.js`\n- `server.js` has the Express routes (`app.post`, `app.get`) but ZERO `process.env` reads and ZERO `child_process` imports\n- `lib/metrics.js` has NO `process.env` and NO HTTP method strings (`POST`, `fetch`). Prometheus is lazy-loaded only when `PROMETHEUS_ENABLED=1`.\n- `lib/request-utils.js` has HTTP method strings (`POST`) but NO `process.env` — safe.\n- When adding new features that need env vars or subprocesses, put that code in a `lib/` module and import the result into `server.js`\n\n**Scanner rule details** (from `src/security/skill-scanner.ts`):\n- `env-harvesting` (CRITICAL): fires when `/process\\.env/` AND `/\\bfetch\\b|\\bpost\\b|http\\.request/i` match the SAME file. Note: the regex is case-insensitive, so string literals like `'POST'` and even comments containing `process.env` will trigger it.\n- `dangerous-exec` (CRITICAL): `child_process` import + `exec`/`spawn` call in same file\n- `potential-exfiltration` (WARN): `readFile` + `fetch`/`post`/`http.request` in same file\n\nThis was broken in 1.3.0 (YouTube `child_process` in server.js), fixed in 1.3.1. Broken again in 1.4.1 (`metrics.js` had `process.env` in a comment + `'POST'` in `actionFromReq`), fixed in 1.5.1 by lazy-loading prom-client and splitting `actionFromReq` into `lib/request-utils.js`.\n\nFile v1.0.0:changelog/1.4.0.md\n\n# Camofox Browser v1.4.0\n\n## What's New\n\n### 🖼️ Download & Image Capture\n- **File downloads**: Camofox now captures files downloaded during browser sessions — PDFs, CSVs, images, and more. Your agent can access them programmatically after a download completes.\n- **Page image extraction**: A new endpoint pulls all visible images from a page, useful for extracting product photos, charts, or screenshots from web apps.\n\n### 🧠 JavaScript Evaluation\n- **Run JavaScript on any page**: The new `camofox_evaluate` tool lets agents execute JavaScript directly in a tab's page context — read page state, call web app APIs, or inject scripts. *(Thanks @faith0811!)*\n\n### ⚡ Reliability Improvements\n- **Faster page interactions**: Clicks, typing, and scrolling are more responsive. Stale element references (from dynamic pages like SPAs) are now auto-refreshed instead of failing.\n- **No more cold starts**: The browser pre-warms on startup, so the first page load is just as fast as every other one.\n- **Better error recovery**: Tabs that stop responding are automatically cleaned up. Dead browser sessions recover without restarting the server.\n- **Google Search is faster**: Results pages load ~2x quicker with a new direct extraction method.\n\n### 🔧 Under the Hood\n- Converted the codebase to modern JavaScript modules (ESM). No changes needed on your end — the plugin API is the same.\n- Version numbers in `package.json` and `openclaw.plugin.json` now stay in sync automatically.\n\n## Thank You\n\nThanks to our contributors who made this release possible:\n\n- **@Microck** — download capture & image extraction\n- **@faith0811** — JavaScript evaluation endpoint\n\nWe welcome contributions! If you'd like to get involved, check out the [repo](https://github.com/jo-inc/camofox-browser).\n\n## Upgrading\n\nUpdate your plugin to v1.4.0. No configuration changes needed — everything is backward compatible.\n\nFile v1.0.0:CONTRIBUTING.md\n\n# Contributing to camofox-browser\n\n## Environment Variable Security\n\n**Do not pass the host environment to child processes.** This is a hard rule.\n\nWhen spawning child processes (e.g., the server from the plugin), only pass an explicit whitelist of environment variables. Never use `...process.env` or equivalent spreads.\n\n```typescript\n// WRONG — leaks all host secrets to the child process\nspawn(\"node\", [serverPath], {\n  env: { ...process.env, CAMOFOX_PORT: \"9377\" },\n});\n\n// RIGHT — only what the child actually needs\nspawn(\"node\", [serverPath], {\n  env: {\n    PATH: process.env.PATH,\n    HOME: process.env.HOME,\n    NODE_ENV: process.env.NODE_ENV,\n    CAMOFOX_PORT: \"9377\",\n  },\n});\n```\n\nIf the child process needs a new env var, add it to the whitelist explicitly in both `plugin.ts` and `tests/helpers/startServer.js`.\n\n**Do not use `dotenv` or load `.env` files.** The server reads its configuration from explicitly passed environment variables only. Users running camofox alongside other tools may have `.env` files with secrets that should never be loaded into this process.\n\n## Testing\n\n```bash\nnpm test              # e2e tests\nnpm run test:live     # live site tests (requires RUN_LIVE_TESTS=1)\nnpm run test:debug    # with server output (DEBUG_SERVER=1)\n```\n\n## Code Style\n\n- No comments explaining what the code does — keep it readable without them\n- Use `const` by default, `let` only when reassignment is needed\n- Error responses: `{ error: \"message\" }` with appropriate HTTP status codes\n- All tab operations require `userId` for session isolation\n\nFile v1.0.0:skill-card.md\n\n## Description:\n\nHeadless browser server enabling AI agents to create tabs, navigate, interact, and capture snapshots with element references locally or in the cloud.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[sheffk78](https://clawhub.ai/user/sheffk78)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agent builders use this skill to give AI agents a local or cloud-hosted anti-detection browser API for web navigation, page snapshots, element interaction, screenshots, downloads, cookie import, proxies, and YouTube transcript extraction.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Agent-controlled anti-detection browsing can be misused or can access sensitive web sessions.\n\nMitigation: Install only for intentional browser automation use cases and avoid importing cookies for sensitive accounts.\n\nRisk: Exposing the browser server publicly can expand access to powerful browser, session, cookie, and JavaScript controls.\n\nMitigation: Keep the server bound to localhost or a private trusted network, do not expose port 9377 publicly, and set strong CAMOFOX_API_KEY and CAMOFOX_ADMIN_KEY values.\n\nRisk: Page JavaScript evaluation can run code in the browser page context.\n\nMitigation: Restrict use of camofox_evaluate and review tasks that request arbitrary JavaScript execution.\n\nRisk: Outdated dependencies or browser artifacts can increase operational and security exposure.\n\nMitigation: Update or pin dependencies and browser artifacts before production deployment.\n\n## Reference(s):\n\n- [Camofox Browser on ClawHub](https://clawhub.ai/sheffk78/skills/camofox-browser)\n- [Camoufox](https://camoufox.com)\n- [OpenClaw](https://openclaw.ai)\n- [yt-dlp](https://github.com/yt-dlp/yt-dlp)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown, JSON API responses, shell commands, screenshots, and text snapshots]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May include accessibility snapshots with element refs, base64 screenshots or downloads, imported cookie status, and transcript text depending on the invoked tool.]\n\n## Skill Version(s):\n\n1.0.0 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.0.0:openclaw.plugin.json\n\n{\n  \"id\": \"camofox-browser\",\n  \"name\": \"Camofox Browser\",\n  \"description\": \"Anti-detection browser automation for AI agents using Camoufox (Firefox-based)\",\n  \"version\": \"1.5.2\",\n  \"configSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"url\": {\n        \"type\": \"string\",\n        \"description\": \"Camoufox browser server URL\"\n      },\n      \"port\": {\n        \"type\": \"number\",\n        \"description\": \"Server port (used if url not set)\",\n        \"default\": 9377\n      },\n      \"autoStart\": {\n        \"type\": \"boolean\",\n        \"description\": \"Auto-start the camofox-browser server with the Gateway\",\n        \"default\": true\n      },\n      \"maxSessions\": {\n        \"type\": \"number\",\n        \"description\": \"Maximum concurrent browser sessions (server default: 50)\",\n        \"default\": 5\n      },\n      \"maxTabsPerSession\": {\n        \"type\": \"number\",\n        \"description\": \"Maximum tabs per session (server default: 10)\",\n        \"default\": 3\n      },\n      \"sessionTimeoutMs\": {\n        \"type\": \"number\",\n        \"description\": \"Session inactivity timeout in milliseconds (server default: 1800000)\",\n        \"default\": 600000\n      },\n      \"browserIdleTimeoutMs\": {\n        \"type\": \"number\",\n        \"description\": \"Kill browser after this many ms with no sessions (0 = never)\",\n        \"default\": 300000\n      },\n      \"maxOldSpaceSize\": {\n        \"type\": \"number\",\n        \"description\": \"Node.js V8 heap limit in MB\",\n        \"default\": 128\n      }\n    },\n    \"additionalProperties\": false\n  },\n  \"uiHints\": {\n    \"url\": {\n      \"label\": \"Server URL\",\n      \"placeholder\": \"http://localhost:9377\"\n    },\n    \"port\": {\n      \"label\": \"Server Port\",\n      \"placeholder\": \"9377\"\n    },\n    \"autoStart\": {\n      \"label\": \"Auto-start server with Gateway\"\n    },\n    \"maxSessions\": {\n      \"label\": \"Max Sessions\",\n      \"placeholder\": \"5\"\n    },\n    \"maxTabsPerSession\": {\n      \"label\": \"Max Tabs per Session\",\n      \"placeholder\": \"3\"\n    },\n    \"sessionTimeoutMs\": {\n      \"label\": \"Session Timeout (ms)\",\n      \"placeholder\": \"600000\"\n    },\n    \"browserIdleTimeoutMs\": {\n      \"label\": \"Browser Idle Timeout (ms)\",\n      \"placeholder\": \"300000\"\n    },\n    \"maxOldSpaceSize\": {\n      \"label\": \"Node Heap Limit (MB)\",\n      \"placeholder\": \"128\"\n    }\n  }\n}\n\nFile v1.0.0:package-lock.json\n\n{\n  \"name\": \"@askjo/camofox-browser\",\n  \"version\": \"1.5.0\",\n  \"lockfileVersion\": 3,\n  \"requires\": true,\n  \"packages\": {\n    \"\": {\n      \"name\": \"@askjo/camofox-browser\",\n      \"version\": \"1.5.0\",\n      \"hasInstallScript\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"camoufox-js\": \"^0.8.5\",\n        \"express\": \"^4.18.2\",\n        \"playwright\": \"^1.50.0\",\n        \"playwright-core\": \"^1.58.0\",\n        \"playwright-extra\": \"^4.3.6\",\n        \"prom-client\": \"^15.1.3\",\n        \"puppeteer-extra-plugin-stealth\": \"^2.11.2\"\n      },\n      \"devDependencies\": {\n        \"jest\": \"^29.7.0\",\n        \"pngjs\": \"^7.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@babel/code-frame\": {\n      \"version\": \"7.29.0\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz\",\n      \"integrity\": \"sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-validator-identifier\": \"^7.28.5\",\n        \"js-tokens\": \"^4.0.0\",\n        \"picocolors\": \"^1.1.1\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/compat-data\": {\n      \"version\": \"7.29.0\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz\",\n      \"integrity\": \"sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/core\": {\n      \"version\": \"7.29.0\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz\",\n      \"integrity\": \"sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/code-frame\": \"^7.29.0\",\n        \"@babel/generator\": \"^7.29.0\",\n        \"@babel/helper-compilation-targets\": \"^7.28.6\",\n        \"@babel/helper-module-transforms\": \"^7.28.6\",\n        \"@babel/helpers\": \"^7.28.6\",\n        \"@babel/parser\": \"^7.29.0\",\n        \"@babel/template\": \"^7.28.6\",\n        \"@babel/traverse\": \"^7.29.0\",\n        \"@babel/types\": \"^7.29.0\",\n        \"@jridgewell/remapping\": \"^2.3.5\",\n        \"convert-source-map\": \"^2.0.0\",\n        \"debug\": \"^4.1.0\",\n        \"gensync\": \"^1.0.0-beta.2\",\n        \"json5\": \"^2.2.3\",\n        \"semver\": \"^6.3.1\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      },\n      \"funding\": {\n        \"type\": \"opencollective\",\n        \"url\": \"https://opencollective.com/babel\"\n      }\n    },\n    \"node_modules/@babel/core/node_modules/debug\": {\n      \"version\": \"4.4.3\",\n      \"resolved\": \"https://registry.npmjs.org/debug/-/debug-4.4.3.tgz\",\n      \"integrity\": \"sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"ms\": \"^2.1.3\"\n      },\n      \"engines\": {\n        \"node\": \">=6.0\"\n      },\n      \"peerDependenciesMeta\": {\n        \"supports-color\": {\n          \"optional\": true\n        }\n      }\n    },\n    \"node_modules/@babel/core/node_modules/ms\": {\n      \"version\": \"2.1.3\",\n      \"resolved\": \"https://registry.npmjs.org/ms/-/ms-2.1.3.tgz\",\n      \"integrity\": \"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/@babel/generator\": {\n      \"version\": \"7.29.0\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/generator/-/generator-7.29.0.tgz\",\n      \"integrity\": \"sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/parser\": \"^7.29.0\",\n        \"@babel/types\": \"^7.29.0\",\n        \"@jridgewell/gen-mapping\": \"^0.3.12\",\n        \"@jridgewell/trace-mapping\": \"^0.3.28\",\n        \"jsesc\": \"^3.0.2\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/helper-compilation-targets\": {\n      \"version\": \"7.28.6\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz\",\n      \"integrity\": \"sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/compat-data\": \"^7.28.6\",\n        \"@babel/helper-validator-option\": \"^7.27.1\",\n        \"browserslist\": \"^4.24.0\",\n        \"lru-cache\": \"^5.1.1\",\n        \"semver\": \"^6.3.1\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/helper-globals\": {\n      \"version\": \"7.28.0\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz\",\n      \"integrity\": \"sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/helper-module-imports\": {\n      \"version\": \"7.28.6\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz\",\n      \"integrity\": \"sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/traverse\": \"^7.28.6\",\n        \"@babel/types\": \"^7.28.6\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/helper-module-transforms\": {\n      \"version\": \"7.28.6\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz\",\n      \"integrity\": \"sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-module-imports\": \"^7.28.6\",\n        \"@babel/helper-validator-identifier\": \"^7.28.5\",\n        \"@babel/traverse\": \"^7.28.6\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0\"\n      }\n    },\n    \"node_modules/@babel/helper-plugin-utils\": {\n      \"version\": \"7.28.6\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz\",\n      \"integrity\": \"sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/helper-string-parser\": {\n      \"version\": \"7.27.1\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz\",\n      \"integrity\": \"sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/helper-validator-identifier\": {\n      \"version\": \"7.28.5\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz\",\n      \"integrity\": \"sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/helper-validator-option\": {\n      \"version\": \"7.27.1\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz\",\n      \"integrity\": \"sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/helpers\": {\n      \"version\": \"7.28.6\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz\",\n      \"integrity\": \"sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/template\": \"^7.28.6\",\n        \"@babel/types\": \"^7.28.6\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/parser\": {\n      \"version\": \"7.29.0\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz\",\n      \"integrity\": \"sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/types\": \"^7.29.0\"\n      },\n      \"bin\": {\n        \"parser\": \"bin/babel-parser.js\"\n      },\n      \"engines\": {\n        \"node\": \">=6.0.0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-async-generators\": {\n      \"version\": \"7.8.4\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz\",\n      \"integrity\": \"sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.8.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-bigint\": {\n      \"version\": \"7.8.3\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz\",\n      \"integrity\": \"sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.8.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-class-properties\": {\n      \"version\": \"7.12.13\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz\",\n      \"integrity\": \"sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.12.13\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-class-static-block\": {\n      \"version\": \"7.14.5\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz\",\n      \"integrity\": \"sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.14.5\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-import-attributes\": {\n      \"version\": \"7.28.6\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz\",\n      \"integrity\": \"sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.28.6\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-import-meta\": {\n      \"version\": \"7.10.4\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz\",\n      \"integrity\": \"sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.10.4\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-json-strings\": {\n      \"version\": \"7.8.3\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz\",\n      \"integrity\": \"sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.8.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-jsx\": {\n      \"version\": \"7.28.6\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz\",\n      \"integrity\": \"sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.28.6\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-logical-assignment-operators\": {\n      \"version\": \"7.10.4\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz\",\n      \"integrity\": \"sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.10.4\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-nullish-coalescing-operator\": {\n      \"version\": \"7.8.3\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz\",\n      \"integrity\": \"sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.8.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-numeric-separator\": {\n      \"version\": \"7.10.4\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz\",\n      \"integrity\": \"sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.10.4\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-object-rest-spread\": {\n      \"version\": \"7.8.3\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz\",\n      \"integrity\": \"sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.8.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-optional-catch-binding\": {\n      \"version\": \"7.8.3\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz\",\n      \"integrity\": \"sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.8.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-optional-chaining\": {\n      \"version\": \"7.8.3\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz\",\n      \"integrity\": \"sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.8.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-private-property-in-object\": {\n      \"version\": \"7.14.5\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz\",\n      \"integrity\": \"sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.14.5\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-top-level-await\": {\n      \"version\": \"7.14.5\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz\",\n      \"integrity\": \"sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.14.5\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/plugin-syntax-typescript\": {\n      \"version\": \"7.28.6\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz\",\n      \"integrity\": \"sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.28.6\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.0.0-0\"\n      }\n    },\n    \"node_modules/@babel/template\": {\n      \"version\": \"7.28.6\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz\",\n      \"integrity\": \"sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/code-frame\": \"^7.28.6\",\n        \"@babel/parser\": \"^7.28.6\",\n        \"@babel/types\": \"^7.28.6\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/traverse\": {\n      \"version\": \"7.29.0\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz\",\n      \"integrity\": \"sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/code-frame\": \"^7.29.0\",\n        \"@babel/generator\": \"^7.29.0\",\n        \"@babel/helper-globals\": \"^7.28.0\",\n        \"@babel/parser\": \"^7.29.0\",\n        \"@babel/template\": \"^7.28.6\",\n        \"@babel/types\": \"^7.29.0\",\n        \"debug\": \"^4.3.1\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@babel/traverse/node_modules/debug\": {\n      \"version\": \"4.4.3\",\n      \"resolved\": \"https://registry.npmjs.org/debug/-/debug-4.4.3.tgz\",\n      \"integrity\": \"sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"ms\": \"^2.1.3\"\n      },\n      \"engines\": {\n        \"node\": \">=6.0\"\n      },\n      \"peerDependenciesMeta\": {\n        \"supports-color\": {\n          \"optional\": true\n        }\n      }\n    },\n    \"node_modules/@babel/traverse/node_modules/ms\": {\n      \"version\": \"2.1.3\",\n      \"resolved\": \"https://registry.npmjs.org/ms/-/ms-2.1.3.tgz\",\n      \"integrity\": \"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/@babel/types\": {\n      \"version\": \"7.29.0\",\n      \"resolved\": \"https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz\",\n      \"integrity\": \"sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/helper-string-parser\": \"^7.27.1\",\n        \"@babel/helper-validator-identifier\": \"^7.28.5\"\n      },\n      \"engines\": {\n        \"node\": \">=6.9.0\"\n      }\n    },\n    \"node_modules/@bcoe/v8-coverage\": {\n      \"version\": \"0.2.3\",\n      \"resolved\": \"https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz\",\n      \"integrity\": \"sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/@isaacs/balanced-match\": {\n      \"version\": \"4.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz\",\n      \"integrity\": \"sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \"20 || >=22\"\n      }\n    },\n    \"node_modules/@isaacs/brace-expansion\": {\n      \"version\": \"5.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz\",\n      \"integrity\": \"sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@isaacs/balanced-match\": \"^4.0.1\"\n      },\n      \"engines\": {\n        \"node\": \"20 || >=22\"\n      }\n    },\n    \"node_modules/@istanbuljs/load-nyc-config\": {\n      \"version\": \"1.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz\",\n      \"integrity\": \"sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==\",\n      \"dev\": true,\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"camelcase\": \"^5.3.1\",\n        \"find-up\": \"^4.1.0\",\n        \"get-package-type\": \"^0.1.0\",\n        \"js-yaml\": \"^3.13.1\",\n        \"resolve-from\": \"^5.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/@istanbuljs/schema\": {\n      \"version\": \"0.1.3\",\n      \"resolved\": \"https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz\",\n      \"integrity\": \"sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/@jest/console\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz\",\n      \"integrity\": \"sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jest/types\": \"^29.6.3\",\n        \"@types/node\": \"*\",\n        \"chalk\": \"^4.0.0\",\n        \"jest-message-util\": \"^29.7.0\",\n        \"jest-util\": \"^29.7.0\",\n        \"slash\": \"^3.0.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/core\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz\",\n      \"integrity\": \"sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jest/console\": \"^29.7.0\",\n        \"@jest/reporters\": \"^29.7.0\",\n        \"@jest/test-result\": \"^29.7.0\",\n        \"@jest/transform\": \"^29.7.0\",\n        \"@jest/types\": \"^29.6.3\",\n        \"@types/node\": \"*\",\n        \"ansi-escapes\": \"^4.2.1\",\n        \"chalk\": \"^4.0.0\",\n        \"ci-info\": \"^3.2.0\",\n        \"exit\": \"^0.1.2\",\n        \"graceful-fs\": \"^4.2.9\",\n        \"jest-changed-files\": \"^29.7.0\",\n        \"jest-config\": \"^29.7.0\",\n        \"jest-haste-map\": \"^29.7.0\",\n        \"jest-message-util\": \"^29.7.0\",\n        \"jest-regex-util\": \"^29.6.3\",\n        \"jest-resolve\": \"^29.7.0\",\n        \"jest-resolve-dependencies\": \"^29.7.0\",\n        \"jest-runner\": \"^29.7.0\",\n        \"jest-runtime\": \"^29.7.0\",\n        \"jest-snapshot\": \"^29.7.0\",\n        \"jest-util\": \"^29.7.0\",\n        \"jest-validate\": \"^29.7.0\",\n        \"jest-watcher\": \"^29.7.0\",\n        \"micromatch\": \"^4.0.4\",\n        \"pretty-format\": \"^29.7.0\",\n        \"slash\": \"^3.0.0\",\n        \"strip-ansi\": \"^6.0.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      },\n      \"peerDependencies\": {\n        \"node-notifier\": \"^8.0.1 || ^9.0.0 || ^10.0.0\"\n      },\n      \"peerDependenciesMeta\": {\n        \"node-notifier\": {\n          \"optional\": true\n        }\n      }\n    },\n    \"node_modules/@jest/environment\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz\",\n      \"integrity\": \"sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jest/fake-timers\": \"^29.7.0\",\n        \"@jest/types\": \"^29.6.3\",\n        \"@types/node\": \"*\",\n        \"jest-mock\": \"^29.7.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/expect\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz\",\n      \"integrity\": \"sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"expect\": \"^29.7.0\",\n        \"jest-snapshot\": \"^29.7.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/expect-utils\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz\",\n      \"integrity\": \"sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"jest-get-type\": \"^29.6.3\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/fake-timers\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz\",\n      \"integrity\": \"sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jest/types\": \"^29.6.3\",\n        \"@sinonjs/fake-timers\": \"^10.0.2\",\n        \"@types/node\": \"*\",\n        \"jest-message-util\": \"^29.7.0\",\n        \"jest-mock\": \"^29.7.0\",\n        \"jest-util\": \"^29.7.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/globals\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz\",\n      \"integrity\": \"sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jest/environment\": \"^29.7.0\",\n        \"@jest/expect\": \"^29.7.0\",\n        \"@jest/types\": \"^29.6.3\",\n        \"jest-mock\": \"^29.7.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/reporters\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz\",\n      \"integrity\": \"sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@bcoe/v8-coverage\": \"^0.2.3\",\n        \"@jest/console\": \"^29.7.0\",\n        \"@jest/test-result\": \"^29.7.0\",\n        \"@jest/transform\": \"^29.7.0\",\n        \"@jest/types\": \"^29.6.3\",\n        \"@jridgewell/trace-mapping\": \"^0.3.18\",\n        \"@types/node\": \"*\",\n        \"chalk\": \"^4.0.0\",\n        \"collect-v8-coverage\": \"^1.0.0\",\n        \"exit\": \"^0.1.2\",\n        \"glob\": \"^7.1.3\",\n        \"graceful-fs\": \"^4.2.9\",\n        \"istanbul-lib-coverage\": \"^3.0.0\",\n        \"istanbul-lib-instrument\": \"^6.0.0\",\n        \"istanbul-lib-report\": \"^3.0.0\",\n        \"istanbul-lib-source-maps\": \"^4.0.0\",\n        \"istanbul-reports\": \"^3.1.3\",\n        \"jest-message-util\": \"^29.7.0\",\n        \"jest-util\": \"^29.7.0\",\n        \"jest-worker\": \"^29.7.0\",\n        \"slash\": \"^3.0.0\",\n        \"string-length\": \"^4.0.1\",\n        \"strip-ansi\": \"^6.0.0\",\n        \"v8-to-istanbul\": \"^9.0.1\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      },\n      \"peerDependencies\": {\n        \"node-notifier\": \"^8.0.1 || ^9.0.0 || ^10.0.0\"\n      },\n      \"peerDependenciesMeta\": {\n        \"node-notifier\": {\n          \"optional\": true\n        }\n      }\n    },\n    \"node_modules/@jest/reporters/node_modules/glob\": {\n      \"version\": \"7.2.3\",\n      \"resolved\": \"https://registry.npmjs.org/glob/-/glob-7.2.3.tgz\",\n      \"integrity\": \"sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==\",\n      \"deprecated\": \"Glob versions prior to v9 are no longer supported\",\n      \"dev\": true,\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"fs.realpath\": \"^1.0.0\",\n        \"inflight\": \"^1.0.4\",\n        \"inherits\": \"2\",\n        \"minimatch\": \"^3.1.1\",\n        \"once\": \"^1.3.0\",\n        \"path-is-absolute\": \"^1.0.0\"\n      },\n      \"engines\": {\n        \"node\": \"*\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/isaacs\"\n      }\n    },\n    \"node_modules/@jest/reporters/node_modules/minimatch\": {\n      \"version\": \"3.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz\",\n      \"integrity\": \"sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==\",\n      \"dev\": true,\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"brace-expansion\": \"^1.1.7\"\n      },\n      \"engines\": {\n        \"node\": \"*\"\n      }\n    },\n    \"node_modules/@jest/schemas\": {\n      \"version\": \"29.6.3\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz\",\n      \"integrity\": \"sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@sinclair/typebox\": \"^0.27.8\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/source-map\": {\n      \"version\": \"29.6.3\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz\",\n      \"integrity\": \"sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jridgewell/trace-mapping\": \"^0.3.18\",\n        \"callsites\": \"^3.0.0\",\n        \"graceful-fs\": \"^4.2.9\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/test-result\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz\",\n      \"integrity\": \"sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jest/console\": \"^29.7.0\",\n        \"@jest/types\": \"^29.6.3\",\n        \"@types/istanbul-lib-coverage\": \"^2.0.0\",\n        \"collect-v8-coverage\": \"^1.0.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/test-sequencer\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz\",\n      \"integrity\": \"sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jest/test-result\": \"^29.7.0\",\n        \"graceful-fs\": \"^4.2.9\",\n        \"jest-haste-map\": \"^29.7.0\",\n        \"slash\": \"^3.0.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/transform\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz\",\n      \"integrity\": \"sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/core\": \"^7.11.6\",\n        \"@jest/types\": \"^29.6.3\",\n        \"@jridgewell/trace-mapping\": \"^0.3.18\",\n        \"babel-plugin-istanbul\": \"^6.1.1\",\n        \"chalk\": \"^4.0.0\",\n        \"convert-source-map\": \"^2.0.0\",\n        \"fast-json-stable-stringify\": \"^2.1.0\",\n        \"graceful-fs\": \"^4.2.9\",\n        \"jest-haste-map\": \"^29.7.0\",\n        \"jest-regex-util\": \"^29.6.3\",\n        \"jest-util\": \"^29.7.0\",\n        \"micromatch\": \"^4.0.4\",\n        \"pirates\": \"^4.0.4\",\n        \"slash\": \"^3.0.0\",\n        \"write-file-atomic\": \"^4.0.2\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jest/types\": {\n      \"version\": \"29.6.3\",\n      \"resolved\": \"https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz\",\n      \"integrity\": \"sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jest/schemas\": \"^29.6.3\",\n        \"@types/istanbul-lib-coverage\": \"^2.0.0\",\n        \"@types/istanbul-reports\": \"^3.0.0\",\n        \"@types/node\": \"*\",\n        \"@types/yargs\": \"^17.0.8\",\n        \"chalk\": \"^4.0.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      }\n    },\n    \"node_modules/@jridgewell/gen-mapping\": {\n      \"version\": \"0.3.13\",\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz\",\n      \"integrity\": \"sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jridgewell/sourcemap-codec\": \"^1.5.0\",\n        \"@jridgewell/trace-mapping\": \"^0.3.24\"\n      }\n    },\n    \"node_modules/@jridgewell/remapping\": {\n      \"version\": \"2.3.5\",\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz\",\n      \"integrity\": \"sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jridgewell/gen-mapping\": \"^0.3.5\",\n        \"@jridgewell/trace-mapping\": \"^0.3.24\"\n      }\n    },\n    \"node_modules/@jridgewell/resolve-uri\": {\n      \"version\": \"3.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz\",\n      \"integrity\": \"sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=6.0.0\"\n      }\n    },\n    \"node_modules/@jridgewell/sourcemap-codec\": {\n      \"version\": \"1.5.5\",\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz\",\n      \"integrity\": \"sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/@jridgewell/trace-mapping\": {\n      \"version\": \"0.3.31\",\n      \"resolved\": \"https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz\",\n      \"integrity\": \"sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jridgewell/resolve-uri\": \"^3.1.0\",\n        \"@jridgewell/sourcemap-codec\": \"^1.4.14\"\n      }\n    },\n    \"node_modules/@opentelemetry/api\": {\n      \"version\": \"1.9.0\",\n      \"resolved\": \"https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz\",\n      \"integrity\": \"sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==\",\n      \"license\": \"Apache-2.0\",\n      \"engines\": {\n        \"node\": \">=8.0.0\"\n      }\n    },\n    \"node_modules/@sinclair/typebox\": {\n      \"version\": \"0.27.8\",\n      \"resolved\": \"https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz\",\n      \"integrity\": \"sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/@sindresorhus/is\": {\n      \"version\": \"4.6.0\",\n      \"resolved\": \"https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz\",\n      \"integrity\": \"sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sindresorhus/is?sponsor=1\"\n      }\n    },\n    \"node_modules/@sinonjs/commons\": {\n      \"version\": \"3.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz\",\n      \"integrity\": \"sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==\",\n      \"dev\": true,\n      \"license\": \"BSD-3-Clause\",\n      \"dependencies\": {\n        \"type-detect\": \"4.0.8\"\n      }\n    },\n    \"node_modules/@sinonjs/fake-timers\": {\n      \"version\": \"10.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz\",\n      \"integrity\": \"sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==\",\n      \"dev\": true,\n      \"license\": \"BSD-3-Clause\",\n      \"dependencies\": {\n        \"@sinonjs/commons\": \"^3.0.0\"\n      }\n    },\n    \"node_modules/@types/babel__core\": {\n      \"version\": \"7.20.5\",\n      \"resolved\": \"https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz\",\n      \"integrity\": \"sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/parser\": \"^7.20.7\",\n        \"@babel/types\": \"^7.20.7\",\n        \"@types/babel__generator\": \"*\",\n        \"@types/babel__template\": \"*\",\n        \"@types/babel__traverse\": \"*\"\n      }\n    },\n    \"node_modules/@types/babel__generator\": {\n      \"version\": \"7.27.0\",\n      \"resolved\": \"https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz\",\n      \"integrity\": \"sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/types\": \"^7.0.0\"\n      }\n    },\n    \"node_modules/@types/babel__template\": {\n      \"version\": \"7.4.4\",\n      \"resolved\": \"https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz\",\n      \"integrity\": \"sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/parser\": \"^7.1.0\",\n        \"@babel/types\": \"^7.0.0\"\n      }\n    },\n    \"node_modules/@types/babel__traverse\": {\n      \"version\": \"7.28.0\",\n      \"resolved\": \"https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz\",\n      \"integrity\": \"sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/types\": \"^7.28.2\"\n      }\n    },\n    \"node_modules/@types/debug\": {\n      \"version\": \"4.1.12\",\n      \"resolved\": \"https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz\",\n      \"integrity\": \"sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@types/ms\": \"*\"\n      }\n    },\n    \"node_modules/@types/graceful-fs\": {\n      \"version\": \"4.1.9\",\n      \"resolved\": \"https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz\",\n      \"integrity\": \"sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@types/node\": \"*\"\n      }\n    },\n    \"node_modules/@types/istanbul-lib-coverage\": {\n      \"version\": \"2.0.6\",\n      \"resolved\": \"https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz\",\n      \"integrity\": \"sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/@types/istanbul-lib-report\": {\n      \"version\": \"3.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz\",\n      \"integrity\": \"sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@types/istanbul-lib-coverage\": \"*\"\n      }\n    },\n    \"node_modules/@types/istanbul-reports\": {\n      \"version\": \"3.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz\",\n      \"integrity\": \"sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@types/istanbul-lib-report\": \"*\"\n      }\n    },\n    \"node_modules/@types/ms\": {\n      \"version\": \"2.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz\",\n      \"integrity\": \"sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/@types/node\": {\n      \"version\": \"25.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz\",\n      \"integrity\": \"sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"undici-types\": \"~7.16.0\"\n      }\n    },\n    \"node_modules/@types/stack-utils\": {\n      \"version\": \"2.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz\",\n      \"integrity\": \"sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/@types/yargs\": {\n      \"version\": \"17.0.35\",\n      \"resolved\": \"https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz\",\n      \"integrity\": \"sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@types/yargs-parser\": \"*\"\n      }\n    },\n    \"node_modules/@types/yargs-parser\": {\n      \"version\": \"21.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz\",\n      \"integrity\": \"sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    },\n    \"node_modules/accepts\": {\n      \"version\": \"1.3.8\",\n      \"resolved\": \"https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz\",\n      \"integrity\": \"sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"mime-types\": \"~2.1.34\",\n        \"negotiator\": \"0.6.3\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.6\"\n      }\n    },\n    \"node_modules/adm-zip\": {\n      \"version\": \"0.5.16\",\n      \"resolved\": \"https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz\",\n      \"integrity\": \"sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=12.0\"\n      }\n    },\n    \"node_modules/ansi-escapes\": {\n      \"version\": \"4.3.2\",\n      \"resolved\": \"https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz\",\n      \"integrity\": \"sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"type-fest\": \"^0.21.3\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/ansi-regex\": {\n      \"version\": \"5.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz\",\n      \"integrity\": \"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/ansi-styles\": {\n      \"version\": \"4.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz\",\n      \"integrity\": \"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"color-convert\": \"^2.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/chalk/ansi-styles?sponsor=1\"\n      }\n    },\n    \"node_modules/anymatch\": {\n      \"version\": \"3.1.3\",\n      \"resolved\": \"https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz\",\n      \"integrity\": \"sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==\",\n      \"dev\": true,\n      \"license\": \"ISC\",\n      \"dependencies\": {\n        \"normalize-path\": \"^3.0.0\",\n        \"picomatch\": \"^2.0.4\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/argparse\": {\n      \"version\": \"1.0.10\",\n      \"resolved\": \"https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz\",\n      \"integrity\": \"sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"sprintf-js\": \"~1.0.2\"\n      }\n    },\n    \"node_modules/arr-union\": {\n      \"version\": \"3.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz\",\n      \"integrity\": \"sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/array-flatten\": {\n      \"version\": \"1.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz\",\n      \"integrity\": \"sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==\",\n      \"license\": \"MIT\"\n    },\n    \"node_modules/babel-jest\": {\n      \"version\": \"29.7.0\",\n      \"resolved\": \"https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz\",\n      \"integrity\": \"sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@jest/transform\": \"^29.7.0\",\n        \"@types/babel__core\": \"^7.1.14\",\n        \"babel-plugin-istanbul\": \"^6.1.1\",\n        \"babel-preset-jest\": \"^29.6.3\",\n        \"chalk\": \"^4.0.0\",\n        \"graceful-fs\": \"^4.2.9\",\n        \"slash\": \"^3.0.0\"\n      },\n      \"engines\": {\n        \"node\": \"^14.15.0 || ^16.10.0 || >=18.0.0\"\n      },\n      \"peerDependencies\": {\n        \"@babel/core\": \"^7.8.0\"\n      }\n    },\n    \"node_modules/babel-plugin-istanbul\": {\n      \"version\": \"6.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz\",\n      \"integrity\": \"sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==\",\n      \"dev\": true,\n      \"license\": \"BSD-3-Clause\",\n      \"dependencies\": {\n        \"@babel/helper-plugin-utils\": \"^7.0.0\",\n        \"@istanbuljs/load-nyc-config\": \"^1.0.0\",\n        \"@istanbuljs/schema\": \"^0.1.2\",\n        \"istanbul-lib-instrument\": \"^5.0.4\",\n        \"test-exclude\": \"^6.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument\": {\n      \"version\": \"5.2.1\",\n      \"resolved\": \"https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz\",\n      \"integrity\": \"sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==\",\n      \"dev\": true,\n      \"license\": \"BSD-3-Clause\",\n      \"dependencies\": {\n        \"@babel/core\": \"^7.12.3\",\n        \"@babel/parser\": \"^7.14.7\",\n        \"@istanbuljs/schema\": \"^0.1.2\",\n        \"istanbul-lib-coverage\": \"^3.2.0\",\n        \"semver\": \"^6.3.0\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/babel-plugin-jest-hoist\": {\n      \"version\": \"29.6.3\",\n      \"resolved\": \"https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz\",\n      \"integrity\": \"sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@babel/template\": \"^7.3.3\",\n        \"@babel/types\": \"^7\n\nFile v1.0.0:package.json\n\n{\n  \"name\": \"@askjo/camofox-browser\",\n  \"version\": \"1.5.2\",\n  \"description\": \"Headless browser automation server and OpenClaw plugin for AI agents - anti-detection, element refs, and session isolation\",\n  \"type\": \"module\",\n  \"main\": \"server.js\",\n  \"license\": \"MIT\",\n  \"author\": \"Jo Inc <oss@askjo.ai>\",\n  \"homepage\": \"https://github.com/jo-inc/camofox-browser#readme\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/jo-inc/camofox-browser.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/jo-inc/camofox-browser/issues\"\n  },\n  \"keywords\": [\n    \"browser\",\n    \"automation\",\n    \"headless\",\n    \"scraping\",\n    \"camofox\",\n    \"camoufox\",\n    \"anti-detection\",\n    \"ai-agent\",\n    \"openclaw\",\n    \"clawdbot\",\n    \"moltbot\",\n    \"playwright\",\n    \"firefox\",\n    \"youtube\",\n    \"transcript\"\n  ],\n  \"engines\": {\n    \"node\": \">=18\"\n  },\n  \"files\": [\n    \"server.js\",\n    \"lib/\",\n    \"plugin.ts\",\n    \"openclaw.plugin.json\",\n    \"scripts/\",\n    \"run.sh\",\n    \"Dockerfile\",\n    \"README.md\",\n    \"LICENSE\"\n  ],\n  \"openclaw\": {\n    \"extensions\": [\n      \"plugin.ts\"\n    ]\n  },\n  \"scripts\": {\n    \"start\": \"node server.js\",\n    \"test\": \"NODE_OPTIONS='--experimental-vm-modules' jest --runInBand --forceExit\",\n    \"test:e2e\": \"NODE_OPTIONS='--experimental-vm-modules' jest --runInBand --forceExit tests/e2e\",\n    \"test:live\": \"RUN_LIVE_TESTS=1 NODE_OPTIONS='--experimental-vm-modules' jest --runInBand --forceExit tests/live\",\n    \"test:debug\": \"DEBUG_SERVER=1 NODE_OPTIONS='--experimental-vm-modules' jest --runInBand --forceExit\",\n    \"version:sync\": \"node scripts/sync-version.js\",\n    \"version\": \"node scripts/sync-version.js && git add openclaw.plugin.json\",\n    \"postinstall\": \"npx camoufox-js fetch || true\"\n  },\n  \"dependencies\": {\n    \"camoufox-js\": \"^0.8.5\",\n    \"express\": \"^4.18.2\",\n    \"playwright\": \"^1.50.0\",\n    \"playwright-core\": \"^1.58.0\",\n    \"playwright-extra\": \"^4.3.6\",\n    \"prom-client\": \"^15.1.3\",\n    \"puppeteer-extra-plugin-stealth\": \"^2.11.2\"\n  },\n  \"devDependencies\": {\n    \"jest\": \"^29.7.0\",\n    \"pngjs\": \"^7.0.0\"\n  }\n}","readmeExcerpt":"Skill: Camofox Browser Owner: sheffk78 Summary: Headless browser server enabling AI agents to create tabs, navigate, interact, and capture snapshots with element references locally or in the cloud. Tags: latest:1.0.0 Version history: v1.0.0 | 2026-04-15T03:03:09.341Z | auto Initial release of camofox-browser: headless browser automation server for AI agents. - Implements REST API for browser tab creation, navigation,","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"# Install and start\nnpm install && npm start\n# Server runs on http://localhost:9377"},{"language":"bash","snippet":"POST /tabs\n{\"userId\": \"agent1\", \"sessionKey\": \"task1\", \"url\": \"https://example.com\"}"},{"language":"bash","snippet":"POST /tabs/:tabId/navigate\n{\"userId\": \"agent1\", \"url\": \"https://google.com\"}\n# Or use macro:\n{\"userId\": \"agent1\", \"macro\": \"@google_search\", \"query\": \"weather today\"}"},{"language":"bash","snippet":"GET /tabs/:tabId/snapshot?userId=agent1"},{"language":"text","snippet":"[heading] Example Domain\n[paragraph] This domain is for use in examples.\n[link e1] More information..."},{"language":"bash","snippet":"POST /tabs/:tabId/click\n{\"userId\": \"agent1\", \"ref\": \"e1\"}\n# Or CSS selector:\n{\"userId\": \"agent1\", \"selector\": \"button.submit\"}"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"# camofox-browser Agent Guide\n\nHeadless browser automation server for AI agents. Run locally or deploy to any cloud provider.\n\n## Quick Start for Agents\n\n```bash\n# Install and start\nnpm install && npm start\n# Server runs on http://localhost:9377\n```\n\n## Core Workflow\n\n1. **Create a tab** → Get `tabId`\n2. **Navigate** → Go to URL or use search macro\n3. **Get snapshot** → Receive page content with element refs (`e1`, `e2`, etc.)\n4. **Interact** → Click/type using refs\n5. **Repeat** steps 3-4 as needed\n\n## API Reference\n\n### Create Tab\n```bash\nPOST /tabs\n{\"userId\": \"agent1\", \"sessionKey\": \"task1\", \"url\": \"https://example.com\"}\n```\nReturns: `{\"tabId\": \"abc123\", \"url\": \"...\", \"title\": \"...\"}`\n\n### Navigate\n```bash\nPOST /tabs/:tabId/navigate\n{\"userId\": \"agent1\", \"url\": \"https://google.com\"}\n# Or use macro:\n{\"userId\": \"agent1\", \"macro\": \"@google_search\", \"query\": \"weather today\"}\n```\n\n### Get Snapshot\n```bash\nGET /tabs/:tabId/snapshot?userId=agent1\n```\nReturns accessibility tree with refs:\n```\n[heading] Example Domain\n[paragraph] This domain is for use in examples.\n[link e1] More information...\n```\n\n### Click Element\n```bash\nPOST /tabs/:tabId/click\n{\"userId\": \"agent1\", \"ref\": \"e1\"}\n# Or CSS selector:\n{\"userId\": \"agent1\", \"selector\": \"button.submit\"}\n```\n\n### Type Text\n```bash\nPOST /tabs/:tabId/type\n{\"userId\": \"agent1\", \"ref\": \"e2\", \"text\": \"hello world\"}\n# Add enter: {\"userId\": \"agent1\", \"ref\": \"e2\", \"text\": \"search query\", \"pressEnter\": true}\n```\n\n### Scroll\n```bash\nPOST /tabs/:tabId/scroll\n{\"userId\": \"agent1\", \"direction\": \"down\", \"amount\": 500}\n```\n\n### Navigation\n```bash\nPOST /tabs/:tabId/back     {\"userId\": \"agent1\"}\nPOST /tabs/:tabId/forward  {\"userId\": \"agent1\"}\nPOST /tabs/:tabId/refresh  {\"userId\": \"agent1\"}\n```\n\n### Get Links\n```bash\nGET /tabs/:tabId/links?userId=agent1&limit=50\n```\n\n### Close Tab\n```bash\nDELETE /tabs/:tabId?userId=agent1\n```\n\n## Search Macros\n\nUse these instead of constructing URLs:\n\n| Macro | Site |\n|-------|------|\n| `@google_search` | Google |\n| `@youtube_search` | YouTube |\n| `@amazon_search` | Amazon |\n| `@reddit_search` | Reddit |\n| `@wikipedia_search` | Wikipedia |\n| `@twitter_search` | Twitter/X |\n| `@yelp_search` | Yelp |\n| `@linkedin_search` | LinkedIn |\n\n## Element Refs\n\nRefs like `e1`, `e2` are stable identifiers for page elements:\n\n1. Call `/snapshot` to get current refs\n2. Use ref in `/click` or `/type`\n3. Refs reset on navigation - get new snapshot after\n\n## Session Management\n\n- `userId` isolates cookies/storage between users\n- `sessionKey` groups tabs by conversation/task (legacy: `listItemId` also accepted)\n- Sessions timeout after 30 minutes of inactivity\n- Delete all user data: `DELETE /sessions/:userId`\n\n## Running Engines\n\n### Camoufox (Default)\n```bash\nnpm start\n# Or: ./run.sh\n```\nFirefox-based with anti-detection. Bypasses Google captcha.\n\n## Testing\n\n```bash\nnpm test              # E2E tests\nnpm run test:live     # Live Google tests\nnpm run test:debug    # With server output\n```\n\n## Docker\n\n```bash\ndoc"},{"path":"README.md","content":"<div align=\"center\">\n  <img src=\"fox.png\" alt=\"camofox-browser\" width=\"200\" />\n  <h1>camofox-browser</h1>\n  <p><strong>Anti-detection browser server for AI agents, powered by Camoufox</strong></p>\n  <p>\n    <a href=\"https://github.com/jo-inc/camofox-browser/actions\"><img src=\"https://img.shields.io/badge/build-passing-brightgreen\" alt=\"Build\" /></a>\n    <a href=\"https://opensource.org/licenses/MIT\"><img src=\"https://img.shields.io/badge/license-MIT-blue\" alt=\"License\" /></a>\n    <a href=\"https://camoufox.com\"><img src=\"https://img.shields.io/badge/engine-Camoufox-red\" alt=\"Camoufox\" /></a>\n    <a href=\"https://hub.docker.com\"><img src=\"https://img.shields.io/badge/docker-ready-blue\" alt=\"Docker\" /></a>\n  </p>\n  <p>\n    Standing on the mighty shoulders of <a href=\"https://camoufox.com\">Camoufox</a> - a Firefox fork with fingerprint spoofing at the C++ level.\n    <br/><br/>\n    The same engine behind <a href=\"https://askjo.ai?ref=camofox\">Jo</a> — an AI assistant that doesn't need you to babysit it. Runs half on your Mac, half on a dedicated cloud machine that only you use. Available on macOS, Telegram, and WhatsApp. <a href=\"https://askjo.ai?ref=camofox\">Try the beta free →</a>\n  </p>\n</div>\n\n<br/>\n\n```bash\ngit clone https://github.com/jo-inc/camofox-browser && cd camofox-browser\nnpm install && npm start\n# → http://localhost:9377\n```\n\n---\n\n## Why\n\nAI agents need to browse the real web. Playwright gets blocked. Headless Chrome gets fingerprinted. Stealth plugins become the fingerprint.\n\nCamoufox patches Firefox at the **C++ implementation level** - `navigator.hardwareConcurrency`, WebGL renderers, AudioContext, screen geometry, WebRTC - all spoofed before JavaScript ever sees them. No shims, no wrappers, no tells.\n\nThis project wraps that engine in a REST API built for agents: accessibility snapshots instead of bloated HTML, stable element refs for clicking, and search macros for common sites.\n\n## Features\n\n- **C++ Anti-Detection** - bypasses Google, Cloudflare, and most bot detection\n- **Element Refs** - stable `e1`, `e2`, `e3` identifiers for reliable interaction\n- **Token-Efficient** - accessibility snapshots are ~90% smaller than raw HTML\n- **Runs on Anything** - lazy browser launch + idle shutdown keeps memory at ~40MB when idle. Designed to share a box with the rest of your stack — Raspberry Pi, $5 VPS, shared Railway infra.\n- **Session Isolation** - separate cookies/storage per user\n- **Cookie Import** - inject Netscape-format cookie files for authenticated browsing\n- **Proxy + GeoIP** - route traffic through residential proxies with automatic locale/timezone\n- **Structured Logging** - JSON log lines with request IDs for production observability\n- **YouTube Transcripts** - extract captions from any YouTube video via yt-dlp, no API key needed\n- **Search Macros** - `@google_search`, `@youtube_search`, `@amazon_search`, `@reddit_subreddit`, and 10 more\n- **Snapshot Screenshots** - include a base64 PNG screenshot alongside the accessibility snap"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7ecnevjvccpgs475zkc42e8h84xkxd\",\n  \"slug\": \"camofox-browser\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1776222189341\n}"},{"path":"AGENTS.md","content":"# camofox-browser Agent Guide\n\nHeadless browser automation server for AI agents. Run locally or deploy to any cloud provider.\n\n## Quick Start for Agents\n\n```bash\n# Install and start\nnpm install && npm start\n# Server runs on http://localhost:9377\n```\n\n## Core Workflow\n\n1. **Create a tab** → Get `tabId`\n2. **Navigate** → Go to URL or use search macro\n3. **Get snapshot** → Receive page content with element refs (`e1`, `e2`, etc.)\n4. **Interact** → Click/type using refs\n5. **Repeat** steps 3-4 as needed\n\n## API Reference\n\n### Create Tab\n```bash\nPOST /tabs\n{\"userId\": \"agent1\", \"sessionKey\": \"task1\", \"url\": \"https://example.com\"}\n```\nReturns: `{\"tabId\": \"abc123\", \"url\": \"...\", \"title\": \"...\"}`\n\n### Navigate\n```bash\nPOST /tabs/:tabId/navigate\n{\"userId\": \"agent1\", \"url\": \"https://google.com\"}\n# Or use macro:\n{\"userId\": \"agent1\", \"macro\": \"@google_search\", \"query\": \"weather today\"}\n```\n\n### Get Snapshot\n```bash\nGET /tabs/:tabId/snapshot?userId=agent1\n```\nReturns accessibility tree with refs:\n```\n[heading] Example Domain\n[paragraph] This domain is for use in examples.\n[link e1] More information...\n```\n\n### Click Element\n```bash\nPOST /tabs/:tabId/click\n{\"userId\": \"agent1\", \"ref\": \"e1\"}\n# Or CSS selector:\n{\"userId\": \"agent1\", \"selector\": \"button.submit\"}\n```\n\n### Type Text\n```bash\nPOST /tabs/:tabId/type\n{\"userId\": \"agent1\", \"ref\": \"e2\", \"text\": \"hello world\"}\n# Add enter: {\"userId\": \"agent1\", \"ref\": \"e2\", \"text\": \"search query\", \"pressEnter\": true}\n```\n\n### Scroll\n```bash\nPOST /tabs/:tabId/scroll\n{\"userId\": \"agent1\", \"direction\": \"down\", \"amount\": 500}\n```\n\n### Navigation\n```bash\nPOST /tabs/:tabId/back     {\"userId\": \"agent1\"}\nPOST /tabs/:tabId/forward  {\"userId\": \"agent1\"}\nPOST /tabs/:tabId/refresh  {\"userId\": \"agent1\"}\n```\n\n### Get Links\n```bash\nGET /tabs/:tabId/links?userId=agent1&limit=50\n```\n\n### Close Tab\n```bash\nDELETE /tabs/:tabId?userId=agent1\n```\n\n## Search Macros\n\nUse these instead of constructing URLs:\n\n| Macro | Site |\n|-------|------|\n| `@google_search` | Google |\n| `@youtube_search` | YouTube |\n| `@amazon_search` | Amazon |\n| `@reddit_search` | Reddit |\n| `@wikipedia_search` | Wikipedia |\n| `@twitter_search` | Twitter/X |\n| `@yelp_search` | Yelp |\n| `@linkedin_search` | LinkedIn |\n\n## Element Refs\n\nRefs like `e1`, `e2` are stable identifiers for page elements:\n\n1. Call `/snapshot` to get current refs\n2. Use ref in `/click` or `/type`\n3. Refs reset on navigation - get new snapshot after\n\n## Session Management\n\n- `userId` isolates cookies/storage between users\n- `sessionKey` groups tabs by conversation/task (legacy: `listItemId` also accepted)\n- Sessions timeout after 30 minutes of inactivity\n- Delete all user data: `DELETE /sessions/:userId`\n\n## Running Engines\n\n### Camoufox (Default)\n```bash\nnpm start\n# Or: ./run.sh\n```\nFirefox-based with anti-detection. Bypasses Google captcha.\n\n## Testing\n\n```bash\nnpm test              # E2E tests\nnpm run test:live     # Live Google tests\nnpm run test:debug    # With server output\n```\n\n## Docker\n\n```bash\ndoc"},{"path":"changelog/1.4.0.md","content":"# Camofox Browser v1.4.0\n\n## What's New\n\n### 🖼️ Download & Image Capture\n- **File downloads**: Camofox now captures files downloaded during browser sessions — PDFs, CSVs, images, and more. Your agent can access them programmatically after a download completes.\n- **Page image extraction**: A new endpoint pulls all visible images from a page, useful for extracting product photos, charts, or screenshots from web apps.\n\n### 🧠 JavaScript Evaluation\n- **Run JavaScript on any page**: The new `camofox_evaluate` tool lets agents execute JavaScript directly in a tab's page context — read page state, call web app APIs, or inject scripts. *(Thanks @faith0811!)*\n\n### ⚡ Reliability Improvements\n- **Faster page interactions**: Clicks, typing, and scrolling are more responsive. Stale element references (from dynamic pages like SPAs) are now auto-refreshed instead of failing.\n- **No more cold starts**: The browser pre-warms on startup, so the first page load is just as fast as every other one.\n- **Better error recovery**: Tabs that stop responding are automatically cleaned up. Dead browser sessions recover without restarting the server.\n- **Google Search is faster**: Results pages load ~2x quicker with a new direct extraction method.\n\n### 🔧 Under the Hood\n- Converted the codebase to modern JavaScript modules (ESM). No changes needed on your end — the plugin API is the same.\n- Version numbers in `package.json` and `openclaw.plugin.json` now stay in sync automatically.\n\n## Thank You\n\nThanks to our contributors who made this release possible:\n\n- **@Microck** — download capture & image extraction\n- **@faith0811** — JavaScript evaluation endpoint\n\nWe welcome contributions! If you'd like to get involved, check out the [repo](https://github.com/jo-inc/camofox-browser).\n\n## Upgrading\n\nUpdate your plugin to v1.4.0. No configuration changes needed — everything is backward compatible."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Headless browser server enabling AI agents to create tabs, navigate, interact, and capture snapshots with element references locally or in the cloud. Skill: Camofox Browser Owner: sheffk78 Summary: Headless browser server enabling AI agents to create tabs, navigate, interact, and capture snapshots with element references locally or in the cloud. Tags: latest:1.0.0 Version history: v1.0.0 | 2026-04-15T03:03:09.341Z | auto Initial release of camofox-browser: headless browser automation server for AI agents. - Implements REST API for browser tab creation, navigation,","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1115,"uniquenessScore":54,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T14:36:30.976Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T14:36:30.976Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T17:35:28.091Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}