Skip to content

Manifest & Permissions

The shipped manifest.json for version 1.0.4, reproduced verbatim, followed by an honest justification for each permission.

{
    "manifest_version": 3,
    "name": "PraisonAI Browser Agent",
    "version": "1.0.4",
    "description": "Side panel browser automation. Navigate, click, type, run JavaScript, extract page data, and capture screenshots.",
    "homepage_url": "https://chrome.praison.ai",
    "minimum_chrome_version": "116",
    "permissions": [
        "sidePanel",
        "tabs",
        "debugger",
        "storage",
        "contextMenus",
        "notifications"
    ],
    "commands": {
        "open-panel": {
            "suggested_key": {
                "default": "Ctrl+Shift+P",
                "mac": "Command+Shift+P"
            },
            "description": "Open PraisonAI panel"
        },
        "capture-screenshot": {
            "suggested_key": {
                "default": "Ctrl+Shift+S",
                "mac": "Command+Shift+S"
            },
            "description": "Capture screenshot"
        }
    },
    "background": {
        "service_worker": "background.js",
        "type": "module"
    },
    "side_panel": {
        "default_path": "sidepanel.html"
    },
    "action": {
        "default_title": "PraisonAI Browser Agent",
        "default_icon": {
            "16": "icons/icon16.png",
            "32": "icons/icon32.png",
            "48": "icons/icon48.png",
            "128": "icons/icon128.png"
        }
    },
    "icons": {
        "16": "icons/icon16.png",
        "32": "icons/icon32.png",
        "48": "icons/icon48.png",
        "128": "icons/icon128.png"
    },
    "content_security_policy": {
        "extension_pages": "script-src 'self'; object-src 'self'"
    }
}

There is no action.default_popup: the toolbar button opens the side panel, because the service worker calls chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }). With that set Chrome never fires chrome.action.onClicked, so the extension deliberately registers no listener for it.

Permissions — six, and why each one is needed

Permission Used by Justification
sidePanel The entire UI The extension is a side panel. side_panel.default_path points at sidepanel.html; the toolbar icon opens it through chrome.sidePanel.setPanelBehavior, and the open-panel command and the Open PraisonAI panel context-menu item call chrome.sidePanel.open({ windowId }).
tabs refreshCurrentTab, getTabInfo The panel must know which tab it is pointed at, and must read that tab's URL to tell you before you act whether Chrome will allow automation there. Used via chrome.tabs.query, chrome.tabs.get, onActivated, onUpdated and onRemoved.
debugger Every action This is how the extension does its job. chrome.debugger.attach + sendCommand execute Page.navigate, Input.dispatchMouseEvent, Input.dispatchKeyEvent, Input.insertText, Runtime.evaluate and Page.captureScreenshot. It attaches only to a tab you act on, only when you act, and Chrome shows its debugging banner the whole time. End Session calls chrome.debugger.detach on that tab.
storage History, last screenshot, console buffer chrome.storage.local holds the 50-entry action history. chrome.storage.session holds the most recent screenshot (lastScreenshot) so it can be shown when you open the panel, and a mirror of each tab's captured console output (console:<tabId>) so it survives Chrome suspending the service worker. All three are local to the device.
contextMenus Right-click menu Creates exactly two items — Capture screenshot and Open PraisonAI panel — registered with contexts: ['all'], so they appear on the page and on links, images and selected text too.
notifications Screenshot feedback A screenshot taken with the keyboard shortcut or the context menu happens while the panel may be closed. A single Chrome notification reports whether it succeeded and where to find it — otherwise the action would be silent.

Chrome APIs used without a permission

chrome.runtime, chrome.commands and chrome.windows need no entry in permissions. The panel calls chrome.windows.getCurrent() once, to learn which window it belongs to, so it only ever drives that window's active tab. That is the complete list of Chrome APIs the shipped code touches: chrome.runtime, chrome.tabs, chrome.windows, chrome.debugger, chrome.storage, chrome.sidePanel, chrome.commands, chrome.contextMenus and chrome.notifications.

Permissions deliberately not requested

Not requested Consequence
host_permissions / <all_urls> The extension has no standing access to any site. Access exists only inside a debugger session you started, on the tab you targeted.
scripting / content scripts No code is injected into pages on load. The only code that runs in a page is the expression you type or the extraction snippet, both through Runtime.evaluate on demand.
downloads Screenshots are never written to disk by the extension. The panel shows the image and offers a save link; you decide.
offscreen No offscreen document, no background media capture, no recording.
alarms Nothing runs on a timer. Every action is user-initiated.
activeTab Not needed — the debugger session, gated behind tabs + debugger, is the access path.
cookies, webRequest, history, bookmarks Never used.
externally_connectable No website or other extension can message this one.

Commands

Both are declared in manifest.json and both are handled in chrome.commands.onCommand.

Command Suggested key Action
open-panel Ctrl+Shift+P / Cmd+Shift+P Calls chrome.sidePanel.open({ windowId }) for the window the shortcut was pressed in
capture-screenshot Ctrl+Shift+S / Cmd+Shift+S Captures a screenshot of the active tab and posts a notification

There is no _execute_action command. Opening the panel needs a real window id, so it is done explicitly and synchronously inside the onCommand listener — chrome.sidePanel.open() requires the user gesture, which is spent as soon as the handler awaits anything.

Chrome will not assign a suggested shortcut that is already taken; reassign at chrome://extensions/shortcuts.

Content security policy

script-src 'self'; object-src 'self'

Extension pages may load scripts only from the extension package. No remotely hosted code is fetched or executed — there is no fetch, no eval of remote content, no CDN script and no socket anywhere in the shipped code.

minimum_chrome_version

116. The floor is chrome.sidePanel.open(), which the open-panel command and the Open PraisonAI panel context-menu item both call, and which was added in Chrome 116. (The sidePanel API itself is older — it shipped in Chrome 114 — but a build that only declared side_panel would not be able to open the panel programmatically.) Every CDP command used here is available well before 116, and nothing else in this build needs a newer Chrome.

Build output

npm run build produces exactly this in dist/:

dist/
├── manifest.json
├── background.js       (from src/background/index.ts)
├── sidepanel.js        (from src/sidepanel/index.ts)
├── sidepanel.html
├── styles.css
└── icons/

vite.config.ts declares only the background and sidepanel entry points, so nothing under future/ is compiled or packaged.