@mearl/native-host 2.16.2 → 2.16.3

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 (2) hide show
  1. package/dist/index.js +2 -2
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -592,7 +592,7 @@ ${candidates}`)}let hit=hits[0],dispatchTarget;if(effectiveClickMode==="dom"||po
592
592
  remaining -= 1;
593
593
  }
594
594
  return element;
595
- }`,arguments:[{value:ancestorDepth}],returnByValue:!1});if(ancestorObjectId=ancestor.result?.objectId,!ancestorObjectId||ancestor.result?.subtype==="null")throw new Error("page_snapshot: cannot resolve an ancestor for rootRef");let ancestorBackendNodeId=(await sendCdpDeviceShellPageCommand(session,"DOM.describeNode",{objectId:ancestorObjectId})).node?.backendNodeId;if(ancestorBackendNodeId===void 0)throw new Error("page_snapshot: cannot resolve an ancestor for rootRef");return ancestorBackendNodeId}finally{ancestorObjectId&&await sendCdpDeviceShellPageCommand(session,"Runtime.releaseObject",{objectId:ancestorObjectId}).catch(()=>{}),await sendCdpDeviceShellPageCommand(session,"Runtime.releaseObject",{objectId}).catch(()=>{})}}async function getViewportBackendNodeIds(session){let[metrics,captured]=await Promise.all([sendCdpDeviceShellPageCommand(session,"Page.getLayoutMetrics",{}),sendCdpDeviceShellPageCommand(session,"DOMSnapshot.captureSnapshot",{computedStyles:[],includeDOMRects:!0,includePaintOrder:!1})]),viewport=resolveDOMSnapshotViewportBounds(metrics);if(!viewport)throw new Error("page_snapshot: viewport metrics are unavailable");let{left,top,width,height}=viewport,right=left+width,bottom=top+height,visible=new Set;for(let document2 of captured.documents??[]){let backendNodeIds=document2.nodes?.backendNodeId??[],nodeIndexes=document2.layout?.nodeIndex??[],bounds=document2.layout?.bounds??[];for(let index=0;index<nodeIndexes.length;index+=1){let box=bounds[index],backendNodeId=backendNodeIds[nodeIndexes[index]];if(!box||backendNodeId===void 0)continue;let[x,y,boxWidth,boxHeight]=box;boxWidth<=0||boxHeight<=0||x<right&&x+boxWidth>left&&y<bottom&&y+boxHeight>top&&visible.add(backendNodeId)}}return visible}async function cdpPageSnapshot(session,data){let mode=data?.mode??"full",rootSelector=data?.rootSelector,rootRef=data?.rootRef,ancestorDepth=data?.ancestorDepth??0,query=resolvePageSnapshotQuery(data?.query);if(!["full","interactive","viewport"].includes(mode))throw new Error("page_snapshot: mode must be one of full, interactive, viewport");if(rootSelector!==void 0&&(typeof rootSelector!="string"||!rootSelector.trim()))throw new Error("page_snapshot: rootSelector must be a non-empty CSS selector");if(rootRef!==void 0&&(typeof rootRef!="string"||!/^@e\d+$/.test(rootRef)))throw new Error('page_snapshot: rootRef must be a snapshot ref like "@e3"');if(rootSelector!==void 0&&rootRef!==void 0)throw new Error("page_snapshot: rootSelector and rootRef are mutually exclusive");if(!Number.isInteger(ancestorDepth)||ancestorDepth<0||ancestorDepth>20)throw new Error("page_snapshot: ancestorDepth must be an integer between 0 and 20");if(ancestorDepth>0&&rootRef===void 0)throw new Error("page_snapshot: ancestorDepth requires rootRef");let rootBackendNodeId;if(rootSelector)rootBackendNodeId=await resolveRootBackendNodeId(session,rootSelector);else if(rootRef){let entry=resolvePageRef(session,rootRef);if(!entry)throw new Error(`Ref ${rootRef} not found. Run page_snapshot first to get fresh refs.`);rootBackendNodeId=await resolveAncestorBackendNodeId(session,entry.backendNodeId,ancestorDepth)}let snapshotRefs=beginPageSnapshotRefs(session),[result,visibleBackendNodeIds]=await Promise.all([sendCdpDeviceShellPageCommand(session,rootBackendNodeId===void 0?"Accessibility.getFullAXTree":"Accessibility.queryAXTree",rootBackendNodeId===void 0?{depth:data?.depth}:{backendNodeId:rootBackendNodeId}),mode==="viewport"||mode==="interactive"?getViewportBackendNodeIds(session):Promise.resolve(void 0)]),nodes=result.nodes??[];await reconcileAXCheckedStates(nodes,backendNodeId=>readDOMCheckedState2(session,backendNodeId));let nodeMap=new Map;for(let node of nodes)nodeMap.set(node.nodeId,node);let rootNode=nodes[0];if(!rootNode)return snapshotRefs.commit(new Set),{snapshot:"(empty page)",refs:{},elementCount:0,outputNodeCount:1,mode,...rootSelector?{rootSelector}:{},...rootRef?{rootRef,ancestorDepth}:{},...query?{query,matches:[],matchCount:0}:{}};let rawTree=buildSnapshotTree(nodeMap,rootNode.nodeId,snapshotRefs),viewportFilteredTree=mode==="interactive"?filterSnapshotTree(rawTree,"viewport",visibleBackendNodeIds):rawTree,effectiveMode=mode;mode==="interactive"&&!hasInteractiveSnapshotNode(viewportFilteredTree)&&(effectiveMode="viewport");let modeFilteredTree=mode==="interactive"?effectiveMode==="interactive"?filterSnapshotTree(viewportFilteredTree,"interactive"):viewportFilteredTree:filterSnapshotTree(rawTree,effectiveMode,visibleBackendNodeIds),queryResult=query?filterSnapshotTreeByQuery(modeFilteredTree,query):{tree:modeFilteredTree,matches:[]},limitedMatches=query?limitPageSnapshotQueryMatches(queryResult.matches):{matches:[],truncated:!1},tree=queryResult.tree,lines=[];tree&&formatSnapshotNode(tree,0,lines);let formatted=formatSnapshotWithLimits(lines.length>0?lines:[query?"(no matches)":"(empty page)"],{maxChars:data?.maxChars,maxNodes:data?.maxNodes}),refs={},outputRefKeys=new Set(Array.from(formatted.snapshot.matchAll(/(?:^|\n)\s*(@e\d+)\s/g),match=>match[1]));for(let match of limitedMatches.matches)match.ref&&outputRefKeys.add(match.ref);snapshotRefs.commit(outputRefKeys);let registry2=getPageRefRegistry(session);for(let ref of outputRefKeys){let entry=registry2.resolve(ref);entry&&(refs[ref.slice(1)]={backendNodeId:entry.backendNodeId})}return{snapshot:formatted.snapshot,refs,elementCount:countSnapshotNodes(tree),outputNodeCount:formatted.outputNodeCount,mode,...rootSelector?{rootSelector}:{},...rootRef?{rootRef,ancestorDepth}:{},...query?{query,matches:limitedMatches.matches,matchCount:queryResult.matches.length,...limitedMatches.truncated?{matchesTruncated:!0}:{}}:{},...effectiveMode!==mode?{fallbackMode:"viewport"}:{},...formatted.truncated?{truncated:!0}:{}}}async function cdpPageFrames(session,_data){let result=await session.client.sendCommand("Page.getFrameTree",{});function flattenFrameTree(tree){if(!tree)return[];let{frame,childFrames=[]}=tree;return[{frameId:frame.id,parentFrameId:frame.parentId??null,url:frame.url,name:frame.name??""},...childFrames.flatMap(flattenFrameTree)]}let frames=flattenFrameTree(result.frameTree);return{frames,count:frames.length}}registerActions([{name:"page_snapshot",session:"page",handler:cdpPageSnapshot},{name:"page_frames",session:"page",handler:cdpPageFrames}]);var MEARL_SERVICE_COOKIE_DOMAINS=["tdbank.alibaba-inc.com","ats2.alibaba-inc.com","g.alibaba-inc.com","login.alibaba-inc.com","token-hub.alibaba-inc.com","alibaba-inc.com"],MEARL_SERVICE_COOKIE_HOSTS=new Set(MEARL_SERVICE_COOKIE_DOMAINS);function normalizeCookieDomain2(input){let value=input.trim();if(!value)throw new Error("Cookie domain must be a non-empty string");let hostname3;try{hostname3=new URL(value.includes("://")?value:`https://${value}`).hostname}catch{throw new Error(`Invalid cookie domain: ${input}`)}if(hostname3=hostname3.toLowerCase().replace(/^\.+/,"").replace(/\.+$/,""),!hostname3)throw new Error(`Invalid cookie domain: ${input}`);return hostname3}function normalizeCookieDomains(inputs){if(!Array.isArray(inputs))throw new Error("copyCookieDomains must be an array of domains");return[...new Set(inputs.map(value=>normalizeCookieDomain2(String(value))))]}function isMearlServiceCookie(cookie){let domain2=normalizeCookieDomain2(cookie.domain);return MEARL_SERVICE_COOKIE_HOSTS.has(domain2)?!0:domain2.endsWith(".tdbank.alibaba-inc.com")||domain2.endsWith(".ats2.alibaba-inc.com")}function normalizeCookieCopyScope(input){let presets=input.presets??[];if(!Array.isArray(presets)||presets.some(preset=>preset!=="mearl-services"))throw new Error("copyCookieDomains.presets contains an unsupported preset");return{includeMearlServices:presets.includes("mearl-services"),domains:normalizeCookieDomains(input.domains??[])}}function resolveCookieCopyPlan(input){let includeMearlServices=!1,explicitDomains=[];if(input==="mearl-services")includeMearlServices=!0;else if(Array.isArray(input))explicitDomains=normalizeCookieDomains(input);else if(input!=null&&typeof input=="object"){let scope=normalizeCookieCopyScope(input);includeMearlServices=scope.includeMearlServices,explicitDomains=scope.domains}else if(input!==void 0)throw new Error('copyCookieDomains must be an array of domains, "mearl-services", or a scope object');return includeMearlServices?{domains:[...new Set([...MEARL_SERVICE_COOKIE_DOMAINS,...explicitDomains])],select:cookies=>cookies.filter(cookie=>isMearlServiceCookie(cookie)||explicitDomains.some(domain2=>cookieMatchesDomain(cookie.domain,domain2)))}:{domains:explicitDomains,select:cookies=>cookies}}function cookieMatchesDomain(cookieDomain,requestedDomain){let cookieHost=cookieDomain.toLowerCase().replace(/^\.+/,"");return cookieHost===requestedDomain||cookieHost.endsWith(`.${requestedDomain}`)||requestedDomain.endsWith(`.${cookieHost}`)}function toTransferableCookie(cookie){let result={name:String(cookie.name),value:String(cookie.value),domain:String(cookie.domain),path:String(cookie.path||"/"),secure:!!cookie.secure,httpOnly:!!cookie.httpOnly};return["Strict","Lax","None"].includes(cookie.sameSite)&&(result.sameSite=cookie.sameSite),typeof cookie.expires=="number"&&cookie.expires>0&&(result.expires=cookie.expires),["Low","Medium","High"].includes(cookie.priority)&&(result.priority=cookie.priority),typeof cookie.sameParty=="boolean"&&(result.sameParty=cookie.sameParty),["Unset","NonSecure","Secure"].includes(cookie.sourceScheme)&&(result.sourceScheme=cookie.sourceScheme),typeof cookie.sourcePort=="number"&&(result.sourcePort=cookie.sourcePort),cookie.partitionKey?.topLevelSite&&(result.partitionKey={topLevelSite:String(cookie.partitionKey.topLevelSite),...typeof cookie.partitionKey.hasCrossSiteAncestor=="boolean"?{hasCrossSiteAncestor:cookie.partitionKey.hasCrossSiteAncestor}:{}}),result}async function cdpExportCookies(session,domains){let requestedDomains=normalizeCookieDomains(domains);return((await(await session.getBrowserClient()).sendCommand("Storage.getCookies",{})).cookies??[]).filter(cookie=>requestedDomains.some(domain2=>cookieMatchesDomain(String(cookie.domain),domain2))).map(toTransferableCookie)}async function cdpImportCookies(session,cookies){if(cookies.length===0)return 0;let browser=await session.getBrowserClient();return cdpImportCookiesWithClient(browser,cookies)}async function cdpImportCookiesWithClient(browser,cookies){return cookies.length===0?0:(await browser.sendCommand("Storage.setCookies",{cookies}),cookies.length)}var BROWSERS_SUBDIR="browsers",STALE_RECORD_TTL_MS=5*6e4,FOCUS_WRITE_THROTTLE_MS=1e3,identity=null,logFn2=()=>{};function initBrowserRegistry(deps){logFn2=deps.log}function recordFileFor2(browserId){return keyedConfigFile(BROWSERS_SUBDIR,browserId)}function ownsRecord(browserId){let existing=readRecord(recordFileFor2(browserId));return!existing||existing.pid===process.pid||!isAlive(existing.pid)}function persist(patch){if(!identity)return;let file2=recordFileFor2(identity.browserId);if(!ownsRecord(identity.browserId))return;let previous=readRecord(file2),record2={pid:process.pid,browserId:identity.browserId,socketPath:identity.socketPath,browserName:identity.browserName,nativeHostVersion:identity.nativeHostVersion,transport:identity.transport,startedAt:identity.startedAt,lastFocusedAt:previous?.lastFocusedAt,extensionVersion:previous?.extensionVersion,...patch,updatedAt:Date.now()};try{writeRecord(file2,record2)}catch(error48){logFn2("browser-registry: failed to write record:",String(error48))}}function registerBrowser(info){let startedAt=identity?.startedAt??new Date().toISOString();identity={browserId:info.browserId,socketPath:info.socketPath,browserName:info.browserName,nativeHostVersion:info.nativeHostVersion,transport:info.transport,startedAt};let previous=readRecord(recordFileFor2(info.browserId));persist({lastFocusedAt:previous?.lastFocusedAt??info.lastFocusedAt??Date.now(),extensionVersion:info.extensionVersion}),logFn2(`browser-registry: registered ${info.browserName} (${info.browserId})`)}var lastFocusWriteAt=0,focusFlushTimer=null;function writeFocusNow(){lastFocusWriteAt=Date.now(),persist({lastFocusedAt:lastFocusWriteAt})}function touchFocus(){if(!identity)return;let elapsed=Date.now()-lastFocusWriteAt;if(elapsed>=FOCUS_WRITE_THROTTLE_MS){writeFocusNow();return}focusFlushTimer||(focusFlushTimer=setTimeout(()=>{focusFlushTimer=null,writeFocusNow()},FOCUS_WRITE_THROTTLE_MS-elapsed),focusFlushTimer.unref())}function unregisterBrowser(){if(focusFlushTimer&&(clearTimeout(focusFlushTimer),focusFlushTimer=null),!identity)return;let file2=recordFileFor2(identity.browserId),existing=readRecord(file2);existing&&existing.pid!==process.pid||removeRecord(file2)}function pruneDeadBrowsers(){let now=Date.now();for(let file2 of listRecordFiles(keyedDir(BROWSERS_SUBDIR))){let rec=readRecord(file2);if(!rec){removeRecord(file2);continue}isAlive(rec.pid)||now-(rec.updatedAt??0)>STALE_RECORD_TTL_MS&&removeRecord(file2)}}function listRegisteredBrowsers(){let records=[];for(let file2 of listRecordFiles(keyedDir(BROWSERS_SUBDIR))){let record2=readRecord(file2);record2&&isAlive(record2.pid)&&records.push(record2)}return records.sort((a,b)=>(b.lastFocusedAt??0)-(a.lastFocusedAt??0)||(b.updatedAt??0)-(a.updatedAt??0))}function providerIdentity(descriptor){return{id:descriptor.providerId,name:descriptor.name,builtin:descriptor.builtin===!0,version:descriptor.version,protocolVersion:descriptor.protocolVersion}}async function ensureDiscoverableCdpBrowser(discovery){return discovery.isPublished()?!0:await discovery.isAvailable()?(discovery.publish(),discovery.monitor(),!0):!1}function ordinaryBrowser(record2){return{browserId:record2.browserId,name:record2.browserName,type:"regular",status:record2.status==="pairing"?"pairing":"connected",extensionVersion:record2.extensionVersion,nativeHostVersion:record2.nativeHostVersion,transport:record2.transport,pairing:record2.pairing}}function externalProviderBrowser(record2){if(!record2.provider||!record2.persistence||!record2.operations)throw new Error(`Provider browser record is incomplete: ${record2.browserId}`);let status=record2.status??"connected";return{browserId:record2.browserId,name:record2.browserName,type:"managed",status,extensionVersion:record2.extensionVersion,nativeHostVersion:record2.nativeHostVersion,transport:record2.transport,provider:record2.provider,ownership:record2.ownership,persistence:record2.persistence,operations:record2.operations,pairing:record2.pairing,references:record2.references,details:record2.details}}function providerBrowserInfo(entry,route){let{browser,provider}=entry,status=route&&(route.status===void 0||route.status==="connected")?"connected":browser.status;return{browserId:browser.browserId,name:browser.name,type:"managed",status,transport:route?.transport??(browser.control.kind==="host"?browser.control.transport:provider.builtin?void 0:"provider"),extensionVersion:route?.extensionVersion,nativeHostVersion:route?.nativeHostVersion,provider,ownership:browser.ownership,persistence:browser.persistence,operations:browser.operations,pairing:browser.pairing,references:browser.references,details:browser.details}}function mergeBrowserInventory(routes,providerBrowsers){let providerById=new Map(providerBrowsers.map(entry=>[entry.browser.browserId,entry])),browsers=[];for(let route of routes){let providerEntry=providerById.get(route.browserId);providerEntry?(providerById.delete(route.browserId),browsers.push(providerBrowserInfo(providerEntry,route))):route.provider||route.transport==="provider"?browsers.push(externalProviderBrowser(route)):browsers.push(ordinaryBrowser(route))}let inactive=[...providerById.values()].sort((a,b)=>{let aCreated=String(a.browser.details?.createdAt??"");return String(b.browser.details?.createdAt??"").localeCompare(aCreated)}).map(entry=>providerBrowserInfo(entry));return browsers.push(...inactive),{count:browsers.length,connectedCount:browsers.filter(browser=>browser.status==="connected").length,browsers}}async function providerInventory(runtimes){return(await Promise.all(runtimes.map(async runtime=>{let provider=providerIdentity(runtime.definition.descriptor);return(await runtime.listBrowsers()).map(browser=>({provider,browser}))}))).flat()}async function listBrowserInventory(runtimes){return mergeBrowserInventory(listRegisteredBrowsers(),await providerInventory(runtimes))}import{randomUUID as randomUUID5}from"node:crypto";var NATIVE_HOST_VERSION="2.16.2";import fs3 from"node:fs";import os2 from"node:os";import path3 from"node:path";var MEARL_CONFIG_FILE=path3.join(os2.homedir(),".mearl","config.json");function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function readMearlConfig(configFile=MEARL_CONFIG_FILE){try{let parsed=JSON.parse(fs3.readFileSync(configFile,"utf8"));return isRecord(parsed)?parsed:{}}catch{return{}}}function writeMearlConfig(config2,configFile=MEARL_CONFIG_FILE){let configDirectory=path3.dirname(configFile),temporaryFile=`${configFile}.${process.pid}.tmp`;fs3.mkdirSync(configDirectory,{recursive:!0});try{fs3.writeFileSync(temporaryFile,`${JSON.stringify(config2,null,2)}
595
+ }`,arguments:[{value:ancestorDepth}],returnByValue:!1});if(ancestorObjectId=ancestor.result?.objectId,!ancestorObjectId||ancestor.result?.subtype==="null")throw new Error("page_snapshot: cannot resolve an ancestor for rootRef");let ancestorBackendNodeId=(await sendCdpDeviceShellPageCommand(session,"DOM.describeNode",{objectId:ancestorObjectId})).node?.backendNodeId;if(ancestorBackendNodeId===void 0)throw new Error("page_snapshot: cannot resolve an ancestor for rootRef");return ancestorBackendNodeId}finally{ancestorObjectId&&await sendCdpDeviceShellPageCommand(session,"Runtime.releaseObject",{objectId:ancestorObjectId}).catch(()=>{}),await sendCdpDeviceShellPageCommand(session,"Runtime.releaseObject",{objectId}).catch(()=>{})}}async function getViewportBackendNodeIds(session){let[metrics,captured]=await Promise.all([sendCdpDeviceShellPageCommand(session,"Page.getLayoutMetrics",{}),sendCdpDeviceShellPageCommand(session,"DOMSnapshot.captureSnapshot",{computedStyles:[],includeDOMRects:!0,includePaintOrder:!1})]),viewport=resolveDOMSnapshotViewportBounds(metrics);if(!viewport)throw new Error("page_snapshot: viewport metrics are unavailable");let{left,top,width,height}=viewport,right=left+width,bottom=top+height,visible=new Set;for(let document2 of captured.documents??[]){let backendNodeIds=document2.nodes?.backendNodeId??[],nodeIndexes=document2.layout?.nodeIndex??[],bounds=document2.layout?.bounds??[];for(let index=0;index<nodeIndexes.length;index+=1){let box=bounds[index],backendNodeId=backendNodeIds[nodeIndexes[index]];if(!box||backendNodeId===void 0)continue;let[x,y,boxWidth,boxHeight]=box;boxWidth<=0||boxHeight<=0||x<right&&x+boxWidth>left&&y<bottom&&y+boxHeight>top&&visible.add(backendNodeId)}}return visible}async function cdpPageSnapshot(session,data){let mode=data?.mode??"full",rootSelector=data?.rootSelector,rootRef=data?.rootRef,ancestorDepth=data?.ancestorDepth??0,query=resolvePageSnapshotQuery(data?.query);if(!["full","interactive","viewport"].includes(mode))throw new Error("page_snapshot: mode must be one of full, interactive, viewport");if(rootSelector!==void 0&&(typeof rootSelector!="string"||!rootSelector.trim()))throw new Error("page_snapshot: rootSelector must be a non-empty CSS selector");if(rootRef!==void 0&&(typeof rootRef!="string"||!/^@e\d+$/.test(rootRef)))throw new Error('page_snapshot: rootRef must be a snapshot ref like "@e3"');if(rootSelector!==void 0&&rootRef!==void 0)throw new Error("page_snapshot: rootSelector and rootRef are mutually exclusive");if(!Number.isInteger(ancestorDepth)||ancestorDepth<0||ancestorDepth>20)throw new Error("page_snapshot: ancestorDepth must be an integer between 0 and 20");if(ancestorDepth>0&&rootRef===void 0)throw new Error("page_snapshot: ancestorDepth requires rootRef");let rootBackendNodeId;if(rootSelector)rootBackendNodeId=await resolveRootBackendNodeId(session,rootSelector);else if(rootRef){let entry=resolvePageRef(session,rootRef);if(!entry)throw new Error(`Ref ${rootRef} not found. Run page_snapshot first to get fresh refs.`);rootBackendNodeId=await resolveAncestorBackendNodeId(session,entry.backendNodeId,ancestorDepth)}let snapshotRefs=beginPageSnapshotRefs(session),[result,visibleBackendNodeIds]=await Promise.all([sendCdpDeviceShellPageCommand(session,rootBackendNodeId===void 0?"Accessibility.getFullAXTree":"Accessibility.queryAXTree",rootBackendNodeId===void 0?{depth:data?.depth}:{backendNodeId:rootBackendNodeId}),mode==="viewport"||mode==="interactive"?getViewportBackendNodeIds(session):Promise.resolve(void 0)]),nodes=result.nodes??[];await reconcileAXCheckedStates(nodes,backendNodeId=>readDOMCheckedState2(session,backendNodeId));let nodeMap=new Map;for(let node of nodes)nodeMap.set(node.nodeId,node);let rootNode=nodes[0];if(!rootNode)return snapshotRefs.commit(new Set),{snapshot:"(empty page)",refs:{},elementCount:0,outputNodeCount:1,mode,...rootSelector?{rootSelector}:{},...rootRef?{rootRef,ancestorDepth}:{},...query?{query,matches:[],matchCount:0}:{}};let rawTree=buildSnapshotTree(nodeMap,rootNode.nodeId,snapshotRefs),viewportFilteredTree=mode==="interactive"?filterSnapshotTree(rawTree,"viewport",visibleBackendNodeIds):rawTree,effectiveMode=mode;mode==="interactive"&&!hasInteractiveSnapshotNode(viewportFilteredTree)&&(effectiveMode="viewport");let modeFilteredTree=mode==="interactive"?effectiveMode==="interactive"?filterSnapshotTree(viewportFilteredTree,"interactive"):viewportFilteredTree:filterSnapshotTree(rawTree,effectiveMode,visibleBackendNodeIds),queryResult=query?filterSnapshotTreeByQuery(modeFilteredTree,query):{tree:modeFilteredTree,matches:[]},limitedMatches=query?limitPageSnapshotQueryMatches(queryResult.matches):{matches:[],truncated:!1},tree=queryResult.tree,lines=[];tree&&formatSnapshotNode(tree,0,lines);let formatted=formatSnapshotWithLimits(lines.length>0?lines:[query?"(no matches)":"(empty page)"],{maxChars:data?.maxChars,maxNodes:data?.maxNodes}),refs={},outputRefKeys=new Set(Array.from(formatted.snapshot.matchAll(/(?:^|\n)\s*(@e\d+)\s/g),match=>match[1]));for(let match of limitedMatches.matches)match.ref&&outputRefKeys.add(match.ref);snapshotRefs.commit(outputRefKeys);let registry2=getPageRefRegistry(session);for(let ref of outputRefKeys){let entry=registry2.resolve(ref);entry&&(refs[ref.slice(1)]={backendNodeId:entry.backendNodeId})}return{snapshot:formatted.snapshot,refs,elementCount:countSnapshotNodes(tree),outputNodeCount:formatted.outputNodeCount,mode,...rootSelector?{rootSelector}:{},...rootRef?{rootRef,ancestorDepth}:{},...query?{query,matches:limitedMatches.matches,matchCount:queryResult.matches.length,...limitedMatches.truncated?{matchesTruncated:!0}:{}}:{},...effectiveMode!==mode?{fallbackMode:"viewport"}:{},...formatted.truncated?{truncated:!0}:{}}}async function cdpPageFrames(session,_data){let result=await session.client.sendCommand("Page.getFrameTree",{});function flattenFrameTree(tree){if(!tree)return[];let{frame,childFrames=[]}=tree;return[{frameId:frame.id,parentFrameId:frame.parentId??null,url:frame.url,name:frame.name??""},...childFrames.flatMap(flattenFrameTree)]}let frames=flattenFrameTree(result.frameTree);return{frames,count:frames.length}}registerActions([{name:"page_snapshot",session:"page",handler:cdpPageSnapshot},{name:"page_frames",session:"page",handler:cdpPageFrames}]);var MEARL_SERVICE_COOKIE_DOMAINS=["tdbank.alibaba-inc.com","ats2.alibaba-inc.com","g.alibaba-inc.com","login.alibaba-inc.com","token-hub.alibaba-inc.com","alibaba-inc.com"],MEARL_SERVICE_COOKIE_HOSTS=new Set(MEARL_SERVICE_COOKIE_DOMAINS);function normalizeCookieDomain2(input){let value=input.trim();if(!value)throw new Error("Cookie domain must be a non-empty string");let hostname3;try{hostname3=new URL(value.includes("://")?value:`https://${value}`).hostname}catch{throw new Error(`Invalid cookie domain: ${input}`)}if(hostname3=hostname3.toLowerCase().replace(/^\.+/,"").replace(/\.+$/,""),!hostname3)throw new Error(`Invalid cookie domain: ${input}`);return hostname3}function normalizeCookieDomains(inputs){if(!Array.isArray(inputs))throw new Error("copyCookieDomains must be an array of domains");return[...new Set(inputs.map(value=>normalizeCookieDomain2(String(value))))]}function isMearlServiceCookie(cookie){let domain2=normalizeCookieDomain2(cookie.domain);return MEARL_SERVICE_COOKIE_HOSTS.has(domain2)?!0:domain2.endsWith(".tdbank.alibaba-inc.com")||domain2.endsWith(".ats2.alibaba-inc.com")}function normalizeCookieCopyScope(input){let presets=input.presets??[];if(!Array.isArray(presets)||presets.some(preset=>preset!=="mearl-services"))throw new Error("copyCookieDomains.presets contains an unsupported preset");return{includeMearlServices:presets.includes("mearl-services"),domains:normalizeCookieDomains(input.domains??[])}}function resolveCookieCopyPlan(input){let includeMearlServices=!1,explicitDomains=[];if(input==="mearl-services")includeMearlServices=!0;else if(Array.isArray(input))explicitDomains=normalizeCookieDomains(input);else if(input!=null&&typeof input=="object"){let scope=normalizeCookieCopyScope(input);includeMearlServices=scope.includeMearlServices,explicitDomains=scope.domains}else if(input!==void 0)throw new Error('copyCookieDomains must be an array of domains, "mearl-services", or a scope object');return includeMearlServices?{domains:[...new Set([...MEARL_SERVICE_COOKIE_DOMAINS,...explicitDomains])],select:cookies=>cookies.filter(cookie=>isMearlServiceCookie(cookie)||explicitDomains.some(domain2=>cookieMatchesDomain(cookie.domain,domain2)))}:{domains:explicitDomains,select:cookies=>cookies}}function cookieMatchesDomain(cookieDomain,requestedDomain){let cookieHost=cookieDomain.toLowerCase().replace(/^\.+/,"");return cookieHost===requestedDomain||cookieHost.endsWith(`.${requestedDomain}`)||requestedDomain.endsWith(`.${cookieHost}`)}function toTransferableCookie(cookie){let result={name:String(cookie.name),value:String(cookie.value),domain:String(cookie.domain),path:String(cookie.path||"/"),secure:!!cookie.secure,httpOnly:!!cookie.httpOnly};return["Strict","Lax","None"].includes(cookie.sameSite)&&(result.sameSite=cookie.sameSite),typeof cookie.expires=="number"&&cookie.expires>0&&(result.expires=cookie.expires),["Low","Medium","High"].includes(cookie.priority)&&(result.priority=cookie.priority),typeof cookie.sameParty=="boolean"&&(result.sameParty=cookie.sameParty),["Unset","NonSecure","Secure"].includes(cookie.sourceScheme)&&(result.sourceScheme=cookie.sourceScheme),typeof cookie.sourcePort=="number"&&(result.sourcePort=cookie.sourcePort),cookie.partitionKey?.topLevelSite&&(result.partitionKey={topLevelSite:String(cookie.partitionKey.topLevelSite),...typeof cookie.partitionKey.hasCrossSiteAncestor=="boolean"?{hasCrossSiteAncestor:cookie.partitionKey.hasCrossSiteAncestor}:{}}),result}async function cdpExportCookies(session,domains){let requestedDomains=normalizeCookieDomains(domains);return((await(await session.getBrowserClient()).sendCommand("Storage.getCookies",{})).cookies??[]).filter(cookie=>requestedDomains.some(domain2=>cookieMatchesDomain(String(cookie.domain),domain2))).map(toTransferableCookie)}async function cdpImportCookies(session,cookies){if(cookies.length===0)return 0;let browser=await session.getBrowserClient();return cdpImportCookiesWithClient(browser,cookies)}async function cdpImportCookiesWithClient(browser,cookies){return cookies.length===0?0:(await browser.sendCommand("Storage.setCookies",{cookies}),cookies.length)}var BROWSERS_SUBDIR="browsers",STALE_RECORD_TTL_MS=5*6e4,FOCUS_WRITE_THROTTLE_MS=1e3,identity=null,logFn2=()=>{};function initBrowserRegistry(deps){logFn2=deps.log}function recordFileFor2(browserId){return keyedConfigFile(BROWSERS_SUBDIR,browserId)}function ownsRecord(browserId){let existing=readRecord(recordFileFor2(browserId));return!existing||existing.pid===process.pid||!isAlive(existing.pid)}function persist(patch){if(!identity)return;let file2=recordFileFor2(identity.browserId);if(!ownsRecord(identity.browserId))return;let previous=readRecord(file2),record2={pid:process.pid,browserId:identity.browserId,socketPath:identity.socketPath,browserName:identity.browserName,nativeHostVersion:identity.nativeHostVersion,transport:identity.transport,startedAt:identity.startedAt,lastFocusedAt:previous?.lastFocusedAt,extensionVersion:previous?.extensionVersion,...patch,updatedAt:Date.now()};try{writeRecord(file2,record2)}catch(error48){logFn2("browser-registry: failed to write record:",String(error48))}}function registerBrowser(info){let startedAt=identity?.startedAt??new Date().toISOString();identity={browserId:info.browserId,socketPath:info.socketPath,browserName:info.browserName,nativeHostVersion:info.nativeHostVersion,transport:info.transport,startedAt};let previous=readRecord(recordFileFor2(info.browserId));persist({lastFocusedAt:previous?.lastFocusedAt??info.lastFocusedAt??Date.now(),extensionVersion:info.extensionVersion}),logFn2(`browser-registry: registered ${info.browserName} (${info.browserId})`)}var lastFocusWriteAt=0,focusFlushTimer=null;function writeFocusNow(){lastFocusWriteAt=Date.now(),persist({lastFocusedAt:lastFocusWriteAt})}function touchFocus(){if(!identity)return;let elapsed=Date.now()-lastFocusWriteAt;if(elapsed>=FOCUS_WRITE_THROTTLE_MS){writeFocusNow();return}focusFlushTimer||(focusFlushTimer=setTimeout(()=>{focusFlushTimer=null,writeFocusNow()},FOCUS_WRITE_THROTTLE_MS-elapsed),focusFlushTimer.unref())}function unregisterBrowser(){if(focusFlushTimer&&(clearTimeout(focusFlushTimer),focusFlushTimer=null),!identity)return;let file2=recordFileFor2(identity.browserId),existing=readRecord(file2);existing&&existing.pid!==process.pid||removeRecord(file2)}function pruneDeadBrowsers(){let now=Date.now();for(let file2 of listRecordFiles(keyedDir(BROWSERS_SUBDIR))){let rec=readRecord(file2);if(!rec){removeRecord(file2);continue}isAlive(rec.pid)||now-(rec.updatedAt??0)>STALE_RECORD_TTL_MS&&removeRecord(file2)}}function listRegisteredBrowsers(){let records=[];for(let file2 of listRecordFiles(keyedDir(BROWSERS_SUBDIR))){let record2=readRecord(file2);record2&&isAlive(record2.pid)&&records.push(record2)}return records.sort((a,b)=>(b.lastFocusedAt??0)-(a.lastFocusedAt??0)||(b.updatedAt??0)-(a.updatedAt??0))}function providerIdentity(descriptor){return{id:descriptor.providerId,name:descriptor.name,builtin:descriptor.builtin===!0,version:descriptor.version,protocolVersion:descriptor.protocolVersion}}async function ensureDiscoverableCdpBrowser(discovery){return discovery.isPublished()?!0:await discovery.isAvailable()?(discovery.publish(),discovery.monitor(),!0):!1}function ordinaryBrowser(record2){return{browserId:record2.browserId,name:record2.browserName,type:"regular",status:record2.status==="pairing"?"pairing":"connected",extensionVersion:record2.extensionVersion,nativeHostVersion:record2.nativeHostVersion,transport:record2.transport,pairing:record2.pairing}}function externalProviderBrowser(record2){if(!record2.provider||!record2.persistence||!record2.operations)throw new Error(`Provider browser record is incomplete: ${record2.browserId}`);let status=record2.status??"connected";return{browserId:record2.browserId,name:record2.browserName,type:"managed",status,extensionVersion:record2.extensionVersion,nativeHostVersion:record2.nativeHostVersion,transport:record2.transport,provider:record2.provider,ownership:record2.ownership,persistence:record2.persistence,operations:record2.operations,pairing:record2.pairing,references:record2.references,details:record2.details}}function providerBrowserInfo(entry,route){let{browser,provider}=entry,status=route&&(route.status===void 0||route.status==="connected")?"connected":browser.status;return{browserId:browser.browserId,name:browser.name,type:"managed",status,transport:route?.transport??(browser.control.kind==="host"?browser.control.transport:provider.builtin?void 0:"provider"),extensionVersion:route?.extensionVersion,nativeHostVersion:route?.nativeHostVersion,provider,ownership:browser.ownership,persistence:browser.persistence,operations:browser.operations,pairing:browser.pairing,references:browser.references,details:browser.details}}function mergeBrowserInventory(routes,providerBrowsers){let providerById=new Map(providerBrowsers.map(entry=>[entry.browser.browserId,entry])),browsers=[];for(let route of routes){let providerEntry=providerById.get(route.browserId);providerEntry?(providerById.delete(route.browserId),browsers.push(providerBrowserInfo(providerEntry,route))):route.provider||route.transport==="provider"?browsers.push(externalProviderBrowser(route)):browsers.push(ordinaryBrowser(route))}let inactive=[...providerById.values()].sort((a,b)=>{let aCreated=String(a.browser.details?.createdAt??"");return String(b.browser.details?.createdAt??"").localeCompare(aCreated)}).map(entry=>providerBrowserInfo(entry));return browsers.push(...inactive),{count:browsers.length,connectedCount:browsers.filter(browser=>browser.status==="connected").length,browsers}}async function providerInventory(runtimes){return(await Promise.all(runtimes.map(async runtime=>{let provider=providerIdentity(runtime.definition.descriptor);return(await runtime.listBrowsers()).map(browser=>({provider,browser}))}))).flat()}async function listBrowserInventory(runtimes){return mergeBrowserInventory(listRegisteredBrowsers(),await providerInventory(runtimes))}import{randomUUID as randomUUID5}from"node:crypto";var NATIVE_HOST_VERSION="2.16.3";import fs3 from"node:fs";import os2 from"node:os";import path3 from"node:path";var MEARL_CONFIG_FILE=path3.join(os2.homedir(),".mearl","config.json");function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function readMearlConfig(configFile=MEARL_CONFIG_FILE){try{let parsed=JSON.parse(fs3.readFileSync(configFile,"utf8"));return isRecord(parsed)?parsed:{}}catch{return{}}}function writeMearlConfig(config2,configFile=MEARL_CONFIG_FILE){let configDirectory=path3.dirname(configFile),temporaryFile=`${configFile}.${process.pid}.tmp`;fs3.mkdirSync(configDirectory,{recursive:!0});try{fs3.writeFileSync(temporaryFile,`${JSON.stringify(config2,null,2)}
596
596
  `,{mode:384}),fs3.renameSync(temporaryFile,configFile)}catch(error48){try{fs3.unlinkSync(temporaryFile)}catch{}throw error48}}var MEASUREMENT_ID="G-W84LJDZ6PH",API_SECRET="X233siI5RrGYLhlD0kBKEQ",ENDPOINT=`https://www.google-analytics.com/mp/collect?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`,CLIENT_ID_PATTERN=/^\d+\.\d+$/,BUILD_CHANNEL=NATIVE_HOST_VERSION==="unknown"?"development":"production";function writeConfig(config2){try{writeMearlConfig(config2)}catch{}}var clientId=null;function createClientId(seed=randomUUID5()){let first=2166136261,second=2246822519;for(let index=0;index<seed.length;index++){let code=seed.charCodeAt(index);first=Math.imul(first^code,16777619)>>>0,second=Math.imul(second^code,3266489917)>>>0}return`${first||1}.${second||1}`}function getClientId(){if(clientId)return clientId;let config2=readMearlConfig();return config2.clientId&&CLIENT_ID_PATTERN.test(config2.clientId)?(clientId=config2.clientId,clientId):(clientId=createClientId(config2.clientId),writeConfig({...config2,clientId}),clientId)}function setAnalyticsClientId(value){return typeof value!="string"||!CLIENT_ID_PATTERN.test(value)?!1:(clientId=value,!0)}function classifyAnalyticsError(error48){let message=error48 instanceof Error?error48.message:String(error48);return/timed?\s*out|timeout/i.test(message)?"timeout":/permission|not authorized|access denied|forbidden/i.test(message)?"permission_denied":/not found|no matching|does not exist/i.test(message)?"not_found":/not connected|disconnected|unavailable|connection (?:failed|closed)/i.test(message)?"unavailable":/invalid|required|must be|unsupported/i.test(message)?"invalid_request":"execution_error"}function trackAction(action,params){let payload={client_id:getClientId(),events:[{name:"agent_action",params:{action,...params,mearl_version:NATIVE_HOST_VERSION,runtime:"native_host",build_channel:BUILD_CHANNEL}}]};fetch(ENDPOINT,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(payload),keepalive:!0}).catch(()=>{})}import{open}from"node:fs/promises";import path5 from"node:path";var TRUNCATED_MARKER=`
597
597
  … [middle truncated for Native Messaging] …
598
598
  `;function truncateUtf82(value,maxBytes){if(Buffer.byteLength(value,"utf8")<=maxBytes)return value;let markerBytes=Buffer.byteLength(TRUNCATED_MARKER,"utf8");if(maxBytes<=markerBytes)return"";let source=Buffer.from(value,"utf8"),contentBytes=maxBytes-markerBytes,headBytes=Math.ceil(contentBytes/2),tailBytes=Math.floor(contentBytes/2);do{let truncated=`${source.subarray(0,headBytes).toString("utf8")}${TRUNCATED_MARKER}${source.subarray(source.length-tailBytes).toString("utf8")}`;if(Buffer.byteLength(truncated,"utf8")<=maxBytes)return truncated;headBytes>=tailBytes?headBytes-=1:tailBytes-=1}while(headBytes>0&&tailBytes>0);return""}function sanitizeValue(value,budget,depth){if(value===null||typeof value=="boolean"||typeof value=="number")return value;if(typeof value=="string"){let available=Math.max(0,budget.remaining),next2=truncateUtf82(value,available),bytes=Buffer.byteLength(next2,"utf8");return budget.remaining=Math.max(0,budget.remaining-bytes),next2!==value&&(budget.truncated=!0),next2}if(typeof value>"u")return null;if(typeof value!="object")return String(value);if(depth>=12||budget.remaining<=0)return budget.truncated=!0,{__transportTruncated:!0};if(budget.seen.has(value))return budget.truncated=!0,"[Circular]";if(budget.seen.add(value),Array.isArray(value)){let next2=value.slice(0,160).map(entry=>sanitizeValue(entry,budget,depth+1));return value.length>160&&(budget.truncated=!0,next2.push({__transportTruncated:!0,originalLength:value.length})),next2}let entries=Object.entries(value),next={};for(let[index,[key,entry]]of entries.entries()){if(index>=160||budget.remaining<=0){budget.truncated=!0;break}budget.remaining=Math.max(0,budget.remaining-Buffer.byteLength(key,"utf8")),next[key]=sanitizeValue(entry,budget,depth+1)}return budget.truncated&&(next.__transportTruncated=!0),next}function sanitizeAgentChatEvent(event){let budget={remaining:409600,truncated:!1,seen:new WeakSet},next={...event,params:sanitizeValue(event.params,budget,0)},bytes=Buffer.byteLength(JSON.stringify(next),"utf8");return bytes<=524288?next:{...event,params:{__transportTruncated:!0,originalBytes:bytes,message:"Agent event exceeded the Native Messaging transport limit."}}}import fs4 from"node:fs";import path4 from"node:path";import{execFile}from"node:child_process";function selectedPath(stdout){return stdout.replace(/[\r\n]+$/,"")||null}var runDirectoryPickerCommand=(command,args)=>new Promise((resolve2,reject)=>{execFile(command,args,{encoding:"utf8",windowsHide:!0},(error48,stdout)=>{if(!error48){resolve2(selectedPath(stdout));return}if(error48.code===1){resolve2(null);return}reject(error48)})});function isMissingCommand(error48){return error48 instanceof Error&&error48.code==="ENOENT"}async function pickWorkspaceDirectory(platform=process.platform,run=runDirectoryPickerCommand){let directory;if(platform==="darwin")directory=await run("osascript",["-e",'POSIX path of (choose folder with prompt "Select a workspace for Agent Chat")']);else if(platform==="win32"){let script=["Add-Type -AssemblyName System.Windows.Forms;","$dialog = New-Object System.Windows.Forms.FolderBrowserDialog;","$dialog.Description = 'Select a workspace for Agent Chat';","if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {","[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;","Write-Output $dialog.SelectedPath;","}"].join(" ");directory=await run("powershell.exe",["-NoProfile","-STA","-Command",script])}else if(platform==="linux")try{directory=await run("zenity",["--file-selection","--directory","--title=Select a workspace for Agent Chat"])}catch(error48){if(!isMissingCommand(error48))throw error48;try{directory=await run("kdialog",["--getexistingdirectory",".","--title","Select a workspace for Agent Chat"])}catch(fallbackError){throw isMissingCommand(fallbackError)?new Error("Install zenity or kdialog to select an Agent Chat workspace",{cause:fallbackError}):fallbackError}}else throw new Error(`Directory selection is not supported on ${platform}`);return directory?resolveWorkspaceDirectory(directory):null}function resolveWorkspaceDirectory(directory){if(typeof directory!="string"||!directory)throw new Error("Workspace directory is required");if(!path4.isAbsolute(directory))throw new Error("Workspace directory must be an absolute path");let resolved;try{resolved=fs4.realpathSync.native(directory)}catch(error48){throw new Error(`Workspace directory does not exist: ${directory}`,{cause:error48})}if(!fs4.statSync(resolved).isDirectory())throw new Error(`Workspace path is not a directory: ${directory}`);return resolved}var MAX_REPLAY_EVENTS=1e3,MAX_REPLAY_BYTES=2048*1024,MAX_REPLAY_PAGE_BYTES=524288,MAX_SKILL_FILE_BYTES=1024*1024,SUPPORTED_IMAGE_MIME_TYPES=new Set(AGENT_CHAT_SUPPORTED_IMAGE_MIME_TYPES),REPLAY_SNAPSHOT_METHODS=new Set(["thread/settings/updated"]);function createAgentDeveloperInstructions({cwd,tabId,initialPageUrl,initialPageTitle}){return[...cwd?[createAgentDirectoryContextInstructions(cwd)]:[],...tabId!==void 0?[createBrowserAgentDeveloperInstructions(tabId,{...initialPageUrl?{url:initialPageUrl}:{},...initialPageTitle?{title:initialPageTitle}:{}})]:[]].join(`
@@ -643,7 +643,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
643
643
  `)}function qoderModelInfo(configOptions){let modelOption=configOptions?.find(option=>option.category==="model"&&option.type==="select");if(!modelOption||typeof modelOption.currentValue!="string")return;let id=modelOption.currentValue.trim().slice(0,MAX_MODEL_CHARS2);if(!id)return;let displayName=modelOption.options.flatMap(option=>"options"in option?option.options:[option]).find(option=>option.value===modelOption.currentValue)?.name.trim().slice(0,MAX_MODEL_CHARS2);return{id,...displayName&&displayName!==id?{displayName}:{}}}function parseQoderSkillsList(output,cwd,home=os4.homedir()){let skills=[],current,finish=()=>{if(!current)return;let skillPath=current.path||(current.builtIn?"bundled":""),relativeToCwd=skillPath&&path8.isAbsolute(skillPath)?path8.relative(cwd,skillPath):"",inProject=!!relativeToCwd&&relativeToCwd!==".."&&!relativeToCwd.startsWith(`..${path8.sep}`)&&!path8.isAbsolute(relativeToCwd);skills.push({name:current.name,path:skillPath,scope:current.builtIn?"built-in":cwd!==home&&inProject?"project":"global"}),current=void 0};for(let line of output.split(/\r?\n/)){let heading=/^(\S+)\s+\[(Enabled|Disabled)\](?:\s+\[Built-in\])?\s*$/.exec(line);if(heading?.[1]&&heading[2]){finish(),current={name:heading[1],builtIn:line.includes("[Built-in]")};continue}if(!current)continue;let skillPath=/^\s+Location:\s*(.*)$/.exec(line)?.[1];skillPath!==void 0&&(current.path=skillPath)}return finish(),skills}function qoderManagedExecutableCandidates(home,readdirSync2=fs7.readdirSync){let directory=path8.join(home,".qoder","bin","qodercli");try{return readdirSync2(directory,{withFileTypes:!0}).filter(entry=>entry.isFile()&&entry.name.startsWith("qodercli-")).sort((left,right)=>right.name.localeCompare(left.name,void 0,{numeric:!0})).map(entry=>path8.join(directory,entry.name))}catch{return[]}}function isPermissionResponse(value){if(!value||typeof value!="object")return!1;let outcome=value.outcome;return!outcome||typeof outcome!="object"?!1:outcome.outcome==="cancelled"?!0:outcome.outcome==="selected"&&typeof outcome.optionId=="string"}function qoderInstallCommand(platform=process.platform){return platform==="win32"?"npm install -g @qoder-ai/qodercli":"curl -fsSL https://qoder.com/install | bash"}function qoderExecutableCandidatePaths(options={}){let env=options.env??process.env,home=(options.homedir??os4.homedir)(),platform=options.platform??process.platform,executableNames2=platform==="win32"?["qodercli.exe","qodercli.cmd"]:["qodercli"];return agentExecutableCandidatePaths({executableNames:executableNames2,configuredPath:env.MEARL_QODER_PATH,env,extraUserCandidates:[...qoderManagedExecutableCandidates(home,options.readdirSync),...executableNames2.map(name=>path8.join(home,".qoder","bin",name))],home,platform,processExecPath:options.processExecPath,readdirSync:options.readdirSync})}function sessionOptions(options){let cwd=path8.resolve(options.cwd||os4.homedir());return{...options,cwd,instructionsSent:!1}}function parsedTimestamp(value){if(!value)return;let timestamp=Date.parse(value);return Number.isNaN(timestamp)?void 0:timestamp}function historyItemId(capture,kind,sourceId){if(sourceId)return`qoder_history_${kind}_${sourceId}`;let id=`qoder_history_${kind}_${capture.nextId}`;return capture.nextId+=1,id}function historyMessageKey(capture,role,messageId){if(messageId)return capture.lastAnonymousMessage=void 0,`${role}:${messageId}`;if(capture.lastAnonymousMessage?.role===role)return capture.lastAnonymousMessage.key;let key=`${role}:anonymous:${capture.nextId}`;return capture.nextId+=1,capture.lastAnonymousMessage={role,key},key}function captureHistoryMessage(capture,role,update){let text=contentText(update.content),imageCount=update.content.type==="image"?1:0;if(!text&&imageCount===0)return;let key=historyMessageKey(capture,role,update.messageId),existing=capture.messageEvents.get(key);if(existing){if(role==="user"){let params=existing.params;text&&(params.text+=text),imageCount>0&&(params.imageCount=(params.imageCount||0)+imageCount)}else if(role==="agent"){let params=existing.params;text&&(params.item.text+=text)}else if(text){let params=existing.params;params.item.summary[0]=`${params.item.summary[0]||""}${text}`}return}let itemId=historyItemId(capture,role,update.messageId),event=role==="user"?{providerSessionId:capture.providerSessionId,kind:"lifecycle",method:"chat/userMessage",params:{text:text||"",...imageCount>0?{imageCount}:{}}}:{providerSessionId:capture.providerSessionId,kind:"notification",method:"item/completed",params:{item:{id:itemId,type:role==="agent"?"agentMessage":"reasoning",...role==="agent"?{text:text||""}:{summary:[text||""]}}}};capture.messageEvents.set(key,event),capture.events.push(event)}function captureHistoryUpdate(capture,update){switch(update.sessionUpdate){case"user_message_chunk":captureHistoryMessage(capture,"user",update);return;case"agent_message_chunk":captureHistoryMessage(capture,"agent",update);return;case"agent_thought_chunk":captureHistoryMessage(capture,"reasoning",update);return;case"tool_call":case"tool_call_update":{capture.lastAnonymousMessage=void 0;let completed=update.status==="completed"||update.status==="failed",existing=capture.toolEvents.get(update.toolCallId),existingItem=existing?existing.params.item:void 0,item={id:historyItemId(capture,"tool",update.toolCallId),type:"dynamicToolCall",namespace:"qoder",tool:update.title||existingItem?.tool||update.toolCallId,status:update.status||(completed?"completed":"inProgress"),kind:update.kind||existingItem?.kind};if(existing)existing.method=completed?"item/completed":"item/started",existing.params={item};else{let event={providerSessionId:capture.providerSessionId,kind:"notification",method:completed?"item/completed":"item/started",params:{item}};capture.toolEvents.set(update.toolCallId,event),capture.events.push(event)}return}case"plan":{capture.lastAnonymousMessage=void 0;let item={id:historyItemId(capture,"plan","current"),type:"plan",text:planText(update.entries)};capture.planEvent?capture.planEvent.params={item}:(capture.planEvent={providerSessionId:capture.providerSessionId,kind:"notification",method:"item/completed",params:{item}},capture.events.push(capture.planEvent));return}default:capture.lastAnonymousMessage=void 0}}var QoderAgentProvider=class{constructor(log2,clientVersion){this.log=log2;this.clientVersion=clientVersion;this.executableResolver=new AgentExecutableResolver({candidates:()=>this.resolveExecutableCandidates(),probe:executable=>this.probeExecutable(executable),notFoundError:QODER_NOT_FOUND_ERROR,unusableError:"Qoder CLI candidates were found, but none is usable.",onFailure:(executable,error48)=>{this.log("[Qoder] Skipping unusable CLI:",executable,error48)}})}id="qoder";process=null;connection=null;context=null;initializeResponse=null;startPromise=null;listeners=new Set;sessions=new Map;pendingPermissions=new Map;historyCaptures=new Map;nextTurnId=1;executableResolver;async getDescriptor(){let{error:error48}=await this.resolveExecutable();return{id:this.id,name:"Qoder",available:error48===null,capabilities:{streaming:!0,resume:!0,listSessions:"runtime",refreshHistory:!1,imageInput:"runtime",interrupt:!0,requests:!0},...error48?{error:error48}:{},setup:{installCommand:qoderInstallCommand(),loginCommand:"qodercli",docsUrl:"https://docs.qoder.com/en/cli/quick-start"}}}async prepare(){await this.ensureStarted()}onEvent(listener){return this.listeners.add(listener),()=>this.listeners.delete(listener)}async listSessions(options){if(await this.ensureStarted(),!this.initializeResponse?.agentCapabilities?.sessionCapabilities?.list)throw new Error("This Qoder CLI version cannot list existing ACP sessions");let result=await this.withTimeout(this.getContext().request(methods.agent.session.list,{cursor:options.cursor??null,...options.cwd?{cwd:options.cwd}:{}}),"Qoder session list");return{sessions:result.sessions.map(session=>{let updatedAt=parsedTimestamp(session.updatedAt);return{providerSessionId:session.sessionId,...session.title?{title:session.title}:{},...session.cwd?{cwd:session.cwd}:{},...updatedAt!==void 0?{updatedAt}:{}}}),...result.nextCursor?{nextCursor:result.nextCursor}:{}}}async listSkills(cwd){let resolution=await this.resolveExecutable();if(resolution.executable===null)throw new Error(resolution.error);let{executable}=resolution,resolvedCwd=cwd||os4.homedir(),output=await this.runSkillsList(executable,resolvedCwd);return parseQoderSkillsList(output,resolvedCwd)}async startSession(options){let session=sessionOptions(options);await this.ensureStarted();let result=await this.withTimeout(this.getContext().request(methods.agent.session.new,{cwd:session.cwd,mcpServers:[]}),"Qoder session creation");if(!result.sessionId)throw new Error("Qoder did not return a session id");let model=qoderModelInfo(result.configOptions);return model&&(session.model=model),this.sessions.set(result.sessionId,session),result.sessionId}async resumeSession(providerSessionId,options){let session=sessionOptions(options);await this.ensureStarted();let capabilities=this.initializeResponse?.agentCapabilities,configOptions,historyCapture=capabilities?.loadSession?{providerSessionId,events:[],messageEvents:new Map,toolEvents:new Map,nextId:1}:null;historyCapture&&this.historyCaptures.set(providerSessionId,historyCapture);try{if(historyCapture)configOptions=(await this.withTimeout(this.getContext().request(methods.agent.session.load,{sessionId:providerSessionId,cwd:session.cwd,mcpServers:[]}),"Qoder session load")).configOptions;else if(capabilities?.sessionCapabilities?.resume)configOptions=(await this.withTimeout(this.getContext().request(methods.agent.session.resume,{sessionId:providerSessionId,cwd:session.cwd,mcpServers:[]}),"Qoder session resume")).configOptions;else throw new Error("This Qoder CLI version cannot resume an existing ACP session")}finally{this.historyCaptures.delete(providerSessionId)}let model=qoderModelInfo(configOptions);return model&&(session.model=model),this.sessions.set(providerSessionId,session),{providerSessionId,...historyCapture?{history:historyCapture.events,historyComplete:!0}:{}}}getSessionModel(providerSessionId){let model=this.sessions.get(providerSessionId)?.model;return model?{...model}:void 0}async validateMessageInput(images){images.length!==0&&(await this.ensureStarted(),this.assertImageInputSupported())}async sendMessage(providerSessionId,message,images=[]){await this.ensureStarted();let session=this.sessions.get(providerSessionId);if(!session)throw new Error(`Unknown Qoder session: ${providerSessionId}`);if(session.activeTurn)throw new Error("The current Qoder turn has not finished");images.length>0&&this.assertImageInputSupported();let turnId=`qoder_turn_${Date.now()}_${this.nextTurnId++}`,turn={id:turnId,assistantItemId:`${turnId}_message`,assistantText:"",reasoningItemId:`${turnId}_reasoning`,reasoningText:"",reasoningStarted:!1,planItemId:`${turnId}_plan`,toolTitles:new Map};session.activeTurn=turn;let prompt=!session.instructionsSent&&session.developerInstructions?`${session.developerInstructions}
644
644
 
645
645
  User request:
646
- ${message}`:message,promptContent=[...prompt?[{type:"text",text:prompt}]:[],...images.map(image=>({type:"image",data:image.data,mimeType:image.mimeType}))];return session.instructionsSent=!0,this.emit({providerSessionId,kind:"notification",method:"turn/started",params:{turn:{id:turnId,status:"inProgress"}}}),this.runPrompt(providerSessionId,session,turn,promptContent),{turnId}}async interrupt(providerSessionId,_turnId){await this.ensureStarted(),await this.getContext().notify(methods.agent.session.cancel,{sessionId:providerSessionId}),this.cancelPermissions(providerSessionId)}async closeSession(providerSessionId){this.sessions.get(providerSessionId)?.activeTurn&&this.context&&await this.context.notify(methods.agent.session.cancel,{sessionId:providerSessionId}).catch(()=>{}),this.cancelPermissions(providerSessionId),this.sessions.delete(providerSessionId)}respondToRequest(requestId,result){let pending=this.pendingPermissions.get(requestId);if(!pending)throw new Error(`Unknown Qoder permission request: ${String(requestId)}`);if(!isPermissionResponse(result))throw new Error("Invalid Qoder permission response");this.pendingPermissions.delete(requestId),pending.resolve(result),this.emit({providerSessionId:pending.providerSessionId,kind:"notification",method:"serverRequest/resolved",params:{requestId}})}dispose(){for(let pending of this.pendingPermissions.values())pending.resolve({outcome:{outcome:"cancelled"}});this.pendingPermissions.clear(),this.historyCaptures.clear(),this.sessions.clear();let connection=this.connection,child=this.process;this.process=null,this.connection=null,this.context=null,this.initializeResponse=null,this.startPromise=null,this.executableResolver.reset(),connection?.close(new Error("Qoder provider disposed")),child&&!child.killed&&child.kill("SIGTERM")}async ensureStarted(){if(!(this.process&&!this.process.killed&&this.context)){if(this.startPromise)return this.startPromise;this.startPromise=this.start();try{await this.startPromise}catch(error48){throw this.startPromise=null,error48}}}async start(){let resolution=await this.resolveExecutable();if(resolution.executable===null)throw new Error(resolution.error);let{executable}=resolution,launch=resolveSpawnCommand(executable,["--acp"]),child=spawn7(launch.command,launch.args,{stdio:["pipe","pipe","pipe"],windowsHide:!0,env:prependNodePath()});this.process=child,child.stderr.on("data",chunk=>{let message=chunk.toString("utf8").trim();message&&this.log("[Qoder]",message)}),child.once("error",error48=>this.handleExit(child,error48)),child.once("exit",(code,signal)=>{this.handleExit(child,new Error(`Qoder ACP exited (code=${String(code)}, signal=${String(signal)})`))});let stream=ndJsonStream(Writable.toWeb(child.stdin),Readable.toWeb(child.stdout)),connection=client({name:"mearl"}).onRequest(methods.client.session.requestPermission,context=>this.handlePermissionRequest(context.requestId,context.params)).onNotification(methods.client.session.update,context=>{this.handleSessionUpdate(context.params)}).connect(stream);this.connection=connection,this.context=connection.agent;try{this.initializeResponse=await this.withTimeout(connection.agent.request(methods.agent.initialize,{protocolVersion:PROTOCOL_VERSION,clientCapabilities:{},clientInfo:{name:"mearl",title:"Mearl",version:this.clientVersion}}),"Qoder ACP initialization")}catch(error48){throw this.process=null,this.connection=null,this.context=null,this.executableResolver.reset(),connection.close(error48),child.killed||child.kill("SIGTERM"),new Error(`Qoder ACP failed to initialize. Start "qodercli", enter "/login", and try again: ${errorMessage(error48)}`,{cause:error48})}}async runPrompt(providerSessionId,session,turn,prompt){try{let response=await this.getContext().request(methods.agent.session.prompt,{sessionId:providerSessionId,prompt});turn.reasoningStarted&&this.emit({providerSessionId,kind:"notification",method:"item/completed",params:{turnId:turn.id,item:{id:turn.reasoningItemId,type:"reasoning",summary:turn.reasoningText?[turn.reasoningText]:[]}}}),turn.assistantText&&this.emit({providerSessionId,kind:"notification",method:"item/completed",params:{turnId:turn.id,item:{id:turn.assistantItemId,type:"agentMessage",text:turn.assistantText}}}),response.usage&&this.emit({providerSessionId,kind:"notification",method:"thread/tokenUsage/updated",params:{turnId:turn.id,usage:response.usage}}),this.emit({providerSessionId,kind:"notification",method:"turn/completed",params:{turn:{id:turn.id,status:response.stopReason==="cancelled"?"cancelled":"completed",stopReason:response.stopReason}}})}catch(error48){this.emit({providerSessionId,kind:"error",method:"chat/error",params:{turnId:turn.id,message:errorMessage(error48)}}),this.emit({providerSessionId,kind:"notification",method:"turn/completed",params:{turn:{id:turn.id,status:"failed"}}})}finally{session.activeTurn===turn&&delete session.activeTurn}}handleSessionUpdate(notification){let historyCapture=this.historyCaptures.get(notification.sessionId);if(historyCapture){captureHistoryUpdate(historyCapture,notification.update);return}let session=this.sessions.get(notification.sessionId),update=notification.update;if(update.sessionUpdate==="config_option_update"){let model=qoderModelInfo(update.configOptions);session&&(model?session.model=model:delete session.model),this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"session/model/updated",params:{model:model??null}}),this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"qoder/sessionUpdate",params:notification});return}let turn=session?.activeTurn;if(!turn){this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"qoder/sessionUpdate",params:notification});return}switch(update.sessionUpdate){case"agent_message_chunk":{let delta=contentText(update.content);if(!delta)return;turn.assistantText+=delta,this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"item/agentMessage/delta",params:{turnId:turn.id,itemId:turn.assistantItemId,delta}});return}case"agent_thought_chunk":{let delta=contentText(update.content);if(!delta)return;turn.reasoningStarted||(turn.reasoningStarted=!0,this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"item/started",params:{turnId:turn.id,item:{id:turn.reasoningItemId,type:"reasoning",summary:[]}}})),turn.reasoningText+=delta,this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"item/reasoning/summaryTextDelta",params:{turnId:turn.id,itemId:turn.reasoningItemId,delta}});return}case"tool_call":turn.toolTitles.set(update.toolCallId,update.title),this.emitToolUpdate(notification.sessionId,turn,update,update.title);return;case"tool_call_update":{let title=update.title||turn.toolTitles.get(update.toolCallId)||update.toolCallId;turn.toolTitles.set(update.toolCallId,title),this.emitToolUpdate(notification.sessionId,turn,update,title);return}case"plan":{let text=planText(update.entries);this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"item/completed",params:{turnId:turn.id,item:{id:turn.planItemId,type:"plan",text}}});return}case"usage_update":this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"thread/tokenUsage/updated",params:{turnId:turn.id,usage:update}});return;default:this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"qoder/sessionUpdate",params:{turnId:turn.id,update}})}}emitToolUpdate(providerSessionId,turn,update,title){let completed=update.status==="completed"||update.status==="failed";this.emit({providerSessionId,kind:"notification",method:completed?"item/completed":"item/started",params:{turnId:turn.id,item:{id:update.toolCallId,type:"dynamicToolCall",namespace:"qoder",tool:title,status:update.status||(completed?"completed":"inProgress"),kind:update.kind,rawInput:update.rawInput,rawOutput:update.rawOutput,content:update.content,locations:update.locations}}})}handlePermissionRequest(requestId,request){return requestId===null?Promise.resolve({outcome:{outcome:"cancelled"}}):new Promise(resolve2=>{this.pendingPermissions.set(requestId,{providerSessionId:request.sessionId,resolve:resolve2}),this.emit({providerSessionId:request.sessionId,kind:"request",method:"qoder/requestPermission",params:request,requestId})})}cancelPermissions(providerSessionId){for(let[requestId,pending]of this.pendingPermissions)pending.providerSessionId===providerSessionId&&(this.pendingPermissions.delete(requestId),pending.resolve({outcome:{outcome:"cancelled"}}))}handleExit(child,error48){if(this.process===child){this.process=null,this.connection?.close(error48),this.connection=null,this.context=null,this.initializeResponse=null,this.startPromise=null,this.executableResolver.reset();for(let providerSessionId of this.sessions.keys())this.cancelPermissions(providerSessionId),this.emit({providerSessionId,kind:"error",method:"provider/exited",params:{message:error48.message}})}}getContext(){if(!this.context)throw new Error("Qoder ACP is not running");return this.context}assertImageInputSupported(){if(!this.initializeResponse?.agentCapabilities?.promptCapabilities?.image)throw new Error("This Qoder CLI version does not support image prompts")}withTimeout(promise2,operation){return new Promise((resolve2,reject)=>{let timer=setTimeout(()=>reject(new Error(`${operation} timed out`)),REQUEST_TIMEOUT_MS2);promise2.then(result=>{clearTimeout(timer),resolve2(result)},error48=>{clearTimeout(timer),reject(error48)})})}probeExecutable(executable){return probeAgentExecutable(executable,{args:["--version"],timeoutMs:PROBE_TIMEOUT_MS2,timeoutError:"Timed out while checking Qoder CLI availability.",startError:error48=>`Failed to start Qoder CLI: ${error48.message}`,exitError:code=>`Qoder CLI check exited with code ${String(code)}`})}runSkillsList(executable,cwd){return new Promise((resolve2,reject)=>{let launch=resolveSpawnCommand(executable,["skill","list","--all"]),child=spawn7(launch.command,launch.args,{cwd,stdio:["ignore","pipe","pipe"],windowsHide:!0,env:prependNodePath()}),stdout="",stderr="",settled=!1,finish=error48=>{settled||(settled=!0,clearTimeout(timer),error48?reject(error48):resolve2(stdout))},timer=setTimeout(()=>{child.kill("SIGTERM"),finish(new Error("Timed out while listing Qoder skills."))},REQUEST_TIMEOUT_MS2);child.stdout.on("data",chunk=>{stdout.length<1e6&&(stdout+=chunk.toString("utf8"))}),child.stderr.on("data",chunk=>{stderr.length<1e4&&(stderr+=chunk.toString("utf8"))}),child.once("error",error48=>finish(error48)),child.once("exit",code=>{code===0?finish():finish(new Error(stderr.trim()||`Qoder skill list exited with code ${String(code)}`))})})}resolveExecutable(){return this.executableResolver.resolve()}resolveExecutableCandidates(){return executableFileCandidates(qoderExecutableCandidatePaths())}emit(event){for(let listener of this.listeners)listener(event)}};import fs8 from"node:fs";import os5 from"node:os";import path9 from"node:path";import{spawn as spawn8}from"node:child_process";var COMMAND_TIMEOUT_MS=12e4,MAX_OUTPUT_LENGTH=1e6,ANSI_ESCAPE_PATTERN=new RegExp(String.raw`\u001B\[[0-?]*[ -/]*[@-~]`,"g");function existingExecutable(candidates,existsSync7){return candidates.find(candidate=>candidate&&existsSync7(candidate))??null}function stripAnsi(value){return value.replace(ANSI_ESCAPE_PATTERN,"")}function resolveAliSkillsExecutable(options={}){let env=options.env??process.env,home=(options.homedir??os5.homedir)(),platform=options.platform??process.platform,existsSync7=options.existsSync??(candidate=>isExecutable(candidate,platform)),processExecPath=options.processExecPath??process.execPath,readdirSync2=options.readdirSync??fs8.readdirSync,names=executableNames("ali-skills",platform),pathKey=Object.keys(env).find(key=>key.toLowerCase()==="path")||"PATH",pathCandidates=(env[pathKey]||"").split(path9.delimiter).filter(Boolean).flatMap(directory=>names.map(name=>path9.join(directory,name))),candidates=[env.MEARL_ALI_SKILLS_PATH,...names.map(name=>path9.join(path9.dirname(processExecPath),name)),...names.map(name=>path9.join(home,".local","bin",name)),...names.map(name=>path9.join(home,".npm-global","bin",name)),...names.map(name=>path9.join(home,".volta","bin",name)),...names.map(name=>path9.join(home,".local","share","pnpm",name)),...names.map(name=>path9.join(home,"Library","pnpm",name)),...versionManagerExecutableCandidates(home,names,{env,platform,readdirSync:readdirSync2}),...platform==="win32"?[]:["/opt/homebrew/bin/ali-skills","/usr/local/bin/ali-skills","/usr/bin/ali-skills"],...pathCandidates];return existingExecutable(candidates,existsSync7)}var runAliSkillsCli=(executable,args,cwd)=>new Promise((resolve2,reject)=>{let launch=resolveSpawnCommand(executable,args),child=spawn8(launch.command,launch.args,{cwd,env:prependNodePath(prependCommandPath(executable)),stdio:["ignore","pipe","pipe"],windowsHide:!0}),stdout="",stderr="",settled=!1,append=(current,chunk)=>current.length>=MAX_OUTPUT_LENGTH?current:(current+chunk.toString("utf8")).slice(0,MAX_OUTPUT_LENGTH),finish=error48=>{settled||(settled=!0,clearTimeout(timer),error48?reject(error48):resolve2({stdout,stderr}))},timer=setTimeout(()=>{child.kill("SIGTERM"),finish(new Error(`Ali Skills command timed out after ${COMMAND_TIMEOUT_MS/1e3}s`))},COMMAND_TIMEOUT_MS);child.stdout.on("data",chunk=>stdout=append(stdout,chunk)),child.stderr.on("data",chunk=>stderr=append(stderr,chunk)),child.once("error",error48=>finish(error48)),child.once("exit",code=>{if(code===0){finish();return}let detail=stripAnsi(stderr||stdout).trim();finish(new Error(detail||`Ali Skills command exited with code ${String(code)}`))})});var SETUP_COMMAND="npm install -g ali-skills";var runSkillCli=runAliSkillsCli;function validateProvider(value){if(value!=="codex"&&value!=="qoder")throw new Error("Skill provider must be codex or qoder");return value}function validateScope(value){if(value!=="project"&&value!=="global")throw new Error("Skill scope must be project or global");return value}function validateArgument(value,label){if(typeof value!="string"||!value.trim()||/[\0\r\n]/.test(value))throw new Error(`${label} is invalid`);return value.trim()}function scopeCwd(scope,cwd){if(scope!=="global")return resolveWorkspaceDirectory(cwd)}function parseSkillUpdates(stdout,scope){let clean=stripAnsi(stdout),updates=[],pattern=/^\s*↑\s+([^\s(]+)(?:\s+\(([^)]+)\))?/gm;for(let match of clean.matchAll(pattern))match[1]&&updates.push({name:match[1],...match[2]?{source:match[2]}:{},scope});return updates}var AgentSkillManager=class{executableResolver;run;constructor(options={}){this.executableResolver=options.resolveExecutable??resolveAliSkillsExecutable,this.run=options.run??runSkillCli}async getStatus(){let executable=this.executableResolver();if(!executable)return{available:!1,error:"Ali Skills CLI is not installed",setupCommand:SETUP_COMMAND};try{let{stdout,stderr}=await this.run(executable,["--version"]),version2=stripAnsi(stdout||stderr).trim().split(/\s+/)[0];return{available:!0,executable,...version2?{version:version2}:{},setupCommand:SETUP_COMMAND}}catch(error48){return{available:!1,executable,error:error48 instanceof Error?error48.message:String(error48),setupCommand:SETUP_COMMAND}}}async install(data){let{executable,providerId,scope,cwd}=await this.mutationContext(data),source=validateArgument(data.source,"Skill source"),name=validateArgument(data.name,"Skill name"),args=["add",source,"--skill",name,"--agent",providerId,scope==="global"?"--global":"--project","--full-depth","--yes"],output=await this.run(executable,args,cwd);return{ok:!0,output:stripAnsi(output.stdout||output.stderr).trim()}}async checkUpdates(data){validateProvider(data.providerId);let cli=await this.getStatus();if(!cli.available||!cli.executable)return{cli,updates:[]};let commands=[this.run(cli.executable,["check","--global"]).then(output=>parseSkillUpdates(`${output.stdout}
646
+ ${message}`:message,promptContent=[...prompt?[{type:"text",text:prompt}]:[],...images.map(image=>({type:"image",data:image.data,mimeType:image.mimeType}))];return session.instructionsSent=!0,this.emit({providerSessionId,kind:"notification",method:"turn/started",params:{turn:{id:turnId,status:"inProgress"}}}),this.runPrompt(providerSessionId,session,turn,promptContent),{turnId}}async interrupt(providerSessionId,_turnId){await this.ensureStarted(),await this.getContext().notify(methods.agent.session.cancel,{sessionId:providerSessionId}),this.cancelPermissions(providerSessionId)}async closeSession(providerSessionId){this.sessions.get(providerSessionId)?.activeTurn&&this.context&&await this.context.notify(methods.agent.session.cancel,{sessionId:providerSessionId}).catch(()=>{}),this.cancelPermissions(providerSessionId),this.sessions.delete(providerSessionId)}respondToRequest(requestId,result){let pending=this.pendingPermissions.get(requestId);if(!pending)throw new Error(`Unknown Qoder permission request: ${String(requestId)}`);if(!isPermissionResponse(result))throw new Error("Invalid Qoder permission response");this.pendingPermissions.delete(requestId),pending.resolve(result),this.emit({providerSessionId:pending.providerSessionId,kind:"notification",method:"serverRequest/resolved",params:{requestId}})}dispose(){for(let pending of this.pendingPermissions.values())pending.resolve({outcome:{outcome:"cancelled"}});this.pendingPermissions.clear(),this.historyCaptures.clear(),this.sessions.clear();let connection=this.connection,child=this.process;this.process=null,this.connection=null,this.context=null,this.initializeResponse=null,this.startPromise=null,this.executableResolver.reset(),connection?.close(new Error("Qoder provider disposed")),child&&!child.killed&&child.kill("SIGTERM")}async ensureStarted(){if(!(this.process&&!this.process.killed&&this.context)){if(this.startPromise)return this.startPromise;this.startPromise=this.start();try{await this.startPromise}catch(error48){throw this.startPromise=null,error48}}}async start(){let resolution=await this.resolveExecutable();if(resolution.executable===null)throw new Error(resolution.error);let{executable}=resolution,launch=resolveSpawnCommand(executable,["--acp"]),child=spawn7(launch.command,launch.args,{stdio:["pipe","pipe","pipe"],windowsHide:!0,env:prependNodePath()});this.process=child,child.stderr.on("data",chunk=>{let message=chunk.toString("utf8").trim();message&&this.log("[Qoder]",message)}),child.once("error",error48=>this.handleExit(child,error48)),child.once("exit",(code,signal)=>{this.handleExit(child,new Error(`Qoder ACP exited (code=${String(code)}, signal=${String(signal)})`))});let stream=ndJsonStream(Writable.toWeb(child.stdin),Readable.toWeb(child.stdout)),connection=client({name:"mearl"}).onRequest(methods.client.session.requestPermission,context=>this.handlePermissionRequest(context.requestId,context.params)).onNotification(methods.client.session.update,context=>{this.handleSessionUpdate(context.params)}).connect(stream);this.connection=connection,this.context=connection.agent;try{this.initializeResponse=await this.withTimeout(connection.agent.request(methods.agent.initialize,{protocolVersion:PROTOCOL_VERSION,clientCapabilities:{},clientInfo:{name:"mearl",title:"Mearl",version:this.clientVersion}}),"Qoder ACP initialization")}catch(error48){throw this.process=null,this.connection=null,this.context=null,this.executableResolver.reset(),connection.close(error48),child.killed||child.kill("SIGTERM"),new Error(`Qoder ACP failed to initialize. Start "qodercli", enter "/login", and try again: ${errorMessage(error48)}`,{cause:error48})}}async runPrompt(providerSessionId,session,turn,prompt){try{let response=await this.getContext().request(methods.agent.session.prompt,{sessionId:providerSessionId,prompt});turn.reasoningStarted&&this.emit({providerSessionId,kind:"notification",method:"item/completed",params:{turnId:turn.id,item:{id:turn.reasoningItemId,type:"reasoning",summary:turn.reasoningText?[turn.reasoningText]:[]}}}),turn.assistantText&&this.emit({providerSessionId,kind:"notification",method:"item/completed",params:{turnId:turn.id,item:{id:turn.assistantItemId,type:"agentMessage",text:turn.assistantText}}}),response.usage&&this.emit({providerSessionId,kind:"notification",method:"thread/tokenUsage/updated",params:{turnId:turn.id,usage:response.usage}}),this.emit({providerSessionId,kind:"notification",method:"turn/completed",params:{turn:{id:turn.id,status:response.stopReason==="cancelled"?"cancelled":"completed",stopReason:response.stopReason}}})}catch(error48){this.emit({providerSessionId,kind:"error",method:"chat/error",params:{turnId:turn.id,message:errorMessage(error48)}}),this.emit({providerSessionId,kind:"notification",method:"turn/completed",params:{turn:{id:turn.id,status:"failed"}}})}finally{session.activeTurn===turn&&delete session.activeTurn}}handleSessionUpdate(notification){let historyCapture=this.historyCaptures.get(notification.sessionId);if(historyCapture){captureHistoryUpdate(historyCapture,notification.update);return}let session=this.sessions.get(notification.sessionId),update=notification.update;if(update.sessionUpdate==="config_option_update"){let model=qoderModelInfo(update.configOptions);session&&(model?session.model=model:delete session.model),this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"session/model/updated",params:{model:model??null}}),this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"qoder/sessionUpdate",params:notification});return}let turn=session?.activeTurn;if(!turn){this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"qoder/sessionUpdate",params:notification});return}switch(update.sessionUpdate){case"agent_message_chunk":{let delta=contentText(update.content);if(!delta)return;turn.assistantText+=delta,this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"item/agentMessage/delta",params:{turnId:turn.id,itemId:turn.assistantItemId,delta}});return}case"agent_thought_chunk":{let delta=contentText(update.content);if(!delta)return;turn.reasoningStarted||(turn.reasoningStarted=!0,this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"item/started",params:{turnId:turn.id,item:{id:turn.reasoningItemId,type:"reasoning",summary:[]}}})),turn.reasoningText+=delta,this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"item/reasoning/summaryTextDelta",params:{turnId:turn.id,itemId:turn.reasoningItemId,delta}});return}case"tool_call":turn.toolTitles.set(update.toolCallId,update.title),this.emitToolUpdate(notification.sessionId,turn,update,update.title);return;case"tool_call_update":{let title=update.title||turn.toolTitles.get(update.toolCallId)||update.toolCallId;turn.toolTitles.set(update.toolCallId,title),this.emitToolUpdate(notification.sessionId,turn,update,title);return}case"plan":{let text=planText(update.entries);this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"item/completed",params:{turnId:turn.id,item:{id:turn.planItemId,type:"plan",text}}});return}case"usage_update":this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"thread/tokenUsage/updated",params:{turnId:turn.id,usage:update}});return;default:this.emit({providerSessionId:notification.sessionId,kind:"notification",method:"qoder/sessionUpdate",params:{turnId:turn.id,update}})}}emitToolUpdate(providerSessionId,turn,update,title){let completed=update.status==="completed"||update.status==="failed";this.emit({providerSessionId,kind:"notification",method:completed?"item/completed":"item/started",params:{turnId:turn.id,item:{id:update.toolCallId,type:"dynamicToolCall",namespace:"qoder",tool:title,status:update.status||(completed?"completed":"inProgress"),kind:update.kind,rawInput:update.rawInput,rawOutput:update.rawOutput,content:update.content,locations:update.locations}}})}handlePermissionRequest(requestId,request){return requestId===null?Promise.resolve({outcome:{outcome:"cancelled"}}):new Promise(resolve2=>{this.pendingPermissions.set(requestId,{providerSessionId:request.sessionId,resolve:resolve2}),this.emit({providerSessionId:request.sessionId,kind:"request",method:"qoder/requestPermission",params:request,requestId})})}cancelPermissions(providerSessionId){for(let[requestId,pending]of this.pendingPermissions)pending.providerSessionId===providerSessionId&&(this.pendingPermissions.delete(requestId),pending.resolve({outcome:{outcome:"cancelled"}}))}handleExit(child,error48){if(this.process===child){this.process=null,this.connection?.close(error48),this.connection=null,this.context=null,this.initializeResponse=null,this.startPromise=null,this.executableResolver.reset();for(let providerSessionId of this.sessions.keys())this.cancelPermissions(providerSessionId),this.emit({providerSessionId,kind:"error",method:"provider/exited",params:{message:error48.message}})}}getContext(){if(!this.context)throw new Error("Qoder ACP is not running");return this.context}assertImageInputSupported(){if(!this.initializeResponse?.agentCapabilities?.promptCapabilities?.image)throw new Error("This Qoder CLI version does not support image prompts")}withTimeout(promise2,operation){return new Promise((resolve2,reject)=>{let timer=setTimeout(()=>reject(new Error(`${operation} timed out`)),REQUEST_TIMEOUT_MS2);promise2.then(result=>{clearTimeout(timer),resolve2(result)},error48=>{clearTimeout(timer),reject(error48)})})}probeExecutable(executable){return probeAgentExecutable(executable,{args:["--version"],timeoutMs:PROBE_TIMEOUT_MS2,timeoutError:"Timed out while checking Qoder CLI availability.",startError:error48=>`Failed to start Qoder CLI: ${error48.message}`,exitError:code=>`Qoder CLI check exited with code ${String(code)}`})}runSkillsList(executable,cwd){return new Promise((resolve2,reject)=>{let launch=resolveSpawnCommand(executable,["skill","list","--all"]),child=spawn7(launch.command,launch.args,{cwd,stdio:["ignore","pipe","pipe"],windowsHide:!0,env:prependNodePath()}),stdout="",stderr="",settled=!1,finish=error48=>{settled||(settled=!0,clearTimeout(timer),error48?reject(error48):resolve2(stdout))},timer=setTimeout(()=>{child.kill("SIGTERM"),finish(new Error("Timed out while listing Qoder skills."))},REQUEST_TIMEOUT_MS2);child.stdout.on("data",chunk=>{stdout.length<1e6&&(stdout+=chunk.toString("utf8"))}),child.stderr.on("data",chunk=>{stderr.length<1e4&&(stderr+=chunk.toString("utf8"))}),child.once("error",error48=>finish(error48)),child.once("exit",code=>{code===0?finish():finish(new Error(stderr.trim()||`Qoder skill list exited with code ${String(code)}`))})})}resolveExecutable(){return this.executableResolver.resolve()}resolveExecutableCandidates(){return executableFileCandidates(qoderExecutableCandidatePaths())}emit(event){for(let listener of this.listeners)listener(event)}};import fs8 from"node:fs";import os5 from"node:os";import path9 from"node:path";import{spawn as spawn8}from"node:child_process";var COMMAND_TIMEOUT_MS=12e4,MAX_OUTPUT_LENGTH=1e6,ANSI_ESCAPE_PATTERN=new RegExp(String.raw`\u001B\[[0-?]*[ -/]*[@-~]`,"g");function existingExecutable(candidates,existsSync7){return candidates.find(candidate=>candidate&&existsSync7(candidate))??null}function stripAnsi(value){return value.replace(ANSI_ESCAPE_PATTERN,"")}function resolveAliSkillsExecutable(options={}){let env=options.env??process.env,home=(options.homedir??os5.homedir)(),platform=options.platform??process.platform,existsSync7=options.existsSync??(candidate=>isExecutable(candidate,platform)),processExecPath=options.processExecPath??process.execPath,readdirSync2=options.readdirSync??fs8.readdirSync,names=executableNames("ali-skills",platform),pathKey=Object.keys(env).find(key=>key.toLowerCase()==="path")||"PATH",pathCandidates=(env[pathKey]||"").split(path9.delimiter).filter(Boolean).flatMap(directory=>names.map(name=>path9.join(directory,name))),candidates=[env.MEARL_ALI_SKILLS_PATH,...names.map(name=>path9.join(path9.dirname(processExecPath),name)),...names.map(name=>path9.join(home,".local","bin",name)),...names.map(name=>path9.join(home,".npm-global","bin",name)),...names.map(name=>path9.join(home,".volta","bin",name)),...names.map(name=>path9.join(home,".local","share","pnpm",name)),...names.map(name=>path9.join(home,"Library","pnpm",name)),...versionManagerExecutableCandidates(home,names,{env,platform,readdirSync:readdirSync2}),...platform==="win32"?[]:["/opt/homebrew/bin/ali-skills","/usr/local/bin/ali-skills","/usr/bin/ali-skills"],...pathCandidates];return existingExecutable(candidates,existsSync7)}var runAliSkillsCli=(executable,args,cwd)=>new Promise((resolve2,reject)=>{let launch=resolveSpawnCommand(executable,args),child=spawn8(launch.command,launch.args,{cwd,env:prependNodePath(prependCommandPath(executable)),stdio:["ignore","pipe","pipe"],windowsHide:!0}),stdout="",stderr="",settled=!1,append=(current,chunk)=>current.length>=MAX_OUTPUT_LENGTH?current:(current+chunk.toString("utf8")).slice(0,MAX_OUTPUT_LENGTH),finish=error48=>{settled||(settled=!0,clearTimeout(timer),error48?reject(error48):resolve2({stdout,stderr}))},timer=setTimeout(()=>{child.kill("SIGTERM"),finish(new Error(`Ali Skills command timed out after ${COMMAND_TIMEOUT_MS/1e3}s`))},COMMAND_TIMEOUT_MS);child.stdout.on("data",chunk=>stdout=append(stdout,chunk)),child.stderr.on("data",chunk=>stderr=append(stderr,chunk)),child.once("error",error48=>finish(error48)),child.once("exit",code=>{if(code===0){finish();return}let detail=stripAnsi(stderr||stdout).trim();finish(new Error(detail||`Ali Skills command exited with code ${String(code)}`))})});var SETUP_COMMAND="npm install -g ali-skills";var runSkillCli=runAliSkillsCli;function validateProvider(value){if(value!=="codex"&&value!=="qoder")throw new Error("Skill provider must be codex or qoder");return value}function validateScope(value){if(value!=="project"&&value!=="global")throw new Error("Skill scope must be project or global");return value}function validateArgument(value,label){if(typeof value!="string"||!value.trim()||/[\0\r\n]/.test(value))throw new Error(`${label} is invalid`);return value.trim()}function scopeCwd(scope,cwd){if(scope!=="global")return resolveWorkspaceDirectory(cwd)}function parseSkillUpdates(stdout,scope){let clean=stripAnsi(stdout),updates=[],pattern=/^\s*↑\s+([^\s(]+)(?:\s+\(([^)]+)\))?/gm;for(let match of clean.matchAll(pattern))match[1]&&updates.push({name:match[1],...match[2]?{source:match[2]}:{},scope});return updates}var AgentSkillManager=class{executableResolver;run;constructor(options={}){this.executableResolver=options.resolveExecutable??resolveAliSkillsExecutable,this.run=options.run??runSkillCli}async getStatus(){let executable=this.executableResolver();if(!executable)return{available:!1,error:"Ali Skills CLI is not installed",setupCommand:SETUP_COMMAND};try{let{stdout,stderr}=await this.run(executable,["--version"]),version2=stripAnsi(stdout||stderr).trim().split(/\s+/)[0];return{available:!0,executable,...version2?{version:version2}:{},setupCommand:SETUP_COMMAND}}catch(error48){return{available:!1,executable,error:error48 instanceof Error?error48.message:String(error48),setupCommand:SETUP_COMMAND}}}async install(data){let{executable,providerId,scope,cwd}=await this.mutationContext(data),source=validateArgument(data.source,"Skill source"),name=validateArgument(data.name,"Skill name"),args=["add",source,"--skill",name,"--agent",providerId,scope==="global"?"--global":"--project","--full-depth","--install-mode","symlink","--yes"],output=await this.run(executable,args,cwd);return{ok:!0,output:stripAnsi(output.stdout||output.stderr).trim()}}async checkUpdates(data){validateProvider(data.providerId);let cli=await this.getStatus();if(!cli.available||!cli.executable)return{cli,updates:[]};let commands=[this.run(cli.executable,["check","--global"]).then(output=>parseSkillUpdates(`${output.stdout}
647
647
  ${output.stderr}`,"global"))];if(data.cwd!==void 0&&data.cwd!==null&&data.cwd!==""){let cwd=resolveWorkspaceDirectory(data.cwd);commands.push(this.run(cli.executable,["check"],cwd).then(output=>parseSkillUpdates(`${output.stdout}
648
648
  ${output.stderr}`,"project")))}return{cli,updates:(await Promise.all(commands)).flat()}}async update(data){let{executable,scope,cwd}=await this.mutationContext(data),args=["update",validateArgument(data.name,"Skill name"),...scope==="global"?["--global"]:[],"--yes"],output=await this.run(executable,args,cwd);return{ok:!0,output:stripAnsi(output.stdout||output.stderr).trim()}}async uninstall(data){let{executable,providerId,scope,cwd}=await this.mutationContext(data),args=["remove",validateArgument(data.name,"Skill name"),"--agent",providerId,...scope==="global"?["--global"]:[],"--yes"],output=await this.run(executable,args,cwd);return{ok:!0,output:stripAnsi(output.stdout||output.stderr).trim()}}async mutationContext(data){let providerId=validateProvider(data.providerId),scope=validateScope(data.scope),cwd=scopeCwd(scope,data.cwd),cli=await this.getStatus();if(!cli.available||!cli.executable)throw new Error(`${cli.error||"Ali Skills CLI is unavailable"}. Run: ${cli.setupCommand}`);return{executable:cli.executable,providerId,scope,...cwd?{cwd}:{}}}};var INTERNAL_EXTENSION_ACTIONS=new Set(["__export_cookies","__send_request_file_start","__send_request_file_chunk","__send_request_file_abort","__send_request_with_files","browser_provider_list","browser_provider_configure","browser_manager_start","browser_manager_sync_cookies"]);function isInternalExtensionAction(action){return typeof action=="string"&&INTERNAL_EXTENSION_ACTIONS.has(action)}function createExtensionRequestMessage(id,action,data,trustedInternal=!1,controlSource="local"){return{id,action,data,controlSource:toControlSourceWireValue(controlSource),...trustedInternal?{trustedInternal:!0}:{}}}import fs10 from"node:fs";import path11 from"node:path";import{fileURLToPath}from"node:url";import fs9 from"node:fs";import path10 from"node:path";function normalizePath(filePath){return filePath.replaceAll("\\","/").replace(/\/+$/,"")}function firstExisting(candidates,existsSync7){return candidates.find(candidate=>existsSync7(candidate))}function fallbackCommand(name,platform){return platform!=="win32"?name:name==="bun"||name==="volta"?`${name}.exe`:`${name}.cmd`}function resolvePackageManagerInstallation(packageRoot,packageName,options={}){let env=options.env??process.env,platform=options.platform??process.platform,existsSync7=options.existsSync??(candidate=>isExecutable(candidate,platform)),processExecPath=options.processExecPath??process.execPath,normalizedRoot=normalizePath(path10.resolve(packageRoot)),lowerRoot=normalizedRoot.toLowerCase(),lowerPackageName=packageName.toLowerCase(),pnpmGlobalRoot=/\/global\/\d+\/(?:\.pnpm\/.*\/)?node_modules\//.exec(lowerRoot);if(pnpmGlobalRoot&&lowerRoot.endsWith(`/node_modules/${lowerPackageName}`)){let pnpmHome=normalizedRoot.slice(0,pnpmGlobalRoot.index);return{manager:"pnpm",command:firstExisting([...executableNames("pnpm",platform).map(name=>path10.join(pnpmHome,name)),...executableNames("pnpm",platform).map(name=>path10.join(path10.dirname(processExecPath),name))],existsSync7)??fallbackCommand("pnpm",platform),binDirectory:pnpmHome}}let bunMarker=`/.bun/install/global/node_modules/${lowerPackageName}`;if(lowerRoot.endsWith(bunMarker)){let bunHome=normalizedRoot.slice(0,lowerRoot.indexOf("/.bun/"));return{manager:"bun",command:firstExisting(executableNames("bun",platform).map(name=>path10.join(bunHome,".bun","bin",name)),existsSync7)??fallbackCommand("bun",platform),binDirectory:path10.join(bunHome,".bun","bin")}}if((lowerRoot.includes("/.config/yarn/global/node_modules/")||lowerRoot.includes("/.yarn/global/node_modules/")||lowerRoot.includes("/yarn/global/node_modules/")||lowerRoot.includes("/yarn/data/global/node_modules/"))&&lowerRoot.endsWith(`/node_modules/${lowerPackageName}`)){let layout=[{marker:"/.config/yarn/global/",binSegments:[".yarn","bin"]},{marker:"/.yarn/global/",binSegments:[".yarn","bin"]},{marker:"/yarn/data/global/",binSegments:["yarn","bin"]},{marker:"/yarn/global/",binSegments:["yarn","bin"]}].find(candidate=>lowerRoot.includes(candidate.marker)),prefix=normalizedRoot.slice(0,lowerRoot.indexOf(layout.marker)),binDirectory=path10.join(prefix,...layout.binSegments);return{manager:"yarn",command:firstExisting([...executableNames("yarn",platform).map(name=>path10.join(binDirectory,name)),...executableNames("yarn",platform).map(name=>path10.join(path10.dirname(processExecPath),name))],existsSync7)??findExecutable("yarn",{env,existsSync:existsSync7,platform})??fallbackCommand("yarn",platform),binDirectory}}if(lowerRoot.includes("/.volta/tools/image/packages/")&&lowerRoot.endsWith(`/lib/node_modules/${lowerPackageName}`)){let voltaHome=normalizedRoot.slice(0,lowerRoot.indexOf("/.volta/"));return{manager:"volta",command:firstExisting(executableNames("volta",platform).map(name=>path10.join(voltaHome,".volta","bin",name)),existsSync7)??fallbackCommand("volta",platform),binDirectory:path10.join(voltaHome,".volta","bin")}}let npmMarker=`/lib/node_modules/${lowerPackageName}`;if(lowerRoot.endsWith(npmMarker)){let prefix=normalizedRoot.slice(0,lowerRoot.length-npmMarker.length);return{manager:"npm",command:firstExisting([...executableNames("npm",platform).map(name=>path10.join(prefix,"bin",name)),...executableNames("npm",platform).map(name=>path10.join(path10.dirname(processExecPath),name))],existsSync7)??fallbackCommand("npm",platform),binDirectory:path10.join(prefix,"bin")}}if(platform==="win32"&&env.APPDATA){let npmRoot=normalizePath(path10.join(env.APPDATA,"npm","node_modules",packageName));if(lowerRoot===npmRoot.toLowerCase()){let binDirectory=path10.join(env.APPDATA,"npm");return{manager:"npm",command:firstExisting([...executableNames("npm",platform).map(name=>path10.join(binDirectory,name)),...executableNames("npm",platform).map(name=>path10.join(path10.dirname(processExecPath),name))],existsSync7)??fallbackCommand("npm",platform),binDirectory}}}return null}var RELEASE_VERSION_PATTERN=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-beta\.(0|[1-9]\d*))?$/,RELEASE_LINE_PATTERN=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.x$/;function parseComponent(value){let component=Number(value);return Number.isSafeInteger(component)&&component<=65535?component:null}function parseMearlReleaseVersion(value){if(typeof value!="string"||value.length>64)return null;let match=RELEASE_VERSION_PATTERN.exec(value);if(!match)return null;let major=parseComponent(match[1]),minor=parseComponent(match[2]),patch=parseComponent(match[3]),beta=match[4]===void 0?void 0:parseComponent(match[4]);return major===null||minor===null||patch===null||beta===null?null:{raw:value,major,minor,patch,channel:beta===void 0?"stable":"beta",...beta===void 0?{}:{beta}}}function isMearlReleaseVersion(value){return parseMearlReleaseVersion(value)!==null}function parseMearlReleaseLine(value){if(typeof value!="string"||value.length>64)return null;let match=RELEASE_LINE_PATTERN.exec(value);if(!match)return null;let major=parseComponent(match[1]),minor=parseComponent(match[2]);return major===null||minor===null?null:{raw:value,major,minor}}function mearlReleaseLineForVersion(version2){let parsed=parseMearlReleaseVersion(version2);if(!parsed)throw new Error("A valid Mearl release version is required");return`${parsed.major}.${parsed.minor}.x`}function isMearlReleaseVersionInLine(version2,releaseLine){let parsedVersion=parseMearlReleaseVersion(version2),parsedLine=parseMearlReleaseLine(releaseLine);return parsedVersion!==null&&parsedLine!==null&&parsedVersion.major===parsedLine.major&&parsedVersion.minor===parsedLine.minor}function compareMearlReleaseLines(leftVersion,rightVersion){let left=parseMearlReleaseVersion(leftVersion),right=parseMearlReleaseVersion(rightVersion);if(!left||!right)throw new Error("Mearl release-line comparison requires valid release versions");for(let key of["major","minor"]){if(left[key]<right[key])return-1;if(left[key]>right[key])return 1}return 0}function compareMearlReleaseVersions(leftVersion,rightVersion){let left=parseMearlReleaseVersion(leftVersion),right=parseMearlReleaseVersion(rightVersion);if(!left||!right)throw new Error("Mearl release comparison requires valid release versions");for(let key of["major","minor","patch"]){if(left[key]<right[key])return-1;if(left[key]>right[key])return 1}if(left.channel!==right.channel)return left.channel==="stable"?1:-1;let leftBeta=left.beta??0,rightBeta=right.beta??0;return leftBeta<rightBeta?-1:leftBeta>rightBeta?1:0}var NATIVE_HOST_UPDATE_TIMEOUT_MS=5*6e4,UPDATE_LOCK_PATH=path11.join(DAEMON_HOME,"native-host-update.lock"),UPDATE_LOCK_POLL_MS=500,INVALID_UPDATE_LOCK_GRACE_MS=1e4,NativeHostUpdateFailure=class extends Error{constructor(updateError,message){super(message);this.updateError=updateError;this.name="NativeHostUpdateFailure"}};function wait(delayMs){return new Promise(resolve2=>setTimeout(resolve2,delayMs))}function readLockState(lockPath){try{let stat2=fs10.lstatSync(lockPath);if(!stat2.isFile()||stat2.isSymbolicLink()||stat2.size>4096)return{modifiedAt:stat2.mtimeMs,owner:null};try{let value=JSON.parse(fs10.readFileSync(lockPath,"utf8")),owner=Number.isInteger(value.pid)&&value.pid>0&&typeof value.startedAt=="number"&&Number.isFinite(value.startedAt)&&value.startedAt>0?{pid:value.pid,startedAt:value.startedAt}:null;return{modifiedAt:stat2.mtimeMs,owner}}catch{return{modifiedAt:stat2.mtimeMs,owner:null}}}catch{return null}}async function withNativeHostUpdateLock(lockPath,targetVersion,operation){fs10.mkdirSync(path11.dirname(lockPath),{recursive:!0,mode:448});let startedAt=Date.now(),owner=`${JSON.stringify({pid:process.pid,startedAt,targetVersion})}
649
649
  `;for(;;)try{let handle=fs10.openSync(lockPath,"wx",384);try{fs10.writeFileSync(handle,owner,"utf8")}finally{fs10.closeSync(handle)}break}catch(error48){if(!(error48 instanceof Error)||!("code"in error48)||error48.code!=="EEXIST")throw error48;let existing=readLockState(lockPath),staleAfter=existing?.owner?NATIVE_HOST_UPDATE_TIMEOUT_MS:INVALID_UPDATE_LOCK_GRACE_MS,referenceTime=existing?.owner?.startedAt??existing?.modifiedAt,expired=referenceTime!==void 0&&Date.now()-referenceTime>staleAfter,ownerAlive=existing?.owner?isAlive(existing.owner.pid):!1;if(expired&&!ownerAlive)try{fs10.unlinkSync(lockPath);continue}catch{}if(Date.now()-startedAt>NATIVE_HOST_UPDATE_TIMEOUT_MS+1e4)throw new NativeHostUpdateFailure("lock-timeout","Another Mearl Native Host update did not finish in time");await wait(UPDATE_LOCK_POLL_MS)}try{return await operation()}finally{try{fs10.readFileSync(lockPath,"utf8")===owner&&fs10.unlinkSync(lockPath)}catch{}}}function classifyUpdateFailure(output){return/\b(?:E404|ETARGET)\b|no matching version found|404\s+not found|package.+not found/i.test(output)?"package-unavailable":/\b(?:EAI_AGAIN|ECONNREFUSED|ECONNRESET|ENETUNREACH|ENOTFOUND|ERR_SOCKET_TIMEOUT)\b|fetch failed|network request/i.test(output)?"network":"setup-failed"}function nativeHostUpdateCommand(targetVersion){if(!isMearlReleaseVersion(targetVersion))throw new Error("The Native Host update target must be a valid Mearl release");let releaseLine=mearlReleaseLineForVersion(targetVersion);return{args:["--yes",`@mearl/setup@${releaseLine}`,"update","--local","--yes","--target-line",releaseLine,"--skip-skill"]}}function currentNativeHostPackageRoot(entryPath){let current=path11.dirname(entryPath);for(let depth=0;depth<12;depth+=1){try{if(JSON.parse(fs10.readFileSync(path11.join(current,"package.json"),"utf8")).name==="@mearl/native-host")return current}catch{}let parent=path11.dirname(current);if(parent===current)break;current=parent}return null}function currentPackageManagerInstallation(environment,nodePath,platform){let packageRoot=currentNativeHostPackageRoot(fileURLToPath(import.meta.url));return packageRoot?resolvePackageManagerInstallation(packageRoot,"@mearl/native-host",{env:environment,platform,processExecPath:nodePath}):null}async function installedLocalPackagesMatch(targetReleaseLine,environment,runner){let installedVersions=[];for(let executableName of["mearl-native-host","mearl"]){let executable=findExecutable(executableName,{env:environment});if(!executable)return!1;try{let result=await runner(executable,["--version"],{capture:!0,env:environment,timeoutMs:5e3}),installedVersion=result.stdout.trim();if(result.code!==0||!isMearlReleaseVersionInLine(installedVersion,targetReleaseLine))return!1;installedVersions.push(installedVersion)}catch{return!1}}return new Set(installedVersions).size===1}async function runNativeHostUpdate(targetVersion,options={}){let command=nativeHostUpdateCommand(targetVersion),targetReleaseLine=mearlReleaseLineForVersion(targetVersion),environment=options.environment??process.env,platform=options.platform??process.platform,nodePath=options.nodePath??process.execPath,currentInstallation=currentPackageManagerInstallation(environment,nodePath,platform),managerCommand=options.packageManagerCommand===void 0?currentInstallation?.command??null:options.packageManagerCommand,packageBinDirectory=options.packageBinDirectory===void 0?currentInstallation?.binDirectory??null:options.packageBinDirectory,childEnvironment=prependNodePath(environment,nodePath);managerCommand&&(childEnvironment=prependCommandPath(managerCommand,childEnvironment)),packageBinDirectory&&(childEnvironment=prependPathDirectory(packageBinDirectory,childEnvironment));let packageRunner=options.packageRunner??findExecutable("npx",{env:childEnvironment,extraDirectories:[path11.dirname(nodePath)],platform});if(!packageRunner)throw new NativeHostUpdateFailure("package-unavailable","The package runner is unavailable");let runner=options.runner??runCommand,areTargetsInstalled=options.areTargetsInstalled??((version2,env)=>installedLocalPackagesMatch(version2,env,runner));await(options.withUpdateLock??((version2,operation)=>withNativeHostUpdateLock(options.updateLockPath??UPDATE_LOCK_PATH,version2,operation)))(targetVersion,async()=>{let result;try{result=await runner(packageRunner,command.args,{capture:!0,env:childEnvironment,timeoutMs:options.timeoutMs??NATIVE_HOST_UPDATE_TIMEOUT_MS})}catch(error48){let message=error48 instanceof Error?error48.message:String(error48),classified=classifyUpdateFailure(message);throw new NativeHostUpdateFailure(/timed out/i.test(message)?"timeout":classified==="setup-failed"?"unknown":classified,"The Native Host update process did not complete")}if(result.code!==0)throw new NativeHostUpdateFailure(classifyUpdateFailure(`${result.stderr.slice(0,8192)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/native-host",
3
- "version": "2.16.2",
3
+ "version": "2.16.3",
4
4
  "description": "Native Messaging Host for Mearl — bridges Chrome Extension and local socket server",
5
5
  "type": "module",
6
6
  "engines": {
@@ -41,11 +41,11 @@
41
41
  "@types/ws": "^8.18.1",
42
42
  "esbuild": "^0.25.2",
43
43
  "typescript": "^5.8.3",
44
- "@mearl/browser-core": "2.16.2",
45
- "@mearl/app-profiles": "2.16.2",
46
- "@mearl/provider": "2.16.2",
47
- "@mearl/daemon-core": "2.16.2",
48
- "@mearl/setup": "2.16.2"
44
+ "@mearl/app-profiles": "2.16.3",
45
+ "@mearl/browser-core": "2.16.3",
46
+ "@mearl/provider": "2.16.3",
47
+ "@mearl/daemon-core": "2.16.3",
48
+ "@mearl/setup": "2.16.3"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "rm -rf dist/ && node build.mjs",