
Browser Bridge
Expose cookies, Chrome DevTools Protocol (CDP), and tab control from a Manifest V3 extension background worker for agent-driven browser automation.
Install
npx skills add https://github.com/liuzhengdongfortest/codestable --skill browser-bridgeWhat is this skill?
- Background service handles cookies, CDP commands, batched requests, and tab create/switch/remove
- Dynamic declarativeNetRequest rule strips CSP headers to allow eval and inline scripts in framed pages
- Message protocol with cmd keys: cookies, cdp, batch, tabs
- Designed as Chrome extension MV3 background.js bridge pattern
- Enables agents to orchestrate tabs without a separate Playwright server for some flows
Adoption & trust: 508 installs on skills.sh; 902 GitHub stars; 0/3 security scanners passed (skills.sh audits).
Recommended Skills
Agent Browservercel-labs/agent-browser
Lark Imlarksuite/cli
Lark Calendarlarksuite/cli
Lark Sheetslarksuite/cli
Lark Vclarksuite/cli
Lark Contactlarksuite/cli
Journey fit
Primary fit
Browser bridge code is built while wiring agent tooling that must talk to real Chrome sessions during product development. Integrations subphase is the canonical shelf for extension bridges that connect coding agents to live browser state.
Common Questions / FAQ
Is Browser Bridge safe to install?
skills.sh reports 0 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Browser Bridge
// background.js - Cookie + CDP Bridge chrome.runtime.onInstalled.addListener(() => { console.log('CDP Bridge installed'); // Strip CSP headers to allow eval/inline scripts chrome.declarativeNetRequest.updateDynamicRules({ removeRuleIds: [9999], addRules: [{ id: 9999, priority: 1, action: { type: 'modifyHeaders', responseHeaders: [ { header: 'content-security-policy', operation: 'remove' }, { header: 'content-security-policy-report-only', operation: 'remove' } ]}, condition: { urlFilter: '*', resourceTypes: ['main_frame', 'sub_frame'] } }] }); }); async function handleExtMessage(msg, sender) { if (msg.cmd === 'cookies') return await handleCookies(msg, sender); if (msg.cmd === 'cdp') return await handleCDP(msg, sender); if (msg.cmd === 'batch') return await handleBatch(msg, sender); if (msg.cmd === 'tabs') { try { if (msg.method === 'create') { const tab = await chrome.tabs.create({ url: msg.url || 'about:blank' }); return { ok: true, data: { id: tab.id, url: tab.url, title: tab.title } }; } if (msg.method === 'switch') { const tab = await chrome.tabs.update(msg.tabId, { active: true }); await chrome.windows.update(tab.windowId, { focused: true }); return { ok: true }; } if (msg.method === 'remove') { await chrome.tabs.remove(msg.tabId); return { ok: true }; } const tabs = (await chrome.tabs.query({})).filter(t => isScriptable(t.url)); const data = tabs.map(t => ({ id: t.id, url: t.url, title: t.title, active: t.active, windowId: t.windowId })); return { ok: true, data }; } catch (e) { return { ok: false, error: e.message }; } } if (msg.cmd === 'management') { try { if (msg.method === 'list') { const all = await chrome.management.getAll(); return { ok: true, data: all.map(e => ({ id: e.id, name: e.name, enabled: e.enabled, type: e.type, version: e.version })) }; } if (msg.method === 'reload') { chrome.alarms.create('cs-self-reload', { when: Date.now() + 200 }); return { ok: true }; } if (msg.method === 'disable') { await chrome.management.setEnabled(msg.extId, false); return { ok: true }; } if (msg.method === 'enable') { await chrome.management.setEnabled(msg.extId, true); return { ok: true }; } return { ok: false, error: 'Unknown method: ' + msg.method }; } catch (e) { return { ok: false, error: e.message }; } } return { ok: false, error: 'Unknown cmd: ' + msg.cmd }; } chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { handleExtMessage(msg, sender).then(sendResponse); return true; }); async function handleCookies(msg, sender) { try { let url = msg.url || sender.tab?.url; if (!url && msg.tabId) { const tab = await chrome.tabs.get(msg.tabId); url = tab.url; } const origin = url.match(/^https?:\/\/[^\/]+/)[0]; const all = await chrome.cookies.getAll({ url }); const part = await chrome.cookies.getAll({ url, partitionKey: { topLevelSite: origin } }).catch(() => []); const merged = [...all]; for (const c of part) { if (!merged.some(x => x.name === c.name && x.domain === c.domain)) merged.push(c); } return { ok: true, data: merged }; } catch (e) { return { ok: false, error: e.message }; } } async function handleBatch(msg, sender) { const R = []; let attached = null; const resolve$N = (params) => JSON.parse(JSON.stringify(params || {}).replace(/"\$(\d+)\.([^"]+)"/g, (_, i, path) => { let v = R[+i]; for (const k of path.split('.')) v = v[k]; return JSON.stringify(v); })); try { for (const c of msg.commands) { if (c.tabId === undefined && msg.tabId !== undefined) c.tabId = msg.tabId; if (c.cmd === 'cookies') { R.push(await handleCookies(c, sender)); } else if (c.cmd === 'tabs') {