@extension.dev/mcp 5.6.0 → 5.7.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +79 -0
- package/README.md +11 -1
- package/dist/module.js +339 -67
- package/dist/src/lib/carrier.d.ts +71 -0
- package/dist/src/tools/dev.d.ts +6 -0
- package/dist/src/tools/stop.d.ts +2 -0
- package/extensions/live-preview/chromium/action/index.css +2 -0
- package/extensions/live-preview/chromium/action/index.html +16 -0
- package/extensions/live-preview/chromium/action/index.js +1 -0
- package/extensions/live-preview/chromium/background/service_worker.js +169 -0
- package/extensions/live-preview/chromium/images/icon.png +0 -0
- package/extensions/live-preview/chromium/manifest.json +66 -0
- package/extensions/live-preview/chromium/sandbox/page-0.html +28 -0
- package/package.json +6 -5
- package/server.json +2 -2
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
(()=>{"use strict";JSON.stringify({alarms:["alarms"],bookmarks:["bookmarks"],contextMenus:["contextMenus"],cookies:["cookies"],downloads:["downloads"],history:["history"],notifications:["notifications"],offscreen:["offscreen"],contentSettings:["contentSettings"],debugger:["debugger"],declarativeNetRequest:["declarativeNetRequest","declarativeNetRequestWithHostAccess"],fontSettings:["fontSettings"],identity:["identity"],idle:["idle"],instanceID:["gcm"],management:["management"],mdns:["mdns"],power:["power"],printerProvider:["printerProvider"],printing:["printing"],privacy:["privacy"],proxy:["proxy"],sidePanel:["sidePanel"],tabCapture:["tabCapture"],tabGroups:["tabGroups"],topSites:["topSites"],tts:["tts"],userScripts:["userScripts"],webNavigation:["webNavigation"],webRequest:["webRequest"]}),JSON.stringify(["getSelf","getPermissionWarningsByManifest","uninstallSelf","installReplacementWebApp"]),JSON.stringify({"system.cpu":["system.cpu"],"system.memory":["system.memory"],"system.storage":["system.storage"],"system.display":["system.display"]}),JSON.stringify(["extension","i18n","runtime","scripting","storage"]);let e={storage:["storage"],notifications:["notifications"],contextMenus:["contextMenus"],downloads:["downloads"],cookies:["cookies"],bookmarks:["bookmarks"],history:["history"],alarms:["alarms"],offscreen:["offscreen"],scripting:["scripting","activeTab"]};function t(t,r){let n=t.split(".")[0]??"",o=e[n];return!o||null===r||o.some(e=>r.has(e))?null:`chrome.${n} needs the "${o.join('" or "')}" permission, which this session did not declare. Declare it in the manifest (and re-register the session) to use this API.`}function r(e,t,r){let n=t.active??t.id===e.activeTabId;return{id:t.id,index:t.index??r,windowId:e.id,url:t.url,title:t.title,active:n,highlighted:n,selected:n,pinned:t.pinned??!1,discarded:t.discarded??!1,groupId:t.tabGroupId??-1,status:t.status??(t.discarded?"unloaded":"complete"),incognito:t.incognito??!1}}let n=new Set(["storage.setAccessLevel","storage.get","storage.set","storage.remove","storage.clear","tabs.create","tabs.update","tabs.remove","tabs.move","tabs.discard","tabs.goBack","tabs.goForward","tabs.group","tabs.ungroup","tabGroups.query","tabGroups.get","tabGroups.update","tabGroups.move","tabs.get"]);async function o(e){let{data:t,windows:n,getStorage:o,setStorage:s,removeStorageKeys:a,clearStorage:i,onStorageChanged:l,onCreateTab:c,onUpdateTab:d,onRemoveTab:u,onMoveTab:g,onDiscardTab:m,onGroupTabs:p,onUngroupTabs:f,onQueryTabGroups:h,onGetTabGroup:w,onUpdateTabGroup:b,onMoveTabGroup:y,onTabGoBack:x,onTabGoForward:v,onSetStorageAccessLevel:k,respond:I}=e;switch(t.api){case"storage.setAccessLevel":{let[e,r]=t.args,n=e?.accessLevel;if("TRUSTED_CONTEXTS"!==n&&"TRUSTED_AND_UNTRUSTED_CONTEXTS"!==n)return void I({error:"Invalid access level."});await k?.(t.extensionId,r??"session",n),I({result:void 0});return}case"storage.get":{let[e,r]=t.args,n=o(t.extensionId,r??"local");if(!e)return void I({result:n});if(Array.isArray(e)){let t={};e.forEach(e=>{e in n&&(t[e]=n[e])}),I({result:t});return}if("string"==typeof e)return void I({result:e in n?{[e]:n[e]}:{}});if("object"==typeof e){let t={};Object.keys(e).forEach(r=>{t[r]=r in n?n[r]:e[r]}),I({result:t});return}I({result:n});return}case"storage.set":{let[e,r]=t.args,n=r??"local";if(e&&"object"==typeof e){let r=o(t.extensionId,n),a={};for(let t of Object.keys(e)){let n=r[t],o=e[t];if(JSON.stringify(n)===JSON.stringify(o))continue;let s={newValue:o};t in r&&(s.oldValue=n),a[t]=s}await s(t.extensionId,e,n),l&&Object.keys(a).length>0&&l(t.extensionId,a,n)}I({result:!0});return}case"storage.remove":{let[e,r]=t.args,n=r??"local",s=(Array.isArray(e)?e:[e]).filter(Boolean).map(String),i=o(t.extensionId,n),c={};for(let e of s)e in i&&(c[e]={oldValue:i[e]});await a(t.extensionId,s,n),l&&Object.keys(c).length>0&&l(t.extensionId,c,n),I({result:!0});return}case"storage.clear":{let[e]=t.args,r=e??"local",n=o(t.extensionId,r),s={};for(let e of Object.keys(n))s[e]={oldValue:n[e]};await i(t.extensionId,r),l&&Object.keys(s).length>0&&l(t.extensionId,s,r),I({result:!0});return}case"tabs.create":{let[e]=t.args;I({result:await c(e?.url??"chrome://newtab",e)??!0});return}case"tabs.update":{let[e,r]=t.args;null!==e&&"object"==typeof e&&(r=e,e=void 0),I({result:await d(e,r??{})??!0});return}case"tabs.remove":{let[e]=t.args;await u(e),I({result:void 0});return}case"tabs.move":{let[e,r]=t.args;I({result:await g?.(t.extensionId,e,r??{})});return}case"tabs.discard":{let[e]=t.args;I({result:await m?.(t.extensionId,e)});return}case"tabs.goBack":{let[e]=t.args;await x?.(t.extensionId,e),I({result:void 0});return}case"tabs.goForward":{let[e]=t.args;await v?.(t.extensionId,e),I({result:void 0});return}case"tabs.group":{let[e]=t.args;I({result:await p?.(t.extensionId,e??{})});return}case"tabs.ungroup":{let[e]=t.args;await f?.(t.extensionId,e),I({result:void 0});return}case"tabGroups.query":{let[e]=t.args;I({result:await h?.(t.extensionId,e)??[]});return}case"tabGroups.get":{let[e]=t.args;I({result:await w?.(t.extensionId,e)});return}case"tabGroups.update":{let[e,r]=t.args;I({result:await b?.(t.extensionId,e,r??{})});return}case"tabGroups.move":{let[e,r]=t.args;I({result:await y?.(t.extensionId,e,r??{})});return}case"tabs.get":{let[e]=t.args;for(let t of n)for(let n=0;n<t.tabs.length;n+=1){let o=t.tabs[n];if(o&&o.id===e)return void I({result:r(t,o,n)})}I({error:`No tab with id: ${String(e)}.`})}}}function s(e,t,r,n){let o;return"function"!=typeof e&&(r({error:(o=n?` on the ${n} lane`:"",`No backend implements ${t}${o}, so the call did not run. This is a preview limitation, not an error in your extension: the API is on the bridge's wire protocol but nothing is wired behind it here.`),refused:!0}),!0)}let a=new Set(["tabs.query","sidePanel.open","action.openPopup","runtime.openOptionsPage","devtools.panels.create","action.setPopup","action.getBadgeText","action.getBadgeBackgroundColor","action.getTitle","action.setBadgeText","action.setBadgeBackgroundColor","action.setTitle","action.setIcon"]);async function i(e){let{data:t,windows:n,onOpenSidePanel:o,onOpenActionPopup:a,onOpenOptionsPage:i,onDevtoolsPanelCreate:l,onSetActionPopup:c,onGetActionState:d,onSetBadgeText:u,onSetBadgeBackground:g,onSetActionTitle:m,onSetActionIcon:p,respond:f}=e;switch(t.api){case"tabs.query":{let[e]=t.args,o=n.find(e=>e.focused)??n[0],s=n;e?.windowId!==void 0?s=-2===e.windowId?o?[o]:[]:n.filter(t=>t.id===e.windowId):e?.currentWindow===!0||e?.lastFocusedWindow===!0?s=o?[o]:[]:e?.currentWindow===!1&&(s=n.filter(e=>e!==o));let a=e?.url?Array.isArray(e.url)?e.url:[e.url]:null;f({result:s.flatMap(t=>t.tabs.map((e,n)=>r(t,e,n)).filter(t=>e?.active===void 0||t.active===e.active).filter(t=>e?.pinned===void 0||t.pinned===e.pinned).filter(t=>e?.status===void 0||t.status===e.status).filter(t=>e?.discarded===void 0||t.discarded===e.discarded).filter(t=>e?.groupId===void 0||(t.groupId??-1)===e.groupId).filter(e=>!a||a.some(t=>(function(e,t){if(!e)return!1;if("<all_urls>"===t)return!0;let r=t.replace(/[.+?^${}()|[\]\\]/g,"\\$&");return RegExp(`^${r.replace(/\*/g,".*")}$`).test(e)})(e.url,t))))});return}case"sidePanel.open":await o(t.extensionId),f({result:void 0});return;case"action.openPopup":if(s(a,t.api,f))return;a?.(t.extensionId),f({result:void 0});return;case"runtime.openOptionsPage":if(s(i,t.api,f))return;i?.(t.extensionId),f({result:void 0});return;case"devtools.panels.create":{if(s(l,t.api,f))return;let[e]=t.args;l?.(t.extensionId,e?.page??""),f({result:!0});return}case"action.setPopup":{if(s(c,t.api,f))return;let[e]=t.args;c?.(t.extensionId,e?.popup??""),f({result:void 0});return}case"action.getBadgeText":{let[e]=t.args,r=await d?.(t.extensionId,e?.tabId);f({result:r?.badgeText??""});return}case"action.getBadgeBackgroundColor":{let[e]=t.args,r=await d?.(t.extensionId,e?.tabId);f({result:r?.badgeColor??null});return}case"action.getTitle":{let[e]=t.args,r=await d?.(t.extensionId,e?.tabId);f({result:r?.title??""});return}case"action.setBadgeText":{let[e]=t.args;await u(t.extensionId,e?.text,e?.tabId),f({result:!0});return}case"action.setBadgeBackgroundColor":{let[e]=t.args;await g(t.extensionId,e?.color,e?.tabId),f({result:!0});return}case"action.setTitle":{let[e]=t.args;await m(t.extensionId,e?.title,e?.tabId),f({result:!0});return}case"action.setIcon":{let[e]=t.args,r="string"==typeof e?.path?e.path:"object"==typeof e?.path?Object.values(e.path)[0]:void 0;await p(t.extensionId,r),f({result:!0})}}}let l=new Set(["contextMenus.create","contextMenus.update","contextMenus.remove","contextMenus.removeAll","notifications.create","notifications.clear","notifications.update","notifications.getAll","downloads.search","downloads.cancel","downloads.pause","downloads.resume","downloads.erase","downloads.download"]);async function c(e){let{data:t,onCreateContextMenu:r,onUpdateContextMenu:n,onRemoveContextMenu:o,onClearContextMenus:a,onCreateNotification:i,onClearNotification:l,onUpdateNotification:c,onGetNotifications:d,onDownload:u,onSearchDownloads:g,onCancelDownload:m,onPauseDownload:p,onResumeDownload:f,onEraseDownloads:h,respond:w}=e;switch(t.api){case"contextMenus.create":{let[e]=t.args;w({result:await r(t.extensionId,e??{})});return}case"contextMenus.update":{let[e,r]=t.args;w({result:await n(t.extensionId,String(e??""),r??{})});return}case"contextMenus.remove":{let[e]=t.args;w({result:await o(t.extensionId,String(e??""))});return}case"contextMenus.removeAll":return void w({result:await a(t.extensionId)});case"notifications.create":{let[e,r]=t.args,n="string"==typeof e?e:void 0,o="string"==typeof e?r:e;w({result:await i(t.extensionId,n,o??{})});return}case"notifications.clear":{let[e]=t.args;w({result:await l(t.extensionId,String(e??""))});return}case"notifications.update":{let[e,r]=t.args;w({result:await c?.(t.extensionId,String(e??""),r??{})??!1});return}case"notifications.getAll":return void w({result:await d(t.extensionId)});case"downloads.search":{let[e]=t.args;w({result:await g?.(t.extensionId,e)??[]});return}case"downloads.cancel":{if(s(m,t.api,w))return;let[e]=t.args;await m?.(t.extensionId,e),w({result:void 0});return}case"downloads.pause":{if(s(p,t.api,w))return;let[e]=t.args;await p?.(t.extensionId,e),w({result:void 0});return}case"downloads.resume":{if(s(f,t.api,w))return;let[e]=t.args;await f?.(t.extensionId,e),w({result:void 0});return}case"downloads.erase":{if(s(h,t.api,w))return;let[e]=t.args;w({result:await h?.(t.extensionId,e)??[]});return}case"downloads.download":{let[e]=t.args;w({result:await u(t.extensionId,e??{})})}}}let d=new Set(["bookmarks.getTree","bookmarks.get","bookmarks.getChildren","bookmarks.search","bookmarks.create","bookmarks.update","bookmarks.remove","bookmarks.removeTree","bookmarks.move","history.search","history.getVisits","history.addUrl","history.deleteUrl","history.deleteRange","history.deleteAll","windows.get","windows.getAll","windows.getCurrent","windows.create","windows.update","windows.remove"]);async function u(e){let{data:t,windows:r,onGetWindow:n,onGetAllWindows:o,onCreateWindow:s,onUpdateWindow:a,onRemoveWindow:i,onGetBookmarkTree:l,onGetBookmarks:c,onGetBookmarkChildren:d,onSearchBookmarks:u,onCreateBookmark:g,onUpdateBookmark:m,onRemoveBookmark:p,onMoveBookmark:f,onSearchHistory:h,onGetHistoryVisits:w,onAddHistoryUrl:b,onDeleteHistoryUrl:y,onDeleteHistoryRange:x,onDeleteAllHistory:v,respond:k}=e;switch(t.api){case"bookmarks.getTree":return void k({result:await l(t.extensionId)});case"bookmarks.get":{let[e]=t.args,r=Array.isArray(e)?e:[e];k({result:await c(t.extensionId,r.filter(e=>null!=e).map(String))});return}case"bookmarks.getChildren":{let[e]=t.args;k({result:await d(t.extensionId,null==e?void 0:String(e))});return}case"bookmarks.search":{let[e]=t.args;k({result:await u(t.extensionId,e)});return}case"bookmarks.create":{let[e]=t.args;k({result:await g(t.extensionId,e??{})});return}case"bookmarks.update":{let[e,r]=t.args;k({result:await m(t.extensionId,String(e??""),r??{})});return}case"bookmarks.remove":{let[e]=t.args;k({result:await p(t.extensionId,String(e??""),!1)});return}case"bookmarks.removeTree":{let[e]=t.args;k({result:await p(t.extensionId,String(e??""),!0)});return}case"bookmarks.move":{let[e,r]=t.args;k({result:await f?.(t.extensionId,String(e??""),r??{})});return}case"history.search":{let[e]=t.args;k({result:await h(t.extensionId,e??{})});return}case"history.getVisits":{let[e]=t.args;k({result:await w(t.extensionId,e??{})});return}case"history.addUrl":{let[e]=t.args;await b(t.extensionId,e??{}),k({result:!0});return}case"history.deleteUrl":{let[e]=t.args;await y(t.extensionId,e??{}),k({result:!0});return}case"history.deleteRange":{let[e]=t.args;await x(t.extensionId,e??{}),k({result:!0});return}case"history.deleteAll":await v(t.extensionId),k({result:!0});return;case"windows.get":{let[e,r]=t.args;k({result:await n(t.extensionId,null==e?void 0:String(e),r)});return}case"windows.getAll":{let[e]=t.args;k({result:await o(t.extensionId,e)});return}case"windows.getCurrent":{let e=r.find(e=>e.focused)??r[0];k({result:e?{id:e.id,focused:!0,tabs:e.tabs}:null});return}case"windows.create":{let[e]=t.args;k({result:await s(t.extensionId,e?.url)});return}case"windows.update":{let[e,r]=t.args;k({result:await a(t.extensionId,null==e?void 0:String(e),r??{})});return}case"windows.remove":{let[e]=t.args;k({result:await i(t.extensionId,null==e?void 0:String(e))})}}}let g=new Set(["runtime.sendMessage","tabs.sendMessage","runtime.getContexts","permissions.contains","permissions.request","permissions.remove","permissions.getAll","offscreen.createDocument","offscreen.closeDocument","offscreen.hasDocument","cookies.get","cookies.getAll","cookies.set","cookies.remove","scripting.executeScript","scripting.insertCSS"]);async function m(e){let{data:t,onRuntimeMessage:r,onTabMessage:n,onGetRuntimeContexts:o,onContainsPermissions:s,onRequestPermissions:a,onRemovePermissions:i,onGetAllPermissions:l,onCreateOffscreenDocument:c,onCloseOffscreenDocument:d,onHasOffscreenDocument:u,onGetCookie:g,onGetAllCookies:m,onSetCookie:p,onRemoveCookie:f,onExecuteScript:h,onInsertCss:w,respond:b,source:y}=e;switch(t.api){case"runtime.sendMessage":{let[e]=t.args;b({result:await r(t.extensionId,e,y)});return}case"tabs.sendMessage":{let[e,r]=t.args;if(!n)return void b({error:"Could not establish connection. Receiving end does not exist."});b({result:await n(t.extensionId,e,r)});return}case"runtime.getContexts":return void b({result:await o(t.extensionId)});case"permissions.contains":{let[e]=t.args;b({result:await s(t.extensionId,e??{})});return}case"permissions.request":{let[e]=t.args;b({result:await a(t.extensionId,e??{})});return}case"permissions.remove":{let[e]=t.args;b({result:await i(t.extensionId,e??{})});return}case"permissions.getAll":return void b({result:await l(t.extensionId)});case"offscreen.createDocument":{let[e]=t.args,r=await c(t.extensionId,e??{});b({result:"boolean"!=typeof r||r});return}case"offscreen.closeDocument":{let e=await d(t.extensionId);b({result:"boolean"!=typeof e||e});return}case"offscreen.hasDocument":return void b({result:await u(t.extensionId)});case"cookies.get":{let[e]=t.args;b({result:await g(t.extensionId,e??{})});return}case"cookies.getAll":{let[e]=t.args;b({result:await m(t.extensionId,e??{})});return}case"cookies.set":{let[e]=t.args;b({result:await p(t.extensionId,e??{})});return}case"cookies.remove":{let[e]=t.args;b({result:await f(t.extensionId,e??{})});return}case"scripting.executeScript":{let[e]=t.args;b({result:await h(t.extensionId,e??{})});return}case"scripting.insertCSS":{let[e]=t.args;await w(t.extensionId,e??{}),b({result:!0})}}}let p=["local","sync","session","managed"];function f(e,t){let r=Math.min(e.length,t.length),n=0;for(;n<r&&e[n]===t[n];)n+=1;return n}function h(e){let t=function(e){let t=[...n,...a,...l,...d,...g].sort();if(t.includes(e))return null;let r=e.split(".");if(3===r.length&&"storage"===r[0]&&p.includes(r[1]??"")){let e=`storage.${r[2]}`;if(t.includes(e))return e}let o=r[r.length-1]??"",s=t.filter(e=>e.endsWith(`.${o}`));if(s.length)return s.sort((t,r)=>f(r,e)-f(t,e))[0];let i=t.filter(e=>e.startsWith(`${r[0]}.`));return i.length?i[0]??null:null}(e),r=`Unsupported API: ${e}.`;if(!t)return`${r} No wire name resembles it, so this API is not on the bridge at all. It may still work natively inside your extension's own contexts: the bridge is what an observer page can DRIVE, not the whole runtime.`;let o=t.startsWith("storage.")&&e.startsWith("storage.")?` The bridge takes the storage area as an ARGUMENT, not as part of the name: ${t}(keys, "local").`:"";return`${r} The closest wire name the bridge routes is ${t}.${o}`}let w=e=>"string"==typeof e&&e.length>0;function b(e,t){return`Error in invocation of ${e}: ${t}`}function y(e,t){return b(e,`missing required property '${t}'`)}function x(e,t,r,n){return b(e,`'${t}' expects a ${r}, got ${typeof n}`)}async function v(e){let{data:r,respond:s,getDeclaredPermissions:p,getSessionStorageAccessLevel:f}=e;try{var b;if(b=r.api,!(n.has(b)||a.has(b)||l.has(b)||d.has(b)||g.has(b)))return void s({error:h(r.api),refused:!0});let v=function(e,t){let r=t[0];switch(e){case"downloads.download":if(!r||!("url"in r))return y(e,"url");if(!w(r.url))return x(e,"url","non-empty string",r.url);if(void 0!==r.filename&&"string"!=typeof r.filename)return x(e,"filename","string",r.filename);return null;case"offscreen.createDocument":if(!r||!w(r.url))return y(e,"url");if(!Array.isArray(r.reasons)||0===r.reasons.length)return y(e,"reasons");if(!w(r.justification))return y(e,"justification");return null;case"contextMenus.create":if(!r)return y(e,"title");if(void 0!==r.id&&"string"!=typeof r.id)return x(e,"id","string",r.id);if("separator"!==r.type&&!w(r.title))return y(e,"title");return null;case"notifications.create":{let r="string"==typeof t[0]?t[1]:t[0];if(!r)return y(e,"options");for(let t of["type","iconUrl","title","message"])if(!w(r[t]))return y(e,t);return null}case"action.setTitle":if(r&&void 0!==r.title&&"string"!=typeof r.title)return x(e,"title","string",r.title);return null;case"action.setBadgeText":if(r&&void 0!==r.text&&"string"!=typeof r.text)return x(e,"text","string",r.text);return null;case"action.setBadgeBackgroundColor":if(!r||void 0===r.color)return y(e,"color");if("string"!=typeof r.color&&!Array.isArray(r.color))return x(e,"color","string or array",r.color);return null;case"tabs.create":if(r?.url!==void 0&&"string"!=typeof r.url)return x(e,"url","string",r.url);return null;default:return null}}(r.api,r.args);if(v)return void s({error:v});if(p){let e=t(r.api,p(r.extensionId));if(e)return void s({error:e,refused:!0})}if(r.untrustedContext&&r.api.startsWith("storage.")){let e="storage.clear"===r.api?r.args[0]:r.args[1],t=f?.(r.extensionId)??"TRUSTED_CONTEXTS";if("session"===e&&"TRUSTED_AND_UNTRUSTED_CONTEXTS"!==t)return void s({error:"Access to storage is not allowed from this context.",refused:!0})}if(n.has(r.api))return void await o(e);if(a.has(r.api))return void await i(e);if(l.has(r.api))return void await c(e);if(d.has(r.api))return void await u(e);if(g.has(r.api))return void await m(e);s({error:h(r.api),refused:!0})}catch(e){s({error:e instanceof Error?e.message:"Unknown error"})}}function k(e,t){throw Error(`${e} did not run on the real lane: ${t}`)}let I=e=>{let t=Number(e);return Number.isFinite(t)?t:void 0},S=e=>{let t=Number(e);return Number.isFinite(t)?t:void 0},C=new Set(["system.cpu.getInfo","system.memory.getInfo","system.storage.getInfo","system.storage.ejectDevice","system.display.getInfo","system.display.getDisplayLayout","system.display.setDisplayProperties","system.display.setDisplayLayout"]),T=["topSites","fontSettings","printing","power","tts","userScripts","debugger","system.network","enterprise.deviceAttributes","instanceID","identity","webNavigation","proxy.settings","contentSettings","tabCapture"].sort((e,t)=>t.length-e.length);function A(e){if(C.has(e))return"refused";for(let t of T)if(e===t||e.startsWith(`${t}.`))return"mocked";return"emulated"}let E=new Set(["storage","action","offscreen","runtime","tabs.sendMessage","scripting","downloads","notifications","contextMenus"]),$=new Set(["local","sync","session","managed"]);function D(e,t){let r=e.split(".");if("storage"!==r[0])return e;if(3===r.length&&$.has(r[1]??""))return`storage.${r[2]}@${r[1]}`;if(2===r.length){let e="clear"===r[1]?t?.[0]:t?.[1];return`storage.${r[1]}@${"string"==typeof e?e:"local"}`}return e}function M(e){return e.refused||N(e.error)?"refused":"real"===e.lane?"real":A(e.api)}function N(e){return!!e&&(e.startsWith("Unsupported API: ")||e.startsWith("No backend implements ")||/needs the ".+" permission, which this (session did not declare|extension's manifest does not declare)/.test(e))}let B=new TextEncoder;function R(e,t){return{path:e,bytes:B.encode(t),text:t}}let P={manifest_version:3,name:"Hello Emulator",version:"0.1.0",description:"Trace fixture exercising emulated, mocked, and refused chrome.* tiers.",permissions:["storage","tabs","notifications","system.cpu","bookmarks","history","cookies","alarms","topSites","fontSettings","power","tts","identity","webNavigation"],action:{default_title:"Hello Emulator",default_popup:"popup.html"},background:{service_worker:"background.js"}},O=`<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<style>
|
|
6
|
+
body {
|
|
7
|
+
width: 300px;
|
|
8
|
+
margin: 0;
|
|
9
|
+
padding: 12px;
|
|
10
|
+
font: 13px/1.45 system-ui, sans-serif;
|
|
11
|
+
color: #1f1f1f;
|
|
12
|
+
background: #fff;
|
|
13
|
+
}
|
|
14
|
+
h1 { font-size: 14px; margin: 0 0 8px; }
|
|
15
|
+
button {
|
|
16
|
+
display: block;
|
|
17
|
+
width: 100%;
|
|
18
|
+
margin: 4px 0;
|
|
19
|
+
padding: 6px 8px;
|
|
20
|
+
font: inherit;
|
|
21
|
+
border: 1px solid #dadce0;
|
|
22
|
+
border-radius: 6px;
|
|
23
|
+
background: #f8f9fa;
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
text-align: left;
|
|
26
|
+
}
|
|
27
|
+
button:hover { background: #f1f3f4; }
|
|
28
|
+
#out {
|
|
29
|
+
margin-top: 8px;
|
|
30
|
+
padding: 8px;
|
|
31
|
+
min-height: 48px;
|
|
32
|
+
border-radius: 6px;
|
|
33
|
+
background: #f1f3f4;
|
|
34
|
+
font-family: ui-monospace, monospace;
|
|
35
|
+
font-size: 11px;
|
|
36
|
+
white-space: pre-wrap;
|
|
37
|
+
word-break: break-word;
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
40
|
+
</head>
|
|
41
|
+
<body>
|
|
42
|
+
<h1>Hello Emulator</h1>
|
|
43
|
+
<button id="count">storage.local: increment counter</button>
|
|
44
|
+
<button id="tabs">tabs.query: current window</button>
|
|
45
|
+
<button id="ping">runtime.sendMessage: ping background</button>
|
|
46
|
+
<button id="notify">notifications.create</button>
|
|
47
|
+
<button id="cpu">system.cpu.getInfo (expect graceful refusal)</button>
|
|
48
|
+
<button id="battery"><strong>Run API battery</strong> (one call per namespace)</button>
|
|
49
|
+
<div id="out">Click a button to call chrome.*</div>
|
|
50
|
+
<script src="popup.js"></script>
|
|
51
|
+
</body>
|
|
52
|
+
</html>
|
|
53
|
+
`,q=`const out = document.getElementById("out");
|
|
54
|
+
const show = (label, value) => {
|
|
55
|
+
out.textContent = label + "\\n" + JSON.stringify(value, null, 2);
|
|
56
|
+
};
|
|
57
|
+
const showError = (label, error) => {
|
|
58
|
+
out.textContent = label + " FAILED\\n" + (error?.message ?? String(error));
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
document.getElementById("count").addEventListener("click", async () => {
|
|
62
|
+
try {
|
|
63
|
+
const { count = 0 } = await chrome.storage.local.get("count");
|
|
64
|
+
const next = count + 1;
|
|
65
|
+
await chrome.storage.local.set({ count: next });
|
|
66
|
+
show("storage.local", { count: next });
|
|
67
|
+
} catch (error) {
|
|
68
|
+
showError("storage.local", error);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
document.getElementById("tabs").addEventListener("click", async () => {
|
|
73
|
+
try {
|
|
74
|
+
const tabs = await chrome.tabs.query({ currentWindow: true });
|
|
75
|
+
show(
|
|
76
|
+
"tabs.query",
|
|
77
|
+
tabs.map((tab) => ({ id: tab.id, url: tab.url, active: tab.active })),
|
|
78
|
+
);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
showError("tabs.query", error);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
document.getElementById("ping").addEventListener("click", async () => {
|
|
85
|
+
try {
|
|
86
|
+
const reply = await chrome.runtime.sendMessage({ type: "ping" });
|
|
87
|
+
show("runtime.sendMessage", reply);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
showError("runtime.sendMessage", error);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
document.getElementById("notify").addEventListener("click", async () => {
|
|
94
|
+
try {
|
|
95
|
+
const id = await chrome.notifications.create({
|
|
96
|
+
type: "basic",
|
|
97
|
+
iconUrl: "icon.png",
|
|
98
|
+
title: "Hello Emulator",
|
|
99
|
+
message: "Notification from the emulated extension.",
|
|
100
|
+
});
|
|
101
|
+
show("notifications.create", { id });
|
|
102
|
+
} catch (error) {
|
|
103
|
+
showError("notifications.create", error);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
document.getElementById("cpu").addEventListener("click", async () => {
|
|
108
|
+
try {
|
|
109
|
+
const info = await chrome.system.cpu.getInfo();
|
|
110
|
+
show("system.cpu.getInfo", info);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
showError("system.cpu.getInfo", error);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// The poke battery: one representative call per namespace, spanning all
|
|
117
|
+
// three tiers, so the coverage matrix fills from a single click. Each call
|
|
118
|
+
// runs in this real guest context through the real shim; failures are
|
|
119
|
+
// expected for the refused tier and counted, not hidden.
|
|
120
|
+
document.getElementById("battery").addEventListener("click", async () => {
|
|
121
|
+
const calls = [
|
|
122
|
+
["storage.local.get", () => chrome.storage.local.get(null)],
|
|
123
|
+
["tabs.query", () => chrome.tabs.query({})],
|
|
124
|
+
["runtime.getPlatformInfo", () => chrome.runtime.getPlatformInfo()],
|
|
125
|
+
["alarms.getAll", () => chrome.alarms.getAll()],
|
|
126
|
+
["bookmarks.getTree", () => chrome.bookmarks.getTree()],
|
|
127
|
+
["history.search", () => chrome.history.search({ text: "" })],
|
|
128
|
+
["cookies.getAll", () => chrome.cookies.getAll({})],
|
|
129
|
+
["topSites.get", () => chrome.topSites.get()],
|
|
130
|
+
["fontSettings.getDefaultFontSize", () =>
|
|
131
|
+
chrome.fontSettings.getDefaultFontSize({})],
|
|
132
|
+
["power.requestKeepAwake", () =>
|
|
133
|
+
Promise.resolve(chrome.power.requestKeepAwake("display"))],
|
|
134
|
+
["tts.getVoices", () => chrome.tts.getVoices()],
|
|
135
|
+
["identity.getProfileUserInfo", () =>
|
|
136
|
+
chrome.identity.getProfileUserInfo()],
|
|
137
|
+
["webNavigation.getAllFrames", () =>
|
|
138
|
+
chrome.webNavigation.getAllFrames({ tabId: 1 })],
|
|
139
|
+
["system.network.getNetworkInterfaces", () =>
|
|
140
|
+
chrome.system.network.getNetworkInterfaces()],
|
|
141
|
+
["system.cpu.getInfo", () => chrome.system.cpu.getInfo()],
|
|
142
|
+
];
|
|
143
|
+
let ok = 0;
|
|
144
|
+
const failed = [];
|
|
145
|
+
for (const [name, call] of calls) {
|
|
146
|
+
try {
|
|
147
|
+
await call();
|
|
148
|
+
ok++;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
failed.push(name + ": " + (error && error.message));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
show("battery", {
|
|
154
|
+
total: calls.length,
|
|
155
|
+
ok,
|
|
156
|
+
failed: failed.length ? failed : "none",
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
`,U=`chrome.runtime.onInstalled.addListener(() => {
|
|
160
|
+
console.log("[hello-emulator] installed");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
164
|
+
if (message?.type === "ping") {
|
|
165
|
+
sendResponse({ type: "pong", from: "background", at: Date.now() });
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
});
|
|
169
|
+
`;R("manifest.json",JSON.stringify(P,null,2)),R("popup.html",O),R("popup.js",q),R("background.js",U),P.name;let{PING:j,PONG:G,BRIDGE:L,SESSION:W,INJECT:V,EVENT_PORT:_,EVENT:H,TRACE:F}={PING:"extensiondev:ping",PONG:"extensiondev:pong",BRIDGE:"extensiondev:bridge",SESSION:"extensiondev:session",INJECT:"extensiondev:inject",EVENT_PORT:"extensiondev:events",EVENT:"extensiondev:event",TRACE:"extensiondev:trace"},J=function(e={}){let t=e.engine??"chromium-emulated",r=e.limit??500,n=e.now??(()=>new Date),o=n().toISOString(),s=[],a=new Set,i=0,l=e=>{for(let t of(s.push(e),s.length>r&&s.splice(0,s.length-r),a))t(e);return e};return{recordCall:(e,r,o,s)=>l({seq:i+=1,api:e,args:o??[],extensionId:r,at:n().toISOString(),tier:A(e),engine:t,policy:s}),recordOutcome(e){let r=D(e.api,e.args),o=s.find(t=>t.extensionId===e.extensionId&&D(t.api,t.args)===r&&void 0===t.status&&void 0===t.requestId),c={requestId:e.requestId,lane:e.lane,status:void 0===e.error?"ok":"error",error:e.error,result:e.result,durationMs:e.durationMs,carrierScoped:!e.refused&&!N(e.error)&&function(e,t){if("real"!==t)return!1;if(E.has(e))return!0;let r=e.split(".")[0]??"";return E.has(r)}(e.api,e.lane)||void 0};if(o){Object.assign(o,c),o.tier=M(e);for(let e of a)e(o);return o}return l({seq:i+=1,api:e.api,args:e.args,extensionId:e.extensionId,at:n().toISOString(),tier:M(e),engine:t,...c})},events:()=>[...s],snapshot(){let e={real:0,emulated:0,mocked:0,refused:0},r=0;for(let t of s)e[t.tier??A(t.api)]+=1,"error"===t.status&&(r+=1);return{engine:t,startedAt:o,events:[...s],counts:{total:s.length,byTier:e,errors:r}}},subscribe:e=>(a.add(e),()=>a.delete(e)),clear(){s.length=0}}}({engine:"chromium-real"}),z=new Set;J.subscribe(e=>{for(let t of z)try{t.postMessage({type:F,event:e})}catch{}});let X=function(){let e={local:{},sync:{},session:{},managed:{}},t=e=>chrome.storage[e??"local"]??chrome.storage.local,r=t=>e[t??"local"]??e.local;for(let e of["local","sync","session","managed"])t(e).get(null).then(t=>{Object.assign(r(e),t)}).catch(()=>void 0);chrome.storage.onChanged.addListener((t,n)=>{if(!(n in e))return;let o=r(n);for(let[e,r]of Object.entries(t))void 0===r.newValue?delete o[e]:o[e]=r.newValue});let n=new Map;return{getStorage:(e,t)=>({...r(t)}),setStorage:(e,n,o)=>(Object.assign(r(o),n),t(o).set(n)),removeStorageKeys:(e,n,o)=>{let s=r(o);for(let e of n)delete s[e];return t(o).remove(n)},clearStorage:(e,n)=>{let o=r(n);for(let e of Object.keys(o))delete o[e];return t(n).clear()},onSetStorageAccessLevel:async(e,t,r)=>{if("session"!==t)return;n.set(e,r);let o=chrome.storage.session;await o.setAccessLevel?.({accessLevel:r})},getSessionStorageAccessLevel:e=>n.get(e),onCreateTab:(e,t)=>chrome.tabs.create({url:e,...t?.active!==void 0?{active:t.active}:{},...t?.pinned!==void 0?{pinned:t.pinned}:{},...t?.index!==void 0?{index:t.index}:{},...t?.windowId!==void 0?{windowId:t.windowId}:{},...t?.openerTabId!==void 0?{openerTabId:t.openerTabId}:{}}),onUpdateTab:(e,t)=>{let r=I(e);return null!=r?chrome.tabs.update(r,t):chrome.tabs.update(t)},onRemoveTab:e=>{let t=I(e);return null==t&&k("tabs.remove",`"${String(e)}" is not a real tab id`),chrome.tabs.remove(t)},onGetWindow:(e,t,r)=>{let n=S(t);return null==n?chrome.windows.getCurrent(r??{}):chrome.windows.get(n,r??{})},onGetAllWindows:(e,t)=>chrome.windows.getAll(t??{}),onCreateWindow:(e,t)=>chrome.windows.create({url:t}),onUpdateWindow:(e,t,r)=>{let n=S(t);return null==n?null:chrome.windows.update(n,r)},onRemoveWindow:async(e,t)=>{let r=S(t);return null!=r&&(await chrome.windows.remove(r),!0)},onOpenSidePanel:async()=>{let e=await chrome.windows.getCurrent();null==e.id&&k("sidePanel.open","there is no focused window to open it in"),await chrome.sidePanel.open({windowId:e.id})},onSetBadgeText:(e,t,r)=>{let n=I(r);return chrome.action.setBadgeText(null!=n?{text:t??"",tabId:n}:{text:t??""})},onSetBadgeBackground:(e,t,r)=>{t||k("action.setBadgeBackgroundColor","no color was given");let n=I(r);return chrome.action.setBadgeBackgroundColor(null!=n?{color:t,tabId:n}:{color:t})},onSetActionTitle:(e,t,r)=>{let n=I(r);return chrome.action.setTitle(null!=n?{title:t??"",tabId:n}:{title:t??""})},onGetActionState:async(e,t)=>{let r=I(t),n=null!=r?{tabId:r}:{},[o,s,a]=await Promise.all([chrome.action.getBadgeText(n),chrome.action.getBadgeBackgroundColor(n),chrome.action.getTitle(n)]);return{badgeText:o,badgeColor:Array.isArray(s)?`rgba(${s[0]}, ${s[1]}, ${s[2]}, ${(s[3]??255)/255})`:s??void 0,title:a}},onSetActionIcon:(e,t)=>(t||k("action.setIcon","no icon path was given"),chrome.action.setIcon({path:t})),onCreateContextMenu:(e,t)=>chrome.contextMenus.create(t),onUpdateContextMenu:async(e,t,r)=>(await chrome.contextMenus.update(t,r),!0),onRemoveContextMenu:async(e,t)=>(await chrome.contextMenus.remove(t),!0),onClearContextMenus:async()=>(await chrome.contextMenus.removeAll(),!0),onCreateNotification:(e,t,r)=>chrome.notifications.create(t??"",r),onClearNotification:(e,t)=>chrome.notifications.clear(t),onGetNotifications:()=>chrome.notifications.getAll(),onDownload:(e,t)=>chrome.downloads.download(t),onSearchDownloads:(e,t)=>chrome.downloads.search(t??{}),onCancelDownload:async(e,t)=>{await chrome.downloads.cancel(t).catch(()=>void 0)},onPauseDownload:(e,t)=>chrome.downloads.pause(t),onResumeDownload:(e,t)=>chrome.downloads.resume(t),onEraseDownloads:(e,t)=>chrome.downloads.erase(t??{}),onGetBookmarkTree:()=>chrome.bookmarks.getTree(),onGetBookmarks:(e,t)=>chrome.bookmarks.get(t),onGetBookmarkChildren:(e,t)=>t?chrome.bookmarks.getChildren(t):chrome.bookmarks.getTree(),onSearchBookmarks:(e,t)=>chrome.bookmarks.search(t??""),onCreateBookmark:(e,t)=>chrome.bookmarks.create(t),onUpdateBookmark:(e,t,r)=>chrome.bookmarks.update(t,r),onRemoveBookmark:async(e,t,r)=>(r?await chrome.bookmarks.removeTree(t):await chrome.bookmarks.remove(t),!0),onSearchHistory:(e,t)=>chrome.history.search({text:t?.text??"",...t}),onGetHistoryVisits:(e,t)=>chrome.history.getVisits({url:t?.url??""}),onAddHistoryUrl:async(e,t)=>{t?.url&&await chrome.history.addUrl({url:t.url})},onDeleteHistoryUrl:async(e,t)=>{t?.url&&await chrome.history.deleteUrl({url:t.url})},onDeleteHistoryRange:async(e,t)=>{await chrome.history.deleteRange({startTime:t?.startTime??0,endTime:t?.endTime??Date.now()})},onDeleteAllHistory:async()=>{await chrome.history.deleteAll()},onRuntimeMessage:(e,t)=>chrome.runtime.sendMessage(t),onGetRuntimeContexts:()=>chrome.runtime.getContexts({}),onContainsPermissions:(e,t)=>chrome.permissions.contains(t),onRequestPermissions:(e,t)=>chrome.permissions.request(t),onRemovePermissions:(e,t)=>chrome.permissions.remove(t),onGetAllPermissions:async()=>{let e=await chrome.permissions.getAll();return{permissions:e.permissions??[],origins:e.origins??[]}},onCreateOffscreenDocument:async(e,t)=>(await chrome.offscreen.createDocument(t),!0),onCloseOffscreenDocument:async()=>(await chrome.offscreen.closeDocument(),await chrome.offscreen.hasDocument().catch(()=>!1)&&k("offscreen.closeDocument","the offscreen document was still open after closeDocument resolved"),!0),onHasOffscreenDocument:()=>chrome.offscreen.hasDocument(),onGetCookie:(e,t)=>chrome.cookies.get(t),onGetAllCookies:(e,t)=>chrome.cookies.getAll(t),onSetCookie:(e,t)=>chrome.cookies.set(t),onRemoveCookie:(e,t)=>chrome.cookies.remove(t),onExecuteScript:(e,t)=>{let r=I(t.target?.tabId);return null==r&&k("scripting.executeScript",`target.tabId "${String(t.target?.tabId)}" is not a real tab id`),t.files?.length||k("scripting.executeScript","only the `files` form is supported here; a func/code body cannot be serialized across the bridge into a real page. Ship the code as a file in your extension and pass files: [...]"),chrome.scripting.executeScript({target:{tabId:r},files:t.files})},onInsertCss:async(e,t)=>{let r=I(t.target?.tabId);(null==r&&k("scripting.insertCSS",`target.tabId "${String(t.target?.tabId)}" is not a real tab id`),t.css)?await chrome.scripting.insertCSS({target:{tabId:r},css:t.css}):t.files?.length?await chrome.scripting.insertCSS({target:{tabId:r},files:t.files}):k("scripting.insertCSS","neither css nor files was given")}}}(),K=new Map;async function Q(){try{return(await chrome.windows.getAll({populate:!0})).map(e=>{let t=e.tabs??[],r=t.find(e=>e.active);return{id:e.id??void 0,focused:e.focused,activeTabId:r?.id??"",tabs:t.map(e=>({id:e.id??"",url:e.url??e.pendingUrl??"",title:e.title??"",active:e.active,index:e.index,status:e.status,pinned:e.pinned,discarded:e.discarded,incognito:e.incognito,tabGroupId:e.groupId}))}})}catch{return[]}}async function Y(e){let r,n=Date.now(),o=t=>(J.recordOutcome({requestId:e.requestId,extensionId:e.extensionId,api:e.api,args:e.args,lane:"real",result:t.result,error:t.error,durationMs:Date.now()-n,source:"frame",...t.refused?{refused:!0}:{}}),t),s=(r=t(e.api,K.get(e.extensionId)??new Set))?{error:r,refused:!0}:null;if(s)return o(s);let a="tabs.query"===e.api||"tabs.get"===e.api||"windows.getCurrent"===e.api?await Q():[];return new Promise(t=>{let r=!1,n=e=>{r||(r=!0,t(o(e)))};v({data:e,windows:a,...X,respond:n,source:"frame"}).catch(e=>n({error:e instanceof Error?e.message:"Bridge error"}))})}async function Z(e){let t,r,{extensionId:n,url:o,code:s,css:a,matches:i=[]}=e,l=K.get(n);if(!(l&&(l.has("scripting")||l.has("activeTab"))))return{ok:!1,error:'This extension does not declare the "scripting" (or "activeTab") permission.'};if(!i.length||!i.some(e=>{let t=function(e){let t;if("<all_urls>"===e)return/^https?:\/\/.*/;let r=/^(\*|https?|file|ftp):\/\/([^/]*)(\/.*)$/.exec(e);if(!r)return null;let[,n="",o="",s=""]=r,a="*"===n?"https?":n.replace(/[.+?^${}()|[\]\\]/g,"\\$&");t="*"===o?"[^/]+":o.startsWith("*.")?`(?:[^/]+\\.)?${o.slice(2).replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*")}`:o.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*");let i=s.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*");return RegExp(`^${a}://${t}${i}$`)}(e);return!!t&&t.test(o)}))return{ok:!1,error:`This extension's content scripts / host permissions do not match ${o}.`};try{t=await chrome.tabs.create({url:o,active:!0})}catch(e){return{ok:!1,error:`Could not open the tab: ${String(e)}`}}let c=t.id;if(null==c)return{ok:!1,error:"No tab id."};if(await new Promise(e=>{let t=setTimeout(()=>e(),8e3),r=(n,o)=>{n===c&&"complete"===o.status&&(clearTimeout(t),chrome.tabs.onUpdated.removeListener(r),e())};chrome.tabs.onUpdated.addListener(r)}),a)try{await chrome.scripting.insertCSS({target:{tabId:c},css:a})}catch(e){r=String(e)}if(!s)return{ok:!0,injected:!1,...r?{cssError:r}:{}};try{let[e]=await chrome.scripting.executeScript({target:{tabId:c},world:"MAIN",func:e=>{try{return(0,eval)(e),{ran:!0}}catch(e){return{ran:!1,error:String(e)}}},args:[s]}),t=e?.result;if(t&&!1===t.ran)return{ok:!0,injected:!1,blocked:!0,reason:t.error,...r?{cssError:r}:{}};return{ok:!0,injected:!0,...r?{cssError:r}:{}}}catch(e){return{ok:!1,error:`Injection failed: ${String(e)}`}}}chrome.runtime.onInstalled.addListener(()=>{}),chrome.runtime.onConnectExternal.addListener(e=>{if(e.name!==_)return;for(let t of(z.add(e),J.events()))try{e.postMessage({type:F,event:t,backlog:!0})}catch{break}let t=[],r=(r,n)=>{let o=chrome[r],s=o?.[n];if(!s||"function"!=typeof s.addListener)return;let a=(...t)=>((t,r,n)=>{try{e.postMessage({type:H,namespace:t,event:r,args:n})}catch{}})(r,n,t);s.addListener(a),t.push(()=>s.removeListener(a))};r("storage","onChanged"),r("alarms","onAlarm"),r("tabs","onCreated"),r("tabs","onUpdated"),r("tabs","onRemoved"),r("tabs","onActivated"),r("windows","onCreated"),r("windows","onRemoved"),r("windows","onFocusChanged"),r("bookmarks","onCreated"),r("bookmarks","onChanged"),r("bookmarks","onMoved"),r("bookmarks","onRemoved"),r("history","onVisited"),r("history","onVisitRemoved"),r("downloads","onCreated"),r("downloads","onChanged"),r("downloads","onErased"),r("cookies","onChanged"),r("notifications","onClicked"),r("notifications","onClosed"),r("notifications","onButtonClicked"),r("contextMenus","onClicked"),e.onDisconnect.addListener(()=>{for(let r of(z.delete(e),t))r()})}),chrome.runtime.onMessageExternal.addListener((e,t,r)=>e?.type===j?(r({type:G,version:chrome.runtime.getManifest().version,ready:!0}),!1):e?.type===W&&"string"==typeof e.extensionId?(K.set(e.extensionId,new Set(Array.isArray(e.permissions)?e.permissions:[])),r({ok:!0}),!1):e?.type===L&&e.request?(Y(e.request).then(r),!0):e?.type===V&&"string"==typeof e.url&&(Z(e).then(r),!0))})();
|
|
Binary file
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.0.1",
|
|
3
|
+
"name": "Extension.dev Live Preview",
|
|
4
|
+
"author": "Extension.dev",
|
|
5
|
+
"description": "Lends real browser powers to the extension you are previewing on Extension.dev. Install while building so your popup, content scripts, and APIs run for real.",
|
|
6
|
+
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6QM4Vy/P3tIFJ+Jq7VyFEka5PICvw3LelaERWJzfVJ4avVWUfEa6vLX2+3Y21rmZ4nm2HhC203QWWRg24uCFlQWsyE9f3EW8yYR6HDvdTCPQxwq5Fv+d5m3YcNztse5IIf1XgnbZoCurI98CEHVilB4c8m6Yoel+PnPlRSjzkV5TjPyQ1NhZWfYfENAYmbxVzcpHD3eDFc9fveBvALOF9KU+21N2zubeLlnJlLjmDKv+Ud/zRLsMoc/5/zhdSE+rV/8DmA8ghyXeMQJ+WmjMgiBz/7wgg3Q1q0Bx1bzfMl18dO1pUjbW6nQC+CIxDBDthJpN4rSwuE/Xt3nEJZ5HtQIDAQAB",
|
|
7
|
+
"icons": {
|
|
8
|
+
"16": "images/icon.png",
|
|
9
|
+
"32": "images/icon.png",
|
|
10
|
+
"48": "images/icon.png",
|
|
11
|
+
"64": "images/icon.png",
|
|
12
|
+
"128": "images/icon.png"
|
|
13
|
+
},
|
|
14
|
+
"background": {
|
|
15
|
+
"service_worker": "background/service_worker.js"
|
|
16
|
+
},
|
|
17
|
+
"manifest_version": 3,
|
|
18
|
+
"action": {
|
|
19
|
+
"default_icon": {
|
|
20
|
+
"16": "images/icon.png",
|
|
21
|
+
"32": "images/icon.png",
|
|
22
|
+
"48": "images/icon.png",
|
|
23
|
+
"64": "images/icon.png",
|
|
24
|
+
"128": "images/icon.png"
|
|
25
|
+
},
|
|
26
|
+
"default_title": "Extension.dev Live Preview",
|
|
27
|
+
"default_popup": "action/index.html"
|
|
28
|
+
},
|
|
29
|
+
"externally_connectable": {
|
|
30
|
+
"matches": [
|
|
31
|
+
"https://intelligence.extension.dev/*",
|
|
32
|
+
"https://inspect.extension.dev/*",
|
|
33
|
+
"https://themes.extension.dev/*",
|
|
34
|
+
"http://localhost/*",
|
|
35
|
+
"http://127.0.0.1/*"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"permissions": [
|
|
39
|
+
"storage",
|
|
40
|
+
"notifications",
|
|
41
|
+
"contextMenus",
|
|
42
|
+
"downloads",
|
|
43
|
+
"cookies",
|
|
44
|
+
"scripting",
|
|
45
|
+
"management",
|
|
46
|
+
"activeTab",
|
|
47
|
+
"tabs",
|
|
48
|
+
"alarms",
|
|
49
|
+
"offscreen",
|
|
50
|
+
"webNavigation",
|
|
51
|
+
"bookmarks",
|
|
52
|
+
"history",
|
|
53
|
+
"sidePanel"
|
|
54
|
+
],
|
|
55
|
+
"host_permissions": [
|
|
56
|
+
"<all_urls>"
|
|
57
|
+
],
|
|
58
|
+
"sandbox": {
|
|
59
|
+
"pages": [
|
|
60
|
+
"sandbox/page-0.html"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"content_security_policy": {
|
|
64
|
+
"sandbox": "sandbox allow-scripts allow-forms allow-modals allow-popups; script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; child-src data: blob:;"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="utf-8">
|
|
3
|
+
<title>Extension preview host</title>
|
|
4
|
+
</head>
|
|
5
|
+
<body>
|
|
6
|
+
<!--
|
|
7
|
+
Sandbox execution surface for guest extension code.
|
|
8
|
+
Declared in manifest.json under chromium:sandbox.pages with a CSP
|
|
9
|
+
that allows unsafe-eval / blob: so guest code can run for real inside this
|
|
10
|
+
installed extension. This file exists so the sandbox reference resolves and
|
|
11
|
+
the build has a host page to mount; the full executor wiring (receiving a
|
|
12
|
+
guest PreviewSession and running its surfaces here) is the next increment.
|
|
13
|
+
Until then, guest surfaces still render through the creator-preview iframes.
|
|
14
|
+
-->
|
|
15
|
+
<div id="preview-host-root"></div>
|
|
16
|
+
<script>
|
|
17
|
+
// Signal the parent (the newtab runtime) that the sandbox host is mounted
|
|
18
|
+
// and ready to receive a guest preview to execute.
|
|
19
|
+
if (window.parent) {
|
|
20
|
+
window.parent.postMessage(
|
|
21
|
+
{ type: "EXTENSION_PREVIEW_HOST_READY" },
|
|
22
|
+
"*",
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
</body></html>
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@extension.dev/mcp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.
|
|
5
|
-
"description": "MCP server that lets AI agents (Claude Code, Claude Desktop, Cursor, Copilot, Codex) build, run, inspect, and publish browser extensions. 33 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, Firefox, and every Chromium- or Gecko-based browser (Brave, Opera, Vivaldi, Yandex, Waterfox, LibreWolf)
|
|
4
|
+
"version": "5.7.0",
|
|
5
|
+
"description": "MCP server that lets AI agents (Claude Code, Claude Desktop, Cursor, Copilot, Codex) build, run, inspect, and publish browser extensions. 33 tools for scaffolding, live DOM inspection, log streaming, and store-ready builds across Chrome, Edge, Firefox, Safari, and every Chromium- or Gecko-based browser (Brave, Opera, Vivaldi, Yandex, Waterfox, LibreWolf). Powered by extension.dev and Extension.js.",
|
|
6
6
|
"mcpName": "io.github.extensiondev/mcp",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"author": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"files": [
|
|
37
37
|
"dist",
|
|
38
38
|
"bin",
|
|
39
|
+
"extensions",
|
|
39
40
|
"claude",
|
|
40
41
|
".claude-plugin",
|
|
41
42
|
"server.json",
|
|
@@ -88,9 +89,9 @@
|
|
|
88
89
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
89
90
|
"browser-extension-manifest-fields": "^2.2.9",
|
|
90
91
|
"cross-spawn": "^7.0.6",
|
|
91
|
-
"extension-create": "
|
|
92
|
-
"extension-develop": "
|
|
93
|
-
"extension-install": "
|
|
92
|
+
"extension-create": "4.0.16-canary.1784889479.74e12044",
|
|
93
|
+
"extension-develop": "4.0.16-canary.1784889479.74e12044",
|
|
94
|
+
"extension-install": "4.0.16-canary.1784889479.74e12044",
|
|
94
95
|
"ws": "^8.20.0"
|
|
95
96
|
},
|
|
96
97
|
"devDependencies": {
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://extension.dev",
|
|
10
|
-
"version": "5.
|
|
10
|
+
"version": "5.7.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@extension.dev/mcp",
|
|
16
|
-
"version": "5.
|
|
16
|
+
"version": "5.7.0",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|