@mirasim/computer-use 0.1.0-rc.7 → 0.1.0-rc.9
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/platform/browser/driver-connect.d.ts +1 -0
- package/dist/platform/browser/driver-connect.js +1 -1
- package/dist/platform/browser/messages.d.ts +1 -0
- package/dist/platform/browser/messages.js +2 -2
- package/dist/platform/browser/state.js +2 -2
- package/dist/platform/browser/tabs.js +2 -2
- package/extension/manifest.json +1 -2
- package/extension/relay.js +17 -1
- package/package.json +5 -5
- package/scripts/extension.mjs +3 -1
- package/extension/icons/icon-48.png +0 -0
|
@@ -6,6 +6,7 @@ export declare abstract class BrowserDriverConnect extends BrowserDriverTabs {
|
|
|
6
6
|
private attachRefusal;
|
|
7
7
|
protected noteBridgeSession(session: string | null): boolean;
|
|
8
8
|
private ensureInner;
|
|
9
|
+
private findReminted;
|
|
9
10
|
private publishClaim;
|
|
10
11
|
private wireContext;
|
|
11
12
|
private installKeepInTab;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{connectBrowserCdp as o}from"./cdp-connection.js";import{acquireManagedBrowser as
|
|
1
|
+
import{connectBrowserCdp as o}from"./cdp-connection.js";import{acquireManagedBrowser as p,openBlankTab as b,publishTabIdentity as g}from"./acquire.js";import{acquireBridge as m,bridgeJustStarted as x,extensionHeldElsewhere as E,waitForExtension as I}from"./bridge/acquire-bridge.js";import{claimIsolatedTab as h,claimNamedTab as T,claimSessionTab as y,newBackgroundPage as k,pageTargetInfo as v}from"./tabs.js";import{bridgeBrowserSession as c}from"./bridge-tabs.js";import{BROWSER_SWITCHED_NOTE as S,isBridgeExtensionGone as C,msgBridgeExtensionElsewhere as D,msgBridgeExtensionGone as l,msgBridgeJustStarted as B,msgEndpointUnavailable as _,REOPENED_NOTE as d,SURFACE_REIDENTIFIED_NOTE as N,TAB_DETACHED_NOTE as R,TAB_REOPENED_AFTER_SELF_CLOSE_NOTE as O,TAB_REOPENED_NOTE as P,tabRefusedNote as U}from"./messages.js";import{SurfaceUnreadableError as f}from"../types.js";import{KEEP_IN_TAB_JS as w}from"./keep-in-tab.js";import{ActionJournal as A}from"./journal.js";import{captureEvidenceShot as $}from"../chromium/shot.js";import{ensureRefSelector as F}from"../chromium/page-refs.js";import{REIDENTIFIED_WHY as W}from"./driver-core.js";import{BrowserDriverTabs as G}from"./driver-tabs.js";class et extends G{endpoints(){return this.explicit?[this.cdpUrl]:[...new Set([this.cdpUrl,this.fallbackCdpUrl])]}async ensure(){try{await this.ensureInner()}catch(e){throw C(e)?new f(await this.extensionGoneMessage()):e}}async extensionGoneMessage(){if(!this.cfg.extension||this.cfg.cdpUrl)return l();const e=await E(this.cfg.home);return e?D(e):x(this.cfg.home,9e4)?B():l()}async attachRefusal(e){if(!e||e==="about:blank"||!this.browser)return null;for(let i=0;i<2;i++){i&&await new Promise(t=>setTimeout(t,500));try{const t=await this.browser.newBrowserCDPSession();try{const s=(await t.send("Mirasim.attachRefusals")).refusals?.find(r=>r.url===e);if(s)return s.reason}finally{await t.detach().catch(()=>{})}}catch{return null}}return null}noteBridgeSession(e){if(!e)return!1;const i=this.bridgeSession;return this.bridgeSession=e,!i||i===e?!1:(this.resetObservationState(),this.active=void 0,this.appendNote(S),!0)}async ensureInner(){if(this.browser?.isConnected()){if(!this.active)return;const t=await this.ownPages();if(!t.length&&this.active.isClosed()){if(this.closedItsLastTab||this.noteBridgeSession(await c(this.browser)))return;this.resetObservationState();const a=this.active?.url()??"",s=this.attached||this.tab?null:await this.findReminted();this.active=s??(this.attached?await this.openOwnWindow():this.tab?await this.reclaimTab():await k(this.context)),this.claim(this.active);const r=!this.active.url()||this.active.url()==="about:blank";this.pendingNote=s||!r?N:this.wokeAfterSelfClose?O:this.managedProfileDir&&!this.tab?d:this.explicit&&!this.managedProfileDir&&!this.attached?(n=>n?U(n):R)(await this.attachRefusal(a)):P,this.wokeAfterSelfClose=!1,await this.publishClaim()}else if(this.active.isClosed()){const a=t.filter(n=>n.url()!=="about:blank"),s=a[a.length-1]??t[t.length-1],r=await this.recognise(s).catch(()=>null);this.makeActive(s,r==="same"?W:"the tab it was on was closed")}return}if(this.browser&&(this.browser=null,this.managedProfileDir&&(this.pendingNote=d),this.resetObservationState(),this.active=void 0),this.cfg.extension&&!this.cfg.cdpUrl){const t=await m(this.cfg.home);if(this.cdpUrl=t.cdpUrl,!await I(this.cdpUrl,t.fresh?0:15e3,this.cfg.home))throw new f(await this.extensionGoneMessage())}const e=this.endpoints();let i;for(const t of e){try{this.browser=await o(t)}catch(a){if(i=a,await b(t))try{this.browser=await o(t)}catch(s){i=s}}if(this.browser){await F(this.browser),this.cdpUrl=t;break}}if(!this.browser&&!this.explicit){const t=await p(this.cfg.profileDir,this.cfg);this.browser=t.browser,this.cdpUrl=t.cdpUrl,this.managedProfileDir=t.profileDir,this.journal=new A(t.profileDir,()=>$(()=>this.page()),()=>{try{return this.page().url()}catch{return""}},this.cfg),this.attached=this.cfg.attached}if(!this.browser){const t=i instanceof Error?i.message:String(i??"unknown error");throw new Error(_(this.cdpUrl,t))}if(this.context=this.browser.contexts()[0]??await this.browser.newContext(),this.noteBridgeSession(await c(this.browser)),this.attached)this.active=await this.openOwnWindow();else if(this.tab?.isolated){const t=await h(this.browser,this.tab.key);this.active=t.page,this.ctxId=t.ctxId,this.pageCtxIds.set(t.page,t.ctxId),this.context=this.active.context()}else this.active=await y(this.context,this.tab?.key??null,this.cfg)??void 0;this.active&&this.claim(this.active),this.wireContext(),await this.installKeepInTab(this.active),await this.publishClaim()}async findReminted(){let e=null;for(const i of this.context.pages()){if(i.isClosed())continue;const t=await this.recognise(i).catch(()=>null);if(t==="same")return i;t==="ours"&&(e??=i)}return e}async publishClaim(){if(!(!this.tab||!this.managedProfileDir||!this.active))try{const{targetId:e,ctxId:i}=await v(this.active);g(this.managedProfileDir,{key:this.tab.key,isolated:this.tab.isolated,ctxId:i,targetId:e})}catch{}}wireContext(){this.context.on("page",e=>{this.windowAppeared=!0,this.adoptWhenReal(e)}),this.context.on("dialog",e=>{(async()=>{const i=e.page();if(this.attached&&(!i||!await this.ownsPage(i).catch(()=>!1)))return;const t=e.type(),a=this.armedDialog;this.armedDialog=null;const s=a?a.accept:t==="alert"||t==="beforeunload";let r=!0;try{await(s?e.accept(a?.text):e.dismiss())}catch{r=!1}if(t==="beforeunload")return;const n=`a JS dialog appeared: \xAB${e.message().slice(0,120)}\xBB`,u=r?a?`${n} \u2014 ${s?"accepted":"dismissed"} as you asked`+(a.text!==void 0?` with text \xAB${a.text.slice(0,60)}\xBB`:""):`${n} \u2014 it was auto-${s?"accepted":"dismissed"}; if the flow needs a confirm, it did not happen`:`${n} \u2014 it was already answered before your ${s?"accept":"dismiss"} could land (another client shares this browser), so the page took an answer you did not choose. Check what the page did before acting on it`;this.appendNote(u)})()})}async installKeepInTab(e){this.keepInTabInstalled||(this.attached||(this.keepInTabInstalled=!0,await this.context.addInitScript(w).catch(()=>{this.keepInTabInstalled=!1})),await e?.evaluate(w).catch(()=>{}))}async reclaimTab(){const e=this.tab;if(!e.isolated)return T(this.context,e.key);const{page:i,ctxId:t}=await h(this.browser,e.key);return this.ctxId=t,this.pageCtxIds.set(i,t),i.context()!==this.context&&(this.context=i.context(),this.keepInTabInstalled=!1,this.wireContext(),await this.installKeepInTab(i)),i}}export{et as BrowserDriverConnect};
|
|
@@ -14,6 +14,7 @@ export declare function msgBridgeExtensionElsewhere(owner: string): string;
|
|
|
14
14
|
export declare function msgBridgeJustStarted(): string;
|
|
15
15
|
export declare function isBridgeExtensionGone(e: unknown): boolean;
|
|
16
16
|
export declare function msgEndpointUnavailable(cdpUrl: string, detail: string): string;
|
|
17
|
+
export declare function msgNewTabNotFound(targetId: string, closed: boolean): string;
|
|
17
18
|
export declare const REOPENED_NOTE: string;
|
|
18
19
|
export declare const TAB_REOPENED_NOTE: string;
|
|
19
20
|
export declare const TAB_DETACHED_NOTE: string;
|
|
@@ -4,5 +4,5 @@ Fix one of:
|
|
|
4
4
|
- point COMPUTER_USE_CHROME at a Chrome/Chromium binary
|
|
5
5
|
- point COMPUTER_USE_CDP_URL at an already-running browser's CDP endpoint`}function s(e,t){return`mirasim-cu: cannot take over this Chrome \u2014 ${t}.
|
|
6
6
|
This profile belongs to the user (${e}), so it will not be force-closed.
|
|
7
|
-
Quit Chrome (\u2318Q) and retry.`}const r=()=>"its debugging port answers, but no usable page can be opened",i=()=>"its debugging port answers, but the handshake timed out (the browser may be frozen)",a=()=>"it is already running, but without a debugging port",h=()=>"mirasim-cu: could not open the agent's own window in the user's Chrome.";function d(e,t){return`mirasim-cu: cannot run the Chrome binary at ${e} (${t}). Point COMPUTER_USE_CHROME at a working Chrome/Chromium.`}function c(e){return`mirasim-cu: Chrome could not be started (${e}).`}const u=()=>"mirasim-cu: Chrome exited immediately instead of publishing a debugging port \u2014 another browser may already own this profile directory.",l=()=>"mirasim-cu: Chrome never published a debugging port within 15s.",m=()=>"mirasim-cu: Chrome started but its debugger refused to attach.",o="https://chromewebstore.google.com/detail/mirasim/khflooofmndpmafhofjdcpjfgnlpikhg";function
|
|
8
|
-
(${t})`}const
|
|
7
|
+
Quit Chrome (\u2318Q) and retry.`}const r=()=>"its debugging port answers, but no usable page can be opened",i=()=>"its debugging port answers, but the handshake timed out (the browser may be frozen)",a=()=>"it is already running, but without a debugging port",h=()=>"mirasim-cu: could not open the agent's own window in the user's Chrome.";function d(e,t){return`mirasim-cu: cannot run the Chrome binary at ${e} (${t}). Point COMPUTER_USE_CHROME at a working Chrome/Chromium.`}function c(e){return`mirasim-cu: Chrome could not be started (${e}).`}const u=()=>"mirasim-cu: Chrome exited immediately instead of publishing a debugging port \u2014 another browser may already own this profile directory.",l=()=>"mirasim-cu: Chrome never published a debugging port within 15s.",m=()=>"mirasim-cu: Chrome started but its debugger refused to attach.",o="https://chromewebstore.google.com/detail/mirasim/khflooofmndpmafhofjdcpjfgnlpikhg";function b(){return`The mirasim bridge extension is not connected, so this browser cannot be reached. Nothing you can do from here fixes it and retrying will not help: ask the user to install or enable the Mirasim extension from the Chrome Web Store (${o}) and remove any older copy loaded unpacked at chrome://extensions, which no longer connects; then continue.`}function g(e){return`The Mirasim extension is connected to another program's browser bridge (${e}), not to this one \u2014 it serves one bridge at a time and that one comes first. Retrying will not help: ask the user to turn browser control off in that program (${e}), then continue.`}function p(){return`The browser bridge started moments ago and the Mirasim extension has not connected yet. An installed extension checks again within a minute; wait about a minute and retry once. If it still does not connect, ask the user to install or enable the Mirasim extension from the Chrome Web Store (${o}).`}function f(e){return/Bridge extension not connected|bridge extension did not connect/i.test(e instanceof Error?e.message:String(e))}function w(e,t){return`mirasim-cu: explicit CDP endpoint ${e} is unavailable.
|
|
8
|
+
(${t})`}function x(e,t){return`mirasim-cu: opened a new tab (${e}) but it did not appear on this connection in time, `+(t?"so it was closed again":"and it could not be closed again (a blank tab may be left)")+" and no tab was taken in its place \u2014 the user's tabs are untouched; try again"}const E="the browser had been closed (most likely by the user), so a fresh one was opened in the background and the page is blank \u2014 re-observe, and navigate again before acting on refs",y="this surface's tab had been closed (most likely by the user), so a fresh blank tab was claimed \u2014 re-observe, and navigate again before acting on refs",C="this surface's tab stopped answering \u2014 it was closed, or the browser took its debugger away, in which case it is still open in the user's window but out of reach from here. A fresh blank tab was claimed \u2014 re-observe, and navigate again before acting on refs. If the same page drops again right after it loads, it cannot be driven through this browser: a password manager's autofill (measured: iCloud Passwords, on any page with a password field) puts its own extension frame into the page, and Chrome then refuses this extension's debugger there. Do not reopen it; tell the user \u2014 turning that extension off for the site, or the managed browser (its own profile), drives it";function _(e){const t=`this surface's tab is out of reach: it is still open in the user's window, but Chrome refused this extension's debugger on it ("${e}"). A fresh blank tab was claimed. `;return/chrome-extension:\/\/ URL of different extension/.test(e)?t+"Another extension has put its own frame into that page \u2014 a password manager's autofill does this on any page with a password field (measured: iCloud Passwords) \u2014 and while it is there no retry from here gets in. Do not reopen the page; tell the user that turning that extension off for this site, or the managed browser (its own profile), lets it be driven":t+"Reopening it will most likely be refused the same way \u2014 tell the user rather than retrying"}const k="this session had closed its last tab, so opening one started a fresh blank tab \u2014 nothing went wrong and nobody took the browser back; navigate before acting on refs",T="this session has no tab open \u2014 it closed its last one and nothing was reopened, so the browser is left as the user had it. open_url or new_tab starts a fresh tab when you need one",N="this session holds no tab yet \u2014 none is opened until asked. Bind one of the listed tabs to act in it, or new_tab opens one of the session's own",R="the browser behind the extension changed \u2014 a different browser, or this one after a restart, is connected now, and the tabs listed are ITS tabs. Nothing was done in it and this session holds no tab there; handles and refs from before belonged to the previous browser and are gone. If this is not the browser the task is about, stop and tell the user; otherwise bind one of the listed tabs, or new_tab opens one of the session's own",O="the connection to this browser dropped and came back, so this surface was re-identified \u2014 the page below is the one it is on now, and refs from before are dead; re-observe before acting";function v(e){return`${e} is the user's own tab, not this session's. Ask them in the conversation whether to close it \u2014 say which page \u2014 and call close_tab again with confirmed: true if they agree. Reading it and acting in it needs no permission; only closing does`}function A(e){const t=[];return e.scope==="group"&&t.push("the user lets the agent see only the tabs it opened and the ones handed to it (the Mirasim tab group), so a page open in another tab is not listed \u2014 ask them to click the Mirasim toolbar icon on that tab to hand it over, then list surfaces again"),e.siteRules&&t.push("tabs on sites the user blocked in Mirasim are not listed either, and cannot be opened"),t.length?`mirasim-cu: ${t.join("; ")}.`:null}const S="The user paused the agent from the Mirasim extension: every browser command is refused until they resume it. Tell them, and wait for them to resume before trying again";export{R as BROWSER_SWITCHED_NOTE,T as NO_TAB_OPEN_NOTE,N as NO_TAB_YET_NOTE,S as PAUSED_BY_USER,E as REOPENED_NOTE,O as SURFACE_REIDENTIFIED_NOTE,C as TAB_DETACHED_NOTE,k as TAB_REOPENED_AFTER_SELF_CLOSE_NOTE,y as TAB_REOPENED_NOTE,A as accessMissNote,f as isBridgeExtensionGone,s as msgAttachedDeadEnd,i as msgAttachedWhyHandshake,r as msgAttachedWhyNoPage,a as msgAttachedWhyNoPort,g as msgBridgeExtensionElsewhere,b as msgBridgeExtensionGone,p as msgBridgeJustStarted,d as msgChromeBinaryUnrunnable,u as msgChromeExitedImmediately,n as msgChromeMissing,l as msgChromeNoPort,m as msgChromeRefusedAttach,c as msgChromeStartFailed,w as msgEndpointUnavailable,x as msgNewTabNotFound,h as msgOwnWindowFailed,_ as tabRefusedNote,v as userTabCloseRefusal};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{newBackgroundPage as m}from"./tabs.js";function p(
|
|
2
|
-
`)[0]??t}export{
|
|
1
|
+
import{newBackgroundPage as m}from"./tabs.js";function p(e){try{const t=new URL(e);return t.protocol==="http:"||t.protocol==="https:"?t.origin:null}catch{return null}}function f(e){const t=e.contexts()[0];if(!t)throw new Error("mirasim-cu: the attached browser exposes no context to read state from.");return t}async function d(e){const t=f(e),i=await t.cookies(),o=new Map;for(const r of t.pages()){if(r.isClosed())continue;const a=p(r.url());a&&!o.has(a)&&o.set(a,r)}const n=[];for(const[r,a]of o)try{const l=await a.evaluate(()=>{const u=[];for(let c=0;c<localStorage.length;c++){const s=localStorage.key(c);s!==null&&u.push({name:s,value:localStorage.getItem(s)??""})}return u});l.length&&n.push({origin:r,localStorage:l})}catch{}return{cookies:i,origins:n}}async function y(e,t){const i=f(e);if(t.cookies?.length)try{await i.addCookies(t.cookies)}catch(o){throw new Error(`mirasim-cu: cookie import failed: ${g(o)}`)}for(const o of t.origins??[])!o?.origin||!o.localStorage?.length||await h(i,o.origin,o.localStorage)}async function h(e,t,i){const o=await m(e);try{await o.route("**/*",n=>n.fulfill({status:200,contentType:"text/html",body:"<!doctype html><title>mirasim-cu state import</title>"})),await o.goto(`${t}/`,{waitUntil:"domcontentloaded",timeout:1e4}),await o.evaluate(n=>{for(const{name:r,value:a}of n)localStorage.setItem(r,a)},i)}catch(n){throw new Error(`mirasim-cu: localStorage import failed for ${t}: ${g(n)}`)}finally{await o.unroute("**/*").catch(()=>{}),await o.close().catch(()=>{})}}function g(e){const t=e instanceof Error?e.message:String(e);return t.split(`
|
|
2
|
+
`)[0]??t}export{d as exportStorageState,y as importStorageState};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{browserConfigFromEnv as p}from"./config.js";const
|
|
2
|
-
`)[0]:String(o)}), so the tab claim "ctx:${e}" is unavailable here \u2014 use a named tab ("name:${e}") for a tab of your own in the same browser, which is the same isolation everywhere except cookies and storage`)}finally{await r.detach().catch(()=>{})}for(let o=0;o<40;o++){for(const c of a())if(!n.has(c)&&(await
|
|
1
|
+
import{browserConfigFromEnv as p}from"./config.js";import{msgNewTabNotFound as b}from"./messages.js";const w=t=>new Promise(e=>setTimeout(e,t)),m=5e3;async function u(t){const e=(await h(t))[0];if(!e)return await t.newPage();const a=new Set(t.pages()),n=await t.newCDPSession(e);let r;try{({targetId:r}=await n.send("Target.createTarget",{url:"about:blank",background:!0,newWindow:!0}))}finally{await n.detach().catch(()=>{})}const s=await T(t,r,a);if(s)return s;const i=await t.newCDPSession(e).catch(()=>null),o=(await i?.send("Target.closeTarget",{targetId:r}).catch(()=>null))?.success===!0;throw await i?.detach().catch(()=>{}),new Error(b(r,o))}async function T(t,e,a){const n=Date.now()+m;for(;Date.now()<n;){for(const r of t.pages())if(!(a.has(r)||r.isClosed())&&(await d(r).catch(()=>null))?.targetId===e)return r;await w(25)}return null}const l="__mirasim_computer_use_tab:";async function f(t,e){return await y(t)===l+e}async function y(t,e=1e3){let a;const n=new Promise(r=>{a=setTimeout(()=>r(""),e),a.unref?.()});try{return String(await Promise.race([t.evaluate("window.name"),n]).catch(()=>""))}finally{clearTimeout(a)}}async function C(t,e){for(const n of t.pages())if(!n.isClosed()&&await f(n,e))return n;const a=await u(t);return await a.evaluate(`window.name = ${JSON.stringify(l+e)}`).catch(()=>{}),a}async function x(t,e){const a=()=>t.contexts().flatMap(o=>o.pages()).filter(o=>!o.isClosed());for(const o of a())if(await f(o,e))return{page:o,ctxId:await g(o)};const n=new Set(a()),r=await t.newBrowserCDPSession();let s,i;try{({browserContextId:s}=await r.send("Target.createBrowserContext")),{targetId:i}=await r.send("Target.createTarget",{url:"about:blank",background:!0,browserContextId:s})}catch(o){throw new Error(`this endpoint cannot create isolated browser contexts (${o instanceof Error?o.message.split(`
|
|
2
|
+
`)[0]:String(o)}), so the tab claim "ctx:${e}" is unavailable here \u2014 use a named tab ("name:${e}") for a tab of your own in the same browser, which is the same isolation everywhere except cookies and storage`)}finally{await r.detach().catch(()=>{})}for(let o=0;o<40;o++){for(const c of a())if(!n.has(c)&&(await d(c).catch(()=>null))?.targetId===i)return await c.evaluate(`window.name = ${JSON.stringify(l+e)}`).catch(()=>{}),{page:c,ctxId:s};await w(25)}throw new Error("isolated-context tab was created but never surfaced over CDP")}async function d(t){const e=await t.context().newCDPSession(t);try{const{targetInfo:a}=await e.send("Target.getTargetInfo");return{targetId:a.targetId,ctxId:a.browserContextId??""}}finally{await e.detach().catch(()=>{})}}async function g(t){return(await d(t)).ctxId}async function P(t,e,a=p()){if(e)return C(t,e);const n=await h(t);return a.cdpUrl?t.browser()?.version()==="mirasim-bridge"?null:await u(t):(a.tabReuse?n.filter(s=>s.url()!=="about:blank").at(-1):void 0)??n.find(s=>s.url()==="about:blank")??await u(t)}async function h(t){const e=t.pages().filter(i=>!i.isClosed()),a=t.browser();if(!a||e.length===0)return e;let n;const r=await a.newBrowserCDPSession();try{n=new Set((await r.send("Target.getBrowserContexts")).browserContextIds)}catch{return e}finally{await r.detach().catch(()=>{})}if(n.size===0)return e;const s=[];for(const i of e)n.has(await g(i).catch(()=>""))||s.push(i);return s}async function _(t){try{const e=t.contexts()[0]??await t.newContext();if(e.pages().some(n=>!n.isClosed()))return;const a=await t.newBrowserCDPSession();try{await a.send("Target.createTarget",{url:"about:blank",background:!0})}finally{await a.detach().catch(()=>{})}for(let n=0;n<40;n++){if(e.pages().some(r=>!r.isClosed()))return;await w(25)}}catch{}}export{x as claimIsolatedTab,C as claimNamedTab,P as claimSessionTab,_ as ensureBackgroundTab,u as newBackgroundPage,g as pageContextId,d as pageTargetInfo};
|
package/extension/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Mirasim",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.4",
|
|
5
5
|
"default_locale": "en",
|
|
6
6
|
"description": "Connect your browser to Mirasim or the open-source Mirasim Computer Use so your agent can complete web tasks you authorize.",
|
|
7
7
|
"permissions": [
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"icons": {
|
|
21
21
|
"16": "icons/icon-16.png",
|
|
22
22
|
"32": "icons/icon-32.png",
|
|
23
|
-
"48": "icons/icon-48.png",
|
|
24
23
|
"128": "icons/icon-128.png"
|
|
25
24
|
},
|
|
26
25
|
"action": {
|
package/extension/relay.js
CHANGED
|
@@ -520,7 +520,7 @@ export class RelayConnection {
|
|
|
520
520
|
return await this._runCommand(method, args);
|
|
521
521
|
}
|
|
522
522
|
case "chrome.downloads.search": {
|
|
523
|
-
const items =
|
|
523
|
+
const items = await searchDownloads(args[0]);
|
|
524
524
|
return items.filter((item) => !policy.siteVerdict(item.finalUrl || item.url) && !policy.siteVerdict(item.referrer));
|
|
525
525
|
}
|
|
526
526
|
default:
|
|
@@ -583,6 +583,7 @@ export class RelayConnection {
|
|
|
583
583
|
if (target?.tabId !== undefined) this._notifyAttached(target.tabId);
|
|
584
584
|
return {};
|
|
585
585
|
}
|
|
586
|
+
if (method === "chrome.downloads.search") return await searchDownloads(args[0]);
|
|
586
587
|
const result = await invokeChromeMethod(method, args);
|
|
587
588
|
// Said before the reply leaves: the bridge attaches the tab only once it has the reply.
|
|
588
589
|
if (method === "chrome.tabs.create" && result?.id !== undefined) {
|
|
@@ -597,6 +598,21 @@ export class RelayConnection {
|
|
|
597
598
|
}
|
|
598
599
|
}
|
|
599
600
|
|
|
601
|
+
/**
|
|
602
|
+
* The one use of the `downloads` permission: the agent's download verb asks which files the
|
|
603
|
+
* browser saved since it started waiting, and where (bridge `Mirasim.downloads`). Called by name
|
|
604
|
+
* rather than through the reflective path below, so the permission's use is plain to read —
|
|
605
|
+
* the Web Store rejected 0.1.3 as "requests but does not use downloads" when it was not.
|
|
606
|
+
* @returns {Promise<chrome.downloads.DownloadItem[]>}
|
|
607
|
+
*/
|
|
608
|
+
async function searchDownloads(query) {
|
|
609
|
+
const { startedAfter, startedBefore } = query && typeof query === "object" ? query : {};
|
|
610
|
+
return await chrome.downloads.search({
|
|
611
|
+
...(typeof startedAfter === "string" ? { startedAfter } : {}),
|
|
612
|
+
...(typeof startedBefore === "string" ? { startedBefore } : {}),
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
600
616
|
function hostFromUrl(url) {
|
|
601
617
|
try {
|
|
602
618
|
return url ? new URL(url).hostname : "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mirasim/computer-use",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.9",
|
|
4
4
|
"description": "Computer use for any agent: an MCP server that drives browsers, macOS apps, Windows apps and Electron apps through their accessibility trees.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
"access": "public"
|
|
71
71
|
},
|
|
72
72
|
"optionalDependencies": {
|
|
73
|
-
"@mirasim/computer-use-darwin-arm64": "0.1.0-rc.
|
|
74
|
-
"@mirasim/computer-use-darwin-x64": "0.1.0-rc.
|
|
75
|
-
"@mirasim/computer-use-win32-x64": "0.1.0-rc.
|
|
76
|
-
"@mirasim/computer-use-win32-arm64": "0.1.0-rc.
|
|
73
|
+
"@mirasim/computer-use-darwin-arm64": "0.1.0-rc.9",
|
|
74
|
+
"@mirasim/computer-use-darwin-x64": "0.1.0-rc.9",
|
|
75
|
+
"@mirasim/computer-use-win32-x64": "0.1.0-rc.9",
|
|
76
|
+
"@mirasim/computer-use-win32-arm64": "0.1.0-rc.9"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/scripts/extension.mjs
CHANGED
|
@@ -17,7 +17,9 @@ export const extensionSource = fileURLToPath(new URL('../extension/', import.met
|
|
|
17
17
|
const files = ['background.js', 'group.js', 'relay.js', 'runtime-info.js', 'transport.js', 'access.js', 'toolbar.js',
|
|
18
18
|
'connect.html', 'connect.js', 'panel.html', 'panel.css', 'panel.js', 'ask.html', 'ask.js',
|
|
19
19
|
'_locales/en/messages.json', '_locales/zh_CN/messages.json',
|
|
20
|
-
|
|
20
|
+
// No 48: chrome://extensions shows the "48" icon at 36 CSS px without resampling, so a real 48 px
|
|
21
|
+
// file is upscaled and blurry on a 2x screen; with no "48" entry Chrome takes the 128 instead.
|
|
22
|
+
...[16, 32, 128].map((size) => `icons/icon-${size}.png`)];
|
|
21
23
|
|
|
22
24
|
export function extensionId(key) {
|
|
23
25
|
return [...createHash('sha256').update(Buffer.from(key, 'base64')).digest().subarray(0, 16)]
|
|
Binary file
|