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.
@@ -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
- server.tool('omniwire_cdp', 'Chrome DevTools Protocol browser control on mesh nodes. Launch headless Chrome, navigate, screenshot, extract DOM/cookies, save PDF. For scraping, testing, recon.', {
1770
- action: z.enum(['launch', 'navigate', 'screenshot', 'html', 'pdf', 'cookies', 'tabs', 'close', 'list']).describe('launch=start Chrome, navigate=open URL, screenshot=capture, html=dump DOM, pdf=save PDF, cookies=extract, tabs=list tabs, close=kill, list=sessions'),
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/screenshot/html/pdf'),
1773
- file: z.string().optional().describe('Output path for screenshot/pdf'),
1774
- session_id: z.string().optional().describe('Session ID from launch'),
1775
- }, async ({ action, node, url, file: outFile, session_id }) => {
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 sdir = '/tmp/.omniwire-cdp';
1778
- if (action === 'launch') {
1779
- const id = `cdp-${Date.now().toString(36)}`;
1780
- const port = 9222 + Math.floor(Math.random() * 100);
1781
- const cmd = `mkdir -p ${sdir}; command -v google-chrome >/dev/null || command -v chromium-browser >/dev/null || { echo "chrome not installed"; exit 1; }; ` +
1782
- `CHROME=$(command -v google-chrome || command -v chromium-browser); ` +
1783
- `$CHROME --headless --disable-gpu --no-sandbox --remote-debugging-port=${port} --user-data-dir=${sdir}/${id} ${url ? `"${url}"` : 'about:blank'} &>/dev/null & ` +
1784
- `echo $! > ${sdir}/${id}.pid; echo ${port} > ${sdir}/${id}.port; sleep 1; ` +
1785
- `echo "session=${id} port=${port} pid=$(cat ${sdir}/${id}.pid)"`;
1786
- const r = await manager.exec(nodeId, cmd);
1787
- if (r.code === 0)
1788
- resultStore.set('cdp_session', id);
1789
- return ok(nodeId, r.durationMs, r.stdout, 'chrome launch');
1790
- }
1791
- const sid = session_id ?? resultStore.get('cdp_session') ?? '';
1792
- if (action === 'navigate' && url) {
1793
- const cmd = `PORT=$(cat ${sdir}/${sid}.port 2>/dev/null); curl -sf "http://localhost:$PORT/json/new?${encodeURIComponent(url)}" 2>/dev/null | python3 -c "import json,sys;d=json.load(sys.stdin);print(f\\"tab={d.get('id','')} url={d.get('url','')}\\")" 2>/dev/null || echo "opened ${url}"`;
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 output = outFile ?? `/tmp/screenshot-${Date.now()}.png`;
1799
- const target = url ?? (sid ? `$(curl -sf http://localhost:$(cat ${sdir}/${sid}.port)/json/list 2>/dev/null | python3 -c "import json,sys;print(json.load(sys.stdin)[0]['url'])" 2>/dev/null)` : 'about:blank');
1800
- const cmd = `CHROME=$(command -v google-chrome || command -v chromium-browser); $CHROME --headless --no-sandbox --disable-gpu --screenshot="${output}" --window-size=1920,1080 "${target}" 2>/dev/null && echo "saved: ${output} ($(du -h "${output}" | cut -f1))"`;
1801
- const r = await manager.exec(nodeId, cmd);
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 target = url ?? 'about:blank';
1806
- const cmd = `CHROME=$(command -v google-chrome || command -v chromium-browser); $CHROME --headless --no-sandbox --disable-gpu --dump-dom "${target}" 2>/dev/null | head -200`;
1807
- const r = await manager.exec(nodeId, cmd);
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 output = outFile ?? `/tmp/page-${Date.now()}.pdf`;
1812
- const target = url ?? 'about:blank';
1813
- const cmd = `CHROME=$(command -v google-chrome || command -v chromium-browser); $CHROME --headless --no-sandbox --disable-gpu --print-to-pdf="${output}" --no-pdf-header-footer "${target}" 2>/dev/null && echo "saved: ${output} ($(du -h "${output}" | cut -f1))"`;
1814
- const r = await manager.exec(nodeId, cmd);
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 cmd = `find ${sdir}/${sid}/ -name "Cookies" 2>/dev/null | head -1 | xargs -I{} sqlite3 "{}" "SELECT host_key,name,value,path,expires_utc FROM cookies LIMIT 50;" 2>/dev/null || echo "no cookies DB"`;
1819
- const r = await manager.exec(nodeId, cmd);
1820
- return ok(nodeId, r.durationMs, r.stdout, 'cdp cookies');
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 cmd = `PORT=$(cat ${sdir}/${sid}.port 2>/dev/null); curl -sf http://localhost:$PORT/json/list 2>/dev/null | python3 -c "import json,sys;[print(f\\"{t['id'][:8]} {t.get('url','')}\\" ) for t in json.load(sys.stdin)]" 2>/dev/null || echo "no tabs"`;
1824
- const r = await manager.exec(nodeId, cmd);
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 cmd = `PID=$(cat ${sdir}/${sid}.pid 2>/dev/null); [ -n "$PID" ] && kill $PID 2>/dev/null && rm -rf ${sdir}/${sid}* && echo "closed ${sid}" || echo "not found"`;
1829
- const r = await manager.exec(nodeId, cmd);
1830
- return ok(nodeId, r.durationMs, r.stdout, 'close');
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 === 'list') {
1833
- const cmd = `ls ${sdir}/*.pid 2>/dev/null | while read f; do id=$(basename "$f" .pid); port=$(cat ${sdir}/$id.port 2>/dev/null); pid=$(cat "$f"); ps -p $pid >/dev/null 2>&1 && s="running" || s="dead"; echo "$id port=$port pid=$pid $s"; done || echo "no sessions"`;
1834
- const r = await manager.exec(nodeId, cmd);
1835
- return ok(nodeId, r.durationMs, r.stdout, 'cdp sessions');
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
  });