{"_id":"@aikeymouse/chromelink-client","_rev":"8-37fe278a798ab8451dc2e87f6e2bafd1","name":"@aikeymouse/chromelink-client","dist-tags":{"latest":"1.2.4"},"versions":{"1.1.3":{"name":"@aikeymouse/chromelink-client","version":"1.1.3","keywords":["chrome","automation","websocket","testing","browser-control","chromelink","browser-automation","remote-control","e2e-testing"],"author":{"name":"aikeymouse"},"license":"MIT","_id":"@aikeymouse/chromelink-client@1.1.3","maintainers":[{"name":"aikeymouse","email":"aikeymouse@gmail.com"}],"homepage":"https://github.com/aikeymouse/chrome-link#readme","bugs":{"url":"https://github.com/aikeymouse/chrome-link/issues"},"dist":{"shasum":"59dc7ae4e25984e1517b7b39ccc1c387901f6b40","tarball":"https://registry.npmjs.org/@aikeymouse/chromelink-client/-/chromelink-client-1.1.3.tgz","fileCount":6,"integrity":"sha512-LE2pFTWIKa13PdrixCggdu8buLofq2aKH4cyIFH5NiuK8xgHhl5PsFK3g6acucsS5+8hTpgJNmY/ZiY87cItfg==","signatures":[{"sig":"MEYCIQCHdB8HcjqK1KaG/JyduY0XsqaMGCogJuYkuzCDV7piDwIhAIIXT6QmDrPLwMAVjE1wtKD7Y9ydSkkgpWJKEUZjIOCl","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aikeymouse%2fchromelink-client@1.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":28098},"main":"index.js","types":"index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"37fdfa509942768dc4115ef9f684445d99a0ae1f","scripts":{"prepublishOnly":"echo 'Publishing @aikeymouse/chromelink-client'"},"_npmUser":{"name":"aikeymouse","email":"aikeymouse@gmail.com"},"repository":{"url":"git+https://github.com/aikeymouse/chrome-link.git","type":"git","directory":"clients/node"},"_npmVersion":"10.8.2","description":"Official Node.js client for ChromeLink browser automation via WebSocket","directories":{},"_nodeVersion":"18.20.8","dependencies":{"ws":"^8.0.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/chromelink-client_1.1.3_1768588649118_0.2731630209289835","host":"s3://npm-registry-packages-npm-production"}},"1.1.4":{"name":"@aikeymouse/chromelink-client","version":"1.1.4","keywords":["chrome","automation","websocket","testing","browser-control","chromelink","browser-automation","remote-control","e2e-testing"],"author":{"name":"aikeymouse"},"license":"MIT","_id":"@aikeymouse/chromelink-client@1.1.4","maintainers":[{"name":"aikeymouse","email":"aikeymouse@gmail.com"}],"homepage":"https://github.com/aikeymouse/chrome-link#readme","bugs":{"url":"https://github.com/aikeymouse/chrome-link/issues"},"dist":{"shasum":"e93b87512b3842716fe9e6389d02f09c54e10a8a","tarball":"https://registry.npmjs.org/@aikeymouse/chromelink-client/-/chromelink-client-1.1.4.tgz","fileCount":6,"integrity":"sha512-AUCgYBiiUVv0WsyRycQVvAkqZ0Piuglvxvkn+z+tT/divXyU9FEm3l9F3f6GP42FKtTuAbNwIv0QcGLFObwwNQ==","signatures":[{"sig":"MEQCIBGnctYvlrI4yqe2ZjbZcAc01xxEGJHjRkxcQmKyO5n9AiBFJSiDv0vJhq10JUr1X842AOw2LKskU6oaNtcQb/mJUQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aikeymouse%2fchromelink-client@1.1.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":29433},"main":"index.js","types":"index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"ffda9a84e2f2ec8e58d1c982c71e1db4750a4ca7","scripts":{"prepublishOnly":"echo 'Publishing @aikeymouse/chromelink-client'"},"_npmUser":{"name":"aikeymouse","email":"aikeymouse@gmail.com"},"repository":{"url":"git+https://github.com/aikeymouse/chrome-link.git","type":"git","directory":"clients/node"},"_npmVersion":"10.8.2","description":"Official Node.js client for ChromeLink browser automation via WebSocket","directories":{},"_nodeVersion":"18.20.8","dependencies":{"ws":"^8.0.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/chromelink-client_1.1.4_1768592560144_0.7349251047489096","host":"s3://npm-registry-packages-npm-production"}},"1.1.5":{"name":"@aikeymouse/chromelink-client","version":"1.1.5","keywords":["chrome","automation","websocket","testing","browser-control","chromelink","browser-automation","remote-control","e2e-testing"],"author":{"name":"aikeymouse"},"license":"MIT","_id":"@aikeymouse/chromelink-client@1.1.5","maintainers":[{"name":"aikeymouse","email":"aikeymouse@gmail.com"}],"homepage":"https://github.com/aikeymouse/chrome-link#readme","bugs":{"url":"https://github.com/aikeymouse/chrome-link/issues"},"dist":{"shasum":"c16013f6c52cf738bb08cb356133caea879e10d6","tarball":"https://registry.npmjs.org/@aikeymouse/chromelink-client/-/chromelink-client-1.1.5.tgz","fileCount":6,"integrity":"sha512-oWjyrGMTvYHzgB1FluthIeUGxvWooRCUDfFTv9i5MisjG67zV2/2knimYPce5OmPq0JYRtm/mrEJA6o4ONTieA==","signatures":[{"sig":"MEUCID60XZiwl5cjhAtPC6yEt8ZMyFWHvvtkCRrJY1zmhkLkAiEAptY+jFW419J2NOWdu/ZxSvUmdipAFw4naUpXG1j4HIg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aikeymouse%2fchromelink-client@1.1.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":41843},"main":"index.js","types":"index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"4d040a7d37d183e73b0febc06b36ad6cdb70264c","scripts":{"prepublishOnly":"echo 'Publishing @aikeymouse/chromelink-client'"},"_npmUser":{"name":"aikeymouse","email":"aikeymouse@gmail.com"},"repository":{"url":"git+https://github.com/aikeymouse/chrome-link.git","type":"git","directory":"clients/node"},"_npmVersion":"10.8.2","description":"Official Node.js client for ChromeLink browser automation via WebSocket","directories":{},"_nodeVersion":"18.20.8","dependencies":{"ws":"^8.0.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/chromelink-client_1.1.5_1768679232936_0.23639071488458407","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@aikeymouse/chromelink-client","version":"1.2.0","keywords":["chrome","automation","websocket","testing","browser-control","chromelink","browser-automation","remote-control","e2e-testing"],"author":{"name":"aikeymouse"},"license":"MIT","_id":"@aikeymouse/chromelink-client@1.2.0","maintainers":[{"name":"aikeymouse","email":"aikeymouse@gmail.com"}],"homepage":"https://github.com/aikeymouse/chrome-link#readme","bugs":{"url":"https://github.com/aikeymouse/chrome-link/issues"},"dist":{"shasum":"a056afd3c66b2a20995674fa6381ee46bd7974d0","tarball":"https://registry.npmjs.org/@aikeymouse/chromelink-client/-/chromelink-client-1.2.0.tgz","fileCount":6,"integrity":"sha512-QZfrUO24Xj8zczNHv7n0Zu3FFUURJcS37VvjF7UkIOXVM8tVcBbB4CkadZrwtkKItNkxmbMbUrZpeQq82uxM4w==","signatures":[{"sig":"MEUCIQD8zupWpEOIkkfOq3egEiYcGly2MtJEkUsMlwyBChce9AIgZW1UHaDTujXTy961y3IkKqiscCDwCG9QLuMCeBRKsTk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aikeymouse%2fchromelink-client@1.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":41843},"main":"index.js","types":"index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"b67718bf1a28c36d26f15ece7db48bac321d3646","scripts":{"prepublishOnly":"echo 'Publishing @aikeymouse/chromelink-client'"},"_npmUser":{"name":"aikeymouse","email":"aikeymouse@gmail.com"},"repository":{"url":"git+https://github.com/aikeymouse/chrome-link.git","type":"git","directory":"clients/node"},"_npmVersion":"10.8.2","description":"Official Node.js client for ChromeLink browser automation via WebSocket","directories":{},"_nodeVersion":"18.20.8","dependencies":{"ws":"^8.0.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/chromelink-client_1.2.0_1768766257064_0.8266124167930882","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@aikeymouse/chromelink-client","version":"1.2.1","keywords":["chrome","automation","websocket","testing","browser-control","chromelink","browser-automation","remote-control","e2e-testing"],"author":{"name":"aikeymouse"},"license":"MIT","_id":"@aikeymouse/chromelink-client@1.2.1","maintainers":[{"name":"aikeymouse","email":"aikeymouse@gmail.com"}],"homepage":"https://github.com/aikeymouse/chrome-link#readme","bugs":{"url":"https://github.com/aikeymouse/chrome-link/issues"},"dist":{"shasum":"5ba8c04c6f7315c5f233cf06ed0c3026d2b5c2a6","tarball":"https://registry.npmjs.org/@aikeymouse/chromelink-client/-/chromelink-client-1.2.1.tgz","fileCount":6,"integrity":"sha512-Y3EqdlDSFkUUSKQs9sf327haFnLmeDgOmZjOsnR8/bf6sG8JY8dizua6GNZ1tATlW7O7PPHUjcQzcYMy921uHw==","signatures":[{"sig":"MEUCIBrNOxjGD/YuaBas/69BsNpMTlqITLVmKGW9S0vsR6GoAiEA/rotfEaXOdP43Z1CV82gXcZg8rPBSji9hyeqhiqC4Yw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aikeymouse%2fchromelink-client@1.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":42478},"main":"index.js","types":"index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"856a32fe5869e289bdef6c20e83a14c17de57b0c","scripts":{"prepublishOnly":"echo 'Publishing @aikeymouse/chromelink-client'"},"_npmUser":{"name":"aikeymouse","email":"aikeymouse@gmail.com"},"repository":{"url":"git+https://github.com/aikeymouse/chrome-link.git","type":"git","directory":"clients/node"},"_npmVersion":"10.8.2","description":"Official Node.js client for ChromeLink browser automation via WebSocket","directories":{},"_nodeVersion":"18.20.8","dependencies":{"ws":"^8.0.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/chromelink-client_1.2.1_1768790798850_0.9130432919164013","host":"s3://npm-registry-packages-npm-production"}},"1.2.2":{"name":"@aikeymouse/chromelink-client","version":"1.2.2","keywords":["chrome","automation","websocket","testing","browser-control","chromelink","browser-automation","remote-control","e2e-testing"],"author":{"name":"aikeymouse"},"license":"MIT","_id":"@aikeymouse/chromelink-client@1.2.2","maintainers":[{"name":"aikeymouse","email":"aikeymouse@gmail.com"}],"homepage":"https://github.com/aikeymouse/chrome-link#readme","bugs":{"url":"https://github.com/aikeymouse/chrome-link/issues"},"dist":{"shasum":"b2378af9fb83696f1099638dc71cee350bd9cbfa","tarball":"https://registry.npmjs.org/@aikeymouse/chromelink-client/-/chromelink-client-1.2.2.tgz","fileCount":6,"integrity":"sha512-VtmP+zBvU4NsVP+W6AYcNHqe8RRtRgIBJ7QNl4P50e7vRqXVM8wb0+A+ypwfq5ud943m3DjeZ+IbJIYKwukYDQ==","signatures":[{"sig":"MEUCIEnJj0catwAYAp4FNcjbkOqPBJ/LOsrdO3koWBALVzB4AiEAsesySyyA9QbLmF/+eXrmvrFdJwbarUk6e+Aufgmmg0Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aikeymouse%2fchromelink-client@1.2.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":42478},"main":"index.js","types":"index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"21ec8598661b3026349a13ad769a7f5e0c6fdf97","scripts":{"prepublishOnly":"echo 'Publishing @aikeymouse/chromelink-client'"},"_npmUser":{"name":"aikeymouse","email":"aikeymouse@gmail.com"},"repository":{"url":"git+https://github.com/aikeymouse/chrome-link.git","type":"git","directory":"clients/node"},"_npmVersion":"10.8.2","description":"Official Node.js client for ChromeLink browser automation via WebSocket","directories":{},"_nodeVersion":"18.20.8","dependencies":{"ws":"^8.0.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/chromelink-client_1.2.2_1768864989689_0.5953659212963642","host":"s3://npm-registry-packages-npm-production"}},"1.2.3":{"name":"@aikeymouse/chromelink-client","version":"1.2.3","keywords":["chrome","automation","websocket","testing","browser-control","chromelink","browser-automation","remote-control","e2e-testing"],"author":{"name":"aikeymouse"},"license":"MIT","_id":"@aikeymouse/chromelink-client@1.2.3","maintainers":[{"name":"aikeymouse","email":"aikeymouse@gmail.com"}],"homepage":"https://github.com/aikeymouse/chrome-link#readme","bugs":{"url":"https://github.com/aikeymouse/chrome-link/issues"},"dist":{"shasum":"e56492aa721c0f70e760dd0b29d95f9fe698790b","tarball":"https://registry.npmjs.org/@aikeymouse/chromelink-client/-/chromelink-client-1.2.3.tgz","fileCount":6,"integrity":"sha512-XZGbo0HGLos2rQykmstTg8HIdWTACnBV/hei+yK5hzQKkDP+rQ6Dhpk2ENTt0WfWXFA/NSWf6okFakO+iPIvcQ==","signatures":[{"sig":"MEUCIQC1H4Lyp/fkDzu+gY50Odc59CKTDaqoeFGRrEXcns6VQQIgHm1bf727eNvsY6TjTticwruJjdgWw9IJaNCSG6yzZqM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aikeymouse%2fchromelink-client@1.2.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":42478},"main":"index.js","types":"index.d.ts","engines":{"node":">=14.0.0"},"gitHead":"a1f9f9961606b4f37a8388d0097bd79335b2e960","scripts":{"prepublishOnly":"echo 'Publishing @aikeymouse/chromelink-client'"},"_npmUser":{"name":"aikeymouse","email":"aikeymouse@gmail.com"},"repository":{"url":"git+https://github.com/aikeymouse/chrome-link.git","type":"git","directory":"clients/node"},"_npmVersion":"10.8.2","description":"Official Node.js client for ChromeLink browser automation via WebSocket","directories":{},"_nodeVersion":"20.20.0","dependencies":{"ws":"^8.0.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/chromelink-client_1.2.3_1771194639843_0.22869588457023649","host":"s3://npm-registry-packages-npm-production"}},"1.2.4":{"name":"@aikeymouse/chromelink-client","version":"1.2.4","description":"Official Node.js client for ChromeLink browser automation via WebSocket","main":"index.js","types":"index.d.ts","scripts":{"prepublishOnly":"echo 'Publishing @aikeymouse/chromelink-client'"},"keywords":["chrome","automation","websocket","testing","browser-control","chromelink","browser-automation","remote-control","e2e-testing"],"author":{"name":"aikeymouse"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/aikeymouse/chrome-link.git","directory":"clients/node"},"homepage":"https://github.com/aikeymouse/chrome-link#readme","bugs":{"url":"https://github.com/aikeymouse/chrome-link/issues"},"engines":{"node":">=14.0.0"},"dependencies":{"ws":"^8.0.0"},"publishConfig":{"access":"public","provenance":true},"_id":"@aikeymouse/chromelink-client@1.2.4","gitHead":"b7ff33145081d960d045917932d216594ab2c18c","_nodeVersion":"20.20.0","_npmVersion":"10.8.2","dist":{"integrity":"sha512-ajeVod8EXA/MEENOJl3q3FJ/fJ+2RhG2INVyBt8Hzd9ypAyJBzW5cwv9xhGbnR7jZS0cVaZbVjbe/BIZVC7fmQ==","shasum":"2d96e82c4e1d543d0d1776833bf897f00c95ce63","tarball":"https://registry.npmjs.org/@aikeymouse/chromelink-client/-/chromelink-client-1.2.4.tgz","fileCount":6,"unpackedSize":42478,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aikeymouse%2fchromelink-client@1.2.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIH/P/qD3DVW2JFBT5UjRDe4iUyR41CXoc6tWkaMFWHMRAiEA1ryeitM0jUxM8BI8T08Fg2dfFP1AXhq8JbRa1hhOj44="}]},"_npmUser":{"name":"aikeymouse","email":"aikeymouse@gmail.com"},"directories":{},"maintainers":[{"name":"aikeymouse","email":"aikeymouse@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/chromelink-client_1.2.4_1771211144782_0.3155134397083721"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-16T18:37:29.061Z","modified":"2026-02-16T03:05:45.252Z","1.1.3":"2026-01-16T18:37:29.257Z","1.1.4":"2026-01-16T19:42:40.287Z","1.1.5":"2026-01-17T19:47:13.084Z","1.2.0":"2026-01-18T19:57:37.210Z","1.2.1":"2026-01-19T02:46:38.971Z","1.2.2":"2026-01-19T23:23:09.826Z","1.2.3":"2026-02-15T22:30:39.992Z","1.2.4":"2026-02-16T03:05:44.929Z"},"bugs":{"url":"https://github.com/aikeymouse/chrome-link/issues"},"author":{"name":"aikeymouse"},"license":"MIT","homepage":"https://github.com/aikeymouse/chrome-link#readme","keywords":["chrome","automation","websocket","testing","browser-control","chromelink","browser-automation","remote-control","e2e-testing"],"repository":{"type":"git","url":"git+https://github.com/aikeymouse/chrome-link.git","directory":"clients/node"},"description":"Official Node.js client for ChromeLink browser automation via WebSocket","maintainers":[{"name":"aikeymouse","email":"aikeymouse@gmail.com"}],"readme":"# @aikeymouse/chromelink-client\n\nOfficial Node.js client for [ChromeLink](https://github.com/aikeymouse/chrome-link) browser automation via WebSocket.\n\nChromeLink enables remote control of Chrome browser through a native messaging host and WebSocket API. This client library provides a clean, Promise-based interface for browser automation, testing, and scripting.\n\n## Installation\n\n```bash\nnpm install @aikeymouse/chromelink-client\n```\n\n**Alternative:** For convenience, you can also install the unscoped package:\n```bash\nnpm install chromelink-client\n```\n\n## Quick Start\n\n```javascript\nconst ChromeLinkClient = require('@aikeymouse/chromelink-client');\n\nasync function example() {\n  // Create client (verbose logging enabled by default)\n  const client = new ChromeLinkClient();\n  \n  // Connect to ChromeLink server\n  await client.connect('ws://localhost:9000');\n  \n  // Open a new tab\n  const { tab } = await client.openTab('https://example.com');\n  const tabId = tab.id;\n  \n  // Interact with elements (tabId required)\n  await client.waitForElement('h1', 10000, tabId);\n  const heading = await client.getText('h1', tabId);\n  console.log('Page heading:', heading.value);\n  \n  // Clean up\n  await client.closeTab(tabId);\n  client.close();\n}\n\nexample().catch(console.error);\n```\n\n## Configuration\n\n### Constructor Options\n\n```javascript\nconst client = new ChromeLinkClient({\n  verbose: false  // Disable console logging (default: true)\n});\n```\n\n**Verbose Mode:**\n- `true` (default): Logs all operations with emoji indicators (✓, ✗, →, ←)\n- `false`: Silent mode for automation/CI environments\n\n## API Reference\n\n### Connection\n\n#### `connect(url, timeout)`\nConnect to WebSocket server and create session.\n\n```javascript\nawait client.connect('ws://localhost:9000', 60000);\n```\n\n**Parameters:**\n- `url` (string): WebSocket URL (default: `'ws://localhost:9000'`)\n- `timeout` (number): Session timeout in ms (default: `60000`)\n\n**Returns:** `Promise<void>`\n\n---\n\n### Navigation\n\n**Deprecated:** The `navigate()` method has been removed. Use `openTab()` or `navigateTab()` instead.\n\n---\n\n### JavaScript Execution\n\n#### `executeJS(code, tabId)`\nExecute JavaScript code in a tab.\n\n```javascript\nconst result = await client.executeJS('document.title', tabId);\nconsole.log('Title:', result.value);\n```\n\n**Parameters:**\n- `code` (string): JavaScript code to execute\n- `tabId` (number): Tab ID to execute in\n\n**Returns:** `Promise<{ value: any, type: string }>`\n\n**Note:** For large results (>1MB), responses are automatically chunked and reassembled.\n\n---\n\n#### `callHelper(functionName, args, tabId)`\nCall predefined DOM helper function (works on CSP-restricted pages).\n\n```javascript\n// Click element\nawait client.callHelper('clickElement', ['button.submit'], tabId);\n\n// Type text\nawait client.callHelper('typeText', ['input[name=\"email\"]', 'test@example.com'], tabId);\n\n// Get element bounds\nconst bounds = await client.callHelper('getElementBounds', ['div.container'], tabId);\n```\n\n**Parameters:**\n- `functionName` (string): Helper function name\n- `args` (array, optional): Function arguments (default: `[]`)\n- `tabId` (number): Tab ID to execute in\n\n**Returns:** `Promise<{ value: any, type: string }>`\n\n**Available Helper Functions:**\n- `clickElement(selector)`\n- `typeText(selector, text, clearFirst)`\n- `getText(selector)`\n- `getHTML(selector)`\n- `elementExists(selector)`\n- `isVisible(selector)`\n- `waitForElement(selector, timeout)`\n- `highlightElement(selector)`\n- `removeHighlights()`\n- `getElementBounds(selector)`\n- `scrollElementIntoView(selector, index)`\n- `inspectElement(selector)`\n- `getContainerElements(containerSelector, elementSelector, includeHidden)`\n- `extractPageElements(containerSelector, includeHidden)` - Extract elements with CSS/XPath selectors and semantic types\n- And more... (see [PROTOCOL.md](https://github.com/aikeymouse/chrome-link/blob/main/docs/PROTOCOL.md#7-call-dom-helper-function))\n\n---\n\n### DOM Interaction\n\n#### `click(selector, tabId)`\nClick an element.\n\n```javascript\nawait client.click('button.submit', tabId);\n```\n\n**Parameters:**\n- `selector` (string): CSS selector\n- `tabId` (number): Tab ID to execute in\n\n**Returns:** `Promise<{ value: boolean }>`\n\n**Throws:** Error if element not found\n\n---\n\n#### `type(selector, text, tabId)`\nType text into an element.\n\n```javascript\nawait client.type('input[name=\"username\"]', 'john@example.com', tabId);\n```\n\n**Parameters:**\n- `selector` (string): CSS selector\n- `text` (string): Text to type\n- `tabId` (number): Tab ID to execute in\n\n**Returns:** `Promise<{ value: boolean }>`\n\n---\n\n#### `getText(selector, tabId)`\nGet text content of an element.\n\n```javascript\nconst result = await client.getText('h1', tabId);\nconsole.log(result.value);\n```\n\n**Parameters:**\n- `selector` (string): CSS selector\n- `tabId` (number): Tab ID to execute in\n\n**Returns:** `Promise<{ value: string }>`\n\n---\n\n#### `waitForElement(selector, timeout, tabId)`\nWait for element to appear in DOM.\n\n```javascript\nawait client.waitForElement('div.loaded', 5000, tabId);\n```\n\n**Parameters:**\n- `selector` (string): CSS selector\n- `timeout` (number): Max wait time in ms (default: `10000`)\n- `tabId` (number): Tab ID to check in\n\n**Returns:** `Promise<{ found: boolean }>`\n\n**Throws:** Error if element not found within timeout\n\n---\n\n### Screenshots\n\n#### `captureScreenshot(options)`\nCapture screenshot of visible area or full page.\n\n```javascript\n// Capture visible area (default)\nconst { screenshot } = await client.captureScreenshot();\n\n// Capture full page\nconst { screenshot } = await client.captureScreenshot({ fullPage: true });\n\n// Custom format and quality\nconst { screenshot } = await client.captureScreenshot({\n  format: 'jpeg',\n  quality: 90,\n  fullPage: true\n});\n```\n\n**Parameters:**\n- `options` (object, optional): Screenshot options\n  - `format` (string): Image format - `'png'` or `'jpeg'` (default: `'png'`)\n  - `quality` (number): JPEG quality 0-100 (default: `90`)\n  - `fullPage` (boolean): Capture full scrollable page (default: `false`)\n\n**Returns:** `Promise<{ success: boolean, screenshot: string }>`\n\n`screenshot` is a base64-encoded data URL (e.g., `data:image/png;base64,...`).\n\n---\n\n### Script Injection\n\n#### `registerInjection(id, code, matches, runAt)`\nRegister a content script to inject into matching pages.\n\n```javascript\nawait client.registerInjection(\n  'my-script',\n  'console.log(\"Page loaded:\", document.title)',\n  ['https://example.com/*'],\n  'document_end'\n);\n```\n\n**Parameters:**\n- `id` (string): Unique injection ID\n- `code` (string): JavaScript code to inject\n- `matches` (array): URL match patterns (e.g., `['https://*.example.com/*']`)\n- `runAt` (string, optional): When to inject - `'document_start'`, `'document_end'`, or `'document_idle'` (default: `'document_end'`)\n\n**Returns:** `Promise<{ success: boolean, id: string }>`\n\n---\n\n#### `unregisterInjection(id)`\nRemove a registered content script injection.\n\n```javascript\nawait client.unregisterInjection('my-script');\n```\n\n**Parameters:**\n- `id` (string): Injection ID to remove\n\n**Returns:** `Promise<{ success: boolean, id: string }>`\n\n---\n\n### Tab Management\n\n#### `listTabs()`\nGet all tabs in current window.\n\n```javascript\nconst result = await client.listTabs();\nconsole.log('Tabs:', result.tabs);\n```\n\n**Returns:** `Promise<{ tabs: Tab[], windowId: number }>`\n\n---\n\n#### `openTab(url, focus)`\nOpen a new tab with URL.\n\n```javascript\nconst { tab } = await client.openTab('https://example.com', true);\nconsole.log('Tab ID:', tab.id);\n```\n\n**Parameters:**\n- `url` (string): URL to open\n- `focus` (boolean, optional): Whether to focus the tab (default: `true`)\n\n**Returns:** `Promise<{ success: boolean, tab: Tab }>`\n\n---\n\n#### `navigateTab(tabId, url, focus)`\nNavigate an existing tab to URL.\n\n```javascript\nawait client.navigateTab(123, 'https://github.com', true);\n```\n\n**Parameters:**\n- `tabId` (number): Tab ID to navigate\n- `url` (string): URL to navigate to\n- `focus` (boolean, optional): Whether to focus the tab (default: `true`)\n\n**Returns:** `Promise<{ success: boolean, tabId: number }>`\n\n---\n\n#### `switchTab(tabId)`\nSwitch to (activate) a tab.\n\n```javascript\nawait client.switchTab(123);\n```\n\n**Parameters:**\n- `tabId` (number): Tab ID to activate\n\n**Returns:** `Promise<{ success: boolean, tabId: number }>`\n\n---\n\n#### `closeTab(tabId)`\nClose a tab.\n\n```javascript\nawait client.closeTab(123);\n```\n\n**Parameters:**\n- `tabId` (number): Tab ID to close\n\n**Returns:** `Promise<{ success: boolean, tabId: number }>`\n\n---\n\n#### `getActiveTab()`\nGet the currently active tab.\n\n```javascript\nconst { tab } = await client.getActiveTab();\nconsole.log('Active tab:', tab.id, tab.url);\n```\n\n**Returns:** `Promise<{ success: boolean, tab: Tab }>`\n\n---\n\n### Navigation History\n\n#### `goBack(tabId)`\nNavigate back in tab history.\n\n```javascript\nawait client.goBack(123);\n```\n\n**Parameters:**\n- `tabId` (number): Tab ID to navigate\n\n**Returns:** `Promise<{ success: boolean, tabId: number, message?: string }>`\n\n**Note:** Returns `success: false` if tab cannot go back (e.g., no history).\n\n---\n\n#### `goForward(tabId)`\nNavigate forward in tab history.\n\n```javascript\nawait client.goForward(123);\n```\n\n**Parameters:**\n- `tabId` (number): Tab ID to navigate\n\n**Returns:** `Promise<{ success: boolean, tabId: number, message?: string }>`\n\n**Note:** Returns `success: false` if tab cannot go forward (e.g., at end of history).\n\n---\n\n### Utilities\n\n#### `wait(ms)`\nWait for a period of time.\n\n```javascript\nawait client.wait(1000); // Wait 1 second\n```\n\n**Parameters:**\n- `ms` (number): Milliseconds to wait\n\n**Returns:** `Promise<void>`\n\n---\n\n#### `close()`\nClose WebSocket connection.\n\n```javascript\nclient.close();\n```\n\n**Returns:** `void`\n\n---\n\n## Usage Examples\n\n### Basic Browser Automation\n\n```javascript\nconst ChromeLinkClient = require('@aikeymouse/chromelink-client');\n\nasync function automateForm() {\n  const client = new ChromeLinkClient();\n  await client.connect();\n  \n  // Open form page\n  const { tab } = await client.openTab('https://example.com/form');\n  const tabId = tab.id;\n  \n  await client.waitForElement('form', 10000, tabId);\n  \n  await client.type('input[name=\"name\"]', 'John Doe', tabId);\n  await client.type('input[name=\"email\"]', 'john@example.com', tabId);\n  await client.click('button[type=\"submit\"]', tabId);\n  \n  await client.wait(2000);\n  const message = await client.getText('.success-message', tabId);\n  console.log('Result:', message.value);\n  \n  await client.closeTab(tabId);\n  client.close();\n}\n\nautomateForm().catch(console.error);\n```\n\n### Silent Mode for CI/Automation\n\n```javascript\nconst ChromeLinkClient = require('@aikeymouse/chromelink-client');\n\nasync function ciTest() {\n  // Disable verbose logging\n  const client = new ChromeLinkClient({ verbose: false });\n  \n  await client.connect();\n  const { tab } = await client.openTab('https://example.com');\n  \n  const title = await client.executeJS('document.title', tab.id);\n  \n  if (title.value !== 'Expected Title') {\n    throw new Error('Title mismatch');\n  }\n  \n  await client.closeTab(tab.id);\n  client.close();\n}\n\nciTest().catch(console.error);\n```\n\n### Using DOM Helpers on CSP-Restricted Pages\n\n```javascript\nconst ChromeLinkClient = require('@aikeymouse/chromelink-client');\n\nasync function cspExample() {\n  const client = new ChromeLinkClient();\n  await client.connect();\n  \n  const { tab } = await client.openTab('https://csp-protected-site.com');\n  const tabId = tab.id;\n  \n  // Use callHelper instead of executeJS for CSP-restricted pages\n  await client.callHelper('clickElement', ['button.action'], tabId);\n  await client.callHelper('typeText', ['input.search', 'query text', true], tabId);\n  \n  const text = await client.callHelper('getText', ['div.result'], tabId);\n  console.log('Result:', text.value);\n  \n  await client.closeTab(tabId);\n  client.close();\n}\n\ncspExample().catch(console.error);\n```\n\n### Handling Large Results (Chunked Responses)\n\n```javascript\nconst ChromeLinkClient = require('@aikeymouse/chromelink-client');\n\nasync function largeData() {\n  const client = new ChromeLinkClient();\n  await client.connect();\n  \n  const { tab } = await client.openTab('https://example.com/big-table');\n  \n  // If result >1MB, it will be automatically chunked and reassembled\n  const result = await client.executeJS(`\n    Array.from(document.querySelectorAll('table tr')).map(row => ({\n      cells: Array.from(row.cells).map(cell => cell.textContent)\n    }))\n  `, tab.id);\n  \n  console.log('Rows:', result.value.length);\n  \n  await client.closeTab(tab.id);\n  client.close();\n}\n\nlargeData().catch(console.error);\n```\n\n### Tab Navigation History\n\n```javascript\nconst ChromeLinkClient = require('@aikeymouse/chromelink-client');\n\nasync function navigationExample() {\n  const client = new ChromeLinkClient();\n  await client.connect();\n  \n  // Open tab with first page\n  const { tab } = await client.openTab('https://example.com/page1');\n  const tabId = tab.id;\n  \n  // Navigate to second page\n  await client.navigateTab(tabId, 'https://example.com/page2');\n  \n  // Navigate to third page\n  await client.navigateTab(tabId, 'https://example.com/page3');\n  \n  // Go back twice\n  await client.goBack(tabId);\n  await client.wait(500);\n  await client.goBack(tabId);\n  \n  // Go forward once\n  await client.wait(500);\n  await client.goForward(tabId);\n  \n  await client.closeTab(tabId);\n  client.close();\n}\n\nnavigationExample().catch(console.error);\n```\n\n### Taking Screenshots\n\n```javascript\nconst ChromeLinkClient = require('@aikeymouse/chromelink-client');\nconst fs = require('fs');\n\nasync function screenshotExample() {\n  const client = new ChromeLinkClient();\n  await client.connect();\n  \n  const { tab } = await client.openTab('https://example.com');\n  await client.waitForElement('body', 5000, tab.id);\n  \n  // Capture full page\n  const { screenshot } = await client.captureScreenshot({ fullPage: true });\n  \n  // Extract base64 data and save\n  const base64Data = screenshot.replace(/^data:image\\/\\w+;base64,/, '');\n  fs.writeFileSync('screenshot.png', Buffer.from(base64Data, 'base64'));\n  \n  console.log('Screenshot saved!');\n  \n  await client.closeTab(tab.id);\n  client.close();\n}\n\nscreenshotExample().catch(console.error);\n```\n\n## Local Development\n\nFor local development and testing, use a file dependency in your `package.json`:\n\n```json\n{\n  \"dependencies\": {\n    \"@aikeymouse/chromelink-client\": \"file:../clients/node\"\n  }\n}\n```\n\nThen run:\n```bash\nnpm install\n```\n\nThis creates a symlink to your local client code for testing changes.\n\n## Protocol Documentation\n\nFor complete protocol specification, see [PROTOCOL.md](https://github.com/aikeymouse/chrome-link/blob/main/docs/PROTOCOL.md).\n\n## Prerequisites\n\n- ChromeLink extension and native host installed\n- ChromeLink server running on `ws://localhost:9000` (default)\n\n## Troubleshooting\n\n**Connection refused:**\n- Ensure ChromeLink native host is installed\n- Verify WebSocket server is running\n- Check firewall settings\n\n**Element not found errors:**\n- Use `waitForElement()` before interacting with dynamic content\n- Verify selector syntax is correct\n- Check page is fully loaded\n\n**CSP errors with executeJS:**\n- Use `callHelper()` instead for CSP-restricted pages\n- Helper functions are pre-injected and CSP-safe\n\n## License\n\nMIT License - see [LICENSE](./LICENSE) file for details.\n\n**Important Licensing Notice:**\n\nThis client library (`@aikeymouse/chromelink-client`) is licensed under the MIT License and can be used freely in both non-commercial and commercial projects.\n\nHowever, this library connects to the **ChromeLink browser extension**, which uses dual licensing:\n\n- **Non-Commercial Use**: Free under CC BY-NC-ND 4.0\n  - Personal projects, education, research, open source (non-commercial)\n  \n- **Commercial Use**: Requires Commercial License\n  - Commercial products, revenue-generating apps, business use, SaaS, client work\n  - Contact: https://github.com/aikeymouse/chrome-link/issues\n\n**Summary:** While this NPM package is MIT licensed, using it in a commercial application requires a ChromeLink commercial license.\n\nSee the main [ChromeLink LICENSE](https://github.com/aikeymouse/chrome-link/blob/main/LICENSE) for complete details.\n\n## Repository\n\nhttps://github.com/aikeymouse/chrome-link\n\n## Issues\n\nhttps://github.com/aikeymouse/chrome-link/issues\n","readmeFilename":"README.md"}