omniwire 3.1.2 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp/server.js +153 -49
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -1766,73 +1766,177 @@ echo "port-knock configured: ${ports.join(' -> ')} -> port ${target}"`;
|
|
|
1766
1766
|
return fail('Invalid action or missing params');
|
|
1767
1767
|
});
|
|
1768
1768
|
// --- Tool 33: omniwire_cdp ---
|
|
1769
|
-
|
|
1770
|
-
|
|
1769
|
+
// Uses the persistent cdp-browser Docker container (puppeteer-core) for all operations.
|
|
1770
|
+
// Falls back to direct Chrome CLI for nodes without the container.
|
|
1771
|
+
const cdpScript = (js) => `docker exec cdp-browser node -e ${JSON.stringify(`const puppeteer=require('puppeteer-core');(async()=>{` +
|
|
1772
|
+
`const r=await fetch('http://127.0.0.1:9222/json/version');const{webSocketDebuggerUrl:ws}=await r.json();` +
|
|
1773
|
+
`const browser=await puppeteer.connect({browserWSEndpoint:ws});` +
|
|
1774
|
+
js +
|
|
1775
|
+
`})().catch(e=>{console.error('ERR:',e.message);process.exit(1)});`)} 2>&1`;
|
|
1776
|
+
server.tool('omniwire_cdp', 'Chrome DevTools Protocol — persistent headless browser via Docker container. Navigate, screenshot, HTML, PDF, cookies, evaluate JS, click, type, wait, network intercept, set-cookies, clear. Reuses pages across calls for speed.', {
|
|
1777
|
+
action: z.enum([
|
|
1778
|
+
'navigate', 'screenshot', 'html', 'text', 'pdf', 'cookies', 'set-cookies', 'clear-cookies',
|
|
1779
|
+
'tabs', 'close-tab', 'evaluate', 'click', 'type', 'wait', 'select',
|
|
1780
|
+
'network', 'status', 'viewport',
|
|
1781
|
+
]).describe('navigate=open URL, screenshot=capture PNG, html=DOM dump, text=innerText, pdf=save PDF, ' +
|
|
1782
|
+
'cookies=get all, set-cookies=inject cookies, clear-cookies=wipe, tabs=list pages, close-tab=close page, ' +
|
|
1783
|
+
'evaluate=run JS in page, click=click selector, type=type into selector, wait=wait for selector, ' +
|
|
1784
|
+
'select=querySelector extract, network=recent requests, status=container health, viewport=set size'),
|
|
1771
1785
|
node: z.string().optional().describe('Node (default: contabo)'),
|
|
1772
|
-
url: z.string().optional().describe('URL for navigate
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1786
|
+
url: z.string().optional().describe('URL for navigate'),
|
|
1787
|
+
selector: z.string().optional().describe('CSS selector for click/type/wait/select'),
|
|
1788
|
+
value: z.string().optional().describe('Text for type action, JS for evaluate, cookies JSON for set-cookies'),
|
|
1789
|
+
file: z.string().optional().describe('Output path for screenshot/pdf (default: /tmp/cdp-*)'),
|
|
1790
|
+
tab: z.number().optional().describe('Tab index (0-based, default: 0 = most recent)'),
|
|
1791
|
+
width: z.number().optional().describe('Viewport width for viewport action (default: 1920)'),
|
|
1792
|
+
height: z.number().optional().describe('Viewport height for viewport action (default: 1080)'),
|
|
1793
|
+
wait_ms: z.number().optional().describe('Wait timeout in ms (default: 10000)'),
|
|
1794
|
+
full_page: z.boolean().optional().describe('Full page screenshot (default: true)'),
|
|
1795
|
+
}, async ({ action, node, url, selector, value, file: outFile, tab, width, height, wait_ms, full_page }) => {
|
|
1776
1796
|
const nodeId = node ?? 'contabo';
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
const
|
|
1782
|
-
`
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
if (
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
const r = await manager.exec(nodeId, cmd);
|
|
1795
|
-
return ok(nodeId, r.durationMs, r.stdout, `navigate`);
|
|
1797
|
+
const tabIdx = tab ?? 0;
|
|
1798
|
+
const timeout = wait_ms ?? 10000;
|
|
1799
|
+
const getPage = `const pages=await browser.pages();const page=pages[${tabIdx}]||pages[0];if(!page){console.log('no pages open');process.exit(0);}`;
|
|
1800
|
+
if (action === 'status') {
|
|
1801
|
+
const r = await manager.exec(nodeId, `docker inspect cdp-browser --format '{{.State.Status}} uptime={{.State.StartedAt}}' 2>/dev/null; ` +
|
|
1802
|
+
`curl -sf http://127.0.0.1:9222/json/version 2>/dev/null | python3 -c "import json,sys;d=json.load(sys.stdin);print(f\\"chrome={d.get('Browser','')} proto={d.get('Protocol-Version','')}\\")" 2>/dev/null; ` +
|
|
1803
|
+
`curl -sf http://127.0.0.1:9222/json/list 2>/dev/null | python3 -c "import json,sys;tabs=json.load(sys.stdin);print(f\\"{len(tabs)} tabs open\\");[print(f\\" {t['id'][:8]} {t.get('url','')[:80]}\\") for t in tabs[:10]]" 2>/dev/null`);
|
|
1804
|
+
return ok(nodeId, r.durationMs, r.stdout, 'cdp status');
|
|
1805
|
+
}
|
|
1806
|
+
if (action === 'navigate') {
|
|
1807
|
+
if (!url)
|
|
1808
|
+
return fail('url required');
|
|
1809
|
+
const u = url.replace(/'/g, "\\'");
|
|
1810
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}` +
|
|
1811
|
+
`await page.goto('${u}',{waitUntil:'networkidle2',timeout:${timeout}});` +
|
|
1812
|
+
`console.log('url='+page.url());console.log('title='+await page.title());`));
|
|
1813
|
+
return ok(nodeId, r.durationMs, r.stdout, 'navigate');
|
|
1796
1814
|
}
|
|
1797
1815
|
if (action === 'screenshot') {
|
|
1798
|
-
const
|
|
1799
|
-
const
|
|
1800
|
-
const
|
|
1801
|
-
|
|
1816
|
+
const out = outFile ?? `/tmp/cdp-screenshot-${Date.now()}.png`;
|
|
1817
|
+
const fp = full_page !== false;
|
|
1818
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}` +
|
|
1819
|
+
`await page.screenshot({path:'${out}',fullPage:${fp}});` +
|
|
1820
|
+
`const fs=require('fs');const sz=fs.statSync('${out}').size;` +
|
|
1821
|
+
`console.log('saved: ${out} ('+Math.round(sz/1024)+'KB) '+page.url());`));
|
|
1802
1822
|
return ok(nodeId, r.durationMs, r.stdout, 'screenshot');
|
|
1803
1823
|
}
|
|
1804
1824
|
if (action === 'html') {
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
|
|
1825
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}` +
|
|
1826
|
+
`const html=await page.content();` +
|
|
1827
|
+
`console.log(html.substring(0,${url ? '50000' : '10000'}));`));
|
|
1808
1828
|
return ok(nodeId, r.durationMs, r.stdout, 'html');
|
|
1809
1829
|
}
|
|
1830
|
+
if (action === 'text') {
|
|
1831
|
+
const sel = selector ? `.replace(/'/g,"\\\\'")` : '';
|
|
1832
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}` +
|
|
1833
|
+
(selector
|
|
1834
|
+
? `const el=await page.$('${selector.replace(/'/g, "\\'")}');const t=el?await page.evaluate(e=>e.innerText,el):'(not found)';console.log(t.substring(0,20000));`
|
|
1835
|
+
: `const t=await page.evaluate(()=>document.body.innerText);console.log(t.substring(0,20000));`)));
|
|
1836
|
+
return ok(nodeId, r.durationMs, r.stdout, 'text');
|
|
1837
|
+
}
|
|
1810
1838
|
if (action === 'pdf') {
|
|
1811
|
-
const
|
|
1812
|
-
const
|
|
1813
|
-
|
|
1814
|
-
|
|
1839
|
+
const out = outFile ?? `/tmp/cdp-page-${Date.now()}.pdf`;
|
|
1840
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}` +
|
|
1841
|
+
`await page.pdf({path:'${out}',format:'A4',printBackground:true});` +
|
|
1842
|
+
`const fs=require('fs');const sz=fs.statSync('${out}').size;` +
|
|
1843
|
+
`console.log('saved: ${out} ('+Math.round(sz/1024)+'KB)');`));
|
|
1815
1844
|
return ok(nodeId, r.durationMs, r.stdout, 'pdf');
|
|
1816
1845
|
}
|
|
1817
1846
|
if (action === 'cookies') {
|
|
1818
|
-
const
|
|
1819
|
-
|
|
1820
|
-
|
|
1847
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}const cookies=await page.cookies();` +
|
|
1848
|
+
`cookies.forEach(c=>console.log(c.domain+'\\t'+c.name+'='+c.value.substring(0,60)+(c.value.length>60?'...':'')));` +
|
|
1849
|
+
`console.log('--- '+cookies.length+' cookies ---');`));
|
|
1850
|
+
return ok(nodeId, r.durationMs, r.stdout, 'cookies');
|
|
1851
|
+
}
|
|
1852
|
+
if (action === 'set-cookies') {
|
|
1853
|
+
if (!value)
|
|
1854
|
+
return fail('value required (JSON array of cookie objects)');
|
|
1855
|
+
const v = value.replace(/'/g, "\\'").replace(/\n/g, '');
|
|
1856
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}const cookies=JSON.parse('${v}');` +
|
|
1857
|
+
`await page.setCookie(...cookies);console.log('set '+cookies.length+' cookies');`));
|
|
1858
|
+
return ok(nodeId, r.durationMs, r.stdout, 'set-cookies');
|
|
1859
|
+
}
|
|
1860
|
+
if (action === 'clear-cookies') {
|
|
1861
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}const client=await page.createCDPSession();` +
|
|
1862
|
+
`await client.send('Network.clearBrowserCookies');console.log('cookies cleared');`));
|
|
1863
|
+
return ok(nodeId, r.durationMs, r.stdout, 'clear-cookies');
|
|
1821
1864
|
}
|
|
1822
1865
|
if (action === 'tabs') {
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1866
|
+
const r = await manager.exec(nodeId, cdpScript(`const pages=await browser.pages();` +
|
|
1867
|
+
`pages.forEach((p,i)=>console.log(i+' '+p.url().substring(0,100)));` +
|
|
1868
|
+
`console.log('--- '+pages.length+' tabs ---');`));
|
|
1825
1869
|
return ok(nodeId, r.durationMs, r.stdout, 'tabs');
|
|
1826
1870
|
}
|
|
1827
|
-
if (action === 'close') {
|
|
1828
|
-
const
|
|
1829
|
-
|
|
1830
|
-
|
|
1871
|
+
if (action === 'close-tab') {
|
|
1872
|
+
const r = await manager.exec(nodeId, cdpScript(`const pages=await browser.pages();` +
|
|
1873
|
+
`if(pages.length<=${tabIdx}){console.log('tab ${tabIdx} not found');process.exit(0);}` +
|
|
1874
|
+
`const url=pages[${tabIdx}].url();await pages[${tabIdx}].close();` +
|
|
1875
|
+
`console.log('closed tab ${tabIdx}: '+url);console.log((pages.length-1)+' tabs remaining');`));
|
|
1876
|
+
return ok(nodeId, r.durationMs, r.stdout, 'close-tab');
|
|
1877
|
+
}
|
|
1878
|
+
if (action === 'evaluate') {
|
|
1879
|
+
if (!value)
|
|
1880
|
+
return fail('value required (JavaScript to evaluate in page context)');
|
|
1881
|
+
const js = value.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n');
|
|
1882
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}const result=await page.evaluate(()=>{${js}});` +
|
|
1883
|
+
`console.log(typeof result==='object'?JSON.stringify(result,null,2):String(result));`));
|
|
1884
|
+
return ok(nodeId, r.durationMs, r.stdout, 'evaluate');
|
|
1885
|
+
}
|
|
1886
|
+
if (action === 'click') {
|
|
1887
|
+
if (!selector)
|
|
1888
|
+
return fail('selector required');
|
|
1889
|
+
const sel = selector.replace(/'/g, "\\'");
|
|
1890
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}await page.waitForSelector('${sel}',{timeout:${timeout}});` +
|
|
1891
|
+
`await page.click('${sel}');console.log('clicked: ${sel}');` +
|
|
1892
|
+
`await new Promise(r=>setTimeout(r,500));console.log('url='+page.url());`));
|
|
1893
|
+
return ok(nodeId, r.durationMs, r.stdout, 'click');
|
|
1894
|
+
}
|
|
1895
|
+
if (action === 'type') {
|
|
1896
|
+
if (!selector || !value)
|
|
1897
|
+
return fail('selector and value required');
|
|
1898
|
+
const sel = selector.replace(/'/g, "\\'");
|
|
1899
|
+
const val = value.replace(/'/g, "\\'");
|
|
1900
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}await page.waitForSelector('${sel}',{timeout:${timeout}});` +
|
|
1901
|
+
`await page.type('${sel}','${val}');console.log('typed ${value.length} chars into ${sel}');`));
|
|
1902
|
+
return ok(nodeId, r.durationMs, r.stdout, 'type');
|
|
1903
|
+
}
|
|
1904
|
+
if (action === 'wait') {
|
|
1905
|
+
if (!selector)
|
|
1906
|
+
return fail('selector required');
|
|
1907
|
+
const sel = selector.replace(/'/g, "\\'");
|
|
1908
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}const el=await page.waitForSelector('${sel}',{timeout:${timeout}});` +
|
|
1909
|
+
`const tag=await page.evaluate(e=>e.tagName+' '+e.className,el);` +
|
|
1910
|
+
`console.log('found: ${sel} → '+tag);`));
|
|
1911
|
+
return ok(nodeId, r.durationMs, r.stdout, 'wait');
|
|
1912
|
+
}
|
|
1913
|
+
if (action === 'select') {
|
|
1914
|
+
if (!selector)
|
|
1915
|
+
return fail('selector required');
|
|
1916
|
+
const sel = selector.replace(/'/g, "\\'");
|
|
1917
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}const els=await page.$$('${sel}');` +
|
|
1918
|
+
`const results=[];for(const el of els.slice(0,20)){` +
|
|
1919
|
+
`const d=await page.evaluate(e=>({tag:e.tagName,text:e.innerText?.substring(0,200),href:e.href||'',src:e.src||''}),el);` +
|
|
1920
|
+
`results.push(d);}` +
|
|
1921
|
+
`console.log(JSON.stringify(results,null,2));console.log('--- '+els.length+' matches ---');`));
|
|
1922
|
+
return ok(nodeId, r.durationMs, r.stdout, 'select');
|
|
1831
1923
|
}
|
|
1832
|
-
if (action === '
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1835
|
-
|
|
1924
|
+
if (action === 'network') {
|
|
1925
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}const client=await page.createCDPSession();` +
|
|
1926
|
+
`const entries=[];client.on('Network.responseReceived',e=>{entries.push({url:e.response.url.substring(0,100),status:e.response.status,type:e.type});});` +
|
|
1927
|
+
`await client.send('Network.enable');` +
|
|
1928
|
+
`await page.reload({waitUntil:'networkidle2',timeout:${timeout}});` +
|
|
1929
|
+
`await client.send('Network.disable');` +
|
|
1930
|
+
`entries.slice(0,30).forEach(e=>console.log(e.status+' '+e.type.padEnd(12)+' '+e.url));` +
|
|
1931
|
+
`console.log('--- '+entries.length+' requests ---');`));
|
|
1932
|
+
return ok(nodeId, r.durationMs, r.stdout, 'network');
|
|
1933
|
+
}
|
|
1934
|
+
if (action === 'viewport') {
|
|
1935
|
+
const w = width ?? 1920;
|
|
1936
|
+
const h = height ?? 1080;
|
|
1937
|
+
const r = await manager.exec(nodeId, cdpScript(`${getPage}await page.setViewport({width:${w},height:${h}});` +
|
|
1938
|
+
`console.log('viewport set to ${w}x${h}');`));
|
|
1939
|
+
return ok(nodeId, r.durationMs, r.stdout, 'viewport');
|
|
1836
1940
|
}
|
|
1837
1941
|
return fail('Invalid action or missing params');
|
|
1838
1942
|
});
|