@mearl/native-host 3.0.0-beta.2 → 3.0.0-beta.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.
- package/dist/index.js +10 -10
- package/dist/install.js +1 -1
- package/dist/plugin-task-host.js +3 -3
- package/dist/plugin-worker.js +2 -2
- package/package.json +9 -9
package/dist/install.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createRequire as __mearlCreateRequire } from 'node:module'; const requi
|
|
|
2
2
|
import fs4 from"node:fs";import path4 from"node:path";import{execFileSync as execFileSync2}from"node:child_process";import{createRequire}from"node:module";import{fileURLToPath}from"node:url";import fs from"node:fs";import path from"node:path";function resolveLauncherNodePaths(execPath){let nodePath=path.resolve(execPath),homebrewNode=nodePath.match(/^(.*)\/Cellar\/(node(?:@\d+(?:\.\d+)*)?)\/[^/]+\/bin\/node$/);if(!homebrewNode)return[nodePath];let[,prefix,formula]=homebrewNode,stablePath=path.join(prefix,"opt",formula,"bin","node");try{if(fs.accessSync(stablePath,fs.constants.X_OK),fs.statSync(stablePath).isFile()&&fs.realpathSync(stablePath)===fs.realpathSync(nodePath))return[stablePath,nodePath]}catch{}return[nodePath,stablePath]}function shellQuote(value){return`'${value.replaceAll("'","'\\''")}'`}function createLauncherScript(hostDir,hostPath,hostName,execPath=process.execPath){let nodePaths=process.platform==="win32"?[path.resolve(execPath)]:resolveLauncherNodePaths(execPath),metadataLine=`mearl-native-host-launcher: ${JSON.stringify({version:1,nodePaths,hostPath})}`;if(process.platform==="win32"){let launcherPath2=path.join(hostDir,`${hostName}-launcher.cmd`),content2=`@echo off\r
|
|
3
3
|
rem ${metadataLine}\r
|
|
4
4
|
"${nodePaths[0]}" "${hostPath}" %*\r
|
|
5
|
-
`;return fs.writeFileSync(launcherPath2,content2,"utf-8"),launcherPath2}let launcherPath=path.join(hostDir,`${hostName}-launcher.sh`),content=["#!/bin/sh",`# ${metadataLine}`,`HOST_SCRIPT=${shellQuote(hostPath)}`,'if [ ! -f "$HOST_SCRIPT" ] || [ ! -r "$HOST_SCRIPT" ]; then',` printf '%s\\n' "Mearl native host: runtime script is missing or unreadable: $HOST_SCRIPT" >&2`," printf '%s\\n' 'Run npx @mearl/setup update --local to repair the installation.' >&2"," exit 1","fi",`for NODE_BIN in ${nodePaths.map(shellQuote).join(" ")}; do`,' if [ -f "$NODE_BIN" ] && [ -x "$NODE_BIN" ]; then',' PATH="${NODE_BIN%/*}${PATH:+:$PATH}"'," export PATH",' exec "$NODE_BIN" "$HOST_SCRIPT" "$@"'," fi","done",`printf '%s\\n' ${shellQuote(`Mearl native host: no executable Node.js found. Tried: ${nodePaths.join(", ")}`)} >&2`,"printf '%s\\n' 'Run mearl-native-host init with a working Node.js, then reopen the extension.' >&2","exit 127",""].join(`
|
|
5
|
+
`;return fs.writeFileSync(launcherPath2,content2,"utf-8"),launcherPath2}let launcherPath=path.join(hostDir,`${hostName}-launcher.sh`),content=["#!/bin/sh",`# ${metadataLine}`,`HOST_SCRIPT=${shellQuote(hostPath)}`,'if [ ! -f "$HOST_SCRIPT" ] || [ ! -r "$HOST_SCRIPT" ]; then',` printf '%s\\n' "Mearl native host: runtime script is missing or unreadable: $HOST_SCRIPT" >&2`," printf '%s\\n' 'Run npx --@mearl:registry=https://registry.npmjs.org @mearl/setup update --local to repair the installation.' >&2"," exit 1","fi",`for NODE_BIN in ${nodePaths.map(shellQuote).join(" ")}; do`,' if [ -f "$NODE_BIN" ] && [ -x "$NODE_BIN" ]; then',' PATH="${NODE_BIN%/*}${PATH:+:$PATH}"'," export PATH",' exec "$NODE_BIN" "$HOST_SCRIPT" "$@"'," fi","done",`printf '%s\\n' ${shellQuote(`Mearl native host: no executable Node.js found. Tried: ${nodePaths.join(", ")}`)} >&2`,"printf '%s\\n' 'Run mearl-native-host init with a working Node.js, then reopen the extension.' >&2","exit 127",""].join(`
|
|
6
6
|
`);return fs.writeFileSync(launcherPath,content,"utf-8"),fs.chmodSync(launcherPath,493),launcherPath}import fs3 from"node:fs";import path3 from"node:path";import{execFileSync}from"node:child_process";import fs2 from"node:fs";import os from"node:os";import path2 from"node:path";var APP_BRIDGE_BEHAVIORS=["navigate","closeAll","closeBackData","postMessage","notifySubscribe","notifyUnsubscribe","notifyPost","sharedFileRead","sharedFileWrite","sharedDataGet","sharedDataSet","legacyBroadcastCreate","legacyBroadcastPost","legacyBroadcastClose","memoryGet","memorySet","memoryDelete","availabilityMap","userInfo","authCode","networkInfo","geolocation","permission","imagePicker","prefetch","fptTrack","mtopStream","previewFile","toast","unsupportedNativeInput","payment","userTrack","calendar","networkQualityMonitor","beaconRequest","voiceRecognition"],BEHAVIORS=new Set(APP_BRIDGE_BEHAVIORS);var textEncoder=new TextEncoder;var ALLOWED_ORIGINS=["*://localhost/*","*://127.0.0.1/*"],ALLOWED_ORIGINS_LIST=[...ALLOWED_ORIGINS];function scrollElementInPage(direction,distance,targetLabel,containerPolicy="self"){let element=this?.nodeType===3?this.parentElement:this,targetDocument=element?.nodeType===9?element:null;if(targetDocument&&(element=targetDocument.scrollingElement),!element||element.isConnected===!1)return{error:"Cannot resolve scroll container: "+targetLabel};let startingElement=element,ancestorDistance=0;function scrollMetrics(candidate){let scrollTop=Number(candidate?.scrollTop),scrollHeight=Number(candidate?.scrollHeight),clientHeight=Number(candidate?.clientHeight);if(!Number.isFinite(scrollTop)||!Number.isFinite(scrollHeight)||!Number.isFinite(clientHeight))return null;let ownerDocument=candidate.ownerDocument,isRootScroller=!!ownerDocument&&candidate===ownerDocument.scrollingElement,maxScrollTop=Math.max(0,scrollHeight-clientHeight),overflowAllowsScroll=!0;try{let overflowY=ownerDocument?.defaultView?.getComputedStyle(candidate).overflowY;!isRootScroller&&(overflowY==="visible"||overflowY==="clip")&&(overflowAllowsScroll=!1)}catch{}return{scrollTop,scrollHeight,clientHeight,maxScrollTop,isRootScroller,usable:isRootScroller||maxScrollTop>0&&overflowAllowsScroll}}function documentFallback(documentRef){let windowRef=documentRef.defaultView,visited=new Set,viewportWidth=Number(windowRef?.innerWidth)||Number(element?.clientWidth)||0,viewportHeight=Number(windowRef?.innerHeight)||Number(element?.clientHeight)||0,points=[[viewportWidth/2,viewportHeight/2],[viewportWidth/2,viewportHeight*.75],[viewportWidth/2,viewportHeight*.25]];function containerDepth(candidate){if(candidate===documentRef.body||candidate===documentRef.documentElement)return 0;let depth=0,cursor=candidate;for(;cursor&&cursor!==documentRef.body&&cursor!==documentRef.documentElement;)depth+=1,cursor=cursor.parentElement;return depth}function canMove(metrics2){return direction==="up"||direction==="top"?metrics2.scrollTop>0:metrics2.scrollTop<metrics2.maxScrollTop}let sampledPageContainer;for(let[x,y]of points){let candidate=null;try{candidate=documentRef.elementFromPoint?.(x,y)??null}catch{}for(;candidate&&candidate.nodeType===1;){if(!visited.has(candidate)){visited.add(candidate);let candidateMetrics=scrollMetrics(candidate);if(candidateMetrics?.usable&&candidateMetrics.maxScrollTop>0&&canMove(candidateMetrics)){let resolved={element:candidate,metrics:candidateMetrics,distance:containerDepth(candidate)};if(candidate===documentRef.body||candidate===documentRef.documentElement)sampledPageContainer??=resolved;else return resolved}}candidate=candidate.parentElement}}if(sampledPageContainer)return sampledPageContainer;let best,candidates=Array.from(documentRef.querySelectorAll?.("*")??[]).slice(0,1e4);for(let candidate of candidates){let candidateMetrics=scrollMetrics(candidate);if(!candidateMetrics?.usable||candidateMetrics.maxScrollTop<=0||!canMove(candidateMetrics))continue;let rect,style;try{rect=candidate.getBoundingClientRect(),style=windowRef?.getComputedStyle(candidate)}catch{continue}if(!rect||rect.width<=0||rect.height<=0||style?.display==="none"||style?.visibility==="hidden"||style?.visibility==="collapse")continue;let visibleWidth=Math.max(0,Math.min(rect.right,viewportWidth)-Math.max(rect.left,0)),visibleHeight=Math.max(0,Math.min(rect.bottom,viewportHeight)-Math.max(rect.top,0)),area=visibleWidth*visibleHeight;if(area<=0)continue;let depth=containerDepth(candidate);(!best||depth<best.distance||depth===best.distance&&area>best.area)&&(best={element:candidate,metrics:candidateMetrics,distance:depth,area})}return best?{element:best.element,metrics:best.metrics,distance:best.distance}:null}function selectorFor(candidate){let documentRef=candidate?.ownerDocument;if(!documentRef||candidate.nodeType!==1)return"";if(candidate===documentRef.documentElement)return"html";if(candidate===documentRef.body)return"body";let escapeCss=value=>documentRef.defaultView?.CSS?.escape?documentRef.defaultView.CSS.escape(value):value.replace(/[^a-zA-Z0-9_-]/g,character=>"\\"+character);if(candidate.id){let idSelector="#"+escapeCss(String(candidate.id));try{if(documentRef.querySelectorAll(idSelector).length===1)return idSelector}catch{}}let path5=[],cursor=candidate;for(;cursor&&cursor.nodeType===1&&path5.length<8;){let tag=String(cursor.tagName||"").toLowerCase();if(!tag)break;let parent=cursor.parentElement;if(!parent){path5.unshift(tag);break}let siblings=Array.from(parent.children).filter(sibling=>sibling.tagName===cursor.tagName),index=siblings.indexOf(cursor)+1;if(path5.unshift(siblings.length>1?tag+":nth-of-type("+index+")":tag),cursor=parent,cursor===documentRef.documentElement){path5.unshift("html");break}}return path5.join(" > ")}let metrics=scrollMetrics(element);if(!metrics)return{error:"Element is not scrollable: "+targetLabel};if(targetDocument){let fallback=documentFallback(targetDocument);fallback&&(element=fallback.element,metrics=fallback.metrics,ancestorDistance=fallback.distance)}if(!metrics.usable&&containerPolicy==="nearest"){let candidate=element.parentElement;for(;candidate;){ancestorDistance+=1;let candidateMetrics=scrollMetrics(candidate);if(candidateMetrics?.usable){element=candidate,metrics=candidateMetrics;break}candidate=candidate.parentElement}}if(!metrics.usable&&!metrics.isRootScroller)return{error:"Element is not scrollable: "+targetLabel+(containerPolicy==="nearest"?" (no scrollable ancestor found)":"")};let targetTop;switch(direction){case"top":targetTop=0;break;case"bottom":targetTop=metrics.maxScrollTop;break;case"up":targetTop=Math.max(0,metrics.scrollTop-Math.abs(distance));break;default:targetTop=Math.min(metrics.maxScrollTop,metrics.scrollTop+Math.abs(distance));break}if(typeof element.scrollTo=="function")try{element.scrollTo({top:targetTop,left:Number(element.scrollLeft)||0,behavior:"instant"})}catch{element.scrollTop=targetTop}else element.scrollTop=targetTop;let actualScrollTop=Number(element.scrollTop);return!metrics.isRootScroller&&targetTop!==metrics.scrollTop&&actualScrollTop===metrics.scrollTop?{error:"Element is not scrollable: "+targetLabel}:{scrollY:Math.round(actualScrollTop),scrollHeight:metrics.scrollHeight,clientHeight:metrics.clientHeight,maxScrollY:Math.round(metrics.maxScrollTop),scrollYBefore:Math.round(metrics.scrollTop),scrollYAfter:Math.round(actualScrollTop),moved:actualScrollTop!==metrics.scrollTop,atTop:actualScrollTop<=0,atBottom:actualScrollTop>=metrics.maxScrollTop,target:targetLabel,resolvedContainer:targetDocument&&element===targetDocument.body&&element!==startingElement?"document-body":targetDocument&&element!==startingElement?"document-descendant":element===startingElement?"self":"nearest-scrollable-ancestor",ancestorDistance,...targetDocument&&element!==startingElement?{containerSelector:selectorFor(element)}:{}}}var SCROLL_ELEMENT_IN_PAGE_SOURCE=scrollElementInPage.toString();function inspectElementsInPage(selector,filter,styles,maxResults,invalidSelectorHint,includeOuterHTML=!1){let context=this?.nodeType===3?this.parentElement:this,documentRef=context?.nodeType===9?context:context?.ownerDocument;if(!documentRef)return{error:"page_inspect cannot resolve the target document"};let scanLimit=1e4,candidates;if(context?.nodeType===9){let effectiveSelector=selector??"*";try{candidates=Array.from(documentRef.querySelectorAll(effectiveSelector))}catch(error){return{error:'Invalid CSS selector "'+effectiveSelector+'": '+(error instanceof Error?error.message:String(error))+". "+invalidSelectorHint}}}else if(context?.nodeType===1&&context.isConnected!==!1)candidates=[context];else return{error:"page_inspect target is detached from the document"};let candidateCount=candidates.length,scanTruncated=filter!=="all"&&candidateCount>scanLimit;candidates=filter==="all"?candidates.slice(0,maxResults):scanTruncated?candidates.slice(0,scanLimit):candidates;let windowRef=documentRef.defaultView,viewportWidth=Number(windowRef?.innerWidth)||0,viewportHeight=Number(windowRef?.innerHeight)||0;function round(value){return Math.round(value*1e3)/1e3}function controlState(element){let state={};for(let name of["checked","selected","pressed","expanded","disabled","readonly"]){let value=element.getAttribute?.("aria-"+name);(value==="true"||value==="false"||value==="mixed"&&(name==="checked"||name==="pressed"))&&(state[name]={value:value==="mixed"?"mixed":value==="true",source:"aria"})}let tag=String(element.tagName||"").toLowerCase(),type=String(element.type||"").toLowerCase();if(tag==="input"&&(type==="checkbox"||type==="radio")&&(state.checked={value:type==="checkbox"&&element.indeterminate?"mixed":!!element.checked,source:"native"}),tag==="option"&&(state.selected={value:!!element.selected,source:"native"}),["button","input","select","textarea","option","optgroup","fieldset"].includes(tag)){let disabled=element.matches?.(":disabled")??!!element.disabled;(disabled||!state.disabled)&&(state.disabled={value:disabled,source:"native"})}return(tag==="textarea"||tag==="input"&&["text","search","tel","url","email","password","date","month","week","time","datetime-local","number"].includes(type))&&(element.readOnly||!state.readonly)&&(state.readonly={value:!!element.readOnly,source:"native"}),state}function escapeCss(value){return windowRef?.CSS&&typeof windowRef.CSS.escape=="function"?windowRef.CSS.escape(value):value.replace(/[^a-zA-Z0-9_-]/g,character=>"\\"+character)}function selectorFor(element){if(element.id){let idSelector="#"+escapeCss(String(element.id));try{if(documentRef.querySelectorAll(idSelector).length===1)return idSelector}catch{}}let path5=[],cursor=element;for(;cursor&&cursor.nodeType===1&&path5.length<8;){let tag=String(cursor.tagName||"").toLowerCase();if(!tag)break;let parent=cursor.parentElement;if(!parent){path5.unshift(tag);break}let siblings=Array.from(parent.children).filter(sibling=>sibling.tagName===cursor.tagName),index=siblings.indexOf(cursor)+1;if(path5.unshift(siblings.length>1?tag+":nth-of-type("+index+")":tag),cursor=parent,cursor===documentRef.documentElement){path5.unshift("html");break}}return path5.join(" > ")}function readStyle(style,property){let cssValue=style.getPropertyValue?.(property);if(cssValue)return String(cssValue);let camelName=property.replace(/-([a-z])/g,(_match,letter)=>letter.toUpperCase());return String(style[camelName]??"")}function isScrollable(element,style){let canScrollX=Number(element.scrollWidth)>Number(element.clientWidth)&&!["visible","clip"].includes(String(style.overflowX)),canScrollY=Number(element.scrollHeight)>Number(element.clientHeight)&&!["visible","clip"].includes(String(style.overflowY));return canScrollX||canScrollY}function nearestScrollContainer(element){let candidate=element,ancestorDistance=0;for(;candidate&&candidate.nodeType===1;){let style=windowRef.getComputedStyle(candidate);if(isScrollable(candidate,style))return{selector:selectorFor(candidate),tag:String(candidate.tagName||"").toLowerCase(),ancestorDistance,scrollWidth:Number(candidate.scrollWidth)||0,scrollHeight:Number(candidate.scrollHeight)||0,clientWidth:Number(candidate.clientWidth)||0,clientHeight:Number(candidate.clientHeight)||0,scrollLeft:Number(candidate.scrollLeft)||0,scrollTop:Number(candidate.scrollTop)||0,overflowX:String(style.overflowX||""),overflowY:String(style.overflowY||"")};candidate=candidate.parentElement,ancestorDistance+=1}}function hitTargetInfo(element,rect,visible,style){let left=Math.max(0,Number(rect.left)||0),right=Math.min(viewportWidth,Number(rect.right)||0),top=Math.max(0,Number(rect.top)||0),bottom=Math.min(viewportHeight,Number(rect.bottom)||0),x=round(left+Math.max(0,right-left)/2),y=round(top+Math.max(0,bottom-top)/2),hit=null;try{hit=visible?documentRef.elementFromPoint?.(x,y)??null:null}catch{}let topmost=!!hit&&(hit===element||element.contains?.(hit)),target=hit?{selector:selectorFor(hit),tag:String(hit.tagName||"").toLowerCase(),text:String(hit.innerText??hit.textContent??"").trim().replace(/\s+/g," ").slice(0,120)}:void 0;return{point:{x,y},topmost,receivesPointerEvents:visible&&style.pointerEvents!=="none"&&topmost,...target?{target}:{},...!topmost&&target?{occludedBy:target}:{}}}let matches=[],count=filter==="all"?candidateCount:0;for(let element of candidates){let style=windowRef.getComputedStyle(element),scrollWidth=Number(element.scrollWidth)||0,scrollHeight=Number(element.scrollHeight)||0,clientWidth=Number(element.clientWidth)||0,clientHeight=Number(element.clientHeight)||0,horizontalOverflow=Math.max(0,scrollWidth-clientWidth),verticalOverflow=Math.max(0,scrollHeight-clientHeight),scrollable=isScrollable(element,style);if(!(filter==="all"||filter==="overflowing"&&(horizontalOverflow>0||verticalOverflow>0)||filter==="horizontal-overflow"&&horizontalOverflow>0||filter==="vertical-overflow"&&verticalOverflow>0||filter==="scrollable"&&scrollable)||(filter!=="all"&&(count+=1),matches.length>=maxResults))continue;let rect=element.getBoundingClientRect(),opacity=Number(style.opacity),visible=rect.width>0&&rect.height>0&&style.display!=="none"&&style.visibility!=="hidden"&&style.visibility!=="collapse"&&(!Number.isFinite(opacity)||opacity>0)&&rect.right>0&&rect.bottom>0&&rect.left<viewportWidth&&rect.top<viewportHeight,computedStyle={};for(let property of styles)computedStyle[property]=readStyle(style,property);let scrollContainer=nearestScrollContainer(element),hitTest=hitTargetInfo(element,rect,visible,style),state=controlState(element);matches.push({selector:selectorFor(element),tag:String(element.tagName||"").toLowerCase(),text:String(element.innerText??element.textContent??"").trim().replace(/\s+/g," ").slice(0,240),visible,hitTest,...Object.keys(state).length?{state}:{},boundingBox:{x:round(rect.x),y:round(rect.y),width:round(rect.width),height:round(rect.height),top:round(rect.top),right:round(rect.right),bottom:round(rect.bottom),left:round(rect.left)},computedStyle,scroll:{scrollWidth,scrollHeight,clientWidth,clientHeight,overflowX:String(style.overflowX||""),overflowY:String(style.overflowY||""),horizontalOverflow,verticalOverflow},...scrollContainer?{scrollContainer}:{},...includeOuterHTML?{outerHTML:String(element.outerHTML??"").slice(0,3e3)}:{}})}return{...selector!==void 0?{selector}:{},filter,candidateCount,count,matches,truncated:scanTruncated||count>matches.length,...scanTruncated?{scanTruncated:!0}:{}}}var INSPECT_ELEMENTS_IN_PAGE_SOURCE=inspectElementsInPage.toString();var SHELL_BODY=btoa('<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"></head><body></body></html>');var MAX_DOCUMENT_BYTES=16*1024*1024;function dragBetweenPointsInPage(from,points,pointerType){let target=document.elementFromPoint(from.x,from.y);if(!target)return{dragged:!1,steps:0,error:`No element found at drag start point (${from.x}, ${from.y})`};if(points.length===0)return{dragged:!1,steps:0,error:"Drag path must contain at least one point"};let view=target.ownerDocument.defaultView??window,realm=view,pointerId=1,pointerInit=(point,buttons,pressure)=>({bubbles:!0,cancelable:!0,composed:!0,view,clientX:point.x,clientY:point.y,screenX:point.x,screenY:point.y,button:0,buttons,pointerId,pointerType,isPrimary:!0,pressure,width:1,height:1}),dispatchPointer=(type,point,buttons,pressure)=>typeof realm.PointerEvent!="function"?!1:(target.dispatchEvent(new realm.PointerEvent(type,pointerInit(point,buttons,pressure))),!0),dispatchMouse=(type,point,buttons)=>typeof realm.MouseEvent!="function"?!1:(target.dispatchEvent(new realm.MouseEvent(type,{bubbles:!0,cancelable:!0,composed:!0,view,clientX:point.x,clientY:point.y,screenX:point.x,screenY:point.y,button:0,buttons})),!0),createTouch=point=>{let init={identifier:pointerId,target,clientX:point.x,clientY:point.y,screenX:point.x,screenY:point.y,pageX:point.x+view.scrollX,pageY:point.y+view.scrollY,radiusX:1,radiusY:1,rotationAngle:0,force:.5};return typeof realm.Touch=="function"?new realm.Touch(init):init},dispatchTouch=(type,point)=>{if(typeof realm.TouchEvent!="function")return!1;let touch=createTouch(point),activeTouches=type==="touchend"?[]:[touch];return target.dispatchEvent(new realm.TouchEvent(type,{bubbles:!0,cancelable:!0,composed:!0,touches:activeTouches,targetTouches:activeTouches,changedTouches:[touch]})),!0},dispatched=dispatchPointer("pointerdown",from,1,.5);dispatched=pointerType==="touch"?dispatchTouch("touchstart",from)||dispatched:dispatchMouse("mousedown",from,1)||dispatched;for(let point of points)dispatched=dispatchPointer("pointermove",point,1,.5)||dispatched,dispatched=pointerType==="touch"?dispatchTouch("touchmove",point)||dispatched:dispatchMouse("mousemove",point,1)||dispatched;let end=points[points.length-1];return dispatched=pointerType==="touch"?dispatchTouch("touchend",end)||dispatched:dispatchMouse("mouseup",end,0)||dispatched,dispatched=dispatchPointer("pointerup",end,0,0)||dispatched,dispatched?{dragged:!0,steps:points.length}:{dragged:!1,steps:0,error:"This page does not support synthetic drag events"}}var DRAG_BETWEEN_POINTS_IN_PAGE_SOURCE=dragBetweenPointsInPage.toString();var PAGE_PRESS_KEY_DESCRIPTORS=Object.freeze({Enter:{key:"Enter",code:"Enter",keyCode:13,text:"\r"},Tab:{key:"Tab",code:"Tab",keyCode:9},Escape:{key:"Escape",code:"Escape",keyCode:27},Backspace:{key:"Backspace",code:"Backspace",keyCode:8},Delete:{key:"Delete",code:"Delete",keyCode:46},ArrowUp:{key:"ArrowUp",code:"ArrowUp",keyCode:38},ArrowDown:{key:"ArrowDown",code:"ArrowDown",keyCode:40},ArrowLeft:{key:"ArrowLeft",code:"ArrowLeft",keyCode:37},ArrowRight:{key:"ArrowRight",code:"ArrowRight",keyCode:39},Home:{key:"Home",code:"Home",keyCode:36},End:{key:"End",code:"End",keyCode:35},PageUp:{key:"PageUp",code:"PageUp",keyCode:33},PageDown:{key:"PageDown",code:"PageDown",keyCode:34},Space:{key:" ",code:"Space",keyCode:32,text:" "}});var TAB_ID_REQUIRED_ACTIONS=["tab_checkpoint","capture_checkpoint","get_requests","get_logs","get_events","set_mock","get_mocks","page_screenshot","tab_close","page_click","page_drag","page_type","page_scroll","page_hover","page_eval","page_inspect","page_snapshot","page_press","page_wait","page_navigate","page_upload","page_download","page_frames","set_device_emulation","set_app_profile","set_timezone","run_actions"],TAB_ID_REQUIRED_ACTION_SET=new Set(TAB_ID_REQUIRED_ACTIONS);var EXTENSION_BROWSER_ACTIONS={tab_checkpoint:!0,capture_checkpoint:!0,get_requests:!0,get_logs:!0,get_events:!0,set_mock:!0,get_mocks:!0,set_rule:!0,get_rules:!0,send_request:!0,send_mtop_request:!0,browser_release:!0,page_screenshot:!0,tab_open:!0,tab_close:!0,tab_list:!0,tab_group:!0,page_click:!0,page_drag:!0,page_type:!0,page_scroll:!0,page_hover:!0,page_eval:!0,page_runtime:!0,page_inspect:!0,page_snapshot:!0,page_press:!0,page_wait:!0,page_navigate:!0,page_upload:!0,page_frames:!0,set_device_emulation:!0,set_app_profile:!0,set_timezone:!0,get_cookie:!0,set_cookie:!0,request_domain_permission:!0,run_actions:!0},BROWSER_CAPABILITY_ACTIONS={...EXTENSION_BROWSER_ACTIONS,page_download:!0};var AGENT_CHAT_MAX_IMAGE_BYTES=10*1024*1024,AGENT_CHAT_MAX_TOTAL_IMAGE_BYTES=20*1024*1024;var MEARL_NATIVE_HOST_NAMES={skills:"ai.mearl.skills",mcp:"ai.mearl.mcp"};function resolveNativeHostDirectory(options={}){let env=options.env??process.env,home=(options.homedir??os.homedir)();switch(options.platform??process.platform){case"darwin":return path2.join(home,"Library/Application Support/Google/Chrome/NativeMessagingHosts");case"linux":return path2.join(home,".config/google-chrome/NativeMessagingHosts");case"win32":return path2.join(env.PROGRAMDATA||"C:\\ProgramData","Google\\Chrome\\NativeMessagingHosts");default:throw new Error(`Unsupported platform: ${String(options.platform??process.platform)}`)}}function unregisterWindowsNativeHost(hostName){let registryKey=`HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\${hostName}`;try{execFileSync("reg",["delete",registryKey,"/f"],{stdio:"pipe"})}catch{}}function uninstallNativeHostArtifacts(options={},dependencies={}){let{mode="both",silent=!1}=options;if(mode==="both")return["skills","mcp"].map(integrationMode=>uninstallNativeHostArtifacts({...options,mode:integrationMode},dependencies)).every(Boolean);let writeLine=dependencies.writeLine??(line=>console.error(line)),log=line=>{silent||writeLine(line)};try{let existsSync=dependencies.existsSync??fs3.existsSync,platform=dependencies.platform??process.platform,hostDir=dependencies.hostDir??resolveNativeHostDirectory({env:dependencies.env,homedir:dependencies.homedir,platform}),unlinkSync=dependencies.unlinkSync??fs3.unlinkSync,unregister=dependencies.unregisterWindowsNativeHost??unregisterWindowsNativeHost,hostName=MEARL_NATIVE_HOST_NAMES[mode],launcherExtension=platform==="win32"?"cmd":"sh",artifacts=[path3.join(hostDir,`${hostName}.json`),path3.join(hostDir,`${hostName}-launcher.${launcherExtension}`)];for(let artifact of artifacts)existsSync(artifact)&&(unlinkSync(artifact),log(`✅ Removed: ${artifact}`));return platform==="win32"&&unregister(hostName),!0}catch(error){return log(`❌ Uninstall failed: ${error instanceof Error?error.message:String(error)}`),!1}}var DEFAULT_EXTENSION_ID="aoehhjnofngknnjefamjbplchbolghkm",__filename=fileURLToPath(import.meta.url),__dirname=path4.dirname(__filename),ALL_MODES=["skills","mcp"];function resolveHostDir(){return resolveNativeHostDirectory()}function resolveNativeHostPath(){let siblingPath=path4.join(__dirname,"index.js");if(fs4.existsSync(siblingPath))return siblingPath;try{return createRequire(import.meta.url).resolve("@mearl/native-host/runtime")}catch{throw new Error("Cannot locate native-host runtime entry point. Ensure @mearl/native-host is installed correctly.")}}function registerWindowsNativeHost(hostName,manifestPath){let regKey=`HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\${hostName}`;execFileSync2("reg",["add",regKey,"/ve","/t","REG_SZ","/d",manifestPath,"/f"],{stdio:"pipe"})}async function installNativeHost(options={}){let{mode="both",extensionId=DEFAULT_EXTENSION_ID,silent=!1,skipIfExists=!1}=options;if(mode==="both")return(await Promise.all(ALL_MODES.map(m=>installNativeHost({...options,mode:m})))).every(Boolean);let hostName=MEARL_NATIVE_HOST_NAMES[mode];if(!hostName)throw new Error(`Unknown mode: ${mode}. Expected 'skills' or 'mcp'.`);let log=(...args)=>{silent||console.error(...args)};try{let hostDir=resolveHostDir();if(skipIfExists){let manifestPath2=path4.join(hostDir,`${hostName}.json`);if(fs4.existsSync(manifestPath2))return log(`✅ Already installed: ${manifestPath2}`),!0}fs4.existsSync(hostDir)||(fs4.mkdirSync(hostDir,{recursive:!0}),log(`✅ Created directory: ${hostDir}`));let resolvedHostPath=resolveNativeHostPath();log(`✅ Native host path: ${resolvedHostPath}`);let launcherPath=createLauncherScript(hostDir,resolvedHostPath,hostName);log(`✅ Launcher script: ${launcherPath}`);let manifest={name:hostName,description:"Mearl Native Messaging Bridge",path:launcherPath,type:"stdio",allowed_origins:[`chrome-extension://${extensionId}/`]},manifestPath=path4.join(hostDir,`${hostName}.json`);return fs4.writeFileSync(manifestPath,JSON.stringify(manifest,null,2)),process.platform==="win32"?(registerWindowsNativeHost(hostName,manifestPath),log(`✅ Windows registry key created for: ${hostName}`)):fs4.chmodSync(manifestPath,420),log(`✅ Manifest installed: ${manifestPath}`),log(`✅ Extension ID: ${extensionId}`),log(`✅ Host name: ${hostName}`),!0}catch(error){return log(`❌ Installation failed: ${error instanceof Error?error.message:String(error)}`),error instanceof Error&&"code"in error&&error.code==="EACCES"&&log(`
|
|
7
7
|
Permission denied. Try running with sudo.`),!1}}async function uninstallNativeHost(options={},dependencies={}){return uninstallNativeHostArtifacts(options,dependencies)}async function parseNativeHostInstallArgs(argv){let normalizedArgv=argv[0]==="--init"?["init",...argv.slice(1)]:argv[0]==="--uninstall"?["uninstall",...argv.slice(1)]:argv,operation="init",mode="both",extensionId,showHelp=!1,skipIfExists=!1,index=0;if(normalizedArgv[0]==="init"||normalizedArgv[0]==="uninstall")operation=normalizedArgv[0],index=1;else if(normalizedArgv[0]&&!normalizedArgv[0].startsWith("-"))throw new Error(`Unknown command: ${normalizedArgv[0]}`);let readValue=name=>{let argument=normalizedArgv[index];if(argument===name){let value=normalizedArgv[index+1];if(value===void 0||value.startsWith("-"))throw new Error(`argument missing for option '${name}'`);return index+=1,value}return argument?.startsWith(`${name}=`)?argument.slice(name.length+1):null};for(;index<normalizedArgv.length;index+=1){let argument=normalizedArgv[index];if(argument==="--help"||argument==="-h"){showHelp=!0;continue}if(argument==="--skip-if-exists"){skipIfExists=!0;continue}let parsedMode=readValue("--mode");if(parsedMode!==null){if(parsedMode!=="skills"&&parsedMode!=="mcp"&&parsedMode!=="both")throw new Error(`Invalid --mode: ${parsedMode}. Allowed choices are skills, mcp, and both`);mode=parsedMode;continue}let parsedExtensionId=readValue("--extension-id");if(parsedExtensionId!==null){extensionId=parsedExtensionId;continue}throw new Error(argument?.startsWith("-")?`unknown option '${argument}'`:`unexpected argument '${argument}'`)}if(operation==="uninstall"&&extensionId)throw new Error("--extension-id is only available for init");if(operation==="uninstall"&&skipIfExists)throw new Error("--skip-if-exists is only available for init");return{showHelp,uninstall:operation==="uninstall",mode,...extensionId?{extensionId}:{},skipIfExists}}function printHelp(){console.error(`Usage:
|
|
8
8
|
mearl-native-host init [options]
|
package/dist/plugin-task-host.js
CHANGED
|
@@ -623,7 +623,7 @@ Defaulting to 2020, but this will stop working in the future.`)),options.ecmaVer
|
|
|
623
623
|
`;case 114:return"\r";case 120:return String.fromCharCode(this.readHexChar(2));case 117:return codePointToString(this.readCodePoint());case 116:return" ";case 98:return"\b";case 118:return"\v";case 102:return"\f";case 13:this.input.charCodeAt(this.pos)===10&&++this.pos;case 10:return this.options.locations&&(this.lineStart=this.pos,++this.curLine),"";case 56:case 57:if(this.strict&&this.invalidStringToken(this.pos-1,"Invalid escape sequence"),inTemplate){var codePos=this.pos-1;this.invalidStringToken(codePos,"Invalid escape sequence in template string")}default:if(ch>=48&&ch<=55){var octalStr=this.input.substr(this.pos-1,3).match(/^[0-7]+/)[0],octal=parseInt(octalStr,8);return octal>255&&(octalStr=octalStr.slice(0,-1),octal=parseInt(octalStr,8)),this.pos+=octalStr.length-1,ch=this.input.charCodeAt(this.pos),(octalStr!=="0"||ch===56||ch===57)&&(this.strict||inTemplate)&&this.invalidStringToken(this.pos-1-octalStr.length,inTemplate?"Octal literal in template string":"Octal literal in strict mode"),String.fromCharCode(octal)}return isNewLine(ch)?(this.options.locations&&(this.lineStart=this.pos,++this.curLine),""):String.fromCharCode(ch)}};pp.readHexChar=function(len){var codePos=this.pos,n=this.readInt(16,len);return n===null&&this.invalidStringToken(codePos,"Bad character escape sequence"),n};pp.readWord1=function(){this.containsEsc=!1;for(var word="",first=!0,chunkStart=this.pos,astral=this.options.ecmaVersion>=6;this.pos<this.input.length;){var ch=this.fullCharCodeAtPos();if(isIdentifierChar(ch,astral))this.pos+=ch<=65535?1:2;else if(ch===92){this.containsEsc=!0,word+=this.input.slice(chunkStart,this.pos);var escStart=this.pos;this.input.charCodeAt(++this.pos)!==117&&this.invalidStringToken(this.pos,"Expecting Unicode escape sequence \\uXXXX"),++this.pos;var esc=this.readCodePoint();(first?isIdentifierStart:isIdentifierChar)(esc,astral)||this.invalidStringToken(escStart,"Invalid Unicode escape"),word+=codePointToString(esc),chunkStart=this.pos}else break;first=!1}return word+this.input.slice(chunkStart,this.pos)};pp.readWord=function(){var word=this.readWord1(),type=types$1.name;return this.keywords.test(word)&&(type=keywords[word]),this.finishToken(type,word)};version="8.19.0";Parser.acorn={Parser,version,defaultOptions,Position,SourceLocation,getLineInfo,Node:Node2,TokenType,tokTypes:types$1,keywordTypes:keywords,TokContext,tokContexts:types,isIdentifierChar,isIdentifierStart,Token,isNewLine,lineBreak,lineBreakG,nonASCIIwhitespace}}});import{existsSync as existsSync3,lstatSync,readFileSync as readFileSync7,renameSync as renameSync4,statSync as statSync5}from"node:fs";import{join as join8}from"node:path";function readInstalledSkillMetadata(directory){if(existsSync3(join8(directory,"SKILL.md"))&&existsSync3(join8(directory,DISABLED_SKILL_MANIFEST)))throw new Error("Conflicting installed Skill manifests");let file=skillManifestPath(directory);if(!file)return;let info=statSync5(file);if(!info.isFile()||info.size>1024*1024)throw new Error("Invalid installed Skill manifest");let frontmatter=readFileSync7(file,"utf8").match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];if(!frontmatter)return{};let scalar=key2=>{let lines=frontmatter.split(/\r?\n/).filter(line=>line.startsWith(key2+":"));if(lines.length===1)return lines[0].match(/^[\w-]+:\s*(?:"([^"\r\n]*)"|'([^'\r\n]*)'|([^\s#"']+))\s*(?:#.*)?$/)?.slice(1).find(value=>value!==void 0)};return{name:scalar("name"),version:scalar("version")}}function skillManifestPath(directory){for(let name of["SKILL.md",DISABLED_SKILL_MANIFEST]){let file=join8(directory,name);if(existsSync3(file))return file}}var DISABLED_SKILL_MANIFEST,init_skillManifest=__esm({"../client/src/plugins/skillManifest.ts"(){"use strict";DISABLED_SKILL_MANIFEST="SKILL.md.mearl-disabled"}});import{randomUUID as randomUUID5}from"node:crypto";import{closeSync as closeSync4,fsyncSync,mkdirSync as mkdirSync5,openSync as openSync4,renameSync as renameSync6,rmSync as rmSync6,writeFileSync as writeFileSync5}from"node:fs";import{dirname as dirname5}from"node:path";function writePluginHostFile(file,data2){mkdirSync5(dirname5(file),{recursive:!0,mode:448});let temporary=`${file}.${randomUUID5()}.tmp`,descriptor;try{descriptor=openSync4(temporary,"wx",384),writeFileSync5(descriptor,data2),fsyncSync(descriptor),closeSync4(descriptor),descriptor=void 0,renameSync6(temporary,file)}finally{descriptor!==void 0&&closeSync4(descriptor),rmSync6(temporary,{force:!0})}}var init_pluginHostFiles=__esm({"../client/src/plugins/runtime/pluginHostFiles.ts"(){"use strict"}});import{createHash as createHash7}from"node:crypto";import{closeSync as closeSync5,openSync as openSync5,opendirSync,readFileSync as readFileSync9,readSync as readSync2,readdirSync as readdirSync6,rmSync as rmSync7,statSync as statSync7}from"node:fs";import{join as join10}from"node:path";function pluginTaskExecutionUnconfirmed(record2){let owner=record2.executorOwner;return owner&&(!Number.isSafeInteger(owner.pid)||owner.pid<1)||owner&&isProcessOwnerAlive(owner)||record2.task.error?.details?.executionStopped===!1||process.platform==="win32"&&record2.task.error?.code==="PLUGIN_TASK_HOST_RESTARTED"?!0:owner&&process.platform!=="win32"?isProcessGroupAlive(owner.pid):!1}var PLUGIN_TASK_RESULT_CHUNK_BYTES,MAX_EVENT_BYTES,MAX_RESULT_BYTES,RETENTION_MS,PluginTaskStore,init_pluginTaskStore=__esm({"../client/src/plugins/runtime/pluginTaskStore.ts"(){"use strict";init_dist2();init_pluginHostFiles();init_pluginHostPaths();init_dist();PLUGIN_TASK_RESULT_CHUNK_BYTES=192*1024,MAX_EVENT_BYTES=512*1024,MAX_RESULT_BYTES=64*1024*1024,RETENTION_MS=10080*60*1e3;PluginTaskStore=class{constructor(directory=pluginHostPaths().tasks){this.directory=directory}directory;activeTaskIds;dedupeIndex;dedupeKeys=new Map;forgetDedupe(taskId){let key2=this.dedupeKeys.get(taskId);if(!key2)return;let ids=this.dedupeIndex?.get(key2);ids?.delete(taskId),ids?.size||this.dedupeIndex?.delete(key2),this.dedupeKeys.delete(taskId)}indexDedupe(record2){if(!this.dedupeIndex)return;let{dedupeKey:key2,task}=record2,keep=key2&&(!key2.startsWith("active:")||isPluginTaskActive(task.state));if(keep&&this.dedupeKeys.get(task.taskId)===key2||(this.forgetDedupe(task.taskId),!keep))return;let ids=this.dedupeIndex.get(key2)??new Set;ids.add(task.taskId),this.dedupeIndex.set(key2,ids),this.dedupeKeys.set(task.taskId,key2)}findDedupe(key2,activeOnly=!1){if(!this.dedupeIndex){this.dedupeIndex=new Map;for(let record2 of this.list())this.indexDedupe(record2)}for(let taskId of this.dedupeIndex.get(key2)??[]){let record2;try{record2=this.read(taskId)}catch(error2){if(error2.code!=="PLUGIN_TASK_NOT_FOUND")throw error2;this.forgetDedupe(taskId);continue}if(this.indexDedupe(record2),record2.dedupeKey===key2&&(!activeOnly||isPluginTaskActive(record2.task.state)))return record2}}path(taskId,file){if(!isPluginTaskId(taskId))throw Object.assign(new Error("Invalid plugin task ID"),{code:"PLUGIN_TASK_INVALID_ID"});return join10(this.directory,taskId,file)}read(taskId){let record2;try{record2=JSON.parse(readFileSync9(this.path(taskId,"task.json"),"utf8"))}catch(error2){throw error2.code==="ENOENT"?Object.assign(new Error("Plugin task was not found"),{code:"PLUGIN_TASK_NOT_FOUND"}):error2}if(record2?.task?.taskId!==taskId||!Array.isArray(record2.events))throw new Error("Invalid persistent plugin task record");return record2}write(record2){writePluginHostFile(this.path(record2.task.taskId,"task.json"),JSON.stringify(record2)),this.indexDedupe(record2),this.indexActive(record2)}indexActive(record2){isPluginTaskActive(record2.task.state)?this.activeTaskIds?.add(record2.task.taskId):this.activeTaskIds?.delete(record2.task.taskId)}listActive(){if(!this.activeTaskIds){this.activeTaskIds=new Set;for(let record2 of this.list())this.indexActive(record2)}let records=[];for(let taskId of this.activeTaskIds)try{let record2=this.read(taskId);this.indexActive(record2),isPluginTaskActive(record2.task.state)&&records.push(record2)}catch(error2){if(error2.code!=="PLUGIN_TASK_NOT_FOUND")throw error2;this.activeTaskIds.delete(taskId)}return records}update(taskId,update,type="state",fields2={}){let record2=this.read(taskId);Object.assign(record2,fields2),record2.task={...record2.task,...update,sequence:record2.task.sequence+1,updatedAt:Date.now()};let event={sequence:record2.task.sequence,time:record2.task.updatedAt,type,state:record2.task.state,...type==="progress"?{progress:record2.task.progress}:{},...type==="checkpoint"?{checkpoint:record2.task.checkpoint}:{}};record2.events.push(event);let bytes=Buffer.byteLength(JSON.stringify(record2.events));for(;record2.events.length>256||bytes>MAX_EVENT_BYTES&&record2.events.length>1;)bytes-=Buffer.byteLength(JSON.stringify(record2.events.shift()))+1;return this.write(record2),record2.task}list(){let names;try{names=readdirSync6(this.directory)}catch(error2){if(error2.code==="ENOENT")return[];throw error2}return names.filter(isPluginTaskId).map(name=>this.read(name))}diagnosticRecords(taskId){let records=[],bytes=0,scanned=0,limited=!1;if(taskId){let size;try{size=statSync7(this.path(taskId,"task.json")).size}catch(error2){if(error2.code==="ENOENT")return{records,limited};throw error2}if(size>16*1024*1024)return{records,limited:!0};records.push(this.read(taskId)),bytes+=size}let directory;try{directory=opendirSync(this.directory)}catch(error2){if(error2.code==="ENOENT")return{records,limited};throw error2}try{let item;for(;(item=directory.readSync())!==null;){if(++scanned>2048){limited=!0;break}if(!(!isPluginTaskId(item.name)||item.name===taskId))try{let size=statSync7(this.path(item.name,"task.json")).size;if(records.length>=512||bytes+size>16*1024*1024){limited=!0;break}bytes+=size,records.push(this.read(item.name))}catch{limited=!0}}}finally{directory.closeSync()}return{records,limited}}events(taskId,cursor){let record2=this.read(taskId);if(!Number.isSafeInteger(cursor)||cursor<0||cursor>record2.task.sequence)throw new Error("Invalid plugin task event cursor");let first=record2.events[0]?.sequence??record2.task.sequence+1,events=[],bytes=0;for(let event of record2.events){if(event.sequence<=cursor)continue;let size=Buffer.byteLength(JSON.stringify(event));if(bytes+size>192*1024&&events.length)break;events.push(event),bytes+=size}return{task:record2.task,events,firstAvailableSequence:first,nextCursor:events.at(-1)?.sequence??cursor,gap:cursor<first-1}}saveResult(taskId,value){let bytes=Buffer.from(JSON.stringify({value}));if(bytes.length>MAX_RESULT_BYTES)throw Object.assign(new Error("Plugin task result exceeds 64 MiB"),{code:"PLUGIN_TASK_RESULT_TOO_LARGE"});return writePluginHostFile(this.path(taskId,"result.json"),bytes),{bytes:bytes.length,sha256:createHash7("sha256").update(bytes).digest("hex"),chunks:Math.ceil(bytes.length/PLUGIN_TASK_RESULT_CHUNK_BYTES)}}resultChunk(taskId,index){let task=this.read(taskId).task;if(task.state!=="success"||!task.result)throw new Error("Plugin task result is not available");if(!Number.isSafeInteger(index)||index<0||index>=task.result.chunks)throw new Error("Invalid plugin task result chunk");let file=this.path(taskId,"result.json");if(statSync7(file).size!==task.result.bytes)throw new Error("Plugin task result size does not match its record");let offset2=index*PLUGIN_TASK_RESULT_CHUNK_BYTES,buffer=Buffer.alloc(Math.min(PLUGIN_TASK_RESULT_CHUNK_BYTES,task.result.bytes-offset2)),descriptor=openSync5(file,"r");try{if(readSync2(descriptor,buffer,0,buffer.length,offset2)!==buffer.length)throw new Error("Plugin task result is incomplete")}finally{closeSync5(descriptor)}return{taskId,index,data:buffer.toString("base64"),...task.result}}recover(epoch2){this.activeTaskIds=new Set;for(let record2 of this.list()){this.indexActive(record2);let{task}=record2;isPluginTaskActive(task.state)&&task.host.epoch!==epoch2?this.update(task.taskId,{state:"interrupted",error:{code:"PLUGIN_TASK_HOST_RESTARTED",message:"The plugin execution service restarted; external operations may need reconciliation"}}):!isPluginTaskActive(task.state)&&Date.now()-task.updatedAt>RETENTION_MS&&!pluginTaskExecutionUnconfirmed(record2)&&(rmSync7(join10(this.directory,task.taskId),{recursive:!0,force:!0}),this.forgetDedupe(task.taskId))}}prune(now=Date.now()){for(let record2 of this.list()){let{task}=record2;!isPluginTaskActive(task.state)&&now-task.updatedAt>RETENTION_MS&&!pluginTaskExecutionUnconfirmed(record2)&&(rmSync7(join10(this.directory,task.taskId),{recursive:!0,force:!0}),this.forgetDedupe(task.taskId))}}}}});import{randomUUID as randomUUID6}from"node:crypto";async function invokePageEvalWithArgs(invoke2,params){if(!Number.isSafeInteger(params.tabId)||params.tabId<0)throw new Error("Invalid target tab");if(resolvePageEvalExpression({expression:params.expression,args:[]}),!Array.isArray(params.args))throw new Error("page_eval args must be an array");if(params.executionWorld!==void 0&&!["main","isolated"].includes(params.executionWorld))throw new Error("Invalid page executionWorld");params.executionWorld==="isolated"&&validatePageUserScriptParams({tabId:params.tabId,worldId:params.worldId??"",code:params.expression});let args=JSON.stringify(params.args);if(args.length>64*1024*1024)throw new Error("Page script arguments must fit within 64 MiB characters");let{args:_args,...request}=params,evaluate=async(expression,transfer=!1)=>{let payload={...request,...transfer?{observe:!1,diagnostics:!1}:{},expression},result=await(transfer?invoke2(payload,!0):invoke2(payload));if(transfer&&result?.action?.success===!1)throw new Error(result.action.error||"page_eval failed");return result};if(args.length+params.expression.length<=CHUNK_CHARACTERS)return evaluate(resolvePageEvalExpression(params));let key2=JSON.stringify("__mearlPageScriptTransfer_"+randomUUID6());try{for(let offset2=0;offset2<args.length;offset2+=CHUNK_CHARACTERS)await evaluate(`void (window[${key2}] = (window[${key2}] || '') + ${JSON.stringify(args.slice(offset2,offset2+CHUNK_CHARACTERS))})`,!0);return await evaluate(`(() => { const args = JSON.parse(window[${key2}]); delete window[${key2}]; return (${params.expression})(...args); })()`)}finally{await evaluate(`delete window[${key2}]`,!0).catch(()=>{})}}var CHUNK_CHARACTERS,init_pageEvalArguments=__esm({"../client/src/pageEvalArguments.ts"(){"use strict";init_dist2();CHUNK_CHARACTERS=48*1024}});import{open,mkdir,rename,rm,stat}from"node:fs/promises";import path2 from"node:path";import{randomUUID as randomUUID7}from"node:crypto";async function stageFile(client,filePath,options){if(!path2.isAbsolute(filePath))throw new Error("Caller-local file paths must be absolute");let source=await open(filePath,"r"),id,pinned=options;try{let metadata=await source.stat();if(!metadata.isFile()||metadata.size>256*1024*1024)throw new Error("Upload requires a regular file of at most 256 MiB");let created=await client.invoke("__artifact_init",{filename:path2.basename(filePath)},options);pinned={...options,browser:created.browserId},id=created.artifact.artifactId;let offset2=0;for await(let chunk of source.createReadStream({highWaterMark:CHUNK_BYTES,autoClose:!1}))await client.invoke("__artifact_write",{artifactId:id,offset:offset2,base64:chunk.toString("base64")},pinned),offset2+=chunk.length;return await client.invoke("__artifact_complete",{artifactId:id,bytes:offset2},pinned),{artifactId:id,options:pinned}}catch(error2){throw id&&await client.invoke("__artifact_remove",{artifactId:id},pinned).catch(()=>{}),error2}finally{await source.close()}}async function exportArtifact(client,artifact,outputPath,options={}){if(!Number.isSafeInteger(artifact.bytes)||artifact.bytes<0)throw new Error("Invalid artifact size");if(!path2.isAbsolute(outputPath))throw new Error("Artifact output paths must be absolute");if(!options.session&&!artifact.browserId&&!options.browser)throw new Error("An artifact requires its originating browser");let pinned={...options,...artifact.browserId?{browser:artifact.browserId}:{}};await mkdir(path2.dirname(outputPath),{recursive:!0});let temporary=`${outputPath}.${randomUUID7()}.part`,target=await open(temporary,"wx",384);try{let offset2=0;if(artifact.bytes===0){let empty2=await client.invoke("__artifact_read",{artifactId:artifact.artifactId,offset:0},pinned);if(empty2.totalBytes!==0||empty2.bytes!==0||empty2.base64!=="")throw new Error("Artifact export size mismatch")}for(;offset2<artifact.bytes;){let chunk=await client.invoke("__artifact_read",{artifactId:artifact.artifactId,offset:offset2},pinned),bytes=Buffer.from(chunk.base64,"base64");if(!bytes.length||bytes.length!==chunk.bytes||chunk.totalBytes!==artifact.bytes||offset2+bytes.length>artifact.bytes)throw new Error("Invalid artifact chunk or truncated transfer");await target.write(bytes),offset2+=bytes.length}if(await target.sync(),await target.close(),(await stat(temporary)).size!==artifact.bytes)throw new Error("Artifact export size mismatch");return await rename(temporary,outputPath),{outputPath,bytes:artifact.bytes}}catch(error2){throw await target.close().catch(()=>{}),await rm(temporary,{force:!0}),error2}}var CHUNK_BYTES,init_artifacts=__esm({"../client/src/artifacts.ts"(){"use strict";CHUNK_BYTES=256*1024}});function resolveCloudConnectorSelector(connectors,selector){let normalized=selector.trim().toLowerCase();if(!normalized)return{status:"not_found"};let resolveMatches=matches=>matches.length===1?{status:"matched",connector:matches[0]}:matches.length>1?{status:"ambiguous"}:null,exactId=resolveMatches(connectors.filter(connector=>connector.connectorId.toLowerCase()===normalized));if(exactId)return exactId;let exactName=resolveMatches(connectors.filter(connector=>connector.name.toLowerCase()===normalized));return exactName||(resolveMatches(connectors.filter(connector=>connector.connectorId.toLowerCase().includes(normalized)||connector.name.toLowerCase().includes(normalized)))??{status:"not_found"})}function isHeartbeatMessage(msg){return msg&&(msg.type==="ping"||msg.type==="pong")}var init_dist3=__esm({"../cloud/cloud-types/dist/index.js"(){"use strict"}});function boundedNumber(value,fallback,minimum,maximum){let numeric=Number(value);return Number.isFinite(numeric)?Math.min(Math.max(numeric,minimum),maximum):fallback}function resolveObservedActionTimeoutSec(data2){let observe=data2.observe??{},observationMs=boundedNumber(observe.timeoutMs,3e3,500,3e4),navigationGraceMs=boundedNumber(observe.navigationGraceMs,1e3,0,5e3),navigationTimeoutMs=boundedNumber(observe.navigationTimeoutMs,15e3,1e3,3e4),totalMs=observationMs+navigationGraceMs+4e3+navigationTimeoutMs+5e3;return Math.max(20,Math.ceil(totalMs/1e3))}function resolveActionTimeoutSec(action,data2,fallback=15){if(action==="send_request"){let transferTimeoutMs=data2.bodyFile!==void 0||Array.isArray(data2.files)&&data2.files.length>0?SEND_REQUEST_FILE_TRANSFER_TIMEOUT_MS:0;return Math.max(fallback,Math.ceil(((data2.timeout??3e4)+transferTimeoutMs+5e3)/1e3))}if(action==="send_mtop_request")return Math.max(fallback,Math.ceil(((data2.timeout??1e4)+5e3)/1e3));if(action==="page_download")return Math.max(fallback,Math.ceil((Number(data2.timeout??3e4)+1e4)/1e3));if(action==="page_wait"){let waitMs=data2.time??data2.timeout??1e4;return Math.max(fallback,Math.ceil((Number(waitMs)+5e3)/1e3))}if(action==="tab_open"||action==="set_cookie"&&data2.reload===!0)return Math.max(fallback,40);if(action==="browser_launch")return Math.max(fallback,90);if(action==="browser_close")return Math.max(fallback,BROWSER_CLOSE_TIMEOUT_SEC);if(action==="run_actions"){let timeoutSec=(Array.isArray(data2.actions)?data2.actions:[{}]).reduce((total,step)=>{let stepAction=String(step?.action??"");return!stepAction||stepAction==="run_actions"?total+15:total+resolveActionTimeoutSec(stepAction,step?.data??{},15)},0);return Math.max(fallback,timeoutSec)}let observedAction=OBSERVED_PAGE_ACTIONS.has(action),observationEnabled=action==="page_eval"?data2.observe!==null&&typeof data2.observe=="object"&&!Array.isArray(data2.observe):data2.observe!==!1;if(action==="page_upload"){let actionTimeoutSec=observationEnabled?resolveObservedActionTimeoutSec(data2):fallback;return Math.max(fallback,actionTimeoutSec+Math.ceil(PAGE_UPLOAD_FILE_TRANSFER_TIMEOUT_MS/1e3))}return observedAction&&observationEnabled?Math.max(fallback,resolveObservedActionTimeoutSec(data2)):fallback}var SEND_REQUEST_FILE_TRANSFER_TIMEOUT_MS,PAGE_UPLOAD_FILE_TRANSFER_TIMEOUT_MS,BROWSER_CLOSE_TIMEOUT_SEC,OBSERVED_PAGE_ACTIONS,init_actionTimeouts=__esm({"../client/src/actionTimeouts.ts"(){"use strict";init_dist2();SEND_REQUEST_FILE_TRANSFER_TIMEOUT_MS=12e4,PAGE_UPLOAD_FILE_TRANSFER_TIMEOUT_MS=18e4,BROWSER_CLOSE_TIMEOUT_SEC=1080;OBSERVED_PAGE_ACTIONS=new Set(PAGE_ACT_ACTIONS)}});function assertProviderProtocolVersion(actual,peer="Provider"){if(actual!==3)throw new Error(`PROVIDER_PROTOCOL_MISMATCH: ${peer} uses protocol ${String(actual??"unknown")}; expected 3. Update Mearl and the Provider package, then restart the services.`)}var init_protocol=__esm({"../provider/dist/protocol.js"(){"use strict"}});var init_types2=__esm({"../provider/dist/types.js"(){"use strict";init_protocol()}});var PROVIDER_ACTIONS,init_actions=__esm({"../provider/dist/actions.js"(){"use strict";init_browserActionProtocol();PROVIDER_ACTIONS=Object.keys(BROWSER_CAPABILITY_ACTIONS)}});var init_provider_runtime=__esm({"../provider/dist/provider-runtime.js"(){"use strict";init_types2();init_actions()}});import{existsSync as existsSync5,readFileSync as readFileSync10,renameSync as renameSync7,rmSync as rmSync8,statSync as statSync8,writeFileSync as writeFileSync6}from"node:fs";import{isAbsolute as isAbsolute4,join as join11,relative as relative3,resolve as resolve4,sep as sep3}from"node:path";function liveProviderFile(providerId){return keyedConfigFile(LIVE_PROVIDERS_SUBDIR,providerId)}function listInstalledProviders(){let manifests=[];for(let file of listRecordFiles(keyedDir(INSTALLED_PROVIDERS_SUBDIR)))try{let parsed=JSON.parse(readFileSync10(file,"utf8"));parsed&&typeof parsed.providerId=="string"&&typeof parsed.packageName=="string"&&manifests.push(parsed)}catch{}return manifests.sort((a,b)=>a.providerId.localeCompare(b.providerId))}function readLiveProvider(providerId){let record2=readRecord(liveProviderFile(providerId));return record2&&isAlive(record2.pid)?record2:null}var INSTALLED_PROVIDERS_SUBDIR,LIVE_PROVIDERS_SUBDIR,init_registry2=__esm({"../provider/dist/registry.js"(){"use strict";init_dist();init_types2();init_provider_runtime();init_protocol();INSTALLED_PROVIDERS_SUBDIR="providers/installed",LIVE_PROVIDERS_SUBDIR="providers/live"}});import{spawn as spawn3}from"node:child_process";import{randomUUID as randomUUID8}from"node:crypto";import{tmpdir as tmpdir2}from"node:os";import{join as join12}from"node:path";function providerControlHostFile(){return keyedConfigFile("control","providers")}function providerControlSocketPath(pid){return process.platform==="win32"?`\\\\.\\pipe\\mearl-provider-control-${pid}`:join12(tmpdir2(),`mearl-provider-control-${pid}.sock`)}function readProviderControlHost(){let record2=readRecord(providerControlHostFile());return!record2?.ready||typeof record2.socketPath!="string"||!isAlive(record2.pid)?null:(assertProviderProtocolVersion(record2.protocolVersion,"Provider control host"),record2)}function ensureProviderControlHost(timeoutMs=1e4){if(startup)return startup;let pending=(async()=>{let file=providerControlHostFile(),deadline=Date.now()+timeoutMs;for(;Date.now()<deadline;){let ready=readProviderControlHost();if(ready)return ready;let previous=readRecord(file);if(!previous||!isAlive(previous.pid)){previous?removeRecordIfPid(file,previous.pid):removeInvalidRecord(file);let token=randomUUID8();if(claimRecord(file,{pid:process.pid,socketPath:"",token,ready:!1,protocolVersion:3}))try{let entry=process.env.MEARL_NATIVE_HOST_ENTRY,child=spawn3(entry?process.execPath:"mearl-native-host",[...entry?[entry]:[],"--provider-control",token],{detached:!0,stdio:"ignore",windowsHide:!0});if(await new Promise((resolve7,reject)=>{child.once("error",reject),child.once("spawn",resolve7)}),child.unref(),!child.pid)throw new Error("Provider control host did not report its pid");writeRecordIfPid(file,process.pid,{pid:child.pid,socketPath:providerControlSocketPath(child.pid),token,ready:!1,protocolVersion:3})}catch(error2){throw removeRecordIfPid(file,process.pid),error2}}await new Promise(resolve7=>setTimeout(resolve7,50))}throw new Error("Provider control host startup timed out")})();return startup=pending,pending.finally(()=>{startup===pending&&(startup=void 0)}).catch(()=>{}),pending}var startup,init_control_host=__esm({"../provider/dist/control-host.js"(){"use strict";init_protocol();init_dist()}});var init_host=__esm({"../provider/dist/host.js"(){"use strict";init_registry2();init_control_host()}});import net from"node:net";import{chmodSync,rmSync as rmSync9}from"node:fs";import{dirname as dirname6}from"node:path";var MAX_REQUEST_BYTES,init_server=__esm({"../provider/dist/server.js"(){"use strict";init_dist();init_actions();init_provider_runtime();init_registry2();init_types2();init_protocol();MAX_REQUEST_BYTES=16*1024*1024}});var init_dist4=__esm({"../provider/dist/index.js"(){"use strict";init_types2();init_protocol();init_actions();init_registry2();init_provider_runtime();init_server()}});import{spawn as spawn4}from"node:child_process";import{closeSync as closeSync6,existsSync as existsSync6,mkdirSync as mkdirSync6,openSync as openSync6}from"node:fs";import{join as join13}from"node:path";var init_launcher=__esm({"../provider/dist/launcher.js"(){"use strict";init_dist();init_registry2();init_protocol()}});var init_runtime=__esm({"../provider/dist/runtime.js"(){"use strict";init_launcher();init_registry2();init_provider_runtime()}});import fs2 from"node:fs";function resolveValue(value){if(typeof value!="string"||!value.startsWith(FILE_REF_PREFIX))return value;let filePath=value.slice(FILE_REF_PREFIX.length);if(!filePath)return value;let content;try{content=fs2.readFileSync(filePath,"utf-8")}catch{return value}try{return JSON.parse(content)}catch{return content}}function resolveFileRefs(data2){if(data2===null||typeof data2!="object"||Array.isArray(data2))return data2;let out={};for(let[key2,value]of Object.entries(data2))out[key2]=resolveValue(value);return out}var FILE_REF_PREFIX,init_fileRefs=__esm({"../client/src/fileRefs.ts"(){"use strict";FILE_REF_PREFIX="@"}});import fs3 from"node:fs";import os from"node:os";import path3 from"node:path";function isAlive2(pid){if(!pid||pid<=0)return!1;try{return process.kill(pid,0),!0}catch(error2){return error2.code==="EPERM"}}function listLiveBrowsers(){let files;try{files=fs3.readdirSync(BROWSERS_DIR).filter(name=>name.endsWith(".json"))}catch{return[]}let out=[];for(let name of files){let rec;try{rec=JSON.parse(fs3.readFileSync(path3.join(BROWSERS_DIR,name),"utf-8"))}catch{continue}rec&&typeof rec.pid=="number"&&rec.socketPath&&isAlive2(rec.pid)&&out.push(rec)}return out.sort((a,b)=>(b.lastFocusedAt??0)-(a.lastFocusedAt??0))}function findBrowser(browsers,selector){let sel=selector.trim().toLowerCase();if(!sel)return null;let byId=browsers.find(b=>b.browserId.toLowerCase()===sel);if(byId)return byId;let exactNameMatches=browsers.filter(b=>b.browserName.toLowerCase()===sel);if(exactNameMatches.length>1)throw new Error(`Browser name "${selector.trim()}" is ambiguous. Use a full browserId: ${exactNameMatches.map(browser=>browser.browserId).join(", ")}`);return exactNameMatches[0]?exactNameMatches[0]:browsers.find(b=>b.browserId.toLowerCase().includes(sel)||b.browserName.toLowerCase().includes(sel))??null}function describeBrowser(b){let idShort=b.browserId.length>12?`${b.browserId.slice(0,8)}…`:b.browserId;return`${b.browserName} [${idShort}]`}var BROWSERS_DIR,init_browserRegistry=__esm({"../client/src/browserRegistry.ts"(){"use strict";BROWSERS_DIR=path3.join(os.homedir(),".mearl","browsers")}});import net2 from"node:net";import fs4 from"node:fs";import os2 from"node:os";import path4 from"node:path";import{StringDecoder}from"node:string_decoder";function toControlSourceWireValue(source){return source==="remote"||source==="cloud"?"cloud":"local"}function setDefaultBrowser(selector){defaultBrowser=selector&&selector.trim()?selector.trim():void 0}function getDefaultBrowser(){return defaultBrowser}function findConnectedBrowser(items,selector){let normalized=selector.trim().toLowerCase(),connected=items.filter(item=>item.status==="connected"),byId=connected.find(item=>String(item.browserId).toLowerCase()===normalized);if(byId)return byId;let exactNameMatches=connected.filter(item=>String(item.name).toLowerCase()===normalized);if(exactNameMatches.length>1)throw new Error(`Browser name "${selector.trim()}" is ambiguous. Use a full browserId: ${exactNameMatches.map(item=>String(item.browserId)).join(", ")}`);return exactNameMatches[0]??connected.find(item=>String(item.browserId).toLowerCase().includes(normalized)||String(item.name).toLowerCase().includes(normalized))??null}function decorateBrowserListResult(result){let browsers=Array.isArray(result?.browsers)?result.browsers:[],selector=getDefaultBrowser()??process.env.MEARL_BROWSER,selected=selector?findConnectedBrowser(browsers,selector):browsers.find(browser=>browser.status==="connected")??null;return{...result,defaultBrowserId:selected?.browserId??null}}function browserListFromProviderRecords(browsers){let providerBrowsers=browsers.filter(browser=>browser.transport==="provider").map(browser=>({browserId:browser.browserId,name:browser.browserName,type:"managed",status:browser.status??"running_disconnected",transport:"provider",provider:browser.provider,ownership:browser.ownership,persistence:browser.persistence,operations:browser.operations,pairing:browser.pairing,references:browser.references,details:browser.details}));return{count:providerBrowsers.length,connectedCount:providerBrowsers.filter(browser=>browser.status==="connected").length,browsers:providerBrowsers}}function withDiscoveryWarning(result,error2){let detail=(error2.cause??error2)?.code??(error2 instanceof Error?error2.message.split(`
|
|
624
624
|
`)[0]:String(error2));return{...result,warnings:[...Array.isArray(result.warnings)?result.warnings:[],`Local CDP discovery could not complete: ${detail}`]}}function cdpOnlyBrowserList(result){let cdpBrowsers=(Array.isArray(result.browsers)?result.browsers:[]).filter(browser=>browser.transport==="cdp");return{...result,count:cdpBrowsers.length,connectedCount:cdpBrowsers.filter(browser=>browser.status==="connected").length,browsers:cdpBrowsers}}function areInstalledProvidersRunning(){return listInstalledProviders().every(provider=>readLiveProvider(provider.providerId))}function isConnectedBrowser(browser){return browser.status===void 0||browser.status==="connected"}function isOrdinaryLocalBrowser(browser){return!browser.browserId.startsWith("managed:")&&(browser.transport==="extension"||browser.transport==="cdp")}function supportsAction(browser,action,data2){if(browser.transport==="provider"){if(action==="browser_control"||action==="browser_release"&&data2[SESSION_OPTION])return!0;if(action.startsWith("__artifact_"))return browser.capabilities?.actions.includes("page_upload")===!0||(action==="__artifact_read"||action==="__artifact_remove")&&browser.capabilities?.actions.includes("page_download")===!0;if(action==="run_actions"&&data2[SESSION_OPTION])return!0}if(action==="get_versions")return!0;if(action==="get_browser_access_url")return browser.transport!=="provider"||browser.operations?.includes("access")===!0;if(action==="browser_close"){if(browser.transport!=="provider")return!0;let required=data2.deleteProfile===!0?"deleteData":"close";return browser.operations?.includes(required)===!0}return action==="browser_release"?browser.transport!=="provider"||browser.operations?.includes("detach")===!0:isConnectedBrowser(browser)?browser.transport!=="provider"||browser.capabilities?.actions.some(capability=>capability===action)===!0:!1}function routableBrowsers(browsers,action,data2,transport){return browsers.filter(browser=>isConnectedBrowser(browser)&&(action!=="browser_list"||browser.transport!=="provider")&&!(transport==="cdp"&&browser.transport==="provider")&&supportsAction(browser,action,data2))}function resolveLocalActionTarget(action,data2,options={}){let selector=options.browser??defaultBrowser??process.env.MEARL_BROWSER??void 0,socketPath=options.socketPath??process.env.MEARL_SOCKET_PATH?.trim();if(socketPath&&(options.socketPath||!selector))return{socketPath};let browsers=listLiveBrowsers(),target=selector?findBrowser(browsers,selector):routableBrowsers(browsers,action,data2,options.transport)[0];return target?{browser:target.browserId}:void 0}function selectBrowserResult(data2){let selector=typeof data2.browser=="string"?data2.browser.trim():"";if(!selector)return setDefaultBrowser(void 0),{selected:null,note:"Cleared: default target is now the most-recently focused browser."};let browsers=listLiveBrowsers(),controllableBrowsers=browsers.filter(isConnectedBrowser),match=findBrowser(controllableBrowsers,selector);if(!match)throw new Error(formatNoMatchError(selector,browsers));return setDefaultBrowser(match.browserId),{selected:{browserId:match.browserId,name:match.browserName}}}function instanceSocketPath(pid){return process.platform==="win32"?`\\\\.\\pipe\\mearl-agent-${pid}`:path4.join(os2.tmpdir(),`mearl-agent-${pid}.sock`)}function discoveryCacheKey(){let browsers=listLiveBrowsers().map(browser=>[browser.browserId,browser.pid,browser.socketPath,browser.status,browser.updatedAt]).sort((a,b)=>String(a[0]).localeCompare(String(b[0]))),providers=listInstalledProviders().map(provider=>provider.providerId).sort();return JSON.stringify({browsers,providers})}async function spawnNativeHost(args){let{spawn:spawn10}=await import("node:child_process");return new Promise((resolve7,reject)=>{let child=spawn10("mearl-native-host",args,{detached:!0,stdio:"ignore",windowsHide:!0});child.once("error",reject),child.once("spawn",()=>{child.unref(),resolve7(child.pid)})})}async function trySpawnNativeHost(){if(persistentSpawnPromise)return persistentSpawnPromise;let spawnPromise=(async()=>{try{return await spawnNativeHost(["--daemon"]),!0}catch{return!1}})();persistentSpawnPromise=spawnPromise;try{return await spawnPromise}finally{persistentSpawnPromise===spawnPromise&&(persistentSpawnPromise=null)}}async function discoverLocalBrowserList(timeoutSec,controlSource){if(discoveryListPromise)return discoveryListPromise;let cacheKey=discoveryCacheKey();if(discoveryListCache&&discoveryListCache.key===cacheKey&&Date.now()<discoveryListCache.until){if(discoveryListCache.error)throw discoveryListCache.error;return discoveryListCache.result}discoveryListCache=null;let request=(async()=>{let pid=await spawnNativeHost(["--daemon","--discover-cdp"]);if(pid===void 0)throw new Error("Native host did not report its process id");let socketPath=instanceSocketPath(pid),deadline=Date.now()+Math.min(timeoutSec*1e3,DISCOVERY_STARTUP_TIMEOUT_MS);for(;;)try{return await invokeOnce("browser_list",{},timeoutSec,void 0,socketPath,controlSource)}catch(error2){if(!isConnError(error2)||Date.now()>=deadline)throw error2;await new Promise(resolve7=>setTimeout(resolve7,DISCOVERY_RETRY_INTERVAL_MS))}})();discoveryListPromise=request;try{let result=await request;return result.browsers?.some(browser=>browser.transport==="cdp"&&browser.status==="connected"&&!browser.browserId?.startsWith("managed:"))||(discoveryListCache={key:cacheKey,result,until:Date.now()+DISCOVERY_MISS_CACHE_MS}),result}catch(error2){throw discoveryListCache={key:cacheKey,error:error2,until:Date.now()+DISCOVERY_MISS_CACHE_MS},error2}finally{discoveryListPromise===request&&(discoveryListPromise=null)}}function readOwnedLegacySocketIdentity(){if(process.platform==="win32")return null;try{let stat2=fs4.lstatSync(SOCKET_PATH);return!stat2.isSocket()||process.getuid&&stat2.uid!==process.getuid()?null:{dev:stat2.dev,ino:stat2.ino}}catch{return null}}function removeStaleLegacySocket(identity){if(identity)try{let current2=fs4.lstatSync(SOCKET_PATH);if(!current2.isSocket()||current2.dev!==identity.dev||current2.ino!==identity.ino)return;fs4.unlinkSync(SOCKET_PATH)}catch{}}function invoke(action,data2={},options={}){if(action==="__resolve_action_target"){if(!isBrowserCapabilityAction(data2.action))return Promise.reject(new Error("Invalid browser capability action"));let target=resolveLocalActionTarget(data2.action,data2.data??{},options),browser2=target?.browser??(target?.socketPath?listLiveBrowsers().find(item=>item.socketPath===target.socketPath&&supportsAction(item,data2.action,data2.data??{}))?.browserId:void 0);return Promise.resolve(browser2?{browser:browser2}:null)}if(action==="browser_select_browser")try{return Promise.resolve(selectBrowserResult(data2))}catch(error2){return Promise.reject(error2)}let{timeoutSec=resolveActionTimeoutSec(action,data2),transport,browser,controlSource="local"}=options;if(action==="__control_session_resolve")return resolveControlSession(data2.sessionId,timeoutSec,controlSource);let resolvedData=resolveFileRefs(data2);if(Object.defineProperty(resolvedData,SESSION_OPTION,{value:options.controlSessionId}),action==="browser_list")return dispatchInvoke(action,resolvedData,timeoutSec,transport,void 0,controlSource,options.socketPath).then(result=>decorateBrowserListResult(transport==="cdp"?cdpOnlyBrowserList(result):result));let selector=browser??defaultBrowser??process.env.MEARL_BROWSER??void 0;return dispatchInvoke(action,resolvedData,timeoutSec,transport,selector,controlSource,options.socketPath)}async function resolveControlSession(sessionId,timeoutSec,controlSource){let browsers=listLiveBrowsers(),configuredSocketPath=process.env.MEARL_SOCKET_PATH?.trim(),sockets=configuredSocketPath?[configuredSocketPath]:[...new Set(browsers.filter(browser=>browser.transport!=="provider").map(browser=>browser.socketPath))],provider=configuredSocketPath?void 0:browsers.find(browser=>browser.transport==="provider");!sockets.length&&!provider&&sockets.push(SOCKET_PATH);let requests=sockets.map(socketPath=>invokeOnce("__control_session_resolve",{sessionId},timeoutSec,void 0,socketPath,controlSource));provider&&requests.push(invokeOnce("__control_session_resolve",{sessionId},timeoutSec,void 0,provider.socketPath,controlSource,provider.browserId));let results=await Promise.allSettled(requests),sessions=results.flatMap(result=>result.status==="fulfilled"&&result.value?.session?.sessionId===sessionId?[result.value.session]:[]);if(sessions.length>1)throw new Error("CONTROL_SESSION_AMBIGUOUS: multiple hosts own this id");if(sessions.length)return{session:sessions[0]};let failure2=results.find(result=>result.status==="rejected"&&!isConnError(result.reason));if(failure2?.status==="rejected")throw failure2.reason;return{session:null}}function isConnError(error2){let code=error2?.code;return code==="ENOENT"||code==="ECONNREFUSED"}function isSocketSandboxError(error2){return error2?.code==="EPERM"}function isUnavailableConnection(error2){let cause=error2.cause??error2,code=cause?.code;return isConnError(cause)||isSocketSandboxError(cause)||code==="EACCES"}async function dispatchInvoke(action,data2,timeoutSec,transport,selector,controlSource,socketPath){let configuredSocketPath=socketPath??process.env.MEARL_SOCKET_PATH?.trim();if(configuredSocketPath&&(socketPath||!selector))try{return await invokeOnce(action,data2,timeoutSec,transport,configuredSocketPath,controlSource)}catch(error2){throw isConnError(error2)||isSocketSandboxError(error2)?new Error(formatConnectError(error2,configuredSocketPath),{cause:error2}):error2}let browsers=listLiveBrowsers(),controllableBrowsers=browsers.filter(isConnectedBrowser);if(action==="browser_list"&&transport==="cdp"){let registeredCdp=controllableBrowsers.find(browser=>browser.transport==="cdp"&&isOrdinaryLocalBrowser(browser));if(registeredCdp)try{return await invokeOnce(action,data2,timeoutSec,transport,registeredCdp.socketPath,controlSource)}catch(error2){if(!isConnError(error2))throw error2}try{return await discoverLocalBrowserList(timeoutSec,controlSource)}catch(error2){if(isUnavailableConnection(error2)&&browsers.some(browser=>browser.transport==="provider")&&areInstalledProvidersRunning())return withDiscoveryWarning(browserListFromProviderRecords(browsers),error2);throw error2}}if(action==="browser_list"&&!controllableBrowsers.some(isOrdinaryLocalBrowser))try{return await discoverLocalBrowserList(timeoutSec,controlSource)}catch(error2){if(isUnavailableConnection(error2)&&browsers.some(browser=>browser.transport==="provider")&&areInstalledProvidersRunning())return withDiscoveryWarning(browserListFromProviderRecords(browsers),error2);throw error2}if(selector){let match=findBrowser(browsers,selector);if(!match)throw new Error(formatNoMatchError(selector,browsers));if(transport==="cdp"&&match.transport==="provider")throw new Error(`Browser ${describeBrowser(match)} does not support the CDP transport`);if(match.status==="pairing"&&action!=="get_versions"&&action!=="browser_close"&&action!=="browser_release"&&action!=="browser_control")throw new Error(`Browser ${describeBrowser(match)} is waiting for device pairing`);if(!supportsAction(match,action,data2))throw new Error(`Browser ${describeBrowser(match)} does not support ${action}`);try{return await invokeOnce(action,data2,timeoutSec,transport,match.socketPath,controlSource,match.transport==="provider"?match.browserId:void 0)}catch(error2){throw isConnError(error2)||isSocketSandboxError(error2)?new Error(formatConnectError(error2,match.socketPath),{cause:error2}):error2}}let candidates=routableBrowsers(browsers,action,data2,transport);if(candidates.length>0){let target=candidates[0];try{return await invokeOnce(action,data2,timeoutSec,transport,target.socketPath,controlSource,target.transport==="provider"?target.browserId:void 0)}catch(error2){if(isSocketSandboxError(error2))throw new Error(formatConnectError(error2,target.socketPath),{cause:error2});if(!isConnError(error2))throw error2}}if(action==="browser_list")try{return await discoverLocalBrowserList(timeoutSec,controlSource)}catch(error2){if(isUnavailableConnection(error2)&&browsers.some(browser=>browser.transport==="provider")&&areInstalledProvidersRunning())return withDiscoveryWarning(browserListFromProviderRecords(browsers),error2);throw error2}return invokeViaLegacy(action,data2,timeoutSec,transport,controlSource,!1)}async function invokeViaLegacy(action,data2,timeoutSec,transport,controlSource,isRetry,localOnly=!1){let legacySocketIdentity=readOwnedLegacySocketIdentity();try{return await invokeOnce(action,data2,timeoutSec,transport,SOCKET_PATH,controlSource)}catch(error2){let err=error2;if((err.code==="ENOENT"||err.code==="ECONNREFUSED")&&!isRetry&&(err.code==="ECONNREFUSED"&&removeStaleLegacySocket(legacySocketIdentity),await trySpawnNativeHost())){await new Promise(r=>setTimeout(r,1200));let target=listLiveBrowsers().find(browser=>isConnectedBrowser(browser)&&(!localOnly||browser.transport!=="provider")&&!(transport==="cdp"&&browser.transport==="provider")&&supportsAction(browser,action,data2));return target?invokeOnce(action,data2,timeoutSec,transport,target.socketPath,controlSource,target.transport==="provider"?target.browserId:void 0):invokeViaLegacy(action,data2,timeoutSec,transport,controlSource,!0,localOnly)}throw err instanceof Error&&!isConnError(err)&&!isSocketSandboxError(err)?err:new Error(formatConnectError(err,SOCKET_PATH),{cause:error2})}}async function invokeOnce(action,data2,timeoutSec,transport,socketPath,controlSource,providerTarget){providerTarget&&(socketPath=(await ensureProviderControlHost(Math.min(timeoutSec*1e3,1e4))).socketPath);let id=`${Date.now()}_${Math.random().toString(36).slice(2,10)}`,deadline=Date.now()+Math.ceil(timeoutSec*1e3);return new Promise((resolve7,reject)=>{if(process.platform!=="win32"&&fs4.existsSync(socketPath)&&process.getuid&&fs4.statSync(socketPath).uid!==process.getuid()){let error2=new Error(`拒绝连接不属于当前用户的 Socket: ${socketPath}`);Object.assign(error2,{code:"EACCES"}),reject(error2);return}let socket=net2.createConnection(socketPath),settled=!1,buffer="",decoder=new StringDecoder("utf8"),settle=(error2,result)=>{settled||(settled=!0,clearTimeout(timer),socket.removeAllListeners(),socket.end(),error2?reject(error2 instanceof Error?error2:new Error(String(error2))):resolve7(result))},timer=setTimeout(()=>{settle(new Error(`请求超时,已等待 ${timeoutSec} 秒`))},timeoutSec*1e3);socket.on("connect",()=>{let message={id,action,data:data2,version:CLIENT_VERSION,protocolVersion:3,controlSource:toControlSourceWireValue(controlSource)};data2[SESSION_OPTION]&&(message.controlSessionId=data2[SESSION_OPTION]),transport&&(message.transport=transport),providerTarget&&(message.providerTarget=providerTarget,message.deadline=deadline),socket.write(JSON.stringify(message)+`
|
|
625
625
|
`)}),socket.on("data",chunk=>{if(buffer+=decoder.write(chunk),Buffer.byteLength(buffer,"utf-8")>MAX_SOCKET_BUFFER_SIZE){settle(new Error(`Socket 响应超过 ${MAX_SOCKET_BUFFER_SIZE} 字节限制`));return}let newlineIndex;for(;(newlineIndex=buffer.indexOf(`
|
|
626
|
-
`))!==-1;){let line=buffer.slice(0,newlineIndex);if(buffer=buffer.slice(newlineIndex+1),!!line.trim())try{let response=JSON.parse(line);if(response.id!==id)continue;response.versionWarning&&console.warn(response.versionWarning),response.success?settle(null,response.data):settle(new Error(response.error||"Socket 服务端返回未知错误"));return}catch{settle(new Error(`无效的 JSON 响应: ${line}`));return}}}),socket.on("error",error2=>settle(error2)),socket.on("end",()=>{settled||settle(new Error("在收到响应前 Socket 已关闭"))})})}function formatConnectError(error2,socketPath){let installHint=`如果尚未安装浏览器插件,请先前往: ${EXTENSION_INSTALL_URL}`;return error2.code==="ENOENT"?[`未找到 Socket: ${socketPath}`,"native host 自动拉起失败。先运行 mearl check --local;缺少命令时运行 npx @mearl/setup --local --yes。","自动拉起只使用默认 daemon 参数;自定义 CDP/Profile/浏览器参数需手工启动,参见 mearl-native-host --help。","","【方式一:浏览器插件模式】",`1. 确认已安装 Mearl 浏览器插件(${EXTENSION_INSTALL_URL})`,"2. 确认已执行 Native Messaging 初始化(安装 @mearl/native-host 时会自动完成)","3. 确认 Chrome 浏览器正在运行,如仍无法连接可在 chrome://extensions 重新加载插件","","【方式二:CDP 远程调试模式(Chrome 145+,无需插件)】",' 访问 chrome://inspect/#remote-debugging,勾选 "Allow remote debugging for this browser instance"'," Chrome 会弹出授权提示,点击允许,native host 自动读取 DevToolsActivePort 文件连接"," 确认有普通网页已打开(非 chrome:// 页面)"].join(`
|
|
626
|
+
`))!==-1;){let line=buffer.slice(0,newlineIndex);if(buffer=buffer.slice(newlineIndex+1),!!line.trim())try{let response=JSON.parse(line);if(response.id!==id)continue;response.versionWarning&&console.warn(response.versionWarning),response.success?settle(null,response.data):settle(new Error(response.error||"Socket 服务端返回未知错误"));return}catch{settle(new Error(`无效的 JSON 响应: ${line}`));return}}}),socket.on("error",error2=>settle(error2)),socket.on("end",()=>{settled||settle(new Error("在收到响应前 Socket 已关闭"))})})}function formatConnectError(error2,socketPath){let installHint=`如果尚未安装浏览器插件,请先前往: ${EXTENSION_INSTALL_URL}`;return error2.code==="ENOENT"?[`未找到 Socket: ${socketPath}`,"native host 自动拉起失败。先运行 mearl check --local;缺少命令时运行 npx --@mearl:registry=https://registry.npmjs.org @mearl/setup --local --yes。","自动拉起只使用默认 daemon 参数;自定义 CDP/Profile/浏览器参数需手工启动,参见 mearl-native-host --help。","","【方式一:浏览器插件模式】",`1. 确认已安装 Mearl 浏览器插件(${EXTENSION_INSTALL_URL})`,"2. 确认已执行 Native Messaging 初始化(安装 @mearl/native-host 时会自动完成)","3. 确认 Chrome 浏览器正在运行,如仍无法连接可在 chrome://extensions 重新加载插件","","【方式二:CDP 远程调试模式(Chrome 145+,无需插件)】",' 访问 chrome://inspect/#remote-debugging,勾选 "Allow remote debugging for this browser instance"'," Chrome 会弹出授权提示,点击允许,native host 自动读取 DevToolsActivePort 文件连接"," 确认有普通网页已打开(非 chrome:// 页面)"].join(`
|
|
627
627
|
`):error2.code==="ECONNREFUSED"?[`Socket 连接被拒绝: ${socketPath}`,"已尝试处理 stale socket,但 native host 自动恢复仍失败。请运行 mearl check --local 定位故障。","请确认 Chrome 浏览器正在运行,可尝试在 chrome://extensions 重新加载插件。","",installHint].join(`
|
|
628
628
|
`):error2.code==="EPERM"?[`Socket 连接被执行环境拦截: ${socketPath}`,"当前进程没有访问该 Mearl Unix Socket 的权限(EPERM)。","如果命令运行在 Agent 沙箱中,请为这个 Socket 配置精确的 Unix Socket 白名单后重试。","无需重新安装浏览器插件,也不要使用 sudo 或改写 Socket 权限。"].join(`
|
|
629
629
|
`):[`Socket 连接失败: ${error2.message}`,"",installHint].join(`
|
|
@@ -656,7 +656,7 @@ Expecting one of '${allowedValues.join("', '")}'`);return this._lifeCycleHooks[e
|
|
|
656
656
|
`),this._exit(0,"commander.version",str)}),this}description(str,argsDescription){return str===void 0&&argsDescription===void 0?this._description:(this._description=str,argsDescription&&(this._argsDescription=argsDescription),this)}summary(str){return str===void 0?this._summary:(this._summary=str,this)}alias(alias){if(alias===void 0)return this._aliases[0];let command=this;if(this.commands.length!==0&&this.commands[this.commands.length-1]._executableHandler&&(command=this.commands[this.commands.length-1]),alias===command._name)throw new Error("Command alias can't be the same as its name");let matchingCommand=this.parent?._findCommand(alias);if(matchingCommand){let existingCmd=[matchingCommand.name()].concat(matchingCommand.aliases()).join("|");throw new Error(`cannot add alias '${alias}' to command '${this.name()}' as already have command '${existingCmd}'`)}return command._aliases.push(alias),this}aliases(aliases){return aliases===void 0?this._aliases:(aliases.forEach(alias=>this.alias(alias)),this)}usage(str){if(str===void 0){if(this._usage)return this._usage;let args=this.registeredArguments.map(arg=>humanReadableArgName(arg));return[].concat(this.options.length||this._helpOption!==null?"[options]":[],this.commands.length?"[command]":[],this.registeredArguments.length?args:[]).join(" ")}return this._usage=str,this}name(str){return str===void 0?this._name:(this._name=str,this)}helpGroup(heading){return heading===void 0?this._helpGroupHeading??"":(this._helpGroupHeading=heading,this)}commandsGroup(heading){return heading===void 0?this._defaultCommandGroup??"":(this._defaultCommandGroup=heading,this)}optionsGroup(heading){return heading===void 0?this._defaultOptionGroup??"":(this._defaultOptionGroup=heading,this)}_initOptionGroup(option){this._defaultOptionGroup&&!option.helpGroupHeading&&option.helpGroup(this._defaultOptionGroup)}_initCommandGroup(cmd){this._defaultCommandGroup&&!cmd.helpGroup()&&cmd.helpGroup(this._defaultCommandGroup)}nameFromFilename(filename){return this._name=path5.basename(filename,path5.extname(filename)),this}executableDir(path8){return path8===void 0?this._executableDir:(this._executableDir=path8,this)}helpInformation(contextOptions){let helper=this.createHelp(),context=this._getOutputContext(contextOptions);helper.prepareContext({error:context.error,helpWidth:context.helpWidth,outputHasColors:context.hasColors});let text3=helper.formatHelp(this,helper);return context.hasColors?text3:this._outputConfiguration.stripColor(text3)}_getOutputContext(contextOptions){contextOptions=contextOptions||{};let error2=!!contextOptions.error,baseWrite,hasColors,helpWidth;return error2?(baseWrite=str=>this._outputConfiguration.writeErr(str),hasColors=this._outputConfiguration.getErrHasColors(),helpWidth=this._outputConfiguration.getErrHelpWidth()):(baseWrite=str=>this._outputConfiguration.writeOut(str),hasColors=this._outputConfiguration.getOutHasColors(),helpWidth=this._outputConfiguration.getOutHelpWidth()),{error:error2,write:str=>(hasColors||(str=this._outputConfiguration.stripColor(str)),baseWrite(str)),hasColors,helpWidth}}outputHelp(contextOptions){let deprecatedCallback;typeof contextOptions=="function"&&(deprecatedCallback=contextOptions,contextOptions=void 0);let outputContext=this._getOutputContext(contextOptions),eventContext={error:outputContext.error,write:outputContext.write,command:this};this._getCommandAndAncestors().reverse().forEach(command=>command.emit("beforeAllHelp",eventContext)),this.emit("beforeHelp",eventContext);let helpInformation=this.helpInformation({error:outputContext.error});if(deprecatedCallback&&(helpInformation=deprecatedCallback(helpInformation),typeof helpInformation!="string"&&!Buffer.isBuffer(helpInformation)))throw new Error("outputHelp callback must return a string or a Buffer");outputContext.write(helpInformation),this._getHelpOption()?.long&&this.emit(this._getHelpOption().long),this.emit("afterHelp",eventContext),this._getCommandAndAncestors().forEach(command=>command.emit("afterAllHelp",eventContext))}helpOption(flags,description){return typeof flags=="boolean"?(flags?(this._helpOption===null&&(this._helpOption=void 0),this._defaultOptionGroup&&this._initOptionGroup(this._getHelpOption())):this._helpOption=null,this):(this._helpOption=this.createOption(flags??"-h, --help",description??"display help for command"),(flags||description)&&this._initOptionGroup(this._helpOption),this)}_getHelpOption(){return this._helpOption===void 0&&this.helpOption(void 0,void 0),this._helpOption}addHelpOption(option){return this._helpOption=option,this._initOptionGroup(option),this}help(contextOptions){this.outputHelp(contextOptions);let exitCode=Number(process2.exitCode??0);exitCode===0&&contextOptions&&typeof contextOptions!="function"&&contextOptions.error&&(exitCode=1),this._exit(exitCode,"commander.help","(outputHelp)")}addHelpText(position,text3){let allowedValues=["beforeAll","before","after","afterAll"];if(!allowedValues.includes(position))throw new Error(`Unexpected value for position to addHelpText.
|
|
657
657
|
Expecting one of '${allowedValues.join("', '")}'`);let helpEvent=`${position}Help`;return this.on(helpEvent,context=>{let helpStr;typeof text3=="function"?helpStr=text3({error:context.error,command:context.command}):helpStr=text3,helpStr&&context.write(`${helpStr}
|
|
658
658
|
`)}),this}_outputHelpIfRequested(args){let helpOption=this._getHelpOption();helpOption&&args.find(arg=>helpOption.is(arg))&&(this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)"))}}}});var program,init_commander=__esm({"../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/index.js"(){init_argument();init_command2();init_error();init_help();init_option();program=new Command}});import{constants,closeSync as closeSync7,fstatSync,lstatSync as lstatSync2,mkdirSync as mkdirSync7,openSync as openSync7,readSync as readSync3,readFileSync as readFileSync13,renameSync as renameSync8,rmSync as rmSync10,writeFileSync as writeFileSync7}from"node:fs";import{createHash as createHash8,randomUUID as randomUUID9}from"node:crypto";import{isAbsolute as isAbsolute6,join as join15}from"node:path";function validateOfficialPluginCatalog(value){if(!value||typeof value!="object"||Array.isArray(value))throw new Error("Invalid managed plugin catalog");let input=value;if(input.schemaVersion!==1||typeof input.revision!="string"||!/^[a-f0-9]{64}$/.test(input.revision)||!input.plugins||typeof input.plugins!="object"||Array.isArray(input.plugins)||Object.keys(input).some(key2=>!["schemaVersion","revision","plugins","selection"].includes(key2))||Object.keys(input.plugins).length>200)throw new Error("Invalid managed plugin catalog");let selection=input.selection;if(selection!==void 0&&(!selection||typeof selection!="object"||Array.isArray(selection)||Object.keys(selection).some(key2=>!["path","sha256"].includes(key2))||typeof selection.path!="string"||!isAbsolute6(selection.path)||selection.path.length>4096||/[\0\r\n]/.test(selection.path)||typeof selection.sha256!="string"||!/^[a-f0-9]{64}$/.test(selection.sha256)))throw new Error("Invalid plugin catalog selection");let plugins={};for(let[name,value2]of Object.entries(input.plugins)){if(assertPluginName(name),!value2||typeof value2!="object"||Array.isArray(value2))throw new Error("Invalid managed plugin declaration");let item=value2;if(Object.keys(item).some(key2=>!["description","installArgs","setup","skill"].includes(key2))||typeof item.description!="string"||item.description.length>1e3||typeof item.skill!="string"||!/^[a-z][a-z0-9-]*$/.test(item.skill)||!Array.isArray(item.installArgs)||item.installArgs.length===0||item.installArgs.length>32||item.installArgs.some(arg=>typeof arg!="string"||arg.length>2048||/[\0\r\n]/.test(arg))||item.setup!==void 0&&(typeof item.setup!="string"||!/^[a-z][a-z0-9-]*$/.test(item.setup)))throw new Error("Invalid managed plugin declaration");plugins[name]={description:item.description,skill:item.skill,installArgs:[...item.installArgs],...item.setup!==void 0&&{setup:item.setup}}}let catalog={schemaVersion:1,revision:input.revision,plugins,...selection?{selection:{path:selection.path,sha256:selection.sha256}}:{}};if(Buffer.byteLength(JSON.stringify(catalog))>MAX_CATALOG_BYTES)throw new Error("Managed plugin catalog exceeds 256 KiB");return catalog}function selectionMatches(selection){try{let before=lstatSync2(selection.path);if(!before.isFile()||before.isSymbolicLink()||before.size>1024*1024)return!1;let fd=openSync7(selection.path,constants.O_RDONLY|(constants.O_NOFOLLOW??0));try{let stat2=fstatSync(fd);if(!stat2.isFile()||stat2.size>1024*1024)return!1;let bytes=Buffer.alloc(stat2.size+1),size=0;for(;size<bytes.length;){let count=readSync3(fd,bytes,size,bytes.length-size,null);if(!count)break;size+=count}return size===stat2.size&&createHash8("sha256").update(bytes.subarray(0,size)).digest("hex")===selection.sha256}finally{closeSync7(fd)}}catch(error2){if(error2.code==="ENOENT")return!1;throw error2}}function readOfficialPluginCatalog(){let source;try{source=readFileSync13(catalogPath(),"utf8")}catch(error2){if(error2.code==="ENOENT")return;throw error2}if(Buffer.byteLength(source)>MAX_CATALOG_BYTES)throw new Error("Managed plugin catalog exceeds 256 KiB");let catalog=validateOfficialPluginCatalog(JSON.parse(source));return catalog.selection&&!selectionMatches(catalog.selection)?void 0:catalog}function replaceOfficialPluginCatalog(revision,plugins,selection){let catalog=validateOfficialPluginCatalog({schemaVersion:1,revision,plugins,...selection?{selection}:{}}),content=JSON.stringify(catalog);if(Buffer.byteLength(content)>MAX_CATALOG_BYTES)throw new Error("Managed plugin catalog exceeds 256 KiB");if(catalog.selection&&!selectionMatches(catalog.selection))throw new Error("PLUGIN_CATALOG_SELECTION_CHANGED");mkdirSync7(pluginDirectory(),{recursive:!0,mode:448});let file=catalogPath(),temporary=`${file}.${randomUUID9()}.tmp`;try{if(writeFileSync7(temporary,content,{mode:384,flag:"wx"}),catalog.selection&&!selectionMatches(catalog.selection))throw new Error("PLUGIN_CATALOG_SELECTION_CHANGED");renameSync8(temporary,file)}finally{rmSync10(temporary,{force:!0})}refreshOfficialPlugins()}function refreshOfficialPlugins(){let plugins={};try{plugins=readOfficialPluginCatalog()?.plugins??{}}catch{}for(let name of Object.keys(OFFICIAL_PLUGINS))delete OFFICIAL_PLUGINS[name];return Object.assign(OFFICIAL_PLUGINS,plugins),OFFICIAL_PLUGINS}function findOfficialPlugin(name){let plugins=refreshOfficialPlugins();return Object.hasOwn(plugins,name)?plugins[name]:void 0}function listOfficialPluginNames(setup){return Object.entries(refreshOfficialPlugins()).filter(([,item])=>setup===void 0||item.setup===setup).map(([name])=>name)}var OFFICIAL_PLUGIN_MANAGER,OFFICIAL_PLUGINS,MAX_CATALOG_BYTES,catalogPath,init_officialPluginCatalog=__esm({"../client/src/plugins/officialPluginCatalog.ts"(){"use strict";init_pluginRegistry();OFFICIAL_PLUGIN_MANAGER="@mearl/setup:official",OFFICIAL_PLUGINS={},MAX_CATALOG_BYTES=256*1024,catalogPath=()=>join15(pluginDirectory(),".official-catalog.json");refreshOfficialPlugins()}});import{closeSync as closeSync8,mkdirSync as mkdirSync8,openSync as openSync8,rmSync as rmSync11,statSync as statSync9,writeFileSync as writeFileSync8}from"node:fs";import{join as join16}from"node:path";import{spawn as spawn5}from"node:child_process";function delay(milliseconds){return new Promise(resolve7=>setTimeout(resolve7,milliseconds))}function installLockPath(name){return join16(pluginDirectory(),`.official-${name}.install.lock`)}async function acquireInstallLock(name){let directory=pluginDirectory();mkdirSync8(directory,{recursive:!0,mode:448});let lockPath=installLockPath(name),deadline=Date.now()+INSTALL_TIMEOUT_MS;for(;;){let descriptor;try{return descriptor=openSync8(lockPath,"wx",384),writeFileSync8(descriptor,`${process.pid}
|
|
659
|
-
`),closeSync8(descriptor),()=>rmSync11(lockPath,{force:!0})}catch(error2){if(descriptor!==void 0&&closeSync8(descriptor),error2.code!=="EEXIST")throw error2}if(findPluginRegistration(name))return()=>{};try{if(Date.now()-statSync9(lockPath).mtimeMs>INSTALL_LOCK_STALE_MS){rmSync11(lockPath,{force:!0});continue}}catch(error2){if(error2.code==="ENOENT")continue;throw error2}if(Date.now()>=deadline)throw new Error(`等待安装官方插件 ${name} 超时`);await delay(250)}}function appendOutput(current2,chunk){return current2.length>=MAX_INSTALL_OUTPUT?current2:(current2+chunk.toString("utf8")).slice(0,MAX_INSTALL_OUTPUT)}function installWithSetup(name){return new Promise((resolve7,reject)=>{let command=process.platform==="win32"?"npx.cmd":"npx",child=spawn5(command,["-y",`@mearl/setup@${CLIENT_VERSION}`,"plugin","ensure",name],{stdio:["ignore","pipe","pipe"],windowsHide:!0}),stdout="",stderr="",settled=!1,finish=error2=>{settled||(settled=!0,clearTimeout(timer),error2?reject(error2):resolve7())},timer=setTimeout(()=>{child.kill("SIGTERM"),finish(new Error(`安装官方插件 ${name} 超时`))},INSTALL_TIMEOUT_MS);child.stdout.on("data",chunk=>stdout=appendOutput(stdout,chunk)),child.stderr.on("data",chunk=>stderr=appendOutput(stderr,chunk)),child.once("error",error2=>finish(error2)),child.once("exit",code=>{if(code===0){finish();return}let detail=(stderr||stdout).replace(/[\r\n]+/g," ").trim().slice(-1e3);finish(new Error(detail?`安装官方插件 ${name} 失败 (exit ${code}): ${detail}`:`安装官方插件 ${name} 失败 (exit ${code})`))})})}async function installOfficialPlugin(name,progress){if(findPluginRegistration(name))return;if(!findOfficialPlugin(name))throw new Error(`未知官方插件: ${name}`);let releaseLock=await acquireInstallLock(name);try{if(findPluginRegistration(name))return;let accepted=JSON.stringify(readOfficialPluginCatalog());if(!findOfficialPlugin(name))throw new Error("PLUGIN_CATALOG_SELECTION_CHANGED");if(progress?.(`正在安装官方插件 ${name}...`),await installWithSetup(name),JSON.stringify(readOfficialPluginCatalog())!==accepted)throw new Error("PLUGIN_CATALOG_SELECTION_CHANGED");if(!findPluginRegistration(name))throw new Error(`官方插件 ${name} 安装完成后仍未注册`);progress?.(`官方插件 ${name} 已安装。`)}finally{releaseLock()}}var INSTALL_TIMEOUT_MS,INSTALL_LOCK_STALE_MS,MAX_INSTALL_OUTPUT,activeInstallations,ensureOfficialPluginRegistered,init_officialPluginInstaller=__esm({"../client/src/plugins/officialPluginInstaller.ts"(){"use strict";init_runtimePaths();init_officialPluginCatalog();init_pluginRegistry();INSTALL_TIMEOUT_MS=18e4,INSTALL_LOCK_STALE_MS=5*6e4,MAX_INSTALL_OUTPUT=1024*1024,activeInstallations=new Map;ensureOfficialPluginRegistered=(name,progress)=>{let active=activeInstallations.get(name);if(active)return active;let installation=installOfficialPlugin(name,progress).finally(()=>{activeInstallations.delete(name)});return activeInstallations.set(name,installation),installation}}});import{spawn as spawn6}from"node:child_process";import{createInterface}from"node:readline";function pythonExecutable(){return process.env.MEARL_PYTHON?.trim()||"python3"}function isObject(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function parseDescriptor(message){if(!Array.isArray(message.commands))throw new Error("Python 插件未返回命令列表");let commands=message.commands.map(value=>{if(!isObject(value)||typeof value.signature!="string"||typeof value.description!="string"||typeof value.toolboxOnly!="boolean"||typeof value.runtimeOnly!="boolean")throw new Error("Python 插件返回了无效的命令描述");return{signature:value.signature,description:value.description,toolboxOnly:value.toolboxOnly,runtimeOnly:value.runtimeOnly,recoveryOnly:value.recoveryOnly===!0,migrationOnly:value.migrationOnly===!0}});if(message.name!==void 0&&typeof message.name!="string")throw new Error("Python 插件返回了无效的 name");if(message.description!==void 0&&typeof message.description!="string")throw new Error("Python 插件返回了无效的 description");return{commands,...typeof message.name=="string"?{name:message.name}:{},...typeof message.description=="string"?{description:message.description}:{},...message.toolbox!==void 0?{toolbox:message.toolbox}:{},...message.pageRuntime!==void 0?{pageRuntime:message.pageRuntime}:{},...message.stateStorage!==void 0?{stateStorage:message.stateStorage}:{},...message.taskRecovery!==void 0?{taskRecovery:message.taskRecovery}:{},...message.appProfiles!==void 0?{appProfiles:message.appProfiles}:{}}}function bridgedError(message){let detail=typeof message.message=="string"?message.message:"Python 插件执行失败",restored=pluginExecutionError({message:detail,...typeof message.code=="string"?{code:message.code}:{},...isObject(message.details)?{details:message.details}:{},...isObject(message.metadata)?{metadata:message.metadata}:{}});return typeof message.code=="string"&&/^PLUGIN_[A-Z0-9_]+$/.test(message.code)?Object.assign(new PluginError(message.code,detail),restored):restored}function errorResponse(error2){let serialized=serializePluginExecutionError(error2);if(error2 instanceof PluginError){let prefix=`${error2.code}: `;return{...serialized,code:error2.code,message:error2.message.startsWith(prefix)?error2.message.slice(prefix.length):error2.message}}return serialized}async function runPythonWorker(entry,initial,app,signal,expectedDescriptor){return new Promise((resolve7,reject)=>{let executable=pythonExecutable(),child=spawn6(executable,["-u","-c",PYTHON_BOOTSTRAP,entry],{stdio:["pipe","pipe","pipe"],windowsHide:!0}),lines=createInterface({input:child.stdout}),descriptor,outcome,protocolError,stderr="",failProtocol=error2=>{protocolError||(protocolError=error2 instanceof Error?error2:new Error(String(error2)),cancel())},write=message=>{try{child.stdin.write(`${JSON.stringify(message)}
|
|
659
|
+
`),closeSync8(descriptor),()=>rmSync11(lockPath,{force:!0})}catch(error2){if(descriptor!==void 0&&closeSync8(descriptor),error2.code!=="EEXIST")throw error2}if(findPluginRegistration(name))return()=>{};try{if(Date.now()-statSync9(lockPath).mtimeMs>INSTALL_LOCK_STALE_MS){rmSync11(lockPath,{force:!0});continue}}catch(error2){if(error2.code==="ENOENT")continue;throw error2}if(Date.now()>=deadline)throw new Error(`等待安装官方插件 ${name} 超时`);await delay(250)}}function appendOutput(current2,chunk){return current2.length>=MAX_INSTALL_OUTPUT?current2:(current2+chunk.toString("utf8")).slice(0,MAX_INSTALL_OUTPUT)}function installWithSetup(name){return new Promise((resolve7,reject)=>{let command=process.platform==="win32"?"npx.cmd":"npx",child=spawn5(command,["-y","--@mearl:registry=https://registry.npmjs.org",`@mearl/setup@${CLIENT_VERSION}`,"plugin","ensure",name],{stdio:["ignore","pipe","pipe"],windowsHide:!0}),stdout="",stderr="",settled=!1,finish=error2=>{settled||(settled=!0,clearTimeout(timer),error2?reject(error2):resolve7())},timer=setTimeout(()=>{child.kill("SIGTERM"),finish(new Error(`安装官方插件 ${name} 超时`))},INSTALL_TIMEOUT_MS);child.stdout.on("data",chunk=>stdout=appendOutput(stdout,chunk)),child.stderr.on("data",chunk=>stderr=appendOutput(stderr,chunk)),child.once("error",error2=>finish(error2)),child.once("exit",code=>{if(code===0){finish();return}let detail=(stderr||stdout).replace(/[\r\n]+/g," ").trim().slice(-1e3);finish(new Error(detail?`安装官方插件 ${name} 失败 (exit ${code}): ${detail}`:`安装官方插件 ${name} 失败 (exit ${code})`))})})}async function installOfficialPlugin(name,progress){if(findPluginRegistration(name))return;if(!findOfficialPlugin(name))throw new Error(`未知官方插件: ${name}`);let releaseLock=await acquireInstallLock(name);try{if(findPluginRegistration(name))return;let accepted=JSON.stringify(readOfficialPluginCatalog());if(!findOfficialPlugin(name))throw new Error("PLUGIN_CATALOG_SELECTION_CHANGED");if(progress?.(`正在安装官方插件 ${name}...`),await installWithSetup(name),JSON.stringify(readOfficialPluginCatalog())!==accepted)throw new Error("PLUGIN_CATALOG_SELECTION_CHANGED");if(!findPluginRegistration(name))throw new Error(`官方插件 ${name} 安装完成后仍未注册`);progress?.(`官方插件 ${name} 已安装。`)}finally{releaseLock()}}var INSTALL_TIMEOUT_MS,INSTALL_LOCK_STALE_MS,MAX_INSTALL_OUTPUT,activeInstallations,ensureOfficialPluginRegistered,init_officialPluginInstaller=__esm({"../client/src/plugins/officialPluginInstaller.ts"(){"use strict";init_runtimePaths();init_officialPluginCatalog();init_pluginRegistry();INSTALL_TIMEOUT_MS=18e4,INSTALL_LOCK_STALE_MS=5*6e4,MAX_INSTALL_OUTPUT=1024*1024,activeInstallations=new Map;ensureOfficialPluginRegistered=(name,progress)=>{let active=activeInstallations.get(name);if(active)return active;let installation=installOfficialPlugin(name,progress).finally(()=>{activeInstallations.delete(name)});return activeInstallations.set(name,installation),installation}}});import{spawn as spawn6}from"node:child_process";import{createInterface}from"node:readline";function pythonExecutable(){return process.env.MEARL_PYTHON?.trim()||"python3"}function isObject(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function parseDescriptor(message){if(!Array.isArray(message.commands))throw new Error("Python 插件未返回命令列表");let commands=message.commands.map(value=>{if(!isObject(value)||typeof value.signature!="string"||typeof value.description!="string"||typeof value.toolboxOnly!="boolean"||typeof value.runtimeOnly!="boolean")throw new Error("Python 插件返回了无效的命令描述");return{signature:value.signature,description:value.description,toolboxOnly:value.toolboxOnly,runtimeOnly:value.runtimeOnly,recoveryOnly:value.recoveryOnly===!0,migrationOnly:value.migrationOnly===!0}});if(message.name!==void 0&&typeof message.name!="string")throw new Error("Python 插件返回了无效的 name");if(message.description!==void 0&&typeof message.description!="string")throw new Error("Python 插件返回了无效的 description");return{commands,...typeof message.name=="string"?{name:message.name}:{},...typeof message.description=="string"?{description:message.description}:{},...message.toolbox!==void 0?{toolbox:message.toolbox}:{},...message.pageRuntime!==void 0?{pageRuntime:message.pageRuntime}:{},...message.stateStorage!==void 0?{stateStorage:message.stateStorage}:{},...message.taskRecovery!==void 0?{taskRecovery:message.taskRecovery}:{},...message.appProfiles!==void 0?{appProfiles:message.appProfiles}:{}}}function bridgedError(message){let detail=typeof message.message=="string"?message.message:"Python 插件执行失败",restored=pluginExecutionError({message:detail,...typeof message.code=="string"?{code:message.code}:{},...isObject(message.details)?{details:message.details}:{},...isObject(message.metadata)?{metadata:message.metadata}:{}});return typeof message.code=="string"&&/^PLUGIN_[A-Z0-9_]+$/.test(message.code)?Object.assign(new PluginError(message.code,detail),restored):restored}function errorResponse(error2){let serialized=serializePluginExecutionError(error2);if(error2 instanceof PluginError){let prefix=`${error2.code}: `;return{...serialized,code:error2.code,message:error2.message.startsWith(prefix)?error2.message.slice(prefix.length):error2.message}}return serialized}async function runPythonWorker(entry,initial,app,signal,expectedDescriptor){return new Promise((resolve7,reject)=>{let executable=pythonExecutable(),child=spawn6(executable,["-u","-c",PYTHON_BOOTSTRAP,entry],{stdio:["pipe","pipe","pipe"],windowsHide:!0}),lines=createInterface({input:child.stdout}),descriptor,outcome,protocolError,stderr="",failProtocol=error2=>{protocolError||(protocolError=error2 instanceof Error?error2:new Error(String(error2)),cancel())},write=message=>{try{child.stdin.write(`${JSON.stringify(message)}
|
|
660
660
|
`)}catch(error2){failProtocol(error2)}};child.stderr.setEncoding("utf8"),child.stderr.on("data",chunk=>{stderr=`${stderr}${chunk}`.slice(-STDERR_LIMIT)}),child.stdin.on("error",()=>{});let killTimer,terminalStopRequested=!1,armKill=()=>{killTimer||(killTimer=setTimeout(()=>{terminalStopRequested=!protocolError&&!signal?.aborted&&!!(outcome||initial.mode==="describe"&&descriptor),child.kill("SIGKILL")},1e3),killTimer.unref())},cancel=()=>{child.kill("SIGTERM"),armKill()};signal?.addEventListener("abort",cancel,{once:!0}),signal?.aborted&&cancel(),child.once("error",error2=>{error2.code==="ENOENT"?reject(new Error(`未找到 ${executable},Python 插件需要 Python 3.9 或更高版本(可通过 MEARL_PYTHON 指定解释器)`)):reject(error2)}),lines.on("line",line=>{if(protocolError)return;let message;try{let value=JSON.parse(line);if(!isObject(value)||typeof value.type!="string")throw new Error("消息不是对象");message=value}catch(error2){failProtocol(new Error(`Python 插件输出了无效协议消息: ${line.slice(0,200)}`,error2 instanceof Error?{cause:error2}:void 0));return}if(message.type==="ready"){try{if(descriptor)throw new Error("Python 插件重复发送 ready");if(descriptor=parseDescriptor(message),initial.mode==="run"&&expectedDescriptor&&pluginRecoverySignature(descriptor)!==pluginRecoverySignature(expectedDescriptor))throw new PluginError("PLUGIN_DECLARATION_CHANGED","Python execution declaration differs from its loaded metadata");write({type:"ready_ack"}),initial.mode==="describe"&&armKill()}catch(error2){failProtocol(error2)}return}if(message.type==="progress"){if(!app||typeof message.message!="string"){failProtocol(new Error("Python 插件发送了无效的 progress 消息"));return}try{app.progress(message.message)}catch(error2){failProtocol(error2)}return}if(message.type==="invoke"){if(!app||typeof message.id!="number"||typeof message.action!="string"||!isObject(message.payload)||!isObject(message.options)){failProtocol(new Error("Python 插件发送了无效的 invoke 消息"));return}let{action,payload,options}=message;Promise.resolve().then(()=>action==="plugin_task_checkpoint"&&app.checkpoint?app.checkpoint(payload.data,payload.expectedSequence):action==="plugin_model"&&payload.operation==="run"&&app.model?app.model(payload.request,message.stream?{onProgress:progress=>{!signal?.aborted&&!outcome&&write({type:"invoke-progress",id:message.id,progress})}}:void 0):action==="plugin_state"&&app.state?app.state(payload.collection,payload.operation,payload):action==="plugin_page"&&app.page?app.page(payload.operation,payload):app.invoke(action,payload,options)).then(result=>{write({type:"response",id:message.id,result:result===void 0?null:result})}).catch(error2=>{write({type:"response",id:message.id,error:errorResponse(error2)})});return}if(message.type==="result"){outcome||(outcome={hasResult:!0,result:message.result}),write({type:"terminal_ack"}),armKill();return}if(message.type==="error"){outcome||(outcome={error:bridgedError(message)}),write({type:"terminal_ack"}),armKill();return}failProtocol(new Error(`Python 插件发送了未知消息类型: ${message.type}`))});let finished=!1,finish=code=>{if(!finished){if(finished=!0,signal?.removeEventListener("abort",cancel),killTimer&&clearTimeout(killTimer),lines.close(),child.stdin.destroy(),child.stdout.destroy(),child.stderr.destroy(),signal?.aborted){reject(signal.reason??new Error("Plugin command cancelled"));return}if(protocolError){reject(protocolError);return}if(outcome?.error){reject(outcome.error);return}if(code!==0&&!terminalStopRequested){reject(new Error(stderr.trim()||`Python 插件进程异常退出${code===null?"":` (exit ${code})`}`));return}if(!descriptor){reject(new Error("Python 插件未返回注册信息"));return}if(initial.mode==="run"&&!outcome?.hasResult){reject(new Error("Python 插件未返回执行结果"));return}resolve7({descriptor,...outcome?.hasResult?{result:outcome.result}:{}})}};child.once("exit",finish),child.once("close",finish),write(initial)})}async function describePythonPlugin(entry,options){let{descriptor}=await runPythonWorker(entry,{mode:"describe",options});return descriptor}async function runPythonPluginCommand(entry,command,input,app,context,recovery=!1,expectedDescriptor,runtimeOnly=!1){let{result}=await runPythonWorker(entry,{mode:"run",command,input,options:app.options,identity:context.identity,recovery,runtime:runtimeOnly},app,context.signal,expectedDescriptor);return result}var STDERR_LIMIT,PYTHON_BOOTSTRAP,init_pythonPlugin=__esm({"../client/src/plugins/runtime/pythonPlugin.ts"(){"use strict";init_pluginRecoveryDefinition();init_pluginRegistry();init_pluginExecutorProtocol();STDERR_LIMIT=64*1024,PYTHON_BOOTSTRAP=String.raw`
|
|
661
661
|
import asyncio
|
|
662
662
|
import importlib.util
|
|
@@ -989,7 +989,7 @@ except BaseException as error:
|
|
|
989
989
|
sys.exit(1)
|
|
990
990
|
`}});import{StringDecoder as StringDecoder2}from"node:string_decoder";function readPluginTaskMessages(socket,receive){let decoder=new StringDecoder2("utf8"),buffer="";socket.on("data",chunk=>{try{buffer+=decoder.write(chunk);let end;for(;(end=buffer.indexOf(`
|
|
991
991
|
`))>=0;){let line=buffer.slice(0,end);if(buffer=buffer.slice(end+1),Buffer.byteLength(line)>MAX_MESSAGE_BYTES)throw new Error("Plugin host message is too large");let message=JSON.parse(line);if(!message||typeof message!="object"||!Number.isSafeInteger(message.id))throw new Error("Invalid plugin host message");receive(message)}if(Buffer.byteLength(buffer)>MAX_MESSAGE_BYTES)throw new Error("Plugin host message buffer exceeded its limit")}catch(error2){socket.destroy(error2 instanceof Error?error2:new Error(String(error2)))}})}function writePluginTaskMessage(socket,message){return new Promise((resolve7,reject)=>{try{let text3=JSON.stringify(message)+`
|
|
992
|
-
`;if(Buffer.byteLength(text3)>MAX_MESSAGE_BYTES)throw new Error("Plugin host message is too large");if(socket.destroyed)throw new Error("Plugin host connection is closed");socket.write(text3,error2=>error2?reject(error2):resolve7())}catch(error2){reject(error2)}})}var MAX_MESSAGE_BYTES,init_pluginTaskWire=__esm({"../client/src/plugins/pluginTaskWire.ts"(){"use strict";MAX_MESSAGE_BYTES=96*1024*1024}});var pluginHostConnection_exports={};__export(pluginHostConnection_exports,{PluginHostConnection:()=>PluginHostConnection});import{spawn as spawn7}from"node:child_process";import{closeSync as closeSync9,mkdirSync as mkdirSync9,openSync as openSync9}from"node:fs";import{createConnection}from"node:net";async function ensureHost(runtimePath){let paths=pluginHostPaths(),work=starting.get(paths.registry);work||(work=(async()=>{let lease=readRecord(paths.lease);if(lease&&isProcessOwnerAlive(lease)){if(lease.protocolVersion!==5)throw new Error("Plugin execution service protocol differs; stop the service before updating");return}let lock=`${paths.lease}.start`,existing=readRecord(lock);if(!(existing&&isProcessOwnerAlive(existing))&&(existing?removeRecordIfPid(lock,existing.pid):removeInvalidRecord(lock),!!claimRecord(lock,{pid:process.pid,processIdentity:processIdentity(process.pid)})))try{let owner=readRecord(paths.lease);if(owner&&isProcessOwnerAlive(owner))return;mkdirSync9(paths.home,{recursive:!0,mode:448}),rotateLogIfNeeded(paths.log);let log=openSync9(paths.log,"a",384);try{let child=spawn7(process.execPath,[runtimePath],{detached:!0,stdio:["ignore",log,log],windowsHide:!0});await new Promise((resolve7,reject)=>{child.once("spawn",resolve7),child.once("error",reject)}),child.unref()}finally{closeSync9(log)}let deadline=Date.now()+1e4;for(;Date.now()<deadline;){if(await new Promise(resolve7=>{let probe=createConnection(paths.socket),finish=value=>{probe.destroy(),resolve7(value)};probe.once("connect",()=>finish(!0)),probe.once("error",()=>finish(!1)),probe.setTimeout(200,()=>finish(!1))}))return;await new Promise(resolve7=>setTimeout(resolve7,50))}throw new Error("Plugin execution service startup timed out")}finally{removeRecordIfPid(lock,process.pid)}})().finally(()=>starting.delete(paths.registry)),starting.set(paths.registry,work)),await work}var starting,PluginHostConnection,init_pluginHostConnection=__esm({"../client/src/plugins/clients/pluginHostConnection.ts"(){"use strict";init_runtimePaths();init_dist();init_pluginHostPaths();init_dist2();init_pluginTaskWire();init_pluginExecutorProtocol();starting=new Map;PluginHostConnection=class{constructor(configuration={}){this.configuration=configuration}configuration;get hasInvocationBridge(){return!!this.configuration.invoke}connection;socket;opening=new Set;generation=0;async connect(generation){let check=()=>{if(generation!==this.generation)throw Object.assign(new Error("Plugin task client was disconnected"),{code:"PLUGIN_TASK_CLIENT_DISCONNECTED"})},connectOnce=()=>(check(),this.open(this.configuration.socketPath??pluginHostPaths().socket,generation));try{return await connectOnce()}catch(error2){if(check(),this.configuration.autoStart===!1||this.configuration.socketPath)throw error2;let code=error2.code;if(!["ENOENT","ECONNREFUSED","PLUGIN_TASK_CONNECTION_LOST"].includes(code??""))throw error2}await ensureHost(this.configuration.runtimePath??PLUGIN_TASK_HOST_PATH);let deadline=Date.now()+1e4,failure2;do{check();try{return await connectOnce()}catch(error2){failure2=error2,await new Promise(resolve7=>setTimeout(resolve7,100))}}while(Date.now()<deadline);throw new Error("Plugin execution service did not become ready; inspect its service.log",{cause:failure2})}async open(socketPath,generation){let socket=createConnection(socketPath);this.opening.add(socket);let pending=new Map,sequence=0,fail2=error2=>{for(let request2 of pending.values())clearTimeout(request2.timer),request2.reject(error2);pending.clear()};socket.on("error",fail2),socket.once("close",()=>{fail2(Object.assign(new Error("Plugin execution service connection was lost"),{code:"PLUGIN_TASK_CONNECTION_LOST"})),this.socket===socket&&(this.connection=void 0,this.socket=void 0)});let request=(method,input={},timeoutMs=15e3)=>new Promise((resolve7,reject)=>{let id=++sequence,timer=setTimeout(()=>{pending.delete(id),reject(Object.assign(new Error("Plugin task request timed out; the task may still be running"),{code:"PLUGIN_TASK_REQUEST_TIMEOUT"}))},timeoutMs);pending.set(id,{resolve:resolve7,reject,timer}),writePluginTaskMessage(socket,{type:"request",id,method,input}).catch(error2=>{clearTimeout(timer),pending.delete(id),reject(error2)})});readPluginTaskMessages(socket,message=>{if(message.type==="response"){let call=pending.get(message.id);if(!call)return;clearTimeout(call.timer),pending.delete(message.id),message.error?call.reject(pluginExecutionError(message.error)):call.resolve(message.result)}else message.type==="invoke"?Promise.resolve().then(()=>{if(!this.configuration.invoke)throw new Error("Plugin task client has no invocation bridge");return this.configuration.invoke(message.action,message.payload,message.options)}).then(result=>writePluginTaskMessage(socket,{type:"invoke-result",id:message.id,result}),error2=>writePluginTaskMessage(socket,{type:"invoke-result",id:message.id,error:serializePluginExecutionError(error2)})).catch(()=>{}):socket.destroy(new Error("Invalid plugin task response direction"))});try{await new Promise((resolve7,reject)=>{let timer=setTimeout(()=>{socket.destroy(),reject(new Error("Plugin task connection timed out"))},2e3);socket.once("connect",()=>{clearTimeout(timer),resolve7()}),socket.once("error",error2=>{clearTimeout(timer),reject(error2)})});let scope={access:this.configuration.access??"public",source:this.configuration.source??"sdk",bridge:this.configuration.invoke?"caller":"service",...this.configuration.plugin?{plugin:this.configuration.plugin}:{},...this.configuration.scopeId?{scopeId:this.configuration.scopeId}:{}},status=await request("hello",{...scope,protocolVersion:5});if(status.protocolVersion!==5)throw new Error("Plugin task protocol version mismatch");if(generation!==this.generation||socket.destroyed)throw new Error("Plugin task client was disconnected");return this.socket=socket,{socket,request,status}}catch(error2){throw socket.destroy(),error2}finally{this.opening.delete(socket)}}async request(method,input={},timeoutMs){if(!this.connection){let attempt=this.connect(this.generation);this.connection=attempt,attempt.catch(()=>{this.connection===attempt&&(this.connection=void 0)})}return(await this.connection).request(method,input,timeoutMs)}disconnect(){this.generation++,this.socket?.destroy();for(let socket of this.opening)socket.destroy();this.socket=void 0,this.connection=void 0}}}});import{createHash as createHash9}from"node:crypto";function localWait(promise,signal,timeoutMs=1/0){return new Promise((resolve7,reject)=>{let abort=()=>{cleanup(),reject(signal?.reason??new Error("Plugin task wait aborted"))},timer=Number.isFinite(timeoutMs)?setTimeout(()=>{cleanup(),reject(Object.assign(new Error("Waiting for the plugin task timed out; execution continues"),{code:"PLUGIN_TASK_WAIT_TIMEOUT"}))},Math.max(0,timeoutMs)):void 0,cleanup=()=>{clearTimeout(timer),signal?.removeEventListener("abort",abort)};signal?.addEventListener("abort",abort,{once:!0}),promise.then(value=>{cleanup(),resolve7(value)},error2=>{cleanup(),reject(error2)}),signal?.aborted&&abort()})}var PluginTaskClient,init_pluginTasks2=__esm({"../client/src/plugins/clients/pluginTasks.ts"(){"use strict";init_pluginHostConnection();init_dist();init_pluginHostPaths();init_unified();init_dist2();init_pluginExecutorProtocol();PluginTaskClient=class{constructor(configuration={}){this.configuration=configuration;this.connection=configuration.connection??new PluginHostConnection(configuration)}configuration;connection;request(method,input={},timeoutMs){return this.connection.request(method,input,timeoutMs)}status(){return this.request("status")}diagnose(options={}){return this.request("diagnose",{...options})}checkpoint(taskId){return this.request("checkpoint",{taskId})}resume(taskId,options={}){return this.request("resume",{taskId,revision:options.revision,environment:Object.fromEntries(Object.entries(options.environment??process.env).filter(entry=>entry[1]!==void 0))},45e3)}async start(plugin,invocation,options={}){let{cwd=process.cwd(),environment=process.env,executionTimeoutMs,idempotencyKey,...routing}=options;if(!this.connection.hasInvocationBridge&&routing.browser&&!decodeBrowserTarget(routing.browser)){let client=new UnifiedClient;try{let selection=await client.invoke("browser_select_browser",{browser:routing.browser},routing);if(!selection?.selected?.browserId)throw new Error("Cannot resolve plugin task browser target");routing.browser=selection.selected.browserId}finally{client.disconnect()}}return this.request("start",{plugin,command:invocation.command,input:invocation.input,cwd,environment:Object.fromEntries(Object.entries(environment).filter(entry=>entry[1]!==void 0)),options:routing,executionTimeoutMs,idempotencyKey})}get(taskId){return this.request("get",{taskId})}list(activeOnly=!1,page={}){return this.request("list",{activeOnly,...page})}cancel(taskId){return this.request("cancel",{taskId})}events(taskId,cursor=0,waitMs=0){return this.request("events",{taskId,cursor,waitMs},waitMs+15e3)}resultChunk(taskId,index){return this.request("result",{taskId,index})}async result(taskId){let task=await this.get(taskId);if(task.state!=="success"||!task.result)throw new Error("Plugin task has no successful result");let parts=[];for(let index=0;index<task.result.chunks;index++){let chunk=await this.resultChunk(taskId,index);if(chunk.taskId!==taskId||chunk.index!==index||chunk.sha256!==task.result.sha256||chunk.bytes!==task.result.bytes||chunk.chunks!==task.result.chunks)throw new Error("Plugin task result chunk identity mismatch");parts.push(Buffer.from(chunk.data,"base64"))}let bytes=Buffer.concat(parts);if(bytes.length!==task.result.bytes||createHash9("sha256").update(bytes).digest("hex")!==task.result.sha256)throw new Error("Plugin task result integrity check failed");return JSON.parse(bytes.toString("utf8")).value}async*subscribe(taskId,options={}){let cursor=options.cursor??0;for(;;){options.signal?.throwIfAborted();let page=await localWait(this.events(taskId,cursor,25e3),options.signal);if(options.signal?.throwIfAborted(),yield page,cursor=page.nextCursor,!isPluginTaskActive(page.task.state)&&cursor>=page.task.sequence)return}}async wait(taskId,options={}){if(options.timeoutMs!==void 0&&(!Number.isSafeInteger(options.timeoutMs)||options.timeoutMs<1))throw new Error("Invalid plugin task wait timeout");let deadline=options.timeoutMs===void 0?1/0:Date.now()+options.timeoutMs,cursor=0,lastSequence=-1;for(;;){if(options.signal?.throwIfAborted(),Date.now()>=deadline)throw Object.assign(new Error("Waiting for the plugin task timed out; execution continues"),{code:"PLUGIN_TASK_WAIT_TIMEOUT"});let page=await localWait(this.events(taskId,cursor,Math.min(1e3,Math.max(0,deadline-Date.now()))),options.signal,deadline-Date.now());if(options.signal?.throwIfAborted(),cursor=page.nextCursor,page.task.progress&&page.task.sequence!==lastSequence&&options.onProgress?.(page.task.progress,page.task),lastSequence=page.task.sequence,page.task.state==="success")return options.readResult===!1?page.task:localWait(this.result(taskId),options.signal,deadline-Date.now());if(!isPluginTaskActive(page.task.state))throw pluginExecutionError(page.task.error??{message:`Plugin task ended with ${page.task.state}`})}}async stopHost(){let original=await this.status();await this.request("shutdown"),this.disconnect();let deadline=Date.now()+15e3;for(;;){let lease=readRecord(pluginHostPaths().lease);if(!lease||lease.epoch!==original.host.epoch||!isProcessOwnerAlive(lease))return;if(Date.now()>=deadline)throw new Error("Plugin execution service did not finish stopping");await new Promise(resolve7=>setTimeout(resolve7,50))}}disconnect(){this.configuration.connection||this.connection.disconnect()}}}});import{rmSync as rmSync12}from"node:fs";import{randomUUID as randomUUID10}from"node:crypto";async function cleanupPluginRuntime(){let paths=pluginHostPaths();if(listPluginNames().length)throw new Error("Registered plugins still use the execution service");let lock=`${paths.lease}.start`,starter=readRecord(lock);if(starter&&!isProcessOwnerAlive(starter)?removeRecordIfPid(lock,starter.pid):starter||removeInvalidRecord(lock),!claimRecord(lock,{pid:process.pid,processIdentity:processIdentity(process.pid)}))throw new Error("Plugin execution service is starting; caches were retained");try{let lease=readRecord(paths.lease);if(lease&&isProcessOwnerAlive(lease)){let client=new PluginTaskClient({autoStart:!1});try{await client.stopHost()}finally{client.disconnect()}}let current2=readRecord(paths.lease);if(current2&&isProcessOwnerAlive(current2)||listPluginNames().length)throw new Error("Plugin execution service restarted during cleanup; caches were retained");let store=new PluginTaskStore;if(store.list().some(pluginTaskExecutionUnconfirmed))throw new Error("Plugin execution is not confirmed stopped; task records were retained");if(store.recover(randomUUID10()),store.list().some(pluginTaskExecutionUnconfirmed))throw new Error("Plugin execution is not confirmed stopped; task records were retained");let replacement=readRecord(paths.lease);if(replacement&&isProcessOwnerAlive(replacement)||listPluginNames().length)throw new Error("Plugin execution service restarted during cleanup; caches were retained");for(let file of[paths.tasks,paths.revisions,paths.log,`${paths.log}.1`])rmSync12(file,{recursive:!0,force:!0});current2&&removeRecordIfPid(paths.lease,current2.pid),process.platform!=="win32"&&rmSync12(paths.socket,{force:!0})}finally{removeRecordIfPid(lock,process.pid)}}var init_pluginRuntimeCleanup=__esm({"../client/src/plugins/runtime/pluginRuntimeCleanup.ts"(){"use strict";init_dist();init_pluginHostPaths();init_pluginRegistry();init_pluginTasks2();init_pluginTaskStore()}});var pluginPages_exports={};__export(pluginPages_exports,{PluginPageClient:()=>PluginPageClient});var PluginPageClient,init_pluginPages2=__esm({"../client/src/plugins/clients/pluginPages.ts"(){"use strict";init_unified();PluginPageClient=class{constructor(configuration={},connection){this.connection=connection;connection||(this.client=new UnifiedClient(configuration))}connection;client;call(plugin,operation,input={},options={}){return this.connection?this.connection.request("page.call",{plugin,input:{...input,operation},options,execution:{cwd:process.cwd(),environment:Object.fromEntries(Object.entries(process.env).filter(entry=>entry[1]!==void 0))}},Math.max(12e4,typeof input.timeoutSec=="number"&&Number.isFinite(input.timeoutSec)?(Math.min(300,input.timeoutSec)+5)*1e3:0)):this.client.invoke("plugin_page",{...input,plugin,operation},options)}attach(plugin,input,options={}){return this.call(plugin,"attach",input,options)}get(plugin,instanceId){return this.call(plugin,"get",{instanceId})}list(plugin){return this.call(plugin,"list")}send(plugin,instanceId,message,options={}){return this.call(plugin,"send",{instanceId,message,...options},options)}detach(plugin,instanceId){return this.call(plugin,"detach",{instanceId})}events(plugin,instanceId,cursor=0,waitMs=0){return this.call(plugin,"events",{instanceId,cursor,waitMs})}disconnect(){this.client?.disconnect()}}}});var pluginState_exports={};__export(pluginState_exports,{PluginStateClient:()=>PluginStateClient});var PluginStateClient,init_pluginState2=__esm({"../client/src/plugins/clients/pluginState.ts"(){"use strict";init_pluginHostConnection();PluginStateClient=class{constructor(options={}){this.options=options;this.connection=options.connection??new PluginHostConnection(options)}options;connection;call(plugin,collection,operation,input={},options={}){return this.connection.request("state.call",{plugin,input:{...input,collection,operation},options},3e4)}get(plugin,collection,key2,input={}){return this.call(plugin,collection,"get",{...input,key:key2})}getMany(plugin,collection,keys,input={}){return this.call(plugin,collection,"get-many",{...input,keys})}list(plugin,collection,input={}){return this.call(plugin,collection,"list",input)}transact(plugin,collection,transaction,input={}){return this.call(plugin,collection,"transact",{...input,transaction})}disconnect(){this.options.connection||this.connection.disconnect()}}}});var pluginModels_exports={};__export(pluginModels_exports,{PluginModelClient:()=>PluginModelClient});var PluginModelClient,init_pluginModels2=__esm({"../client/src/plugins/clients/pluginModels.ts"(){"use strict";init_pluginTasks2();PluginModelClient=class{tasks;constructor(options={}){this.tasks=new PluginTaskClient(options)}capabilities(){return this.tasks.connection.request("model.capabilities")}list(provider="codex"){return this.tasks.connection.request("model.list",{provider},2e4)}async start(plugin,request,options={}){let{cwd=process.cwd(),environment=process.env,executionTimeoutMs,idempotencyKey,...routing}=options;return this.tasks.connection.request("start",{kind:"model",plugin,command:"model",input:{request},cwd,environment:Object.fromEntries(Object.entries(environment).filter(entry=>entry[1]!==void 0)),options:routing,executionTimeoutMs,idempotencyKey})}async run(plugin,request,options={}){let{signal,onProgress,...execution}=options;signal?.throwIfAborted();let started=await this.start(plugin,request,execution),cancel=()=>{this.tasks.cancel(started.taskId).catch(()=>{})};signal?.addEventListener("abort",cancel,{once:!0});try{return signal?.aborted&&cancel(),await this.tasks.wait(started.taskId,{onProgress})}finally{signal?.removeEventListener("abort",cancel)}}disconnect(){this.tasks.disconnect()}}}});var plugin_exports={};__export(plugin_exports,{OFFICIAL_PLUGINS:()=>OFFICIAL_PLUGINS,OFFICIAL_PLUGIN_MANAGER:()=>OFFICIAL_PLUGIN_MANAGER,PluginError:()=>PluginError,PluginModelClient:()=>PluginModelClient,PluginPageClient:()=>PluginPageClient,PluginStateClient:()=>PluginStateClient,PluginTaskClient:()=>PluginTaskClient,assertPluginName:()=>assertPluginName,cleanupPluginRuntime:()=>cleanupPluginRuntime,executeRegisteredPlugin:()=>executeRegisteredPlugin,findOfficialPlugin:()=>findOfficialPlugin,findPluginDirectoryEntry:()=>findPluginDirectoryEntry,findPluginEntry:()=>findPluginEntry,findPluginRegistration:()=>findPluginRegistration,invokePageEvalWithArgs:()=>invokePageEvalWithArgs,invokeRegisteredPlugin:()=>invokeRegisteredPlugin,invokeRegisteredRecoveryPlugin:()=>invokeRegisteredRecoveryPlugin,invokeRegisteredRuntimePlugin:()=>invokeRegisteredRuntimePlugin,invokeRegisteredToolboxPlugin:()=>invokeRegisteredToolboxPlugin,listOfficialPluginNames:()=>listOfficialPluginNames,listPluginNames:()=>listPluginNames,loadPlugin:()=>loadPlugin,loadRegisteredPlugin:()=>loadRegisteredPlugin,pluginDirectory:()=>pluginDirectory,pluginRegistrationNeedsRefresh:()=>pluginRegistrationNeedsRefresh,readOfficialPluginCatalog:()=>readOfficialPluginCatalog,readPluginPageBundleChunk:()=>readPluginPageBundleChunk,refreshRegisteredPlugin:()=>refreshRegisteredPlugin,registerPlugin:()=>registerPlugin,replaceOfficialPluginCatalog:()=>replaceOfficialPluginCatalog,resolvePluginEntry:()=>resolvePluginEntry,unregisterPlugin:()=>unregisterPlugin,validateOfficialPluginCatalog:()=>validateOfficialPluginCatalog});import{dirname as dirname8,extname as extname2,resolve as resolve6}from"node:path";import{pathToFileURL}from"node:url";function readPluginVersion(entry){try{return readInstalledSkillMetadata(resolve6(dirname8(entry),".."))?.version}catch{return}}function isObject2(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function isPluginInvocation(value){return isObject2(value)&&typeof value.command=="string"&&isObject2(value.input)}function normalizePluginProgress(progress){if(typeof progress=="string"){if(!progress)throw new Error("app.progress 需要非空消息");return progress}if(!isObject2(progress)||typeof progress.message!="string"||!progress.message)throw new Error("app.progress 需要非空消息");if(progress.data!==void 0&&!isObject2(progress.data))throw new Error("app.progress data 必须是对象");let data2;if(progress.data)try{let serialized=JSON.stringify(progress.data),parsed=serialized===void 0?void 0:JSON.parse(serialized);if(!isObject2(parsed))throw new Error("invalid object");data2=Object.freeze(parsed)}catch{throw new Error("app.progress data 必须是可 JSON 序列化的对象")}return Object.freeze({message:progress.message,...data2?{data:data2}:{}})}function missingPluginEntryMessage(entry,pluginName){let name=pluginName??"<name>";return["插件入口已失效","",`插件:${name}`,`入口:${entry}`,"",`卸载命令:npx @mearl/setup plugin uninstall ${name}`].join(`
|
|
992
|
+
`;if(Buffer.byteLength(text3)>MAX_MESSAGE_BYTES)throw new Error("Plugin host message is too large");if(socket.destroyed)throw new Error("Plugin host connection is closed");socket.write(text3,error2=>error2?reject(error2):resolve7())}catch(error2){reject(error2)}})}var MAX_MESSAGE_BYTES,init_pluginTaskWire=__esm({"../client/src/plugins/pluginTaskWire.ts"(){"use strict";MAX_MESSAGE_BYTES=96*1024*1024}});var pluginHostConnection_exports={};__export(pluginHostConnection_exports,{PluginHostConnection:()=>PluginHostConnection});import{spawn as spawn7}from"node:child_process";import{closeSync as closeSync9,mkdirSync as mkdirSync9,openSync as openSync9}from"node:fs";import{createConnection}from"node:net";async function ensureHost(runtimePath){let paths=pluginHostPaths(),work=starting.get(paths.registry);work||(work=(async()=>{let lease=readRecord(paths.lease);if(lease&&isProcessOwnerAlive(lease)){if(lease.protocolVersion!==5)throw new Error("Plugin execution service protocol differs; stop the service before updating");return}let lock=`${paths.lease}.start`,existing=readRecord(lock);if(!(existing&&isProcessOwnerAlive(existing))&&(existing?removeRecordIfPid(lock,existing.pid):removeInvalidRecord(lock),!!claimRecord(lock,{pid:process.pid,processIdentity:processIdentity(process.pid)})))try{let owner=readRecord(paths.lease);if(owner&&isProcessOwnerAlive(owner))return;mkdirSync9(paths.home,{recursive:!0,mode:448}),rotateLogIfNeeded(paths.log);let log=openSync9(paths.log,"a",384);try{let child=spawn7(process.execPath,[runtimePath],{detached:!0,stdio:["ignore",log,log],windowsHide:!0});await new Promise((resolve7,reject)=>{child.once("spawn",resolve7),child.once("error",reject)}),child.unref()}finally{closeSync9(log)}let deadline=Date.now()+1e4;for(;Date.now()<deadline;){if(await new Promise(resolve7=>{let probe=createConnection(paths.socket),finish=value=>{probe.destroy(),resolve7(value)};probe.once("connect",()=>finish(!0)),probe.once("error",()=>finish(!1)),probe.setTimeout(200,()=>finish(!1))}))return;await new Promise(resolve7=>setTimeout(resolve7,50))}throw new Error("Plugin execution service startup timed out")}finally{removeRecordIfPid(lock,process.pid)}})().finally(()=>starting.delete(paths.registry)),starting.set(paths.registry,work)),await work}var starting,PluginHostConnection,init_pluginHostConnection=__esm({"../client/src/plugins/clients/pluginHostConnection.ts"(){"use strict";init_runtimePaths();init_dist();init_pluginHostPaths();init_dist2();init_pluginTaskWire();init_pluginExecutorProtocol();starting=new Map;PluginHostConnection=class{constructor(configuration={}){this.configuration=configuration}configuration;get hasInvocationBridge(){return!!this.configuration.invoke}connection;socket;opening=new Set;generation=0;async connect(generation){let check=()=>{if(generation!==this.generation)throw Object.assign(new Error("Plugin task client was disconnected"),{code:"PLUGIN_TASK_CLIENT_DISCONNECTED"})},connectOnce=()=>(check(),this.open(this.configuration.socketPath??pluginHostPaths().socket,generation));try{return await connectOnce()}catch(error2){if(check(),this.configuration.autoStart===!1||this.configuration.socketPath)throw error2;let code=error2.code;if(!["ENOENT","ECONNREFUSED","PLUGIN_TASK_CONNECTION_LOST"].includes(code??""))throw error2}await ensureHost(this.configuration.runtimePath??PLUGIN_TASK_HOST_PATH);let deadline=Date.now()+1e4,failure2;do{check();try{return await connectOnce()}catch(error2){failure2=error2,await new Promise(resolve7=>setTimeout(resolve7,100))}}while(Date.now()<deadline);throw new Error("Plugin execution service did not become ready; inspect its service.log",{cause:failure2})}async open(socketPath,generation){let socket=createConnection(socketPath);this.opening.add(socket);let pending=new Map,sequence=0,fail2=error2=>{for(let request2 of pending.values())clearTimeout(request2.timer),request2.reject(error2);pending.clear()};socket.on("error",fail2),socket.once("close",()=>{fail2(Object.assign(new Error("Plugin execution service connection was lost"),{code:"PLUGIN_TASK_CONNECTION_LOST"})),this.socket===socket&&(this.connection=void 0,this.socket=void 0)});let request=(method,input={},timeoutMs=15e3)=>new Promise((resolve7,reject)=>{let id=++sequence,timer=setTimeout(()=>{pending.delete(id),reject(Object.assign(new Error("Plugin task request timed out; the task may still be running"),{code:"PLUGIN_TASK_REQUEST_TIMEOUT"}))},timeoutMs);pending.set(id,{resolve:resolve7,reject,timer}),writePluginTaskMessage(socket,{type:"request",id,method,input}).catch(error2=>{clearTimeout(timer),pending.delete(id),reject(error2)})});readPluginTaskMessages(socket,message=>{if(message.type==="response"){let call=pending.get(message.id);if(!call)return;clearTimeout(call.timer),pending.delete(message.id),message.error?call.reject(pluginExecutionError(message.error)):call.resolve(message.result)}else message.type==="invoke"?Promise.resolve().then(()=>{if(!this.configuration.invoke)throw new Error("Plugin task client has no invocation bridge");return this.configuration.invoke(message.action,message.payload,message.options)}).then(result=>writePluginTaskMessage(socket,{type:"invoke-result",id:message.id,result}),error2=>writePluginTaskMessage(socket,{type:"invoke-result",id:message.id,error:serializePluginExecutionError(error2)})).catch(()=>{}):socket.destroy(new Error("Invalid plugin task response direction"))});try{await new Promise((resolve7,reject)=>{let timer=setTimeout(()=>{socket.destroy(),reject(new Error("Plugin task connection timed out"))},2e3);socket.once("connect",()=>{clearTimeout(timer),resolve7()}),socket.once("error",error2=>{clearTimeout(timer),reject(error2)})});let scope={access:this.configuration.access??"public",source:this.configuration.source??"sdk",bridge:this.configuration.invoke?"caller":"service",...this.configuration.plugin?{plugin:this.configuration.plugin}:{},...this.configuration.scopeId?{scopeId:this.configuration.scopeId}:{}},status=await request("hello",{...scope,protocolVersion:5});if(status.protocolVersion!==5)throw new Error("Plugin task protocol version mismatch");if(generation!==this.generation||socket.destroyed)throw new Error("Plugin task client was disconnected");return this.socket=socket,{socket,request,status}}catch(error2){throw socket.destroy(),error2}finally{this.opening.delete(socket)}}async request(method,input={},timeoutMs){if(!this.connection){let attempt=this.connect(this.generation);this.connection=attempt,attempt.catch(()=>{this.connection===attempt&&(this.connection=void 0)})}return(await this.connection).request(method,input,timeoutMs)}disconnect(){this.generation++,this.socket?.destroy();for(let socket of this.opening)socket.destroy();this.socket=void 0,this.connection=void 0}}}});import{createHash as createHash9}from"node:crypto";function localWait(promise,signal,timeoutMs=1/0){return new Promise((resolve7,reject)=>{let abort=()=>{cleanup(),reject(signal?.reason??new Error("Plugin task wait aborted"))},timer=Number.isFinite(timeoutMs)?setTimeout(()=>{cleanup(),reject(Object.assign(new Error("Waiting for the plugin task timed out; execution continues"),{code:"PLUGIN_TASK_WAIT_TIMEOUT"}))},Math.max(0,timeoutMs)):void 0,cleanup=()=>{clearTimeout(timer),signal?.removeEventListener("abort",abort)};signal?.addEventListener("abort",abort,{once:!0}),promise.then(value=>{cleanup(),resolve7(value)},error2=>{cleanup(),reject(error2)}),signal?.aborted&&abort()})}var PluginTaskClient,init_pluginTasks2=__esm({"../client/src/plugins/clients/pluginTasks.ts"(){"use strict";init_pluginHostConnection();init_dist();init_pluginHostPaths();init_unified();init_dist2();init_pluginExecutorProtocol();PluginTaskClient=class{constructor(configuration={}){this.configuration=configuration;this.connection=configuration.connection??new PluginHostConnection(configuration)}configuration;connection;request(method,input={},timeoutMs){return this.connection.request(method,input,timeoutMs)}status(){return this.request("status")}diagnose(options={}){return this.request("diagnose",{...options})}checkpoint(taskId){return this.request("checkpoint",{taskId})}resume(taskId,options={}){return this.request("resume",{taskId,revision:options.revision,environment:Object.fromEntries(Object.entries(options.environment??process.env).filter(entry=>entry[1]!==void 0))},45e3)}async start(plugin,invocation,options={}){let{cwd=process.cwd(),environment=process.env,executionTimeoutMs,idempotencyKey,...routing}=options;if(!this.connection.hasInvocationBridge&&routing.browser&&!decodeBrowserTarget(routing.browser)){let client=new UnifiedClient;try{let selection=await client.invoke("browser_select_browser",{browser:routing.browser},routing);if(!selection?.selected?.browserId)throw new Error("Cannot resolve plugin task browser target");routing.browser=selection.selected.browserId}finally{client.disconnect()}}return this.request("start",{plugin,command:invocation.command,input:invocation.input,cwd,environment:Object.fromEntries(Object.entries(environment).filter(entry=>entry[1]!==void 0)),options:routing,executionTimeoutMs,idempotencyKey})}get(taskId){return this.request("get",{taskId})}list(activeOnly=!1,page={}){return this.request("list",{activeOnly,...page})}cancel(taskId){return this.request("cancel",{taskId})}events(taskId,cursor=0,waitMs=0){return this.request("events",{taskId,cursor,waitMs},waitMs+15e3)}resultChunk(taskId,index){return this.request("result",{taskId,index})}async result(taskId){let task=await this.get(taskId);if(task.state!=="success"||!task.result)throw new Error("Plugin task has no successful result");let parts=[];for(let index=0;index<task.result.chunks;index++){let chunk=await this.resultChunk(taskId,index);if(chunk.taskId!==taskId||chunk.index!==index||chunk.sha256!==task.result.sha256||chunk.bytes!==task.result.bytes||chunk.chunks!==task.result.chunks)throw new Error("Plugin task result chunk identity mismatch");parts.push(Buffer.from(chunk.data,"base64"))}let bytes=Buffer.concat(parts);if(bytes.length!==task.result.bytes||createHash9("sha256").update(bytes).digest("hex")!==task.result.sha256)throw new Error("Plugin task result integrity check failed");return JSON.parse(bytes.toString("utf8")).value}async*subscribe(taskId,options={}){let cursor=options.cursor??0;for(;;){options.signal?.throwIfAborted();let page=await localWait(this.events(taskId,cursor,25e3),options.signal);if(options.signal?.throwIfAborted(),yield page,cursor=page.nextCursor,!isPluginTaskActive(page.task.state)&&cursor>=page.task.sequence)return}}async wait(taskId,options={}){if(options.timeoutMs!==void 0&&(!Number.isSafeInteger(options.timeoutMs)||options.timeoutMs<1))throw new Error("Invalid plugin task wait timeout");let deadline=options.timeoutMs===void 0?1/0:Date.now()+options.timeoutMs,cursor=0,lastSequence=-1;for(;;){if(options.signal?.throwIfAborted(),Date.now()>=deadline)throw Object.assign(new Error("Waiting for the plugin task timed out; execution continues"),{code:"PLUGIN_TASK_WAIT_TIMEOUT"});let page=await localWait(this.events(taskId,cursor,Math.min(1e3,Math.max(0,deadline-Date.now()))),options.signal,deadline-Date.now());if(options.signal?.throwIfAborted(),cursor=page.nextCursor,page.task.progress&&page.task.sequence!==lastSequence&&options.onProgress?.(page.task.progress,page.task),lastSequence=page.task.sequence,page.task.state==="success")return options.readResult===!1?page.task:localWait(this.result(taskId),options.signal,deadline-Date.now());if(!isPluginTaskActive(page.task.state))throw pluginExecutionError(page.task.error??{message:`Plugin task ended with ${page.task.state}`})}}async stopHost(){let original=await this.status();await this.request("shutdown"),this.disconnect();let deadline=Date.now()+15e3;for(;;){let lease=readRecord(pluginHostPaths().lease);if(!lease||lease.epoch!==original.host.epoch||!isProcessOwnerAlive(lease))return;if(Date.now()>=deadline)throw new Error("Plugin execution service did not finish stopping");await new Promise(resolve7=>setTimeout(resolve7,50))}}disconnect(){this.configuration.connection||this.connection.disconnect()}}}});import{rmSync as rmSync12}from"node:fs";import{randomUUID as randomUUID10}from"node:crypto";async function cleanupPluginRuntime(){let paths=pluginHostPaths();if(listPluginNames().length)throw new Error("Registered plugins still use the execution service");let lock=`${paths.lease}.start`,starter=readRecord(lock);if(starter&&!isProcessOwnerAlive(starter)?removeRecordIfPid(lock,starter.pid):starter||removeInvalidRecord(lock),!claimRecord(lock,{pid:process.pid,processIdentity:processIdentity(process.pid)}))throw new Error("Plugin execution service is starting; caches were retained");try{let lease=readRecord(paths.lease);if(lease&&isProcessOwnerAlive(lease)){let client=new PluginTaskClient({autoStart:!1});try{await client.stopHost()}finally{client.disconnect()}}let current2=readRecord(paths.lease);if(current2&&isProcessOwnerAlive(current2)||listPluginNames().length)throw new Error("Plugin execution service restarted during cleanup; caches were retained");let store=new PluginTaskStore;if(store.list().some(pluginTaskExecutionUnconfirmed))throw new Error("Plugin execution is not confirmed stopped; task records were retained");if(store.recover(randomUUID10()),store.list().some(pluginTaskExecutionUnconfirmed))throw new Error("Plugin execution is not confirmed stopped; task records were retained");let replacement=readRecord(paths.lease);if(replacement&&isProcessOwnerAlive(replacement)||listPluginNames().length)throw new Error("Plugin execution service restarted during cleanup; caches were retained");for(let file of[paths.tasks,paths.revisions,paths.log,`${paths.log}.1`])rmSync12(file,{recursive:!0,force:!0});current2&&removeRecordIfPid(paths.lease,current2.pid),process.platform!=="win32"&&rmSync12(paths.socket,{force:!0})}finally{removeRecordIfPid(lock,process.pid)}}var init_pluginRuntimeCleanup=__esm({"../client/src/plugins/runtime/pluginRuntimeCleanup.ts"(){"use strict";init_dist();init_pluginHostPaths();init_pluginRegistry();init_pluginTasks2();init_pluginTaskStore()}});var pluginPages_exports={};__export(pluginPages_exports,{PluginPageClient:()=>PluginPageClient});var PluginPageClient,init_pluginPages2=__esm({"../client/src/plugins/clients/pluginPages.ts"(){"use strict";init_unified();PluginPageClient=class{constructor(configuration={},connection){this.connection=connection;connection||(this.client=new UnifiedClient(configuration))}connection;client;call(plugin,operation,input={},options={}){return this.connection?this.connection.request("page.call",{plugin,input:{...input,operation},options,execution:{cwd:process.cwd(),environment:Object.fromEntries(Object.entries(process.env).filter(entry=>entry[1]!==void 0))}},Math.max(12e4,typeof input.timeoutSec=="number"&&Number.isFinite(input.timeoutSec)?(Math.min(300,input.timeoutSec)+5)*1e3:0)):this.client.invoke("plugin_page",{...input,plugin,operation},options)}attach(plugin,input,options={}){return this.call(plugin,"attach",input,options)}get(plugin,instanceId){return this.call(plugin,"get",{instanceId})}list(plugin){return this.call(plugin,"list")}send(plugin,instanceId,message,options={}){return this.call(plugin,"send",{instanceId,message,...options},options)}detach(plugin,instanceId){return this.call(plugin,"detach",{instanceId})}events(plugin,instanceId,cursor=0,waitMs=0){return this.call(plugin,"events",{instanceId,cursor,waitMs})}disconnect(){this.client?.disconnect()}}}});var pluginState_exports={};__export(pluginState_exports,{PluginStateClient:()=>PluginStateClient});var PluginStateClient,init_pluginState2=__esm({"../client/src/plugins/clients/pluginState.ts"(){"use strict";init_pluginHostConnection();PluginStateClient=class{constructor(options={}){this.options=options;this.connection=options.connection??new PluginHostConnection(options)}options;connection;call(plugin,collection,operation,input={},options={}){return this.connection.request("state.call",{plugin,input:{...input,collection,operation},options},3e4)}get(plugin,collection,key2,input={}){return this.call(plugin,collection,"get",{...input,key:key2})}getMany(plugin,collection,keys,input={}){return this.call(plugin,collection,"get-many",{...input,keys})}list(plugin,collection,input={}){return this.call(plugin,collection,"list",input)}transact(plugin,collection,transaction,input={}){return this.call(plugin,collection,"transact",{...input,transaction})}disconnect(){this.options.connection||this.connection.disconnect()}}}});var pluginModels_exports={};__export(pluginModels_exports,{PluginModelClient:()=>PluginModelClient});var PluginModelClient,init_pluginModels2=__esm({"../client/src/plugins/clients/pluginModels.ts"(){"use strict";init_pluginTasks2();PluginModelClient=class{tasks;constructor(options={}){this.tasks=new PluginTaskClient(options)}capabilities(){return this.tasks.connection.request("model.capabilities")}list(provider="codex"){return this.tasks.connection.request("model.list",{provider},2e4)}async start(plugin,request,options={}){let{cwd=process.cwd(),environment=process.env,executionTimeoutMs,idempotencyKey,...routing}=options;return this.tasks.connection.request("start",{kind:"model",plugin,command:"model",input:{request},cwd,environment:Object.fromEntries(Object.entries(environment).filter(entry=>entry[1]!==void 0)),options:routing,executionTimeoutMs,idempotencyKey})}async run(plugin,request,options={}){let{signal,onProgress,...execution}=options;signal?.throwIfAborted();let started=await this.start(plugin,request,execution),cancel=()=>{this.tasks.cancel(started.taskId).catch(()=>{})};signal?.addEventListener("abort",cancel,{once:!0});try{return signal?.aborted&&cancel(),await this.tasks.wait(started.taskId,{onProgress})}finally{signal?.removeEventListener("abort",cancel)}}disconnect(){this.tasks.disconnect()}}}});var plugin_exports={};__export(plugin_exports,{OFFICIAL_PLUGINS:()=>OFFICIAL_PLUGINS,OFFICIAL_PLUGIN_MANAGER:()=>OFFICIAL_PLUGIN_MANAGER,PluginError:()=>PluginError,PluginModelClient:()=>PluginModelClient,PluginPageClient:()=>PluginPageClient,PluginStateClient:()=>PluginStateClient,PluginTaskClient:()=>PluginTaskClient,assertPluginName:()=>assertPluginName,cleanupPluginRuntime:()=>cleanupPluginRuntime,executeRegisteredPlugin:()=>executeRegisteredPlugin,findOfficialPlugin:()=>findOfficialPlugin,findPluginDirectoryEntry:()=>findPluginDirectoryEntry,findPluginEntry:()=>findPluginEntry,findPluginRegistration:()=>findPluginRegistration,invokePageEvalWithArgs:()=>invokePageEvalWithArgs,invokeRegisteredPlugin:()=>invokeRegisteredPlugin,invokeRegisteredRecoveryPlugin:()=>invokeRegisteredRecoveryPlugin,invokeRegisteredRuntimePlugin:()=>invokeRegisteredRuntimePlugin,invokeRegisteredToolboxPlugin:()=>invokeRegisteredToolboxPlugin,listOfficialPluginNames:()=>listOfficialPluginNames,listPluginNames:()=>listPluginNames,loadPlugin:()=>loadPlugin,loadRegisteredPlugin:()=>loadRegisteredPlugin,pluginDirectory:()=>pluginDirectory,pluginRegistrationNeedsRefresh:()=>pluginRegistrationNeedsRefresh,readOfficialPluginCatalog:()=>readOfficialPluginCatalog,readPluginPageBundleChunk:()=>readPluginPageBundleChunk,refreshRegisteredPlugin:()=>refreshRegisteredPlugin,registerPlugin:()=>registerPlugin,replaceOfficialPluginCatalog:()=>replaceOfficialPluginCatalog,resolvePluginEntry:()=>resolvePluginEntry,unregisterPlugin:()=>unregisterPlugin,validateOfficialPluginCatalog:()=>validateOfficialPluginCatalog});import{dirname as dirname8,extname as extname2,resolve as resolve6}from"node:path";import{pathToFileURL}from"node:url";function readPluginVersion(entry){try{return readInstalledSkillMetadata(resolve6(dirname8(entry),".."))?.version}catch{return}}function isObject2(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function isPluginInvocation(value){return isObject2(value)&&typeof value.command=="string"&&isObject2(value.input)}function normalizePluginProgress(progress){if(typeof progress=="string"){if(!progress)throw new Error("app.progress 需要非空消息");return progress}if(!isObject2(progress)||typeof progress.message!="string"||!progress.message)throw new Error("app.progress 需要非空消息");if(progress.data!==void 0&&!isObject2(progress.data))throw new Error("app.progress data 必须是对象");let data2;if(progress.data)try{let serialized=JSON.stringify(progress.data),parsed=serialized===void 0?void 0:JSON.parse(serialized);if(!isObject2(parsed))throw new Error("invalid object");data2=Object.freeze(parsed)}catch{throw new Error("app.progress data 必须是可 JSON 序列化的对象")}return Object.freeze({message:progress.message,...data2?{data:data2}:{}})}function missingPluginEntryMessage(entry,pluginName){let name=pluginName??"<name>";return["插件入口已失效","",`插件:${name}`,`入口:${entry}`,"",`卸载命令:npx --@mearl:registry=https://registry.npmjs.org @mearl/setup plugin uninstall ${name}`].join(`
|
|
993
993
|
`)}async function refreshRegisteredPlugin(name,includePage=!1){let key2=`${pluginDirectory()}/${name}`,pending=registrationRefreshes.get(key2);if(pending)return await pending,refreshRegisteredPlugin(name,includePage);let registration=findPluginRegistration(name);if(!registration)return;let entryChanged=pluginRegistrationNeedsRefresh(registration);if(!entryChanged&&(!includePage||!pluginRegistrationNeedsRefresh(registration,!0)))return registration;let refresh=(async()=>{let entryVersion=pluginFileVersion(registration.entry),loaded=entryChanged?await loadPlugin(registration.entry,{options:{},invoke:async()=>{throw new Error("注册时不能执行浏览器操作")},progress:()=>{throw new Error("注册时不能报告执行进度")}},name):registration,current2=findPluginRegistration(name);if(JSON.stringify(current2)!==JSON.stringify(registration))return current2;if(pluginFileVersion(registration.entry)!==entryVersion)throw new PluginError("PLUGIN_SOURCE_CHANGED",`插件更新进行中,请重新打开工具: ${name}`);return registerPlugin(name,registration.entry,loaded.description,{hasPublicCommands:"commandHelp"in loaded?!!loaded.commandHelp(name):loaded.hasPublicCommands,toolbox:loaded.toolbox,appProfiles:loaded.appProfiles,profileSources:"profileSources"in loaded?loaded.profileSources:loaded.source?.profiles,...registration.managedBy?{managedBy:registration.managedBy}:{}}),findPluginRegistration(name)})();registrationRefreshes.set(key2,refresh);try{return await refresh}finally{registrationRefreshes.delete(key2)}}async function loadPlugin(entry,context,pluginName){let entryVersion;try{entryVersion=pluginFileVersion(entry)}catch{throw new PluginError("PLUGIN_ENTRY_MISSING",missingPluginEntryMessage(entry,pluginName))}let commands=new Map,recoveries=new Map,migrations=new Map,executing=!1,checkpointDeclaration,invocationGeneration=0,registering=!0,name,description,toolbox,pageRuntime,stateStorage,taskRecovery,appProfiles,profileSources,registerCommand=(signature,handler,description2="",examples=[],toolboxOnly=!1,runtimeOnly=!1)=>{if(!registering)throw new Error("命令只能在入口注册阶段声明");let[name2,...args]=signature.trim().split(/\s+/);if(!/^[a-z][a-z0-9-]*$/.test(name2)||commands.has(name2)||typeof handler!="function"||!Array.isArray(examples)||examples.some(example=>typeof example!="string"||!example.trim()))throw new Error(`无效或重复的命令: ${signature}`);let command=new Command(name2).description(description2);for(let argument of args){if(!/^(?:<[a-zA-Z][\w-]*(?:\.\.\.)?>|\[[a-zA-Z][\w-]*(?:\.\.\.)?\])$/.test(argument))throw new Error(`无效的位置参数: ${argument}`);command.argument(argument)}let names=command.registeredArguments.map(argument=>argument.name());if(new Set(names).size!==names.length)throw new Error(`参数名称重复: ${signature}`);commands.set(name2,{command,signature,handler,examples:[...examples],toolboxOnly,runtimeOnly})},app=Object.freeze({options:Object.freeze({...context.options}),command:(signature,handler,description2="",examples=[])=>registerCommand(signature,handler,description2,examples),runtimeCommand:(signature,handler,description2="",examples=[])=>registerCommand(signature,handler,description2,examples,!1,!0),recoveryCommand(command,handler){if(!registering||!/^[a-z][a-z0-9-]*$/.test(command)||typeof handler!="function"||recoveries.has(command))throw new Error("Invalid or duplicate recovery command");recoveries.set(command,handler)},checkpointMigration(command,handler){if(!registering||!/^[a-z][a-z0-9-]*$/.test(command)||typeof handler!="function"||migrations.has(command))throw new Error("Invalid or duplicate checkpoint migration");migrations.set(command,(input,commandContext)=>handler(input,commandContext))},checkpoint(data2,expectedSequence){if(!executing||!context.invocation?.taskId)throw new PluginError("PLUGIN_TASK_REQUIRED","Checkpoints require a managed task");return context.signal?.throwIfAborted(),context.invoke("plugin_task_checkpoint",{plugin:pluginName??name,data:data2,expectedSequence,expectedDeclaration:checkpointDeclaration})},state(collection,operation,input={}){if(!executing)throw new Error("app.state is only available during command execution");if(context.signal?.throwIfAborted(),!stateStorage||!Object.hasOwn(stateStorage.collections,collection))throw new PluginError("PLUGIN_STATE_NOT_DECLARED","Plugin state collection is not declared");return context.invoke("plugin_state",{...input,plugin:pluginName??name,collection,operation,expectedDeclaration:pluginStateSignature(stateStorage.collections[collection])})},model(request,options={}){if(!executing)throw new Error("app.model is only available during command execution");context.signal?.throwIfAborted();let generation=invocationGeneration,observing=()=>executing&&generation===invocationGeneration&&!context.signal?.aborted;if(context.runModel)return context.runModel(pluginName??name,request,{signal:context.signal,onProgress:progress=>{observing()&&(context.progress(progress),options.onProgress?.(progress))}});let payload={plugin:pluginName??name,operation:"run",request};return options.onProgress?context.invoke("plugin_model",payload,{},progress=>{observing()&&options.onProgress?.(progress)}):context.invoke("plugin_model",payload)},page(operation,input={}){if(!executing)throw new Error("app.page is only available during command execution");return context.signal?.throwIfAborted(),context.invoke("plugin_page",{...input,plugin:pluginName??name,operation})},toolboxCommand:(signature,handler,description2="",examples=[])=>registerCommand(signature,handler,description2,examples,!0),invoke(action,payload={},options={}){if(!executing)throw new Error("app.invoke 只能在命令执行期间调用");return context.signal?.throwIfAborted(),context.invoke(action,payload,options)},progress(progress){if(!executing)throw new Error("app.progress 只能在命令执行期间调用");context.progress(normalizePluginProgress(progress))}});try{if(extname2(entry).toLowerCase()===".py"){let descriptor=await describePythonPlugin(entry,app.options);name=descriptor.name,description=descriptor.description,toolbox=normalizePluginToolboxContribution(descriptor.toolbox),pageRuntime=normalizePluginPageRuntime(descriptor.pageRuntime),stateStorage=normalizePluginState(descriptor.stateStorage),taskRecovery=normalizePluginTaskRecovery(descriptor.taskRecovery),{appProfiles,profileSources}=loadPluginAppProfiles(entry,descriptor.appProfiles);for(let command of descriptor.commands){let commandName=command.signature.trim().split(/\s+/)[0];if(command.migrationOnly){app.checkpointMigration(commandName,(input,commandContext)=>runPythonPluginCommand(entry,commandName,input,app,commandContext,"migration",descriptor));continue}if(command.recoveryOnly){app.recoveryCommand(commandName,(input,commandContext)=>runPythonPluginCommand(entry,commandName,input,app,commandContext,!0,descriptor));continue}(command.runtimeOnly?app.runtimeCommand:command.toolboxOnly?app.toolboxCommand:app.command)(command.signature,(input,commandContext)=>runPythonPluginCommand(entry,commandName,input,app,commandContext,!1,descriptor,command.runtimeOnly),command.description)}}else{let moduleUrl=pathToFileURL(entry);moduleUrl.searchParams.set("mearl-version",entryVersion);let module=await import(moduleUrl.href);if(module.name!==void 0&&typeof module.name!="string")throw new Error("入口的 name 导出必须为字符串");if(name=module.name,module.description!==void 0&&typeof module.description!="string")throw new Error("入口的 description 导出必须为字符串");if(description=module.description,toolbox=normalizePluginToolboxContribution(module.toolbox),pageRuntime=normalizePluginPageRuntime(module.pageRuntime),stateStorage=normalizePluginState(module.stateStorage),taskRecovery=normalizePluginTaskRecovery(module.taskRecovery),{appProfiles,profileSources}=loadPluginAppProfiles(entry,module.appProfiles),module.default!==void 0&&typeof module.default!="function")throw new Error("入口必须默认导出 register(app) 函数");if(typeof module.default=="function")await module.default(app);else if(!appProfiles)throw new Error("入口必须默认导出 register(app) 函数")}if(commands.size===0&&!appProfiles)throw new Error("入口未注册任何命令");for(let command of Object.keys(taskRecovery?.commands??{}))if(!commands.has(command)||commands.get(command)?.runtimeOnly||commands.get(command)?.toolboxOnly||!recoveries.has(command))throw new Error(`Recovery requires a command and recoveryCommand: ${command}`);for(let command of recoveries.keys())if(!Object.hasOwn(taskRecovery?.commands??{},command))throw new Error(`Undeclared recovery command: ${command}`);for(let[command,definition]of Object.entries(taskRecovery?.commands??{}))if(!!definition.migrateFrom?.length!==migrations.has(command))throw new Error(`Checkpoint migrations require a declaration and checkpointMigration: ${command}`);for(let command of migrations.keys())if(!Object.hasOwn(taskRecovery?.commands??{},command))throw new Error(`Undeclared checkpoint migration: ${command}`);let runtimeCommands=new Set(Object.values(pageRuntime?.events??{}).map(event=>event.command));pageRuntime?.initialize&&runtimeCommands.add(pageRuntime.initialize);for(let[name2,command]of commands)if(command.runtimeOnly&&!runtimeCommands.has(name2))throw new Error(`runtimeCommand is not declared in pageRuntime.events: ${name2}`);for(let name2 of runtimeCommands)if(!commands.has(name2)||commands.get(name2)?.toolboxOnly)throw new Error(`Invalid page runtime command: ${name2}`);let toolboxCommands=toolbox?.page.commands??[];for(let[name2,command]of commands)if(command.toolboxOnly&&!toolboxCommands.includes(name2))throw new Error(`toolboxCommand 未声明在 toolbox.page.commands 中: ${name2}`);for(let command of toolboxCommands)if(!commands.has(command))throw new Error(`toolbox 命令未注册: ${command}`)}catch(error2){throw new PluginError("PLUGIN_LOAD_FAILED",`${entry}: ${error2 instanceof Error?error2.message:String(error2)}`)}finally{registering=!1}let find=(name2,toolboxPage=!1)=>{let command=commands.get(name2);if(!command)throw new PluginError("PLUGIN_COMMAND_NOT_FOUND",`未知子命令: ${name2}`);if(toolboxPage==="runtime"){if(!(pageRuntime?.initialize===name2||Object.values(pageRuntime?.events??{}).some(event=>event.command===name2))||command.toolboxOnly)throw new PluginError("PLUGIN_COMMAND_NOT_ALLOWED","Page runtime command is not declared")}else{if(command.runtimeOnly)throw new PluginError("PLUGIN_COMMAND_RUNTIME_ONLY",`命令仅供页面运行时调用: ${name2}`);if(toolboxPage){if(!toolbox?.page.commands.includes(name2))throw new PluginError("PLUGIN_COMMAND_NOT_ALLOWED",`工具页面不允许调用命令: ${name2}`)}else if(command.toolboxOnly)throw new PluginError("PLUGIN_COMMAND_TOOLBOX_ONLY",`命令仅供工具页面调用: ${name2}`)}return command},publicCommands=()=>[...commands.values()].filter(command=>!command.toolboxOnly&&!command.runtimeOnly),run=async(positionals,payload,namedInput={},toolboxPage=!1)=>{if(payload!==void 0&&positionals.length>0)throw new PluginError("PLUGIN_INVALID_INPUT","位置参数调用与 --payload 调用不能混用");if(payload!==void 0&&Object.keys(namedInput).length>0)throw new PluginError("PLUGIN_INVALID_INPUT","插件业务选项与 --payload 调用不能混用");if(payload!==void 0&&(!isObject2(payload)||typeof payload.command!="string"||!isObject2(payload.input)))throw new PluginError("PLUGIN_INVALID_INPUT",'--payload 必须为 {"command":"...","input":{...}}');let envelope=payload,commandName=envelope?.command??positionals[0]??"",hidden=toolboxPage==="recovery"||toolboxPage==="migration",selected=find(commandName,hidden?!1:toolboxPage),command=selected.command,handler=toolboxPage==="recovery"?recoveries.get(commandName):toolboxPage==="migration"?migrations.get(commandName):selected.handler;if(!handler||toolboxPage==="recovery"&&!context.invocation?.recovery)throw new PluginError("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Recovery handler requires a managed recovery attempt");if(toolboxPage==="recovery"&&context.invocation?.recovery?.declarationHash!==pluginRecoverySignature(taskRecovery.commands[commandName]))throw new PluginError("PLUGIN_RECOVERY_DECLARATION_CHANGED","Recovery declaration differs from the admitted format");let positionalInput=envelope?{...envelope.input}:Object.fromEntries(command.registeredArguments.map((argument,index)=>[argument.name(),argument.variadic?positionals.slice(index+1):positionals[index+1]]).filter(([,value])=>value!==void 0));for(let name2 of Object.keys(namedInput))if(Object.hasOwn(positionalInput,name2))throw new PluginError("PLUGIN_INVALID_INPUT",`参数不能重复传入: ${name2}`);let input={...positionalInput,...namedInput};if(!envelope&&!command.registeredArguments.some(argument=>argument.variadic)&&positionals.length-1>command.registeredArguments.length)throw new PluginError("PLUGIN_INVALID_INPUT","位置参数过多");for(let argument of hidden?[]:command.registeredArguments){let value=Object.hasOwn(input,argument.name())?input[argument.name()]:void 0;if(argument.required&&(value==null||value===""||Array.isArray(value)&&value.length===0))throw new PluginError("PLUGIN_INVALID_INPUT",`缺少必填参数: ${argument.name()}`);if(argument.variadic&&value!==void 0&&!Array.isArray(value))throw new PluginError("PLUGIN_INVALID_INPUT",`${argument.name()} 必须为数组`)}if(executing)throw new PluginError("PLUGIN_BUSY","同一命令实例不能并发执行");let signal=context.signal??new AbortController().signal;signal.throwIfAborted();let identity=context.invocation??createPluginInvocationIdentity(pluginName??name??"unregistered",context.source??"sdk",context.parentInvocation),generation=++invocationGeneration,assertActive=()=>{if(!executing||generation!==invocationGeneration)throw new PluginError("PLUGIN_INVOCATION_ENDED","命令调用已结束");signal.throwIfAborted()},commandContext=Object.freeze({identity,signal,...identity.recovery?{recovery:structuredClone(identity.recovery)}:{},checkpoint:(data2,expectedSequence)=>(assertActive(),app.checkpoint(data2,expectedSequence)),...identity.page?{page:Object.freeze({send:(message,options={})=>(assertActive(),app.page("send",{instanceId:identity.page.instanceId,message,...options.timeoutSec!==void 0?{timeoutSec:options.timeoutSec}:{}})),get:()=>(assertActive(),app.page("get",{instanceId:identity.page.instanceId}))})}:{},invoke:(action,payload2,options)=>(assertActive(),app.invoke(action,payload2,options)),state:(collection,operation,input2)=>(assertActive(),app.state(collection,operation,{...identity.page?{instanceId:identity.page.instanceId}:{},...input2})),model:(request,options)=>(assertActive(),app.model(request,options)),progress:progress=>{assertActive(),app.progress(progress)}});checkpointDeclaration=taskRecovery&&Object.hasOwn(taskRecovery.commands,commandName)?pluginRecoverySignature(taskRecovery.commands[commandName]):void 0,executing=!0,context.onInvocation?.(identity);try{return await handler(input,commandContext)}finally{executing=!1,checkpointDeclaration=void 0,context.onInvocation?.(void 0)}};return{name,description,toolbox,pageRuntime,stateStorage,taskRecovery,appProfiles,profileSources,version:readPluginVersion(entry),commandHelp(namespace){return publicCommands().map(({command,signature})=>` ${namespace} ${signature} ${command.description()}`).join(`
|
|
994
994
|
`)},help(namespace,name2){let selected=name2?[find(name2)]:publicCommands();if(!selected.length){let location2=toolbox?`请在 Mearl ${toolbox.displayMode==="popup"?"Popup":"工具箱"} 中打开「${toolbox.title}」使用。`:"此插件未提供公开 CLI 命令。";return`${namespace} 未提供 CLI 命令。
|
|
995
995
|
${location2}`}let examples=selected.flatMap(({examples:examples2})=>examples2);return["用法:",...selected.map(({command,signature})=>` ${namespace} ${signature} ${command.description()}`),...examples.length?["","示例:",...examples.map(example=>` ${namespace} ${example}`)]:[]].join(`
|