oh-my-opencode 5.1.57 → 5.1.58

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.
Files changed (65) hide show
  1. package/dist/cli/index.js +23 -19
  2. package/dist/cli-node/index.js +23 -19
  3. package/dist/index.js +204 -185
  4. package/dist/skills/browser/runtime/omowright/index.js +20 -19
  5. package/dist/skills/browser/runtime/omowright/manifest.json +3 -3
  6. package/dist/skills/ultimate-browsing/engine/executor.py +56 -5
  7. package/dist/skills/ultimate-browsing/engine/tests/test_executor_timeout.py +144 -0
  8. package/dist/tui.js +20 -16
  9. package/package.json +15 -15
  10. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  11. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +5 -1
  12. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  13. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  14. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  15. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  16. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  17. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  18. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  19. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  21. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  22. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  24. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  26. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  28. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  30. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  31. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  32. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  33. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  34. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  35. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  36. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  37. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  38. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  39. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  40. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  41. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  42. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  56. package/packages/omo-codex/plugin/package-lock.json +12 -12
  57. package/packages/omo-codex/plugin/package.json +1 -1
  58. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/index.js +20 -19
  59. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +3 -3
  60. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/executor.py +56 -5
  61. package/packages/omo-codex/scripts/install-dist/install-local.mjs +7 -3
  62. package/packages/shared-skills/skills/browser/runtime/omowright/index.js +20 -19
  63. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +3 -3
  64. package/packages/shared-skills/skills/ultimate-browsing/engine/executor.py +56 -5
  65. package/packages/shared-skills/skills/ultimate-browsing/engine/tests/test_executor_timeout.py +144 -0
@@ -1465,14 +1465,15 @@ ${qn.join(`
1465
1465
  }`,arguments:[{value:ai}],executionContextId:ii,returnByValue:true,awaitPromise:false,userGesture:false},jn.getFrame(qn)?.sessionId),Yn=Vn.value;if(Yn){if(Yn.error){Jn&&(ri=Yn.error);return}ni.set(qn,Yn);return}}catch(xn){Qn=xn,isContextDestroyedError(xn)&&jn.invalidateContext(qn)}Jn&&Zn-1}Jn&&(ri=Qn)}));let ii=ni.get(ei);if(!ii){let xn=ri;throw xn instanceof Error?xn:typeof xn==`string`&&xn.length>0?Error(xn):Error(ei===qn?`Main frame snapshot failed`:`Ref subtree snapshot failed`)}let ai=new Map,oi=new Map;await Promise.all(ti.map(async({frameId:Vn})=>{let qn=ni.get(Vn),Jn=jn.getFrame(Vn);if(!qn||!Jn?.isolatedContextId)return;let Yn=Object.entries(qn.refs).filter(([,xn])=>xn.tagName.toLowerCase()===`iframe`).map(([xn])=>xn);if(Yn.length===0)return;let Zn=await Promise.all(Yn.map(async(qn)=>{let{result:Yn}=await xn.send(`Runtime.callFunctionOn`,{functionDeclaration:`function(ref) {
1466
1466
  return globalThis.__omowright.elementRegistry.get(ref);
1467
1467
  }`,arguments:[{value:qn}],executionContextId:Jn.isolatedContextId,returnByValue:false},Jn.sessionId).catch((xn)=>(isContextDestroyedError(xn)&&jn.invalidateContext(Vn),{result:{}})),Zn=typeof Yn==`object`&&Yn&&`objectId`in Yn?Yn.objectId:undefined;if(!Zn)return null;try{let{node:Vn}=await xn.send(`DOM.describeNode`,{objectId:Zn},Jn.sessionId),Yn=Vn.frameId?jn.getFrame(Vn.frameId)?.url:null,Qn=getCdpNodeAttribute(Vn.attributes,`src`),ei=getKnownIframeOriginLabel(Yn??``)??getKnownIframeOriginLabel(Qn??``);return{childFrameId:Vn.frameId,iframeRef:qn,originLabel:ei}}catch{return null}finally{await xn.send(`Runtime.releaseObject`,{objectId:Zn},Jn.sessionId).catch(()=>{})}})),Qn=new Map,ei=new Map;for(let xn of Zn)xn&&(xn.childFrameId&&Qn.set(xn.childFrameId,xn.iframeRef),xn.originLabel&&ei.set(xn.iframeRef,xn.originLabel));ai.set(Vn,Qn),ei.size>0&&oi.set(Vn,ei)}));let si=[...ti].sort((xn,jn)=>jn.depth-xn.depth),ci={};for(let xn of ni.values())Object.assign(ci,xn.refs);for(let[xn,jn]of oi){let Vn=ni.get(xn);if(Vn)for(let[xn,qn]of jn)Vn.tree=tagIframeLine(Vn.tree,xn,qn)}for(let xn of si){if(xn.frameId===qn||!xn.parentFrameId)continue;let Vn=ni.get(xn.frameId),Jn=ni.get(xn.parentFrameId);if(!Vn||!Jn||!Vn.tree)continue;let Yn=ai.get(xn.parentFrameId)?.get(xn.frameId),Zn=getKnownIframeOriginLabel(jn.getFrame(xn.frameId)?.url??``);Jn.tree=Yn?inlineChildTree(Jn.tree,Yn,Vn.tree):appendOrphanChildTree(Jn.tree,Vn.tree,Zn)}return{tree:ii.tree,refs:ci}}var KNOWN_IFRAME_ORIGINS;var SNAPSHOT_RETRY_DELAY_MS;var SNAPSHOT_MAX_RETRIES;var init_snapshot=__esmMin(()=>{init_extension_ids(),init_utils$13(),init_client$6(),init_frame_manager(),KNOWN_IFRAME_ORIGINS=[{urlPrefix:`chrome-extension://aeblfdkhhhdcdjpifhhbdiojplfjncoa/`,label:`1Password`},{urlPrefix:`chrome-extension://nngceckbapebfimnlniiiahkandclblb/`,label:`Bitwarden`},{urlPrefix:`chrome-extension://fdjamakpfbbddfjaooikfcpapjohcfmg/`,label:`Dashlane`},{urlPrefix:`chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/`,label:`LastPass`},{urlPrefix:`${PASSWORD_MANAGER_EXTENSION_ORIGIN}/`,label:`OmO Password Manager`}],SNAPSHOT_RETRY_DELAY_MS=300,SNAPSHOT_MAX_RETRIES=3});var formatRange;var init_diff=__esmMin(()=>{formatRange=(xn,jn)=>jn===1?String(xn):`${xn},${jn}`});var init_session_tab_queries=__esmMin(()=>{init_drizzle_orm(),init_event_bus(),init_extension_bridge(),init_schema(),init_state_db(),init_nanoid(),init_utils$13(),init_store$1()});var ANNOTATION_OVERLAY_ID;var init_annotate_screenshot=__esmMin(()=>{ANNOTATION_OVERLAY_ID=`__omowright_annotations__`});var AI_TAB_STABLE_VIEWPORT;var MIN_STABLE_AI_TAB_VIEWPORT;var init_browser=__esmMin(()=>{init_context(),init_cdp(),init_directory(),init_event_bus(),init_extension_bridge(),init_diff(),init_utils$13(),init_session_tab_queries(),init_store$1(),init_types$8(),init_utils$4(),init_annotate_screenshot(),init_frame_manager(),init_page(),init_snapshot(),AI_TAB_STABLE_VIEWPORT={width:1440,height:900},MIN_STABLE_AI_TAB_VIEWPORT={width:960,height:540}});function defaultVideoSize(xn){let jn=Math.min(1,DEFAULT_VIDEO_MAX_EDGE/xn.width,DEFAULT_VIDEO_MAX_EDGE/xn.height),Vn=Math.max(2,Math.round(xn.width*jn)),qn=Math.max(2,Math.round(xn.height*jn));return{height:qn%2==0?qn:qn+1,width:Vn%2==0?Vn:Vn+1}}function inferVideoFormat(xn){return xn.format?xn.format:xn.path?.toLowerCase().endsWith(`.mp4`)?`mp4`:DEFAULT_VIDEO_FORMAT}async function ensureDirForFile(xn){await fs$16.mkdir(path.dirname(xn),{recursive:true})}async function readProcessStderr(xn){return await readProcessStream(xn.stderr).catch(()=>``)}var DEFAULT_VIDEO_FORMAT;var DEFAULT_VIDEO_FRAMERATE;var DEFAULT_VIDEO_JPEG_QUALITY;var DEFAULT_VIDEO_MAX_EDGE;var FfmpegVideoRecorder;var OmOVideo;var init_video=__esmMin(()=>{init_context(),init_process(),init_browser(),DEFAULT_VIDEO_FORMAT=`webm`,DEFAULT_VIDEO_FRAMERATE=25,DEFAULT_VIDEO_JPEG_QUALITY=90,DEFAULT_VIDEO_MAX_EDGE=800,FfmpegVideoRecorder=class{#e;#t;#n=0;#r=false;#i=null;#a=Promise.resolve();#o=0;#s=[];constructor(xn,jn,Vn,qn){this.#e=Vn;let Jn=qn===`mp4`?[`-c:v`,`libx264`,`-movflags`,`+faststart`]:[`-c:v`,`libvpx`,`-qmin`,`0`,`-qmax`,`50`,`-crf`,`8`,`-deadline`,`realtime`,`-speed`,`8`,`-b:v`,`1M`];this.#t=spawnCommand([`ffmpeg`,`-loglevel`,`error`,`-f`,`image2pipe`,`-avioflags`,`direct`,`-fpsprobesize`,`0`,`-probesize`,`32`,`-analyzeduration`,`0`,`-c:v`,`mjpeg`,`-i`,`pipe:0`,`-y`,`-an`,`-r`,String(Vn),...Jn,`-threads`,`1`,`-vf`,`scale=${jn.width}:${jn.height}:force_original_aspect_ratio=decrease,pad=${jn.width}:${jn.height}:(ow-iw)/2:(oh-ih)/2:gray`,`-pix_fmt`,`yuv420p`,xn],{stdio:[`pipe`,`ignore`,`pipe`]})}writeFrame(xn,jn){if(this.#r)return;this.#n||=jn;let Vn=Math.floor((jn-this.#n)*this.#e);if(this.#i){let xn=Vn-this.#i.frameNumber;for(let jn=0;jn<xn;jn+=1)this.#s.push(this.#i.buffer);this.#a=this.#a.then(()=>this.#c())}this.#i={buffer:xn,frameNumber:Vn,timestamp:jn},this.#o=Date.now()}async stop(xn){if(this.#r)return;if(!this.#i){if(!xn)throw Error(`Video recorder did not capture any frames.`);this.writeFrame(xn,Date.now()/1000)}let jn=this.#i;if(!jn)throw Error(`Video recorder did not capture any frames.`);let Vn=Math.max((Date.now()-this.#o)/1000,1);if(this.writeFrame(Buffer.from([]),jn.timestamp+Vn),this.#r=true,await withCurrentReplAbort(async()=>await this.#a),!this.#t.stdin)throw Error(`ffmpeg stdin is unavailable.`);let qn=this.#t.stdin;await withCurrentReplAbort(async()=>await endProcessInput(qn));let Jn=await withCurrentReplAbort(async()=>await waitForExitCode(this.#t));if(Jn!==0){let xn=await readProcessStderr(this.#t);throw Error(xn.trim()||`ffmpeg exited with code ${Jn}`)}}async#c(){if(!this.#t.stdin)throw Error(`ffmpeg stdin is unavailable.`);let xn=this.#t.stdin;for(;this.#s.length>0;){let jn=this.#s.shift();if(!(!jn||jn.length===0))try{await withCurrentReplAbort(async()=>await writeProcessChunk(xn,jn))}catch(xn){let jn=(await readProcessStderr(this.#t)).trim();throw Error(jn.length>0?`ffmpeg rejected video frame input: ${jn}`:xn instanceof Error?xn.message:String(xn))}}}},OmOVideo=class{#e;#t=null;#n=null;constructor(xn){this.#e=xn}async start(xn={}){return await withCurrentReplAbort(async()=>{if(this.#t)throw Error(`Video recording is already active.`);let jn=await this.#e.resolveSessionId(),Vn=this.#e.viewportSize()??await this.#e.refreshViewportSize().catch(()=>null)??AI_TAB_STABLE_VIEWPORT,qn=inferVideoFormat(xn),Jn=xn.size??defaultVideoSize(Vn),Yn=await this.#e.browser.ensurePath(xn.path??`video-${Date.now()}.${qn}`,`write`,`page.video.start`),Zn=xn.quality??DEFAULT_VIDEO_JPEG_QUALITY,Qn=xn.framerate??DEFAULT_VIDEO_FRAMERATE;await ensureDirForFile(Yn);let ei=new FfmpegVideoRecorder(Yn,Jn,Qn,qn),{promise:ti,resolve:ni,reject:ri}=Promise.withResolvers(),ii=this.#e.cdp.on(`Page.screencastFrame`,(xn,Vn)=>{if(Vn.sessionId!==jn)return;let qn=Buffer.from(xn.data,`base64`),Jn=xn.metadata?.timestamp??Date.now()/1000;ei.writeFrame(qn,Jn),this.#e.cdp.send(`Page.screencastFrameAck`,{sessionId:xn.sessionId},jn).catch(()=>{})});this.#t={completion:ti,format:qn,outputPath:Yn,pendingCopies:new Set,recorder:ei,rejectCompletion:ri,resolveCompletion:ni,sessionId:jn,unsubscribe:ii},this.#n=Yn;try{await this.#e.cdp.send(`Page.startScreencast`,{everyNthFrame:1,format:`jpeg`,maxHeight:Jn.height,maxWidth:Jn.width,quality:Zn},jn)}catch(xn){throw ii(),this.#t=null,this.#n=null,ri(xn),xn}})}async stop(xn={}){return await withCurrentReplAbort(async()=>{let jn=this.#t;if(!jn)throw Error(`Video recording has not been started.`);this.#t=null,jn.unsubscribe(),await this.#e.cdp.send(`Page.stopScreencast`,undefined,jn.sessionId).catch(()=>{});try{let Vn=await this.#e.screenshot({type:`jpeg`}).catch(()=>{return}),qn=xn.path?await this.#e.browser.ensurePath(xn.path,`write`,`page.video.stop`):jn.outputPath;await jn.recorder.stop(Vn),qn!==jn.outputPath&&(await ensureDirForFile(qn),await fs$16.copyFile(jn.outputPath,qn)),this.#n=qn;for(let xn of jn.pendingCopies){let jn=await this.#e.browser.ensurePath(xn,`write`,`page.video.saveAs`);jn!==qn&&(await ensureDirForFile(jn),await fs$16.copyFile(qn,jn))}jn.resolveCompletion(qn)}catch(xn){throw jn.rejectCompletion(xn),xn}})}async path(){if(this.#t)return this.#t.outputPath;if(this.#n)return this.#n;throw Error(`Video recording has not been started.`)}async saveAs(xn){return await withCurrentReplAbort(async()=>{let jn=await this.#e.browser.ensurePath(xn,`write`,`page.video.saveAs`);if(this.#t){this.#t.pendingCopies.add(xn),await this.#t.completion;return}if(!this.#n)throw Error(`Video recording has not been started.`);await ensureDirForFile(jn),await fs$16.copyFile(this.#n,jn)})}async delete(){return await withCurrentReplAbort(async()=>{if(this.#t&&await this.stop(),!this.#n)throw Error(`Video recording has not been started.`);let xn=this.#n;this.#n=null,await fs$16.unlink(xn).catch(()=>{})})}}});function isOmOEvaluateOptions(xn){if(typeof xn!=`object`||!xn||Array.isArray(xn))return false;let jn=Object.keys(xn);return jn.length===1&&jn[0]===`userGesture`}function resolveEvaluateCall(xn,jn,Vn){if(Vn!==undefined&&!isOmOEvaluateOptions(Vn))throw Error(`page.evaluate() accepts a single optional argument. Pass multiple values as one object or array, e.g. page.evaluate(fn, [a, b]).`);return typeof xn==`string`&&isOmOEvaluateOptions(jn)?{actualArg:undefined,evaluateOptions:jn}:{actualArg:jn,evaluateOptions:Vn??{}}}function clampScreenshotClipToTextureLimit(xn,jn){let Vn=Math.max(1,Math.floor(MAX_SCREENSHOT_TEXTURE_SIZE/(Number.isFinite(jn)&&jn>0?jn:1)));return{...xn,width:Math.max(1,Math.min(xn.width,Vn)),height:Math.max(1,Math.min(xn.height,Vn))}}function normalizeScreenshotTimeout(xn){if(xn!==undefined){if(!Number.isFinite(xn)||xn<0)throw Error(`page.screenshot: timeout must be a non-negative number`);return xn}}function screenshotCaptureLabel(xn){return xn.clip?`clipped screenshot`:xn.fullPage?`full-page screenshot`:`viewport screenshot`}function formatScreenshotError(xn){return xn instanceof Error?xn.message:String(xn)}function rewriteCaptureScreenshotError(xn,jn,Vn){let qn=formatScreenshotError(xn);if(qn!==`CDP command timeout: Page.captureScreenshot`)return xn instanceof Error?xn:Error(qn);let Jn=jn===undefined?`the default screenshot timeout`:jn===0?`a disabled page.screenshot timeout`:`the requested ${jn}ms page.screenshot timeout`;return Error(`page.screenshot: browser CDP command timed out while capturing ${Vn} before ${Jn} completed`)}function isCaptureScreenshotTimeout(xn){let jn=formatScreenshotError(xn);return jn===`CDP command timeout: Page.captureScreenshot`||jn.startsWith(`page.screenshot: browser CDP command timed out while capturing `)}function formatPdfLength(xn){if(xn!==undefined)return typeof xn==`number`?`${xn}px`:xn}var DEFAULT_WAIT_TIMEOUT_MS;var NETWORK_IDLE_MS;var MAX_SCREENSHOT_TEXTURE_SIZE;var FILECHOOSER_OOPIF_FALLBACK_MS;var OmOPage;var init_page=__esmMin(()=>{init_emittery(),init_context(),init_extension_bridge(),init_notification_grants(),init_utils$13(),init_client$6(),init_errors$3(),init_utils$4(),init_console_capture(),init_download(),init_file_chooser(),init_frame(),init_frame_manager(),init_keyboard(),init_locator(),init_mouse(),init_snapshot(),init_video(),init_wait(),DEFAULT_WAIT_TIMEOUT_MS=30000,NETWORK_IDLE_MS=500,MAX_SCREENSHOT_TEXTURE_SIZE=16384,FILECHOOSER_OOPIF_FALLBACK_MS=2000,OmOPage=class{cdp;frameManager;targetId;modifierState;mouse;keyboard;events=new Emittery;browser;#e=new Map;#t=null;#n;#r=``;#i=null;#a=[];#o=null;#s=false;#c=0;constructor(xn,jn,Vn,qn,Jn){this.cdp=xn,this.frameManager=jn,this.targetId=Vn,this.modifierState=new ModifierState,this.mouse=new OmOMouse(this,this.modifierState),this.keyboard=new OmOKeyboard(this,this.modifierState),this.browser=Jn,this.#n=qn,this.#r=this.frameManager.getFrame(this.frameManager.mainFrameId)?.url??``,this.#l()}#l(){let xn=new Set,jn=(xn)=>{replPrint(`[error] page event listener threw: ${xn instanceof Error?xn.message:xn}`)};this.#u(`Page.frameNavigated`,(xn)=>{xn.frame.parentId||(this.#r=xn.frame.url)}),this.#u(`Page.loadEventFired`,()=>{this.events.emit(`load`).catch(jn)}),this.#u(`Page.domContentEventFired`,()=>{this.events.emit(`domcontentloaded`).catch(jn)}),this.#u(`Page.javascriptDialogOpening`,(xn)=>{this.events.emit(`dialog`,{type:xn.type,message:xn.message,defaultPrompt:xn.defaultPrompt??``,url:xn.url}).catch(jn)}),this.#a.push(this.cdp.on(`Page.fileChooserOpened`,(xn,Vn)=>{!Vn.sessionId||!xn.backendNodeId||this.events.emit(`filechooser`,new FileChooser(this,xn.frameId,xn.backendNodeId,xn.mode===`selectMultiple`)).catch(jn)})),this.#u(`Page.frameAttached`,(xn)=>{this.events.emit(`frameattached`,{frameId:xn.frameId,parentFrameId:xn.parentFrameId}).catch(jn)}),this.#u(`Page.frameDetached`,(xn)=>{this.events.emit(`framedetached`,{frameId:xn.frameId}).catch(jn)}),this.#u(`Page.frameNavigated`,(xn)=>{this.events.emit(`framenavigated`,{frameId:xn.frame.id,url:xn.frame.url,name:xn.frame.name??``}).catch(jn)});let Vn=(Vn)=>{xn.has(Vn.guid)||this.frameManager.getFrame(Vn.frameId)&&(xn.add(Vn.guid),this.events.emit(`download`,new OmODownload(this,{guid:Vn.guid,startedAfterMs:Date.now(),suggestedFilename:Vn.suggestedFilename,url:Vn.url})).catch(jn))};this.#a.push(this.cdp.on(`Browser.downloadWillBegin`,(xn)=>Vn(xn))),this.#u(`Page.downloadWillBegin`,(xn)=>{Vn(xn)}),this.#a.push(this.cdp.on(`Page.windowOpen`,(xn,Vn)=>{!Vn.sessionId||!this.frameManager.hasSession(Vn.sessionId)||this.events.emit(`popup`,{url:xn.url,windowName:xn.windowName}).catch(jn)})),this.#u(`Inspector.targetCrashed`,()=>{this.events.emit(`crash`).catch(jn)}),this.frameManager.onSessionInitialized=(xn)=>{this.#c>0&&this.#p(true,[xn])}}#u(xn,jn){this.#a.push(this.cdp.on(xn,(xn,Vn)=>{Vn.sessionId===this.#n&&jn(xn)}))}on(xn,jn){let Vn=this.#e.get(xn)??new WeakMap,qn=this.events.on(xn,({data:xn})=>jn(xn));return Vn.set(jn,qn),this.#e.set(xn,Vn),xn===`filechooser`&&this.#m(),()=>{qn(),Vn.delete(jn),xn===`filechooser`&&this.#h()}}off(xn,jn){let Vn=this.#e.get(xn),qn=Vn?.get(jn);qn?.(),Vn?.delete(jn),qn&&xn===`filechooser`&&this.#h()}async waitForEvent(xn,jn={}){let Vn=jn.timeout??DEFAULT_WAIT_TIMEOUT_MS;if(xn===`download`){let qn=Date.now();return await new Promise((Jn,Yn)=>{let Zn=false,Qn=()=>{ri(),clearTimeout(ni)},ei=(xn)=>{Zn||(Zn=true,Qn(),xn())},ti=(xn)=>{(async()=>{try{if(jn.predicate&&!await jn.predicate(xn))return;ei(()=>Jn(xn))}catch(xn){ei(()=>Yn(xn instanceof Error?xn:Error(String(xn))))}})()},ni=setTimeout(()=>{ei(()=>Yn(Error(`Timed out waiting for page event "${String(xn)}"`)))},Vn),ri=this.on(`download`,ti);this.cdp.send(`OmO.waitForDownload`,{startedAfterMs:qn,timeoutMs:Vn},undefined,{extensionBridge:this.browser.extensionBridgeRoute}).then((xn)=>{ti(new OmODownload(this,{guid:String(xn.downloadId),sourcePath:xn.absolutePath,startedAfterMs:qn,suggestedFilename:path.basename(xn.absolutePath),url:this.url()}))}).catch(()=>{})})}return await new Promise((qn,Jn)=>{let Yn=false,Zn=null,Qn=()=>{ni(),clearTimeout(ti),Zn&&clearTimeout(Zn)},ei=(xn)=>{Yn||(Yn=true,Qn(),xn())},ti=setTimeout(()=>{ei(()=>Jn(Error(`Timed out waiting for page event "${String(xn)}"`)))},Vn);xn===`filechooser`&&Vn>FILECHOOSER_OOPIF_FALLBACK_MS&&(Zn=setTimeout(()=>{Yn||this.#g().then((xn)=>{xn&&!Yn&&ei(()=>qn(xn))})},FILECHOOSER_OOPIF_FALLBACK_MS));let ni=this.on(xn,(xn)=>{(async()=>{try{if(jn.predicate&&!await jn.predicate(xn))return;ei(()=>qn(xn))}catch(xn){ei(()=>Jn(xn instanceof Error?xn:Error(String(xn))))}})()})})}#d(){for(let xn of this.#a)xn();this.#a=[]}async dispose(){this.#s||(this.#s=true,await this.#o?.stop().catch(()=>{}),this.#d(),this.#c=0,await this.events.emit(`close`),this.events.clearListeners(),this.#e.clear())}reconnect(xn){this.#n=xn,this.#s=false,this.#d(),this.#l(),this.#r=this.frameManager.getFrame(this.frameManager.mainFrameId)?.url??this.#r,this.#c>0&&this.#p(true)}#f(){return[...new Set([this.#n,...[...this.frameManager.frames.values()].map((xn)=>xn.sessionId)])]}async prepareForPotentialFileChooser(){this.#c!==0&&await this.#p(true)}#p(xn,jn=this.#f()){return withoutReplContext(async()=>{await Promise.allSettled(jn.map((jn)=>this.cdp.send(`Page.setInterceptFileChooserDialog`,{enabled:xn},jn)))})}#m(){this.#c+=1,this.#c===1&&this.#p(true)}#h(){this.#c!==0&&(--this.#c,this.#c===0&&this.#p(false))}async#g(){for(let xn of this.frameManager.frames.values())if(xn.sessionId!==this.#n)try{let{root:jn}=await this.cdp.send(`DOM.getDocument`,{depth:0},xn.sessionId),{nodeIds:Vn}=await this.cdp.send(`DOM.querySelectorAll`,{nodeId:jn.nodeId,selector:`input[type=file]`},xn.sessionId);if(Vn.length===0)continue;let qn=Vn[Vn.length-1],{node:Jn}=await this.cdp.send(`DOM.describeNode`,{nodeId:qn},xn.sessionId),Yn=(Jn.attributes??[]).includes(`multiple`);return new FileChooser(this,xn.frameId,Jn.backendNodeId,Yn)}catch{}return null}async resolveSessionId(xn){if(xn&&this.frameManager.hasSession(xn))return xn;let jn=await this.browser.resolvePageSession(this.targetId,this.#n);return jn!==this.#n&&(this.#n=jn),jn}async goto(xn,jn={}){let Vn=jn.waitUntil??`interactive`,qn=await this.resolveSessionId(this.#n),Jn=()=>{},Yn=Vn===`commit`?null:Vn===`interactive`||Vn===`stable`?waitForNavigationReadiness(this,Vn,DEFAULT_WAIT_TIMEOUT_MS).then(()=>{}):Vn===`networkidle`?this.#S(DEFAULT_WAIT_TIMEOUT_MS):new Promise((xn,jn)=>{let Yn=false,Zn,Qn=()=>{},ei=(xn)=>{Yn||(Yn=true,clearTimeout(Zn),Qn(),xn())};Zn=setTimeout(()=>{ei(()=>jn(Error(`Timed out waiting for "${Vn}" after goto`)))},DEFAULT_WAIT_TIMEOUT_MS);let ti=Vn===`load`?`Page.loadEventFired`:`Page.domContentEventFired`;Qn=this.cdp.on(ti,(jn,Vn)=>{Vn.sessionId===qn&&ei(xn)}),Jn=()=>ei(xn)}),Zn=await this.cdp.send(`Page.navigate`,{url:xn},qn);if(Zn.errorText)throw Error(Zn.errorText);if(this.#r=xn,Vn===`domcontentloaded`&&Yn){if(!Zn.loaderId){Jn(),await this.#v(`Timed out waiting for "${Vn}" after goto`);return}try{await Yn}catch(xn){if(await this.#_())return;throw xn}return}Yn&&await Yn}async goBack(){let xn=await this._sendToTarget(`Page.getNavigationHistory`),jn=xn.currentIndex-1;if(jn<0)return;let Vn=xn.entries[jn];if(!Vn)return;let qn=this.#C();await this._sendToTarget(`Page.navigateToHistoryEntry`,{entryId:Vn.id}),await qn}async goForward(){let xn=await this._sendToTarget(`Page.getNavigationHistory`),jn=xn.currentIndex+1;if(jn>=xn.entries.length)return;let Vn=xn.entries[jn];if(!Vn)return;let qn=this.#C();await this._sendToTarget(`Page.navigateToHistoryEntry`,{entryId:Vn.id}),await qn}async reload(){let xn=this.#C();await this._sendToTarget(`Page.reload`),await xn}async waitForLoadState(xn=`interactive`,jn=DEFAULT_WAIT_TIMEOUT_MS){if(xn===`interactive`||xn===`stable`){await waitForNavigationReadiness(this,xn,jn);return}if(xn===`networkidle`||xn===`load`){replPrint(`[system] this is redundant wait; openTab() already waits for the page to become usable. Falling back to 'stable' with 5s timeout.`),await waitForNavigationReadiness(this,`stable`,5000).catch(()=>{});return}if(await this.#_())return;let Vn=`Page.domContentEventFired`,qn=await this.resolveSessionId(this.#n);await new Promise((jn,Vn)=>{let Jn=setTimeout(()=>{Yn(),Vn(Error(`Timed out waiting for load state "${xn}"`))},DEFAULT_WAIT_TIMEOUT_MS),Yn=this.cdp.on(`Page.domContentEventFired`,(xn,Vn)=>{Vn.sessionId===qn&&(clearTimeout(Jn),Yn(),jn())})})}async#_(){return await this.evaluate(`document.readyState`).catch(()=>`loading`)!==`loading`}async#v(xn,jn=DEFAULT_WAIT_TIMEOUT_MS){let Vn=Date.now();for(;Date.now()-Vn<jn;){if(await this.#_())return;await sleep$12(50)}throw Error(xn)}async waitForURL(xn,jn={}){let Vn=jn.timeout??DEFAULT_WAIT_TIMEOUT_MS,qn=Date.now();for(;Date.now()-qn<Vn;){let jn=this.url();if(typeof xn==`string`?jn===xn:xn.test(jn))return;await sleep$12(100)}throw Error(`Timed out waiting for URL match: ${String(xn)}`)}async waitForSelector(xn,jn={}){let Vn=jn.state??`visible`,qn=jn.timeout??3000,Jn=[0,20,50,100,100,500],Yn=Date.now(),Zn=0;for(;;){if(Date.now()-Yn>=qn)throw Error(`waitForSelector: timed out after ${qn}ms waiting for "${xn}" to be ${Vn}`);let jn=await this.evaluate((xn)=>{let jn=document.querySelector(xn);if(!jn)return{attached:false,visible:false};let Vn=jn.getBoundingClientRect(),qn=window.getComputedStyle(jn);return{attached:true,visible:qn.visibility!==`hidden`&&qn.display!==`none`&&parseFloat(qn.opacity)!==0&&Vn.width>0&&Vn.height>0}},xn);if(Vn===`attached`?jn.attached:Vn===`detached`?!jn.attached:Vn===`visible`?jn.visible:!jn.visible)return;let Qn=Jn[Math.min(Zn++,Jn.length-1)];await sleep$12(Qn)}}locator(xn,jn={}){return new Locator(this,xn).filter(jn)}async click(xn,jn){await this.locator(xn).click(jn)}async fill(xn,jn){await this.locator(xn).fill(jn)}async $(xn){return replPrint(`warn`,`page.$() is deprecated in OmOWright — use page.locator(selector).first() instead`),this.locator(xn).first()}async $$(xn){return replPrint(`warn`,`page.$$() is deprecated in OmOWright — use page.locator(selector).all() instead`),await this.locator(xn).all()}async $$eval(xn,jn,Vn){return await this.locator(xn).evaluateAll(jn,Vn)}getByRole(xn,jn={}){return new Locator(this,Locator.createRoleSelector(xn,jn))}getByLabel(xn,jn={}){if(xn instanceof RegExp)return new Locator(this,`internal:label=/${xn.source}/${xn.flags}`);let Vn=JSON.stringify(xn);return new Locator(this,jn.exact?`internal:label:exact:${Vn}`:`internal:label:${Vn}`)}getByText(xn,jn={}){if(xn instanceof RegExp)return new Locator(this,`text:/${xn.source}/${xn.flags}`);let Vn=JSON.stringify(xn);return new Locator(this,jn.exact?`text:exact:${Vn}`:`text:${Vn}`)}frameLocator(xn){return new FrameLocator(this,this.mainFrame(),xn)}async evaluate(xn,jn,Vn){let{actualArg:qn,evaluateOptions:Jn}=resolveEvaluateCall(xn,jn,Vn);if(typeof xn==`string`&&qn===undefined){let{result:jn,exceptionDetails:Vn}=await this._sendToTarget(`Runtime.evaluate`,{expression:xn,returnByValue:true,awaitPromise:true,userGesture:Jn.userGesture});if(Vn)throw Error(formatRuntimeException(Vn,`Evaluation failed`));return jn.value}let Yn=typeof xn==`function`?xn.toString():xn,Zn=await this.#x();try{let{result:xn,exceptionDetails:jn}=await this._sendToTarget(`Runtime.callFunctionOn`,{objectId:Zn,functionDeclaration:`function(arg) { var __name = (t) => t; return (${Yn})(arg); }`,arguments:[{value:qn}],returnByValue:true,awaitPromise:true,userGesture:Jn.userGesture});if(jn)throw Error(formatRuntimeException(jn,`Evaluation failed`));return xn.value}finally{await this._sendToTarget(`Runtime.releaseObject`,{objectId:Zn}).catch(()=>{})}}async evaluateInFrame(xn,jn,Vn,qn){let Jn=this.frameManager.getFrame(xn);if(!Jn)throw Error(`Frame is not available: ${xn}`);let{actualArg:Yn,evaluateOptions:Zn}=resolveEvaluateCall(jn,Vn,qn),Qn=await this.resolveSessionId(Jn.sessionId);if(typeof jn==`string`&&Yn===undefined){let Vn=await this.frameManager.ensureInjected(xn);if(!Vn)throw Error(`Frame isolated world is not ready: ${xn}`);let{result:qn,exceptionDetails:Jn}=await this.cdp.send(`Runtime.evaluate`,{expression:jn,contextId:Vn,returnByValue:true,awaitPromise:true,userGesture:Zn.userGesture},Qn);if(Jn)throw Error(formatRuntimeException(Jn,`Evaluation failed`));return qn.value}let ei=typeof jn==`function`?jn.toString():jn,ti=await this.frameManager.ensureInjected(xn);if(!ti)throw Error(`Frame isolated world is not ready: ${xn}`);let{result:ni,exceptionDetails:ri}=await this.cdp.send(`Runtime.callFunctionOn`,{functionDeclaration:`function(arg) { var __name = (t) => t; return (${ei})(arg); }`,arguments:[{value:Yn}],executionContextId:ti,returnByValue:true,awaitPromise:true,userGesture:Zn.userGesture},Qn);if(ri)throw Error(formatRuntimeException(ri,`Evaluation failed`));return ni.value}async grantNotificationPermission(){await sendCommandToExtension({method:`OmO.grantNotificationPermission`,params:{targetId:this.targetId}},this.browser.extensionBridgeRoute);let xn=new URL(this.#r).origin,jn=this.browser.daemonSession;upsertNotificationGrant({accountId:jn.accountId,origin:xn,grantedBySessionId:jn.id})}url({truncate:xn=false}={}){return xn?truncateUrlForLLM(this.#r):this.#r}async title(){return await this.evaluate(`document.title`).catch(()=>``)}async content(){return await this.evaluate(`document.documentElement.outerHTML`)}async screenshot(xn={}){let jn=normalizeScreenshotTimeout(xn.timeout),Vn=jn&&jn>0?Date.now()+jn:undefined,qn;if(xn.clip)qn={x:xn.clip.x,y:xn.clip.y,width:xn.clip.width,height:xn.clip.height,scale:1};else if(xn.fullPage){let xn=await this._sendToTarget(`Page.getLayoutMetrics`),jn=xn.cssContentSize??xn.contentSize;qn={x:0,y:0,width:jn.width,height:jn.height,scale:1}}qn&&=clampScreenshotClipToTextureLimit(qn,await this.#y());let Jn={format:xn.type??`png`,captureBeyondViewport:!!xn.fullPage,quality:xn.quality,...qn?{clip:qn}:{}},Yn;try{({data:Yn}=await this.#b(Jn,jn,screenshotCaptureLabel(xn)))}catch(jn){if(!xn.fullPage||xn.clip||!isCaptureScreenshotTimeout(jn))throw jn;let qn=Vn?Math.max(0,Vn-Date.now()):undefined;if(qn===0)throw jn;let Jn={format:xn.type??`png`,captureBeyondViewport:false,quality:xn.quality};try{({data:Yn}=await this.#b(Jn,qn,`viewport fallback screenshot`)),replPrint(`[system] full-page screenshot failed; returned viewport fallback. ${formatScreenshotError(jn)}`)}catch(xn){throw Error(`${formatScreenshotError(jn)}
1468
- Viewport fallback also failed: ${formatScreenshotError(xn)}`)}}let Zn=Buffer.from(Yn,`base64`);if(xn.path)if(xn.path.startsWith(`/tmp`)){let jn=this.browser.resolvePath(`tmp`),Vn=path.join(jn,path.basename(xn.path));await mkdir(path.dirname(Vn),{recursive:true}),await writeFile(Vn,Zn),replPrint(`[WARN] Use ${jn} instead of /tmp. Saved it to ${Vn}`)}else{let jn=this.browser.resolvePath(xn.path);await mkdir(path.dirname(jn),{recursive:true}),await writeFile(jn,Zn)}return Zn}async#y(){return await this.evaluate(`window.devicePixelRatio`).catch(()=>1)}async#b(xn,jn,Vn){try{return await this._sendToTarget(`Page.captureScreenshot`,xn,{timeoutMs:jn})}catch(xn){throw rewriteCaptureScreenshotError(xn,jn,Vn)}}async snapshot(xn={}){return await takeSnapshot(this.cdp,this.frameManager,xn)}frames(){return this.frameManager.collectDescendantFrames(this.frameManager.mainFrameId).map(({frameId:xn})=>new OmOFrame(this,xn))}mainFrame(){let xn=this.frameManager.getFrame(this.frameManager.mainFrameId);if(!xn)throw Error(`Main frame is not available`);return new OmOFrame(this,xn.frameId)}async bringToFront(){this.browser.page=this,replPrint(`page is now [${await this.title().catch(()=>``)}](${this.url()})`)}async openPopupUrl(xn){await this.browser.openPopupTab(xn,this.targetId)}get console(){return this.#t??=new PageConsole(this)}video(){return this.#w()}viewportSize(){return this.#i?{...this.#i}:null}async refreshViewportSize(){let xn=await this.evaluate(()=>({width:Math.round(globalThis.visualViewport?.width??globalThis.innerWidth??0),height:Math.round(globalThis.visualViewport?.height??globalThis.innerHeight??0)}));if(xn.width<=0||xn.height<=0)throw Error(`Measured viewport size is invalid: ${JSON.stringify(xn)}`);return this.#i=xn,xn}setCachedViewportSize(xn){this.#i=xn?{...xn}:null}async pdf(xn={}){let jn=xn.path?await this.browser.ensurePath(xn.path,`write`,`page.pdf`):undefined,Vn={...xn,height:formatPdfLength(xn.height),margin:xn.margin?{bottom:formatPdfLength(xn.margin.bottom),left:formatPdfLength(xn.margin.left),right:formatPdfLength(xn.margin.right),top:formatPdfLength(xn.margin.top)}:undefined,printBackground:xn.printBackground??true,transferMode:`ReturnAsBase64`,width:formatPdfLength(xn.width)},{data:qn}=await this._sendToTarget(`Page.printToPDF`,Vn),Jn=Buffer.from(qn,`base64`);return jn&&(await mkdir(path.dirname(jn),{recursive:true}).catch(()=>{}),await writeFile(jn,Jn)),Jn}async close(xn={}){if(await this.#w().stop().catch(()=>{}),xn.runBeforeUnload){let jn=await this.resolveSessionId(this.#n);await this.cdp.send(`Page.close`,undefined,jn).catch(async()=>{await this.browser.closeTab(this,xn)});return}await this.browser.closeTab(this,xn)}async#x(){let{result:xn,exceptionDetails:jn}=await this._sendToTarget(`Runtime.evaluate`,{expression:`globalThis`,serializationOptions:{serialization:`idOnly`}});if(jn)throw Error(formatRuntimeException(jn,`Failed to resolve main world`));if(!xn.objectId)throw Error(`Main world object is not available`);return xn.objectId}async#S(xn){await this._sendToTarget(`Network.enable`).catch(()=>{});let jn=await this.resolveSessionId(this.#n);await new Promise((Vn,qn)=>{let Jn=new Set,Yn=null,Zn=null,Qn=()=>{ti(),ri(),ii(),Yn&&=(clearTimeout(Yn),null),Zn&&=(clearTimeout(Zn),null)},ei=()=>{Yn&&clearTimeout(Yn),Jn.size===0&&(Yn=setTimeout(()=>{Qn(),Vn()},NETWORK_IDLE_MS))},ti=this.cdp.on(`Network.requestWillBeSent`,(xn,Vn)=>{Vn.sessionId===jn&&(xn.request.url.startsWith(`data:`)||Jn.add(xn.requestId))}),ni=(xn)=>{Jn.delete(xn),ei()},ri=this.cdp.on(`Network.loadingFinished`,(xn,Vn)=>{Vn.sessionId===jn&&ni(xn.requestId)}),ii=this.cdp.on(`Network.loadingFailed`,(xn,Vn)=>{Vn.sessionId===jn&&ni(xn.requestId)});Zn=setTimeout(()=>{Qn(),qn(Error(`Timed out waiting for load state "networkidle"`))},xn),ei()})}async#C(){let xn=await this.resolveSessionId(this.#n);await new Promise((jn,Vn)=>{let qn=setTimeout(()=>{Jn(),Vn(Error(`Timed out waiting for navigation`))},DEFAULT_WAIT_TIMEOUT_MS),Jn=this.cdp.on(`Page.frameNavigated`,(Vn,Yn)=>{Yn.sessionId===xn&&(Vn.frame.parentId||(clearTimeout(qn),Jn(),jn()))})})}async _sendToTarget(xn,jn,Vn={}){if(this.#s)throw Error(`Page ${this.targetId} is disposed`);let{preferredSessionId:qn,...Jn}=Vn;if(qn!==undefined){if(!this.frameManager.hasSession(qn))throw Error(`Frame is stale or detached: CDP session ${qn} is unavailable`);return await this.cdp.send(xn,jn,qn,Jn)}let Yn=await this.resolveSessionId(this.#n);return await this.cdp.send(xn,jn,Yn,Jn)}#w(){return this.#o??=new OmOVideo(this),this.#o}}});init_client$6();init_session_manager();init_frame_manager();init_page();import{isDeepStrictEqual}from"node:util";var shared=new Map;function withinTimeout(pending,timeoutMs,message){let timer;return Promise.race([pending,new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error(message)),timeoutMs)})]).finally(()=>clearTimeout(timer))}function release(key,entry){entry.refs-=1;if(entry.refs>0)return Promise.resolve();if(shared.get(key)===entry)shared.delete(key);return entry.ready.then((connection)=>connection.close(),()=>{})}function bindTo(target,value){return typeof value==="function"?value.bind(target):value}class SharedConnectionError extends Error{constructor(){super("this browser connection is shared; pass dialogPolicy to connect()/connectNamed() so this holder gets its own connection");this.name="SharedConnectionError";this.code="shared_connection"}}var RESERVED=new Set(["cdp","close","setDialogPolicy"]);function refuse(what){return(_target,property)=>{throw new TypeError(`cannot change ${String(property)} on ${what} through a handle: every holder of this connection uses it`)}}function lease(key,entry,connection){let closed;const overlay=Object.create(null);const subscriptions=[];const ensureOpen=(property)=>{if(closed&&typeof property==="string"&&property!=="then"){throw new Error("this browser connection handle was closed")}};const close=()=>{if(!closed){for(const unsubscribe of subscriptions.splice(0))unsubscribe();closed=release(key,entry)}return closed};const setDialogPolicy=(policy)=>{ensureOpen("setDialogPolicy");if(entry.refs>1)throw new SharedConnectionError;if(shared.get(key)===entry)shared.delete(key);return connection.setDialogPolicy(policy)};const tracked=(subscribe)=>(...args)=>{const unsubscribe=subscribe(...args);subscriptions.push(unsubscribe);return unsubscribe};const readOnly=(what,get)=>({get,set:refuse(what),defineProperty:refuse(what),deleteProperty:refuse(what)});const client=connection.cdp;const transportSubscriptions=[];const transportEvents=new Proxy(client.transportEvents,readOnly("the shared transport events",(target,property)=>{ensureOpen(property);if(property==="on"){return(...args)=>{const unsubscribe=tracked(target.on.bind(target))(...args);transportSubscriptions.push(unsubscribe);return unsubscribe}}if(property==="clearListeners"){return()=>{for(const unsubscribe of transportSubscriptions.splice(0))unsubscribe()}}if(property==="emit"){return()=>{throw new TypeError("cannot emit transport events through a handle: every holder of this connection hears them")}}return bindTo(target,Reflect.get(target,property,target))}));const clientView=new Proxy(client,readOnly("the shared CDP client",(target,property)=>{ensureOpen(property);if(property==="setDialogPolicy")return setDialogPolicy;if(property==="close")return close;if(property==="on")return tracked(target.on.bind(target));if(property==="transportEvents")return transportEvents;return bindTo(target,Reflect.get(target,property,target))}));return new Proxy(connection,{get(target,property){if(property==="close")return close;ensureOpen(property);if(property==="setDialogPolicy")return setDialogPolicy;if(property==="cdp")return clientView;if(Object.hasOwn(overlay,property))return overlay[property];return bindTo(target,Reflect.get(target,property,target))},set(target,property,value){ensureOpen(property);if(RESERVED.has(property))return refuse("a handle")(target,property);return Reflect.set(overlay,property,value)},defineProperty(target,property,descriptor){ensureOpen(property);if(RESERVED.has(property))return refuse("a handle")(target,property);if(descriptor.configurable!==true)return false;return Reflect.defineProperty(overlay,property,descriptor)},deleteProperty(target,property){ensureOpen(property);if(Object.hasOwn(overlay,property))return Reflect.deleteProperty(overlay,property);return refuse("the shared connection")(target,property)},has(target,property){return Object.hasOwn(overlay,property)||Reflect.has(target,property)},ownKeys(target){return[...new Set([...Reflect.ownKeys(target),...Reflect.ownKeys(overlay)])]},getOwnPropertyDescriptor(target,property){return Object.hasOwn(overlay,property)?Reflect.getOwnPropertyDescriptor(overlay,property):Reflect.getOwnPropertyDescriptor(target,property)}})}function isPlainData(value,path=new Set){if(typeof value==="function"||value===null||typeof value!=="object")return true;if(path.has(value))return false;const prototype=Object.getPrototypeOf(value);const isArray=Array.isArray(value);if(isArray?prototype!==Array.prototype:prototype!==Object.prototype&&prototype!==null){return false}path.add(value);try{for(const key of Reflect.ownKeys(value)){if(isArray&&key==="length")continue;if(typeof key==="symbol")return false;const descriptor=Object.getOwnPropertyDescriptor(value,key);if(!descriptor.enumerable||!("value"in descriptor))return false;if(!isPlainData(descriptor.value,path))return false}return true}finally{path.delete(value)}}function canShareConnection(options){return options?.client?.authorizer===undefined&&isPlainData(options)}async function acquireSharedConnection(webSocketDebuggerUrl,options,{open,timeoutMs}){if(!canShareConnection(options))return open(options);let entry=shared.get(webSocketDebuggerUrl);if(entry&&!isDeepStrictEqual(entry.options,options))return open(options);if(!entry){entry={options,refs:0,ready:open(options)};shared.set(webSocketDebuggerUrl,entry)}entry.refs+=1;try{const connection=await entry.ready;const reconnected=connection.cdp.ensureConnected();await(timeoutMs===undefined?reconnected:withinTimeout(reconnected,timeoutMs,`timed out after ${timeoutMs}ms reconnecting the CDP session on ${webSocketDebuggerUrl}`));return lease(webSocketDebuggerUrl,entry,connection)}catch(error){if(shared.get(webSocketDebuggerUrl)===entry)shared.delete(webSocketDebuggerUrl);await release(webSocketDebuggerUrl,entry);throw error}}class TabRepository{#client;constructor(client){this.#client=client}async listTargets(){return this.#client.discoverTargets()}}class BrowserConnection{#client;#sessions;#pages=new Map;#storageRoot;constructor(client,options={}){this.#client=client;this.#sessions=new SessionManager(client);this.tabs=new TabRepository(client);this.#storageRoot=path2.resolve(options.storageRoot??process.cwd())}async initialize(){await this.#client.ensureConnected();return this}get cdp(){return this.#client}setDialogPolicy(policy){if(typeof this.#client.setDialogPolicy!=="function"){throw new Error("dialog policy is not supported by this transport")}this.#client.setDialogPolicy(policy)}async listTargets(){return this.tabs.listTargets()}async newTab(url="about:blank"){await Promise.resolve().then(() => init_agent_tabs());return(await createAgentTabs(this).create(url)).page}async attachPage(targetId){if(this.#pages.has(targetId))return this.#pages.get(targetId).page;const sessionId=await this.#sessions.resolvePageSession(targetId);const frames=new FrameManager(this.#client,targetId,sessionId);await frames.initialize();const entry={frames,page:null};const host=this.#createPageHost(targetId,entry);const page=new OmOPage(this.#client,frames,targetId,sessionId,host);entry.page=page;this.#pages.set(targetId,entry);return page}#createPageHost(targetId,entry){return{daemonSession:{accountId:0,id:"standalone"},extensionBridgeRoute:{},page:null,resolvePageSession:(id,preferred)=>this.#sessions.resolvePageSession(id,preferred),resolvePath:(value)=>path2.resolve(this.#storageRoot,value),ensurePath:async(value)=>{const resolved=path2.resolve(this.#storageRoot,value);await mkdir2(path2.dirname(resolved),{recursive:true});return resolved},registerReadableDownload:(value)=>value,isReadableDownload:()=>false,openPopupTab:async()=>{throw Error("Popup tab control requires the extension bridge")},closeTab:async(_page,options={})=>{if(options.runBeforeUnload){await this.#client.send("Page.close",undefined,await this.#sessions.resolvePageSession(targetId))}else{await this.#client.send("Target.closeTarget",{targetId})}await entry.page?.dispose();await entry.frames.dispose();this.#pages.delete(targetId)}}}async close(){for(const[targetId,entry]of[...this.#pages]){await entry.page.dispose().catch(()=>{});await entry.frames.dispose().catch(()=>{});this.#pages.delete(targetId)}await this.#sessions.dispose();await this.#client.close()}}async function browserKey(client,cdpUrl){if(/^wss?:\/\//.test(cdpUrl))return cdpUrl;return(await client.discoverVersion()).webSocketDebuggerUrl}function createClient(cdpHttpUrl,options){if(typeof cdpHttpUrl!=="string"||cdpHttpUrl.length===0){throw new TypeError("connect(cdpHttpUrl) requires a Chromium CDP HTTP URL")}return new CdpClient({cdpUrl:cdpHttpUrl,...options.client,dialogPolicy:options.dialogPolicy??options.client?.dialogPolicy})}async function openConnection(cdpHttpUrl,options={}){return new BrowserConnection(createClient(cdpHttpUrl,options),options).initialize()}async function connect(cdpHttpUrl,options={}){const client=createClient(cdpHttpUrl,options);if(!canShareConnection(options))return new BrowserConnection(client,options).initialize();const key=await browserKey(client,cdpHttpUrl);return acquireSharedConnection(key,options,{open:()=>openConnection(key,options)})}var OMOWRIGHT_PAGE_CDP_METHODS=Object.freeze(["Browser.getVersion","DOM.describeNode","DOM.enable","DOM.getBoxModel","DOM.getContentQuads","DOM.getDocument","DOM.querySelectorAll","DOM.resolveNode","DOM.scrollIntoViewIfNeeded","DOM.setFileInputFiles","Emulation.setDeviceMetricsOverride","Emulation.setFocusEmulationEnabled","Input.dispatchKeyEvent","Input.dispatchMouseEvent","Input.insertText","Inspector.enable","Network.enable","Page.addScriptToEvaluateOnNewDocument","Page.captureScreenshot","Page.close","Page.createIsolatedWorld","Page.enable","Page.getFrameTree","Page.getLayoutMetrics","Page.getNavigationHistory","Page.handleJavaScriptDialog","Page.navigate","Page.navigateToHistoryEntry","Page.printToPDF","Page.reload","Page.removeScriptToEvaluateOnNewDocument","Page.screencastFrameAck","Page.setInterceptFileChooserDialog","Page.setWebLifecycleState","Page.startScreencast","Page.stopScreencast","Runtime.callFunctionOn","Runtime.evaluate","Runtime.releaseObject","Target.activateTarget","Target.attachToTarget","Target.closeTarget","Target.createTarget","Target.detachFromTarget","Target.getTargetInfo","Target.getTargets","Target.setAutoAttach","Target.setDiscoverTargets"]);import{spawn}from"node:child_process";init_internal_capability();class Emittery2{#listeners=new Map;on(event,fn){const list=this.#listeners.get(event)??[];list.push(fn);this.#listeners.set(event,list);return()=>{const cur=this.#listeners.get(event)??[];this.#listeners.set(event,cur.filter((f)=>f!==fn))}}async emit(event,data){for(const fn of this.#listeners.get(event)??[])await fn(data)}}var DEFAULT_COMMAND_TIMEOUT_MS2=30000;var DEFAULT_READINESS_TIMEOUT_MS=1e4;var GRACEFUL_CLOSE_TIMEOUT_MS=2000;var STDIO_TAIL_LIMIT=16384;class PipeCdpClient{#child;#writeStream;#readStream;#buffer=Buffer.alloc(0);#seq=1;#pending=new Map;#events=new Emittery2;#connected=false;#commandTimeoutMs;#readinessTimeoutMs;#browserPath;#browserArgs;#spawnOptions;#stdioTail="";#dialogPolicy;transportEvents=new Emittery2;logId;constructor({browserPath,browserArgs=[],spawnOptions={},commandTimeoutMs,readinessTimeoutMs,logId,dialogPolicy}){if(!browserPath)throw new TypeError("PipeCdpClient requires browserPath");this.#browserPath=browserPath;this.#browserArgs=browserArgs;this.#spawnOptions=spawnOptions;this.#commandTimeoutMs=commandTimeoutMs??DEFAULT_COMMAND_TIMEOUT_MS2;this.#readinessTimeoutMs=readinessTimeoutMs??DEFAULT_READINESS_TIMEOUT_MS;this.logId=logId??`cdp-pipe-${Date.now()}`;this.#dialogPolicy=normalizeDialogPolicy(dialogPolicy)}get isConnected(){return this.#connected}get isHeadless(){return this.#browserArgs.some((arg)=>/^--headless(?:=|$)/.test(arg))}get childProcess(){return this.#child}get dialogPolicy(){return this.#dialogPolicy}setDialogPolicy(policy){this.#dialogPolicy=normalizeDialogPolicy(policy)}async ensureConnected(){if(this.#connected)return;const stdio=["ignore","pipe","pipe","pipe","pipe"];this.#child=spawn(this.#browserPath,[...this.#browserArgs,"--remote-debugging-pipe"],{...this.#spawnOptions,stdio});this.#stdioTail="";for(const stream of[this.#child.stdout,this.#child.stderr]){if(!stream)continue;stream.setEncoding("utf8");stream.on("data",(chunk)=>{this.#stdioTail=(this.#stdioTail+chunk).slice(-STDIO_TAIL_LIMIT)});stream.on("error",()=>{})}this.#writeStream=this.#child.stdio[3];this.#readStream=this.#child.stdio[4];this.#readStream.on("data",(chunk)=>this.#onData(chunk));for(const stream of[this.#writeStream,this.#readStream]){stream.on("error",(error)=>this.#failAll(Error(`CDP pipe error: ${error.code??error.message}`)))}this.#child.on("exit",()=>{this.#connected=false;this.#failAll(Error("CDP pipe browser exited"));this.transportEvents.emit("disconnected").catch(()=>{})});this.#connected=true;await this.#awaitReady();await this.send("Target.setDiscoverTargets",{discover:true}).catch(()=>{});await this.transportEvents.emit("connected")}async#awaitReady(){const deadline=Date.now()+this.#readinessTimeoutMs;let lastError;while(Date.now()<deadline){try{await this.send("Browser.getVersion",undefined,undefined,{timeoutMs:1000});return}catch(error){lastError=error;await new Promise((resolve)=>setTimeout(resolve,100))}}const reason=lastError?String(lastError.message??lastError):"no response";const tail=this.#stdioTail.trim();throw Error(`Browser did not become ready over the CDP pipe (${reason})`+(tail?`
1468
+ Viewport fallback also failed: ${formatScreenshotError(xn)}`)}}let Zn=Buffer.from(Yn,`base64`);if(xn.path)if(xn.path.startsWith(`/tmp`)){let jn=this.browser.resolvePath(`tmp`),Vn=path.join(jn,path.basename(xn.path));await mkdir(path.dirname(Vn),{recursive:true}),await writeFile(Vn,Zn),replPrint(`[WARN] Use ${jn} instead of /tmp. Saved it to ${Vn}`)}else{let jn=this.browser.resolvePath(xn.path);await mkdir(path.dirname(jn),{recursive:true}),await writeFile(jn,Zn)}return Zn}async#y(){return await this.evaluate(`window.devicePixelRatio`).catch(()=>1)}async#b(xn,jn,Vn){try{return await this._sendToTarget(`Page.captureScreenshot`,xn,{timeoutMs:jn})}catch(xn){throw rewriteCaptureScreenshotError(xn,jn,Vn)}}async snapshot(xn={}){return await takeSnapshot(this.cdp,this.frameManager,xn)}frames(){return this.frameManager.collectDescendantFrames(this.frameManager.mainFrameId).map(({frameId:xn})=>new OmOFrame(this,xn))}mainFrame(){let xn=this.frameManager.getFrame(this.frameManager.mainFrameId);if(!xn)throw Error(`Main frame is not available`);return new OmOFrame(this,xn.frameId)}async bringToFront(){this.browser.page=this,replPrint(`page is now [${await this.title().catch(()=>``)}](${this.url()})`)}async openPopupUrl(xn){await this.browser.openPopupTab(xn,this.targetId)}get console(){return this.#t??=new PageConsole(this)}video(){return this.#w()}viewportSize(){return this.#i?{...this.#i}:null}async refreshViewportSize(){let xn=await this.evaluate(()=>({width:Math.round(globalThis.visualViewport?.width??globalThis.innerWidth??0),height:Math.round(globalThis.visualViewport?.height??globalThis.innerHeight??0)}));if(xn.width<=0||xn.height<=0)throw Error(`Measured viewport size is invalid: ${JSON.stringify(xn)}`);return this.#i=xn,xn}setCachedViewportSize(xn){this.#i=xn?{...xn}:null}async pdf(xn={}){let jn=xn.path?await this.browser.ensurePath(xn.path,`write`,`page.pdf`):undefined,Vn={...xn,height:formatPdfLength(xn.height),margin:xn.margin?{bottom:formatPdfLength(xn.margin.bottom),left:formatPdfLength(xn.margin.left),right:formatPdfLength(xn.margin.right),top:formatPdfLength(xn.margin.top)}:undefined,printBackground:xn.printBackground??true,transferMode:`ReturnAsBase64`,width:formatPdfLength(xn.width)},{data:qn}=await this._sendToTarget(`Page.printToPDF`,Vn),Jn=Buffer.from(qn,`base64`);return jn&&(await mkdir(path.dirname(jn),{recursive:true}).catch(()=>{}),await writeFile(jn,Jn)),Jn}async close(xn={}){if(await this.#w().stop().catch(()=>{}),xn.runBeforeUnload){let jn=await this.resolveSessionId(this.#n);await this.cdp.send(`Page.close`,undefined,jn).catch(async()=>{await this.browser.closeTab(this,xn)});return}await this.browser.closeTab(this,xn)}async#x(){let{result:xn,exceptionDetails:jn}=await this._sendToTarget(`Runtime.evaluate`,{expression:`globalThis`,serializationOptions:{serialization:`idOnly`}});if(jn)throw Error(formatRuntimeException(jn,`Failed to resolve main world`));if(!xn.objectId)throw Error(`Main world object is not available`);return xn.objectId}async#S(xn){await this._sendToTarget(`Network.enable`).catch(()=>{});let jn=await this.resolveSessionId(this.#n);await new Promise((Vn,qn)=>{let Jn=new Set,Yn=null,Zn=null,Qn=()=>{ti(),ri(),ii(),Yn&&=(clearTimeout(Yn),null),Zn&&=(clearTimeout(Zn),null)},ei=()=>{Yn&&clearTimeout(Yn),Jn.size===0&&(Yn=setTimeout(()=>{Qn(),Vn()},NETWORK_IDLE_MS))},ti=this.cdp.on(`Network.requestWillBeSent`,(xn,Vn)=>{Vn.sessionId===jn&&(xn.request.url.startsWith(`data:`)||Jn.add(xn.requestId))}),ni=(xn)=>{Jn.delete(xn),ei()},ri=this.cdp.on(`Network.loadingFinished`,(xn,Vn)=>{Vn.sessionId===jn&&ni(xn.requestId)}),ii=this.cdp.on(`Network.loadingFailed`,(xn,Vn)=>{Vn.sessionId===jn&&ni(xn.requestId)});Zn=setTimeout(()=>{Qn(),qn(Error(`Timed out waiting for load state "networkidle"`))},xn),ei()})}async#C(){let xn=await this.resolveSessionId(this.#n);await new Promise((jn,Vn)=>{let qn=setTimeout(()=>{Jn(),Vn(Error(`Timed out waiting for navigation`))},DEFAULT_WAIT_TIMEOUT_MS),Jn=this.cdp.on(`Page.frameNavigated`,(Vn,Yn)=>{Yn.sessionId===xn&&(Vn.frame.parentId||(clearTimeout(qn),Jn(),jn()))})})}async _sendToTarget(xn,jn,Vn={}){if(this.#s)throw Error(`Page ${this.targetId} is disposed`);let{preferredSessionId:qn,...Jn}=Vn;if(qn!==undefined){if(!this.frameManager.hasSession(qn))throw Error(`Frame is stale or detached: CDP session ${qn} is unavailable`);return await this.cdp.send(xn,jn,qn,Jn)}let Yn=await this.resolveSessionId(this.#n);return await this.cdp.send(xn,jn,Yn,Jn)}#w(){return this.#o??=new OmOVideo(this),this.#o}}});init_client$6();init_session_manager();init_frame_manager();init_page();import{isDeepStrictEqual}from"node:util";var shared=new Map;function withinTimeout(pending,timeoutMs,message){let timer;return Promise.race([pending,new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error(message)),timeoutMs)})]).finally(()=>clearTimeout(timer))}function release(key,entry){entry.refs-=1;if(entry.refs>0)return Promise.resolve();if(shared.get(key)===entry)shared.delete(key);return entry.ready.then((connection)=>connection.close(),()=>{})}function bindTo(target,value){return typeof value==="function"?value.bind(target):value}class SharedConnectionError extends Error{constructor(){super("this browser connection is shared; pass dialogPolicy to connect()/connectNamed() so this holder gets its own connection");this.name="SharedConnectionError";this.code="shared_connection"}}var RESERVED=new Set(["cdp","close","setDialogPolicy"]);function refuse(what){return(_target,property)=>{throw new TypeError(`cannot change ${String(property)} on ${what} through a handle: every holder of this connection uses it`)}}function lease(key,entry,connection){let closed;const overlay=Object.create(null);const subscriptions=[];const ensureOpen=(property)=>{if(closed&&typeof property==="string"&&property!=="then"){throw new Error("this browser connection handle was closed")}};const close=()=>{if(!closed){for(const unsubscribe of subscriptions.splice(0))unsubscribe();closed=release(key,entry)}return closed};const setDialogPolicy=(policy)=>{ensureOpen("setDialogPolicy");if(entry.refs>1)throw new SharedConnectionError;if(shared.get(key)===entry)shared.delete(key);return connection.setDialogPolicy(policy)};const tracked=(subscribe)=>(...args)=>{const unsubscribe=subscribe(...args);subscriptions.push(unsubscribe);return unsubscribe};const readOnly=(what,get)=>({get,set:refuse(what),defineProperty:refuse(what),deleteProperty:refuse(what)});const client=connection.cdp;const transportSubscriptions=[];const transportEvents=new Proxy(client.transportEvents,readOnly("the shared transport events",(target,property)=>{ensureOpen(property);if(property==="on"){return(...args)=>{const unsubscribe=tracked(target.on.bind(target))(...args);transportSubscriptions.push(unsubscribe);return unsubscribe}}if(property==="clearListeners"){return()=>{for(const unsubscribe of transportSubscriptions.splice(0))unsubscribe()}}if(property==="emit"){return()=>{throw new TypeError("cannot emit transport events through a handle: every holder of this connection hears them")}}return bindTo(target,Reflect.get(target,property,target))}));const clientView=new Proxy(client,readOnly("the shared CDP client",(target,property)=>{ensureOpen(property);if(property==="setDialogPolicy")return setDialogPolicy;if(property==="close")return close;if(property==="on")return tracked(target.on.bind(target));if(property==="transportEvents")return transportEvents;return bindTo(target,Reflect.get(target,property,target))}));return new Proxy(connection,{get(target,property){if(property==="close")return close;ensureOpen(property);if(property==="setDialogPolicy")return setDialogPolicy;if(property==="cdp")return clientView;if(Object.hasOwn(overlay,property))return overlay[property];return bindTo(target,Reflect.get(target,property,target))},set(target,property,value){ensureOpen(property);if(RESERVED.has(property))return refuse("a handle")(target,property);return Reflect.set(overlay,property,value)},defineProperty(target,property,descriptor){ensureOpen(property);if(RESERVED.has(property))return refuse("a handle")(target,property);if(descriptor.configurable!==true)return false;return Reflect.defineProperty(overlay,property,descriptor)},deleteProperty(target,property){ensureOpen(property);if(Object.hasOwn(overlay,property))return Reflect.deleteProperty(overlay,property);return refuse("the shared connection")(target,property)},has(target,property){return Object.hasOwn(overlay,property)||Reflect.has(target,property)},ownKeys(target){return[...new Set([...Reflect.ownKeys(target),...Reflect.ownKeys(overlay)])]},getOwnPropertyDescriptor(target,property){return Object.hasOwn(overlay,property)?Reflect.getOwnPropertyDescriptor(overlay,property):Reflect.getOwnPropertyDescriptor(target,property)}})}function isPlainData(value,path=new Set){if(typeof value==="function"||value===null||typeof value!=="object")return true;if(path.has(value))return false;const prototype=Object.getPrototypeOf(value);const isArray=Array.isArray(value);if(isArray?prototype!==Array.prototype:prototype!==Object.prototype&&prototype!==null){return false}path.add(value);try{for(const key of Reflect.ownKeys(value)){if(isArray&&key==="length")continue;if(typeof key==="symbol")return false;const descriptor=Object.getOwnPropertyDescriptor(value,key);if(!descriptor.enumerable||!("value"in descriptor))return false;if(!isPlainData(descriptor.value,path))return false}return true}finally{path.delete(value)}}function canShareConnection(options){return options?.client?.authorizer===undefined&&isPlainData(options)}async function acquireSharedConnection(webSocketDebuggerUrl,options,{open,timeoutMs}){if(!canShareConnection(options))return open(options);let entry=shared.get(webSocketDebuggerUrl);if(entry&&!isDeepStrictEqual(entry.options,options))return open(options);if(!entry){entry={options,refs:0,ready:open(options)};shared.set(webSocketDebuggerUrl,entry)}entry.refs+=1;try{const connection=await entry.ready;const reconnected=connection.cdp.ensureConnected();await(timeoutMs===undefined?reconnected:withinTimeout(reconnected,timeoutMs,`timed out after ${timeoutMs}ms reconnecting the CDP session on ${webSocketDebuggerUrl}`));return lease(webSocketDebuggerUrl,entry,connection)}catch(error){if(shared.get(webSocketDebuggerUrl)===entry)shared.delete(webSocketDebuggerUrl);await release(webSocketDebuggerUrl,entry);throw error}}class TabRepository{#client;constructor(client){this.#client=client}async listTargets(){return this.#client.discoverTargets()}}class BrowserConnection{#client;#sessions;#pages=new Map;#storageRoot;constructor(client,options={}){this.#client=client;this.#sessions=new SessionManager(client);this.tabs=new TabRepository(client);this.#storageRoot=path2.resolve(options.storageRoot??process.cwd())}async initialize(){await this.#client.ensureConnected();return this}get cdp(){return this.#client}setDialogPolicy(policy){if(typeof this.#client.setDialogPolicy!=="function"){throw new Error("dialog policy is not supported by this transport")}this.#client.setDialogPolicy(policy)}async listTargets(){return this.tabs.listTargets()}async newTab(url="about:blank"){await Promise.resolve().then(() => init_agent_tabs());return(await createAgentTabs(this).create(url)).page}async attachPage(targetId){if(this.#pages.has(targetId))return this.#pages.get(targetId).page;const sessionId=await this.#sessions.resolvePageSession(targetId);const frames=new FrameManager(this.#client,targetId,sessionId);await frames.initialize();const entry={frames,page:null};const host=this.#createPageHost(targetId,entry);const page=new OmOPage(this.#client,frames,targetId,sessionId,host);entry.page=page;this.#pages.set(targetId,entry);return page}#createPageHost(targetId,entry){return{daemonSession:{accountId:0,id:"standalone"},extensionBridgeRoute:{},page:null,resolvePageSession:(id,preferred)=>this.#sessions.resolvePageSession(id,preferred),resolvePath:(value)=>path2.resolve(this.#storageRoot,value),ensurePath:async(value)=>{const resolved=path2.resolve(this.#storageRoot,value);await mkdir2(path2.dirname(resolved),{recursive:true});return resolved},registerReadableDownload:(value)=>value,isReadableDownload:()=>false,openPopupTab:async()=>{throw Error("Popup tab control requires the extension bridge")},closeTab:async(_page,options={})=>{if(options.runBeforeUnload){await this.#client.send("Page.close",undefined,await this.#sessions.resolvePageSession(targetId))}else{await this.#client.send("Target.closeTarget",{targetId})}await entry.page?.dispose();await entry.frames.dispose();this.#pages.delete(targetId)}}}async close(){for(const[targetId,entry]of[...this.#pages]){await entry.page.dispose().catch(()=>{});await entry.frames.dispose().catch(()=>{});this.#pages.delete(targetId)}await this.#sessions.dispose();await this.#client.close()}}async function browserKey(client,cdpUrl){if(/^wss?:\/\//.test(cdpUrl))return cdpUrl;return(await client.discoverVersion()).webSocketDebuggerUrl}function createClient(cdpHttpUrl,options){if(typeof cdpHttpUrl!=="string"||cdpHttpUrl.length===0){throw new TypeError("connect(cdpHttpUrl) requires a Chromium CDP HTTP URL")}return new CdpClient({cdpUrl:cdpHttpUrl,...options.client,dialogPolicy:options.dialogPolicy??options.client?.dialogPolicy})}async function openConnection(cdpHttpUrl,options={}){return new BrowserConnection(createClient(cdpHttpUrl,options),options).initialize()}async function connect(cdpHttpUrl,options={}){const client=createClient(cdpHttpUrl,options);if(!canShareConnection(options))return new BrowserConnection(client,options).initialize();const key=await browserKey(client,cdpHttpUrl);return acquireSharedConnection(key,options,{open:()=>openConnection(key,options)})}var OMOWRIGHT_PAGE_CDP_METHODS=Object.freeze(["Browser.getVersion","DOM.describeNode","DOM.enable","DOM.getBoxModel","DOM.getContentQuads","DOM.getDocument","DOM.querySelectorAll","DOM.resolveNode","DOM.scrollIntoViewIfNeeded","DOM.setFileInputFiles","Emulation.setDeviceMetricsOverride","Emulation.setFocusEmulationEnabled","Input.dispatchKeyEvent","Input.dispatchMouseEvent","Input.insertText","Inspector.enable","Network.enable","Page.addScriptToEvaluateOnNewDocument","Page.captureScreenshot","Page.close","Page.createIsolatedWorld","Page.enable","Page.getFrameTree","Page.getLayoutMetrics","Page.getNavigationHistory","Page.handleJavaScriptDialog","Page.navigate","Page.navigateToHistoryEntry","Page.printToPDF","Page.reload","Page.removeScriptToEvaluateOnNewDocument","Page.screencastFrameAck","Page.setInterceptFileChooserDialog","Page.setWebLifecycleState","Page.startScreencast","Page.stopScreencast","Runtime.callFunctionOn","Runtime.evaluate","Runtime.releaseObject","Target.activateTarget","Target.attachToTarget","Target.closeTarget","Target.createTarget","Target.detachFromTarget","Target.getTargetInfo","Target.getTargets","Target.setAutoAttach","Target.setDiscoverTargets"]);import{spawn}from"node:child_process";import{mkdirSync,readFileSync as readFileSync2,rmSync,writeFileSync}from"node:fs";import path4 from"node:path";import{setTimeout as sleep2}from"node:timers/promises";import{randomUUID as randomUUID2}from"node:crypto";import{watch}from"node:fs";import{link as link2,readdir,rename,stat,unlink as unlink2}from"node:fs/promises";import path3 from"node:path";import{setTimeout as sleep}from"node:timers/promises";import{execFile}from"node:child_process";import{randomUUID}from"node:crypto";import{chmod,link,open as open2,readFile,unlink}from"node:fs/promises";import{promisify}from"node:util";var execFileAsync=promisify(execFile);var ownStartTime;async function processStartTime(pid){if(process.platform==="win32")return null;if(process.platform==="linux"){try{const text=await readFile(`/proc/${pid}/stat`,"utf8");return text.slice(text.lastIndexOf(")")+2).split(" ")[19]??null}catch{return null}}try{const{stdout}=await execFileAsync("ps",["-o","lstart=","-p",String(pid)],{env:{...process.env,LC_ALL:"C",TZ:"UTC"}});return stdout.trim()||null}catch{return null}}async function createLock(lockPath){ownStartTime??=processStartTime(process.pid);const token=randomUUID();const tempPath=`${lockPath}.${process.pid}.${randomUUID()}.tmp`;let handle;try{handle=await open2(tempPath,"wx",384);await handle.writeFile(`${JSON.stringify({pid:process.pid,start:await ownStartTime,token,acquiredAt:new Date().toISOString()})}
1469
+ `,"utf8");const{ino}=await handle.stat();await handle.close();handle=null;await chmod(tempPath,384);try{await link(tempPath,lockPath)}catch(error){if(error?.code==="EEXIST")return null;throw error}return{ino,token}}finally{await handle?.close().catch(()=>{});await unlink(tempPath).catch(()=>{})}}async function readLockState(lockPath){let handle;try{handle=await open2(lockPath,"r")}catch(error){if(error?.code==="ENOENT")return null;throw error}try{const stats=await handle.stat();const text=await handle.readFile("utf8");const state={ino:stats.ino,mtimeMs:stats.mtimeMs,pid:null,start:null,token:null};try{const value=JSON.parse(text);if(Number.isInteger(value?.pid)&&value.pid>0)state.pid=value.pid;if(typeof value?.start==="string")state.start=value.start;if(typeof value?.token==="string")state.token=value.token}catch{}return state}finally{await handle.close().catch(()=>{})}}function isSameLock(state,ino,token){return state!==null&&state.ino===ino&&state.token===token}var LOCK_POLL_INTERVAL_MS=250;var LOCK_RETRY_JITTER_MS=25;var LOCK_PUT_BACK_TIMEOUT_MS=30000;var localLocks=new Map;function isPidAlive(pid){try{process.kill(pid,0);return true}catch(error){if(error?.code==="EPERM")return true;if(error?.code==="ESRCH")return false;throw error}}async function unlinkIfPresent(target){await unlink2(target).catch((error)=>{if(error?.code!=="ENOENT")throw error})}async function isAbandoned(state,staleGraceMs){if(state.pid===null)return Date.now()-state.mtimeMs>=staleGraceMs;if(!isPidAlive(state.pid))return true;if(state.start===null)return false;const start=await processStartTime(state.pid);return start!==null&&start!==state.start}async function reclaimLock(lockPath,ino,token,{deadline=Date.now()+LOCK_PUT_BACK_TIMEOUT_MS,hooks={}}={}){if(!isSameLock(await readLockState(lockPath),ino,token)){return await stat(lockPath).catch(()=>null)===null}const movedPath=`${lockPath}.${process.pid}.${randomUUID2()}.stale`;await hooks.beforeMove?.();try{await rename(lockPath,movedPath)}catch(error){if(error?.code==="ENOENT")return true;throw error}await hooks.afterMove?.();if(isSameLock(await readLockState(movedPath).catch(()=>null),ino,token)){await unlinkIfPresent(movedPath);return true}await putBack(movedPath,lockPath,deadline,hooks);return false}async function putBack(movedPath,lockPath,deadline,hooks){for(;;){try{await link2(movedPath,lockPath);await unlinkIfPresent(movedPath);return}catch(error){if(error?.code==="ENOENT")return;if(error?.code!=="EEXIST")throw error}await hooks.onPutBackBlocked?.();if(await stat(movedPath).catch(()=>null)===null)return;const remainingMs=deadline-Date.now();if(remainingMs<=0)return;await waitForLockRelease(lockPath,Math.min(remainingMs,LOCK_POLL_INTERVAL_MS))}}async function releaseLock(lockPath,ino,token){const prefix=`${path3.basename(lockPath)}.`;const entries=await readdir(path3.dirname(lockPath)).catch(()=>[]);for(const entry of entries){if(!entry.startsWith(prefix)||!entry.endsWith(".stale"))continue;const moved=path3.join(path3.dirname(lockPath),entry);if(isSameLock(await readLockState(moved).catch(()=>null),ino,token)){await unlinkIfPresent(moved)}}if(!isSameLock(await readLockState(lockPath),ino,token))return;await unlinkIfPresent(lockPath)}function waitForLockRelease(lockPath,timeoutMs){return new Promise((resolve,reject)=>{let settled=false;let watcher;let timer;const finish=(error,released)=>{if(settled)return;settled=true;clearTimeout(timer);watcher?.close();if(error)reject(error);else resolve(released)};const check=()=>{stat(lockPath).then(()=>{},(error)=>{if(error?.code==="ENOENT")finish(null,true);else finish(error)})};try{watcher=watch(path3.dirname(lockPath),(event,filename)=>{if((event==="rename"||event==="change")&&(!filename||filename.toString()===path3.basename(lockPath))){check()}})}catch(error){if(error?.code!=="ENOENT"){finish(error);return}}timer=setTimeout(()=>finish(null,false),timeoutMs);check()})}async function acquireLock(lockPath,timeoutMs,staleGraceMs){const deadline=Date.now()+timeoutMs;let holderPid=null;for(;;){const ours=await createLock(lockPath);if(ours!==null)return()=>releaseLock(lockPath,ours.ino,ours.token);if(Date.now()>=deadline){throw new Error(`timed out waiting for instance lock held by pid ${holderPid??"unknown"}`)}const state=await readLockState(lockPath);if(state===null)continue;holderPid=state.pid??holderPid;if(await isAbandoned(state,staleGraceMs)){await reclaimLock(lockPath,state.ino,state.token,{deadline});await sleep(Math.random()*LOCK_RETRY_JITTER_MS);continue}const remainingMs=deadline-Date.now();if(remainingMs>0){await waitForLockRelease(lockPath,Math.min(remainingMs,LOCK_POLL_INTERVAL_MS))}}}async function acquireLocalLock(key){const previous=localLocks.get(key)??Promise.resolve();let releaseCurrent;const current=new Promise((resolve)=>{releaseCurrent=resolve});const tail=previous.then(()=>current);localLocks.set(key,tail);await previous;return()=>{releaseCurrent();if(localLocks.get(key)===tail)localLocks.delete(key)}}async function withLock(lockPath,{timeoutMs,staleGraceMs},run){const releaseLocal=await acquireLocalLock(lockPath);try{const release=await acquireLock(lockPath,timeoutMs,staleGraceMs);try{return await run()}finally{await release()}}finally{releaseLocal()}}var TERM_GRACE_MS=5000;var PID_FILE_NAME="omowright-browser.pid";var REAP_POLL_MS=50;var browserSpawnDetached=process.platform!=="win32";function profileDirFromArgs(browserArgs){for(let index=0;index<browserArgs.length;index+=1){const argument=browserArgs[index];if(argument==="--user-data-dir"&&browserArgs[index+1])return path4.resolve(browserArgs[index+1]);if(argument.startsWith("--user-data-dir="))return path4.resolve(argument.slice("--user-data-dir=".length))}return null}async function terminateChild(child){if(child.exitCode!==null||child.signalCode!==null)return;const exited=new Promise((resolve)=>child.once("exit",resolve));try{child.kill("SIGTERM")}catch{}let timer;const graceOver=new Promise((resolve)=>{timer=setTimeout(resolve,TERM_GRACE_MS)});await Promise.race([exited,graceOver]);clearTimeout(timer);if(child.exitCode===null&&child.signalCode===null){try{child.kill("SIGKILL")}catch{}await exited}}function pidFilePath(profileDir){return path4.join(profileDir,PID_FILE_NAME)}async function writeBrowserPidFile(profileDir,pid){if(!profileDir||!pid)return;const[start,ownerStart]=await Promise.all([processStartTime(pid),processStartTime(process.pid)]);mkdirSync(profileDir,{recursive:true});const record={pid,start,profileDir,owner:{pid:process.pid,start:ownerStart}};writeFileSync(pidFilePath(profileDir),`${JSON.stringify(record)}
1470
+ `,{mode:384})}function removeBrowserPidFile(profileDir,pid){if(!profileDir)return;try{const recorded=JSON.parse(readFileSync2(pidFilePath(profileDir),"utf8"));if(recorded.pid!==pid)return}catch{return}rmSync(pidFilePath(profileDir),{force:true})}async function sameProcessAlive(pid,start){if(!Number.isInteger(pid)||pid<=0)return null;if(!isPidAlive(pid))return false;if(start===null||start===undefined)return null;const now=await processStartTime(pid);if(now===null)return null;return now===start}async function reapOrphanedBrowser(profileDir){if(!profileDir)return;let recorded;try{recorded=JSON.parse(readFileSync2(pidFilePath(profileDir),"utf8"))}catch{return}const{pid,start,owner}=recorded;const browserAlive=Number.isInteger(pid)&&pid>0?await sameProcessAlive(pid,start):false;if(browserAlive===false){rmSync(pidFilePath(profileDir),{force:true});return}if(browserAlive===null||pid===process.pid)return;const ownerAlive=await sameProcessAlive(owner?.pid,owner?.start);if(ownerAlive!==false)return;try{process.kill(pid,"SIGTERM")}catch{}const deadline=Date.now()+TERM_GRACE_MS;while(isPidAlive(pid)&&Date.now()<deadline)await sleep2(REAP_POLL_MS);if(isPidAlive(pid)){try{process.kill(pid,"SIGKILL")}catch{}}rmSync(pidFilePath(profileDir),{force:true})}init_internal_capability();class Emittery2{#listeners=new Map;on(event,fn){const list=this.#listeners.get(event)??[];list.push(fn);this.#listeners.set(event,list);return()=>{const cur=this.#listeners.get(event)??[];this.#listeners.set(event,cur.filter((f)=>f!==fn))}}async emit(event,data){for(const fn of this.#listeners.get(event)??[])await fn(data)}}var DEFAULT_COMMAND_TIMEOUT_MS2=30000;var DEFAULT_READINESS_TIMEOUT_MS=1e4;var GRACEFUL_CLOSE_TIMEOUT_MS=2000;var STDIO_TAIL_LIMIT=16384;class PipeCdpClient{#child;#writeStream;#readStream;#buffer=Buffer.alloc(0);#seq=1;#pending=new Map;#events=new Emittery2;#connected=false;#commandTimeoutMs;#readinessTimeoutMs;#browserPath;#browserArgs;#spawnOptions;#stdioTail="";#dialogPolicy;transportEvents=new Emittery2;logId;constructor({browserPath,browserArgs=[],spawnOptions={},commandTimeoutMs,readinessTimeoutMs,logId,dialogPolicy}){if(!browserPath)throw new TypeError("PipeCdpClient requires browserPath");this.#browserPath=browserPath;this.#browserArgs=browserArgs;this.#spawnOptions=spawnOptions;this.#commandTimeoutMs=commandTimeoutMs??DEFAULT_COMMAND_TIMEOUT_MS2;this.#readinessTimeoutMs=readinessTimeoutMs??DEFAULT_READINESS_TIMEOUT_MS;this.logId=logId??`cdp-pipe-${Date.now()}`;this.#dialogPolicy=normalizeDialogPolicy(dialogPolicy)}get isConnected(){return this.#connected}get isHeadless(){return this.#browserArgs.some((arg)=>/^--headless(?:=|$)/.test(arg))}get childProcess(){return this.#child}get dialogPolicy(){return this.#dialogPolicy}setDialogPolicy(policy){this.#dialogPolicy=normalizeDialogPolicy(policy)}async ensureConnected(){if(this.#connected)return;const stdio=["ignore","pipe","pipe","pipe","pipe"];const profileDir=profileDirFromArgs(this.#browserArgs);await reapOrphanedBrowser(profileDir);this.#child=spawn(this.#browserPath,[...this.#browserArgs,"--remote-debugging-pipe"],{detached:browserSpawnDetached,...this.#spawnOptions,stdio});const browserPid=this.#child.pid;this.#child.once("exit",()=>removeBrowserPidFile(profileDir,browserPid));this.#stdioTail="";for(const stream of[this.#child.stdout,this.#child.stderr]){if(!stream)continue;stream.setEncoding("utf8");stream.on("data",(chunk)=>{this.#stdioTail=(this.#stdioTail+chunk).slice(-STDIO_TAIL_LIMIT)});stream.on("error",()=>{})}this.#writeStream=this.#child.stdio[3];this.#readStream=this.#child.stdio[4];this.#readStream.on("data",(chunk)=>this.#onData(chunk));for(const stream of[this.#writeStream,this.#readStream]){stream.on("error",(error)=>this.#failAll(Error(`CDP pipe error: ${error.code??error.message}`)))}this.#child.on("exit",()=>{this.#connected=false;this.#failAll(Error("CDP pipe browser exited"));this.transportEvents.emit("disconnected").catch(()=>{})});try{await writeBrowserPidFile(profileDir,browserPid)}catch(error){const child=this.#child;this.#child=undefined;await terminateChild(child);throw error}this.#connected=true;await this.#awaitReady();await this.send("Target.setDiscoverTargets",{discover:true}).catch(()=>{});await this.transportEvents.emit("connected")}async#awaitReady(){const deadline=Date.now()+this.#readinessTimeoutMs;let lastError;while(Date.now()<deadline){try{await this.send("Browser.getVersion",undefined,undefined,{timeoutMs:1000});return}catch(error){lastError=error;await new Promise((resolve)=>setTimeout(resolve,100))}}const reason=lastError?String(lastError.message??lastError):"no response";const tail=this.#stdioTail.trim();throw Error(`Browser did not become ready over the CDP pipe (${reason})`+(tail?`
1469
1471
  Browser stdio tail:
1470
- ${tail}`:""))}async send(method,params,sessionId,opts={}){if(method==="Target.createTarget"&&opts.capability!==targetCreationCapability)throw Error("Target.createTarget is reserved for createAgentTabs");if(String(method).startsWith("OmO.")){throw Error(`Extension commands are unavailable over pipe transport: ${method}`)}await this.ensureConnected();const id=this.#seq++;const msg={id,method};if(params!==undefined)msg.params=params;if(sessionId)msg.sessionId=sessionId;const{promise,resolve,reject}=Promise.withResolvers();const timeoutMs=opts.timeoutMs??this.#commandTimeoutMs;const timeout=timeoutMs>0?setTimeout(()=>{this.#pending.delete(id);reject(Error(`CDP command timeout: ${method}`))},timeoutMs):undefined;this.#pending.set(id,{method,resolve,reject,timeout});this.#writeStream.write(JSON.stringify(msg)+"\x00");return promise}on(method,fn){return this.#events.on(method,({data})=>fn(data.payload,data.meta))}async discoverTargets(){const{targetInfos}=await this.send("Target.getTargets");return targetInfos.map((t)=>({id:t.targetId,type:t.type,url:t.url??"",title:t.title??"",description:t.description,faviconUrl:t.faviconUrl,parentId:t.openerId}))}async close(){const child=this.#child;this.#child=undefined;if(child&&child.exitCode===null){const exited=new Promise((resolve)=>child.once("exit",resolve));if(this.#connected){await Promise.race([this.send("Browser.close",undefined,undefined,{timeoutMs:GRACEFUL_CLOSE_TIMEOUT_MS}).catch(()=>{}),exited])}this.#connected=false;this.#failAll(Error("CDP pipe client closed"));if(child.exitCode===null){await Promise.race([exited,new Promise((resolve)=>setTimeout(resolve,GRACEFUL_CLOSE_TIMEOUT_MS))])}if(child.exitCode===null){try{child.kill("SIGKILL")}catch{}await exited}return}this.#connected=false;this.#failAll(Error("CDP pipe client closed"))}#onData(chunk){this.#buffer=Buffer.concat([this.#buffer,chunk]);let idx;while((idx=this.#buffer.indexOf(0))!==-1){const raw=this.#buffer.subarray(0,idx).toString("utf8");this.#buffer=this.#buffer.subarray(idx+1);if(raw.length===0)continue;let msg;try{msg=JSON.parse(raw)}catch{continue}this._handleTransportMessage(msg)}}_handleTransportMessage(msg){if(typeof msg.id==="number"){const entry=this.#pending.get(msg.id);if(!entry)return;this.#pending.delete(msg.id);if(entry.timeout)clearTimeout(entry.timeout);if(msg.error){const err=Error(msg.error.message??`CDP command failed: ${entry.method}`);err.code=msg.error.code;err.data=msg.error.data;entry.reject(err)}else{entry.resolve(msg.result)}return}if(typeof msg.method==="string"){const handled=msg.method==="Page.javascriptDialogOpening"&&msg.sessionId?this.#handleJavaScriptDialog(msg).catch(()=>{}):undefined;this.#events.emit(msg.method,{data:{payload:msg.params,meta:{sessionId:msg.sessionId}}}).catch(()=>{});return handled}}async#handleJavaScriptDialog(msg){const params=await resolveDialogAction(this.#dialogPolicy,msg.params);await this.send("Page.handleJavaScriptDialog",params,msg.sessionId)}#failAll(err){for(const entry of this.#pending.values()){if(entry.timeout)clearTimeout(entry.timeout);entry.reject(err)}this.#pending.clear()}}async function connectPipe({browserPath,browserArgs=[],spawnOptions,storageRoot,commandTimeoutMs,readinessTimeoutMs,logId,dialogPolicy}={}){const client=new PipeCdpClient({browserPath,browserArgs,spawnOptions,commandTimeoutMs,readinessTimeoutMs,logId,dialogPolicy});await client.ensureConnected();const connection=new BrowserConnection(client,{storageRoot});await connection.initialize();connection.browserProcess=client.childProcess;return connection}import{spawn as spawn2}from"node:child_process";import{createHash,randomUUID as randomUUID3}from"node:crypto";import{chmod as chmod2,mkdir as mkdir3,open as open3,readFile as readFile3,readdir as readdir2,rename as rename2,unlink as unlink3}from"node:fs/promises";import os from"node:os";import path5 from"node:path";import{randomUUID as randomUUID2}from"node:crypto";import{watch}from"node:fs";import{link as link2,readdir,rename,stat,unlink as unlink2}from"node:fs/promises";import path3 from"node:path";import{setTimeout as sleep}from"node:timers/promises";import{execFile}from"node:child_process";import{randomUUID}from"node:crypto";import{chmod,link,open as open2,readFile,unlink}from"node:fs/promises";import{promisify}from"node:util";var execFileAsync=promisify(execFile);var ownStartTime;async function processStartTime(pid){if(process.platform==="win32")return null;if(process.platform==="linux"){try{const text=await readFile(`/proc/${pid}/stat`,"utf8");return text.slice(text.lastIndexOf(")")+2).split(" ")[19]??null}catch{return null}}try{const{stdout}=await execFileAsync("ps",["-o","lstart=","-p",String(pid)],{env:{...process.env,LC_ALL:"C",TZ:"UTC"}});return stdout.trim()||null}catch{return null}}async function createLock(lockPath){ownStartTime??=processStartTime(process.pid);const token=randomUUID();const tempPath=`${lockPath}.${process.pid}.${randomUUID()}.tmp`;let handle;try{handle=await open2(tempPath,"wx",384);await handle.writeFile(`${JSON.stringify({pid:process.pid,start:await ownStartTime,token,acquiredAt:new Date().toISOString()})}
1471
- `,"utf8");const{ino}=await handle.stat();await handle.close();handle=null;await chmod(tempPath,384);try{await link(tempPath,lockPath)}catch(error){if(error?.code==="EEXIST")return null;throw error}return{ino,token}}finally{await handle?.close().catch(()=>{});await unlink(tempPath).catch(()=>{})}}async function readLockState(lockPath){let handle;try{handle=await open2(lockPath,"r")}catch(error){if(error?.code==="ENOENT")return null;throw error}try{const stats=await handle.stat();const text=await handle.readFile("utf8");const state={ino:stats.ino,mtimeMs:stats.mtimeMs,pid:null,start:null,token:null};try{const value=JSON.parse(text);if(Number.isInteger(value?.pid)&&value.pid>0)state.pid=value.pid;if(typeof value?.start==="string")state.start=value.start;if(typeof value?.token==="string")state.token=value.token}catch{}return state}finally{await handle.close().catch(()=>{})}}function isSameLock(state,ino,token){return state!==null&&state.ino===ino&&state.token===token}var LOCK_POLL_INTERVAL_MS=250;var LOCK_RETRY_JITTER_MS=25;var LOCK_PUT_BACK_TIMEOUT_MS=30000;var localLocks=new Map;function isPidAlive(pid){try{process.kill(pid,0);return true}catch(error){if(error?.code==="EPERM")return true;if(error?.code==="ESRCH")return false;throw error}}async function unlinkIfPresent(target){await unlink2(target).catch((error)=>{if(error?.code!=="ENOENT")throw error})}async function isAbandoned(state,staleGraceMs){if(state.pid===null)return Date.now()-state.mtimeMs>=staleGraceMs;if(!isPidAlive(state.pid))return true;if(state.start===null)return false;const start=await processStartTime(state.pid);return start!==null&&start!==state.start}async function reclaimLock(lockPath,ino,token,{deadline=Date.now()+LOCK_PUT_BACK_TIMEOUT_MS,hooks={}}={}){if(!isSameLock(await readLockState(lockPath),ino,token)){return await stat(lockPath).catch(()=>null)===null}const movedPath=`${lockPath}.${process.pid}.${randomUUID2()}.stale`;await hooks.beforeMove?.();try{await rename(lockPath,movedPath)}catch(error){if(error?.code==="ENOENT")return true;throw error}await hooks.afterMove?.();if(isSameLock(await readLockState(movedPath).catch(()=>null),ino,token)){await unlinkIfPresent(movedPath);return true}await putBack(movedPath,lockPath,deadline,hooks);return false}async function putBack(movedPath,lockPath,deadline,hooks){for(;;){try{await link2(movedPath,lockPath);await unlinkIfPresent(movedPath);return}catch(error){if(error?.code==="ENOENT")return;if(error?.code!=="EEXIST")throw error}await hooks.onPutBackBlocked?.();if(await stat(movedPath).catch(()=>null)===null)return;const remainingMs=deadline-Date.now();if(remainingMs<=0)return;await waitForLockRelease(lockPath,Math.min(remainingMs,LOCK_POLL_INTERVAL_MS))}}async function releaseLock(lockPath,ino,token){const prefix=`${path3.basename(lockPath)}.`;const entries=await readdir(path3.dirname(lockPath)).catch(()=>[]);for(const entry of entries){if(!entry.startsWith(prefix)||!entry.endsWith(".stale"))continue;const moved=path3.join(path3.dirname(lockPath),entry);if(isSameLock(await readLockState(moved).catch(()=>null),ino,token)){await unlinkIfPresent(moved)}}if(!isSameLock(await readLockState(lockPath),ino,token))return;await unlinkIfPresent(lockPath)}function waitForLockRelease(lockPath,timeoutMs){return new Promise((resolve,reject)=>{let settled=false;let watcher;let timer;const finish=(error,released)=>{if(settled)return;settled=true;clearTimeout(timer);watcher?.close();if(error)reject(error);else resolve(released)};const check=()=>{stat(lockPath).then(()=>{},(error)=>{if(error?.code==="ENOENT")finish(null,true);else finish(error)})};try{watcher=watch(path3.dirname(lockPath),(event,filename)=>{if((event==="rename"||event==="change")&&(!filename||filename.toString()===path3.basename(lockPath))){check()}})}catch(error){if(error?.code!=="ENOENT"){finish(error);return}}timer=setTimeout(()=>finish(null,false),timeoutMs);check()})}async function acquireLock(lockPath,timeoutMs,staleGraceMs){const deadline=Date.now()+timeoutMs;let holderPid=null;for(;;){const ours=await createLock(lockPath);if(ours!==null)return()=>releaseLock(lockPath,ours.ino,ours.token);if(Date.now()>=deadline){throw new Error(`timed out waiting for instance lock held by pid ${holderPid??"unknown"}`)}const state=await readLockState(lockPath);if(state===null)continue;holderPid=state.pid??holderPid;if(await isAbandoned(state,staleGraceMs)){await reclaimLock(lockPath,state.ino,state.token,{deadline});await sleep(Math.random()*LOCK_RETRY_JITTER_MS);continue}const remainingMs=deadline-Date.now();if(remainingMs>0){await waitForLockRelease(lockPath,Math.min(remainingMs,LOCK_POLL_INTERVAL_MS))}}}async function acquireLocalLock(key){const previous=localLocks.get(key)??Promise.resolve();let releaseCurrent;const current=new Promise((resolve)=>{releaseCurrent=resolve});const tail=previous.then(()=>current);localLocks.set(key,tail);await previous;return()=>{releaseCurrent();if(localLocks.get(key)===tail)localLocks.delete(key)}}async function withLock(lockPath,{timeoutMs,staleGraceMs},run){const releaseLocal=await acquireLocalLock(lockPath);try{const release=await acquireLock(lockPath,timeoutMs,staleGraceMs);try{return await run()}finally{await release()}}finally{releaseLocal()}}import{watch as watch2}from"node:fs";import{readFile as readFile2,stat as stat2}from"node:fs/promises";import path4 from"node:path";var ACTIVE_PORT_POLL_MS=100;function httpEndpointFromWebSocket(webSocketUrl){const endpoint=new URL(webSocketUrl);endpoint.protocol=endpoint.protocol==="wss:"?"https:":"http:";endpoint.pathname="/";endpoint.search="";endpoint.hash="";return endpoint.toString().replace(/\/$/,"")}function parseActivePort(text){const[portLine,browserPath]=text.trim().split(/\r?\n/);const port=Number(portLine);if(!Number.isInteger(port)||port<1||port>65535)return null;return{endpoint:`http://127.0.0.1:${port}`,browserPath:browserPath?.trim()||null}}function activePortIdentity(info){return{ino:info.ino,mtimeMs:info.mtimeMs,size:info.size}}async function snapshotActivePort(profileDir){if(!profileDir)return null;try{return activePortIdentity(await stat2(path4.join(profileDir,"DevToolsActivePort")))}catch(error){if(error?.code==="ENOENT")return null;throw error}}function isSameFile(info,previous){return previous!==null&&info.ino===previous.ino&&info.mtimeMs===previous.mtimeMs&&info.size===previous.size}function assertLaunchedBrowser(child,report,webSocketDebuggerUrl){if(child.exitCode!==null||child.signalCode!==null){throw new Error(`browser exited before its DevTools endpoint ${report.endpoint} could be verified`)}const answered=new URL(webSocketDebuggerUrl).pathname;if(report.browserPath===null||answered!==report.browserPath){throw new Error(`DevTools endpoint ${report.endpoint} answers as ${answered}, not as the launched browser (${report.browserPath??"no browser id reported"})`)}}async function discoverLaunchedEndpoint(child,{profileDir,timeoutMs,previousActivePort=null}={}){const activePortPath=profileDir?path4.join(profileDir,"DevToolsActivePort"):null;return new Promise((resolve,reject)=>{let settled=false;let stderr="";let activePortWatcher;let activePortReading=false;let activePortChangedWhileReading=false;const cleanups=[];const finish=(error,endpoint)=>{if(settled)return;settled=true;for(const cleanup of cleanups.splice(0))cleanup();activePortWatcher?.close();if(error)reject(error);else resolve(endpoint)};const readActivePort=async()=>{const info=await stat2(activePortPath);if(isSameFile(activePortIdentity(info),previousActivePort))return;const report=parseActivePort(await readFile2(activePortPath,"utf8"));if(report)finish(null,report)};const tryActivePort=async()=>{if(!activePortPath||settled)return;if(activePortReading){activePortChangedWhileReading=true;return}activePortReading=true;try{do{activePortChangedWhileReading=false;await readActivePort()}while(activePortChangedWhileReading&&!settled)}catch(error){if(error?.code!=="ENOENT")finish(error)}finally{activePortReading=false}};const onStderr=(chunk)=>{stderr=`${stderr}${chunk}`.slice(-16384);const match=/DevTools listening on (ws:\/\/\S+)/.exec(stderr);if(match){finish(null,{endpoint:httpEndpointFromWebSocket(match[1]),browserPath:new URL(match[1]).pathname})}else tryActivePort()};const onError=(error)=>finish(error);const onExit=(code,signal)=>{finish(new Error(`browser exited before exposing DevTools (code ${code}, signal ${signal??"none"})${stderr?`
1472
- ${stderr}`:""}`))};child.stderr.setEncoding("utf8");child.stderr.on("data",onStderr);child.once("error",onError);child.once("exit",onExit);cleanups.push(()=>child.stderr.off("data",onStderr));cleanups.push(()=>child.off("error",onError));cleanups.push(()=>child.off("exit",onExit));if(activePortPath){try{activePortWatcher=watch2(profileDir,(event,filename)=>{if((event==="rename"||event==="change")&&(!filename||filename.toString()===path4.basename(activePortPath))){tryActivePort()}})}catch(error){if(error?.code!=="ENOENT")finish(error)}tryActivePort();const poll=setInterval(()=>void tryActivePort(),ACTIVE_PORT_POLL_MS);cleanups.push(()=>clearInterval(poll))}const timeout=setTimeout(()=>{finish(new Error(`browser did not expose a DevTools endpoint within ${timeoutMs}ms${stderr?`
1473
- ${stderr}`:""}`))},timeoutMs);cleanups.push(()=>clearTimeout(timeout))})}var RECORD_VERSION=2;var DEFAULT_REGISTRY_ROOT=path5.join(os.homedir(),".local","share","omowright","instances");var DEFAULT_PROBE_TIMEOUT_MS=1000;var DEFAULT_LAUNCH_TIMEOUT_MS=1e4;var DEFAULT_CONNECT_TIMEOUT_MS=1e4;var LOCK_TIMEOUT_MARGIN_MS=5000;var DEFAULT_LOCK_TIMEOUT_MS=resolveLockTimeoutMs({});var STALE_LOCK_GRACE_MS=DEFAULT_LOCK_TIMEOUT_MS;function normalizeName(name){if(typeof name!=="string"||!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)){throw new TypeError("instance name must start with a letter or number and contain only letters, numbers, dots, dashes, or underscores")}return name}function registryPaths(registryRoot,name){const root=path5.resolve(registryRoot??DEFAULT_REGISTRY_ROOT);return{root,recordPath:path5.join(root,`${name}.json`),lockPath:path5.join(root,`${name}.lock`)}}async function ensureRegistryRoot(root){await mkdir3(root,{recursive:true,mode:448});await chmod2(root,448)}function isRecord(value,expectedName){return value!==null&&typeof value==="object"&&value.version===RECORD_VERSION&&value.name===expectedName&&Number.isInteger(value.pid)&&value.pid>0&&typeof value.endpoint==="string"&&value.endpoint.length>0&&typeof value.webSocketDebuggerUrl==="string"&&value.webSocketDebuggerUrl.length>0&&(typeof value.profileDir==="string"||value.profileDir===null)&&typeof value.browserPath==="string"&&value.browserPath.length>0&&typeof value.startedAt==="string"&&!Number.isNaN(Date.parse(value.startedAt))}async function readRecord(recordPath,expectedName){let text;try{text=await readFile3(recordPath,"utf8")}catch(error){if(error?.code==="ENOENT")return{exists:false,record:null};throw error}try{const record=JSON.parse(text);return{exists:true,record:isRecord(record,expectedName)?record:null}}catch{return{exists:true,record:null}}}async function writeRecordAtomic(root,recordPath,record){const tempPath=path5.join(root,`.${record.name}.${process.pid}.${randomUUID3()}.tmp`);let handle;try{handle=await open3(tempPath,"wx",384);await handle.writeFile(`${JSON.stringify(record,null,2)}
1474
- `,"utf8");await handle.close();handle=null;await chmod2(tempPath,384);await rename2(tempPath,recordPath);await chmod2(recordPath,384)}catch(error){await handle?.close().catch(()=>{});await unlink3(tempPath).catch(()=>{});throw error}}function isConnectionRefused(error){for(let cause=error;cause;cause=cause?.cause){if(cause.code==="ECONNREFUSED"||cause.code==="ENOTFOUND"||cause.code==="ConnectionRefused"){return true}}return false}async function probeEndpoint(endpoint,timeoutMs){const versionUrl=new URL("/json/version",endpoint);const response=await fetch(versionUrl,{signal:AbortSignal.timeout(timeoutMs)});if(!response.ok){throw new Error(`CDP discovery request failed (${response.status} ${response.statusText})`)}const version=await response.json();if(typeof version?.webSocketDebuggerUrl!=="string"||version.webSocketDebuggerUrl.length===0){throw new Error("CDP discovery response lacks webSocketDebuggerUrl")}return version}function resolveConnectTimeoutMs(options){return options.connectTimeoutMs??DEFAULT_CONNECT_TIMEOUT_MS}function resolveLockTimeoutMs(options){if(options.lockTimeoutMs!==undefined)return options.lockTimeoutMs;return(options.launchTimeoutMs??DEFAULT_LAUNCH_TIMEOUT_MS)+(options.probeTimeoutMs??DEFAULT_PROBE_TIMEOUT_MS)+resolveConnectTimeoutMs(options)+LOCK_TIMEOUT_MARGIN_MS}function nameLockOptions(options){return{timeoutMs:resolveLockTimeoutMs(options),staleGraceMs:STALE_LOCK_GRACE_MS}}async function connectWithin(endpoint,options,timeoutMs){const pending=openConnection(endpoint,options);let timer;try{return await Promise.race([pending,new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error(`timed out after ${timeoutMs}ms opening a CDP session on ${endpoint}`)),timeoutMs)})])}catch(error){pending.then((connection)=>connection.close().catch(()=>{}),()=>{});throw error}finally{clearTimeout(timer)}}async function removeOwnedRecord(recordPath,name,pid){const loaded=await readRecord(recordPath,name);if(loaded.record?.pid!==pid)return;await unlinkIfPresent(recordPath)}function profileDirFromArgs(browserArgs){for(let index=0;index<browserArgs.length;index+=1){const argument=browserArgs[index];if(argument==="--user-data-dir"&&browserArgs[index+1]){return path5.resolve(browserArgs[index+1])}if(argument.startsWith("--user-data-dir=")){return path5.resolve(argument.slice("--user-data-dir=".length))}}return null}async function terminateBrowser(child){if(!child||child.exitCode!==null||child.signalCode!==null)return;const exited=new Promise((resolve)=>child.once("exit",resolve));child.kill("SIGTERM");let forceTimer;const force=new Promise((resolve)=>{forceTimer=setTimeout(()=>{if(child.exitCode===null&&child.signalCode===null){child.kill("SIGKILL")}resolve()},5000)});await Promise.race([exited,force]);clearTimeout(forceTimer);if(child.exitCode===null&&child.signalCode===null)await exited}function connectionOptions(options,profileDir){const result={...options.connectionOptions??{}};if(options.storageRoot!==undefined)result.storageRoot=options.storageRoot;else if(result.storageRoot===undefined&&profileDir){result.storageRoot=profileDir}if(options.client!==undefined){result.client={...result.client??{},...options.client}}if(options.dialogPolicy!==undefined)result.dialogPolicy=options.dialogPolicy;return result}async function launchNamed(name,{root,recordPath,lockPath},options){if(typeof options.browserPath!=="string"||options.browserPath.length===0){throw new TypeError(`connectNamed("${name}") requires browserPath when no healthy instance is registered`)}const browserArgs=[...options.browserArgs??[]];const profileDir=options.profileDir?path5.resolve(options.profileDir):profileDirFromArgs(browserArgs);const previousActivePort=await snapshotActivePort(profileDir);const child=spawn2(options.browserPath,[...browserArgs,"--remote-debugging-address=127.0.0.1","--remote-debugging-port=0"],{...options.spawnOptions??{},stdio:["ignore","ignore","pipe"]});let connection;try{const launchTimeoutMs=options.launchTimeoutMs??DEFAULT_LAUNCH_TIMEOUT_MS;const report=await discoverLaunchedEndpoint(child,{profileDir,timeoutMs:launchTimeoutMs,previousActivePort});const{endpoint}=report;const{webSocketDebuggerUrl}=await probeEndpoint(endpoint,launchTimeoutMs);assertLaunchedBrowser(child,report,webSocketDebuggerUrl);connection=await connectWithin(endpoint,connectionOptions(options,profileDir),resolveConnectTimeoutMs(options));const record={version:RECORD_VERSION,name,pid:child.pid,endpoint,webSocketDebuggerUrl,profileDir,browserPath:path5.resolve(options.browserPath),startedAt:new Date().toISOString()};await writeRecordAtomic(root,recordPath,record);connection.browserProcess=child;const disconnect=connection.close.bind(connection);connection.close=async()=>{let failure;try{await disconnect()}catch(error){failure=error}await terminateBrowser(child);try{await withLock(lockPath,nameLockOptions(options),()=>removeOwnedRecord(recordPath,name,child.pid))}catch(error){failure??=error}if(failure)throw failure};return connection}catch(error){await connection?.close().catch(()=>{});await terminateBrowser(child);throw error}}async function withEngineLock(registryRoot,engineKey,run){const root=path5.resolve(registryRoot??DEFAULT_REGISTRY_ROOT);await ensureRegistryRoot(root);const digest=createHash("sha256").update(path5.resolve(engineKey)).digest("hex").slice(0,16);const lockPath=path5.join(root,`_engine-${digest}.lock`);return withLock(lockPath,{timeoutMs:DEFAULT_LOCK_TIMEOUT_MS,staleGraceMs:STALE_LOCK_GRACE_MS},run)}async function connectNamed(name,options={}){const normalizedName=normalizeName(name);const paths=registryPaths(options.registryRoot,normalizedName);await ensureRegistryRoot(paths.root);return withLock(paths.lockPath,nameLockOptions(options),()=>attachOrLaunch(normalizedName,paths,options))}async function attachOrLaunch(normalizedName,paths,options){const loaded=await readRecord(paths.recordPath,normalizedName);if(loaded.record){await chmod2(paths.recordPath,384);if(isPidAlive(loaded.record.pid)){let version=null;try{version=await probeEndpoint(loaded.record.endpoint,options.probeTimeoutMs??DEFAULT_PROBE_TIMEOUT_MS)}catch(error){if(!isConnectionRefused(error)){throw new Error(`unresponsive holder "${normalizedName}" pid ${loaded.record.pid} endpoint ${loaded.record.endpoint}: ${error.message}`,{cause:error})}}if(version?.webSocketDebuggerUrl===loaded.record.webSocketDebuggerUrl){const{webSocketDebuggerUrl}=loaded.record;const timeoutMs=resolveConnectTimeoutMs(options);try{return await acquireSharedConnection(webSocketDebuggerUrl,connectionOptions(options,loaded.record.profileDir),{open:(shared)=>connectWithin(webSocketDebuggerUrl,shared,timeoutMs),timeoutMs})}catch(error){throw new Error(`unresponsive holder "${normalizedName}" pid ${loaded.record.pid} endpoint ${loaded.record.endpoint}: ${error.message}`,{cause:error})}}}}if(loaded.exists){await unlink3(paths.recordPath).catch((error)=>{if(error?.code!=="ENOENT")throw error})}return await launchNamed(normalizedName,paths,options)}async function listInstances(options={}){const root=path5.resolve(options.registryRoot??DEFAULT_REGISTRY_ROOT);await ensureRegistryRoot(root);const entries=await readdir2(root,{withFileTypes:true});const records=await Promise.all(entries.filter((entry)=>entry.isFile()&&entry.name.endsWith(".json")).map(async(entry)=>{const expectedName=entry.name.slice(0,-".json".length);const recordPath=path5.join(root,entry.name);const loaded=await readRecord(recordPath,expectedName);if(!loaded.record)return null;await chmod2(recordPath,384);const alive=isPidAlive(loaded.record.pid);let responsive=false;if(alive){responsive=await probeEndpoint(loaded.record.endpoint,options.probeTimeoutMs??DEFAULT_PROBE_TIMEOUT_MS).then((version)=>version.webSocketDebuggerUrl===loaded.record.webSocketDebuggerUrl,()=>false)}return{...loaded.record,alive,responsive}}));return records.filter((record)=>record!==null).sort((left,right)=>left.name.localeCompare(right.name))}import{randomInt}from"node:crypto";import{chmod as chmod3,mkdir as mkdir4,open as open4,readFile as readFile4}from"node:fs/promises";import{globSync}from"node:fs";import os2 from"node:os";import path6 from"node:path";var LEGACY_CLOAK_PROBE_URL="http://127.0.0.1:9242";var LEGACY_PROBE_TIMEOUT_MS=1500;var MIN_FINGERPRINT_SEED=1e4;var MAX_FINGERPRINT_SEED=99999;var METADATA_FILE=".omowright-cloak.json";var DEFAULT_PROFILE_DIR=path6.join(os2.homedir(),".local","share","omowright-cloak");function normalizeFingerprintSeed(value){const seed=typeof value==="string"&&value.trim()!==""?Number(value):value;if(!Number.isInteger(seed)||seed<MIN_FINGERPRINT_SEED||seed>MAX_FINGERPRINT_SEED){throw new RangeError(`fingerprint seed must be an integer from ${MIN_FINGERPRINT_SEED} to ${MAX_FINGERPRINT_SEED}`)}return seed}function platformFingerprint(){return process.platform==="darwin"?"macos":"windows"}function identityFlag(value){return/^(--user-data-dir|--fingerprint|--fingerprint-platform)(?:=|$)/.test(value)}function buildCloakBrowserArgs({profileDir,fingerprintSeed,extraArgs=[]}={}){const profile=path6.resolve(profileDir??DEFAULT_PROFILE_DIR);const seed=normalizeFingerprintSeed(fingerprintSeed);const conflicting=extraArgs.filter(identityFlag);if(conflicting.length>0){throw new Error(`extraArgs cannot override fixed profile identity: ${conflicting.join(", ")}`)}return["--no-first-run","--no-default-browser-check","--disable-session-crashed-bubble",`--user-data-dir=${profile}`,`--fingerprint=${seed}`,`--fingerprint-platform=${platformFingerprint()}`,...extraArgs]}async function readMetadata(metadataPath){try{const parsed=JSON.parse(await readFile4(metadataPath,"utf8"));return normalizeFingerprintSeed(parsed.fingerprintSeed)}catch(error){if(error?.code==="ENOENT")return null;if(error instanceof SyntaxError){throw new Error(`invalid CloakBrowser profile metadata: ${metadataPath}`)}throw error}}async function writeMetadata(metadataPath,fingerprintSeed){const handle=await open4(metadataPath,"wx",384);try{await handle.writeFile(`${JSON.stringify({fingerprintSeed},null,2)}
1475
- `,"utf8")}finally{await handle.close()}await chmod3(metadataPath,384)}async function resolveCloakProfile({profileDir=DEFAULT_PROFILE_DIR,fingerprintSeed,randomSeed=()=>randomInt(MIN_FINGERPRINT_SEED,MAX_FINGERPRINT_SEED+1)}={}){const resolvedProfile=path6.resolve(profileDir);const metadataPath=path6.join(resolvedProfile,METADATA_FILE);await mkdir4(resolvedProfile,{recursive:true,mode:448});await chmod3(resolvedProfile,448);const requested=fingerprintSeed===undefined?null:normalizeFingerprintSeed(fingerprintSeed);let stored=await readMetadata(metadataPath);if(stored===null){const candidate=requested??normalizeFingerprintSeed(randomSeed());try{await writeMetadata(metadataPath,candidate);stored=candidate}catch(error){if(error?.code!=="EEXIST")throw error;stored=await readMetadata(metadataPath);if(stored===null){throw new Error(`CloakBrowser profile seed metadata disappeared: ${metadataPath}`)}}}if(requested!==null&&requested!==stored){throw new Error(`fingerprint seed mismatch: profile is fixed at ${stored}, requested ${requested}`)}await chmod3(metadataPath,384);return{profileDir:resolvedProfile,metadataPath,fingerprintSeed:stored,browserArgs:buildCloakBrowserArgs({profileDir:resolvedProfile,fingerprintSeed:stored})}}function findCloakBrowserPath({browserRoot=path6.join(os2.homedir(),".cloakbrowser")}={}){const candidates=globSync(path6.join(path6.resolve(browserRoot),"chromium-*","Chromium.app","Contents","MacOS","Chromium")).sort();const browserPath=candidates.at(-1);if(!browserPath){throw new Error(`CloakBrowser binary not found under ${path6.resolve(browserRoot)}; set CLOAKBROWSER_BIN`)}return browserPath}async function assertCloakLaunchClear({browserPath,profileDir,registryRoot,legacyProbeUrl=LEGACY_CLOAK_PROBE_URL,probeTimeoutMs}){const records=await listInstances({registryRoot,probeTimeoutMs});for(const record of records){if(!record.alive||!record.responsive)continue;if(!record.profileDir)continue;if(path6.resolve(record.profileDir)===path6.resolve(profileDir))continue;if(path6.resolve(record.browserPath)!==path6.resolve(browserPath))continue;throw new Error(`CloakBrowser allows one instance: "${record.name}" (pid ${record.pid}, profile ${record.profileDir}) is live — connectNamed("${record.name}") to reuse it or close it first`)}const legacyHost=new URL(legacyProbeUrl).host;const explainedByRecord=records.some((record)=>record.alive&&record.responsive&&new URL(record.endpoint).host===legacyHost);if(explainedByRecord)return;try{const response=await fetch(new URL("/json/version",legacyProbeUrl),{signal:AbortSignal.timeout(probeTimeoutMs??LEGACY_PROBE_TIMEOUT_MS)});if(response.ok){throw new Error(`an unregistered CloakBrowser is answering on ${legacyProbeUrl} — attach with connect("${legacyProbeUrl}") or close it before launching`)}}catch(error){const unreachable=error instanceof TypeError||error?.name==="TimeoutError"||error?.name==="AbortError";if(!unreachable)throw error}}async function connectCloakProfile({browserPath=process.env.CLOAKBROWSER_BIN??findCloakBrowserPath(),profileDir=process.env.CLOAK_PROFILE_DIR??DEFAULT_PROFILE_DIR,fingerprintSeed=process.env.CLOAK_FINGERPRINT_SEED,browserArgs=[],storageRoot,name,...options}={}){const profile=await resolveCloakProfile({profileDir,fingerprintSeed});const resolvedBrowserArgs=buildCloakBrowserArgs({profileDir:profile.profileDir,fingerprintSeed:profile.fingerprintSeed,extraArgs:browserArgs});if(name===undefined){return connectPipe({browserPath,browserArgs:resolvedBrowserArgs,storageRoot:storageRoot??profile.profileDir,...options})}const{legacyProbeUrl,...connectOptions}=options;return withEngineLock(connectOptions.registryRoot,browserPath,async()=>{await assertCloakLaunchClear({browserPath,profileDir:profile.profileDir,registryRoot:connectOptions.registryRoot,legacyProbeUrl,probeTimeoutMs:connectOptions.probeTimeoutMs});return connectNamed(name,{browserPath,browserArgs:resolvedBrowserArgs,profileDir:profile.profileDir,storageRoot:storageRoot??profile.profileDir,...connectOptions})})}var SNAPSHOT_PARSE_ERROR="compactSnapshot expected a snapshot() result (JSON string or {tree, refs} object)";function compactSnapshot(snapshot){const obj=typeof snapshot==="string"?JSON.parse(snapshot):snapshot;if(!obj||typeof obj.tree!=="string")throw new TypeError(SNAPSHOT_PARSE_ERROR);return obj.tree}function snapshotTokens(snapshot){const text=typeof snapshot==="string"?snapshot:JSON.stringify(snapshot);return{bytes:Buffer.byteLength(text,"utf8"),estTokens:Math.ceil(Buffer.byteLength(text,"utf8")/4)}}var toolSchemas=[{name:"newTab",description:"Open a tab and return its page. Waits for interactivity.",parameters:{type:"object",properties:{url:{type:"string"}},required:["url"]}},{name:"attachPage",description:"Attach to an existing tab by targetId from listTargets().",parameters:{type:"object",properties:{targetId:{type:"string"}},required:["targetId"]}},{name:"page.goto",description:"Navigate the page; resolves when the page looks interactive.",parameters:{type:"object",properties:{url:{type:"string"}},required:["url"]}},{name:"page.snapshot",description:"PRIMARY page read. Returns a compact a11y tree; interactive elements carry [ref=eN] ids for page.locator. Options: maxDepth, maxChars, interactive, showHidden, selector, ref.",parameters:{type:"object",properties:{options:{type:"object"}}}},{name:"page.locator",description:"Resolve a ref id (e.g. 'e1') or CSS selector to a Locator: click, fill, press, hover, check, selectOption, dragTo, setInputFiles, textContent, isVisible, waitFor.",parameters:{type:"object",properties:{selectorOrRef:{type:"string"}},required:["selectorOrRef"]}},{name:"page.evaluate",description:"Run JS in the page and return the result.",parameters:{type:"object",properties:{expression:{type:"string"}},required:["expression"]}},{name:"page.screenshot",description:"Capture page image bytes (path, fullPage, clip, type, quality).",parameters:{type:"object",properties:{options:{type:"object"}}}},{name:"page.pdf",description:"Print the page to PDF bytes (path, format, margin, printBackground).",parameters:{type:"object",properties:{options:{type:"object"}}}},{name:"compactSnapshot",description:"Pass page.snapshot() output through this before reading: returns the raw tree without the refs map (refs resolve in-page; the map is ~54% of bytes on real pages).",parameters:{type:"object",properties:{snapshot:{}},required:["snapshot"]}},{name:"createCua",description:"Coordinate fallback for UI that refs cannot target: click/doubleClick/drag/move/scroll/type/keypress at viewport points, getVisibleScreenshot() as base64 PNG.",parameters:{type:"object",properties:{page:{}},required:["page"]}},{name:"createCaptcha",description:"Captcha helpers: click(bounds) and drag(from,to) return a post-action snapshot tree; readText(bounds?) OCRs a screenshot region (macOS Vision default, custom ocr injectable).",parameters:{type:"object",properties:{page:{},options:{type:"object"}},required:["page"]}},{name:"createChromeApi",description:"Chrome MV3-shaped APIs: tabs.query/get, windows.*, bookmarks/history/topSites (read-only from profile files), downloads.search/download. Writes throw UnsupportedOperationError.",parameters:{type:"object",properties:{connection:{},options:{type:"object"}},required:["connection"]}},{name:"injectCookies",description:"Inject raw cookie exports into a page with CDP sanitization handled (negative expires dropped, __Host- forced secure+root path+url-scoped, SameSite=None dropped when not secure, name+domain deduped).",parameters:{type:"object",properties:{page:{},cookies:{type:"array"}},required:["page","cookies"]}},{name:"createNetworkSnoop",description:"Buffer a page's CDP network traffic and return {pop,peek,popJson,waitFor,summary,dispose}; match is a predicate or {url,method,mimeType,resourceType}.",parameters:{type:"object",properties:{page:{},match:{},bodies:{type:"boolean"},maxEntries:{type:"number"},maxBodyBytes:{type:"number"}},required:["page"]}},{name:"snoop.waitFor",description:"Wait until a finished entry matches a predicate or {url,method,mimeType,resourceType}; timeoutMs defaults to 30000.",parameters:{type:"object",properties:{match:{},timeoutMs:{type:"number"}}}},{name:"snoop.popJson",description:"Drain the buffer and parse JSON bodies; skips non-JSON mime types and payloads that fail to parse.",parameters:{type:"object",properties:{}}},{name:"snoop.summary",description:"Return a compact text view of buffered entries (method, status, mimeType, size, url) capped by {max}.",parameters:{type:"object",properties:{max:{type:"number"}}}},{name:"collectWhileScrolling",description:"Async generator that scrolls the page, drains snoop.popJson(), and yields extracted items until minItems, maxScrolls, or noGrowth.",parameters:{type:"object",properties:{page:{},snoop:{},minItems:{type:"number"},maxScrolls:{type:"number"},extract:{},scroll:{type:"string",enum:["wheel","end"]},settleMs:{type:"number"}},required:["page","snoop"]}},{name:"createTrace",description:"Start a page flight recorder that writes dir/trace.jsonl, dir/trace.har, dir/summary.json and screenshots; never sends Runtime.enable.",parameters:{type:"object",properties:{page:{},dir:{type:"string"},screenshots:{type:"boolean"},network:{type:"boolean"},console:{type:"boolean"},maxBodyBytes:{type:"number"},events:{}},required:["page","dir"]}},{name:"trace.step",description:"Run named fn as a traced step with optional before/after screenshots; records the error and rethrows on failure.",parameters:{type:"object",properties:{name:{type:"string"},fn:{}},required:["name","fn"]}},{name:"trace.stop",description:"Flush listeners, write summary.json and trace.har, dispose the snoop, and return the summary for dir.",parameters:{type:"object",properties:{}}},{name:"reconcileFrames",description:"Adopt OOPIF iframe targets missing from FrameManager so later snapshots include them; returns the frames that were added.",parameters:{type:"object",properties:{page:{}},required:["page"]}},{name:"snapshotWithFrames",description:"Reconcile missing OOPIF frames then snapshot; refs stay page-level: page.locator(ref), never frame.locator.",parameters:{type:"object",properties:{page:{},options:{type:"object"}},required:["page"]}},{name:"connectPipe.dialogPolicy",description:"Wire connectPipe({dialogPolicy}), connect(url, {dialogPolicy}) and setDialogPolicy to {accept,promptText} or (dialog)=>boolean|{accept,promptText}; a throwing policy accepts.",parameters:{type:"object",properties:{accept:{type:"boolean"},promptText:{type:"string"}}}},{name:"requestHuman",description:"Pause for a human: inject a shadow-DOM Done banner, bringToFront first, and remove the banner on every path; until is {url}|{selector}|async predicate.",parameters:{type:"object",properties:{page:{},prompt:{type:"string"},until:{},timeoutMs:{type:"number"},pollMs:{type:"number"},signal:{}},required:["page"]}},{name:"describeLayers",description:"Sample a viewport grid and return {blocking,candidates,viewport} so overlay coverage and selectorHint are visible.",parameters:{type:"object",properties:{page:{},grid:{type:"number"}},required:["page"]}},{name:"snapshotWithLayers",description:"Prepend a @layers header to the snapshot tree and attach a layers field so overlays are visible before the a11y tree.",parameters:{type:"object",properties:{page:{},options:{type:"object"}},required:["page"]}},{name:"emulate",description:"Apply a DEVICE_PRESETS name, a custom metrics object, or null to restore the original UA captured on first call.",parameters:{type:"object",properties:{page:{},preset:{}},required:["page","preset"]}},{name:"createRoutes",description:"Install page request interception; Fetch.enable is sent only on the first route(); interception is fingerprintable, keep it off by default.",parameters:{type:"object",properties:{page:{}},required:["page"]}},{name:"route.continue",description:"Continue an intercepted request, optionally overriding url, method, headers, or postData.",parameters:{type:"object",properties:{url:{type:"string"},method:{type:"string"},headers:{type:"object"},postData:{}}}},{name:"route.fulfill",description:"Fulfill an intercepted request with {status,headers,contentType,body} instead of hitting the network.",parameters:{type:"object",properties:{status:{type:"number"},headers:{type:"object"},contentType:{type:"string"},body:{}}}},{name:"route.abort",description:"Abort an intercepted request with a CDP errorReason (defaults to Failed).",parameters:{type:"object",properties:{reason:{type:"string"}}}}];var MODIFIER_KEYS2=new Set(["Alt","Control","Meta","Shift"]);function splitModifiers(keys=[]){const modifiers=keys.filter((key)=>MODIFIER_KEYS2.has(key)||/^(cmd|command|ctrl|option|controlormeta)$/i.test(key));const rest=keys.filter((key)=>!modifiers.includes(key));return{modifiers,rest}}function createCua(page){async function holdModifiers(keys,fn){const{modifiers}=splitModifiers(keys);for(const key of modifiers)await page.keyboard.down(key);try{return await fn()}finally{for(const key of[...modifiers].reverse())await page.keyboard.up(key)}}return{page,async click({x,y,button="left",keypress}){await holdModifiers(keypress,()=>page.mouse.click(x,y,{button}))},async doubleClick({x,y,keypress}){await holdModifiers(keypress,()=>page.mouse.click(x,y,{clickCount:2}))},async drag({path,keys}){if(!Array.isArray(path)||path.length<2){throw new TypeError("cua.drag requires path with at least two points")}await holdModifiers(keys,async()=>{const[first,...rest]=path;await page.mouse.move(first.x,first.y);await page.mouse.down();try{for(const point of rest)await page.mouse.move(point.x,point.y)}finally{await page.mouse.up()}})},async getVisibleScreenshot(){const buffer=await page.screenshot({type:"png"});return buffer.toString("base64")},async keypress({keys}){if(!Array.isArray(keys)||keys.length===0){throw new TypeError("cua.keypress requires a non-empty keys array")}await page.keyboard.press(keys.join("+"))},async move({x,y,keys}){await holdModifiers(keys,()=>page.mouse.move(x,y))},async scroll({x,y,scrollX,scrollY,keypress}){await holdModifiers(keypress,async()=>{await page.mouse.move(x,y);await page.mouse.wheel(scrollX,scrollY)})},async type({text}){await page.keyboard.type(text)}}}import{spawn as spawn3}from"node:child_process";import{mkdtempSync,writeFileSync,rmSync}from"node:fs";import{tmpdir as tmpdir2}from"node:os";import path7 from"node:path";var MAX_TIMER_MS=2147483647;var STOP=Symbol("wait stopped");async function waitForCaptcha(page,{until,timeoutMs=1e4,pollMs=100,signal}={}){if(typeof until!=="function")throw new TypeError("until must be a function");if(!Number.isFinite(timeoutMs)||timeoutMs<0||timeoutMs>MAX_TIMER_MS){throw new RangeError("timeoutMs must be between 0 and 2147483647")}if(!Number.isFinite(pollMs)||pollMs<=0||pollMs>MAX_TIMER_MS){throw new RangeError("pollMs must be greater than 0 and at most 2147483647")}if(signal!==undefined&&(typeof signal?.aborted!=="boolean"||typeof signal.addEventListener!=="function"||typeof signal.removeEventListener!=="function")){throw new TypeError("signal must be AbortSignal-compatible")}const started=performance.now();const deadline=started+timeoutMs;const operation=new AbortController;let deadlineTimer;let pollTimer;let stop;const stopped=new Promise((resolve)=>{stop=()=>resolve(STOP)});const terminal=()=>signal?.aborted?"cancelled":performance.now()>=deadline?"timed_out":null;const result=(outcome)=>({outcome,elapsedMs:performance.now()-started});const scheduleDeadline=()=>{const remaining=deadline-performance.now();if(remaining<=0)stop();else deadlineTimer=setTimeout(scheduleDeadline,Math.ceil(remaining))};try{const initial=terminal();if(initial)return result(initial);signal?.addEventListener("abort",stop,{once:true});scheduleDeadline();for(;;){let accepted;try{accepted=await Promise.race([Promise.resolve().then(()=>terminal()?STOP:until(page,{signal:operation.signal})),stopped])}catch(error){const outcome=terminal();if(outcome)return result(outcome);throw error}const outcome=terminal();if(outcome)return result(outcome);if(accepted===true)return result("matched");await Promise.race([new Promise((resolve)=>{pollTimer=setTimeout(resolve,Math.ceil(Math.min(pollMs,deadline-performance.now())))}),stopped]);clearTimeout(pollTimer);const afterDelay=terminal();if(afterDelay)return result(afterDelay)}}finally{clearTimeout(deadlineTimer);clearTimeout(pollTimer);signal?.removeEventListener("abort",stop);operation.abort();stop()}}var DEFAULT_DRAG_STEPS=20;var POST_ACTION_SETTLE_MS=3000;function validatePoint(point){if(!Number.isFinite(point?.x)||!Number.isFinite(point?.y)){throw new TypeError("point coordinates must be finite numbers")}}function validateSteps(steps){if(!Number.isSafeInteger(steps)||steps<=0){throw new RangeError("steps must be a positive safe integer")}}function validateSettleMs(value){if(!Number.isFinite(value)||value<0||value>2147483647){throw new RangeError("settleMs must be between 0 and 2147483647")}}var MACOS_VISION_OCR_SWIFT=`
1472
+ ${tail}`:""))}async send(method,params,sessionId,opts={}){if(method==="Target.createTarget"&&opts.capability!==targetCreationCapability)throw Error("Target.createTarget is reserved for createAgentTabs");if(String(method).startsWith("OmO.")){throw Error(`Extension commands are unavailable over pipe transport: ${method}`)}await this.ensureConnected();const id=this.#seq++;const msg={id,method};if(params!==undefined)msg.params=params;if(sessionId)msg.sessionId=sessionId;const{promise,resolve,reject}=Promise.withResolvers();const timeoutMs=opts.timeoutMs??this.#commandTimeoutMs;const timeout=timeoutMs>0?setTimeout(()=>{this.#pending.delete(id);reject(Error(`CDP command timeout: ${method}`))},timeoutMs):undefined;this.#pending.set(id,{method,resolve,reject,timeout});this.#writeStream.write(JSON.stringify(msg)+"\x00");return promise}on(method,fn){return this.#events.on(method,({data})=>fn(data.payload,data.meta))}async discoverTargets(){const{targetInfos}=await this.send("Target.getTargets");return targetInfos.map((t)=>({id:t.targetId,type:t.type,url:t.url??"",title:t.title??"",description:t.description,faviconUrl:t.faviconUrl,parentId:t.openerId}))}async close(){const child=this.#child;this.#child=undefined;if(child&&child.exitCode===null){const exited=new Promise((resolve)=>child.once("exit",resolve));if(this.#connected){await Promise.race([this.send("Browser.close",undefined,undefined,{timeoutMs:GRACEFUL_CLOSE_TIMEOUT_MS}).catch(()=>{}),exited])}this.#connected=false;this.#failAll(Error("CDP pipe client closed"));if(child.exitCode===null){await Promise.race([exited,new Promise((resolve)=>setTimeout(resolve,GRACEFUL_CLOSE_TIMEOUT_MS))])}await terminateChild(child);return}this.#connected=false;this.#failAll(Error("CDP pipe client closed"))}#onData(chunk){this.#buffer=Buffer.concat([this.#buffer,chunk]);let idx;while((idx=this.#buffer.indexOf(0))!==-1){const raw=this.#buffer.subarray(0,idx).toString("utf8");this.#buffer=this.#buffer.subarray(idx+1);if(raw.length===0)continue;let msg;try{msg=JSON.parse(raw)}catch{continue}this._handleTransportMessage(msg)}}_handleTransportMessage(msg){if(typeof msg.id==="number"){const entry=this.#pending.get(msg.id);if(!entry)return;this.#pending.delete(msg.id);if(entry.timeout)clearTimeout(entry.timeout);if(msg.error){const err=Error(msg.error.message??`CDP command failed: ${entry.method}`);err.code=msg.error.code;err.data=msg.error.data;entry.reject(err)}else{entry.resolve(msg.result)}return}if(typeof msg.method==="string"){const handled=msg.method==="Page.javascriptDialogOpening"&&msg.sessionId?this.#handleJavaScriptDialog(msg).catch(()=>{}):undefined;this.#events.emit(msg.method,{data:{payload:msg.params,meta:{sessionId:msg.sessionId}}}).catch(()=>{});return handled}}async#handleJavaScriptDialog(msg){const params=await resolveDialogAction(this.#dialogPolicy,msg.params);await this.send("Page.handleJavaScriptDialog",params,msg.sessionId)}#failAll(err){for(const entry of this.#pending.values()){if(entry.timeout)clearTimeout(entry.timeout);entry.reject(err)}this.#pending.clear()}}async function connectPipe({browserPath,browserArgs=[],spawnOptions,storageRoot,commandTimeoutMs,readinessTimeoutMs,logId,dialogPolicy}={}){const client=new PipeCdpClient({browserPath,browserArgs,spawnOptions,commandTimeoutMs,readinessTimeoutMs,logId,dialogPolicy});await client.ensureConnected();const connection=new BrowserConnection(client,{storageRoot});await connection.initialize();connection.browserProcess=client.childProcess;return connection}import{spawn as spawn2}from"node:child_process";import{createHash,randomUUID as randomUUID3}from"node:crypto";import{chmod as chmod2,mkdir as mkdir3,open as open3,readFile as readFile3,readdir as readdir2,rename as rename2,unlink as unlink3}from"node:fs/promises";import os from"node:os";import path6 from"node:path";import{watch as watch2}from"node:fs";import{readFile as readFile2,stat as stat2}from"node:fs/promises";import path5 from"node:path";var ACTIVE_PORT_POLL_MS=100;function httpEndpointFromWebSocket(webSocketUrl){const endpoint=new URL(webSocketUrl);endpoint.protocol=endpoint.protocol==="wss:"?"https:":"http:";endpoint.pathname="/";endpoint.search="";endpoint.hash="";return endpoint.toString().replace(/\/$/,"")}function parseActivePort(text){const[portLine,browserPath]=text.trim().split(/\r?\n/);const port=Number(portLine);if(!Number.isInteger(port)||port<1||port>65535)return null;return{endpoint:`http://127.0.0.1:${port}`,browserPath:browserPath?.trim()||null}}function activePortIdentity(info){return{ino:info.ino,mtimeMs:info.mtimeMs,size:info.size}}async function snapshotActivePort(profileDir){if(!profileDir)return null;try{return activePortIdentity(await stat2(path5.join(profileDir,"DevToolsActivePort")))}catch(error){if(error?.code==="ENOENT")return null;throw error}}function isSameFile(info,previous){return previous!==null&&info.ino===previous.ino&&info.mtimeMs===previous.mtimeMs&&info.size===previous.size}function assertLaunchedBrowser(child,report,webSocketDebuggerUrl){if(child.exitCode!==null||child.signalCode!==null){throw new Error(`browser exited before its DevTools endpoint ${report.endpoint} could be verified`)}const answered=new URL(webSocketDebuggerUrl).pathname;if(report.browserPath===null||answered!==report.browserPath){throw new Error(`DevTools endpoint ${report.endpoint} answers as ${answered}, not as the launched browser (${report.browserPath??"no browser id reported"})`)}}async function discoverLaunchedEndpoint(child,{profileDir,timeoutMs,previousActivePort=null}={}){const activePortPath=profileDir?path5.join(profileDir,"DevToolsActivePort"):null;return new Promise((resolve,reject)=>{let settled=false;let stderr="";let activePortWatcher;let activePortReading=false;let activePortChangedWhileReading=false;const cleanups=[];const finish=(error,endpoint)=>{if(settled)return;settled=true;for(const cleanup of cleanups.splice(0))cleanup();activePortWatcher?.close();if(error)reject(error);else resolve(endpoint)};const readActivePort=async()=>{const info=await stat2(activePortPath);if(isSameFile(activePortIdentity(info),previousActivePort))return;const report=parseActivePort(await readFile2(activePortPath,"utf8"));if(report)finish(null,report)};const tryActivePort=async()=>{if(!activePortPath||settled)return;if(activePortReading){activePortChangedWhileReading=true;return}activePortReading=true;try{do{activePortChangedWhileReading=false;await readActivePort()}while(activePortChangedWhileReading&&!settled)}catch(error){if(error?.code!=="ENOENT")finish(error)}finally{activePortReading=false}};const onStderr=(chunk)=>{stderr=`${stderr}${chunk}`.slice(-16384);const match=/DevTools listening on (ws:\/\/\S+)/.exec(stderr);if(match){finish(null,{endpoint:httpEndpointFromWebSocket(match[1]),browserPath:new URL(match[1]).pathname})}else tryActivePort()};const onError=(error)=>finish(error);const onExit=(code,signal)=>{finish(new Error(`browser exited before exposing DevTools (code ${code}, signal ${signal??"none"})${stderr?`
1473
+ ${stderr}`:""}`))};child.stderr.setEncoding("utf8");child.stderr.on("data",onStderr);child.once("error",onError);child.once("exit",onExit);cleanups.push(()=>child.stderr.off("data",onStderr));cleanups.push(()=>child.off("error",onError));cleanups.push(()=>child.off("exit",onExit));if(activePortPath){try{activePortWatcher=watch2(profileDir,(event,filename)=>{if((event==="rename"||event==="change")&&(!filename||filename.toString()===path5.basename(activePortPath))){tryActivePort()}})}catch(error){if(error?.code!=="ENOENT")finish(error)}tryActivePort();const poll=setInterval(()=>void tryActivePort(),ACTIVE_PORT_POLL_MS);cleanups.push(()=>clearInterval(poll))}const timeout=setTimeout(()=>{finish(new Error(`browser did not expose a DevTools endpoint within ${timeoutMs}ms${stderr?`
1474
+ ${stderr}`:""}`))},timeoutMs);cleanups.push(()=>clearTimeout(timeout))})}var RECORD_VERSION=2;var DEFAULT_REGISTRY_ROOT=path6.join(os.homedir(),".local","share","omowright","instances");var DEFAULT_PROBE_TIMEOUT_MS=1000;var DEFAULT_LAUNCH_TIMEOUT_MS=1e4;var DEFAULT_CONNECT_TIMEOUT_MS=1e4;var LOCK_TIMEOUT_MARGIN_MS=5000;var DEFAULT_LOCK_TIMEOUT_MS=resolveLockTimeoutMs({});var STALE_LOCK_GRACE_MS=DEFAULT_LOCK_TIMEOUT_MS;function normalizeName(name){if(typeof name!=="string"||!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)){throw new TypeError("instance name must start with a letter or number and contain only letters, numbers, dots, dashes, or underscores")}return name}function registryPaths(registryRoot,name){const root=path6.resolve(registryRoot??DEFAULT_REGISTRY_ROOT);return{root,recordPath:path6.join(root,`${name}.json`),lockPath:path6.join(root,`${name}.lock`)}}async function ensureRegistryRoot(root){await mkdir3(root,{recursive:true,mode:448});await chmod2(root,448)}function isRecord(value,expectedName){return value!==null&&typeof value==="object"&&value.version===RECORD_VERSION&&value.name===expectedName&&Number.isInteger(value.pid)&&value.pid>0&&typeof value.endpoint==="string"&&value.endpoint.length>0&&typeof value.webSocketDebuggerUrl==="string"&&value.webSocketDebuggerUrl.length>0&&(typeof value.profileDir==="string"||value.profileDir===null)&&typeof value.browserPath==="string"&&value.browserPath.length>0&&typeof value.startedAt==="string"&&!Number.isNaN(Date.parse(value.startedAt))}async function readRecord(recordPath,expectedName){let text;try{text=await readFile3(recordPath,"utf8")}catch(error){if(error?.code==="ENOENT")return{exists:false,record:null};throw error}try{const record=JSON.parse(text);return{exists:true,record:isRecord(record,expectedName)?record:null}}catch{return{exists:true,record:null}}}async function writeRecordAtomic(root,recordPath,record){const tempPath=path6.join(root,`.${record.name}.${process.pid}.${randomUUID3()}.tmp`);let handle;try{handle=await open3(tempPath,"wx",384);await handle.writeFile(`${JSON.stringify(record,null,2)}
1475
+ `,"utf8");await handle.close();handle=null;await chmod2(tempPath,384);await rename2(tempPath,recordPath);await chmod2(recordPath,384)}catch(error){await handle?.close().catch(()=>{});await unlink3(tempPath).catch(()=>{});throw error}}function isConnectionRefused(error){for(let cause=error;cause;cause=cause?.cause){if(cause.code==="ECONNREFUSED"||cause.code==="ENOTFOUND"||cause.code==="ConnectionRefused"){return true}}return false}async function probeEndpoint(endpoint,timeoutMs){const versionUrl=new URL("/json/version",endpoint);const response=await fetch(versionUrl,{signal:AbortSignal.timeout(timeoutMs)});if(!response.ok){throw new Error(`CDP discovery request failed (${response.status} ${response.statusText})`)}const version=await response.json();if(typeof version?.webSocketDebuggerUrl!=="string"||version.webSocketDebuggerUrl.length===0){throw new Error("CDP discovery response lacks webSocketDebuggerUrl")}return version}function resolveConnectTimeoutMs(options){return options.connectTimeoutMs??DEFAULT_CONNECT_TIMEOUT_MS}function resolveLockTimeoutMs(options){if(options.lockTimeoutMs!==undefined)return options.lockTimeoutMs;return(options.launchTimeoutMs??DEFAULT_LAUNCH_TIMEOUT_MS)+(options.probeTimeoutMs??DEFAULT_PROBE_TIMEOUT_MS)+resolveConnectTimeoutMs(options)+LOCK_TIMEOUT_MARGIN_MS}function nameLockOptions(options){return{timeoutMs:resolveLockTimeoutMs(options),staleGraceMs:STALE_LOCK_GRACE_MS}}async function connectWithin(endpoint,options,timeoutMs){const pending=openConnection(endpoint,options);let timer;try{return await Promise.race([pending,new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error(`timed out after ${timeoutMs}ms opening a CDP session on ${endpoint}`)),timeoutMs)})])}catch(error){pending.then((connection)=>connection.close().catch(()=>{}),()=>{});throw error}finally{clearTimeout(timer)}}async function removeOwnedRecord(recordPath,name,pid){const loaded=await readRecord(recordPath,name);if(loaded.record?.pid!==pid)return;await unlinkIfPresent(recordPath)}function connectionOptions(options,profileDir){const result={...options.connectionOptions??{}};if(options.storageRoot!==undefined)result.storageRoot=options.storageRoot;else if(result.storageRoot===undefined&&profileDir){result.storageRoot=profileDir}if(options.client!==undefined){result.client={...result.client??{},...options.client}}if(options.dialogPolicy!==undefined)result.dialogPolicy=options.dialogPolicy;return result}async function launchNamed(name,{root,recordPath,lockPath},options){if(typeof options.browserPath!=="string"||options.browserPath.length===0){throw new TypeError(`connectNamed("${name}") requires browserPath when no healthy instance is registered`)}const browserArgs=[...options.browserArgs??[]];const profileDir=options.profileDir?path6.resolve(options.profileDir):profileDirFromArgs(browserArgs);const previousActivePort=await snapshotActivePort(profileDir);const child=spawn2(options.browserPath,[...browserArgs,"--remote-debugging-address=127.0.0.1","--remote-debugging-port=0"],{...options.spawnOptions??{},stdio:["ignore","ignore","pipe"]});let connection;try{const launchTimeoutMs=options.launchTimeoutMs??DEFAULT_LAUNCH_TIMEOUT_MS;const report=await discoverLaunchedEndpoint(child,{profileDir,timeoutMs:launchTimeoutMs,previousActivePort});const{endpoint}=report;const{webSocketDebuggerUrl}=await probeEndpoint(endpoint,launchTimeoutMs);assertLaunchedBrowser(child,report,webSocketDebuggerUrl);connection=await connectWithin(endpoint,connectionOptions(options,profileDir),resolveConnectTimeoutMs(options));const record={version:RECORD_VERSION,name,pid:child.pid,endpoint,webSocketDebuggerUrl,profileDir,browserPath:path6.resolve(options.browserPath),startedAt:new Date().toISOString()};await writeRecordAtomic(root,recordPath,record);connection.browserProcess=child;const disconnect=connection.close.bind(connection);connection.close=async()=>{let failure;try{await disconnect()}catch(error){failure=error}await terminateChild(child);try{await withLock(lockPath,nameLockOptions(options),()=>removeOwnedRecord(recordPath,name,child.pid))}catch(error){failure??=error}if(failure)throw failure};return connection}catch(error){await connection?.close().catch(()=>{});await terminateChild(child);throw error}}async function withEngineLock(registryRoot,engineKey,run){const root=path6.resolve(registryRoot??DEFAULT_REGISTRY_ROOT);await ensureRegistryRoot(root);const digest=createHash("sha256").update(path6.resolve(engineKey)).digest("hex").slice(0,16);const lockPath=path6.join(root,`_engine-${digest}.lock`);return withLock(lockPath,{timeoutMs:DEFAULT_LOCK_TIMEOUT_MS,staleGraceMs:STALE_LOCK_GRACE_MS},run)}async function connectNamed(name,options={}){const normalizedName=normalizeName(name);const paths=registryPaths(options.registryRoot,normalizedName);await ensureRegistryRoot(paths.root);return withLock(paths.lockPath,nameLockOptions(options),()=>attachOrLaunch(normalizedName,paths,options))}async function attachOrLaunch(normalizedName,paths,options){const loaded=await readRecord(paths.recordPath,normalizedName);if(loaded.record){await chmod2(paths.recordPath,384);if(isPidAlive(loaded.record.pid)){let version=null;try{version=await probeEndpoint(loaded.record.endpoint,options.probeTimeoutMs??DEFAULT_PROBE_TIMEOUT_MS)}catch(error){if(!isConnectionRefused(error)){throw new Error(`unresponsive holder "${normalizedName}" pid ${loaded.record.pid} endpoint ${loaded.record.endpoint}: ${error.message}`,{cause:error})}}if(version?.webSocketDebuggerUrl===loaded.record.webSocketDebuggerUrl){const{webSocketDebuggerUrl}=loaded.record;const timeoutMs=resolveConnectTimeoutMs(options);try{return await acquireSharedConnection(webSocketDebuggerUrl,connectionOptions(options,loaded.record.profileDir),{open:(shared)=>connectWithin(webSocketDebuggerUrl,shared,timeoutMs),timeoutMs})}catch(error){throw new Error(`unresponsive holder "${normalizedName}" pid ${loaded.record.pid} endpoint ${loaded.record.endpoint}: ${error.message}`,{cause:error})}}}}if(loaded.exists){await unlink3(paths.recordPath).catch((error)=>{if(error?.code!=="ENOENT")throw error})}return await launchNamed(normalizedName,paths,options)}async function listInstances(options={}){const root=path6.resolve(options.registryRoot??DEFAULT_REGISTRY_ROOT);await ensureRegistryRoot(root);const entries=await readdir2(root,{withFileTypes:true});const records=await Promise.all(entries.filter((entry)=>entry.isFile()&&entry.name.endsWith(".json")).map(async(entry)=>{const expectedName=entry.name.slice(0,-".json".length);const recordPath=path6.join(root,entry.name);const loaded=await readRecord(recordPath,expectedName);if(!loaded.record)return null;await chmod2(recordPath,384);const alive=isPidAlive(loaded.record.pid);let responsive=false;if(alive){responsive=await probeEndpoint(loaded.record.endpoint,options.probeTimeoutMs??DEFAULT_PROBE_TIMEOUT_MS).then((version)=>version.webSocketDebuggerUrl===loaded.record.webSocketDebuggerUrl,()=>false)}return{...loaded.record,alive,responsive}}));return records.filter((record)=>record!==null).sort((left,right)=>left.name.localeCompare(right.name))}import{randomInt}from"node:crypto";import{chmod as chmod3,mkdir as mkdir4,open as open4,readFile as readFile4}from"node:fs/promises";import{globSync}from"node:fs";import os2 from"node:os";import path7 from"node:path";var LEGACY_CLOAK_PROBE_URL="http://127.0.0.1:9242";var LEGACY_PROBE_TIMEOUT_MS=1500;var MIN_FINGERPRINT_SEED=1e4;var MAX_FINGERPRINT_SEED=99999;var METADATA_FILE=".omowright-cloak.json";var DEFAULT_PROFILE_DIR=path7.join(os2.homedir(),".local","share","omowright-cloak");function normalizeFingerprintSeed(value){const seed=typeof value==="string"&&value.trim()!==""?Number(value):value;if(!Number.isInteger(seed)||seed<MIN_FINGERPRINT_SEED||seed>MAX_FINGERPRINT_SEED){throw new RangeError(`fingerprint seed must be an integer from ${MIN_FINGERPRINT_SEED} to ${MAX_FINGERPRINT_SEED}`)}return seed}function platformFingerprint(){return process.platform==="darwin"?"macos":"windows"}function identityFlag(value){return/^(--user-data-dir|--fingerprint|--fingerprint-platform)(?:=|$)/.test(value)}function buildCloakBrowserArgs({profileDir,fingerprintSeed,extraArgs=[]}={}){const profile=path7.resolve(profileDir??DEFAULT_PROFILE_DIR);const seed=normalizeFingerprintSeed(fingerprintSeed);const conflicting=extraArgs.filter(identityFlag);if(conflicting.length>0){throw new Error(`extraArgs cannot override fixed profile identity: ${conflicting.join(", ")}`)}return["--no-first-run","--no-default-browser-check","--disable-session-crashed-bubble",`--user-data-dir=${profile}`,`--fingerprint=${seed}`,`--fingerprint-platform=${platformFingerprint()}`,...extraArgs]}async function readMetadata(metadataPath){try{const parsed=JSON.parse(await readFile4(metadataPath,"utf8"));return normalizeFingerprintSeed(parsed.fingerprintSeed)}catch(error){if(error?.code==="ENOENT")return null;if(error instanceof SyntaxError){throw new Error(`invalid CloakBrowser profile metadata: ${metadataPath}`)}throw error}}async function writeMetadata(metadataPath,fingerprintSeed){const handle=await open4(metadataPath,"wx",384);try{await handle.writeFile(`${JSON.stringify({fingerprintSeed},null,2)}
1476
+ `,"utf8")}finally{await handle.close()}await chmod3(metadataPath,384)}async function resolveCloakProfile({profileDir=DEFAULT_PROFILE_DIR,fingerprintSeed,randomSeed=()=>randomInt(MIN_FINGERPRINT_SEED,MAX_FINGERPRINT_SEED+1)}={}){const resolvedProfile=path7.resolve(profileDir);const metadataPath=path7.join(resolvedProfile,METADATA_FILE);await mkdir4(resolvedProfile,{recursive:true,mode:448});await chmod3(resolvedProfile,448);const requested=fingerprintSeed===undefined?null:normalizeFingerprintSeed(fingerprintSeed);let stored=await readMetadata(metadataPath);if(stored===null){const candidate=requested??normalizeFingerprintSeed(randomSeed());try{await writeMetadata(metadataPath,candidate);stored=candidate}catch(error){if(error?.code!=="EEXIST")throw error;stored=await readMetadata(metadataPath);if(stored===null){throw new Error(`CloakBrowser profile seed metadata disappeared: ${metadataPath}`)}}}if(requested!==null&&requested!==stored){throw new Error(`fingerprint seed mismatch: profile is fixed at ${stored}, requested ${requested}`)}await chmod3(metadataPath,384);return{profileDir:resolvedProfile,metadataPath,fingerprintSeed:stored,browserArgs:buildCloakBrowserArgs({profileDir:resolvedProfile,fingerprintSeed:stored})}}function findCloakBrowserPath({browserRoot=path7.join(os2.homedir(),".cloakbrowser")}={}){const candidates=globSync(path7.join(path7.resolve(browserRoot),"chromium-*","Chromium.app","Contents","MacOS","Chromium")).sort();const browserPath=candidates.at(-1);if(!browserPath){throw new Error(`CloakBrowser binary not found under ${path7.resolve(browserRoot)}; set CLOAKBROWSER_BIN`)}return browserPath}async function assertCloakLaunchClear({browserPath,profileDir,registryRoot,legacyProbeUrl=LEGACY_CLOAK_PROBE_URL,probeTimeoutMs}){const records=await listInstances({registryRoot,probeTimeoutMs});for(const record of records){if(!record.alive||!record.responsive)continue;if(!record.profileDir)continue;if(path7.resolve(record.profileDir)===path7.resolve(profileDir))continue;if(path7.resolve(record.browserPath)!==path7.resolve(browserPath))continue;throw new Error(`CloakBrowser allows one instance: "${record.name}" (pid ${record.pid}, profile ${record.profileDir}) is live — connectNamed("${record.name}") to reuse it or close it first`)}const legacyHost=new URL(legacyProbeUrl).host;const explainedByRecord=records.some((record)=>record.alive&&record.responsive&&new URL(record.endpoint).host===legacyHost);if(explainedByRecord)return;try{const response=await fetch(new URL("/json/version",legacyProbeUrl),{signal:AbortSignal.timeout(probeTimeoutMs??LEGACY_PROBE_TIMEOUT_MS)});if(response.ok){throw new Error(`an unregistered CloakBrowser is answering on ${legacyProbeUrl} — attach with connect("${legacyProbeUrl}") or close it before launching`)}}catch(error){const unreachable=error instanceof TypeError||error?.name==="TimeoutError"||error?.name==="AbortError";if(!unreachable)throw error}}async function connectCloakProfile({browserPath=process.env.CLOAKBROWSER_BIN??findCloakBrowserPath(),profileDir=process.env.CLOAK_PROFILE_DIR??DEFAULT_PROFILE_DIR,fingerprintSeed=process.env.CLOAK_FINGERPRINT_SEED,browserArgs=[],storageRoot,name,...options}={}){const profile=await resolveCloakProfile({profileDir,fingerprintSeed});const resolvedBrowserArgs=buildCloakBrowserArgs({profileDir:profile.profileDir,fingerprintSeed:profile.fingerprintSeed,extraArgs:browserArgs});if(name===undefined){return connectPipe({browserPath,browserArgs:resolvedBrowserArgs,storageRoot:storageRoot??profile.profileDir,...options})}const{legacyProbeUrl,...connectOptions}=options;return withEngineLock(connectOptions.registryRoot,browserPath,async()=>{await assertCloakLaunchClear({browserPath,profileDir:profile.profileDir,registryRoot:connectOptions.registryRoot,legacyProbeUrl,probeTimeoutMs:connectOptions.probeTimeoutMs});return connectNamed(name,{browserPath,browserArgs:resolvedBrowserArgs,profileDir:profile.profileDir,storageRoot:storageRoot??profile.profileDir,...connectOptions})})}var SNAPSHOT_PARSE_ERROR="compactSnapshot expected a snapshot() result (JSON string or {tree, refs} object)";function compactSnapshot(snapshot){const obj=typeof snapshot==="string"?JSON.parse(snapshot):snapshot;if(!obj||typeof obj.tree!=="string")throw new TypeError(SNAPSHOT_PARSE_ERROR);return obj.tree}function snapshotTokens(snapshot){const text=typeof snapshot==="string"?snapshot:JSON.stringify(snapshot);return{bytes:Buffer.byteLength(text,"utf8"),estTokens:Math.ceil(Buffer.byteLength(text,"utf8")/4)}}var toolSchemas=[{name:"newTab",description:"Open a tab and return its page. Waits for interactivity.",parameters:{type:"object",properties:{url:{type:"string"}},required:["url"]}},{name:"attachPage",description:"Attach to an existing tab by targetId from listTargets().",parameters:{type:"object",properties:{targetId:{type:"string"}},required:["targetId"]}},{name:"page.goto",description:"Navigate the page; resolves when the page looks interactive.",parameters:{type:"object",properties:{url:{type:"string"}},required:["url"]}},{name:"page.snapshot",description:"PRIMARY page read. Returns a compact a11y tree; interactive elements carry [ref=eN] ids for page.locator. Options: maxDepth, maxChars, interactive, showHidden, selector, ref.",parameters:{type:"object",properties:{options:{type:"object"}}}},{name:"page.locator",description:"Resolve a ref id (e.g. 'e1') or CSS selector to a Locator: click, fill, press, hover, check, selectOption, dragTo, setInputFiles, textContent, isVisible, waitFor.",parameters:{type:"object",properties:{selectorOrRef:{type:"string"}},required:["selectorOrRef"]}},{name:"page.evaluate",description:"Run JS in the page and return the result.",parameters:{type:"object",properties:{expression:{type:"string"}},required:["expression"]}},{name:"page.screenshot",description:"Capture page image bytes (path, fullPage, clip, type, quality).",parameters:{type:"object",properties:{options:{type:"object"}}}},{name:"page.pdf",description:"Print the page to PDF bytes (path, format, margin, printBackground).",parameters:{type:"object",properties:{options:{type:"object"}}}},{name:"compactSnapshot",description:"Pass page.snapshot() output through this before reading: returns the raw tree without the refs map (refs resolve in-page; the map is ~54% of bytes on real pages).",parameters:{type:"object",properties:{snapshot:{}},required:["snapshot"]}},{name:"createCua",description:"Coordinate fallback for UI that refs cannot target: click/doubleClick/drag/move/scroll/type/keypress at viewport points, getVisibleScreenshot() as base64 PNG.",parameters:{type:"object",properties:{page:{}},required:["page"]}},{name:"createCaptcha",description:"Captcha helpers: click(bounds) and drag(from,to) return a post-action snapshot tree; readText(bounds?) OCRs a screenshot region (macOS Vision default, custom ocr injectable).",parameters:{type:"object",properties:{page:{},options:{type:"object"}},required:["page"]}},{name:"createChromeApi",description:"Chrome MV3-shaped APIs: tabs.query/get, windows.*, bookmarks/history/topSites (read-only from profile files), downloads.search/download. Writes throw UnsupportedOperationError.",parameters:{type:"object",properties:{connection:{},options:{type:"object"}},required:["connection"]}},{name:"injectCookies",description:"Inject raw cookie exports into a page with CDP sanitization handled (negative expires dropped, __Host- forced secure+root path+url-scoped, SameSite=None dropped when not secure, name+domain deduped).",parameters:{type:"object",properties:{page:{},cookies:{type:"array"}},required:["page","cookies"]}},{name:"createNetworkSnoop",description:"Buffer a page's CDP network traffic and return {pop,peek,popJson,waitFor,summary,dispose}; match is a predicate or {url,method,mimeType,resourceType}.",parameters:{type:"object",properties:{page:{},match:{},bodies:{type:"boolean"},maxEntries:{type:"number"},maxBodyBytes:{type:"number"}},required:["page"]}},{name:"snoop.waitFor",description:"Wait until a finished entry matches a predicate or {url,method,mimeType,resourceType}; timeoutMs defaults to 30000.",parameters:{type:"object",properties:{match:{},timeoutMs:{type:"number"}}}},{name:"snoop.popJson",description:"Drain the buffer and parse JSON bodies; skips non-JSON mime types and payloads that fail to parse.",parameters:{type:"object",properties:{}}},{name:"snoop.summary",description:"Return a compact text view of buffered entries (method, status, mimeType, size, url) capped by {max}.",parameters:{type:"object",properties:{max:{type:"number"}}}},{name:"collectWhileScrolling",description:"Async generator that scrolls the page, drains snoop.popJson(), and yields extracted items until minItems, maxScrolls, or noGrowth.",parameters:{type:"object",properties:{page:{},snoop:{},minItems:{type:"number"},maxScrolls:{type:"number"},extract:{},scroll:{type:"string",enum:["wheel","end"]},settleMs:{type:"number"}},required:["page","snoop"]}},{name:"createTrace",description:"Start a page flight recorder that writes dir/trace.jsonl, dir/trace.har, dir/summary.json and screenshots; never sends Runtime.enable.",parameters:{type:"object",properties:{page:{},dir:{type:"string"},screenshots:{type:"boolean"},network:{type:"boolean"},console:{type:"boolean"},maxBodyBytes:{type:"number"},events:{}},required:["page","dir"]}},{name:"trace.step",description:"Run named fn as a traced step with optional before/after screenshots; records the error and rethrows on failure.",parameters:{type:"object",properties:{name:{type:"string"},fn:{}},required:["name","fn"]}},{name:"trace.stop",description:"Flush listeners, write summary.json and trace.har, dispose the snoop, and return the summary for dir.",parameters:{type:"object",properties:{}}},{name:"reconcileFrames",description:"Adopt OOPIF iframe targets missing from FrameManager so later snapshots include them; returns the frames that were added.",parameters:{type:"object",properties:{page:{}},required:["page"]}},{name:"snapshotWithFrames",description:"Reconcile missing OOPIF frames then snapshot; refs stay page-level: page.locator(ref), never frame.locator.",parameters:{type:"object",properties:{page:{},options:{type:"object"}},required:["page"]}},{name:"connectPipe.dialogPolicy",description:"Wire connectPipe({dialogPolicy}), connect(url, {dialogPolicy}) and setDialogPolicy to {accept,promptText} or (dialog)=>boolean|{accept,promptText}; a throwing policy accepts.",parameters:{type:"object",properties:{accept:{type:"boolean"},promptText:{type:"string"}}}},{name:"requestHuman",description:"Pause for a human: inject a shadow-DOM Done banner, bringToFront first, and remove the banner on every path; until is {url}|{selector}|async predicate.",parameters:{type:"object",properties:{page:{},prompt:{type:"string"},until:{},timeoutMs:{type:"number"},pollMs:{type:"number"},signal:{}},required:["page"]}},{name:"describeLayers",description:"Sample a viewport grid and return {blocking,candidates,viewport} so overlay coverage and selectorHint are visible.",parameters:{type:"object",properties:{page:{},grid:{type:"number"}},required:["page"]}},{name:"snapshotWithLayers",description:"Prepend a @layers header to the snapshot tree and attach a layers field so overlays are visible before the a11y tree.",parameters:{type:"object",properties:{page:{},options:{type:"object"}},required:["page"]}},{name:"emulate",description:"Apply a DEVICE_PRESETS name, a custom metrics object, or null to restore the original UA captured on first call.",parameters:{type:"object",properties:{page:{},preset:{}},required:["page","preset"]}},{name:"createRoutes",description:"Install page request interception; Fetch.enable is sent only on the first route(); interception is fingerprintable, keep it off by default.",parameters:{type:"object",properties:{page:{}},required:["page"]}},{name:"route.continue",description:"Continue an intercepted request, optionally overriding url, method, headers, or postData.",parameters:{type:"object",properties:{url:{type:"string"},method:{type:"string"},headers:{type:"object"},postData:{}}}},{name:"route.fulfill",description:"Fulfill an intercepted request with {status,headers,contentType,body} instead of hitting the network.",parameters:{type:"object",properties:{status:{type:"number"},headers:{type:"object"},contentType:{type:"string"},body:{}}}},{name:"route.abort",description:"Abort an intercepted request with a CDP errorReason (defaults to Failed).",parameters:{type:"object",properties:{reason:{type:"string"}}}}];var MODIFIER_KEYS2=new Set(["Alt","Control","Meta","Shift"]);function splitModifiers(keys=[]){const modifiers=keys.filter((key)=>MODIFIER_KEYS2.has(key)||/^(cmd|command|ctrl|option|controlormeta)$/i.test(key));const rest=keys.filter((key)=>!modifiers.includes(key));return{modifiers,rest}}function createCua(page){async function holdModifiers(keys,fn){const{modifiers}=splitModifiers(keys);for(const key of modifiers)await page.keyboard.down(key);try{return await fn()}finally{for(const key of[...modifiers].reverse())await page.keyboard.up(key)}}return{page,async click({x,y,button="left",keypress}){await holdModifiers(keypress,()=>page.mouse.click(x,y,{button}))},async doubleClick({x,y,keypress}){await holdModifiers(keypress,()=>page.mouse.click(x,y,{clickCount:2}))},async drag({path,keys}){if(!Array.isArray(path)||path.length<2){throw new TypeError("cua.drag requires path with at least two points")}await holdModifiers(keys,async()=>{const[first,...rest]=path;await page.mouse.move(first.x,first.y);await page.mouse.down();try{for(const point of rest)await page.mouse.move(point.x,point.y)}finally{await page.mouse.up()}})},async getVisibleScreenshot(){const buffer=await page.screenshot({type:"png"});return buffer.toString("base64")},async keypress({keys}){if(!Array.isArray(keys)||keys.length===0){throw new TypeError("cua.keypress requires a non-empty keys array")}await page.keyboard.press(keys.join("+"))},async move({x,y,keys}){await holdModifiers(keys,()=>page.mouse.move(x,y))},async scroll({x,y,scrollX,scrollY,keypress}){await holdModifiers(keypress,async()=>{await page.mouse.move(x,y);await page.mouse.wheel(scrollX,scrollY)})},async type({text}){await page.keyboard.type(text)}}}import{spawn as spawn3}from"node:child_process";import{mkdtempSync,writeFileSync as writeFileSync2,rmSync as rmSync2}from"node:fs";import{tmpdir as tmpdir2}from"node:os";import path8 from"node:path";var MAX_TIMER_MS=2147483647;var STOP=Symbol("wait stopped");async function waitForCaptcha(page,{until,timeoutMs=1e4,pollMs=100,signal}={}){if(typeof until!=="function")throw new TypeError("until must be a function");if(!Number.isFinite(timeoutMs)||timeoutMs<0||timeoutMs>MAX_TIMER_MS){throw new RangeError("timeoutMs must be between 0 and 2147483647")}if(!Number.isFinite(pollMs)||pollMs<=0||pollMs>MAX_TIMER_MS){throw new RangeError("pollMs must be greater than 0 and at most 2147483647")}if(signal!==undefined&&(typeof signal?.aborted!=="boolean"||typeof signal.addEventListener!=="function"||typeof signal.removeEventListener!=="function")){throw new TypeError("signal must be AbortSignal-compatible")}const started=performance.now();const deadline=started+timeoutMs;const operation=new AbortController;let deadlineTimer;let pollTimer;let stop;const stopped=new Promise((resolve)=>{stop=()=>resolve(STOP)});const terminal=()=>signal?.aborted?"cancelled":performance.now()>=deadline?"timed_out":null;const result=(outcome)=>({outcome,elapsedMs:performance.now()-started});const scheduleDeadline=()=>{const remaining=deadline-performance.now();if(remaining<=0)stop();else deadlineTimer=setTimeout(scheduleDeadline,Math.ceil(remaining))};try{const initial=terminal();if(initial)return result(initial);signal?.addEventListener("abort",stop,{once:true});scheduleDeadline();for(;;){let accepted;try{accepted=await Promise.race([Promise.resolve().then(()=>terminal()?STOP:until(page,{signal:operation.signal})),stopped])}catch(error){const outcome=terminal();if(outcome)return result(outcome);throw error}const outcome=terminal();if(outcome)return result(outcome);if(accepted===true)return result("matched");await Promise.race([new Promise((resolve)=>{pollTimer=setTimeout(resolve,Math.ceil(Math.min(pollMs,deadline-performance.now())))}),stopped]);clearTimeout(pollTimer);const afterDelay=terminal();if(afterDelay)return result(afterDelay)}}finally{clearTimeout(deadlineTimer);clearTimeout(pollTimer);signal?.removeEventListener("abort",stop);operation.abort();stop()}}var DEFAULT_DRAG_STEPS=20;var POST_ACTION_SETTLE_MS=3000;function validatePoint(point){if(!Number.isFinite(point?.x)||!Number.isFinite(point?.y)){throw new TypeError("point coordinates must be finite numbers")}}function validateSteps(steps){if(!Number.isSafeInteger(steps)||steps<=0){throw new RangeError("steps must be a positive safe integer")}}function validateSettleMs(value){if(!Number.isFinite(value)||value<0||value>2147483647){throw new RangeError("settleMs must be between 0 and 2147483647")}}var MACOS_VISION_OCR_SWIFT=`
1476
1477
  import Vision
1477
1478
  import AppKit
1478
1479
  let path = CommandLine.arguments[1]
@@ -1487,31 +1488,31 @@ let text = (request.results ?? [])
1487
1488
  .compactMap { $0.topCandidates(1).first?.string }
1488
1489
  .joined(separator: " ")
1489
1490
  print(text)
1490
- `;async function macOSVisionOcr(pngBuffer){if(process.platform!=="darwin"){throw new Error("macOSVisionOcr is only available on macOS; pass a custom ocr(buffer) instead")}const dir=mkdtempSync(path7.join(tmpdir2(),"omowright-ocr-"));try{const imagePath=path7.join(dir,"region.png");const scriptPath=path7.join(dir,"ocr.swift");writeFileSync(imagePath,pngBuffer);writeFileSync(scriptPath,MACOS_VISION_OCR_SWIFT);return await new Promise((resolve,reject)=>{const child=spawn3("swift",[scriptPath,imagePath],{stdio:["ignore","pipe","pipe"]});let stdout="";let stderr="";child.stdout.on("data",(chunk)=>{stdout+=chunk});child.stderr.on("data",(chunk)=>{stderr+=chunk});child.on("error",reject);child.on("close",(code)=>{if(code===0){const text=stdout.trim();resolve(text.length>0?text:null)}else{reject(new Error(`Vision OCR failed (exit ${code}): ${stderr.trim()}`))}})})}finally{rmSync(dir,{recursive:true,force:true})}}function createCaptcha(page,options={}){const ocr=options.ocr??macOSVisionOcr;function centerOf(bounds){validatePoint(bounds);if(!Number.isFinite(bounds.width)||bounds.width<=0||!Number.isFinite(bounds.height)||bounds.height<=0||!Number.isFinite(bounds.x+bounds.width)||!Number.isFinite(bounds.y+bounds.height)){throw new RangeError("bounds must have positive finite dimensions and finite edges")}return{x:bounds.x+bounds.width/2,y:bounds.y+bounds.height/2}}async function settleAndSnapshot(settleMs){await new Promise((resolve)=>setTimeout(resolve,settleMs));return compactSnapshot(await page.snapshot())}return{page,waitFor(opts){return waitForCaptcha(page,opts)},async click(bounds,opts={}){const center=centerOf(bounds);const point=opts.point===undefined?center:opts.point;validatePoint(point);if(point.x<bounds.x||point.x>=bounds.x+bounds.width||point.y<bounds.y||point.y>=bounds.y+bounds.height){throw new RangeError("point must be inside bounds")}const settleMs=opts.settleMs===undefined?POST_ACTION_SETTLE_MS:opts.settleMs;validateSettleMs(settleMs);if(opts.approachSteps!==undefined){validateSteps(opts.approachSteps);await page.mouse.move(point.x,point.y,{steps:opts.approachSteps})}await page.mouse.click(point.x,point.y);return settleAndSnapshot(settleMs)},async drag(from,to,opts={}){validatePoint(from);validatePoint(to);const steps=opts.steps===undefined?DEFAULT_DRAG_STEPS:opts.steps;validateSteps(steps);const settleMs=opts.settleMs===undefined?POST_ACTION_SETTLE_MS:opts.settleMs;validateSettleMs(settleMs);await page.mouse.move(from.x,from.y);await page.mouse.down();try{await page.mouse.move(to.x,to.y,{steps})}finally{await page.mouse.up()}return settleAndSnapshot(settleMs)},async readText(bounds){const buffer=await page.screenshot(bounds?{type:"png",clip:bounds}:{type:"png"});return ocr(buffer)}}}var EVENT_NAMES=["tabOpened","tabClosed","popupOpened","downloadStarted","downloadProgress","downloadFinished"];var BROWSER_EVENT_NAMES=Object.freeze([...EVENT_NAMES]);var DEFAULT_TIMEOUT=30000;var TERMINAL_LIMIT=4096;function pageTarget(info){return info?.type==="page"&&info?.targetId?info:null}function normalizeTarget(info){return{targetId:String(info.targetId),type:"page",url:info.url??"about:blank",title:info.title??"",openerId:info.openerId??null,openerFrameId:info.openerFrameId??null,browserContextId:info.browserContextId??null,attached:Boolean(info.attached),subtype:info.subtype??null}}function errorValue(value){return value instanceof Error?value:Error(String(value))}class BrowserEventBridge{#cdp;#options;#listeners=new Map;#unsubscribers=[];#targetInfos=new Map;#crashes=new Map;#downloads=new Map;#terminalDownloads=new Map;#terminal=new Set;#frameToTarget=new Map;#sessionToTarget=new Map;#waits=new Set;#disposed=false;#explicitDispose=false;ready;constructor(connection,options){if(!connection?.cdp)throw new TypeError("createEvents requires a browser connection");this.#cdp=connection.cdp;this.#options=this.#validate(options);this.#register();this.ready=this.#initialize()}#validate(options){const behavior=options.downloadBehavior??"default";if(!["default","allow","deny","allowAndName"].includes(behavior))throw new TypeError("Invalid downloadBehavior");if(behavior==="allow"&&!pathIsAbsolute(options.downloadPath))throw new TypeError("downloadPath must be an absolute path for allow");return{...options,downloadBehavior:behavior}}#register(){const on=(method,fn)=>this.#unsubscribers.push(this.#cdp.on(method,fn));on("Target.targetCreated",(info)=>this.#created(info,false));on("Target.targetDestroyed",(info)=>this.#destroyed(info));on("Target.targetInfoChanged",(info)=>{const target=pageTarget(info?.targetInfo);if(target&&this.#targetInfos.has(target.targetId))this.#targetInfos.set(target.targetId,normalizeTarget(target))});on("Target.targetCrashed",(info)=>{if(info?.targetId)this.#crashes.set(info.targetId,{status:info.status??"",errorCode:info.errorCode??0})});on("Target.attachedToTarget",(info,meta)=>{const targetId=info?.targetInfo?.targetId;const sessionId=info?.sessionId??meta?.sessionId;if(targetId&&sessionId){this.#sessionToTarget.set(sessionId,targetId)}});on("Target.detachedFromTarget",(info,meta)=>{const sessionId=info?.sessionId??meta?.sessionId;if(sessionId)this.#sessionToTarget.delete(sessionId)});on("Browser.downloadWillBegin",(info)=>this.#downloadBegin(info));on("Browser.downloadProgress",(info)=>this.#downloadProgress(info));on("Page.frameAttached",(info,meta)=>{const sessionId=info?.sessionId??meta?.sessionId;if(sessionId&&info?.frameId)this.#mapFrame(info.frameId,sessionId)});on("Page.frameNavigated",(info,meta)=>{const sessionId=info?.sessionId??meta?.sessionId;if(sessionId&&info?.frame?.id)this.#mapFrame(info.frame.id,sessionId)});if(this.#cdp.transportEvents?.on){this.#unsubscribers.push(this.#cdp.transportEvents.on("disconnected",()=>{this.#disconnectDownloads();this.#cancelWaits(Error("Browser transport disconnected"))}));this.#unsubscribers.push(this.#cdp.transportEvents.on("reconnected",()=>{this.#reconcile().catch((error)=>console.warn("[events] target reconciliation failed",error));this.#configureDownloads().catch((error)=>console.warn("[events] download reconfiguration failed",error))}))}}async#initialize(){await this.#cdp.ensureConnected();await this.#cdp.send("Target.setDiscoverTargets",{discover:true});const result=await this.#cdp.send("Target.getTargets");await this.#configureDownloads();const targets=(result?.targetInfos??[]).filter((info)=>pageTarget(info));targets.sort((a,b)=>Boolean(a.openerId)-Boolean(b.openerId));for(const info of targets)this.#created(info,true)}async#reconcile(){const result=await this.#cdp.send("Target.getTargets");const current=new Set((result?.targetInfos??[]).filter((info)=>pageTarget(info)).map((info)=>String(info.targetId)));for(const id of[...this.#targetInfos.keys()])if(!current.has(id))this.#destroyed({targetId:id});const targets=(result?.targetInfos??[]).filter((info)=>pageTarget(info));targets.sort((a,b)=>Boolean(a.openerId)-Boolean(b.openerId));for(const info of targets)this.#created(info,true)}async#configureDownloads(){const params={behavior:this.#options.downloadBehavior,eventsEnabled:true};if(this.#options.downloadBehavior==="allow"||this.#options.downloadBehavior==="allowAndName")params.downloadPath=this.#options.downloadPath;await this.#cdp.send("Browser.setDownloadBehavior",params)}#mapFrame(frameId,sessionId){const targetId=this.#sessionToTarget.get(sessionId)??[...this.#targetInfos.values()].find((info)=>info.attached&&info.targetId===sessionId)?.targetId;if(targetId)this.#frameToTarget.set(frameId,targetId)}#created(info,initial){const target=pageTarget(info?.targetInfo??info);if(!target)return;const normalized=normalizeTarget(target);const id=normalized.targetId;if(this.#targetInfos.has(id))return;this.#targetInfos.set(id,normalized);const popup=Boolean(normalized.openerId&&this.#targetInfos.has(normalized.openerId));this.#dispatch("tabOpened",{...normalized,popup,initial:Boolean(initial)});if(popup)this.#dispatch("popupOpened",{targetId:id,openerTargetId:normalized.openerId,openerFrameId:normalized.openerFrameId,url:normalized.url,title:normalized.title,initial:Boolean(initial)})}#destroyed(info){const id=info?.targetId;const last=this.#targetInfos.get(id);if(!last)return;this.#targetInfos.delete(id);const crash=this.#crashes.get(id)??null;this.#crashes.delete(id);for(const[frame,targetId]of this.#frameToTarget)if(targetId===id)this.#frameToTarget.delete(frame);this.#dispatch("tabClosed",{targetId:id,reason:crash?"crashed":"destroyed",lastKnown:last,crash})}#rememberTerminal(guid){this.#terminal.delete(guid);this.#terminal.add(guid);if(this.#terminal.size>TERMINAL_LIMIT){const oldest=this.#terminal.values().next().value;this.#terminal.delete(oldest);this.#terminalDownloads.delete(oldest)}}#downloadBegin(info={}){if(!info.guid||this.#terminal.has(info.guid))return;const record={guid:info.guid,url:info.url??null,suggestedFilename:info.suggestedFilename??"",frameId:info.frameId??null,targetId:this.#frameToTarget.get(info.frameId)??null,totalBytes:0,receivedBytes:0,filePath:null};this.#downloads.set(info.guid,record);this.#dispatch("downloadStarted",{guid:record.guid,url:record.url,suggestedFilename:record.suggestedFilename,frameId:record.frameId,targetId:record.targetId})}#downloadProgress(info={}){if(!info.guid)return;const alreadyTerminal=this.#terminal.has(info.guid);const record=this.#downloads.get(info.guid)??this.#terminalDownloads.get(info.guid);if(!record)return;Object.assign(record,{totalBytes:Number(info.totalBytes??0),receivedBytes:Number(info.receivedBytes??0),filePath:info.filePath??null});this.#dispatch("downloadProgress",{guid:info.guid,totalBytes:record.totalBytes,receivedBytes:record.receivedBytes,state:info.state,filePath:record.filePath,targetId:record.targetId});if((info.state==="completed"||info.state==="canceled")&&!alreadyTerminal){this.#rememberTerminal(info.guid);this.#downloads.delete(info.guid);this.#terminalDownloads.set(info.guid,record);this.#dispatch("downloadFinished",{guid:info.guid,totalBytes:record.totalBytes,receivedBytes:record.receivedBytes,state:info.state,cdpState:info.state,filePath:record.filePath,targetId:record.targetId,error:null})}}#disconnectDownloads(){if(this.#explicitDispose)return;for(const record of this.#downloads.values()){this.#rememberTerminal(record.guid);this.#dispatch("downloadFinished",{guid:record.guid,totalBytes:record.totalBytes,receivedBytes:record.receivedBytes,state:"failed",cdpState:null,filePath:record.filePath,targetId:record.targetId,error:{code:"browserDisconnected",message:"Browser transport disconnected"}})}this.#downloads.clear()}#dispatch(name,payload){const listeners=[...this.#listeners.get(name)??[]];Promise.allSettled(listeners.map((listener)=>Promise.resolve().then(()=>listener(payload)))).then((results)=>results.filter((r)=>r.status==="rejected").forEach((r)=>console.warn(`[events] ${name} listener failed`,r.reason)))}on(name,listener){this.#assertName(name);if(typeof listener!=="function")throw new TypeError("listener must be a function");const set=this.#listeners.get(name)??new Set;set.add(listener);this.#listeners.set(name,set);return()=>this.off(name,listener)}off(name,listener){this.#assertName(name);this.#listeners.get(name)?.delete(listener)}#cancelWaits(error){for(const cancel of this.#waits)cancel(error);this.#waits.clear()}waitForEvent(name,options={}){this.#assertName(name);if(this.#disposed)return Promise.reject(Error("Events bridge is disposed"));const timeout=options.timeout??DEFAULT_TIMEOUT;return new Promise((resolve,reject)=>{let done=false;let timer;const off=this.on(name,async(value)=>{try{if(options.predicate&&!await options.predicate(value))return;if(done)return;done=true;clearTimeout(timer);this.#waits.delete(cancel);off();resolve(value)}catch(error){if(!done){done=true;clearTimeout(timer);this.#waits.delete(cancel);off();reject(errorValue(error))}}});const cancel=(error)=>{if(!done){done=true;clearTimeout(timer);this.#waits.delete(cancel);off();reject(error??Error("Events bridge disposed"))}};this.#waits.add(cancel);timer=setTimeout(()=>cancel(Error(`Timed out waiting for browser event "${name}"`)),timeout)})}#assertName(name){if(!EVENT_NAMES.includes(name))throw new TypeError(`Unknown browser event: ${String(name)}`)}async dispose(){if(this.#disposed)return;this.#disposed=true;this.#explicitDispose=true;this.#cancelWaits();for(const off of this.#unsubscribers)off();this.#unsubscribers=[];this.#listeners.clear();await this.ready.catch(()=>{})}}function pathIsAbsolute(value){return typeof value==="string"&&(value.startsWith("/")||/^[A-Za-z]:[\\/]/.test(value))}function createEvents(connection,options={}){return new BrowserEventBridge(connection,options)}init_agent_tabs();init_viewport();import{randomUUID as randomUUID4}from"node:crypto";import path10 from"node:path";import{mkdir as mkdir5,open as open5,readFile as readFile5,rename as rename3,lstat,chmod as chmod4}from"node:fs/promises";import{homedir}from"node:os";import{TextDecoder as TextDecoder2}from"node:util";import{mkdirSync}from"node:fs";import path9 from"node:path";import{tmpdir as tmpdir3}from"node:os";import{existsSync,readFileSync as readFileSync2}from"node:fs";import path8 from"node:path";var DatabaseCtor;async function loadSqlite(){if(DatabaseCtor)return DatabaseCtor;try{const mod=await import("node:sqlite");DatabaseCtor=(file)=>new mod.DatabaseSync(file,{readOnly:true,readBigInts:true})}catch{const mod=await import("bun:sqlite");DatabaseCtor=(file)=>new mod.Database(file,{readonly:true})}return DatabaseCtor}var CHROME_EPOCH_OFFSET_MS=11644473600000;var chromeTimeToMs=(time)=>Number(BigInt(time)/1000n)-CHROME_EPOCH_OFFSET_MS;var msToChromeTime=(ms)=>BigInt(Math.floor((ms+CHROME_EPOCH_OFFSET_MS)*1000));var EXTENSION_BRIDGE_REQUIRED="requires the extension bridge in standalone OmOWright";function createProfileApis(profilePath){function profileDir(){if(!profilePath){throw new UnsupportedOperationError("chrome.bookmarks/history/topSites need a profilePath (the browser's user-data-dir); pass it to createChromeApi(connection, { profilePath })")}return path8.join(profilePath,"Default")}async function openDb(fileName){const file=path8.join(profileDir(),fileName);if(!existsSync(file))return null;const ctor=await loadSqlite();return ctor(file)}async function withDb(fileName,fn){const db=await openDb(fileName);if(!db)return[];try{return fn(db)}finally{db.close()}}function bookmarkRoots(){const file=path8.join(profileDir(),"Bookmarks");if(!existsSync(file))return[];const parsed=JSON.parse(readFileSync2(file,"utf8"));return Object.values(parsed.roots??{}).filter((node)=>node&&typeof node==="object"&&node.id)}function walkBookmarks(nodes,visit,acc=[]){for(const node of nodes){visit(node,acc);if(Array.isArray(node.children))walkBookmarks(node.children,visit,acc)}return acc}function findBookmark(id){for(const root of bookmarkRoots()){const found=walkBookmarks([root],(node,acc)=>{if(node.id===String(id))acc.push(node)});if(found.length>0)return found[0]}return null}function toBookmark(node){const out={id:node.id,title:node.name??"",type:node.type};if(node.url)out.url=node.url;if(node.date_added)out.dateAdded=chromeTimeToMs(node.date_added);if(node.children)out.children=node.children.map(toBookmark);return out}const bookmarks={async get(idOrIdList){const ids=Array.isArray(idOrIdList)?idOrIdList:[idOrIdList];return ids.map(findBookmark).filter(Boolean).map(toBookmark)},async getChildren(id){const node=findBookmark(id);return(node?.children??[]).map(toBookmark)},async getRecent(numberOfItems){const urls=walkBookmarks(bookmarkRoots(),(node,acc)=>{if(node.type==="url")acc.push(node)});return urls.sort((a,b)=>Number(b.date_added??0)-Number(a.date_added??0)).slice(0,numberOfItems).map(toBookmark)},async getSubTree(id){const node=findBookmark(id);return node?[toBookmark(node)]:[]},async getTree(){return bookmarkRoots().map(toBookmark)},async search(query){const needle=typeof query==="string"?query.toLowerCase():(query.query??"").toLowerCase();const url=typeof query==="object"?query.url?.toLowerCase():undefined;const title=typeof query==="object"?query.title?.toLowerCase():undefined;return walkBookmarks(bookmarkRoots(),(node,acc)=>{if(node.type!=="url")return;const name=(node.name??"").toLowerCase();const href=(node.url??"").toLowerCase();const ok=needle&&(name.includes(needle)||href.includes(needle))||url&&href.includes(url)||title&&name.includes(title);if(ok)acc.push(node)}).map(toBookmark)},async create(){throw new UnsupportedOperationError(`chrome.bookmarks.create ${EXTENSION_BRIDGE_REQUIRED}`)},async update(){throw new UnsupportedOperationError(`chrome.bookmarks.update ${EXTENSION_BRIDGE_REQUIRED}`)},async move(){throw new UnsupportedOperationError(`chrome.bookmarks.move ${EXTENSION_BRIDGE_REQUIRED}`)},async remove(){throw new UnsupportedOperationError(`chrome.bookmarks.remove ${EXTENSION_BRIDGE_REQUIRED}`)},async removeTree(){throw new UnsupportedOperationError(`chrome.bookmarks.removeTree ${EXTENSION_BRIDGE_REQUIRED}`)}};const history={async search(query){const text=`%${query.text??""}%`;const start=query.startTime!=null?msToChromeTime(query.startTime):0n;const end=query.endTime!=null?msToChromeTime(query.endTime):99999999999999999n;const max=query.maxResults??100;return await withDb("History",(db)=>db.prepare(`SELECT id, url, title, visit_count, last_visit_time FROM urls
1491
+ `;async function macOSVisionOcr(pngBuffer){if(process.platform!=="darwin"){throw new Error("macOSVisionOcr is only available on macOS; pass a custom ocr(buffer) instead")}const dir=mkdtempSync(path8.join(tmpdir2(),"omowright-ocr-"));try{const imagePath=path8.join(dir,"region.png");const scriptPath=path8.join(dir,"ocr.swift");writeFileSync2(imagePath,pngBuffer);writeFileSync2(scriptPath,MACOS_VISION_OCR_SWIFT);return await new Promise((resolve,reject)=>{const child=spawn3("swift",[scriptPath,imagePath],{stdio:["ignore","pipe","pipe"]});let stdout="";let stderr="";child.stdout.on("data",(chunk)=>{stdout+=chunk});child.stderr.on("data",(chunk)=>{stderr+=chunk});child.on("error",reject);child.on("close",(code)=>{if(code===0){const text=stdout.trim();resolve(text.length>0?text:null)}else{reject(new Error(`Vision OCR failed (exit ${code}): ${stderr.trim()}`))}})})}finally{rmSync2(dir,{recursive:true,force:true})}}function createCaptcha(page,options={}){const ocr=options.ocr??macOSVisionOcr;function centerOf(bounds){validatePoint(bounds);if(!Number.isFinite(bounds.width)||bounds.width<=0||!Number.isFinite(bounds.height)||bounds.height<=0||!Number.isFinite(bounds.x+bounds.width)||!Number.isFinite(bounds.y+bounds.height)){throw new RangeError("bounds must have positive finite dimensions and finite edges")}return{x:bounds.x+bounds.width/2,y:bounds.y+bounds.height/2}}async function settleAndSnapshot(settleMs){await new Promise((resolve)=>setTimeout(resolve,settleMs));return compactSnapshot(await page.snapshot())}return{page,waitFor(opts){return waitForCaptcha(page,opts)},async click(bounds,opts={}){const center=centerOf(bounds);const point=opts.point===undefined?center:opts.point;validatePoint(point);if(point.x<bounds.x||point.x>=bounds.x+bounds.width||point.y<bounds.y||point.y>=bounds.y+bounds.height){throw new RangeError("point must be inside bounds")}const settleMs=opts.settleMs===undefined?POST_ACTION_SETTLE_MS:opts.settleMs;validateSettleMs(settleMs);if(opts.approachSteps!==undefined){validateSteps(opts.approachSteps);await page.mouse.move(point.x,point.y,{steps:opts.approachSteps})}await page.mouse.click(point.x,point.y);return settleAndSnapshot(settleMs)},async drag(from,to,opts={}){validatePoint(from);validatePoint(to);const steps=opts.steps===undefined?DEFAULT_DRAG_STEPS:opts.steps;validateSteps(steps);const settleMs=opts.settleMs===undefined?POST_ACTION_SETTLE_MS:opts.settleMs;validateSettleMs(settleMs);await page.mouse.move(from.x,from.y);await page.mouse.down();try{await page.mouse.move(to.x,to.y,{steps})}finally{await page.mouse.up()}return settleAndSnapshot(settleMs)},async readText(bounds){const buffer=await page.screenshot(bounds?{type:"png",clip:bounds}:{type:"png"});return ocr(buffer)}}}var EVENT_NAMES=["tabOpened","tabClosed","popupOpened","downloadStarted","downloadProgress","downloadFinished"];var BROWSER_EVENT_NAMES=Object.freeze([...EVENT_NAMES]);var DEFAULT_TIMEOUT=30000;var TERMINAL_LIMIT=4096;function pageTarget(info){return info?.type==="page"&&info?.targetId?info:null}function normalizeTarget(info){return{targetId:String(info.targetId),type:"page",url:info.url??"about:blank",title:info.title??"",openerId:info.openerId??null,openerFrameId:info.openerFrameId??null,browserContextId:info.browserContextId??null,attached:Boolean(info.attached),subtype:info.subtype??null}}function errorValue(value){return value instanceof Error?value:Error(String(value))}class BrowserEventBridge{#cdp;#options;#listeners=new Map;#unsubscribers=[];#targetInfos=new Map;#crashes=new Map;#downloads=new Map;#terminalDownloads=new Map;#terminal=new Set;#frameToTarget=new Map;#sessionToTarget=new Map;#waits=new Set;#disposed=false;#explicitDispose=false;ready;constructor(connection,options){if(!connection?.cdp)throw new TypeError("createEvents requires a browser connection");this.#cdp=connection.cdp;this.#options=this.#validate(options);this.#register();this.ready=this.#initialize()}#validate(options){const behavior=options.downloadBehavior??"default";if(!["default","allow","deny","allowAndName"].includes(behavior))throw new TypeError("Invalid downloadBehavior");if(behavior==="allow"&&!pathIsAbsolute(options.downloadPath))throw new TypeError("downloadPath must be an absolute path for allow");return{...options,downloadBehavior:behavior}}#register(){const on=(method,fn)=>this.#unsubscribers.push(this.#cdp.on(method,fn));on("Target.targetCreated",(info)=>this.#created(info,false));on("Target.targetDestroyed",(info)=>this.#destroyed(info));on("Target.targetInfoChanged",(info)=>{const target=pageTarget(info?.targetInfo);if(target&&this.#targetInfos.has(target.targetId))this.#targetInfos.set(target.targetId,normalizeTarget(target))});on("Target.targetCrashed",(info)=>{if(info?.targetId)this.#crashes.set(info.targetId,{status:info.status??"",errorCode:info.errorCode??0})});on("Target.attachedToTarget",(info,meta)=>{const targetId=info?.targetInfo?.targetId;const sessionId=info?.sessionId??meta?.sessionId;if(targetId&&sessionId){this.#sessionToTarget.set(sessionId,targetId)}});on("Target.detachedFromTarget",(info,meta)=>{const sessionId=info?.sessionId??meta?.sessionId;if(sessionId)this.#sessionToTarget.delete(sessionId)});on("Browser.downloadWillBegin",(info)=>this.#downloadBegin(info));on("Browser.downloadProgress",(info)=>this.#downloadProgress(info));on("Page.frameAttached",(info,meta)=>{const sessionId=info?.sessionId??meta?.sessionId;if(sessionId&&info?.frameId)this.#mapFrame(info.frameId,sessionId)});on("Page.frameNavigated",(info,meta)=>{const sessionId=info?.sessionId??meta?.sessionId;if(sessionId&&info?.frame?.id)this.#mapFrame(info.frame.id,sessionId)});if(this.#cdp.transportEvents?.on){this.#unsubscribers.push(this.#cdp.transportEvents.on("disconnected",()=>{this.#disconnectDownloads();this.#cancelWaits(Error("Browser transport disconnected"))}));this.#unsubscribers.push(this.#cdp.transportEvents.on("reconnected",()=>{this.#reconcile().catch((error)=>console.warn("[events] target reconciliation failed",error));this.#configureDownloads().catch((error)=>console.warn("[events] download reconfiguration failed",error))}))}}async#initialize(){await this.#cdp.ensureConnected();await this.#cdp.send("Target.setDiscoverTargets",{discover:true});const result=await this.#cdp.send("Target.getTargets");await this.#configureDownloads();const targets=(result?.targetInfos??[]).filter((info)=>pageTarget(info));targets.sort((a,b)=>Boolean(a.openerId)-Boolean(b.openerId));for(const info of targets)this.#created(info,true)}async#reconcile(){const result=await this.#cdp.send("Target.getTargets");const current=new Set((result?.targetInfos??[]).filter((info)=>pageTarget(info)).map((info)=>String(info.targetId)));for(const id of[...this.#targetInfos.keys()])if(!current.has(id))this.#destroyed({targetId:id});const targets=(result?.targetInfos??[]).filter((info)=>pageTarget(info));targets.sort((a,b)=>Boolean(a.openerId)-Boolean(b.openerId));for(const info of targets)this.#created(info,true)}async#configureDownloads(){const params={behavior:this.#options.downloadBehavior,eventsEnabled:true};if(this.#options.downloadBehavior==="allow"||this.#options.downloadBehavior==="allowAndName")params.downloadPath=this.#options.downloadPath;await this.#cdp.send("Browser.setDownloadBehavior",params)}#mapFrame(frameId,sessionId){const targetId=this.#sessionToTarget.get(sessionId)??[...this.#targetInfos.values()].find((info)=>info.attached&&info.targetId===sessionId)?.targetId;if(targetId)this.#frameToTarget.set(frameId,targetId)}#created(info,initial){const target=pageTarget(info?.targetInfo??info);if(!target)return;const normalized=normalizeTarget(target);const id=normalized.targetId;if(this.#targetInfos.has(id))return;this.#targetInfos.set(id,normalized);const popup=Boolean(normalized.openerId&&this.#targetInfos.has(normalized.openerId));this.#dispatch("tabOpened",{...normalized,popup,initial:Boolean(initial)});if(popup)this.#dispatch("popupOpened",{targetId:id,openerTargetId:normalized.openerId,openerFrameId:normalized.openerFrameId,url:normalized.url,title:normalized.title,initial:Boolean(initial)})}#destroyed(info){const id=info?.targetId;const last=this.#targetInfos.get(id);if(!last)return;this.#targetInfos.delete(id);const crash=this.#crashes.get(id)??null;this.#crashes.delete(id);for(const[frame,targetId]of this.#frameToTarget)if(targetId===id)this.#frameToTarget.delete(frame);this.#dispatch("tabClosed",{targetId:id,reason:crash?"crashed":"destroyed",lastKnown:last,crash})}#rememberTerminal(guid){this.#terminal.delete(guid);this.#terminal.add(guid);if(this.#terminal.size>TERMINAL_LIMIT){const oldest=this.#terminal.values().next().value;this.#terminal.delete(oldest);this.#terminalDownloads.delete(oldest)}}#downloadBegin(info={}){if(!info.guid||this.#terminal.has(info.guid))return;const record={guid:info.guid,url:info.url??null,suggestedFilename:info.suggestedFilename??"",frameId:info.frameId??null,targetId:this.#frameToTarget.get(info.frameId)??null,totalBytes:0,receivedBytes:0,filePath:null};this.#downloads.set(info.guid,record);this.#dispatch("downloadStarted",{guid:record.guid,url:record.url,suggestedFilename:record.suggestedFilename,frameId:record.frameId,targetId:record.targetId})}#downloadProgress(info={}){if(!info.guid)return;const alreadyTerminal=this.#terminal.has(info.guid);const record=this.#downloads.get(info.guid)??this.#terminalDownloads.get(info.guid);if(!record)return;Object.assign(record,{totalBytes:Number(info.totalBytes??0),receivedBytes:Number(info.receivedBytes??0),filePath:info.filePath??null});this.#dispatch("downloadProgress",{guid:info.guid,totalBytes:record.totalBytes,receivedBytes:record.receivedBytes,state:info.state,filePath:record.filePath,targetId:record.targetId});if((info.state==="completed"||info.state==="canceled")&&!alreadyTerminal){this.#rememberTerminal(info.guid);this.#downloads.delete(info.guid);this.#terminalDownloads.set(info.guid,record);this.#dispatch("downloadFinished",{guid:info.guid,totalBytes:record.totalBytes,receivedBytes:record.receivedBytes,state:info.state,cdpState:info.state,filePath:record.filePath,targetId:record.targetId,error:null})}}#disconnectDownloads(){if(this.#explicitDispose)return;for(const record of this.#downloads.values()){this.#rememberTerminal(record.guid);this.#dispatch("downloadFinished",{guid:record.guid,totalBytes:record.totalBytes,receivedBytes:record.receivedBytes,state:"failed",cdpState:null,filePath:record.filePath,targetId:record.targetId,error:{code:"browserDisconnected",message:"Browser transport disconnected"}})}this.#downloads.clear()}#dispatch(name,payload){const listeners=[...this.#listeners.get(name)??[]];Promise.allSettled(listeners.map((listener)=>Promise.resolve().then(()=>listener(payload)))).then((results)=>results.filter((r)=>r.status==="rejected").forEach((r)=>console.warn(`[events] ${name} listener failed`,r.reason)))}on(name,listener){this.#assertName(name);if(typeof listener!=="function")throw new TypeError("listener must be a function");const set=this.#listeners.get(name)??new Set;set.add(listener);this.#listeners.set(name,set);return()=>this.off(name,listener)}off(name,listener){this.#assertName(name);this.#listeners.get(name)?.delete(listener)}#cancelWaits(error){for(const cancel of this.#waits)cancel(error);this.#waits.clear()}waitForEvent(name,options={}){this.#assertName(name);if(this.#disposed)return Promise.reject(Error("Events bridge is disposed"));const timeout=options.timeout??DEFAULT_TIMEOUT;return new Promise((resolve,reject)=>{let done=false;let timer;const off=this.on(name,async(value)=>{try{if(options.predicate&&!await options.predicate(value))return;if(done)return;done=true;clearTimeout(timer);this.#waits.delete(cancel);off();resolve(value)}catch(error){if(!done){done=true;clearTimeout(timer);this.#waits.delete(cancel);off();reject(errorValue(error))}}});const cancel=(error)=>{if(!done){done=true;clearTimeout(timer);this.#waits.delete(cancel);off();reject(error??Error("Events bridge disposed"))}};this.#waits.add(cancel);timer=setTimeout(()=>cancel(Error(`Timed out waiting for browser event "${name}"`)),timeout)})}#assertName(name){if(!EVENT_NAMES.includes(name))throw new TypeError(`Unknown browser event: ${String(name)}`)}async dispose(){if(this.#disposed)return;this.#disposed=true;this.#explicitDispose=true;this.#cancelWaits();for(const off of this.#unsubscribers)off();this.#unsubscribers=[];this.#listeners.clear();await this.ready.catch(()=>{})}}function pathIsAbsolute(value){return typeof value==="string"&&(value.startsWith("/")||/^[A-Za-z]:[\\/]/.test(value))}function createEvents(connection,options={}){return new BrowserEventBridge(connection,options)}init_agent_tabs();init_viewport();import{randomUUID as randomUUID4}from"node:crypto";import path11 from"node:path";import{mkdir as mkdir5,open as open5,readFile as readFile5,rename as rename3,lstat,chmod as chmod4}from"node:fs/promises";import{homedir}from"node:os";import{TextDecoder as TextDecoder2}from"node:util";import{mkdirSync as mkdirSync2}from"node:fs";import path10 from"node:path";import{tmpdir as tmpdir3}from"node:os";import{existsSync,readFileSync as readFileSync3}from"node:fs";import path9 from"node:path";var DatabaseCtor;async function loadSqlite(){if(DatabaseCtor)return DatabaseCtor;try{const mod=await import("node:sqlite");DatabaseCtor=(file)=>new mod.DatabaseSync(file,{readOnly:true,readBigInts:true})}catch{const mod=await import("bun:sqlite");DatabaseCtor=(file)=>new mod.Database(file,{readonly:true})}return DatabaseCtor}var CHROME_EPOCH_OFFSET_MS=11644473600000;var chromeTimeToMs=(time)=>Number(BigInt(time)/1000n)-CHROME_EPOCH_OFFSET_MS;var msToChromeTime=(ms)=>BigInt(Math.floor((ms+CHROME_EPOCH_OFFSET_MS)*1000));var EXTENSION_BRIDGE_REQUIRED="requires the extension bridge in standalone OmOWright";function createProfileApis(profilePath){function profileDir(){if(!profilePath){throw new UnsupportedOperationError("chrome.bookmarks/history/topSites need a profilePath (the browser's user-data-dir); pass it to createChromeApi(connection, { profilePath })")}return path9.join(profilePath,"Default")}async function openDb(fileName){const file=path9.join(profileDir(),fileName);if(!existsSync(file))return null;const ctor=await loadSqlite();return ctor(file)}async function withDb(fileName,fn){const db=await openDb(fileName);if(!db)return[];try{return fn(db)}finally{db.close()}}function bookmarkRoots(){const file=path9.join(profileDir(),"Bookmarks");if(!existsSync(file))return[];const parsed=JSON.parse(readFileSync3(file,"utf8"));return Object.values(parsed.roots??{}).filter((node)=>node&&typeof node==="object"&&node.id)}function walkBookmarks(nodes,visit,acc=[]){for(const node of nodes){visit(node,acc);if(Array.isArray(node.children))walkBookmarks(node.children,visit,acc)}return acc}function findBookmark(id){for(const root of bookmarkRoots()){const found=walkBookmarks([root],(node,acc)=>{if(node.id===String(id))acc.push(node)});if(found.length>0)return found[0]}return null}function toBookmark(node){const out={id:node.id,title:node.name??"",type:node.type};if(node.url)out.url=node.url;if(node.date_added)out.dateAdded=chromeTimeToMs(node.date_added);if(node.children)out.children=node.children.map(toBookmark);return out}const bookmarks={async get(idOrIdList){const ids=Array.isArray(idOrIdList)?idOrIdList:[idOrIdList];return ids.map(findBookmark).filter(Boolean).map(toBookmark)},async getChildren(id){const node=findBookmark(id);return(node?.children??[]).map(toBookmark)},async getRecent(numberOfItems){const urls=walkBookmarks(bookmarkRoots(),(node,acc)=>{if(node.type==="url")acc.push(node)});return urls.sort((a,b)=>Number(b.date_added??0)-Number(a.date_added??0)).slice(0,numberOfItems).map(toBookmark)},async getSubTree(id){const node=findBookmark(id);return node?[toBookmark(node)]:[]},async getTree(){return bookmarkRoots().map(toBookmark)},async search(query){const needle=typeof query==="string"?query.toLowerCase():(query.query??"").toLowerCase();const url=typeof query==="object"?query.url?.toLowerCase():undefined;const title=typeof query==="object"?query.title?.toLowerCase():undefined;return walkBookmarks(bookmarkRoots(),(node,acc)=>{if(node.type!=="url")return;const name=(node.name??"").toLowerCase();const href=(node.url??"").toLowerCase();const ok=needle&&(name.includes(needle)||href.includes(needle))||url&&href.includes(url)||title&&name.includes(title);if(ok)acc.push(node)}).map(toBookmark)},async create(){throw new UnsupportedOperationError(`chrome.bookmarks.create ${EXTENSION_BRIDGE_REQUIRED}`)},async update(){throw new UnsupportedOperationError(`chrome.bookmarks.update ${EXTENSION_BRIDGE_REQUIRED}`)},async move(){throw new UnsupportedOperationError(`chrome.bookmarks.move ${EXTENSION_BRIDGE_REQUIRED}`)},async remove(){throw new UnsupportedOperationError(`chrome.bookmarks.remove ${EXTENSION_BRIDGE_REQUIRED}`)},async removeTree(){throw new UnsupportedOperationError(`chrome.bookmarks.removeTree ${EXTENSION_BRIDGE_REQUIRED}`)}};const history={async search(query){const text=`%${query.text??""}%`;const start=query.startTime!=null?msToChromeTime(query.startTime):0n;const end=query.endTime!=null?msToChromeTime(query.endTime):99999999999999999n;const max=query.maxResults??100;return await withDb("History",(db)=>db.prepare(`SELECT id, url, title, visit_count, last_visit_time FROM urls
1491
1492
  WHERE (url LIKE ? OR title LIKE ?) AND last_visit_time BETWEEN ? AND ?
1492
1493
  ORDER BY last_visit_time DESC LIMIT ?`).all(text,text,start,end,max).map((row)=>({id:String(row.id),url:row.url,title:row.title??"",visitCount:Number(row.visit_count),lastVisitTime:chromeTimeToMs(row.last_visit_time)})))},async getVisits(details){return await withDb("History",(db)=>db.prepare(`SELECT v.id, v.visit_time, v.transition FROM visits v
1493
1494
  JOIN urls u ON u.id = v.url WHERE u.url = ?
1494
1495
  ORDER BY v.visit_time DESC`).all(details.url).map((row)=>({id:String(row.id),url:details.url,visitTime:chromeTimeToMs(row.visit_time),transition:Number(row.transition)})))},async addUrl(){throw new UnsupportedOperationError(`chrome.history.addUrl ${EXTENSION_BRIDGE_REQUIRED}`)},async deleteUrl(){throw new UnsupportedOperationError(`chrome.history.deleteUrl ${EXTENSION_BRIDGE_REQUIRED}`)},async deleteRange(){throw new UnsupportedOperationError(`chrome.history.deleteRange ${EXTENSION_BRIDGE_REQUIRED}`)}};const topSites={async get(){return(await withDb("Top Sites",(db)=>db.prepare(`SELECT url, title, url_rank FROM top_sites ORDER BY url_rank ASC LIMIT 50`).all())).map((row)=>({url:row.url,title:row.title??""}))}};return{bookmarks,history,topSites,withDb}}var EXTENSION_BRIDGE_REQUIRED2="requires the extension bridge in standalone OmOWright";var DOWNLOAD_TIMEOUT_MS=30000;function matchUrlPattern(url,pattern){const patterns=Array.isArray(pattern)?pattern:[pattern];return patterns.some((item)=>{if(!item.includes("*"))return url.startsWith(item);const regex=new RegExp(`^${item.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")}$`);return regex.test(url)})}function createChromeApi(connection,options={}){const profile=createProfileApis(options.profilePath);const cdp=connection.cdp;async function pageTargets(){return(await connection.listTargets()).filter((target)=>target.type==="page")}async function toTab(target){let windowId=0;try{const win=await cdp.send("Browser.getWindowForTarget",{targetId:target.targetId??target.id});windowId=win.windowId}catch{}return{id:target.targetId??target.id,url:target.url??"",title:target.title??"",windowId,active:false,pinned:false,audible:false,discarded:false,groupId:-1,index:-1}}const tabs={async query(queryInfo={}){let targets=await pageTargets();if(queryInfo.url)targets=targets.filter((t)=>matchUrlPattern(t.url??"",queryInfo.url));if(queryInfo.title)targets=targets.filter((t)=>(t.title??"").includes(queryInfo.title));const out=[];for(const target of targets){const tab=await toTab(target);if(queryInfo.windowId!=null&&tab.windowId!==queryInfo.windowId)continue;out.push(tab)}return out},async get(tabId){const targets=await pageTargets();const target=targets.find((t)=>(t.targetId??t.id)===tabId);if(!target)throw new Error(`No tab with id: ${tabId}`);return toTab(target)}};async function collectWindows(queryOptions={}){const byWindow=new Map;for(const target of await pageTargets()){const tab=await toTab(target);const list=byWindow.get(tab.windowId)??[];list.push(tab);byWindow.set(tab.windowId,list)}return[...byWindow.entries()].map(([windowId,windowTabs],index)=>{const win={id:windowId,focused:index===0,type:"normal",state:"normal",alwaysOnTop:false,incognito:false};if(queryOptions.populate)win.tabs=windowTabs;return win})}const windows={async get(windowId,queryOptions={}){const all=await collectWindows(queryOptions);const win=all.find((item)=>item.id===windowId);if(!win)throw new Error(`No window with id: ${windowId}`);return win},async getCurrent(queryOptions={}){const all=await collectWindows(queryOptions);if(all.length===0)throw new Error("No browser window found");return all[0]},async getLastFocused(queryOptions={}){const all=await collectWindows(queryOptions);if(all.length===0)throw new Error("No browser window found");return all[0]},async getAll(queryOptions={}){return collectWindows(queryOptions)}};const downloads={async search(query={}){return profile.withDb("History",(db)=>{const rows=db.prepare(`SELECT d.id, d.target_path, d.start_time, d.received_bytes, d.total_bytes, d.state,
1495
1496
  (SELECT url FROM downloads_url_chains c WHERE c.id = d.id AND c.chain_index = 0) AS url
1496
- FROM downloads d ORDER BY d.start_time DESC LIMIT ?`).all(query.limit??100);return rows.map((row)=>({id:Number(row.id),url:row.url??"",filename:row.target_path,startTime:row.start_time?new Date(chromeTimeToMs(row.start_time)).toISOString():null,bytesReceived:Number(row.received_bytes),totalBytes:Number(row.total_bytes),state:Number(row.state)===1?"complete":Number(row.state)===2?"canceled":Number(row.state)===3?"interrupted":"in_progress"})).filter((item)=>!query.url||item.url.includes(query.url)).filter((item)=>!query.filename||item.filename.includes(query.filename))})},async download(downloadOptions){const downloadDir=options.downloadDir??path9.join(tmpdir3(),"omowright-downloads");mkdirSync(downloadDir,{recursive:true});let resolveBegin;let resolveDone;let rejectDone;const began=new Promise((resolve)=>{resolveBegin=resolve});const done=new Promise((resolve,reject)=>{resolveDone=resolve;rejectDone=reject});const offBegin=cdp.on("Browser.downloadWillBegin",(params)=>resolveBegin(params));const offProgress=cdp.on("Browser.downloadProgress",(params)=>{if(params.state==="completed")resolveDone(params);else if(params.state==="canceled")rejectDone(new Error("Download was canceled"))});const timer=setTimeout(()=>rejectDone(new Error(`chrome.downloads.download timed out after ${DOWNLOAD_TIMEOUT_MS}ms`)),DOWNLOAD_TIMEOUT_MS);const page=await connection.newTab("about:blank");try{await cdp.send("Browser.setDownloadBehavior",{behavior:"allow",downloadPath:downloadDir,eventsEnabled:true});await page.evaluate(`(() => {
1497
+ FROM downloads d ORDER BY d.start_time DESC LIMIT ?`).all(query.limit??100);return rows.map((row)=>({id:Number(row.id),url:row.url??"",filename:row.target_path,startTime:row.start_time?new Date(chromeTimeToMs(row.start_time)).toISOString():null,bytesReceived:Number(row.received_bytes),totalBytes:Number(row.total_bytes),state:Number(row.state)===1?"complete":Number(row.state)===2?"canceled":Number(row.state)===3?"interrupted":"in_progress"})).filter((item)=>!query.url||item.url.includes(query.url)).filter((item)=>!query.filename||item.filename.includes(query.filename))})},async download(downloadOptions){const downloadDir=options.downloadDir??path10.join(tmpdir3(),"omowright-downloads");mkdirSync2(downloadDir,{recursive:true});let resolveBegin;let resolveDone;let rejectDone;const began=new Promise((resolve)=>{resolveBegin=resolve});const done=new Promise((resolve,reject)=>{resolveDone=resolve;rejectDone=reject});const offBegin=cdp.on("Browser.downloadWillBegin",(params)=>resolveBegin(params));const offProgress=cdp.on("Browser.downloadProgress",(params)=>{if(params.state==="completed")resolveDone(params);else if(params.state==="canceled")rejectDone(new Error("Download was canceled"))});const timer=setTimeout(()=>rejectDone(new Error(`chrome.downloads.download timed out after ${DOWNLOAD_TIMEOUT_MS}ms`)),DOWNLOAD_TIMEOUT_MS);const page=await connection.newTab("about:blank");try{await cdp.send("Browser.setDownloadBehavior",{behavior:"allow",downloadPath:downloadDir,eventsEnabled:true});await page.evaluate(`(() => {
1497
1498
  const a = document.createElement("a");
1498
1499
  a.href = ${JSON.stringify(downloadOptions.url)};
1499
1500
  document.body.appendChild(a);
1500
1501
  a.click();
1501
- })()`);const begin=await Promise.race([began,done.then(()=>null)]);if(!begin)throw new Error("No download started for the given URL");await done;const filename=downloadOptions.filename??begin.suggestedFilename;return{id:begin.guid,url:begin.url??downloadOptions.url,filename:path9.join(downloadDir,filename??"download"),state:"complete"}}finally{clearTimeout(timer);offBegin();offProgress();await page.close().catch(()=>{})}},async pause(){throw new UnsupportedOperationError(`chrome.downloads.pause ${EXTENSION_BRIDGE_REQUIRED2}`)},async resume(){throw new UnsupportedOperationError(`chrome.downloads.resume ${EXTENSION_BRIDGE_REQUIRED2}`)},async cancel(){throw new UnsupportedOperationError(`chrome.downloads.cancel ${EXTENSION_BRIDGE_REQUIRED2}`)},async erase(){throw new UnsupportedOperationError(`chrome.downloads.erase ${EXTENSION_BRIDGE_REQUIRED2}`)}};return{tabs,windows,downloads,bookmarks:profile.bookmarks,history:profile.history,topSites:profile.topSites}}var HOST_NAME="com.omowright.cloakbridge";var PROTOCOL=1;var MAX_MESSAGE_BYTES=1024*1024;var EVENT_NAMES2=Object.freeze(["notifications.shown","notifications.clicked","notifications.closed","tabGroups.created","tabGroups.updated","tabGroups.moved","tabs.activated","windows.focusChanged"]);var COMMAND_NAMES=Object.freeze(["bookmarks.create","bookmarks.move","bookmarks.remove","history.deleteUrl","history.deleteRange","tabGroups.update","debugger.attach","debugger.detach"]);var MUTATING_COMMANDS=new Set(COMMAND_NAMES);var id=string2().min(1).max(128);var uuid3=string2().regex(/^[0-9a-fA-F-]{16,128}$/);var finite=number2().finite();var color=_enum2(["grey","blue","red","yellow","green","pink","purple","cyan","orange"]);var group=object({id:number2().int().min(0),collapsed:boolean2(),color,title:string2().max(1024).nullable().optional(),windowId:number2().int().min(-1)}).strict();var bookmarkNode=lazy(()=>object({id:string2().min(1),parentId:string2().min(1).optional(),index:number2().int().min(0).optional(),title:string2(),url:string2().optional(),dateAdded:finite.min(0).optional(),dateGroupModified:finite.min(0).optional(),dateLastUsed:finite.min(0).optional(),folderType:string2().optional(),syncing:boolean2().optional(),unmodifiable:string2().optional(),type:_enum2(["bookmark","folder","url"]).optional(),children:array(bookmarkNode).optional()}).strict());var empty=object({}).strict();var payloadSchemas={"notifications.shown":object({notificationId:string2().min(1).max(1024)}).strict(),"notifications.clicked":object({notificationId:string2().min(1).max(1024)}).strict(),"notifications.closed":object({notificationId:string2().min(1).max(1024),byUser:boolean2()}).strict(),"tabGroups.created":object({group}).strict(),"tabGroups.updated":object({group}).strict(),"tabGroups.moved":object({group}).strict(),"tabs.activated":object({tabId:number2().int().min(0),windowId:number2().int().min(-1),previousTabId:number2().int().min(0).optional()}).strict(),"windows.focusChanged":object({windowId:number2().int().min(-1)}).strict()};var commandParams={"bookmarks.create":object({details:object({parentId:string2().min(1).optional(),index:number2().int().min(0).optional(),title:string2().max(8192),url:string2().url().max(8192).optional()}).strict()}).strict(),"bookmarks.move":object({details:object({id:string2().min(1),parentId:string2().min(1).optional(),index:number2().int().min(0).optional()}).strict()}).strict(),"bookmarks.remove":object({details:object({id:string2().min(1)}).strict()}).strict(),"history.deleteUrl":object({details:object({url:string2().url().max(8192)}).strict()}).strict(),"history.deleteRange":object({details:object({startTime:finite.min(0),endTime:finite.min(0)}).strict()}).strict().refine((v)=>v.details.startTime<=v.details.endTime,"startTime must not exceed endTime"),"tabGroups.update":object({details:object({groupId:number2().int().min(0),updateProperties:object({collapsed:boolean2().optional(),color:color.optional(),title:string2().max(1024).nullable().optional()}).strict().refine((v)=>Object.keys(v).length>0,"updateProperties must not be empty")}).strict()}).strict(),"debugger.attach":object({details:object({tabId:number2().int().min(0),requiredVersion:string2().regex(/^\d+\.\d+$/)}).strict()}).strict(),"debugger.detach":object({details:object({tabId:number2().int().min(0)}).strict()}).strict()};var resultSchemas={"bookmarks.create":bookmarkNode,"bookmarks.move":bookmarkNode,"bookmarks.remove":empty,"history.deleteUrl":empty,"history.deleteRange":empty,"tabGroups.update":group,"debugger.attach":object({tabId:number2().int().min(0),attached:literal(true),requiredVersion:string2()}).strict(),"debugger.detach":object({tabId:number2().int().min(0),detached:literal(true)}).strict()};var base={protocol:literal(PROTOCOL)};function unique(values,ctx){if(new Set(values).size!==values.length)ctx.addIssue({code:ZodIssueCode.custom,message:"array entries must be unique"})}var names=(allowed)=>array(_enum2([...allowed])).superRefine(unique);var ExtensionHelloSchema=object({...base,type:literal("hello"),role:literal("extension"),connectionId:id,extensionId:string2().regex(/^[a-p]{32}$/),extensionVersion:string2().min(1).max(64),events:names(EVENT_NAMES2),commands:names(COMMAND_NAMES),maxMessageBytes:number2().int().min(1).max(MAX_MESSAGE_BYTES)}).strict();var HostHelloSchema=object({...base,type:literal("hello"),role:literal("host"),connectionId:id,hostName:literal(HOST_NAME),hostVersion:string2().min(1).max(64),events:names(EVENT_NAMES2),commands:names(COMMAND_NAMES),maxMessageBytes:number2().int().min(1).max(MAX_MESSAGE_BYTES)}).strict();var EventSchema=object({...base,type:literal("event"),connectionId:id,seq:number2().int().min(1),occurredAt:number2().int().min(0),name:_enum2(EVENT_NAMES2),payload:record(string2(),unknown())}).strict().superRefine((v,ctx)=>{if(!payloadSchemas[v.name].safeParse(v.payload).success)ctx.addIssue({code:ZodIssueCode.custom,message:"invalid event payload"})});var CommandSchema=object({...base,type:literal("command"),requestId:uuid3,name:_enum2(COMMAND_NAMES),params:record(string2(),unknown()),deadlineAt:number2().int().min(0).optional()}).strict().superRefine((v,ctx)=>{if(!commandParams[v.name].safeParse(v.params).success)ctx.addIssue({code:ZodIssueCode.custom,message:"invalid command parameters"})});var errorSchema=object({code:_enum2(["INVALID_MESSAGE","UNKNOWN_COMMAND","UNSUPPORTED_COMMAND","CHROME_ERROR","DEBUGGER_ERROR","NATIVE_HOST_DISCONNECTED","TIMEOUT","PAYLOAD_TOO_LARGE","INTERNAL_ERROR"]),message:string2().min(1).max(512),details:record(string2(),unknown()).optional()}).strict();var responseNames=[...COMMAND_NAMES,"protocolError"];var ResponseSchema=object({...base,type:literal("response"),requestId:uuid3,name:_enum2(responseNames),ok:boolean2(),result:unknown().optional(),error:errorSchema.optional()}).strict().superRefine((v,ctx)=>{const valid=v.ok?v.result!==undefined&&v.error===undefined&&v.name!=="protocolError":v.error!==undefined&&v.result===undefined;if(!valid)ctx.addIssue({code:ZodIssueCode.custom,message:"response must contain exactly one result or error"});if(v.ok&&resultSchemas[v.name]&&!resultSchemas[v.name].safeParse(v.result).success)ctx.addIssue({code:ZodIssueCode.custom,message:"invalid command result"})});class BridgeUnavailableError extends Error{constructor(message="Cloak bridge is unavailable"){super(message);this.name="BridgeUnavailableError"}}class BridgeProtocolError extends Error{constructor(message){super(message);this.name="BridgeProtocolError"}}var decoder=new TextDecoder2("utf-8",{fatal:true});var checked=(schema,value,message)=>{const result=schema.safeParse(value);if(!result.success)throw new BridgeProtocolError(message);return result.data};function schemaFor(value){if(value.type==="hello")return value.role==="host"?HostHelloSchema:ExtensionHelloSchema;if(value.type==="event")return EventSchema;if(value.type==="command")return CommandSchema;if(value.type==="response")return ResponseSchema;return null}function encodeFrame(value,maxMessageBytes=MAX_MESSAGE_BYTES){const schema=schemaFor(value);if(schema)checked(schema,value,"invalid outbound message");let json;try{json=JSON.stringify(value)}catch{throw new BridgeProtocolError("invalid JSON")}const body=Buffer.from(json,"utf8");if(body.length===0||body.length>maxMessageBytes)throw new BridgeProtocolError("PAYLOAD_TOO_LARGE");const frame=Buffer.allocUnsafe(4+body.length);frame.writeUInt32LE(body.length,0);body.copy(frame,4);return frame}function decodeFrames(buffer,maxMessageBytes=MAX_MESSAGE_BYTES){const frames=[];let offset=0;while(buffer.length-offset>=4){const length=buffer.readUInt32LE(offset);if(!length||length>maxMessageBytes)throw new BridgeProtocolError("PAYLOAD_TOO_LARGE");if(buffer.length-offset<length+4)break;const body=buffer.subarray(offset+4,offset+4+length);let text;try{text=decoder.decode(body)}catch{throw new BridgeProtocolError("invalid UTF-8")}try{frames.push(JSON.parse(text))}catch{throw new BridgeProtocolError("invalid JSON")}offset+=length+4}return{frames,remainder:buffer.subarray(offset)}}function createNativeMessagingHost({stdin=process.stdin,stdout=process.stdout,stderr=process.stderr,extensionId,hostName=HOST_NAME,maxMessageBytes=MAX_MESSAGE_BYTES,commandTimeoutMs=30000,maxPendingWrites=256,onEvent,onDisconnect}={}){if(!/^[a-p]{32}$/.test(extensionId??""))throw new BridgeProtocolError("a valid extensionId is required");if(!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(hostName)||hostName!==HOST_NAME)throw new BridgeProtocolError("invalid hostName");let buffer=Buffer.alloc(0),closed=false,hello=null,hostConnectionId=null,effectiveMax=maxMessageBytes,lastEventSeq=0,sequence=0,mutationBusy=false;const pending=new Map,listeners=new Map,commandQueue=[],writeQueue=[];let writing=false;const disconnect=(reason)=>{if(closed)return;closed=true;const error=reason instanceof Error?reason:new BridgeUnavailableError(reason??"native host disconnected");for(const item of pending.values()){item.cleanup?.();item.reject(error)}pending.clear();for(const item of commandQueue.splice(0)){item.cleanup?.();item.reject(error)}for(const item of writeQueue.splice(0))item.reject(error);onDisconnect?.(error)};const write=(value)=>{if(closed)return Promise.reject(new BridgeUnavailableError);let frame;try{const schema=schemaFor(value);if(!schema)throw new BridgeProtocolError("invalid outbound message type");checked(schema,value,"invalid outbound message");frame=encodeFrame(value,effectiveMax)}catch(error){return Promise.reject(error)}if(writeQueue.length+(writing?1:0)>=maxPendingWrites)return Promise.reject(new BridgeProtocolError("BOUNDED_TRANSPORT"));return new Promise((resolve,reject)=>{writeQueue.push({frame,resolve,reject});pumpWrites()})};const pumpWrites=()=>{if(writing||closed||!writeQueue.length)return;writing=true;const item=writeQueue.shift();let finished=false;const done=(error)=>{if(finished)return;finished=true;writing=false;if(error){item.reject(error);disconnect(error)}else item.resolve();pumpWrites()};try{const result=stdout.write(item.frame,(error)=>done(error));if(result===false&&stdout.once)stdout.once("error",done)}catch(error){done(error)}};const pumpCommands=()=>{if(closed||mutationBusy||!commandQueue.length)return;const item=commandQueue.shift();item.started=true;mutationBusy=true;pending.set(item.requestId,item);write(item.command).catch((error)=>{if(pending.delete(item.requestId)){item.settled=true;item.cleanup();item.reject(error)}disconnect(error)})};const dispatchEvent=(event)=>{if(!hello||event.connectionId!==hello.connectionId||event.seq<=lastEventSeq||!hello.events.includes(event.name))throw new BridgeProtocolError("invalid event ordering or capability");lastEventSeq=event.seq;try{Promise.resolve(onEvent?.(event)).catch((error)=>stderr.write?.(`bridge listener: ${error.message}
1502
+ })()`);const begin=await Promise.race([began,done.then(()=>null)]);if(!begin)throw new Error("No download started for the given URL");await done;const filename=downloadOptions.filename??begin.suggestedFilename;return{id:begin.guid,url:begin.url??downloadOptions.url,filename:path10.join(downloadDir,filename??"download"),state:"complete"}}finally{clearTimeout(timer);offBegin();offProgress();await page.close().catch(()=>{})}},async pause(){throw new UnsupportedOperationError(`chrome.downloads.pause ${EXTENSION_BRIDGE_REQUIRED2}`)},async resume(){throw new UnsupportedOperationError(`chrome.downloads.resume ${EXTENSION_BRIDGE_REQUIRED2}`)},async cancel(){throw new UnsupportedOperationError(`chrome.downloads.cancel ${EXTENSION_BRIDGE_REQUIRED2}`)},async erase(){throw new UnsupportedOperationError(`chrome.downloads.erase ${EXTENSION_BRIDGE_REQUIRED2}`)}};return{tabs,windows,downloads,bookmarks:profile.bookmarks,history:profile.history,topSites:profile.topSites}}var HOST_NAME="com.omowright.cloakbridge";var PROTOCOL=1;var MAX_MESSAGE_BYTES=1024*1024;var EVENT_NAMES2=Object.freeze(["notifications.shown","notifications.clicked","notifications.closed","tabGroups.created","tabGroups.updated","tabGroups.moved","tabs.activated","windows.focusChanged"]);var COMMAND_NAMES=Object.freeze(["bookmarks.create","bookmarks.move","bookmarks.remove","history.deleteUrl","history.deleteRange","tabGroups.update","debugger.attach","debugger.detach"]);var MUTATING_COMMANDS=new Set(COMMAND_NAMES);var id=string2().min(1).max(128);var uuid3=string2().regex(/^[0-9a-fA-F-]{16,128}$/);var finite=number2().finite();var color=_enum2(["grey","blue","red","yellow","green","pink","purple","cyan","orange"]);var group=object({id:number2().int().min(0),collapsed:boolean2(),color,title:string2().max(1024).nullable().optional(),windowId:number2().int().min(-1)}).strict();var bookmarkNode=lazy(()=>object({id:string2().min(1),parentId:string2().min(1).optional(),index:number2().int().min(0).optional(),title:string2(),url:string2().optional(),dateAdded:finite.min(0).optional(),dateGroupModified:finite.min(0).optional(),dateLastUsed:finite.min(0).optional(),folderType:string2().optional(),syncing:boolean2().optional(),unmodifiable:string2().optional(),type:_enum2(["bookmark","folder","url"]).optional(),children:array(bookmarkNode).optional()}).strict());var empty=object({}).strict();var payloadSchemas={"notifications.shown":object({notificationId:string2().min(1).max(1024)}).strict(),"notifications.clicked":object({notificationId:string2().min(1).max(1024)}).strict(),"notifications.closed":object({notificationId:string2().min(1).max(1024),byUser:boolean2()}).strict(),"tabGroups.created":object({group}).strict(),"tabGroups.updated":object({group}).strict(),"tabGroups.moved":object({group}).strict(),"tabs.activated":object({tabId:number2().int().min(0),windowId:number2().int().min(-1),previousTabId:number2().int().min(0).optional()}).strict(),"windows.focusChanged":object({windowId:number2().int().min(-1)}).strict()};var commandParams={"bookmarks.create":object({details:object({parentId:string2().min(1).optional(),index:number2().int().min(0).optional(),title:string2().max(8192),url:string2().url().max(8192).optional()}).strict()}).strict(),"bookmarks.move":object({details:object({id:string2().min(1),parentId:string2().min(1).optional(),index:number2().int().min(0).optional()}).strict()}).strict(),"bookmarks.remove":object({details:object({id:string2().min(1)}).strict()}).strict(),"history.deleteUrl":object({details:object({url:string2().url().max(8192)}).strict()}).strict(),"history.deleteRange":object({details:object({startTime:finite.min(0),endTime:finite.min(0)}).strict()}).strict().refine((v)=>v.details.startTime<=v.details.endTime,"startTime must not exceed endTime"),"tabGroups.update":object({details:object({groupId:number2().int().min(0),updateProperties:object({collapsed:boolean2().optional(),color:color.optional(),title:string2().max(1024).nullable().optional()}).strict().refine((v)=>Object.keys(v).length>0,"updateProperties must not be empty")}).strict()}).strict(),"debugger.attach":object({details:object({tabId:number2().int().min(0),requiredVersion:string2().regex(/^\d+\.\d+$/)}).strict()}).strict(),"debugger.detach":object({details:object({tabId:number2().int().min(0)}).strict()}).strict()};var resultSchemas={"bookmarks.create":bookmarkNode,"bookmarks.move":bookmarkNode,"bookmarks.remove":empty,"history.deleteUrl":empty,"history.deleteRange":empty,"tabGroups.update":group,"debugger.attach":object({tabId:number2().int().min(0),attached:literal(true),requiredVersion:string2()}).strict(),"debugger.detach":object({tabId:number2().int().min(0),detached:literal(true)}).strict()};var base={protocol:literal(PROTOCOL)};function unique(values,ctx){if(new Set(values).size!==values.length)ctx.addIssue({code:ZodIssueCode.custom,message:"array entries must be unique"})}var names=(allowed)=>array(_enum2([...allowed])).superRefine(unique);var ExtensionHelloSchema=object({...base,type:literal("hello"),role:literal("extension"),connectionId:id,extensionId:string2().regex(/^[a-p]{32}$/),extensionVersion:string2().min(1).max(64),events:names(EVENT_NAMES2),commands:names(COMMAND_NAMES),maxMessageBytes:number2().int().min(1).max(MAX_MESSAGE_BYTES)}).strict();var HostHelloSchema=object({...base,type:literal("hello"),role:literal("host"),connectionId:id,hostName:literal(HOST_NAME),hostVersion:string2().min(1).max(64),events:names(EVENT_NAMES2),commands:names(COMMAND_NAMES),maxMessageBytes:number2().int().min(1).max(MAX_MESSAGE_BYTES)}).strict();var EventSchema=object({...base,type:literal("event"),connectionId:id,seq:number2().int().min(1),occurredAt:number2().int().min(0),name:_enum2(EVENT_NAMES2),payload:record(string2(),unknown())}).strict().superRefine((v,ctx)=>{if(!payloadSchemas[v.name].safeParse(v.payload).success)ctx.addIssue({code:ZodIssueCode.custom,message:"invalid event payload"})});var CommandSchema=object({...base,type:literal("command"),requestId:uuid3,name:_enum2(COMMAND_NAMES),params:record(string2(),unknown()),deadlineAt:number2().int().min(0).optional()}).strict().superRefine((v,ctx)=>{if(!commandParams[v.name].safeParse(v.params).success)ctx.addIssue({code:ZodIssueCode.custom,message:"invalid command parameters"})});var errorSchema=object({code:_enum2(["INVALID_MESSAGE","UNKNOWN_COMMAND","UNSUPPORTED_COMMAND","CHROME_ERROR","DEBUGGER_ERROR","NATIVE_HOST_DISCONNECTED","TIMEOUT","PAYLOAD_TOO_LARGE","INTERNAL_ERROR"]),message:string2().min(1).max(512),details:record(string2(),unknown()).optional()}).strict();var responseNames=[...COMMAND_NAMES,"protocolError"];var ResponseSchema=object({...base,type:literal("response"),requestId:uuid3,name:_enum2(responseNames),ok:boolean2(),result:unknown().optional(),error:errorSchema.optional()}).strict().superRefine((v,ctx)=>{const valid=v.ok?v.result!==undefined&&v.error===undefined&&v.name!=="protocolError":v.error!==undefined&&v.result===undefined;if(!valid)ctx.addIssue({code:ZodIssueCode.custom,message:"response must contain exactly one result or error"});if(v.ok&&resultSchemas[v.name]&&!resultSchemas[v.name].safeParse(v.result).success)ctx.addIssue({code:ZodIssueCode.custom,message:"invalid command result"})});class BridgeUnavailableError extends Error{constructor(message="Cloak bridge is unavailable"){super(message);this.name="BridgeUnavailableError"}}class BridgeProtocolError extends Error{constructor(message){super(message);this.name="BridgeProtocolError"}}var decoder=new TextDecoder2("utf-8",{fatal:true});var checked=(schema,value,message)=>{const result=schema.safeParse(value);if(!result.success)throw new BridgeProtocolError(message);return result.data};function schemaFor(value){if(value.type==="hello")return value.role==="host"?HostHelloSchema:ExtensionHelloSchema;if(value.type==="event")return EventSchema;if(value.type==="command")return CommandSchema;if(value.type==="response")return ResponseSchema;return null}function encodeFrame(value,maxMessageBytes=MAX_MESSAGE_BYTES){const schema=schemaFor(value);if(schema)checked(schema,value,"invalid outbound message");let json;try{json=JSON.stringify(value)}catch{throw new BridgeProtocolError("invalid JSON")}const body=Buffer.from(json,"utf8");if(body.length===0||body.length>maxMessageBytes)throw new BridgeProtocolError("PAYLOAD_TOO_LARGE");const frame=Buffer.allocUnsafe(4+body.length);frame.writeUInt32LE(body.length,0);body.copy(frame,4);return frame}function decodeFrames(buffer,maxMessageBytes=MAX_MESSAGE_BYTES){const frames=[];let offset=0;while(buffer.length-offset>=4){const length=buffer.readUInt32LE(offset);if(!length||length>maxMessageBytes)throw new BridgeProtocolError("PAYLOAD_TOO_LARGE");if(buffer.length-offset<length+4)break;const body=buffer.subarray(offset+4,offset+4+length);let text;try{text=decoder.decode(body)}catch{throw new BridgeProtocolError("invalid UTF-8")}try{frames.push(JSON.parse(text))}catch{throw new BridgeProtocolError("invalid JSON")}offset+=length+4}return{frames,remainder:buffer.subarray(offset)}}function createNativeMessagingHost({stdin=process.stdin,stdout=process.stdout,stderr=process.stderr,extensionId,hostName=HOST_NAME,maxMessageBytes=MAX_MESSAGE_BYTES,commandTimeoutMs=30000,maxPendingWrites=256,onEvent,onDisconnect}={}){if(!/^[a-p]{32}$/.test(extensionId??""))throw new BridgeProtocolError("a valid extensionId is required");if(!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(hostName)||hostName!==HOST_NAME)throw new BridgeProtocolError("invalid hostName");let buffer=Buffer.alloc(0),closed=false,hello=null,hostConnectionId=null,effectiveMax=maxMessageBytes,lastEventSeq=0,sequence=0,mutationBusy=false;const pending=new Map,listeners=new Map,commandQueue=[],writeQueue=[];let writing=false;const disconnect=(reason)=>{if(closed)return;closed=true;const error=reason instanceof Error?reason:new BridgeUnavailableError(reason??"native host disconnected");for(const item of pending.values()){item.cleanup?.();item.reject(error)}pending.clear();for(const item of commandQueue.splice(0)){item.cleanup?.();item.reject(error)}for(const item of writeQueue.splice(0))item.reject(error);onDisconnect?.(error)};const write=(value)=>{if(closed)return Promise.reject(new BridgeUnavailableError);let frame;try{const schema=schemaFor(value);if(!schema)throw new BridgeProtocolError("invalid outbound message type");checked(schema,value,"invalid outbound message");frame=encodeFrame(value,effectiveMax)}catch(error){return Promise.reject(error)}if(writeQueue.length+(writing?1:0)>=maxPendingWrites)return Promise.reject(new BridgeProtocolError("BOUNDED_TRANSPORT"));return new Promise((resolve,reject)=>{writeQueue.push({frame,resolve,reject});pumpWrites()})};const pumpWrites=()=>{if(writing||closed||!writeQueue.length)return;writing=true;const item=writeQueue.shift();let finished=false;const done=(error)=>{if(finished)return;finished=true;writing=false;if(error){item.reject(error);disconnect(error)}else item.resolve();pumpWrites()};try{const result=stdout.write(item.frame,(error)=>done(error));if(result===false&&stdout.once)stdout.once("error",done)}catch(error){done(error)}};const pumpCommands=()=>{if(closed||mutationBusy||!commandQueue.length)return;const item=commandQueue.shift();item.started=true;mutationBusy=true;pending.set(item.requestId,item);write(item.command).catch((error)=>{if(pending.delete(item.requestId)){item.settled=true;item.cleanup();item.reject(error)}disconnect(error)})};const dispatchEvent=(event)=>{if(!hello||event.connectionId!==hello.connectionId||event.seq<=lastEventSeq||!hello.events.includes(event.name))throw new BridgeProtocolError("invalid event ordering or capability");lastEventSeq=event.seq;try{Promise.resolve(onEvent?.(event)).catch((error)=>stderr.write?.(`bridge listener: ${error.message}
1502
1503
  `))}catch(error){stderr.write?.(`bridge listener: ${error.message}
1503
1504
  `)}for(const fn of listeners.get(event.name)??[]){try{Promise.resolve(fn(event.payload)).catch((error)=>stderr.write?.(`bridge listener: ${error.message}
1504
1505
  `))}catch(error){stderr.write?.(`bridge listener: ${error.message}
1505
1506
  `)}}};const handle=(raw)=>{if(!hello){const parsed=checked(ExtensionHelloSchema,raw,"extension hello rejected");if(parsed.extensionId!==extensionId)throw new BridgeProtocolError("extension hello rejected");hello=parsed;hostConnectionId=randomUUID4();effectiveMax=Math.min(maxMessageBytes,parsed.maxMessageBytes);lastEventSeq=0;return write({protocol:PROTOCOL,type:"hello",role:"host",connectionId:hostConnectionId,hostName,hostVersion:"0.1.0",events:EVENT_NAMES2.filter((x)=>parsed.events.includes(x)),commands:COMMAND_NAMES.filter((x)=>parsed.commands.includes(x)),maxMessageBytes:effectiveMax})}if(raw.type==="event")return dispatchEvent(checked(EventSchema,raw,"invalid event"));if(raw.type==="response"){const parsed=checked(ResponseSchema,raw,"invalid response");if(parsed.name==="protocolError")throw Object.assign(new BridgeProtocolError(parsed.error.message),{code:parsed.error.code,details:parsed.error.details});const item=pending.get(parsed.requestId);if(!item||item.name!==parsed.name)throw new BridgeProtocolError("invalid or unknown response");item.settled=true;pending.delete(parsed.requestId);item.cleanup();mutationBusy=false;if(parsed.ok)item.resolve(parsed.result);else item.reject(Object.assign(new Error(parsed.error.message),{code:parsed.error.code,details:parsed.error.details}));pumpCommands();return}throw new BridgeProtocolError("unexpected message")};const onData=(chunk)=>{try{buffer=Buffer.concat([buffer,Buffer.from(chunk)]);const out=decodeFrames(buffer,effectiveMax);buffer=out.remainder;for(const raw of out.frames)Promise.resolve(handle(raw)).catch((error)=>{stderr.write?.(`bridge: ${error.message}
1506
1507
  `);disconnect(error)})}catch(error){stderr.write?.(`bridge: ${error.message}
1507
- `);disconnect(error)}};stdin.on("data",onData);stdin.once?.("end",()=>disconnect());stdin.once?.("error",disconnect);return{get isConnected(){return!closed&&!!hello},get capabilities(){return{commands:new Set(hello?.commands??[]),events:new Set(hello?.events??[])}},send:write,request(name,params,{signal,timeoutMs=commandTimeoutMs}={}){if(!hello||!hello.commands.includes(name))return Promise.reject(new BridgeUnavailableError(`unsupported bridge command: ${name}`));let command;try{command=checked(CommandSchema,{protocol:PROTOCOL,type:"command",requestId:randomUUID4(),name,params},"invalid command parameters")}catch(error){return Promise.reject(error)}return new Promise((resolve,reject)=>{if(commandQueue.length+pending.size>=maxPendingWrites){reject(new BridgeProtocolError("BOUNDED_TRANSPORT"));return}const item={requestId:command.requestId,name,command,resolve,reject,timer:null,abortHandler:null,cleanup:null,settled:false};const cleanup=()=>{clearTimeout(item.timer);if(item.abortHandler){signal?.removeEventListener("abort",item.abortHandler);item.abortHandler=null}};item.cleanup=cleanup;const failIndeterminate=(code,reason=null)=>{if(item.settled)return;item.settled=true;cleanup();if(pending.delete(item.requestId)||commandQueue.includes(item)){const i=commandQueue.indexOf(item);if(i>=0)commandQueue.splice(i,1);item.reject(reason??Object.assign(new Error(`bridge command timed out: ${name}`),{code}))}if(item.started)disconnect(Object.assign(new Error("mutation completion is indeterminate"),{code}))};item.timer=setTimeout(()=>failIndeterminate("TIMEOUT"),timeoutMs);if(signal?.aborted){item.settled=true;cleanup();reject(signal.reason??Object.assign(new Error(`bridge command aborted: ${name}`),{code:"ABORT_ERR"}));return}item.abortHandler=()=>failIndeterminate("ABORT_ERR",signal.reason);signal?.addEventListener("abort",item.abortHandler,{once:true});commandQueue.push(item);pumpCommands()})},on(name,listener){if(!EVENT_NAMES2.includes(name))throw new BridgeProtocolError("invalid event name");const set=listeners.get(name)??new Set;set.add(listener);listeners.set(name,set);return()=>{set.delete(listener);if(!set.size)listeners.delete(name)}},emitEvent(name,payload){if(!hello?.events.includes(name))return Promise.reject(new BridgeUnavailableError("unsupported bridge event"));const checkedPayload=payloadSchemas[name].safeParse(payload);if(!checkedPayload.success)return Promise.reject(new BridgeProtocolError("invalid event payload"));sequence+=1;return write({protocol:PROTOCOL,type:"event",connectionId:hostConnectionId,seq:sequence,occurredAt:Date.now(),name,payload:checkedPayload.data})},close(){disconnect("closed");stdin.removeListener?.("data",onData)}}}var NATIVE_HOST_DIRS={darwin:{chrome:["Library","Application Support","Google","Chrome"],chromium:["Library","Application Support","Chromium"]},linux:{chrome:[".config","google-chrome"],chromium:[".config","chromium"]}};function defaultNativeMessagingHostDir(browser="chrome",platform=process.platform){const byBrowser=NATIVE_HOST_DIRS[platform];if(!byBrowser)throw new BridgeProtocolError(`no default nativeMessagingHostDir on ${platform}; pass nativeMessagingHostDir`);const segments=byBrowser[browser];if(!segments)throw new BridgeProtocolError("custom browser requires nativeMessagingHostDir");return path10.join(homedir(),...segments,"NativeMessagingHosts")}async function secureDirectory(dir){const absolute=path10.resolve(dir);const parts=absolute.split(path10.sep).filter(Boolean);let current=path10.parse(absolute).root;let final;for(const[index,part]of parts.entries()){current=path10.join(current,part);let info=await lstat(current).catch(()=>null);if(!info){await mkdir5(current,{mode:448});info=await lstat(current)}if(current!=="/var"&&(info.isSymbolicLink()||!info.isDirectory()))throw new BridgeProtocolError("nativeMessagingHostDir security rejected");if(current!=="/var"&&info.uid!==process.getuid?.()&&info.uid!==0)throw new BridgeProtocolError("nativeMessagingHostDir ownership rejected");const stickyRootAncestor=index<parts.length-1&&info.uid===0&&(info.mode&512)!==0;if(current!=="/var"&&!stickyRootAncestor&&(info.mode&18)!==0)throw new BridgeProtocolError("nativeMessagingHostDir security rejected");final=info}if(!final||final.uid!==process.getuid?.()&&final.uid!==0)throw new BridgeProtocolError("nativeMessagingHostDir ownership rejected");if((final.mode&63)!==0)await chmod4(absolute,448)}async function secureExecutable(hostPath){const info=await lstat(hostPath).catch(()=>null);if(!info?.isFile()||info.isSymbolicLink()||info.uid!==process.getuid?.()||(info.mode&73)===0||(info.mode&18)!==0)throw new BridgeProtocolError("hostPath must be a securely owned executable file")}async function installNativeMessagingHost({hostName=HOST_NAME,hostPath,extensionId,browser="chrome",nativeMessagingHostDir,force=false,endpointPath}={}){if(endpointPath!==undefined)throw new BridgeProtocolError("endpointPath is not supported");if(hostName!==HOST_NAME||!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(hostName))throw new BridgeProtocolError("invalid hostName");if(!path10.isAbsolute(hostPath??""))throw new BridgeProtocolError("hostPath must be absolute");await secureExecutable(hostPath);if(!/^[a-p]{32}$/.test(extensionId??""))throw new BridgeProtocolError("invalid extensionId");if(!nativeMessagingHostDir&&browser==="cloakbrowser")throw new BridgeProtocolError("CloakBrowser nativeMessagingHostDir must be explicit and verified");const dir=nativeMessagingHostDir??defaultNativeMessagingHostDir(browser);await secureDirectory(dir);const manifest={name:hostName,description:"OmOWright internal CloakBrowser native messaging bridge.",path:hostPath,type:"stdio",allowed_origins:[`chrome-extension://${extensionId}/`]};const file=path10.join(dir,`${hostName}.json`);if(!force&&await lstat(file).catch(()=>null))throw new BridgeProtocolError("registration manifest already exists; use force");const temp=`${file}.${process.pid}.${randomUUID4()}.tmp`;const handle=await open5(temp,"wx",384);try{await handle.writeFile(JSON.stringify(manifest,null,2)+`
1508
- `);await handle.sync()}finally{await handle.close()}await rename3(temp,file);const parsed=JSON.parse(await readFile5(file,"utf8"));await chmod4(file,384);return{path:file,manifest:parsed}}function createBridgeClient(options={}){return createNativeMessagingHost(options)}function createChromeApi2(connection,options={}){const chrome=createChromeApi(connection,options);const bridge=options.bridge;const request=(wire,params)=>bridge?.isConnected&&bridge.capabilities?.commands?.has(wire)?bridge.request(wire,params):Promise.reject(new UnsupportedOperationError(`chrome.${wire} requires the extension bridge in standalone OmOWright`));chrome.bookmarks.create=(details)=>request("bookmarks.create",{details});chrome.bookmarks.move=(id,details={})=>request("bookmarks.move",{details:{...details,id:String(id)}});chrome.bookmarks.remove=(id)=>request("bookmarks.remove",{details:{id:String(id)}});chrome.history.deleteUrl=(details)=>request("history.deleteUrl",{details});chrome.history.deleteRange=(details)=>request("history.deleteRange",{details});chrome.tabGroups={update:(groupId,updateProperties)=>request("tabGroups.update",{details:{groupId,updateProperties}})};chrome.debugger={attach:(target,requiredVersion)=>request("debugger.attach",{details:{tabId:target.tabId??target,requiredVersion}}),detach:(target)=>request("debugger.detach",{details:{tabId:target.tabId??target}})};const eventMap={notifications:["onShown","onClicked","onClosed"],tabGroups:["onCreated","onUpdated","onMoved"],tabs:["onActivated"],windows:["onFocusChanged"]};for(const[namespace,events]of Object.entries(eventMap)){chrome[namespace]??={};for(const event of events){const wire=`${namespace}.${event.slice(2,3).toLowerCase()+event.slice(3)}`;const subscriptions=new Map;chrome[namespace][event]={addListener(listener){if(!bridge?.on)return;const unsubscribe=bridge.on(wire,(payload)=>listener(...namespace==="notifications"&&event==="onClosed"?[payload.notificationId,payload.byUser]:namespace==="notifications"?[payload.notificationId]:namespace==="tabGroups"?[payload.group]:namespace==="windows"?[payload.windowId]:[payload]));subscriptions.set(listener,unsubscribe);return unsubscribe},removeListener(listener){const unsubscribe=subscriptions.get(listener);if(unsubscribe){unsubscribe();subscriptions.delete(listener)}}}}}return chrome}function sanitizeCookies(cookies){const byKey=new Map;for(const cookie of cookies){const out={name:cookie.name,value:cookie.value,path:cookie.path||"/",secure:Boolean(cookie.secure),httpOnly:Boolean(cookie.httpOnly)};if(cookie.name.startsWith("__Host-")){out.secure=true;out.path="/";out.url=`https://${cookie.domain.replace(/^\./,"")}`}else{out.domain=cookie.domain}if(cookie.expires&&cookie.expires>0)out.expires=cookie.expires;if(cookie.sameSite&&(cookie.sameSite!=="None"||out.secure))out.sameSite=cookie.sameSite;const key=`${out.name}|${out.domain??out.url}`;const prev=byKey.get(key);if(!prev||(out.expires??0)>(prev.expires??0))byKey.set(key,out)}return[...byKey.values()]}async function injectCookies(page,cookies){await page._sendToTarget("Network.setCookies",{cookies:sanitizeCookies(cookies)})}var DEVICE_PRESETS=Object.freeze({"iphone-14":{width:390,height:844,deviceScaleFactor:3,mobile:true,hasTouch:true,userAgent:"Mozilla/5.0 (iPhone; CPU iPhone OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1",platform:"iPhone"},"pixel-7":{width:412,height:915,deviceScaleFactor:2.625,mobile:true,hasTouch:true,userAgent:"Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36",platform:"Linux armv8l"},"ipad-air":{width:820,height:1180,deviceScaleFactor:2,mobile:true,hasTouch:true,userAgent:"Mozilla/5.0 (iPad; CPU OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1",platform:"MacIntel"},"desktop-1440":{width:1440,height:900,deviceScaleFactor:1,mobile:false,hasTouch:false,userAgent:undefined,platform:undefined}});var originalUAs=new WeakMap;async function emulate(page,presetNameOrObject){let preset;if(presetNameOrObject===null){preset=null}else if(typeof presetNameOrObject==="string"){if(!DEVICE_PRESETS[presetNameOrObject]){throw new Error(`unknown device preset: ${presetNameOrObject}`)}preset=DEVICE_PRESETS[presetNameOrObject]}else{preset=presetNameOrObject}const sessionId=await page.resolveSessionId();if(preset===null){await page.cdp.send("Emulation.clearDeviceMetricsOverride",{},sessionId);await page.cdp.send("Emulation.setTouchEmulationEnabled",{enabled:false},sessionId);const originalUA=originalUAs.get(page);if(originalUA){await page.cdp.send("Emulation.setUserAgentOverride",{userAgent:originalUA},sessionId)}}else{if(!originalUAs.has(page)){const ua=await page.evaluate(()=>navigator.userAgent);originalUAs.set(page,ua)}await page.cdp.send("Emulation.setDeviceMetricsOverride",{width:preset.width,height:preset.height,deviceScaleFactor:preset.deviceScaleFactor,mobile:preset.mobile,screenWidth:preset.width,screenHeight:preset.height},sessionId);await page.cdp.send("Emulation.setTouchEmulationEnabled",{enabled:preset.hasTouch,maxTouchPoints:preset.hasTouch?5:1},sessionId);if(preset.userAgent!==undefined){await page.cdp.send("Emulation.setUserAgentOverride",{userAgent:preset.userAgent,platform:preset.platform},sessionId)}}if(page.refreshViewportSize){await page.refreshViewportSize()}return preset}var AUTO_ATTACH={autoAttach:true,waitForDebuggerOnStart:false,flatten:true};var REF_PATTERN2=/^(f\d+)?e\d+$/;function detach(page,sessionId){return page.cdp.send("Target.detachFromTarget",{sessionId}).catch(()=>{})}async function iframeTargets(page){const{targetInfo}=await page.cdp.send("Target.getTargetInfo",{targetId:page.targetId});const browserContextId=targetInfo?.browserContextId;const{targetInfos}=await page.cdp.send("Target.getTargets",{filter:[{type:"iframe"}]});return targetInfos.filter((info)=>info.type==="iframe"&&(!browserContextId||!info.browserContextId||info.browserContextId===browserContextId))}function isAttributed(fm,frameId){const seen=new Set;let frame=fm.frames.get(frameId);while(frame&&!seen.has(frame.frameId)){if(frame.frameId===fm.mainFrameId)return true;seen.add(frame.frameId);frame=frame.parentFrameId?fm.frames.get(frame.parentFrameId):null}return false}async function describeTarget(page,targetId){const{sessionId}=await page.cdp.send("Target.attachToTarget",{targetId,flatten:true});try{await page.cdp.send("Page.enable",undefined,sessionId);const{frameTree}=await page.cdp.send("Page.getFrameTree",undefined,sessionId);return{sessionId,frame:frameTree.frame}}catch(error){await detach(page,sessionId);throw error}}async function adopt(page,sessionId,frame,parentFrameId){await page.cdp.send("DOM.enable",undefined,sessionId);await page.cdp.send("Target.setAutoAttach",AUTO_ATTACH,sessionId).catch(()=>{});page.frameManager.frames.set(frame.id,{frameId:frame.id,parentFrameId,url:frame.url??"",name:frame.name??"",sessionId});const isolatedContextId=await page.frameManager.ensureInjected(frame.id);return{frameId:frame.id,parentFrameId,url:frame.url??"",sessionId,isolatedContextId}}async function reconcileFrames(page){const fm=page.frameManager;const added=[];let pending=(await iframeTargets(page)).filter((info)=>!isAttributed(fm,info.targetId));let progress=true;while(progress&&pending.length>0){progress=false;const retry=[];for(const target of pending){if(isAttributed(fm,target.targetId))continue;const{sessionId,frame}=await describeTarget(page,target.targetId);const parentFrameId=frame.parentId??null;if(!parentFrameId||!isAttributed(fm,parentFrameId)){await detach(page,sessionId);if(parentFrameId)retry.push(target);continue}added.push(await adopt(page,sessionId,frame,parentFrameId));progress=true}pending=retry}return added}function framePrefixes(fm){let counter=1;return fm.collectDescendantFrames(fm.mainFrameId).map(({frameId})=>({frameId,prefix:frameId===fm.mainFrameId?"":`f${counter++}`}))}function collectMissingFrames(page,refs){const seen=new Set(Object.keys(refs??{}).map((ref)=>ref.match(REF_PATTERN2)?.[1]??""));const missing=[];for(const{frameId,prefix}of framePrefixes(page.frameManager)){if(prefix===""||seen.has(prefix))continue;const frame=page.frameManager.getFrame(frameId);if(frame?.isolatedContextId)continue;missing.push({frameId,url:frame?.url??"",injected:false})}return missing}async function snapshotWithFrames(page,opts={}){await reconcileFrames(page);const result=await page.snapshot(opts);const missingFrames=collectMissingFrames(page,result.refs);return missingFrames.length>0?{...result,missingFrames}:result}var HTTP_VERSION="HTTP/1.1";var CREATOR=Object.freeze({name:"omowright",version:"0.0.1"});function headerList(headers){return Object.entries(headers??{}).map(([name,value])=>({name,value:String(value)}))}function headerValue(headers,name){for(const[key,value]of Object.entries(headers??{})){if(key.toLowerCase()===name)return String(value)}return null}function queryString(url){try{return[...new URL(String(url)).searchParams].map(([name,value])=>({name,value}))}catch{return[]}}function elapsed(entry){const started=Number(entry.startedAt);const finished=Number(entry.finishedAt);if(!Number.isFinite(started)||!Number.isFinite(finished))return 0;return Math.max(0,finished-started)}function requestOf(entry){const postData=entry.postData??null;const request={method:entry.method??"GET",url:entry.url??"",httpVersion:HTTP_VERSION,cookies:[],headers:headerList(entry.requestHeaders),queryString:queryString(entry.url),headersSize:-1,bodySize:postData===null?0:Buffer.byteLength(postData)};if(postData!==null){request.postData={mimeType:headerValue(entry.requestHeaders,"content-type")??"",text:postData}}return request}function contentOf(entry){const content={size:Number(entry.encodedDataLength??0),mimeType:entry.mimeType??""};if(typeof entry.body==="string")content.text=entry.body;if(entry.base64Encoded)content.encoding="base64";return content}function responseOf(entry){return{status:entry.failed?0:Number(entry.status??0),statusText:entry.statusText??"",httpVersion:HTTP_VERSION,cookies:[],headers:headerList(entry.responseHeaders),content:contentOf(entry),redirectURL:headerValue(entry.responseHeaders,"location")??"",headersSize:-1,bodySize:Number(entry.encodedDataLength??0)}}function toHar(entries,{creator=CREATOR,pages=[]}={}){const log={version:"1.2",creator,pages,entries:[...entries??[]].map((entry)=>{const time=elapsed(entry);const har={startedDateTime:new Date(Number(entry.startedAt)||0).toISOString(),time,request:requestOf(entry),response:responseOf(entry),cache:{},timings:{send:0,wait:time,receive:0}};if(entry.failed)har._errorText=entry.errorText??"";return har})};return{log}}function injectHumanBanner(prompt){if(document.querySelector('[data-omowright-human="1"]'))return true;const host=document.createElement("div");host.setAttribute("data-omowright-human","1");host.style.cssText="position:fixed;top:0;left:0;right:0;z-index:2147483647;display:block;visibility:visible;opacity:1;pointer-events:auto;";const root=host.attachShadow({mode:"open"});const bar=document.createElement("div");bar.setAttribute("style","display:flex;align-items:center;gap:12px;padding:10px 16px;background:#111;color:#fff;font:14px/1.4 system-ui,sans-serif;");const message=document.createElement("span");message.textContent=prompt==null?"":String(prompt);const button=document.createElement("button");button.textContent="Done";button.addEventListener("click",()=>{window.__omowrightHumanDone=true});bar.append(message,button);root.append(bar);(document.documentElement??document.body).appendChild(host);return true}function humanBannerPresent(){return document.querySelector('[data-omowright-human="1"]')!==null}function humanDoneFlag(){return window.__omowrightHumanDone===true}function querySelectorExists(selector){return document.querySelector(selector)!==null}function removeHumanBanner(){document.querySelectorAll('[data-omowright-human="1"]').forEach((node)=>node.remove());try{delete window.__omowrightHumanDone}catch{window.__omowrightHumanDone=undefined}}function sleep2(ms,signal){return new Promise((resolve)=>{if(signal?.aborted){resolve();return}const timer=setTimeout(()=>{signal?.removeEventListener("abort",onAbort);resolve()},ms);function onAbort(){clearTimeout(timer);resolve()}signal?.addEventListener("abort",onAbort,{once:true})})}async function ensureBanner(page,prompt){try{if(await page.evaluate(humanBannerPresent))return;await page.evaluate(injectHumanBanner,prompt??"")}catch{}}async function matchesUntil(page,until){if(until==null)return false;if(typeof until==="function")return Boolean(await until(page));if(typeof until==="object"){if(Object.prototype.hasOwnProperty.call(until,"url")){const current=page.url();const expected=until.url;if(expected instanceof RegExp)return expected.test(current);return String(current).includes(String(expected))}if(typeof until.selector==="string"){try{return Boolean(await page.evaluate(querySelectorExists,until.selector))}catch{return false}}}return false}async function isDone(page){try{return await page.evaluate(humanDoneFlag)===true}catch{return false}}async function requestHuman(page,{prompt,until,timeoutMs=300000,pollMs=500,signal}={}){const started=Date.now();const finish=(outcome,reason)=>({outcome,elapsedMs:Date.now()-started,reason});try{await page.bringToFront().catch(()=>{});await ensureBanner(page,prompt);for(;;){if(signal?.aborted)return finish("cancelled","signal");if(Date.now()-started>=timeoutMs)return finish("timed_out","timed_out");if(await matchesUntil(page,until))return finish("continued","until");if(await isDone(page))return finish("continued","done-button");await ensureBanner(page,prompt);const remaining=timeoutMs-(Date.now()-started);if(remaining<=0)return finish("timed_out","timed_out");await sleep2(Math.min(pollMs,remaining),signal)}}finally{await page.evaluate(removeHumanBanner).catch(()=>{})}}async function describeLayers(page,{grid=5}={}){return page.evaluate((gridSize)=>{const width=Math.round(globalThis.visualViewport?.width??globalThis.innerWidth??0);const height=Math.round(globalThis.visualViewport?.height??globalThis.innerHeight??0);const viewport={width,height};const total=gridSize*gridSize;const hits=new Map;function pierceTopmost(x,y){const stack=document.elementsFromPoint(x,y);let el=stack[0]??null;const seen=new Set;while(el?.shadowRoot&&typeof el.shadowRoot.elementsFromPoint==="function"){if(seen.has(el))break;seen.add(el);const inner=el.shadowRoot.elementsFromPoint(x,y)??[];const next=inner.find((node)=>node&&node!==el);if(!next)break;el=next}return el}function parentOf(node){if(!node)return null;if(node.parentElement)return node.parentElement;const root=node.getRootNode?.();return root?.host??null}function promote(el){const minArea=0.5*width*height;let node=el;while(node&&node.nodeType===1){const position=getComputedStyle(node).position;if(position==="fixed"||position==="sticky"||position==="absolute"){const rect=node.getBoundingClientRect();if(rect.width*rect.height>=minArea)return node}node=parentOf(node)}return el}function labelledByText(el){const ids=(el.getAttribute("aria-labelledby")||"").trim();if(!ids)return"";return ids.split(/\s+/).map((id)=>document.getElementById(id)?.textContent?.trim()??"").filter(Boolean).join(" ")}function elementName(el){const ariaLabel=el.getAttribute("aria-label");if(ariaLabel)return ariaLabel;const labelled=labelledByText(el);if(labelled)return labelled;const heading=el.querySelector("h1,h2,h3,h4,h5,h6")?.textContent?.trim();if(heading)return heading;return(el.innerText||el.textContent||"").trim().replace(/\s+/g," ").slice(0,80)}function selectorHint(el){if(el.id)return`#${el.id}`;const classes=[...el.classList].join(".");return classes?`${el.tagName.toLowerCase()}.${classes}`:el.tagName.toLowerCase()}function describe(el,coverage){return{tagName:el.tagName,role:el.getAttribute("role")||"",name:elementName(el),coverage,position:getComputedStyle(el).position,selectorHint:selectorHint(el),ariaModal:el.getAttribute("aria-modal")==="true"}}function isMainContent(el){if(el===document.documentElement||el===document.body)return true;if(el.tagName==="MAIN")return true;return(el.getAttribute("role")||"").toLowerCase()==="main"}function isBlocking(el,info){if(info.coverage<0.6)return false;if(getComputedStyle(el).pointerEvents==="none")return false;const role=(info.role||"").toLowerCase();if(role==="dialog"||role==="alertdialog")return true;if(info.ariaModal)return true;if(isMainContent(el))return false;return info.position==="fixed"||info.position==="absolute"}const insetX=width*0.05;const insetY=height*0.05;const spanX=width*0.9;const spanY=height*0.9;for(let row=0;row<gridSize;row++){for(let col=0;col<gridSize;col++){const x=insetX+(col+0.5)*spanX/gridSize;const y=insetY+(row+0.5)*spanY/gridSize;const top=pierceTopmost(x,y);if(!top)continue;const candidate=promote(top);hits.set(candidate,(hits.get(candidate)??0)+1)}}const candidates=[...hits.entries()].map(([el,count])=>{const info=describe(el,total>0?count/total:0);return{el,info}}).sort((a,b)=>b.info.coverage-a.info.coverage);const blockingEntry=candidates.find((entry)=>isBlocking(entry.el,entry.info));return{blocking:blockingEntry?blockingEntry.info:null,candidates:candidates.map((entry)=>entry.info),viewport}},grid)}function layersHeader(result){const blocking=result?.blocking;if(!blocking)return"@layers none";const who=blocking.role||blocking.tagName;const pct=Math.round(blocking.coverage*100);return`@layers blocking=${who} "${blocking.name}" coverage=${pct}% hint=${blocking.selectorHint}`}async function snapshotWithLayers(page,snapshotOptions){const snapshot=await page.snapshot(snapshotOptions);const layers=await describeLayers(page);return{...snapshot,layers,tree:`${layersHeader(layers)}
1508
+ `);disconnect(error)}};stdin.on("data",onData);stdin.once?.("end",()=>disconnect());stdin.once?.("error",disconnect);return{get isConnected(){return!closed&&!!hello},get capabilities(){return{commands:new Set(hello?.commands??[]),events:new Set(hello?.events??[])}},send:write,request(name,params,{signal,timeoutMs=commandTimeoutMs}={}){if(!hello||!hello.commands.includes(name))return Promise.reject(new BridgeUnavailableError(`unsupported bridge command: ${name}`));let command;try{command=checked(CommandSchema,{protocol:PROTOCOL,type:"command",requestId:randomUUID4(),name,params},"invalid command parameters")}catch(error){return Promise.reject(error)}return new Promise((resolve,reject)=>{if(commandQueue.length+pending.size>=maxPendingWrites){reject(new BridgeProtocolError("BOUNDED_TRANSPORT"));return}const item={requestId:command.requestId,name,command,resolve,reject,timer:null,abortHandler:null,cleanup:null,settled:false};const cleanup=()=>{clearTimeout(item.timer);if(item.abortHandler){signal?.removeEventListener("abort",item.abortHandler);item.abortHandler=null}};item.cleanup=cleanup;const failIndeterminate=(code,reason=null)=>{if(item.settled)return;item.settled=true;cleanup();if(pending.delete(item.requestId)||commandQueue.includes(item)){const i=commandQueue.indexOf(item);if(i>=0)commandQueue.splice(i,1);item.reject(reason??Object.assign(new Error(`bridge command timed out: ${name}`),{code}))}if(item.started)disconnect(Object.assign(new Error("mutation completion is indeterminate"),{code}))};item.timer=setTimeout(()=>failIndeterminate("TIMEOUT"),timeoutMs);if(signal?.aborted){item.settled=true;cleanup();reject(signal.reason??Object.assign(new Error(`bridge command aborted: ${name}`),{code:"ABORT_ERR"}));return}item.abortHandler=()=>failIndeterminate("ABORT_ERR",signal.reason);signal?.addEventListener("abort",item.abortHandler,{once:true});commandQueue.push(item);pumpCommands()})},on(name,listener){if(!EVENT_NAMES2.includes(name))throw new BridgeProtocolError("invalid event name");const set=listeners.get(name)??new Set;set.add(listener);listeners.set(name,set);return()=>{set.delete(listener);if(!set.size)listeners.delete(name)}},emitEvent(name,payload){if(!hello?.events.includes(name))return Promise.reject(new BridgeUnavailableError("unsupported bridge event"));const checkedPayload=payloadSchemas[name].safeParse(payload);if(!checkedPayload.success)return Promise.reject(new BridgeProtocolError("invalid event payload"));sequence+=1;return write({protocol:PROTOCOL,type:"event",connectionId:hostConnectionId,seq:sequence,occurredAt:Date.now(),name,payload:checkedPayload.data})},close(){disconnect("closed");stdin.removeListener?.("data",onData)}}}var NATIVE_HOST_DIRS={darwin:{chrome:["Library","Application Support","Google","Chrome"],chromium:["Library","Application Support","Chromium"]},linux:{chrome:[".config","google-chrome"],chromium:[".config","chromium"]}};function defaultNativeMessagingHostDir(browser="chrome",platform=process.platform){const byBrowser=NATIVE_HOST_DIRS[platform];if(!byBrowser)throw new BridgeProtocolError(`no default nativeMessagingHostDir on ${platform}; pass nativeMessagingHostDir`);const segments=byBrowser[browser];if(!segments)throw new BridgeProtocolError("custom browser requires nativeMessagingHostDir");return path11.join(homedir(),...segments,"NativeMessagingHosts")}async function secureDirectory(dir){const absolute=path11.resolve(dir);const parts=absolute.split(path11.sep).filter(Boolean);let current=path11.parse(absolute).root;let final;for(const[index,part]of parts.entries()){current=path11.join(current,part);let info=await lstat(current).catch(()=>null);if(!info){await mkdir5(current,{mode:448});info=await lstat(current)}if(current!=="/var"&&(info.isSymbolicLink()||!info.isDirectory()))throw new BridgeProtocolError("nativeMessagingHostDir security rejected");if(current!=="/var"&&info.uid!==process.getuid?.()&&info.uid!==0)throw new BridgeProtocolError("nativeMessagingHostDir ownership rejected");const stickyRootAncestor=index<parts.length-1&&info.uid===0&&(info.mode&512)!==0;if(current!=="/var"&&!stickyRootAncestor&&(info.mode&18)!==0)throw new BridgeProtocolError("nativeMessagingHostDir security rejected");final=info}if(!final||final.uid!==process.getuid?.()&&final.uid!==0)throw new BridgeProtocolError("nativeMessagingHostDir ownership rejected");if((final.mode&63)!==0)await chmod4(absolute,448)}async function secureExecutable(hostPath){const info=await lstat(hostPath).catch(()=>null);if(!info?.isFile()||info.isSymbolicLink()||info.uid!==process.getuid?.()||(info.mode&73)===0||(info.mode&18)!==0)throw new BridgeProtocolError("hostPath must be a securely owned executable file")}async function installNativeMessagingHost({hostName=HOST_NAME,hostPath,extensionId,browser="chrome",nativeMessagingHostDir,force=false,endpointPath}={}){if(endpointPath!==undefined)throw new BridgeProtocolError("endpointPath is not supported");if(hostName!==HOST_NAME||!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(hostName))throw new BridgeProtocolError("invalid hostName");if(!path11.isAbsolute(hostPath??""))throw new BridgeProtocolError("hostPath must be absolute");await secureExecutable(hostPath);if(!/^[a-p]{32}$/.test(extensionId??""))throw new BridgeProtocolError("invalid extensionId");if(!nativeMessagingHostDir&&browser==="cloakbrowser")throw new BridgeProtocolError("CloakBrowser nativeMessagingHostDir must be explicit and verified");const dir=nativeMessagingHostDir??defaultNativeMessagingHostDir(browser);await secureDirectory(dir);const manifest={name:hostName,description:"OmOWright internal CloakBrowser native messaging bridge.",path:hostPath,type:"stdio",allowed_origins:[`chrome-extension://${extensionId}/`]};const file=path11.join(dir,`${hostName}.json`);if(!force&&await lstat(file).catch(()=>null))throw new BridgeProtocolError("registration manifest already exists; use force");const temp=`${file}.${process.pid}.${randomUUID4()}.tmp`;const handle=await open5(temp,"wx",384);try{await handle.writeFile(JSON.stringify(manifest,null,2)+`
1509
+ `);await handle.sync()}finally{await handle.close()}await rename3(temp,file);const parsed=JSON.parse(await readFile5(file,"utf8"));await chmod4(file,384);return{path:file,manifest:parsed}}function createBridgeClient(options={}){return createNativeMessagingHost(options)}function createChromeApi2(connection,options={}){const chrome=createChromeApi(connection,options);const bridge=options.bridge;const request=(wire,params)=>bridge?.isConnected&&bridge.capabilities?.commands?.has(wire)?bridge.request(wire,params):Promise.reject(new UnsupportedOperationError(`chrome.${wire} requires the extension bridge in standalone OmOWright`));chrome.bookmarks.create=(details)=>request("bookmarks.create",{details});chrome.bookmarks.move=(id,details={})=>request("bookmarks.move",{details:{...details,id:String(id)}});chrome.bookmarks.remove=(id)=>request("bookmarks.remove",{details:{id:String(id)}});chrome.history.deleteUrl=(details)=>request("history.deleteUrl",{details});chrome.history.deleteRange=(details)=>request("history.deleteRange",{details});chrome.tabGroups={update:(groupId,updateProperties)=>request("tabGroups.update",{details:{groupId,updateProperties}})};chrome.debugger={attach:(target,requiredVersion)=>request("debugger.attach",{details:{tabId:target.tabId??target,requiredVersion}}),detach:(target)=>request("debugger.detach",{details:{tabId:target.tabId??target}})};const eventMap={notifications:["onShown","onClicked","onClosed"],tabGroups:["onCreated","onUpdated","onMoved"],tabs:["onActivated"],windows:["onFocusChanged"]};for(const[namespace,events]of Object.entries(eventMap)){chrome[namespace]??={};for(const event of events){const wire=`${namespace}.${event.slice(2,3).toLowerCase()+event.slice(3)}`;const subscriptions=new Map;chrome[namespace][event]={addListener(listener){if(!bridge?.on)return;const unsubscribe=bridge.on(wire,(payload)=>listener(...namespace==="notifications"&&event==="onClosed"?[payload.notificationId,payload.byUser]:namespace==="notifications"?[payload.notificationId]:namespace==="tabGroups"?[payload.group]:namespace==="windows"?[payload.windowId]:[payload]));subscriptions.set(listener,unsubscribe);return unsubscribe},removeListener(listener){const unsubscribe=subscriptions.get(listener);if(unsubscribe){unsubscribe();subscriptions.delete(listener)}}}}}return chrome}function sanitizeCookies(cookies){const byKey=new Map;for(const cookie of cookies){const out={name:cookie.name,value:cookie.value,path:cookie.path||"/",secure:Boolean(cookie.secure),httpOnly:Boolean(cookie.httpOnly)};if(cookie.name.startsWith("__Host-")){out.secure=true;out.path="/";out.url=`https://${cookie.domain.replace(/^\./,"")}`}else{out.domain=cookie.domain}if(cookie.expires&&cookie.expires>0)out.expires=cookie.expires;if(cookie.sameSite&&(cookie.sameSite!=="None"||out.secure))out.sameSite=cookie.sameSite;const key=`${out.name}|${out.domain??out.url}`;const prev=byKey.get(key);if(!prev||(out.expires??0)>(prev.expires??0))byKey.set(key,out)}return[...byKey.values()]}async function injectCookies(page,cookies){await page._sendToTarget("Network.setCookies",{cookies:sanitizeCookies(cookies)})}var DEVICE_PRESETS=Object.freeze({"iphone-14":{width:390,height:844,deviceScaleFactor:3,mobile:true,hasTouch:true,userAgent:"Mozilla/5.0 (iPhone; CPU iPhone OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1",platform:"iPhone"},"pixel-7":{width:412,height:915,deviceScaleFactor:2.625,mobile:true,hasTouch:true,userAgent:"Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36",platform:"Linux armv8l"},"ipad-air":{width:820,height:1180,deviceScaleFactor:2,mobile:true,hasTouch:true,userAgent:"Mozilla/5.0 (iPad; CPU OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1",platform:"MacIntel"},"desktop-1440":{width:1440,height:900,deviceScaleFactor:1,mobile:false,hasTouch:false,userAgent:undefined,platform:undefined}});var originalUAs=new WeakMap;async function emulate(page,presetNameOrObject){let preset;if(presetNameOrObject===null){preset=null}else if(typeof presetNameOrObject==="string"){if(!DEVICE_PRESETS[presetNameOrObject]){throw new Error(`unknown device preset: ${presetNameOrObject}`)}preset=DEVICE_PRESETS[presetNameOrObject]}else{preset=presetNameOrObject}const sessionId=await page.resolveSessionId();if(preset===null){await page.cdp.send("Emulation.clearDeviceMetricsOverride",{},sessionId);await page.cdp.send("Emulation.setTouchEmulationEnabled",{enabled:false},sessionId);const originalUA=originalUAs.get(page);if(originalUA){await page.cdp.send("Emulation.setUserAgentOverride",{userAgent:originalUA},sessionId)}}else{if(!originalUAs.has(page)){const ua=await page.evaluate(()=>navigator.userAgent);originalUAs.set(page,ua)}await page.cdp.send("Emulation.setDeviceMetricsOverride",{width:preset.width,height:preset.height,deviceScaleFactor:preset.deviceScaleFactor,mobile:preset.mobile,screenWidth:preset.width,screenHeight:preset.height},sessionId);await page.cdp.send("Emulation.setTouchEmulationEnabled",{enabled:preset.hasTouch,maxTouchPoints:preset.hasTouch?5:1},sessionId);if(preset.userAgent!==undefined){await page.cdp.send("Emulation.setUserAgentOverride",{userAgent:preset.userAgent,platform:preset.platform},sessionId)}}if(page.refreshViewportSize){await page.refreshViewportSize()}return preset}var AUTO_ATTACH={autoAttach:true,waitForDebuggerOnStart:false,flatten:true};var REF_PATTERN2=/^(f\d+)?e\d+$/;function detach(page,sessionId){return page.cdp.send("Target.detachFromTarget",{sessionId}).catch(()=>{})}async function iframeTargets(page){const{targetInfo}=await page.cdp.send("Target.getTargetInfo",{targetId:page.targetId});const browserContextId=targetInfo?.browserContextId;const{targetInfos}=await page.cdp.send("Target.getTargets",{filter:[{type:"iframe"}]});return targetInfos.filter((info)=>info.type==="iframe"&&(!browserContextId||!info.browserContextId||info.browserContextId===browserContextId))}function isAttributed(fm,frameId){const seen=new Set;let frame=fm.frames.get(frameId);while(frame&&!seen.has(frame.frameId)){if(frame.frameId===fm.mainFrameId)return true;seen.add(frame.frameId);frame=frame.parentFrameId?fm.frames.get(frame.parentFrameId):null}return false}async function describeTarget(page,targetId){const{sessionId}=await page.cdp.send("Target.attachToTarget",{targetId,flatten:true});try{await page.cdp.send("Page.enable",undefined,sessionId);const{frameTree}=await page.cdp.send("Page.getFrameTree",undefined,sessionId);return{sessionId,frame:frameTree.frame}}catch(error){await detach(page,sessionId);throw error}}async function adopt(page,sessionId,frame,parentFrameId){await page.cdp.send("DOM.enable",undefined,sessionId);await page.cdp.send("Target.setAutoAttach",AUTO_ATTACH,sessionId).catch(()=>{});page.frameManager.frames.set(frame.id,{frameId:frame.id,parentFrameId,url:frame.url??"",name:frame.name??"",sessionId});const isolatedContextId=await page.frameManager.ensureInjected(frame.id);return{frameId:frame.id,parentFrameId,url:frame.url??"",sessionId,isolatedContextId}}async function reconcileFrames(page){const fm=page.frameManager;const added=[];let pending=(await iframeTargets(page)).filter((info)=>!isAttributed(fm,info.targetId));let progress=true;while(progress&&pending.length>0){progress=false;const retry=[];for(const target of pending){if(isAttributed(fm,target.targetId))continue;const{sessionId,frame}=await describeTarget(page,target.targetId);const parentFrameId=frame.parentId??null;if(!parentFrameId||!isAttributed(fm,parentFrameId)){await detach(page,sessionId);if(parentFrameId)retry.push(target);continue}added.push(await adopt(page,sessionId,frame,parentFrameId));progress=true}pending=retry}return added}function framePrefixes(fm){let counter=1;return fm.collectDescendantFrames(fm.mainFrameId).map(({frameId})=>({frameId,prefix:frameId===fm.mainFrameId?"":`f${counter++}`}))}function collectMissingFrames(page,refs){const seen=new Set(Object.keys(refs??{}).map((ref)=>ref.match(REF_PATTERN2)?.[1]??""));const missing=[];for(const{frameId,prefix}of framePrefixes(page.frameManager)){if(prefix===""||seen.has(prefix))continue;const frame=page.frameManager.getFrame(frameId);if(frame?.isolatedContextId)continue;missing.push({frameId,url:frame?.url??"",injected:false})}return missing}async function snapshotWithFrames(page,opts={}){await reconcileFrames(page);const result=await page.snapshot(opts);const missingFrames=collectMissingFrames(page,result.refs);return missingFrames.length>0?{...result,missingFrames}:result}var HTTP_VERSION="HTTP/1.1";var CREATOR=Object.freeze({name:"omowright",version:"0.0.1"});function headerList(headers){return Object.entries(headers??{}).map(([name,value])=>({name,value:String(value)}))}function headerValue(headers,name){for(const[key,value]of Object.entries(headers??{})){if(key.toLowerCase()===name)return String(value)}return null}function queryString(url){try{return[...new URL(String(url)).searchParams].map(([name,value])=>({name,value}))}catch{return[]}}function elapsed(entry){const started=Number(entry.startedAt);const finished=Number(entry.finishedAt);if(!Number.isFinite(started)||!Number.isFinite(finished))return 0;return Math.max(0,finished-started)}function requestOf(entry){const postData=entry.postData??null;const request={method:entry.method??"GET",url:entry.url??"",httpVersion:HTTP_VERSION,cookies:[],headers:headerList(entry.requestHeaders),queryString:queryString(entry.url),headersSize:-1,bodySize:postData===null?0:Buffer.byteLength(postData)};if(postData!==null){request.postData={mimeType:headerValue(entry.requestHeaders,"content-type")??"",text:postData}}return request}function contentOf(entry){const content={size:Number(entry.encodedDataLength??0),mimeType:entry.mimeType??""};if(typeof entry.body==="string")content.text=entry.body;if(entry.base64Encoded)content.encoding="base64";return content}function responseOf(entry){return{status:entry.failed?0:Number(entry.status??0),statusText:entry.statusText??"",httpVersion:HTTP_VERSION,cookies:[],headers:headerList(entry.responseHeaders),content:contentOf(entry),redirectURL:headerValue(entry.responseHeaders,"location")??"",headersSize:-1,bodySize:Number(entry.encodedDataLength??0)}}function toHar(entries,{creator=CREATOR,pages=[]}={}){const log={version:"1.2",creator,pages,entries:[...entries??[]].map((entry)=>{const time=elapsed(entry);const har={startedDateTime:new Date(Number(entry.startedAt)||0).toISOString(),time,request:requestOf(entry),response:responseOf(entry),cache:{},timings:{send:0,wait:time,receive:0}};if(entry.failed)har._errorText=entry.errorText??"";return har})};return{log}}function injectHumanBanner(prompt){if(document.querySelector('[data-omowright-human="1"]'))return true;const host=document.createElement("div");host.setAttribute("data-omowright-human","1");host.style.cssText="position:fixed;top:0;left:0;right:0;z-index:2147483647;display:block;visibility:visible;opacity:1;pointer-events:auto;";const root=host.attachShadow({mode:"open"});const bar=document.createElement("div");bar.setAttribute("style","display:flex;align-items:center;gap:12px;padding:10px 16px;background:#111;color:#fff;font:14px/1.4 system-ui,sans-serif;");const message=document.createElement("span");message.textContent=prompt==null?"":String(prompt);const button=document.createElement("button");button.textContent="Done";button.addEventListener("click",()=>{window.__omowrightHumanDone=true});bar.append(message,button);root.append(bar);(document.documentElement??document.body).appendChild(host);return true}function humanBannerPresent(){return document.querySelector('[data-omowright-human="1"]')!==null}function humanDoneFlag(){return window.__omowrightHumanDone===true}function querySelectorExists(selector){return document.querySelector(selector)!==null}function removeHumanBanner(){document.querySelectorAll('[data-omowright-human="1"]').forEach((node)=>node.remove());try{delete window.__omowrightHumanDone}catch{window.__omowrightHumanDone=undefined}}function sleep3(ms,signal){return new Promise((resolve)=>{if(signal?.aborted){resolve();return}const timer=setTimeout(()=>{signal?.removeEventListener("abort",onAbort);resolve()},ms);function onAbort(){clearTimeout(timer);resolve()}signal?.addEventListener("abort",onAbort,{once:true})})}async function ensureBanner(page,prompt){try{if(await page.evaluate(humanBannerPresent))return;await page.evaluate(injectHumanBanner,prompt??"")}catch{}}async function matchesUntil(page,until){if(until==null)return false;if(typeof until==="function")return Boolean(await until(page));if(typeof until==="object"){if(Object.prototype.hasOwnProperty.call(until,"url")){const current=page.url();const expected=until.url;if(expected instanceof RegExp)return expected.test(current);return String(current).includes(String(expected))}if(typeof until.selector==="string"){try{return Boolean(await page.evaluate(querySelectorExists,until.selector))}catch{return false}}}return false}async function isDone(page){try{return await page.evaluate(humanDoneFlag)===true}catch{return false}}async function requestHuman(page,{prompt,until,timeoutMs=300000,pollMs=500,signal}={}){const started=Date.now();const finish=(outcome,reason)=>({outcome,elapsedMs:Date.now()-started,reason});try{await page.bringToFront().catch(()=>{});await ensureBanner(page,prompt);for(;;){if(signal?.aborted)return finish("cancelled","signal");if(Date.now()-started>=timeoutMs)return finish("timed_out","timed_out");if(await matchesUntil(page,until))return finish("continued","until");if(await isDone(page))return finish("continued","done-button");await ensureBanner(page,prompt);const remaining=timeoutMs-(Date.now()-started);if(remaining<=0)return finish("timed_out","timed_out");await sleep3(Math.min(pollMs,remaining),signal)}}finally{await page.evaluate(removeHumanBanner).catch(()=>{})}}async function describeLayers(page,{grid=5}={}){return page.evaluate((gridSize)=>{const width=Math.round(globalThis.visualViewport?.width??globalThis.innerWidth??0);const height=Math.round(globalThis.visualViewport?.height??globalThis.innerHeight??0);const viewport={width,height};const total=gridSize*gridSize;const hits=new Map;function pierceTopmost(x,y){const stack=document.elementsFromPoint(x,y);let el=stack[0]??null;const seen=new Set;while(el?.shadowRoot&&typeof el.shadowRoot.elementsFromPoint==="function"){if(seen.has(el))break;seen.add(el);const inner=el.shadowRoot.elementsFromPoint(x,y)??[];const next=inner.find((node)=>node&&node!==el);if(!next)break;el=next}return el}function parentOf(node){if(!node)return null;if(node.parentElement)return node.parentElement;const root=node.getRootNode?.();return root?.host??null}function promote(el){const minArea=0.5*width*height;let node=el;while(node&&node.nodeType===1){const position=getComputedStyle(node).position;if(position==="fixed"||position==="sticky"||position==="absolute"){const rect=node.getBoundingClientRect();if(rect.width*rect.height>=minArea)return node}node=parentOf(node)}return el}function labelledByText(el){const ids=(el.getAttribute("aria-labelledby")||"").trim();if(!ids)return"";return ids.split(/\s+/).map((id)=>document.getElementById(id)?.textContent?.trim()??"").filter(Boolean).join(" ")}function elementName(el){const ariaLabel=el.getAttribute("aria-label");if(ariaLabel)return ariaLabel;const labelled=labelledByText(el);if(labelled)return labelled;const heading=el.querySelector("h1,h2,h3,h4,h5,h6")?.textContent?.trim();if(heading)return heading;return(el.innerText||el.textContent||"").trim().replace(/\s+/g," ").slice(0,80)}function selectorHint(el){if(el.id)return`#${el.id}`;const classes=[...el.classList].join(".");return classes?`${el.tagName.toLowerCase()}.${classes}`:el.tagName.toLowerCase()}function describe(el,coverage){return{tagName:el.tagName,role:el.getAttribute("role")||"",name:elementName(el),coverage,position:getComputedStyle(el).position,selectorHint:selectorHint(el),ariaModal:el.getAttribute("aria-modal")==="true"}}function isMainContent(el){if(el===document.documentElement||el===document.body)return true;if(el.tagName==="MAIN")return true;return(el.getAttribute("role")||"").toLowerCase()==="main"}function isBlocking(el,info){if(info.coverage<0.6)return false;if(getComputedStyle(el).pointerEvents==="none")return false;const role=(info.role||"").toLowerCase();if(role==="dialog"||role==="alertdialog")return true;if(info.ariaModal)return true;if(isMainContent(el))return false;return info.position==="fixed"||info.position==="absolute"}const insetX=width*0.05;const insetY=height*0.05;const spanX=width*0.9;const spanY=height*0.9;for(let row=0;row<gridSize;row++){for(let col=0;col<gridSize;col++){const x=insetX+(col+0.5)*spanX/gridSize;const y=insetY+(row+0.5)*spanY/gridSize;const top=pierceTopmost(x,y);if(!top)continue;const candidate=promote(top);hits.set(candidate,(hits.get(candidate)??0)+1)}}const candidates=[...hits.entries()].map(([el,count])=>{const info=describe(el,total>0?count/total:0);return{el,info}}).sort((a,b)=>b.info.coverage-a.info.coverage);const blockingEntry=candidates.find((entry)=>isBlocking(entry.el,entry.info));return{blocking:blockingEntry?blockingEntry.info:null,candidates:candidates.map((entry)=>entry.info),viewport}},grid)}function layersHeader(result){const blocking=result?.blocking;if(!blocking)return"@layers none";const who=blocking.role||blocking.tagName;const pct=Math.round(blocking.coverage*100);return`@layers blocking=${who} "${blocking.name}" coverage=${pct}% hint=${blocking.selectorHint}`}async function snapshotWithLayers(page,snapshotOptions){const snapshot=await page.snapshot(snapshotOptions);const layers=await describeLayers(page);return{...snapshot,layers,tree:`${layersHeader(layers)}
1509
1510
  ${snapshot.tree}`}}var DEFAULT_TIMEOUT_MS=30000;var JSON_MIME=/^application\/json\s*(;|$)|\+json\s*(;|$)/i;function matchesText(value,pattern){if(pattern===undefined)return true;if(value===null||value===undefined)return false;if(pattern instanceof RegExp)return pattern.test(String(value));return String(value).includes(String(pattern))}function matchesToken(value,expected){if(expected===undefined)return true;if(value===null||value===undefined)return false;return String(value).toLowerCase()===String(expected).toLowerCase()}function toPredicate(match){if(match===undefined||match===null)return()=>true;if(typeof match==="function")return match;if(typeof match!=="object")throw new TypeError("match must be a predicate function or a descriptor object");return(entry)=>matchesText(entry.url,match.url)&&matchesToken(entry.method,match.method)&&matchesText(entry.mimeType,match.mimeType)&&matchesToken(entry.resourceType,match.resourceType)}function startedAtOf(params){const wallTime=Number(params?.wallTime);return Number.isFinite(wallTime)&&wallTime>0?Math.round(wallTime*1000):Date.now()}function decodeBody(entry){if(typeof entry.body!=="string")return null;return entry.base64Encoded?Buffer.from(entry.body,"base64").toString("utf8"):entry.body}function createNetworkSnoop(page,options={}){if(!page?.cdp||typeof page.resolveSessionId!=="function"){throw new TypeError("createNetworkSnoop requires a page with cdp and resolveSessionId")}const{match,bodies=true,maxEntries=500,maxBodyBytes=5000000}=options;const wanted=toPredicate(match);const inflight=new Map;const buffer=[];const waiters=new Set;const unsubscribers=[];let sessionId=null;let disposed=false;let queue=Promise.resolve();const ready=Promise.resolve(page.resolveSessionId()).then((id)=>{sessionId=id});ready.catch((error)=>console.warn("[network-snoop] failed to resolve the page session",error));function subscribe(method,handler){unsubscribers.push(page.cdp.on(method,(params,meta)=>{if(disposed)return;if(sessionId===null){ready.then(()=>{if(!disposed&&meta?.sessionId===sessionId)handler(params)}).catch(()=>{});return}if(meta?.sessionId!==sessionId)return;handler(params)}))}function enqueue(task){queue=queue.then(task).catch((error)=>console.warn("[network-snoop] entry processing failed",error))}async function loadBody(entry){if(entry.encodedDataLength>maxBodyBytes){entry.bodySkipped=true;return}try{const result=await page.cdp.send("Network.getResponseBody",{requestId:entry.requestId},sessionId);entry.body=result?.body??null;entry.base64Encoded=Boolean(result?.base64Encoded)}catch{entry.body=null}}function commit(entry){buffer.push(entry);while(buffer.length>maxEntries)buffer.shift();for(const waiter of[...waiters])if(waiter.test(entry))waiter.resolve(entry)}subscribe("Network.requestWillBeSent",(params)=>{const requestId=params?.requestId;if(!requestId)return;inflight.set(requestId,{requestId,url:params.request?.url??params.documentURL??"",method:params.request?.method??"GET",resourceType:params.type??null,status:null,statusText:null,mimeType:null,requestHeaders:params.request?.headers??{},responseHeaders:null,remoteIPAddress:null,postData:params.request?.postData??null,encodedDataLength:0,body:null,base64Encoded:false,bodySkipped:false,failed:false,errorText:null,startedAt:startedAtOf(params),finishedAt:null})});subscribe("Network.responseReceived",(params)=>{const entry=inflight.get(params?.requestId);if(!entry)return;const response=params.response??{};entry.status=response.status??null;entry.statusText=response.statusText??"";entry.mimeType=response.mimeType??null;entry.responseHeaders=response.headers??{};entry.remoteIPAddress=response.remoteIPAddress??null;if(params.type)entry.resourceType=params.type});subscribe("Network.loadingFinished",(params)=>{const entry=inflight.get(params?.requestId);if(!entry)return;inflight.delete(entry.requestId);entry.encodedDataLength=Number(params.encodedDataLength??0);entry.finishedAt=Date.now();enqueue(async()=>{if(!wanted(entry))return;if(bodies)await loadBody(entry);commit(entry)})});subscribe("Network.loadingFailed",(params)=>{const entry=inflight.get(params?.requestId);if(!entry)return;inflight.delete(entry.requestId);entry.failed=true;entry.errorText=params.errorText??"";entry.encodedDataLength=Number(params.encodedDataLength??entry.encodedDataLength);entry.finishedAt=Date.now();enqueue(async()=>{if(wanted(entry))commit(entry)})});function pop(){return buffer.splice(0,buffer.length)}function peek(){return[...buffer]}function popJson(){const values=[];for(const entry of pop()){if(!JSON_MIME.test(String(entry.mimeType??"")))continue;const text=decodeBody(entry);if(text===null)continue;try{values.push(JSON.parse(text))}catch{}}return values}function waitFor(match,{timeoutMs=DEFAULT_TIMEOUT_MS}={}){if(disposed)return Promise.reject(Error("Network snoop is disposed"));const predicate=toPredicate(match);return new Promise((resolve,reject)=>{let settled=false;const waiter={};const finish=(callback)=>(value)=>{if(settled)return;settled=true;clearTimeout(timer);waiters.delete(waiter);callback(value)};waiter.resolve=finish(resolve);waiter.reject=finish(reject);waiter.test=(entry)=>{try{return Boolean(predicate(entry))}catch(error){waiter.reject(error instanceof Error?error:Error(String(error)));return false}};const timer=setTimeout(()=>waiter.reject(Error(`Timed out waiting for network response after ${timeoutMs}ms`)),timeoutMs);waiters.add(waiter)})}function summary({max=50}={}){const shown=buffer.slice(0,max);const lines=shown.map((entry)=>[entry.method,entry.status??(entry.failed?"FAILED":"-"),entry.mimeType??"-",`${entry.encodedDataLength}B`,entry.url].join(" "));const hidden=buffer.length-shown.length;if(hidden>0)lines.push(`... +${hidden} more`);return lines.join(`
1510
- `)}function dispose(){if(disposed)return;disposed=true;for(const off of unsubscribers.splice(0,unsubscribers.length))off();for(const waiter of[...waiters])waiter.reject(Error("Network snoop disposed"));waiters.clear();inflight.clear()}return{pop,peek,popJson,waitFor,summary,dispose}}function globToRegExp(glob){let source="";for(const char of glob){if(char==="*")source+=".*";else if(char==="?")source+=".";else if("\\^$+{}[]()|.".includes(char))source+=`\\${char}`;else source+=char}return new RegExp(`^${source}$`)}function hasGlobChars(value){return value.includes("*")||value.includes("?")}function matches(match,request){if(typeof match==="function"){return Boolean(match({url:request.url,method:request.method,resourceType:request.resourceType,headers:request.headers}))}if(match instanceof RegExp)return match.test(request.url);if(typeof match==="string"){if(hasGlobChars(match))return globToRegExp(match).test(request.url);return request.url.includes(match)}return false}function sameMatch(left,right){if(left===right)return true;if(left instanceof RegExp&&right instanceof RegExp)return left.source===right.source&&left.flags===right.flags;return false}function toHeaderEntries(headers){if(!headers)return[];if(Array.isArray(headers)){return headers.map((header)=>({name:String(header.name),value:String(header.value??"")}))}return Object.entries(headers).map(([name,value])=>({name,value:String(value??"")}))}function encodeBody(body){if(body==null)return"";if(Buffer.isBuffer(body))return body.toString("base64");if(body instanceof Uint8Array)return Buffer.from(body).toString("base64");return Buffer.from(String(body)).toString("base64")}function buildContinue(requestId,{url,method,headers,postData}={}){const params={requestId};if(url!==undefined)params.url=url;if(method!==undefined)params.method=method;if(headers!==undefined)params.headers=toHeaderEntries(headers);if(postData!==undefined)params.postData=encodeBody(postData);return params}function buildFulfill(requestId,{status=200,headers={},contentType,body=""}={}){const responseHeaders=toHeaderEntries(headers);if(contentType!=null&&String(contentType).length>0){const hasType=responseHeaders.some((header)=>header.name.toLowerCase()==="content-type");if(!hasType)responseHeaders.push({name:"content-type",value:String(contentType)})}return{requestId,responseCode:status,responseHeaders,body:encodeBody(body)}}function createRoutes(page){if(!page?.cdp)throw new TypeError("createRoutes requires a page with cdp");const entries=[];const warned=new WeakSet;const settledIds=new Set;let enabled=false;let sessionId;let unsubscribe=null;let enablePromise=null;function warnOnce(handler,error){if(typeof handler==="function"){if(warned.has(handler))return;warned.add(handler)}if(error)console.warn("[routes] route handler failed; continuing request",error);else console.warn("[routes] route handler did not settle the request; continuing")}function findHandler(request){for(const entry of entries){if(matches(entry.match,request))return entry.handler}return}async function handlePaused(params,meta){if(!sessionId||meta?.sessionId!==sessionId)return;const requestId=params?.requestId;if(!requestId||settledIds.has(requestId))return;const request={url:params.request?.url??"",method:params.request?.method??"GET",headers:params.request?.headers??{},postData:params.request?.postData,resourceType:params.resourceType};let settled=false;const settle=(method,cdpParams)=>{if(settled)return Promise.resolve();settled=true;settledIds.add(requestId);return page.cdp.send(method,cdpParams,sessionId)};const route={request,continue(overrides={}){return settle("Fetch.continueRequest",buildContinue(requestId,overrides))},fulfill(options={}){return settle("Fetch.fulfillRequest",buildFulfill(requestId,options))},abort(errorReason="Failed"){return settle("Fetch.failRequest",{requestId,errorReason})}};const handler=findHandler(request);if(!handler){await settle("Fetch.continueRequest",{requestId});return}try{await handler(route)}catch(error){warnOnce(handler,error)}if(!settled){warnOnce(handler);await settle("Fetch.continueRequest",{requestId})}}function onPaused(params,meta){handlePaused(params,meta).catch((error)=>{console.warn("[routes] requestPaused handling failed",error)})}async function ensureEnabled(){if(enabled)return;if(!enablePromise){enablePromise=(async()=>{sessionId=await page.resolveSessionId();if(!unsubscribe)unsubscribe=page.cdp.on("Fetch.requestPaused",onPaused);await page.cdp.send("Fetch.enable",{patterns:[{urlPattern:"*",requestStage:"Request"}]},sessionId);enabled=true})()}try{await enablePromise}catch(error){enablePromise=null;throw error}}async function disableFetch(){const pending=enablePromise;enablePromise=null;if(pending)await pending.catch(()=>{});if(unsubscribe){unsubscribe();unsubscribe=null}if(!enabled)return;enabled=false;await page.cdp.send("Fetch.disable",{},sessionId)}async function route(match,handler){if(typeof handler!=="function")throw new TypeError("handler must be a function");entries.push({match,handler});await ensureEnabled()}async function unroute(match){if(match===undefined)entries.length=0;else{for(let index=entries.length-1;index>=0;index-=1){if(sameMatch(entries[index].match,match))entries.splice(index,1)}}if(entries.length===0)await disableFetch()}async function dispose(){entries.length=0;await disableFetch()}return{route,unroute,dispose,get enabled(){return enabled}}}async function viewportOf(page){return page.viewportSize()??await page.evaluate(()=>({width:innerWidth,height:innerHeight}))}async function scrollOnce(page,mode){if(mode==="end"){await page.evaluate(()=>scrollTo(0,document.scrollingElement.scrollHeight));return}const viewport=await viewportOf(page);await page.mouse.move(Math.floor(viewport.width/2),Math.floor(viewport.height/2));await page.mouse.wheel(0,Math.floor(viewport.height*0.9))}async function*collectWhileScrolling(page,snoop,options={}){const{minItems=Infinity,maxScrolls=10,extract=(json)=>Array.isArray(json)?json:[],scroll="wheel",settleMs=800}=options;if(scroll!=="wheel"&&scroll!=="end")throw new TypeError(`Unknown scroll mode: ${String(scroll)}`);let rounds=0;let total=0;let lastHeight=null;let unchanged=0;let stoppedBecause="maxScrolls";for(;;){for(const json of snoop.popJson()){for(const item of extract(json)){total+=1;yield item}}if(total>=minItems){stoppedBecause="minItems";break}if(rounds>=maxScrolls){stoppedBecause="maxScrolls";break}const height=await page.evaluate(()=>document.scrollingElement.scrollHeight);if(lastHeight!==null&&height===lastHeight){unchanged+=1;if(unchanged>=2){stoppedBecause="noGrowth";break}}else{unchanged=0}lastHeight=height;await scrollOnce(page,scroll);rounds+=1;await new Promise((resolve)=>setTimeout(resolve,settleMs))}return{rounds,total,stoppedBecause}}import{mkdir as mkdir6,writeFile as writeFile2}from"node:fs/promises";import path11 from"node:path";var SLUG_MAX=48;var SNOOP_MAX_ENTRIES=5000;function slug(name){const value=String(name??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");return(value||"step").slice(0,SLUG_MAX)}function messageOf(error){return error instanceof Error?error.message:String(error)}async function readConsole(page){const api=page.console;const entries=typeof api==="function"?await api.call(page):await api?.logs?.();return Array.isArray(entries)?entries:[]}function createTrace(page,options={}){if(!page?.cdp||typeof page.resolveSessionId!=="function"){throw new TypeError("createTrace requires a page with cdp and resolveSessionId")}const{dir,screenshots=true,network=true,console:consoleLogs=true,maxBodyBytes=1e6,events}=options;if(typeof dir!=="string"||dir.length===0)throw new TypeError("createTrace requires a dir");const shotDir=path11.join(dir,"screenshots");const ready=mkdir6(shotDir,{recursive:true});const startedAt=Date.now();const records=[];const netEntries=[];const unsubscribers=[];const snoop=network?createNetworkSnoop(page,{bodies:true,maxBodyBytes,maxEntries:SNOOP_MAX_ENTRIES}):null;let seq=0;let consoleSeen=0;let sessionId=null;let stopped=false;let stopping=null;function record(event){const entry={...event,ts:Date.now(),seq:seq++};records.push(entry);return entry}const sessionReady=Promise.resolve(page.resolveSessionId()).then((id)=>{sessionId=id});sessionReady.catch((error)=>record({kind:"warning",scope:"session",message:messageOf(error)}));unsubscribers.push(page.cdp.on("Page.frameNavigated",(params,meta)=>{if(stopped||params?.frame?.parentId)return;const add=()=>record({kind:"navigation",url:params?.frame?.url??"",frameId:params?.frame?.id??null});if(sessionId===null){sessionReady.then(()=>{if(!stopped&&meta?.sessionId===sessionId)add()}).catch(()=>{});return}if(meta?.sessionId===sessionId)add()}));unsubscribers.push(page.on("dialog",(dialog)=>record({kind:"dialog",type:dialog?.type??null,message:dialog?.message??"",defaultPrompt:dialog?.defaultPrompt??"",url:dialog?.url??null})));if(events){unsubscribers.push(events.on("downloadStarted",(payload)=>record({kind:"download",phase:"started",...payload})));unsubscribers.push(events.on("downloadFinished",(payload)=>record({kind:"download",phase:"finished",...payload})))}async function capture(step,phase){const file=path11.join(shotDir,`${step.seq}-${slug(step.name)}-${phase}.png`);try{await writeFile2(file,await page.screenshot({type:"png"}));step.screenshots[phase]=file}catch(error){record({kind:"warning",scope:`screenshot:${phase}`,step:step.seq,message:messageOf(error)})}}function drainNetwork(){for(const entry of snoop?.pop()??[]){if(typeof entry.body==="string"&&Buffer.byteLength(entry.body)>maxBodyBytes){entry.body=null;entry.bodySkipped=true}netEntries.push(entry);record({kind:"network",...entry})}}async function drainConsole(){if(!consoleLogs)return;let entries;try{entries=await readConsole(page)}catch{return}if(entries.length<consoleSeen)consoleSeen=0;for(const entry of entries.slice(consoleSeen))record({kind:"console",...entry});consoleSeen=entries.length}async function drain(){drainNetwork();await drainConsole()}async function step(name,fn){if(typeof fn!=="function")throw new TypeError("trace.step requires a function");await ready;const entry=record({kind:"step",name:String(name),startedAt:Date.now(),screenshots:{}});if(screenshots)await capture(entry,"before");try{return await fn()}catch(error){entry.error=messageOf(error);throw error}finally{entry.endedAt=Date.now();entry.durationMs=entry.endedAt-entry.startedAt;if(screenshots)await capture(entry,"after");await drain()}}function mark(name,data){return record({kind:"mark",name:String(name),data:data??null})}async function finish(){await ready;await drain();stopped=true;for(const off of unsubscribers.splice(0,unsubscribers.length))off();snoop?.dispose();const ordered=[...records].sort((a,b)=>a.seq-b.seq);const summary={steps:ordered.filter((entry)=>entry.kind==="step").length,events:ordered.length,network:netEntries.length,startedAt,endedAt:Date.now(),url:page.url()};await writeFile2(path11.join(dir,"trace.jsonl"),ordered.map((entry)=>`${JSON.stringify(entry)}
1511
- `).join(""));await writeFile2(path11.join(dir,"trace.har"),`${JSON.stringify(toHar(netEntries),null,2)}
1512
- `);await writeFile2(path11.join(dir,"summary.json"),`${JSON.stringify(summary,null,2)}
1513
- `);return summary}function stop(){return stopping??=finish()}return{step,mark,stop,get dir(){return dir}}}import{connect as connect2}from"node:net";import{existsSync as existsSync3}from"node:fs";import path13 from"node:path";import{randomBytes}from"node:crypto";import{existsSync as existsSync2,readFileSync as readFileSync3}from"node:fs";import{homedir as homedir2}from"node:os";import path12 from"node:path";import{spawn as spawn4}from"node:child_process";import{createHash as createHash2}from"node:crypto";function resolveBskHome(env=process.env){const override=env.BSK_HOME;if(typeof override==="string"&&override.length>0)return override;const home=env.HOME&&env.HOME.length>0?env.HOME:homedir2();return path12.join(home,".bsk")}function daemonInfoPath(home){return path12.join(home,"daemon.json")}function readDaemonInfo(home){let raw;try{raw=readFileSync3(daemonInfoPath(home),"utf8")}catch{return null}let info;try{info=JSON.parse(raw)}catch{return null}if(!info||typeof info.sock_path!=="string"||info.sock_path.length===0)return null;return info}function findBskBinary(env=process.env){const home=env.HOME&&env.HOME.length>0?env.HOME:homedir2();const candidates=[env.BSK_BIN,path12.join(home,".local","bin",process.platform==="win32"?"bsk.exe":"bsk")].filter(Boolean);for(const candidate of candidates)if(existsSync2(candidate))return candidate;return"bsk"}var NO_BREAKAWAY=/Job Object/;function windowsDaemonTaskName(home){return`bsk-daemon-${createHash2("sha256").update(path12.resolve(home).toLowerCase()).digest("hex").slice(0,12)}`}function windowsDaemonLaunch(home,bskBin){const literal=(value)=>`'${value.replace(/['\u2018\u2019\u201A\u201B]/g,(quote)=>quote+quote)}'`;const script=`$env:BSK_HOME = ${literal(home)}; & ${literal(bskBin)} daemon start --foreground; exit $LASTEXITCODE`;return`--headless powershell.exe -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(script,"utf16le").toString("base64")}`}function startDaemonInWindowsTask({home,bskBin,timeoutMs}){const script=["$action = New-ScheduledTaskAction -Execute (Join-Path $env:SystemRoot 'System32\\conhost.exe') -Argument $env:BSK_TASK_ARGUMENT","$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit ([TimeSpan]::Zero) -MultipleInstances IgnoreNew","Register-ScheduledTask -TaskName $env:BSK_TASK_NAME -Action $action -Settings $settings -Force | Out-Null","Start-ScheduledTask -TaskName $env:BSK_TASK_NAME"].join("; ");return runToExit("powershell.exe",["-NoProfile","-NonInteractive","-Command",script],{env:{...process.env,BSK_TASK_NAME:windowsDaemonTaskName(home),BSK_TASK_ARGUMENT:windowsDaemonLaunch(home,bskBin)},timeoutMs,label:"registering the bsk daemon task"})}function runToExit(command,args,{env,timeoutMs,label}){return new Promise((resolve,reject)=>{const child=spawn4(command,args,{env,stdio:["ignore","pipe","pipe"],windowsHide:true});let output="";const keep=(chunk)=>{output=(output+chunk).slice(-4096)};child.stdout.on("data",keep);child.stderr.on("data",keep);const timer=setTimeout(()=>{child.kill("SIGKILL");reject(new Error(`${label} did not return within ${timeoutMs}ms`))},timeoutMs);child.on("error",(error)=>{clearTimeout(timer);reject(new Error(`could not run ${command}: ${error.message}`))});child.on("exit",(code)=>{clearTimeout(timer);resolve({code,output:output.trim()})})})}async function startDaemonWithCli({home,env=process.env,bskBin=findBskBinary(env),timeoutMs=20000}={}){const status=await runToExit(bskBin,["status","--json"],{env:{...env,BSK_HOME:home},timeoutMs,label:"bsk status"});if(status.code===0)return;if(process.platform==="win32"&&NO_BREAKAWAY.test(status.output)){const task=await startDaemonInWindowsTask({home,bskBin,timeoutMs});if(task.code===0)return;throw new Error(`registering the bsk daemon task exited with ${task.code}${task.output?`: ${task.output}`:""}`)}throw new Error(`bsk status exited with ${status.code}${status.output?`: ${status.output}`:""}`)}var DEFAULT_TIMEOUT_MS2=35000;var DISCOVERY_RETRY_MS=250;var DISCOVERY_BUDGET_MS=15000;var TOOL_TIMEOUT_GRACE_MS=5000;var HUMAN_WAIT_METHODS=new Set(["tool.request_help","tool.tab_borrow"]);var HUMAN_WAIT_BACKSTOP_MS=3600000;function ownBudgetMs(params){const budgets=[params?.timeout_ms,params?.confirmation_timeout_ms].filter((value)=>typeof value==="number");return budgets.length>0?Math.max(...budgets)+TOOL_TIMEOUT_GRACE_MS:undefined}var PROBE_TIMEOUT_MS=2000;function defaultSocketPath(home){return path13.join(home,"run","daemon.sock")}function probeSocket(sockPath){if(process.platform==="win32"||!existsSync3(sockPath))return Promise.resolve(false);return new Promise((resolve)=>{const socket=connect2(sockPath);const done=(alive)=>{clearTimeout(timer);socket.destroy();resolve(alive)};const timer=setTimeout(()=>done(false),PROBE_TIMEOUT_MS);socket.on("error",()=>done(false));socket.on("connect",()=>socket.write(JSON.stringify({id:"probe-orphan",method:"system.ping"})+`
1514
- `));socket.on("data",()=>done(true))})}class BskRpcError extends Error{constructor(code,message,data){super(message);this.name="BskRpcError";this.code=code;this.data=data}}function shortId(){return randomBytes(6).toString("hex")}function sleep3(ms){return new Promise((resolve)=>setTimeout(resolve,ms))}class BskIpcClient{#home;#sockPath;#autoStart;#startDaemon;#timeoutMs;#starting=null;constructor({home,env=process.env,sockPath,autoStart,startDaemon,timeoutMs=DEFAULT_TIMEOUT_MS2,bskBin}={}){this.#home=home??resolveBskHome(env);this.#sockPath=sockPath??null;this.#autoStart=autoStart??env.BSK_AUTO_START!=="0";this.#startDaemon=startDaemon??(()=>startDaemonWithCli({home:this.#home,env,bskBin}));this.#timeoutMs=timeoutMs}get home(){return this.#home}async resolveSocket({isAborted=()=>false}={}){if(this.#sockPath)return this.#sockPath;const found=readDaemonInfo(this.#home);if(found)return found.sock_path;const orphan=defaultSocketPath(this.#home);if(existsSync3(orphan)){if(await probeSocket(orphan))return orphan;if(isAborted())throw new BskRpcError("cancelled","cancelled before the daemon was located")}if(!this.#autoStart){throw new BskRpcError("no_daemon",`no BrowserSkill daemon published at ${this.#home}/daemon.json and automatic start is disabled (BSK_AUTO_START=0); run \`bsk daemon start\` in the environment that owns it`)}if(!this.#starting)this.#starting=this.#startDaemon().finally(()=>{this.#starting=null});await this.#starting;const deadline=Date.now()+DISCOVERY_BUDGET_MS;while(Date.now()<deadline&&!isAborted()){const info=readDaemonInfo(this.#home);if(info)return info.sock_path;await sleep3(DISCOVERY_RETRY_MS)}if(isAborted())throw new BskRpcError("cancelled","cancelled while waiting for the daemon to publish daemon.json");throw new BskRpcError("no_daemon",`daemon start was requested but ${this.#home}/daemon.json never appeared`)}call(method,params,options={}){return this.callWithHandle(method,params,options).promise}callWithHandle(method,params,{idPrefix=method.replace(/[^a-z0-9]+/gi,"-"),timeoutMs}={}){const ownBudget=ownBudgetMs(params);const humanWait=HUMAN_WAIT_METHODS.has(method);const waitMs=humanWait&&ownBudget===undefined&&timeoutMs===undefined?HUMAN_WAIT_BACKSTOP_MS:Math.max(timeoutMs??this.#timeoutMs,ownBudget??0);const rpcId=`${idPrefix}-${shortId()}`;const frame={id:rpcId,method};if(params!==undefined)frame.params=params;let aborted=false;let abortSocket=()=>{};const promise=(async()=>{const sockPath=await this.resolveSocket({isAborted:()=>aborted});if(aborted)throw new BskRpcError("cancelled",`${method} cancelled by caller`);return new Promise((resolve,reject)=>{const socket=connect2(sockPath);let buffer="";let settled=false;const finish=(fn,value)=>{if(settled)return;settled=true;clearTimeout(timer);socket.destroy();fn(value)};const timer=setTimeout(()=>{const cancels=method!=="cancel"&&!humanWait;finish(reject,new BskRpcError("timeout",`${method} did not answer within ${waitMs}ms${cancels?"; a cancel was requested at the daemon":""}`));if(cancels)this.cancel(rpcId).catch(()=>{})},waitMs);abortSocket=()=>finish(reject,new BskRpcError("cancelled",`${method} cancelled by caller`));socket.on("error",(error)=>finish(reject,new BskRpcError("no_daemon",`IPC socket ${sockPath}: ${error.code??error.message}`)));socket.on("connect",()=>socket.write(JSON.stringify(frame)+`
1511
+ `)}function dispose(){if(disposed)return;disposed=true;for(const off of unsubscribers.splice(0,unsubscribers.length))off();for(const waiter of[...waiters])waiter.reject(Error("Network snoop disposed"));waiters.clear();inflight.clear()}return{pop,peek,popJson,waitFor,summary,dispose}}function globToRegExp(glob){let source="";for(const char of glob){if(char==="*")source+=".*";else if(char==="?")source+=".";else if("\\^$+{}[]()|.".includes(char))source+=`\\${char}`;else source+=char}return new RegExp(`^${source}$`)}function hasGlobChars(value){return value.includes("*")||value.includes("?")}function matches(match,request){if(typeof match==="function"){return Boolean(match({url:request.url,method:request.method,resourceType:request.resourceType,headers:request.headers}))}if(match instanceof RegExp)return match.test(request.url);if(typeof match==="string"){if(hasGlobChars(match))return globToRegExp(match).test(request.url);return request.url.includes(match)}return false}function sameMatch(left,right){if(left===right)return true;if(left instanceof RegExp&&right instanceof RegExp)return left.source===right.source&&left.flags===right.flags;return false}function toHeaderEntries(headers){if(!headers)return[];if(Array.isArray(headers)){return headers.map((header)=>({name:String(header.name),value:String(header.value??"")}))}return Object.entries(headers).map(([name,value])=>({name,value:String(value??"")}))}function encodeBody(body){if(body==null)return"";if(Buffer.isBuffer(body))return body.toString("base64");if(body instanceof Uint8Array)return Buffer.from(body).toString("base64");return Buffer.from(String(body)).toString("base64")}function buildContinue(requestId,{url,method,headers,postData}={}){const params={requestId};if(url!==undefined)params.url=url;if(method!==undefined)params.method=method;if(headers!==undefined)params.headers=toHeaderEntries(headers);if(postData!==undefined)params.postData=encodeBody(postData);return params}function buildFulfill(requestId,{status=200,headers={},contentType,body=""}={}){const responseHeaders=toHeaderEntries(headers);if(contentType!=null&&String(contentType).length>0){const hasType=responseHeaders.some((header)=>header.name.toLowerCase()==="content-type");if(!hasType)responseHeaders.push({name:"content-type",value:String(contentType)})}return{requestId,responseCode:status,responseHeaders,body:encodeBody(body)}}function createRoutes(page){if(!page?.cdp)throw new TypeError("createRoutes requires a page with cdp");const entries=[];const warned=new WeakSet;const settledIds=new Set;let enabled=false;let sessionId;let unsubscribe=null;let enablePromise=null;function warnOnce(handler,error){if(typeof handler==="function"){if(warned.has(handler))return;warned.add(handler)}if(error)console.warn("[routes] route handler failed; continuing request",error);else console.warn("[routes] route handler did not settle the request; continuing")}function findHandler(request){for(const entry of entries){if(matches(entry.match,request))return entry.handler}return}async function handlePaused(params,meta){if(!sessionId||meta?.sessionId!==sessionId)return;const requestId=params?.requestId;if(!requestId||settledIds.has(requestId))return;const request={url:params.request?.url??"",method:params.request?.method??"GET",headers:params.request?.headers??{},postData:params.request?.postData,resourceType:params.resourceType};let settled=false;const settle=(method,cdpParams)=>{if(settled)return Promise.resolve();settled=true;settledIds.add(requestId);return page.cdp.send(method,cdpParams,sessionId)};const route={request,continue(overrides={}){return settle("Fetch.continueRequest",buildContinue(requestId,overrides))},fulfill(options={}){return settle("Fetch.fulfillRequest",buildFulfill(requestId,options))},abort(errorReason="Failed"){return settle("Fetch.failRequest",{requestId,errorReason})}};const handler=findHandler(request);if(!handler){await settle("Fetch.continueRequest",{requestId});return}try{await handler(route)}catch(error){warnOnce(handler,error)}if(!settled){warnOnce(handler);await settle("Fetch.continueRequest",{requestId})}}function onPaused(params,meta){handlePaused(params,meta).catch((error)=>{console.warn("[routes] requestPaused handling failed",error)})}async function ensureEnabled(){if(enabled)return;if(!enablePromise){enablePromise=(async()=>{sessionId=await page.resolveSessionId();if(!unsubscribe)unsubscribe=page.cdp.on("Fetch.requestPaused",onPaused);await page.cdp.send("Fetch.enable",{patterns:[{urlPattern:"*",requestStage:"Request"}]},sessionId);enabled=true})()}try{await enablePromise}catch(error){enablePromise=null;throw error}}async function disableFetch(){const pending=enablePromise;enablePromise=null;if(pending)await pending.catch(()=>{});if(unsubscribe){unsubscribe();unsubscribe=null}if(!enabled)return;enabled=false;await page.cdp.send("Fetch.disable",{},sessionId)}async function route(match,handler){if(typeof handler!=="function")throw new TypeError("handler must be a function");entries.push({match,handler});await ensureEnabled()}async function unroute(match){if(match===undefined)entries.length=0;else{for(let index=entries.length-1;index>=0;index-=1){if(sameMatch(entries[index].match,match))entries.splice(index,1)}}if(entries.length===0)await disableFetch()}async function dispose(){entries.length=0;await disableFetch()}return{route,unroute,dispose,get enabled(){return enabled}}}async function viewportOf(page){return page.viewportSize()??await page.evaluate(()=>({width:innerWidth,height:innerHeight}))}async function scrollOnce(page,mode){if(mode==="end"){await page.evaluate(()=>scrollTo(0,document.scrollingElement.scrollHeight));return}const viewport=await viewportOf(page);await page.mouse.move(Math.floor(viewport.width/2),Math.floor(viewport.height/2));await page.mouse.wheel(0,Math.floor(viewport.height*0.9))}async function*collectWhileScrolling(page,snoop,options={}){const{minItems=Infinity,maxScrolls=10,extract=(json)=>Array.isArray(json)?json:[],scroll="wheel",settleMs=800}=options;if(scroll!=="wheel"&&scroll!=="end")throw new TypeError(`Unknown scroll mode: ${String(scroll)}`);let rounds=0;let total=0;let lastHeight=null;let unchanged=0;let stoppedBecause="maxScrolls";for(;;){for(const json of snoop.popJson()){for(const item of extract(json)){total+=1;yield item}}if(total>=minItems){stoppedBecause="minItems";break}if(rounds>=maxScrolls){stoppedBecause="maxScrolls";break}const height=await page.evaluate(()=>document.scrollingElement.scrollHeight);if(lastHeight!==null&&height===lastHeight){unchanged+=1;if(unchanged>=2){stoppedBecause="noGrowth";break}}else{unchanged=0}lastHeight=height;await scrollOnce(page,scroll);rounds+=1;await new Promise((resolve)=>setTimeout(resolve,settleMs))}return{rounds,total,stoppedBecause}}import{mkdir as mkdir6,writeFile as writeFile2}from"node:fs/promises";import path12 from"node:path";var SLUG_MAX=48;var SNOOP_MAX_ENTRIES=5000;function slug(name){const value=String(name??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");return(value||"step").slice(0,SLUG_MAX)}function messageOf(error){return error instanceof Error?error.message:String(error)}async function readConsole(page){const api=page.console;const entries=typeof api==="function"?await api.call(page):await api?.logs?.();return Array.isArray(entries)?entries:[]}function createTrace(page,options={}){if(!page?.cdp||typeof page.resolveSessionId!=="function"){throw new TypeError("createTrace requires a page with cdp and resolveSessionId")}const{dir,screenshots=true,network=true,console:consoleLogs=true,maxBodyBytes=1e6,events}=options;if(typeof dir!=="string"||dir.length===0)throw new TypeError("createTrace requires a dir");const shotDir=path12.join(dir,"screenshots");const ready=mkdir6(shotDir,{recursive:true});const startedAt=Date.now();const records=[];const netEntries=[];const unsubscribers=[];const snoop=network?createNetworkSnoop(page,{bodies:true,maxBodyBytes,maxEntries:SNOOP_MAX_ENTRIES}):null;let seq=0;let consoleSeen=0;let sessionId=null;let stopped=false;let stopping=null;function record(event){const entry={...event,ts:Date.now(),seq:seq++};records.push(entry);return entry}const sessionReady=Promise.resolve(page.resolveSessionId()).then((id)=>{sessionId=id});sessionReady.catch((error)=>record({kind:"warning",scope:"session",message:messageOf(error)}));unsubscribers.push(page.cdp.on("Page.frameNavigated",(params,meta)=>{if(stopped||params?.frame?.parentId)return;const add=()=>record({kind:"navigation",url:params?.frame?.url??"",frameId:params?.frame?.id??null});if(sessionId===null){sessionReady.then(()=>{if(!stopped&&meta?.sessionId===sessionId)add()}).catch(()=>{});return}if(meta?.sessionId===sessionId)add()}));unsubscribers.push(page.on("dialog",(dialog)=>record({kind:"dialog",type:dialog?.type??null,message:dialog?.message??"",defaultPrompt:dialog?.defaultPrompt??"",url:dialog?.url??null})));if(events){unsubscribers.push(events.on("downloadStarted",(payload)=>record({kind:"download",phase:"started",...payload})));unsubscribers.push(events.on("downloadFinished",(payload)=>record({kind:"download",phase:"finished",...payload})))}async function capture(step,phase){const file=path12.join(shotDir,`${step.seq}-${slug(step.name)}-${phase}.png`);try{await writeFile2(file,await page.screenshot({type:"png"}));step.screenshots[phase]=file}catch(error){record({kind:"warning",scope:`screenshot:${phase}`,step:step.seq,message:messageOf(error)})}}function drainNetwork(){for(const entry of snoop?.pop()??[]){if(typeof entry.body==="string"&&Buffer.byteLength(entry.body)>maxBodyBytes){entry.body=null;entry.bodySkipped=true}netEntries.push(entry);record({kind:"network",...entry})}}async function drainConsole(){if(!consoleLogs)return;let entries;try{entries=await readConsole(page)}catch{return}if(entries.length<consoleSeen)consoleSeen=0;for(const entry of entries.slice(consoleSeen))record({kind:"console",...entry});consoleSeen=entries.length}async function drain(){drainNetwork();await drainConsole()}async function step(name,fn){if(typeof fn!=="function")throw new TypeError("trace.step requires a function");await ready;const entry=record({kind:"step",name:String(name),startedAt:Date.now(),screenshots:{}});if(screenshots)await capture(entry,"before");try{return await fn()}catch(error){entry.error=messageOf(error);throw error}finally{entry.endedAt=Date.now();entry.durationMs=entry.endedAt-entry.startedAt;if(screenshots)await capture(entry,"after");await drain()}}function mark(name,data){return record({kind:"mark",name:String(name),data:data??null})}async function finish(){await ready;await drain();stopped=true;for(const off of unsubscribers.splice(0,unsubscribers.length))off();snoop?.dispose();const ordered=[...records].sort((a,b)=>a.seq-b.seq);const summary={steps:ordered.filter((entry)=>entry.kind==="step").length,events:ordered.length,network:netEntries.length,startedAt,endedAt:Date.now(),url:page.url()};await writeFile2(path12.join(dir,"trace.jsonl"),ordered.map((entry)=>`${JSON.stringify(entry)}
1512
+ `).join(""));await writeFile2(path12.join(dir,"trace.har"),`${JSON.stringify(toHar(netEntries),null,2)}
1513
+ `);await writeFile2(path12.join(dir,"summary.json"),`${JSON.stringify(summary,null,2)}
1514
+ `);return summary}function stop(){return stopping??=finish()}return{step,mark,stop,get dir(){return dir}}}import{connect as connect2}from"node:net";import{existsSync as existsSync3}from"node:fs";import path14 from"node:path";import{randomBytes}from"node:crypto";import{existsSync as existsSync2,readFileSync as readFileSync4}from"node:fs";import{homedir as homedir2}from"node:os";import path13 from"node:path";import{spawn as spawn4}from"node:child_process";import{createHash as createHash2}from"node:crypto";function resolveBskHome(env=process.env){const override=env.BSK_HOME;if(typeof override==="string"&&override.length>0)return override;const home=env.HOME&&env.HOME.length>0?env.HOME:homedir2();return path13.join(home,".bsk")}function daemonInfoPath(home){return path13.join(home,"daemon.json")}function readDaemonInfo(home){let raw;try{raw=readFileSync4(daemonInfoPath(home),"utf8")}catch{return null}let info;try{info=JSON.parse(raw)}catch{return null}if(!info||typeof info.sock_path!=="string"||info.sock_path.length===0)return null;return info}function findBskBinary(env=process.env){const home=env.HOME&&env.HOME.length>0?env.HOME:homedir2();const candidates=[env.BSK_BIN,path13.join(home,".local","bin",process.platform==="win32"?"bsk.exe":"bsk")].filter(Boolean);for(const candidate of candidates)if(existsSync2(candidate))return candidate;return"bsk"}var NO_BREAKAWAY=/Job Object/;function windowsDaemonTaskName(home){return`bsk-daemon-${createHash2("sha256").update(path13.resolve(home).toLowerCase()).digest("hex").slice(0,12)}`}function windowsDaemonLaunch(home,bskBin){const literal=(value)=>`'${value.replace(/['\u2018\u2019\u201A\u201B]/g,(quote)=>quote+quote)}'`;const script=`$env:BSK_HOME = ${literal(home)}; & ${literal(bskBin)} daemon start --foreground; exit $LASTEXITCODE`;return`--headless powershell.exe -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(script,"utf16le").toString("base64")}`}function startDaemonInWindowsTask({home,bskBin,timeoutMs}){const script=["$action = New-ScheduledTaskAction -Execute (Join-Path $env:SystemRoot 'System32\\conhost.exe') -Argument $env:BSK_TASK_ARGUMENT","$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit ([TimeSpan]::Zero) -MultipleInstances IgnoreNew","Register-ScheduledTask -TaskName $env:BSK_TASK_NAME -Action $action -Settings $settings -Force | Out-Null","Start-ScheduledTask -TaskName $env:BSK_TASK_NAME"].join("; ");return runToExit("powershell.exe",["-NoProfile","-NonInteractive","-Command",script],{env:{...process.env,BSK_TASK_NAME:windowsDaemonTaskName(home),BSK_TASK_ARGUMENT:windowsDaemonLaunch(home,bskBin)},timeoutMs,label:"registering the bsk daemon task"})}function runToExit(command,args,{env,timeoutMs,label}){return new Promise((resolve,reject)=>{const child=spawn4(command,args,{env,stdio:["ignore","pipe","pipe"],windowsHide:true});let output="";const keep=(chunk)=>{output=(output+chunk).slice(-4096)};child.stdout.on("data",keep);child.stderr.on("data",keep);const timer=setTimeout(()=>{child.kill("SIGKILL");reject(new Error(`${label} did not return within ${timeoutMs}ms`))},timeoutMs);child.on("error",(error)=>{clearTimeout(timer);reject(new Error(`could not run ${command}: ${error.message}`))});child.on("exit",(code)=>{clearTimeout(timer);resolve({code,output:output.trim()})})})}async function startDaemonWithCli({home,env=process.env,bskBin=findBskBinary(env),timeoutMs=20000}={}){const status=await runToExit(bskBin,["status","--json"],{env:{...env,BSK_HOME:home},timeoutMs,label:"bsk status"});if(status.code===0)return;if(process.platform==="win32"&&NO_BREAKAWAY.test(status.output)){const task=await startDaemonInWindowsTask({home,bskBin,timeoutMs});if(task.code===0)return;throw new Error(`registering the bsk daemon task exited with ${task.code}${task.output?`: ${task.output}`:""}`)}throw new Error(`bsk status exited with ${status.code}${status.output?`: ${status.output}`:""}`)}var DEFAULT_TIMEOUT_MS2=35000;var DISCOVERY_RETRY_MS=250;var DISCOVERY_BUDGET_MS=15000;var TOOL_TIMEOUT_GRACE_MS=5000;var HUMAN_WAIT_METHODS=new Set(["tool.request_help","tool.tab_borrow"]);var HUMAN_WAIT_BACKSTOP_MS=3600000;function ownBudgetMs(params){const budgets=[params?.timeout_ms,params?.confirmation_timeout_ms].filter((value)=>typeof value==="number");return budgets.length>0?Math.max(...budgets)+TOOL_TIMEOUT_GRACE_MS:undefined}var PROBE_TIMEOUT_MS=2000;function defaultSocketPath(home){return path14.join(home,"run","daemon.sock")}function probeSocket(sockPath){if(process.platform==="win32"||!existsSync3(sockPath))return Promise.resolve(false);return new Promise((resolve)=>{const socket=connect2(sockPath);const done=(alive)=>{clearTimeout(timer);socket.destroy();resolve(alive)};const timer=setTimeout(()=>done(false),PROBE_TIMEOUT_MS);socket.on("error",()=>done(false));socket.on("connect",()=>socket.write(JSON.stringify({id:"probe-orphan",method:"system.ping"})+`
1515
+ `));socket.on("data",()=>done(true))})}class BskRpcError extends Error{constructor(code,message,data){super(message);this.name="BskRpcError";this.code=code;this.data=data}}function shortId(){return randomBytes(6).toString("hex")}function sleep4(ms){return new Promise((resolve)=>setTimeout(resolve,ms))}class BskIpcClient{#home;#sockPath;#autoStart;#startDaemon;#timeoutMs;#starting=null;constructor({home,env=process.env,sockPath,autoStart,startDaemon,timeoutMs=DEFAULT_TIMEOUT_MS2,bskBin}={}){this.#home=home??resolveBskHome(env);this.#sockPath=sockPath??null;this.#autoStart=autoStart??env.BSK_AUTO_START!=="0";this.#startDaemon=startDaemon??(()=>startDaemonWithCli({home:this.#home,env,bskBin}));this.#timeoutMs=timeoutMs}get home(){return this.#home}async resolveSocket({isAborted=()=>false}={}){if(this.#sockPath)return this.#sockPath;const found=readDaemonInfo(this.#home);if(found)return found.sock_path;const orphan=defaultSocketPath(this.#home);if(existsSync3(orphan)){if(await probeSocket(orphan))return orphan;if(isAborted())throw new BskRpcError("cancelled","cancelled before the daemon was located")}if(!this.#autoStart){throw new BskRpcError("no_daemon",`no BrowserSkill daemon published at ${this.#home}/daemon.json and automatic start is disabled (BSK_AUTO_START=0); run \`bsk daemon start\` in the environment that owns it`)}if(!this.#starting)this.#starting=this.#startDaemon().finally(()=>{this.#starting=null});await this.#starting;const deadline=Date.now()+DISCOVERY_BUDGET_MS;while(Date.now()<deadline&&!isAborted()){const info=readDaemonInfo(this.#home);if(info)return info.sock_path;await sleep4(DISCOVERY_RETRY_MS)}if(isAborted())throw new BskRpcError("cancelled","cancelled while waiting for the daemon to publish daemon.json");throw new BskRpcError("no_daemon",`daemon start was requested but ${this.#home}/daemon.json never appeared`)}call(method,params,options={}){return this.callWithHandle(method,params,options).promise}callWithHandle(method,params,{idPrefix=method.replace(/[^a-z0-9]+/gi,"-"),timeoutMs}={}){const ownBudget=ownBudgetMs(params);const humanWait=HUMAN_WAIT_METHODS.has(method);const waitMs=humanWait&&ownBudget===undefined&&timeoutMs===undefined?HUMAN_WAIT_BACKSTOP_MS:Math.max(timeoutMs??this.#timeoutMs,ownBudget??0);const rpcId=`${idPrefix}-${shortId()}`;const frame={id:rpcId,method};if(params!==undefined)frame.params=params;let aborted=false;let abortSocket=()=>{};const promise=(async()=>{const sockPath=await this.resolveSocket({isAborted:()=>aborted});if(aborted)throw new BskRpcError("cancelled",`${method} cancelled by caller`);return new Promise((resolve,reject)=>{const socket=connect2(sockPath);let buffer="";let settled=false;const finish=(fn,value)=>{if(settled)return;settled=true;clearTimeout(timer);socket.destroy();fn(value)};const timer=setTimeout(()=>{const cancels=method!=="cancel"&&!humanWait;finish(reject,new BskRpcError("timeout",`${method} did not answer within ${waitMs}ms${cancels?"; a cancel was requested at the daemon":""}`));if(cancels)this.cancel(rpcId).catch(()=>{})},waitMs);abortSocket=()=>finish(reject,new BskRpcError("cancelled",`${method} cancelled by caller`));socket.on("error",(error)=>finish(reject,new BskRpcError("no_daemon",`IPC socket ${sockPath}: ${error.code??error.message}`)));socket.on("connect",()=>socket.write(JSON.stringify(frame)+`
1515
1516
  `));socket.on("data",(chunk)=>{buffer+=chunk.toString("utf8");const idx=buffer.indexOf(`
1516
1517
  `);if(idx===-1)return;try{finish(resolve,decodeResponse(buffer.slice(0,idx),rpcId,method))}catch(error){finish(reject,error)}});socket.on("close",()=>finish(reject,new BskRpcError("protocol_error",`${method}: daemon closed the connection without a reply`)))})})();return{rpcId,promise,abort:()=>{aborted=true;abortSocket()}}}async cancel(rpcId){return this.call("cancel",{rpc_id:rpcId},{idPrefix:"cancel",timeoutMs:2000})}}function decodeResponse(line,rpcId,method){let reply;try{reply=JSON.parse(line)}catch{throw new BskRpcError("protocol_error",`${method}: daemon reply is not JSON: ${line.slice(0,120)}`)}const undecodableFrame=reply.id==="0"&&reply.error;if(reply.id!==rpcId&&!undecodableFrame){throw new BskRpcError("protocol_error",`${method}: reply id ${reply.id} does not match request ${rpcId}`)}if(reply.error){const{code="protocol_error",message="daemon error",data}=reply.error;throw new BskRpcError(code,message,data)}return reply.result}var REF_SHAPE=/^@?e\d+$/;function resolveTarget(target){if(typeof target==="string"){return REF_SHAPE.test(target)?{ref:target}:{selector:target}}if(target&&typeof target==="object"){if(typeof target.ref==="string")return{ref:target.ref};if(typeof target.selector==="string")return{selector:target.selector};if(typeof target.captureId==="string"){return{capture_id:target.captureId,image_x:target.x,image_y:target.y}}}throw new TypeError("target must be a ref ('e3' / '@e3'), a CSS selector, or {captureId, x, y}")}function optionalTarget(target){return target===undefined?{}:resolveTarget(target)}function compact(params){const out={};for(const[key,value]of Object.entries(params))if(value!==undefined)out[key]=value;return out}async function captureScreenshot(session,{ref,tabId,fullPage=false,scope,timeoutMs}={}){if(!fullPage){const result=await session.tool("screenshot",{ref,tab_id:tabId});return{buffer:Buffer.from(result.image_base64,"base64"),width:result.width,height:result.height,format:result.format,captureId:result.capture_id??null,captureUnavailable:result.capture_unavailable??null,tabId:result.tab_id,dialogs:result.dialogs??[]}}const started=await session.tool("screenshot_full_page",{scope,tab_id:tabId,timeout_ms:timeoutMs});const buffer=await readCapture(session,started.capture_id,started.byte_size);return{buffer,width:started.width,height:started.height,format:started.format,captureId:started.capture_id,scope:started.scope??scope??null,tabId:started.tab_id,dialogs:started.dialogs??[]}}async function readCapture(session,captureId,byteSize){const chunks=[];let offset=0;try{for(;;){const chunk=await session.tool("screenshot_read",{capture_id:captureId,offset});const bytes=Buffer.from(chunk.data_base64??"","base64");chunks.push(bytes);if(chunk.eof||bytes.length===0)break;if(chunk.next_offset<=offset)throw new Error(`screenshot_read did not advance past offset ${offset}`);offset=chunk.next_offset}}finally{await session.tool("screenshot_release",{capture_id:captureId}).catch(()=>{})}const buffer=Buffer.concat(chunks);if(typeof byteSize==="number"&&buffer.length!==byteSize){throw new Error(`screenshot capture ${captureId}: expected ${byteSize} bytes, assembled ${buffer.length}`)}return buffer}var SESSION_STOP_TIMEOUT_MS=3600000;class BskSession{#client;#stopped=false;constructor(client,{sessionId,browserInstanceId=null,agentWindowId=null,interaction=null}={}){if(typeof sessionId!=="string"||sessionId.length===0)throw new TypeError("BskSession requires a sessionId");this.#client=client;this.sessionId=sessionId;this.browserInstanceId=browserInstanceId;this.agentWindowId=agentWindowId;this.interaction=interaction}get client(){return this.#client}get stopped(){return this.#stopped}tool(name,params={}){if(this.#stopped)return Promise.reject(new Error(`BrowserSkill session ${this.sessionId} is stopped`));return this.#client.call(`tool.${name}`,compact({session_id:this.sessionId,...params}),{idPrefix:name.replace(/_/g,"-")})}navigate(url,{tabId,waitUntil,timeoutMs}={}){return this.tool("navigate",{url,tab_id:tabId,wait_until:waitUntil,timeout_ms:timeoutMs})}back({tabId,waitUntil,timeoutMs}={}){return this.tool("navigate_back",{tab_id:tabId,wait_until:waitUntil,timeout_ms:timeoutMs})}forward({tabId,waitUntil,timeoutMs}={}){return this.tool("navigate_forward",{tab_id:tabId,wait_until:waitUntil,timeout_ms:timeoutMs})}reload({tabId,waitUntil,timeoutMs,hard}={}){return this.tool("reload",{tab_id:tabId,wait_until:waitUntil,timeout_ms:timeoutMs,hard})}observe({cursor,tabId,maxDepth,maxTokens,debugSurfaces,probeHover}={}){return this.tool("observe",{cursor,tab_id:tabId,max_depth:maxDepth,max_tokens:maxTokens,debug_surfaces:debugSurfaces,probe_hover:probeHover})}snapshot({tabId,maxDepth,maxTokens}={}){return this.tool("snapshot",{tab_id:tabId,max_depth:maxDepth,max_tokens:maxTokens})}getHtml({ref,tabId,maxBytes}={}){return this.tool("get_html",{ref,tab_id:tabId,max_bytes:maxBytes})}screenshot(options){return captureScreenshot(this,options)}click(target,{button,clickCount,modifiers,tabId,timeoutMs}={}){return this.tool("click",{...resolveTarget(target),button,click_count:clickCount,modifiers,tab_id:tabId,timeout_ms:timeoutMs})}hover(target,{modifiers,settleMs,tabId,timeoutMs}={}){return this.tool("hover",{...resolveTarget(target),modifiers,settle_ms:settleMs,tab_id:tabId,timeout_ms:timeoutMs})}focus(target,{tabId,timeoutMs}={}){return this.tool("focus",{...resolveTarget(target),tab_id:tabId,timeout_ms:timeoutMs})}blur(target,{tabId,timeoutMs}={}){return this.tool("blur",{...resolveTarget(target),tab_id:tabId,timeout_ms:timeoutMs})}fill(target,value,{clearBefore,tabId,timeoutMs}={}){return this.tool("fill",{...resolveTarget(target),value,clear_before:clearBefore,tab_id:tabId,timeout_ms:timeoutMs})}press(key,{target,modifiers,holdMs,tabId,timeoutMs}={}){return this.tool("press",{key,...optionalTarget(target),modifiers,hold_ms:holdMs,tab_id:tabId,timeout_ms:timeoutMs})}select(target,values,{tabId,timeoutMs}={}){return this.tool("select",{...resolveTarget(target),values:Array.isArray(values)?values:[values],tab_id:tabId,timeout_ms:timeoutMs})}scrollTo(target,{tabId,timeoutMs}={}){return this.tool("scroll_to",{...resolveTarget(target),tab_id:tabId,timeout_ms:timeoutMs})}wheel({deltaX=0,deltaY=0,target,modifiers,tabId,timeoutMs}={}){return this.tool("wheel",{...optionalTarget(target),delta_x:deltaX,delta_y:deltaY,modifiers,tab_id:tabId,timeout_ms:timeoutMs})}evaluate(expression,{tabId,awaitPromise,returnByValue,timeoutMs}={}){return this.tool("evaluate",{expression,tab_id:tabId,await_promise:awaitPromise,return_by_value:returnByValue,timeout_ms:timeoutMs})}tabList({scope}={}){return this.tool("tab_list",{scope})}tabCreate({url,active,index}={}){return this.tool("tab_create",{url,active,index})}tabClose(tabId){return this.tool("tab_close",{tab_id:tabId})}tabSelect(tabId){return this.tool("tab_select",{tab_id:tabId})}tabBorrow(tabId,{confirmationTimeoutMs}={}){return this.tool("tab_borrow",{tab_id:tabId,confirmation_timeout_ms:confirmationTimeoutMs})}tabReturn(tabId){return this.tool("tab_return",{tab_id:tabId})}waitForNavigation({tabId,waitUntil,timeoutMs}={}){return this.tool("wait_for_navigation",{tab_id:tabId,wait_until:waitUntil,timeout_ms:timeoutMs})}requestHelp({prompt,title,targets,completionCriteria,tabId,timeoutMs}={}){return this.tool("request_help",{prompt,title,tab_id:tabId,timeout_ms:timeoutMs,targets:targets?.map((t)=>resolveTarget(t)),completion_criteria:completionCriteria})}console({tabId,since,limit,maxTextChars,includeStack}={}){return this.tool("console",{tab_id:tabId,since,limit,max_text_chars:maxTextChars,include_stack:includeStack})}network({tabId,since,limit,maxTextChars}={}){return this.tool("network",{tab_id:tabId,since,limit,max_text_chars:maxTextChars})}resize(width,height){return this.tool("window_resize",{width,height})}emulate({tabId,off,overrides}={}){return this.tool("emulate",{tab_id:tabId,off,overrides})}async stop(){if(this.#stopped)return null;this.#stopped=true;return this.#client.call("session.stop",{session_id:this.sessionId},{idPrefix:"session-stop",timeoutMs:SESSION_STOP_TIMEOUT_MS})}}var DEFAULT_WAIT_FOR_BROWSER_MS=15000;async function listBrowsers(client,{waitForBrowserMs}={}){const status=await client.call("system.status",compact({wait_for_browser_ms:waitForBrowserMs}),{idPrefix:"status"});return status.browsers??[]}async function connectBrowserSkill({client,home,env,sockPath,autoStart,timeoutMs,bskBin,name,browser,width,height,focused=false,waitForBrowserMs=DEFAULT_WAIT_FOR_BROWSER_MS}={}){const ipc=client??new BskIpcClient({home,env,sockPath,autoStart,timeoutMs,bskBin});const browsers=await listBrowsers(ipc,{waitForBrowserMs});if(browsers.length===0){throw new BskRpcError("no_browser_connected","the BrowserSkill daemon is up but no browser extension is connected; open the browser that has the BrowserSkill extension enabled (bskDoctor() explains the remaining step) instead of falling back to a headless browser")}const started=await ipc.call("session.start",compact({task_name:name,browser_instance_id:browser,width,height,focused}),{idPrefix:"session-start"});return new BskSession(ipc,{sessionId:started.session_id,browserInstanceId:started.browser_instance_id??null,agentWindowId:started.agent_window_id??null,interaction:started.interaction??null})}var CSS_PATH_FN=String(function cssPath(el){if(!(el instanceof Element))return null;if(el.getRootNode()!==document)return null;const parts=[];let node=el;while(node&&node.nodeType===1&&node!==document.documentElement){let part=node.localName;if(node.id&&/^[A-Za-z_][\w-]*$/.test(node.id)&&document.querySelectorAll("#"+node.id).length===1){parts.unshift("#"+node.id);return parts.join(" > ")}const parent=node.parentElement;if(parent){const same=Array.from(parent.children).filter((c)=>c.localName===node.localName);if(same.length>1)part+=`:nth-of-type(${same.indexOf(node)+1})`}parts.unshift(part);node=parent}parts.unshift("html");return parts.join(" > ")});function buildSnapshotExpression(options={}){return`(function () {
1517
1518
  const globalThis = {};
@@ -1522,5 +1523,5 @@ ${snapshot.tree}`}}var DEFAULT_TIMEOUT_MS=30000;var JSON_MIME=/^application\/jso
1522
1523
  const css = {};
1523
1524
  for (const ref of Object.keys(result.refs || {})) css[ref] = cssPath(api.elementRegistry.get(ref));
1524
1525
  return { tree: result.tree, refs: result.refs || {}, css, error: result.error };
1525
- })()`}async function bskSnapshot(session,{tabId,timeoutMs,...snapshotOptions}={}){const reply=await session.evaluate(buildSnapshotExpression(snapshotOptions),{tabId,timeoutMs,awaitPromise:false,returnByValue:true});if(!reply||reply.ok===false){const text=reply?.error?.text??"unknown error";throw new Error(`bskSnapshot failed inside the page: ${text}`)}const value=reply.value;if(!value||typeof value.tree!=="string")throw new Error("bskSnapshot: page returned no tree");if(value.error)throw new Error(`bskSnapshot: ${value.error}`);return{tree:value.tree,refs:value.refs,css:value.css}}import{existsSync as existsSync5,mkdirSync as mkdirSync2,readFileSync as readFileSync4,rmSync as rmSync2,writeFileSync as writeFileSync2}from"node:fs";import{spawn as spawn5}from"node:child_process";import path15 from"node:path";import{existsSync as existsSync4}from"node:fs";import{homedir as homedir3}from"node:os";import path14 from"node:path";var BROWSERSKILL_EXTENSION_IDS=Object.freeze({chrome:"hhcmgoofomhgciiibhipgmgkgnoenaoi",edge:"emacgiaaaiojkkpkddmmdfhmokgmnikg"});var STORE_UPDATE_URLS=Object.freeze({chrome:"https://clients2.google.com/service/update2/crx",edge:"https://edge.microsoft.com/extensionwebstorebase/v1/crx"});var STORE_PAGE_URLS=Object.freeze({chrome:`https://chromewebstore.google.com/detail/${BROWSERSKILL_EXTENSION_IDS.chrome}`,edge:`https://microsoftedge.microsoft.com/addons/detail/browserskill/${BROWSERSKILL_EXTENSION_IDS.edge}`});var NON_CHROMIUM_DEFAULTS=Object.freeze([{id:"safari",label:"Safari",match:["com.apple.safari","safarihtml"]},{id:"firefox",label:"Firefox",match:["org.mozilla.firefox","firefox.desktop","firefoxurl","firefoxhtml"]},{id:"zen",label:"Zen",match:["app.zen-browser.zen","zen.desktop","zenhtml"]},{id:"orion",label:"Orion",match:["com.kagi.kagimacos"]}]);var MAC=[{id:"chrome",label:"Google Chrome",store:"chrome",userData:"Library/Application Support/Google/Chrome",app:"Google Chrome",bundleId:"com.google.Chrome"},{id:"edge",label:"Microsoft Edge",store:"edge",userData:"Library/Application Support/Microsoft Edge",app:"Microsoft Edge",bundleId:"com.microsoft.edgemac"},{id:"brave",label:"Brave",store:"chrome",userData:"Library/Application Support/BraveSoftware/Brave-Browser",app:"Brave Browser",bundleId:"com.brave.Browser"},{id:"chromium",label:"Chromium",store:"chrome",userData:"Library/Application Support/Chromium",app:"Chromium",bundleId:"org.chromium.Chromium"},{id:"arc",label:"Arc",store:"chrome",userData:"Library/Application Support/Arc/User Data",app:"Arc",bundleId:"company.thebrowser.Browser"},{id:"dia",label:"Dia",store:"chrome",userData:"Library/Application Support/Dia/User Data",app:"Dia",bundleId:"company.thebrowser.dia"},{id:"vivaldi",label:"Vivaldi",store:"chrome",userData:"Library/Application Support/Vivaldi",app:"Vivaldi",bundleId:"com.vivaldi.Vivaldi"},{id:"opera",label:"Opera",store:"chrome",userData:"Library/Application Support/com.operasoftware.Opera",app:"Opera",bundleId:"com.operasoftware.Opera",externalExtensions:false},{id:"comet",label:"Comet",store:"chrome",userData:"Library/Application Support/Comet",app:"Comet",bundleId:"ai.perplexity.comet"},{id:"aside",label:"Aside",store:"chrome",userData:"Library/Application Support/Aside",app:"Aside",bundleId:"at.studio.AsideBrowser"},{id:"whale",label:"Naver Whale",store:"chrome",userData:"Library/Application Support/Naver/Whale",app:"Naver Whale",bundleId:"com.naver.Whale"}];var LINUX=[{id:"chrome",label:"Google Chrome",store:"chrome",userData:".config/google-chrome",binaries:["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/opt/google/chrome/chrome"],externalDir:"/opt/google/chrome/extensions",userExternalDir:false,desktopIds:["google-chrome.desktop","google-chrome-stable.desktop","com.google.Chrome.desktop"],processNames:["chrome","google-chrome"]},{id:"chromium",label:"Chromium",store:"chrome",userData:".config/chromium",binaries:["/usr/bin/chromium","/usr/bin/chromium-browser","/snap/bin/chromium"],externalDir:"/usr/share/chromium/extensions",userExternalDir:true,desktopIds:["chromium.desktop","chromium-browser.desktop","chromium_chromium.desktop","org.chromium.Chromium.desktop"],processNames:["chromium","chromium-browse"]},{id:"edge",label:"Microsoft Edge",store:"edge",userData:".config/microsoft-edge",binaries:["/usr/bin/microsoft-edge","/opt/microsoft/msedge/msedge"],externalDir:"/usr/share/microsoft-edge/extensions",userExternalDir:false,desktopIds:["microsoft-edge.desktop","microsoft-edge-stable.desktop","com.microsoft.Edge.desktop"],processNames:["msedge"]},{id:"brave",label:"Brave",store:"chrome",userData:".config/BraveSoftware/Brave-Browser",binaries:["/usr/bin/brave-browser","/opt/brave.com/brave/brave"],externalDir:"/usr/share/brave/extensions",userExternalDir:false,desktopIds:["brave-browser.desktop","com.brave.Browser.desktop"],processNames:["brave"]},{id:"vivaldi",label:"Vivaldi",store:"chrome",userData:".config/vivaldi",binaries:["/usr/bin/vivaldi","/opt/vivaldi/vivaldi"],externalDir:null,userExternalDir:false,externalExtensions:false,desktopIds:["vivaldi-stable.desktop","vivaldi.desktop"],processNames:["vivaldi-bin"]},{id:"opera",label:"Opera",store:"chrome",userData:".config/opera",binaries:["/usr/bin/opera"],externalDir:null,userExternalDir:false,externalExtensions:false,desktopIds:["opera.desktop"],processNames:["opera"]}];var WINDOWS=[{id:"chrome",label:"Google Chrome",store:"chrome",userData:"Google/Chrome/User Data",registryKey:"HKCU\\Software\\Google\\Chrome\\Extensions",binaryRel:"Google/Chrome/Application/chrome.exe",progIds:["chromehtml"],processNames:["chrome.exe"]},{id:"edge",label:"Microsoft Edge",store:"edge",userData:"Microsoft/Edge/User Data",registryKey:"HKCU\\Software\\Microsoft\\Edge\\Extensions",binaryRel:"Microsoft/Edge/Application/msedge.exe",progIds:["msedgehtm"],processNames:["msedge.exe"]},{id:"brave",label:"Brave",store:"chrome",userData:"BraveSoftware/Brave-Browser/User Data",registryKey:"HKCU\\Software\\BraveSoftware\\Brave-Browser\\Extensions",binaryRel:"BraveSoftware/Brave-Browser/Application/brave.exe",progIds:["bravehtml"],processNames:["brave.exe"]},{id:"chromium",label:"Chromium",store:"chrome",userData:"Chromium/User Data",registryKey:"HKCU\\Software\\Chromium\\Extensions",binaryRel:"Chromium/Application/chrome.exe",progIds:["chromiumhtm"],processNames:[]},{id:"vivaldi",label:"Vivaldi",store:"chrome",userData:"Vivaldi/User Data",registryKey:null,externalExtensions:false,binaryRel:"Vivaldi/Application/vivaldi.exe",progIds:["vivaldihtm"],processNames:["vivaldi.exe"]},{id:"whale",label:"Naver Whale",store:"chrome",userData:"Naver/Naver Whale/User Data",registryKey:null,externalExtensions:false,binaryRel:"Naver/Naver Whale/Application/whale.exe",progIds:["whalehtml"],processNames:["whale.exe"]}];var BROWSER_IDS=Object.freeze([...new Set([...MAC,...LINUX,...WINDOWS].map((b)=>b.id))]);function macRow(b,home,exists){const apps=[path14.join("/Applications",`${b.app}.app`),path14.join(home,"Applications",`${b.app}.app`)];const appPath=apps.find((p)=>exists(p))??null;const userDataDir=path14.join(home,b.userData);return{id:b.id,label:b.label,store:b.store,userDataDir,binary:path14.join(appPath??apps[0],"Contents","MacOS",b.app),bundleId:b.bundleId,processNames:apps.map((a)=>path14.join(a,"Contents","MacOS",b.app)),defaultIds:[b.bundleId.toLowerCase()],externalExtensions:b.externalExtensions!==false,installed:appPath!==null||exists(userDataDir),hasProfile:exists(userDataDir)}}function linuxRow(b,home,exists){const userDataDir=path14.join(home,b.userData);const binary=b.binaries.find((p)=>exists(p))??null;return{id:b.id,label:b.label,store:b.store,userDataDir,binary,externalDir:b.externalDir,userExternalDir:b.userExternalDir,processNames:b.processNames,defaultIds:b.desktopIds.map((d)=>d.toLowerCase()),externalExtensions:b.externalExtensions!==false,installed:binary!==null||exists(userDataDir),hasProfile:exists(userDataDir)}}function windowsRow(b,env,home,exists){const localAppData=env.LOCALAPPDATA??path14.join(home,"AppData","Local");const programFiles=[localAppData,env.PROGRAMFILES,env["PROGRAMFILES(X86)"],"C:\\Program Files","C:\\Program Files (x86)"].filter(Boolean);const userDataDir=path14.join(localAppData,b.userData);const binary=programFiles.map((root)=>path14.join(root,b.binaryRel)).find((p)=>exists(p))??null;return{id:b.id,label:b.label,store:b.store,userDataDir,registryKey:b.registryKey,binary,processNames:b.processNames,defaultIds:b.progIds,externalExtensions:b.externalExtensions!==false,installed:binary!==null||exists(userDataDir),hasProfile:exists(userDataDir)}}function catalogBrowsers({platform=process.platform,home=homedir3(),env=process.env,exists=existsSync4}={}){if(platform==="darwin")return MAC.map((b)=>macRow(b,home,exists));if(platform==="linux")return LINUX.map((b)=>linuxRow(b,home,exists));if(platform==="win32")return WINDOWS.map((b)=>windowsRow(b,env,home,exists));return[]}function detectBrowsers(options={}){return catalogBrowsers(options).filter((b)=>b.installed)}var ENABLE_STEP=(browser)=>`Quit ${browser} completely and open it again; a dialog offers to enable "BrowserSkill" — click Enable.`;var BADGE_STEP=(browser)=>`In ${browser}, open the menu (the badge on the toolbar) and enable "BrowserSkill" when prompted; no restart is needed.`;var storeStep=(browser,label)=>`Open ${STORE_PAGE_URLS[browser.store]} in ${label} and click the "Add" button.`;function externalExtensionEntry({platform=process.platform,browser}){const id=BROWSERSKILL_EXTENSION_IDS[browser.store];const updateUrl=STORE_UPDATE_URLS[browser.store];const label=browser.label??browser.id.charAt(0).toUpperCase()+browser.id.slice(1);if(browser.externalExtensions===false){return{kind:"store",needsRestart:false,storeUrl:STORE_PAGE_URLS[browser.store],humanStep:storeStep(browser,label)}}if(platform==="win32"){const key=`${browser.registryKey}\\${id}`;return{kind:"registry",key,needsRestart:false,storeUrl:STORE_PAGE_URLS[browser.store],regAddArgs:["add",key,"/v","update_url","/t","REG_SZ","/d",updateUrl,"/f"],regDeleteArgs:["delete",key,"/f"],humanStep:BADGE_STEP(label)}}const content=JSON.stringify({external_update_url:updateUrl},null,2)+`
1526
- `;if(platform==="linux"&&!browser.userExternalDir){return{kind:"file",path:path15.join(browser.externalDir,`${id}.json`),content,needsRestart:true,storeUrl:STORE_PAGE_URLS[browser.store],humanStep:`Quit ${label} completely and open it again; the extension installs without a prompt.`}}return{kind:"file",path:path15.join(browser.userDataDir,"External Extensions",`${id}.json`),content,needsRestart:true,storeUrl:STORE_PAGE_URLS[browser.store],humanStep:platform==="linux"?`Quit ${label} completely and open it again; the extension installs without a prompt.`:ENABLE_STEP(label)}}function isBlocklisted(browser,id,{readFile=readFileSync4}={}){if(!browser.userDataDir)return false;for(const profile of["Default","Profile 1","Profile 2","Profile 3"]){const file=path15.join(browser.userDataDir,profile,"Preferences");let prefs;try{prefs=JSON.parse(readFile(file,"utf8"))}catch{continue}if(Array.isArray(prefs?.extensions?.external_uninstalls)&&prefs.extensions.external_uninstalls.includes(id))return true}return false}function runRegDefault(args){return new Promise((resolve)=>{const child=spawn5("reg",args,{stdio:["ignore","ignore","pipe"],windowsHide:true});let stderr="";child.stderr.on("data",(c)=>{stderr+=c});child.on("error",(error)=>resolve({code:-1,stderr:error.message}));child.on("exit",(code)=>resolve({code,stderr}))})}async function registerExternalExtension({platform=process.platform,browser,writeFile=writeFileSync2,readFile=readFileSync4,runReg=runRegDefault}={}){const entry=externalExtensionEntry({platform,browser});const id=BROWSERSKILL_EXTENSION_IDS[browser.store];const label=browser.label??browser.id;const storeStep=`Open ${entry.storeUrl} in ${label} and click the "Add" button.`;if(entry.kind==="store")return{...entry,registered:false,alreadyPresent:false,reason:"store_only"};if(isBlocklisted(browser,id,{readFile})){return{...entry,registered:false,alreadyPresent:false,reason:"blocklisted",humanStep:`The extension was removed from ${label} before, so external registration is ignored. ${storeStep}`}}if(entry.kind==="registry"){const result=await runReg(entry.regAddArgs);if(result.code!==0)return{...entry,registered:false,alreadyPresent:false,reason:"reg_failed",detail:result.stderr,humanStep:storeStep};return{...entry,registered:true,alreadyPresent:false}}let alreadyPresent=false;try{alreadyPresent=readFile(entry.path,"utf8")===entry.content}catch{}if(alreadyPresent)return{...entry,registered:true,alreadyPresent:true};try{mkdirSync2(path15.dirname(entry.path),{recursive:true});writeFile(entry.path,entry.content)}catch(error){if(error.code==="EACCES"||error.code==="EPERM"||error.code==="EROFS"){return{...entry,registered:false,alreadyPresent:false,reason:"not_writable",humanStep:`${entry.path} is not writable without administrator rights. ${storeStep}`}}throw error}return{...entry,registered:true,alreadyPresent:false}}async function unregisterExternalExtension({platform=process.platform,browser,runReg=runRegDefault}={}){const entry=externalExtensionEntry({platform,browser});if(entry.kind==="store")return{...entry,removed:false};if(entry.kind==="registry"){const result=await runReg(entry.regDeleteArgs);return{...entry,removed:result.code===0,detail:result.stderr}}const present=existsSync5(entry.path);if(present)rmSync2(entry.path,{force:true});return{...entry,removed:present}}import{execFile as execFile2}from"node:child_process";import{statSync}from"node:fs";import{homedir as homedir4}from"node:os";import path16 from"node:path";var PROBE_TIMEOUT_MS2=3000;function runDefault(command,args){return new Promise((resolve)=>{execFile2(command,args,{timeout:PROBE_TIMEOUT_MS2,windowsHide:true,maxBuffer:8*1024*1024},(error,stdout)=>{resolve(error?null:String(stdout))})})}function parseMacDefaultBrowser(plistJson){let handlers;try{handlers=JSON.parse(plistJson)?.LSHandlers}catch{return null}if(!Array.isArray(handlers))return null;const pick=(scheme)=>handlers.find((h)=>h?.LSHandlerURLScheme===scheme&&typeof h.LSHandlerRoleAll==="string");const handler=pick("https")??pick("http");return handler?handler.LSHandlerRoleAll.toLowerCase():"com.apple.safari"}function parseWindowsProgId(regOutput){const match=/ProgId\s+REG_SZ\s+(\S+)/i.exec(regOutput??"");return match?match[1].toLowerCase():null}function parseProcessNames(platform,output){const names=new Set;for(const raw of String(output??"").split(/\r?\n/)){const line=raw.trim();if(!line)continue;if(platform==="win32"){const image=/^"([^"]+)"/.exec(line)?.[1];if(image)names.add(image.toLowerCase())}else if(platform==="darwin"){if(/\.app\/Contents\/MacOS\/[^/]+$/.test(line))names.add(line)}else{names.add(path16.basename(line))}}return[...names]}async function probeDefault({platform,home,run}){if(platform==="darwin"){const plist=path16.join(home,"Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist");const json=await run("plutil",["-convert","json","-o","-",plist]);return json===null?null:parseMacDefaultBrowser(json)}if(platform==="linux"){const out=await run("xdg-settings",["get","default-web-browser"]);return out?.trim()?out.trim().toLowerCase():null}if(platform==="win32"){const key="HKCU\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\https\\UserChoice";return parseWindowsProgId(await run("reg",["query",key,"/v","ProgId"]))}return null}async function probeRunning({platform,run}){const out=platform==="win32"?await run("tasklist",["/fo","csv","/nh"]):await run("ps",platform==="darwin"?["-axo","comm="]:["-eo","comm="]);return out===null?null:parseProcessNames(platform,out)}function lastUsedAt(browser,stat){let latest=null;for(const rel of["Local State",path16.join("Default","History"),path16.join("Default","Preferences")]){try{const mtime=stat(path16.join(browser.userDataDir,rel)).mtimeMs;if(latest===null||mtime>latest)latest=mtime}catch{}}return latest}async function probeBrowserSignals({platform=process.platform,home=homedir4(),browsers=[],run=runDefault,stat=statSync}={}){const[defaultBrowser,running]=await Promise.all([probeDefault({platform,home,run}),probeRunning({platform,run})]);const lastUsed=Object.fromEntries(browsers.map((b)=>[b.id,lastUsedAt(b,stat)]));return{defaultBrowser,running,lastUsed}}var RECENT_USE_MS=7*24*60*60*1000;function matchesDefault(browser,defaultId){if(!defaultId)return false;return browser.defaultIds.some((id)=>defaultId===id||defaultId.startsWith(id))}function describeDefault(defaultId,catalog){if(defaultId===null||defaultId===undefined)return{raw:null,id:null,label:null,supported:null};const hit=catalog.find((b)=>matchesDefault(b,defaultId));if(hit)return{raw:defaultId,id:hit.id,label:hit.label,supported:true};const other=NON_CHROMIUM_DEFAULTS.find((b)=>b.match.some((m)=>defaultId===m||defaultId.startsWith(m)));return other?{raw:defaultId,id:other.id,label:other.label,supported:false}:{raw:defaultId,id:null,label:defaultId,supported:false}}function candidateRow(browser,{defaultBrowser,running,lastUsed},now){const isDefault=defaultBrowser.id===browser.id;const isRunning=Array.isArray(running)?browser.processNames.some((n)=>running.includes(n)||running.includes(n.toLowerCase())):null;const last=lastUsed?.[browser.id]??null;const recentlyUsed=last!==null&&now-last<=RECENT_USE_MS;const signals=[isDefault&&"default browser",isRunning&&"running now",recentlyUsed&&"used in the last 7 days"].filter(Boolean);return{id:browser.id,label:browser.label,installed:browser.installed,isDefault,running:isRunning,lastUsedAt:last===null?null:new Date(last).toISOString(),recentlyUsed,signals,browser}}function choose(candidates,primary,confidence,reason,defaultBrowser){return{primary:primary.browser,primaryId:primary.id,confidence,needsChoice:false,reason,defaultBrowser,candidates:candidates.map(({browser,...row})=>row)}}function ask(candidates,reason,defaultBrowser,suggested=null){return{primary:null,primaryId:null,confidence:null,needsChoice:true,reason,suggested,defaultBrowser,candidates:candidates.map(({browser,...row})=>row)}}function identifyBrowser({catalog,signals={},explicit=null,now=Date.now()}){const defaultBrowser=describeDefault(signals.defaultBrowser??null,catalog);const installed=catalog.filter((b)=>b.installed||b.id===explicit);const candidates=installed.map((b)=>candidateRow(b,{defaultBrowser,running:signals.running??null,lastUsed:signals.lastUsed},now));if(explicit){const hit=candidates.find((c)=>c.id===explicit);if(hit)return choose(candidates,hit,"explicit",`${hit.label} was chosen explicitly`,defaultBrowser);return ask(candidates,`"${explicit}" is not a Chromium-family browser this library knows; pick one of the candidates`,defaultBrowser)}if(candidates.length===0)return ask(candidates,"no Chromium-family browser is installed",defaultBrowser);const inUse=candidates.filter((c)=>c.running||c.recentlyUsed);const def=candidates.find((c)=>c.isDefault);if(def&&(def.running||def.recentlyUsed))return choose(candidates,def,"default-and-in-use",`${def.label} is the default browser and ${def.signals.slice(1).join(", ")}`,defaultBrowser);if(def&&inUse.length===0)return choose(candidates,def,"default",`${def.label} is the default browser`,defaultBrowser);if(def)return ask(candidates,`${def.label} is the default browser, but ${inUse.map((c)=>c.label).join(" and ")} ${inUse.length===1?"is":"are"} the one in use`,defaultBrowser);if(defaultBrowser.supported===false){const suggested=inUse.length===1?inUse[0].id:null;return ask(candidates,`the default browser is ${defaultBrowser.label}, which BrowserSkill cannot drive; the user has to name the Chromium browser to use`,defaultBrowser,suggested)}const runningNow=inUse.filter((c)=>c.running);if(inUse.length===1)return choose(candidates,inUse[0],"only-in-use",`${inUse[0].label} is the only browser ${inUse[0].signals.join(", ")}`,defaultBrowser);if(runningNow.length===1)return choose(candidates,runningNow[0],"only-running",`${runningNow[0].label} is the only browser running now`,defaultBrowser);if(inUse.length===0&&candidates.length===1)return choose(candidates,candidates[0],"only-installed",`${candidates[0].label} is the only Chromium-family browser installed`,defaultBrowser);return ask(candidates,inUse.length>1?`several browsers are in use (${inUse.map((c)=>c.label).join(", ")})`:"no browser shows recent use",defaultBrowser)}import{existsSync as existsSync6,readFileSync as readFileSync5,rmSync as rmSync3,writeFileSync as writeFileSync3}from"node:fs";import{homedir as homedir5}from"node:os";import path17 from"node:path";import{spawn as spawn6}from"node:child_process";var INSTALL_SH="https://raw.githubusercontent.com/Tencent/BrowserSkill/main/install.sh";var INSTALL_PS1="https://raw.githubusercontent.com/Tencent/BrowserSkill/main/install.ps1";var RC_FILES=[".zshrc",".bashrc",".profile"];function cliInstallCommand({platform=process.platform,installDir,version}={}){const env={BSK_INSTALL_DIR:installDir};if(version)env.BSK_VERSION=version;if(platform==="win32"){return{command:"powershell",args:["-NoProfile","-ExecutionPolicy","Bypass","-Command",`irm ${INSTALL_PS1} | iex`],env}}return{command:"sh",args:["-c",`curl -fsSL ${INSTALL_SH} | sh`],env}}function runDefault2(command,args,env){return new Promise((resolve)=>{const child=spawn6(command,args,{env:{...process.env,...env},stdio:["ignore","pipe","pipe"],windowsHide:true});let stdout="",stderr="";child.stdout.on("data",(c)=>{stdout=(stdout+c).slice(-8192)});child.stderr.on("data",(c)=>{stderr=(stderr+c).slice(-8192)});child.on("error",(error)=>resolve({code:-1,stdout,stderr:error.message}));child.on("exit",(code)=>resolve({code,stdout,stderr}))})}function snapshotRcFiles(home){const snapshot=new Map;for(const name of RC_FILES){const file=path17.join(home,name);snapshot.set(file,existsSync6(file)?readFileSync5(file):null)}return snapshot}function restoreRcFiles(snapshot){const restored=[];for(const[file,before]of snapshot){const after=existsSync6(file)?readFileSync5(file):null;if(before===null&&after===null)continue;if(before!==null&&after!==null&&before.equals(after))continue;if(before===null)rmSync3(file,{force:true});else writeFileSync3(file,before);restored.push(file)}return restored}async function installBskCli({platform=process.platform,home=homedir5(),installDir=path17.join(home,".local","bin"),version,run=runDefault2}={}){const spec=cliInstallCommand({platform,installDir,version});const rcBefore=platform==="win32"?new Map:snapshotRcFiles(home);const result=await run(spec.command,spec.args,spec.env);const restoredRcFiles=restoreRcFiles(rcBefore);const bskBin=path17.join(installDir,platform==="win32"?"bsk.exe":"bsk");const installed=result.code===0&&existsSync6(bskBin);return{installed,bskBin:installed?bskBin:null,code:result.code,stdout:result.stdout,stderr:result.stderr,restoredRcFiles}}function cliVersionDefault(bskBin){return new Promise((resolve)=>{const child=spawn6(bskBin,["--version"],{stdio:["ignore","pipe","ignore"],windowsHide:true});let out="";child.stdout.on("data",(c)=>{out+=c});child.on("error",()=>resolve(null));child.on("exit",(code)=>resolve(code===0?out.trim():null))})}import{homedir as homedir6}from"node:os";import{existsSync as existsSync7,readFileSync as readFileSync6}from"node:fs";var DEFAULT_WAIT_TOTAL_MS=180000;function resolveBskBin(bskBin,env){if(bskBin===null)return null;return bskBin??findBskBinary(env)}async function cliReport({bskBin,env,cliVersion}){const bin=resolveBskBin(bskBin,env);if(!bin)return{installed:false,bskBin:null,version:null};const raw=await cliVersion(bin);return raw?{installed:true,bskBin:bin,version:raw.replace(/^bsk\s+/,"")}:{installed:false,bskBin:bin,version:null}}async function daemonReport(client,waitForBrowserMs){try{const status=await client.call("system.status",waitForBrowserMs>0?{wait_for_browser_ms:waitForBrowserMs}:{},{idPrefix:"status"});return{running:true,version:status.daemon_version,protocolVersion:status.protocol_version,browsers:status.browsers??[],sessions:status.sessions??[]}}catch(error){return{running:false,error:error.message,code:error.code,browsers:[],sessions:[]}}}var NO_BROWSER_STEP=`No Chromium-family browser was found. Install one (Chrome, Edge, Brave, Arc, ...), then add the extension from ${STORE_PAGE_URLS.chrome}.`;function withRegistration(platform,browser){const entry=externalExtensionEntry({platform,browser});const extensionRegistered=entry.kind==="file"&&existsSync7(entry.path)&&readFileSync6(entry.path,"utf8")===entry.content;return{...browser,registration:entry,extensionRegistered}}async function identify({platform,home,env,exists,browser,signals,probeSignals,now}){const catalog=catalogBrowsers({platform,home,env,exists}).map((b)=>withRegistration(platform,b));const observed=signals??await probeSignals({platform,home,browsers:catalog.filter((b)=>b.hasProfile)});const explicit=browser??env.OMOWRIGHT_BROWSER??null;const{primary,...identification}=identifyBrowser({catalog,signals:observed,explicit:explicit||null,now});const browsers=catalog.filter((b)=>b.installed);const registeredElsewhere=browsers.filter((b)=>b.extensionRegistered&&b.id!==identification.primaryId).map((b)=>b.id);return{browsers,identification:{...identification,registeredElsewhere},primary}}function choiceStep(identification){const listed=identification.candidates.map((c)=>`${c.label} [${c.id}]${c.signals.length?` — ${c.signals.join(", ")}`:""}`).join("; ");const hint=identification.suggested?` Likely ${identification.suggested}, but confirm first.`:"";return`Ask the user which browser they actually use (${identification.reason}). Candidates: ${listed}.${hint} Then re-run with browser: "<id>"; register nothing until they answer.`}function nextStepFor({cli,daemon,browsers,browsersConnected,identification,primary}){if(browsersConnected.length>0)return null;if(!cli.installed)return"Install the bsk CLI (bskOnboard() runs the official installer).";if(!daemon.running)return"Start the BrowserSkill daemon: run `bsk status` once, or `bsk daemon start` in the environment that owns it.";if(browsers.length===0&&!primary)return NO_BROWSER_STEP;if(identification.needsChoice)return choiceStep(identification);if(!primary.extensionRegistered&&primary.registration.kind!=="store")return`Register the BrowserSkill extension for ${primary.label} (bskOnboard() does this), then let the user enable it.`;return primary.registration.humanStep}async function bskDoctor({platform=process.platform,home=homedir6(),env=process.env,client,bskBin,cliVersion=cliVersionDefault,waitForBrowserMs=0,exists=existsSync7,browser,signals,probeSignals=probeBrowserSignals,now=Date.now()}={}){const cli=await cliReport({bskBin,env,cliVersion});const ipc=client??new BskIpcClient({env,bskBin:cli.bskBin??undefined,autoStart:false});const daemon=await daemonReport(ipc,waitForBrowserMs);const{browsers,identification,primary}=await identify({platform,home,env,exists,browser,signals,probeSignals,now});const browsersConnected=daemon.browsers;const report={cli,daemon,browsers,primary:primary??null,identification,browsersConnected,ready:browsersConnected.length>0};report.nextStep=nextStepFor(report);return report}async function bskOnboard({platform=process.platform,home=homedir6(),env=process.env,client,bskBin,installDir,installCli=installBskCli,cliVersion=cliVersionDefault,onHumanStep=()=>{},waitForBrowserMs=15000,waitTotalMs=DEFAULT_WAIT_TOTAL_MS,exists=existsSync7,browser,signals,probeSignals=probeBrowserSignals,now=Date.now()}={}){let cli=await cliReport({bskBin,env,cliVersion});let install=null;if(!cli.installed){install=await installCli({platform,home,installDir});cli=install.installed?await cliReport({bskBin:install.bskBin,env,cliVersion}):cli}const ipc=client??new BskIpcClient({env,bskBin:cli.bskBin??undefined,autoStart:true});let daemon=await daemonReport(ipc,0);const{browsers,identification,primary}=await identify({platform,home,env,exists,browser,signals,probeSignals,now});const registrations=[];let humanStep=null;let needsChoice=false;if(daemon.browsers.length===0){if(browsers.length===0&&!primary){humanStep=NO_BROWSER_STEP}else if(identification.needsChoice){needsChoice=true;humanStep=choiceStep(identification)}else{const registration=await registerExternalExtension({platform,browser:primary});registrations.push({browser:primary.id,...registration});humanStep=registration.humanStep;onHumanStep({browser:primary.id,step:registration.humanStep,registered:registration.registered,needsRestart:registration.needsRestart})}}const deadline=Date.now()+waitTotalMs;let browsersConnected=daemon.browsers;while(!needsChoice&&browsersConnected.length===0&&daemon.running&&Date.now()<deadline){browsersConnected=await listBrowsers(ipc,{waitForBrowserMs});if(waitForBrowserMs===0)break}daemon={...daemon,browsers:browsersConnected};const ready=browsersConnected.length>0;return{cli,install,daemon,browsers,primary:primary??null,identification,needsChoice,registrations,humanStep:ready?null:humanStep,browsersConnected,ready}}export{AgentTabManager,BROWSERSKILL_EXTENSION_IDS,BROWSER_EVENT_NAMES,BridgeProtocolError,BridgeUnavailableError,BrowserCdpCommandError,BrowserConnection,BskIpcClient,BskRpcError,BskSession,CdpClient,CommandSchema,DEFAULT_AGENT_VIEWPORT,DEVICE_PRESETS,ElementHandle,EventSchema,ExtensionHelloSchema,FrameLocator,FrameManager,HostHelloSchema,Locator,ModifierState,OMOWRIGHT_PAGE_CDP_METHODS,OmOKeyboard,OmOMouse,OmOPage,PipeCdpClient,ResponseSchema,STORE_PAGE_URLS,SessionManager,SharedConnectionError,TabRepository,UnsupportedOperationError,bskDoctor,bskOnboard,bskSnapshot,buildCloakBrowserArgs,buildSnapshotExpression,catalogBrowsers,collectWhileScrolling,compactSnapshot,connect,connectBrowserSkill,connectCloakProfile,connectNamed,connectPipe,createAgentTabs,createBridgeClient,createCaptcha,createChromeApi2 as createChromeApi,createCua,createEvents,createNativeMessagingHost,createNetworkSnoop,createRoutes,createTrace,decodeFrames,describeLayers,detectBrowsers,emulate,encodeFrame,externalExtensionEntry,findCloakBrowserPath,identifyBrowser,injectCookies,installBskCli,installNativeMessagingHost,layersHeader,listBrowsers,listInstances,macOSVisionOcr,normalizeDialogPolicy,pageBundle,probeBrowserSignals,readDaemonInfo,reconcileFrames,registerExternalExtension,repinViewport,requestHuman,resolveBskHome,resolveCloakProfile,resolveDialogAction,sanitizeCookies,snapshotTokens,snapshotWithFrames,snapshotWithLayers,takeSnapshot,toHar,toolSchemas,unregisterExternalExtension};
1526
+ })()`}async function bskSnapshot(session,{tabId,timeoutMs,...snapshotOptions}={}){const reply=await session.evaluate(buildSnapshotExpression(snapshotOptions),{tabId,timeoutMs,awaitPromise:false,returnByValue:true});if(!reply||reply.ok===false){const text=reply?.error?.text??"unknown error";throw new Error(`bskSnapshot failed inside the page: ${text}`)}const value=reply.value;if(!value||typeof value.tree!=="string")throw new Error("bskSnapshot: page returned no tree");if(value.error)throw new Error(`bskSnapshot: ${value.error}`);return{tree:value.tree,refs:value.refs,css:value.css}}import{existsSync as existsSync5,mkdirSync as mkdirSync3,readFileSync as readFileSync5,rmSync as rmSync3,writeFileSync as writeFileSync3}from"node:fs";import{spawn as spawn5}from"node:child_process";import path16 from"node:path";import{existsSync as existsSync4}from"node:fs";import{homedir as homedir3}from"node:os";import path15 from"node:path";var BROWSERSKILL_EXTENSION_IDS=Object.freeze({chrome:"hhcmgoofomhgciiibhipgmgkgnoenaoi",edge:"emacgiaaaiojkkpkddmmdfhmokgmnikg"});var STORE_UPDATE_URLS=Object.freeze({chrome:"https://clients2.google.com/service/update2/crx",edge:"https://edge.microsoft.com/extensionwebstorebase/v1/crx"});var STORE_PAGE_URLS=Object.freeze({chrome:`https://chromewebstore.google.com/detail/${BROWSERSKILL_EXTENSION_IDS.chrome}`,edge:`https://microsoftedge.microsoft.com/addons/detail/browserskill/${BROWSERSKILL_EXTENSION_IDS.edge}`});var NON_CHROMIUM_DEFAULTS=Object.freeze([{id:"safari",label:"Safari",match:["com.apple.safari","safarihtml"]},{id:"firefox",label:"Firefox",match:["org.mozilla.firefox","firefox.desktop","firefoxurl","firefoxhtml"]},{id:"zen",label:"Zen",match:["app.zen-browser.zen","zen.desktop","zenhtml"]},{id:"orion",label:"Orion",match:["com.kagi.kagimacos"]}]);var MAC=[{id:"chrome",label:"Google Chrome",store:"chrome",userData:"Library/Application Support/Google/Chrome",app:"Google Chrome",bundleId:"com.google.Chrome"},{id:"edge",label:"Microsoft Edge",store:"edge",userData:"Library/Application Support/Microsoft Edge",app:"Microsoft Edge",bundleId:"com.microsoft.edgemac"},{id:"brave",label:"Brave",store:"chrome",userData:"Library/Application Support/BraveSoftware/Brave-Browser",app:"Brave Browser",bundleId:"com.brave.Browser"},{id:"chromium",label:"Chromium",store:"chrome",userData:"Library/Application Support/Chromium",app:"Chromium",bundleId:"org.chromium.Chromium"},{id:"arc",label:"Arc",store:"chrome",userData:"Library/Application Support/Arc/User Data",app:"Arc",bundleId:"company.thebrowser.Browser"},{id:"dia",label:"Dia",store:"chrome",userData:"Library/Application Support/Dia/User Data",app:"Dia",bundleId:"company.thebrowser.dia"},{id:"vivaldi",label:"Vivaldi",store:"chrome",userData:"Library/Application Support/Vivaldi",app:"Vivaldi",bundleId:"com.vivaldi.Vivaldi"},{id:"opera",label:"Opera",store:"chrome",userData:"Library/Application Support/com.operasoftware.Opera",app:"Opera",bundleId:"com.operasoftware.Opera",externalExtensions:false},{id:"comet",label:"Comet",store:"chrome",userData:"Library/Application Support/Comet",app:"Comet",bundleId:"ai.perplexity.comet"},{id:"aside",label:"Aside",store:"chrome",userData:"Library/Application Support/Aside",app:"Aside",bundleId:"at.studio.AsideBrowser"},{id:"whale",label:"Naver Whale",store:"chrome",userData:"Library/Application Support/Naver/Whale",app:"Naver Whale",bundleId:"com.naver.Whale"}];var LINUX=[{id:"chrome",label:"Google Chrome",store:"chrome",userData:".config/google-chrome",binaries:["/usr/bin/google-chrome","/usr/bin/google-chrome-stable","/opt/google/chrome/chrome"],externalDir:"/opt/google/chrome/extensions",userExternalDir:false,desktopIds:["google-chrome.desktop","google-chrome-stable.desktop","com.google.Chrome.desktop"],processNames:["chrome","google-chrome"]},{id:"chromium",label:"Chromium",store:"chrome",userData:".config/chromium",binaries:["/usr/bin/chromium","/usr/bin/chromium-browser","/snap/bin/chromium"],externalDir:"/usr/share/chromium/extensions",userExternalDir:true,desktopIds:["chromium.desktop","chromium-browser.desktop","chromium_chromium.desktop","org.chromium.Chromium.desktop"],processNames:["chromium","chromium-browse"]},{id:"edge",label:"Microsoft Edge",store:"edge",userData:".config/microsoft-edge",binaries:["/usr/bin/microsoft-edge","/opt/microsoft/msedge/msedge"],externalDir:"/usr/share/microsoft-edge/extensions",userExternalDir:false,desktopIds:["microsoft-edge.desktop","microsoft-edge-stable.desktop","com.microsoft.Edge.desktop"],processNames:["msedge"]},{id:"brave",label:"Brave",store:"chrome",userData:".config/BraveSoftware/Brave-Browser",binaries:["/usr/bin/brave-browser","/opt/brave.com/brave/brave"],externalDir:"/usr/share/brave/extensions",userExternalDir:false,desktopIds:["brave-browser.desktop","com.brave.Browser.desktop"],processNames:["brave"]},{id:"vivaldi",label:"Vivaldi",store:"chrome",userData:".config/vivaldi",binaries:["/usr/bin/vivaldi","/opt/vivaldi/vivaldi"],externalDir:null,userExternalDir:false,externalExtensions:false,desktopIds:["vivaldi-stable.desktop","vivaldi.desktop"],processNames:["vivaldi-bin"]},{id:"opera",label:"Opera",store:"chrome",userData:".config/opera",binaries:["/usr/bin/opera"],externalDir:null,userExternalDir:false,externalExtensions:false,desktopIds:["opera.desktop"],processNames:["opera"]}];var WINDOWS=[{id:"chrome",label:"Google Chrome",store:"chrome",userData:"Google/Chrome/User Data",registryKey:"HKCU\\Software\\Google\\Chrome\\Extensions",binaryRel:"Google/Chrome/Application/chrome.exe",progIds:["chromehtml"],processNames:["chrome.exe"]},{id:"edge",label:"Microsoft Edge",store:"edge",userData:"Microsoft/Edge/User Data",registryKey:"HKCU\\Software\\Microsoft\\Edge\\Extensions",binaryRel:"Microsoft/Edge/Application/msedge.exe",progIds:["msedgehtm"],processNames:["msedge.exe"]},{id:"brave",label:"Brave",store:"chrome",userData:"BraveSoftware/Brave-Browser/User Data",registryKey:"HKCU\\Software\\BraveSoftware\\Brave-Browser\\Extensions",binaryRel:"BraveSoftware/Brave-Browser/Application/brave.exe",progIds:["bravehtml"],processNames:["brave.exe"]},{id:"chromium",label:"Chromium",store:"chrome",userData:"Chromium/User Data",registryKey:"HKCU\\Software\\Chromium\\Extensions",binaryRel:"Chromium/Application/chrome.exe",progIds:["chromiumhtm"],processNames:[]},{id:"vivaldi",label:"Vivaldi",store:"chrome",userData:"Vivaldi/User Data",registryKey:null,externalExtensions:false,binaryRel:"Vivaldi/Application/vivaldi.exe",progIds:["vivaldihtm"],processNames:["vivaldi.exe"]},{id:"whale",label:"Naver Whale",store:"chrome",userData:"Naver/Naver Whale/User Data",registryKey:null,externalExtensions:false,binaryRel:"Naver/Naver Whale/Application/whale.exe",progIds:["whalehtml"],processNames:["whale.exe"]}];var BROWSER_IDS=Object.freeze([...new Set([...MAC,...LINUX,...WINDOWS].map((b)=>b.id))]);function macRow(b,home,exists){const apps=[path15.join("/Applications",`${b.app}.app`),path15.join(home,"Applications",`${b.app}.app`)];const appPath=apps.find((p)=>exists(p))??null;const userDataDir=path15.join(home,b.userData);return{id:b.id,label:b.label,store:b.store,userDataDir,binary:path15.join(appPath??apps[0],"Contents","MacOS",b.app),bundleId:b.bundleId,processNames:apps.map((a)=>path15.join(a,"Contents","MacOS",b.app)),defaultIds:[b.bundleId.toLowerCase()],externalExtensions:b.externalExtensions!==false,installed:appPath!==null||exists(userDataDir),hasProfile:exists(userDataDir)}}function linuxRow(b,home,exists){const userDataDir=path15.join(home,b.userData);const binary=b.binaries.find((p)=>exists(p))??null;return{id:b.id,label:b.label,store:b.store,userDataDir,binary,externalDir:b.externalDir,userExternalDir:b.userExternalDir,processNames:b.processNames,defaultIds:b.desktopIds.map((d)=>d.toLowerCase()),externalExtensions:b.externalExtensions!==false,installed:binary!==null||exists(userDataDir),hasProfile:exists(userDataDir)}}function windowsRow(b,env,home,exists){const localAppData=env.LOCALAPPDATA??path15.join(home,"AppData","Local");const programFiles=[localAppData,env.PROGRAMFILES,env["PROGRAMFILES(X86)"],"C:\\Program Files","C:\\Program Files (x86)"].filter(Boolean);const userDataDir=path15.join(localAppData,b.userData);const binary=programFiles.map((root)=>path15.join(root,b.binaryRel)).find((p)=>exists(p))??null;return{id:b.id,label:b.label,store:b.store,userDataDir,registryKey:b.registryKey,binary,processNames:b.processNames,defaultIds:b.progIds,externalExtensions:b.externalExtensions!==false,installed:binary!==null||exists(userDataDir),hasProfile:exists(userDataDir)}}function catalogBrowsers({platform=process.platform,home=homedir3(),env=process.env,exists=existsSync4}={}){if(platform==="darwin")return MAC.map((b)=>macRow(b,home,exists));if(platform==="linux")return LINUX.map((b)=>linuxRow(b,home,exists));if(platform==="win32")return WINDOWS.map((b)=>windowsRow(b,env,home,exists));return[]}function detectBrowsers(options={}){return catalogBrowsers(options).filter((b)=>b.installed)}var ENABLE_STEP=(browser)=>`Quit ${browser} completely and open it again; a dialog offers to enable "BrowserSkill" — click Enable.`;var BADGE_STEP=(browser)=>`In ${browser}, open the menu (the badge on the toolbar) and enable "BrowserSkill" when prompted; no restart is needed.`;var storeStep=(browser,label)=>`Open ${STORE_PAGE_URLS[browser.store]} in ${label} and click the "Add" button.`;function externalExtensionEntry({platform=process.platform,browser}){const id=BROWSERSKILL_EXTENSION_IDS[browser.store];const updateUrl=STORE_UPDATE_URLS[browser.store];const label=browser.label??browser.id.charAt(0).toUpperCase()+browser.id.slice(1);if(browser.externalExtensions===false){return{kind:"store",needsRestart:false,storeUrl:STORE_PAGE_URLS[browser.store],humanStep:storeStep(browser,label)}}if(platform==="win32"){const key=`${browser.registryKey}\\${id}`;return{kind:"registry",key,needsRestart:false,storeUrl:STORE_PAGE_URLS[browser.store],regAddArgs:["add",key,"/v","update_url","/t","REG_SZ","/d",updateUrl,"/f"],regDeleteArgs:["delete",key,"/f"],humanStep:BADGE_STEP(label)}}const content=JSON.stringify({external_update_url:updateUrl},null,2)+`
1527
+ `;if(platform==="linux"&&!browser.userExternalDir){return{kind:"file",path:path16.join(browser.externalDir,`${id}.json`),content,needsRestart:true,storeUrl:STORE_PAGE_URLS[browser.store],humanStep:`Quit ${label} completely and open it again; the extension installs without a prompt.`}}return{kind:"file",path:path16.join(browser.userDataDir,"External Extensions",`${id}.json`),content,needsRestart:true,storeUrl:STORE_PAGE_URLS[browser.store],humanStep:platform==="linux"?`Quit ${label} completely and open it again; the extension installs without a prompt.`:ENABLE_STEP(label)}}function isBlocklisted(browser,id,{readFile=readFileSync5}={}){if(!browser.userDataDir)return false;for(const profile of["Default","Profile 1","Profile 2","Profile 3"]){const file=path16.join(browser.userDataDir,profile,"Preferences");let prefs;try{prefs=JSON.parse(readFile(file,"utf8"))}catch{continue}if(Array.isArray(prefs?.extensions?.external_uninstalls)&&prefs.extensions.external_uninstalls.includes(id))return true}return false}function runRegDefault(args){return new Promise((resolve)=>{const child=spawn5("reg",args,{stdio:["ignore","ignore","pipe"],windowsHide:true});let stderr="";child.stderr.on("data",(c)=>{stderr+=c});child.on("error",(error)=>resolve({code:-1,stderr:error.message}));child.on("exit",(code)=>resolve({code,stderr}))})}async function registerExternalExtension({platform=process.platform,browser,writeFile=writeFileSync3,readFile=readFileSync5,runReg=runRegDefault}={}){const entry=externalExtensionEntry({platform,browser});const id=BROWSERSKILL_EXTENSION_IDS[browser.store];const label=browser.label??browser.id;const storeStep=`Open ${entry.storeUrl} in ${label} and click the "Add" button.`;if(entry.kind==="store")return{...entry,registered:false,alreadyPresent:false,reason:"store_only"};if(isBlocklisted(browser,id,{readFile})){return{...entry,registered:false,alreadyPresent:false,reason:"blocklisted",humanStep:`The extension was removed from ${label} before, so external registration is ignored. ${storeStep}`}}if(entry.kind==="registry"){const result=await runReg(entry.regAddArgs);if(result.code!==0)return{...entry,registered:false,alreadyPresent:false,reason:"reg_failed",detail:result.stderr,humanStep:storeStep};return{...entry,registered:true,alreadyPresent:false}}let alreadyPresent=false;try{alreadyPresent=readFile(entry.path,"utf8")===entry.content}catch{}if(alreadyPresent)return{...entry,registered:true,alreadyPresent:true};try{mkdirSync3(path16.dirname(entry.path),{recursive:true});writeFile(entry.path,entry.content)}catch(error){if(error.code==="EACCES"||error.code==="EPERM"||error.code==="EROFS"){return{...entry,registered:false,alreadyPresent:false,reason:"not_writable",humanStep:`${entry.path} is not writable without administrator rights. ${storeStep}`}}throw error}return{...entry,registered:true,alreadyPresent:false}}async function unregisterExternalExtension({platform=process.platform,browser,runReg=runRegDefault}={}){const entry=externalExtensionEntry({platform,browser});if(entry.kind==="store")return{...entry,removed:false};if(entry.kind==="registry"){const result=await runReg(entry.regDeleteArgs);return{...entry,removed:result.code===0,detail:result.stderr}}const present=existsSync5(entry.path);if(present)rmSync3(entry.path,{force:true});return{...entry,removed:present}}import{execFile as execFile2}from"node:child_process";import{statSync}from"node:fs";import{homedir as homedir4}from"node:os";import path17 from"node:path";var PROBE_TIMEOUT_MS2=3000;function runDefault(command,args){return new Promise((resolve)=>{execFile2(command,args,{timeout:PROBE_TIMEOUT_MS2,windowsHide:true,maxBuffer:8*1024*1024},(error,stdout)=>{resolve(error?null:String(stdout))})})}function parseMacDefaultBrowser(plistJson){let handlers;try{handlers=JSON.parse(plistJson)?.LSHandlers}catch{return null}if(!Array.isArray(handlers))return null;const pick=(scheme)=>handlers.find((h)=>h?.LSHandlerURLScheme===scheme&&typeof h.LSHandlerRoleAll==="string");const handler=pick("https")??pick("http");return handler?handler.LSHandlerRoleAll.toLowerCase():"com.apple.safari"}function parseWindowsProgId(regOutput){const match=/ProgId\s+REG_SZ\s+(\S+)/i.exec(regOutput??"");return match?match[1].toLowerCase():null}function parseProcessNames(platform,output){const names=new Set;for(const raw of String(output??"").split(/\r?\n/)){const line=raw.trim();if(!line)continue;if(platform==="win32"){const image=/^"([^"]+)"/.exec(line)?.[1];if(image)names.add(image.toLowerCase())}else if(platform==="darwin"){if(/\.app\/Contents\/MacOS\/[^/]+$/.test(line))names.add(line)}else{names.add(path17.basename(line))}}return[...names]}async function probeDefault({platform,home,run}){if(platform==="darwin"){const plist=path17.join(home,"Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist");const json=await run("plutil",["-convert","json","-o","-",plist]);return json===null?null:parseMacDefaultBrowser(json)}if(platform==="linux"){const out=await run("xdg-settings",["get","default-web-browser"]);return out?.trim()?out.trim().toLowerCase():null}if(platform==="win32"){const key="HKCU\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\https\\UserChoice";return parseWindowsProgId(await run("reg",["query",key,"/v","ProgId"]))}return null}async function probeRunning({platform,run}){const out=platform==="win32"?await run("tasklist",["/fo","csv","/nh"]):await run("ps",platform==="darwin"?["-axo","comm="]:["-eo","comm="]);return out===null?null:parseProcessNames(platform,out)}function lastUsedAt(browser,stat){let latest=null;for(const rel of["Local State",path17.join("Default","History"),path17.join("Default","Preferences")]){try{const mtime=stat(path17.join(browser.userDataDir,rel)).mtimeMs;if(latest===null||mtime>latest)latest=mtime}catch{}}return latest}async function probeBrowserSignals({platform=process.platform,home=homedir4(),browsers=[],run=runDefault,stat=statSync}={}){const[defaultBrowser,running]=await Promise.all([probeDefault({platform,home,run}),probeRunning({platform,run})]);const lastUsed=Object.fromEntries(browsers.map((b)=>[b.id,lastUsedAt(b,stat)]));return{defaultBrowser,running,lastUsed}}var RECENT_USE_MS=7*24*60*60*1000;function matchesDefault(browser,defaultId){if(!defaultId)return false;return browser.defaultIds.some((id)=>defaultId===id||defaultId.startsWith(id))}function describeDefault(defaultId,catalog){if(defaultId===null||defaultId===undefined)return{raw:null,id:null,label:null,supported:null};const hit=catalog.find((b)=>matchesDefault(b,defaultId));if(hit)return{raw:defaultId,id:hit.id,label:hit.label,supported:true};const other=NON_CHROMIUM_DEFAULTS.find((b)=>b.match.some((m)=>defaultId===m||defaultId.startsWith(m)));return other?{raw:defaultId,id:other.id,label:other.label,supported:false}:{raw:defaultId,id:null,label:defaultId,supported:false}}function candidateRow(browser,{defaultBrowser,running,lastUsed},now){const isDefault=defaultBrowser.id===browser.id;const isRunning=Array.isArray(running)?browser.processNames.some((n)=>running.includes(n)||running.includes(n.toLowerCase())):null;const last=lastUsed?.[browser.id]??null;const recentlyUsed=last!==null&&now-last<=RECENT_USE_MS;const signals=[isDefault&&"default browser",isRunning&&"running now",recentlyUsed&&"used in the last 7 days"].filter(Boolean);return{id:browser.id,label:browser.label,installed:browser.installed,isDefault,running:isRunning,lastUsedAt:last===null?null:new Date(last).toISOString(),recentlyUsed,signals,browser}}function choose(candidates,primary,confidence,reason,defaultBrowser){return{primary:primary.browser,primaryId:primary.id,confidence,needsChoice:false,reason,defaultBrowser,candidates:candidates.map(({browser,...row})=>row)}}function ask(candidates,reason,defaultBrowser,suggested=null){return{primary:null,primaryId:null,confidence:null,needsChoice:true,reason,suggested,defaultBrowser,candidates:candidates.map(({browser,...row})=>row)}}function identifyBrowser({catalog,signals={},explicit=null,now=Date.now()}){const defaultBrowser=describeDefault(signals.defaultBrowser??null,catalog);const installed=catalog.filter((b)=>b.installed||b.id===explicit);const candidates=installed.map((b)=>candidateRow(b,{defaultBrowser,running:signals.running??null,lastUsed:signals.lastUsed},now));if(explicit){const hit=candidates.find((c)=>c.id===explicit);if(hit)return choose(candidates,hit,"explicit",`${hit.label} was chosen explicitly`,defaultBrowser);return ask(candidates,`"${explicit}" is not a Chromium-family browser this library knows; pick one of the candidates`,defaultBrowser)}if(candidates.length===0)return ask(candidates,"no Chromium-family browser is installed",defaultBrowser);const inUse=candidates.filter((c)=>c.running||c.recentlyUsed);const def=candidates.find((c)=>c.isDefault);if(def&&(def.running||def.recentlyUsed))return choose(candidates,def,"default-and-in-use",`${def.label} is the default browser and ${def.signals.slice(1).join(", ")}`,defaultBrowser);if(def&&inUse.length===0)return choose(candidates,def,"default",`${def.label} is the default browser`,defaultBrowser);if(def)return ask(candidates,`${def.label} is the default browser, but ${inUse.map((c)=>c.label).join(" and ")} ${inUse.length===1?"is":"are"} the one in use`,defaultBrowser);if(defaultBrowser.supported===false){const suggested=inUse.length===1?inUse[0].id:null;return ask(candidates,`the default browser is ${defaultBrowser.label}, which BrowserSkill cannot drive; the user has to name the Chromium browser to use`,defaultBrowser,suggested)}const runningNow=inUse.filter((c)=>c.running);if(inUse.length===1)return choose(candidates,inUse[0],"only-in-use",`${inUse[0].label} is the only browser ${inUse[0].signals.join(", ")}`,defaultBrowser);if(runningNow.length===1)return choose(candidates,runningNow[0],"only-running",`${runningNow[0].label} is the only browser running now`,defaultBrowser);if(inUse.length===0&&candidates.length===1)return choose(candidates,candidates[0],"only-installed",`${candidates[0].label} is the only Chromium-family browser installed`,defaultBrowser);return ask(candidates,inUse.length>1?`several browsers are in use (${inUse.map((c)=>c.label).join(", ")})`:"no browser shows recent use",defaultBrowser)}import{existsSync as existsSync6,readFileSync as readFileSync6,rmSync as rmSync4,writeFileSync as writeFileSync4}from"node:fs";import{homedir as homedir5}from"node:os";import path18 from"node:path";import{spawn as spawn6}from"node:child_process";var INSTALL_SH="https://raw.githubusercontent.com/Tencent/BrowserSkill/main/install.sh";var INSTALL_PS1="https://raw.githubusercontent.com/Tencent/BrowserSkill/main/install.ps1";var RC_FILES=[".zshrc",".bashrc",".profile"];function cliInstallCommand({platform=process.platform,installDir,version}={}){const env={BSK_INSTALL_DIR:installDir};if(version)env.BSK_VERSION=version;if(platform==="win32"){return{command:"powershell",args:["-NoProfile","-ExecutionPolicy","Bypass","-Command",`irm ${INSTALL_PS1} | iex`],env}}return{command:"sh",args:["-c",`curl -fsSL ${INSTALL_SH} | sh`],env}}function runDefault2(command,args,env){return new Promise((resolve)=>{const child=spawn6(command,args,{env:{...process.env,...env},stdio:["ignore","pipe","pipe"],windowsHide:true});let stdout="",stderr="";child.stdout.on("data",(c)=>{stdout=(stdout+c).slice(-8192)});child.stderr.on("data",(c)=>{stderr=(stderr+c).slice(-8192)});child.on("error",(error)=>resolve({code:-1,stdout,stderr:error.message}));child.on("exit",(code)=>resolve({code,stdout,stderr}))})}function snapshotRcFiles(home){const snapshot=new Map;for(const name of RC_FILES){const file=path18.join(home,name);snapshot.set(file,existsSync6(file)?readFileSync6(file):null)}return snapshot}function restoreRcFiles(snapshot){const restored=[];for(const[file,before]of snapshot){const after=existsSync6(file)?readFileSync6(file):null;if(before===null&&after===null)continue;if(before!==null&&after!==null&&before.equals(after))continue;if(before===null)rmSync4(file,{force:true});else writeFileSync4(file,before);restored.push(file)}return restored}async function installBskCli({platform=process.platform,home=homedir5(),installDir=path18.join(home,".local","bin"),version,run=runDefault2}={}){const spec=cliInstallCommand({platform,installDir,version});const rcBefore=platform==="win32"?new Map:snapshotRcFiles(home);const result=await run(spec.command,spec.args,spec.env);const restoredRcFiles=restoreRcFiles(rcBefore);const bskBin=path18.join(installDir,platform==="win32"?"bsk.exe":"bsk");const installed=result.code===0&&existsSync6(bskBin);return{installed,bskBin:installed?bskBin:null,code:result.code,stdout:result.stdout,stderr:result.stderr,restoredRcFiles}}function cliVersionDefault(bskBin){return new Promise((resolve)=>{const child=spawn6(bskBin,["--version"],{stdio:["ignore","pipe","ignore"],windowsHide:true});let out="";child.stdout.on("data",(c)=>{out+=c});child.on("error",()=>resolve(null));child.on("exit",(code)=>resolve(code===0?out.trim():null))})}import{homedir as homedir6}from"node:os";import{existsSync as existsSync7,readFileSync as readFileSync7}from"node:fs";var DEFAULT_WAIT_TOTAL_MS=180000;function resolveBskBin(bskBin,env){if(bskBin===null)return null;return bskBin??findBskBinary(env)}async function cliReport({bskBin,env,cliVersion}){const bin=resolveBskBin(bskBin,env);if(!bin)return{installed:false,bskBin:null,version:null};const raw=await cliVersion(bin);return raw?{installed:true,bskBin:bin,version:raw.replace(/^bsk\s+/,"")}:{installed:false,bskBin:bin,version:null}}async function daemonReport(client,waitForBrowserMs){try{const status=await client.call("system.status",waitForBrowserMs>0?{wait_for_browser_ms:waitForBrowserMs}:{},{idPrefix:"status"});return{running:true,version:status.daemon_version,protocolVersion:status.protocol_version,browsers:status.browsers??[],sessions:status.sessions??[]}}catch(error){return{running:false,error:error.message,code:error.code,browsers:[],sessions:[]}}}var NO_BROWSER_STEP=`No Chromium-family browser was found. Install one (Chrome, Edge, Brave, Arc, ...), then add the extension from ${STORE_PAGE_URLS.chrome}.`;function withRegistration(platform,browser){const entry=externalExtensionEntry({platform,browser});const extensionRegistered=entry.kind==="file"&&existsSync7(entry.path)&&readFileSync7(entry.path,"utf8")===entry.content;return{...browser,registration:entry,extensionRegistered}}async function identify({platform,home,env,exists,browser,signals,probeSignals,now}){const catalog=catalogBrowsers({platform,home,env,exists}).map((b)=>withRegistration(platform,b));const observed=signals??await probeSignals({platform,home,browsers:catalog.filter((b)=>b.hasProfile)});const explicit=browser??env.OMOWRIGHT_BROWSER??null;const{primary,...identification}=identifyBrowser({catalog,signals:observed,explicit:explicit||null,now});const browsers=catalog.filter((b)=>b.installed);const registeredElsewhere=browsers.filter((b)=>b.extensionRegistered&&b.id!==identification.primaryId).map((b)=>b.id);return{browsers,identification:{...identification,registeredElsewhere},primary}}function choiceStep(identification){const listed=identification.candidates.map((c)=>`${c.label} [${c.id}]${c.signals.length?` — ${c.signals.join(", ")}`:""}`).join("; ");const hint=identification.suggested?` Likely ${identification.suggested}, but confirm first.`:"";return`Ask the user which browser they actually use (${identification.reason}). Candidates: ${listed}.${hint} Then re-run with browser: "<id>"; register nothing until they answer.`}function nextStepFor({cli,daemon,browsers,browsersConnected,identification,primary}){if(browsersConnected.length>0)return null;if(!cli.installed)return"Install the bsk CLI (bskOnboard() runs the official installer).";if(!daemon.running)return"Start the BrowserSkill daemon: run `bsk status` once, or `bsk daemon start` in the environment that owns it.";if(browsers.length===0&&!primary)return NO_BROWSER_STEP;if(identification.needsChoice)return choiceStep(identification);if(!primary.extensionRegistered&&primary.registration.kind!=="store")return`Register the BrowserSkill extension for ${primary.label} (bskOnboard() does this), then let the user enable it.`;return primary.registration.humanStep}async function bskDoctor({platform=process.platform,home=homedir6(),env=process.env,client,bskBin,cliVersion=cliVersionDefault,waitForBrowserMs=0,exists=existsSync7,browser,signals,probeSignals=probeBrowserSignals,now=Date.now()}={}){const cli=await cliReport({bskBin,env,cliVersion});const ipc=client??new BskIpcClient({env,bskBin:cli.bskBin??undefined,autoStart:false});const daemon=await daemonReport(ipc,waitForBrowserMs);const{browsers,identification,primary}=await identify({platform,home,env,exists,browser,signals,probeSignals,now});const browsersConnected=daemon.browsers;const report={cli,daemon,browsers,primary:primary??null,identification,browsersConnected,ready:browsersConnected.length>0};report.nextStep=nextStepFor(report);return report}async function bskOnboard({platform=process.platform,home=homedir6(),env=process.env,client,bskBin,installDir,installCli=installBskCli,cliVersion=cliVersionDefault,onHumanStep=()=>{},waitForBrowserMs=15000,waitTotalMs=DEFAULT_WAIT_TOTAL_MS,exists=existsSync7,browser,signals,probeSignals=probeBrowserSignals,now=Date.now()}={}){let cli=await cliReport({bskBin,env,cliVersion});let install=null;if(!cli.installed){install=await installCli({platform,home,installDir});cli=install.installed?await cliReport({bskBin:install.bskBin,env,cliVersion}):cli}const ipc=client??new BskIpcClient({env,bskBin:cli.bskBin??undefined,autoStart:true});let daemon=await daemonReport(ipc,0);const{browsers,identification,primary}=await identify({platform,home,env,exists,browser,signals,probeSignals,now});const registrations=[];let humanStep=null;let needsChoice=false;if(daemon.browsers.length===0){if(browsers.length===0&&!primary){humanStep=NO_BROWSER_STEP}else if(identification.needsChoice){needsChoice=true;humanStep=choiceStep(identification)}else{const registration=await registerExternalExtension({platform,browser:primary});registrations.push({browser:primary.id,...registration});humanStep=registration.humanStep;onHumanStep({browser:primary.id,step:registration.humanStep,registered:registration.registered,needsRestart:registration.needsRestart})}}const deadline=Date.now()+waitTotalMs;let browsersConnected=daemon.browsers;while(!needsChoice&&browsersConnected.length===0&&daemon.running&&Date.now()<deadline){browsersConnected=await listBrowsers(ipc,{waitForBrowserMs});if(waitForBrowserMs===0)break}daemon={...daemon,browsers:browsersConnected};const ready=browsersConnected.length>0;return{cli,install,daemon,browsers,primary:primary??null,identification,needsChoice,registrations,humanStep:ready?null:humanStep,browsersConnected,ready}}export{AgentTabManager,BROWSERSKILL_EXTENSION_IDS,BROWSER_EVENT_NAMES,BridgeProtocolError,BridgeUnavailableError,BrowserCdpCommandError,BrowserConnection,BskIpcClient,BskRpcError,BskSession,CdpClient,CommandSchema,DEFAULT_AGENT_VIEWPORT,DEVICE_PRESETS,ElementHandle,EventSchema,ExtensionHelloSchema,FrameLocator,FrameManager,HostHelloSchema,Locator,ModifierState,OMOWRIGHT_PAGE_CDP_METHODS,OmOKeyboard,OmOMouse,OmOPage,PipeCdpClient,ResponseSchema,STORE_PAGE_URLS,SessionManager,SharedConnectionError,TabRepository,UnsupportedOperationError,bskDoctor,bskOnboard,bskSnapshot,buildCloakBrowserArgs,buildSnapshotExpression,catalogBrowsers,collectWhileScrolling,compactSnapshot,connect,connectBrowserSkill,connectCloakProfile,connectNamed,connectPipe,createAgentTabs,createBridgeClient,createCaptcha,createChromeApi2 as createChromeApi,createCua,createEvents,createNativeMessagingHost,createNetworkSnoop,createRoutes,createTrace,decodeFrames,describeLayers,detectBrowsers,emulate,encodeFrame,externalExtensionEntry,findCloakBrowserPath,identifyBrowser,injectCookies,installBskCli,installNativeMessagingHost,layersHeader,listBrowsers,listInstances,macOSVisionOcr,normalizeDialogPolicy,pageBundle,probeBrowserSignals,readDaemonInfo,reconcileFrames,registerExternalExtension,repinViewport,requestHuman,resolveBskHome,resolveCloakProfile,resolveDialogAction,sanitizeCookies,snapshotTokens,snapshotWithFrames,snapshotWithLayers,takeSnapshot,toHar,toolSchemas,unregisterExternalExtension};