@mearl/native-host 2.22.5 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +42 -43
- package/dist/install.js +1 -1
- package/dist/plugin-task-host.js +3 -3
- package/dist/plugin-worker.js +1 -1
- package/package.json +8 -8
package/dist/plugin-task-host.js
CHANGED
|
@@ -608,7 +608,7 @@ button, input, select, textarea { font: inherit; }
|
|
|
608
608
|
`,{mode:384,flag:"wx"}),renameSync3(temporary,file)}finally{rmSync3(temporary,{force:!0})}return removePluginPageBundles(name,builtPage?.hash),entry}function unregisterPlugin(name){assertPluginName(name);let file=join5(pluginDirectory(),`${name}.json`);try{return rmSync3(file),rmSync3(join5(pluginDirectory(),".host","installations",`${name}.json`),{force:!0}),removePluginPageBundles(name),!0}catch(error2){if(error2.code==="ENOENT")return!1;throw new PluginError("PLUGIN_UNINSTALL_FAILED",`无法移除插件注册: ${file}`)}}function findPluginRegistration(name){if(!/^[a-z][a-z0-9-]*$/.test(name))return;let file=join5(pluginDirectory(),`${name}.json`),content;try{content=readFileSync5(file,"utf8")}catch(error2){if(error2.code==="ENOENT")return;throw new PluginError("PLUGIN_REGISTRY_INVALID",`无法读取插件记录: ${file}`)}try{let record2=JSON.parse(content);if(typeof record2?.entry!="string"||!isAbsolute2(record2.entry))throw new Error("invalid entry");if(record2.hasPublicCommands!==void 0&&typeof record2.hasPublicCommands!="boolean")throw new Error("invalid public commands");let description=typeof record2.description=="string"&&record2.description.trim()?record2.description.trim():void 0,managedBy=typeof record2.managedBy=="string"&&record2.managedBy.trim()?record2.managedBy.trim():void 0,toolbox=normalizePluginToolboxContribution(record2.toolbox),pageBundle=toolbox?normalizePageBundle(record2.pageBundle):void 0,appProfiles=validateAppProfileContribution(record2.appProfiles),source=record2.source;if(source!==void 0&&(!source||typeof source!="object"||Array.isArray(source)||typeof source.entry!="string"||source.profiles!==void 0&&(!source.profiles||typeof source.profiles!="object"||Array.isArray(source.profiles)||Object.entries(source.profiles).some(([path7,version])=>!isAbsolute2(path7)||typeof version!="string"))||source.pageRuntime!==void 0&&(typeof source.pageRuntime!="string"||!/^[a-f0-9]{64}$/.test(source.pageRuntime))||source.page!==void 0&&(!source.page||typeof source.page!="object"||Array.isArray(source.page)||Object.entries(source.page).some(([path7,version])=>!isAbsolute2(path7)||typeof version!="string"))))throw new Error("invalid source");return{entry:record2.entry,...record2.hasPublicCommands!==void 0?{hasPublicCommands:record2.hasPublicCommands}:{},...description?{description}:{},...managedBy?{managedBy}:{},...toolbox?{toolbox}:{},...pageBundle?{pageBundle}:{},...appProfiles?{appProfiles}:{},...source?{source}:{}}}catch(cause){let reason=cause instanceof SyntaxError?"JSON 格式错误":cause instanceof Error?cause.message:"未知校验错误";throw new PluginError("PLUGIN_REGISTRY_INVALID",`无效的插件记录: ${file} (${reason})`,{cause})}}function readPluginPageBundleChunk(name,hash,index){assertPluginName(name);let bundle=findPluginRegistration(name)?.pageBundle;if(!bundle||bundle.hash!==hash)throw new PluginError("PLUGIN_PAGE_CHANGED",`插件页面已更新: ${name}`);if(!Number.isInteger(index)||index<0||index>=bundle.chunks)throw new PluginError("PLUGIN_PAGE_CHUNK_INVALID",`插件页面分块无效: ${index}`);let path7=pluginPageBundlePath(name,hash),size;try{size=statSync4(path7).size}catch{throw new PluginError("PLUGIN_PAGE_INVALID",`插件页面产物不存在: ${name}`)}if(size!==bundle.size)throw new PluginError("PLUGIN_PAGE_INVALID",`插件页面产物损坏: ${name}`);let start=index*PLUGIN_PAGE_CHUNK_BYTES,content=Buffer.allocUnsafe(Math.min(PLUGIN_PAGE_CHUNK_BYTES,bundle.size-start)),descriptor;try{descriptor=openSync3(path7,"r")}catch{throw new PluginError("PLUGIN_PAGE_INVALID",`插件页面产物无法读取: ${name}`)}let bytesRead;try{bytesRead=readSync(descriptor,content,0,content.byteLength,start)}finally{closeSync3(descriptor)}if(bytesRead!==content.byteLength)throw new PluginError("PLUGIN_PAGE_INVALID",`插件页面产物损坏: ${name}`);return{data:content.toString("base64"),hash,index,total:bundle.chunks}}function findPluginEntry(name){return findPluginRegistration(name)?.entry}function listPluginNames(){try{return readdirSync4(pluginDirectory()).filter(file=>/^[a-z][a-z0-9-]*\.json$/.test(file)).map(file=>file.slice(0,-5)).sort()}catch(error2){if(error2.code==="ENOENT")return[];throw new PluginError("PLUGIN_REGISTRY_INVALID","无法读取插件目录")}}var RESERVED_NAMES,PluginError,init_pluginRegistry=__esm({"../client/src/plugins/pluginRegistry.ts"(){"use strict";init_app_profile_definition();init_toolbox_icon();init_commands();init_pluginPageBundle();init_pluginSource();RESERVED_NAMES=new Set(["check","run","help","connector_list","capture_checkpoint","plugin","plugins"]),PluginError=class extends Error{constructor(code,message,options){super(`${code}: ${message}`,options);this.code=code}}}});import{createHash as createHash3}from"node:crypto";import{tmpdir}from"node:os";import{join as join6}from"node:path";function pluginHostPaths(){let registry=pluginDirectory(),key2=createHash3("sha256").update(registry).digest("hex").slice(0,16),home=join6(registry,".host");return{registry,home,lease:join6(home,"service.json"),log:join6(home,"service.log"),installations:join6(home,"installations"),revisions:join6(home,"revisions"),tasks:join6(home,"tasks"),state:join6(home,"state"),socket:process.platform==="win32"?`\\\\.\\pipe\\mearl-plugin-${key2}`:join6(tmpdir(),`mp-${key2}.sock`)}}var init_pluginHostPaths=__esm({"../client/src/plugins/runtime/pluginHostPaths.ts"(){"use strict";init_pluginRegistry()}});function isPluginPageInvalidation(error2){let value=error2,codes=["PLUGIN_PAGE_INVALIDATED","PLUGIN_PAGE_NOT_FOUND","PLUGIN_PAGE_TARGET_CHANGED","DOMAIN_PERMISSION_REQUIRED"],message=typeof value?.message=="string"?value.message:"";return codes.includes(String(value?.code))||codes.some(code=>message.startsWith(`${code}:`))}function isPluginPageUnavailable(error2){return error2?.code==="PLUGIN_PAGE_UNAVAILABLE"}function pluginPageUnavailable(error2){return Object.assign(new Error("Plugin page host is temporarily unavailable",{cause:error2}),{code:"PLUGIN_PAGE_UNAVAILABLE"})}function isPluginPageMatchPattern(value){return typeof value=="string"&&value.length<=2048&&pattern.test(value)}function matchesPluginPageURL(matches,value){let url;try{url=new URL(value)}catch{return!1}return["http:","https:"].includes(url.protocol)?matches.some(item=>{let match=item.match(pattern);if(!match||match[1]!=="*"&&match[1].toLowerCase()+":"!==url.protocol)return!1;let host=match[2].toLowerCase();if(host!=="*"&&host!==url.hostname&&!(host.startsWith("*.")&&(url.hostname===host.slice(2)||url.hostname.endsWith(host.slice(1))))||match[3]&&match[3].slice(1)!==(url.port||(url.protocol==="https:"?"443":"80")))return!1;let expression="^"+match[4].split("*").map(part=>part.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join(".*")+"$";return new RegExp(expression).test(url.pathname+url.search)}):!1}function isPluginPageInstanceId(value){return typeof value=="string"&&/^pi_[0-9a-f-]{36}$/i.test(value)}var pattern,init_plugin_pages=__esm({"../client/src/generated/plugin-pages.ts"(){"use strict";pattern=/^(https?|\*):\/\/(\*|\*\.[a-z0-9.-]+|[a-z0-9.-]+|localhost)(:\d+)?(\/[^#]*)$/i}});var CODEX_MODEL_CAPABILITY,init_capabilities=__esm({"../agent-runtime/dist/capabilities.js"(){"use strict";CODEX_MODEL_CAPABILITY={provider:"codex",modes:["agent"],permissions:["read-only"],structuredOutput:!0,streaming:!0,cancellation:!0}}});var pluginModelDefinition_exports={};__export(pluginModelDefinition_exports,{normalizePluginModel:()=>normalizePluginModel,pluginModelCapabilities:()=>pluginModelCapabilities,validatePluginModelResult:()=>validatePluginModelResult});function pluginModelCapabilities(){return{providers:[CODEX_MODEL_CAPABILITY]}}function normalizePluginModel(value){let request=value;if(!request||typeof request!="object"||Array.isArray(request)||typeof request.prompt!="string"||!request.prompt.trim()||Buffer.byteLength(JSON.stringify(request))>256*1024||request.instructions!==void 0&&typeof request.instructions!="string"||request.conversation!==void 0&&(typeof request.conversation!="string"||!/^[a-zA-Z0-9:_-]{1,80}$/.test(request.conversation))||request.model!==void 0&&(typeof request.model!="string"||!request.model.trim()||request.model.length>256)||request.reasoningEffort!==void 0&&!["none","minimal","low","medium","high","xhigh","max"].includes(request.reasoningEffort)||Object.keys(request).some(key2=>!["provider","mode","prompt","instructions","model","reasoningEffort","outputSchema","permissions","conversation"].includes(key2)))throw new Error("Invalid model execution request");let capability=pluginModelCapabilities().providers.find(item=>item.provider===request.provider);if(!capability?.modes.includes(request.mode)||request.mode==="agent"&&!capability.permissions.includes(request.permissions)||request.mode==="generate"&&request.permissions!==void 0)throw Object.assign(new Error("Requested model mode or permissions are unsupported; choose an advertised capability explicitly"),{code:"PLUGIN_MODEL_CAPABILITY_UNSUPPORTED"});if(request.outputSchema!==void 0&&(!capability.structuredOutput||!request.outputSchema||typeof request.outputSchema!="object"||Array.isArray(request.outputSchema)||request.outputSchema.$async||Buffer.byteLength(JSON.stringify(request.outputSchema))>64*1024))throw new Error("Invalid model output schema");return JSON.parse(JSON.stringify(request))}function validatePluginModelResult(request,value){let result=value;if(!result||typeof result.text!="string"||result.provider!==request.provider||result.mode!==request.mode)throw new Error("Invalid model execution result");if(request.outputSchema){let output;try{output=JSON.parse(result.text)}catch{throw Object.assign(new Error("Model returned invalid JSON"),{code:"PLUGIN_MODEL_SCHEMA_INVALID"})}return{...result,output}}return result}var init_pluginModelDefinition=__esm({"../client/src/plugins/sdk/pluginModelDefinition.ts"(){"use strict";init_capabilities()}});import{createHash as createHash4}from"node:crypto";function normalizePluginTaskRecovery(value){if(value===void 0)return;let declaration=JSON.parse(JSON.stringify(value));if(!declaration||declaration.version!==1||!declaration.commands||typeof declaration.commands!="object"||Array.isArray(declaration.commands)||Object.keys(declaration.commands).length>32||Buffer.byteLength(JSON.stringify(declaration))>128*1024)throw new Error("Invalid taskRecovery declaration");for(let[command,definition]of Object.entries(declaration.commands)){if(!/^[a-z][a-z0-9-]*$/.test(command)||!definition||!Number.isSafeInteger(definition.checkpointVersion)||definition.checkpointVersion<1||!definition.schema||typeof definition.schema!="object"||Array.isArray(definition.schema)||definition.schema.$async)throw new Error("Invalid task checkpoint definition");if(definition.migrateFrom!==void 0){if(!Array.isArray(definition.migrateFrom)||definition.migrateFrom.length<1||definition.migrateFrom.length>16)throw new Error("Invalid checkpoint migration sources");let versions=new Set;for(let source of definition.migrateFrom){if(!source||!Number.isSafeInteger(source.version)||source.version<1||source.version>=definition.checkpointVersion||versions.has(source.version)||!source.schema||typeof source.schema!="object"||Array.isArray(source.schema)||source.schema.$async)throw new Error("Checkpoint migrations require distinct older versions and schemas");versions.add(source.version)}}}return declaration}function pluginRecoverySignature(value){return createHash4("sha256").update(JSON.stringify(value,(_key,item)=>item&&typeof item=="object"&&!Array.isArray(item)?Object.fromEntries(Object.entries(item).sort(([a],[b])=>a<b?-1:a>b?1:0)):item)).digest("hex")}var init_pluginRecoveryDefinition=__esm({"../client/src/plugins/sdk/pluginRecoveryDefinition.ts"(){"use strict"}});import{randomUUID as randomUUID3}from"node:crypto";import{linkSync as linkSync2,mkdirSync as mkdirSync3,readFileSync as readFileSync6,rmSync as rmSync4,writeFileSync as writeFileSync3}from"node:fs";import{join as join7}from"node:path";function localPluginExecutionHost(){let directory=pluginDirectory(),hostId=hostIds.get(directory);if(!hostId){mkdirSync3(directory,{recursive:!0,mode:448});let file=join7(directory,".execution-host-id"),proposed=randomUUID3(),temporary=`${file}.${proposed}.tmp`;try{writeFileSync3(temporary,proposed,{flag:"wx",mode:384}),linkSync2(temporary,file)}catch(error2){if(error2.code!=="EEXIST")throw error2}finally{rmSync4(temporary,{force:!0})}if(hostId=readFileSync6(file,"utf8").trim(),!UUID.test(hostId))throw new Error("Invalid plugin execution host identity");hostIds.set(directory,hostId)}return Object.freeze({hostId,epoch})}function createPluginInvocationIdentity(plugin,source,parent){return Object.freeze({invocationId:randomUUID3(),host:parent?.host??localPluginExecutionHost(),plugin,source,...parent?{parentInvocationId:parent.invocationId}:{}})}var epoch,hostIds,UUID,init_pluginExecution=__esm({"../client/src/plugins/runtime/pluginExecution.ts"(){"use strict";init_pluginRegistry();epoch=randomUUID3(),hostIds=new Map,UUID=/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i}});function serializePluginExecutionError(error2){let value=error2,metadata=Object.fromEntries(Object.entries(value&&typeof value=="object"?value:{}).filter(([key2,field])=>!["__proto__","prototype","constructor","message","stack","code","details"].includes(key2)&&(field===null||["string","number","boolean"].includes(typeof field)))),serialized={message:typeof value?.message=="string"?value.message:String(error2),...typeof value?.code=="string"?{code:value.code}:{},...value?.details&&typeof value.details=="object"&&!Array.isArray(value.details)?{details:value.details}:{},...Object.keys(metadata).length?{metadata}:{}};try{if(Buffer.byteLength(JSON.stringify(serialized))<=64*1024)return serialized}catch{}return{message:Buffer.from(serialized.message).subarray(0,8192).toString("utf8"),...serialized.code?{code:serialized.code.slice(0,256)}:{},details:{diagnosticTruncated:!0},metadata:Object.fromEntries(Object.entries(metadata).slice(0,32).map(([key2,field])=>[key2.slice(0,128),typeof field=="string"?field.slice(0,512):field]))}}function pluginExecutionError(value){return Object.assign(new Error(value.message),{...Object.fromEntries(Object.entries(value.metadata??{}).filter(([key2])=>!["__proto__","prototype","constructor","message","stack","code","details"].includes(key2))),...value.code?{code:value.code}:{},...value.details?{details:value.details}:{}})}var init_pluginExecutorProtocol=__esm({"../client/src/plugins/runtime/pluginExecutorProtocol.ts"(){"use strict"}});import{execFile}from"node:child_process";async function stopPluginProcessTree(pid){if(!Number.isSafeInteger(pid)||pid<1)throw new Error("Invalid plugin process group");if(process.platform==="win32")return new Promise((resolve7,reject)=>{execFile("taskkill",["/PID",String(pid),"/T","/F"],{windowsHide:!0,timeout:5e3},error2=>{error2?reject(error2):resolve7()})});try{process.kill(-pid,"SIGKILL")}catch(error2){if(error2.code==="ESRCH")return;throw error2}let deadline=Date.now()+5e3;for(;isProcessGroupAlive(pid);){if(Date.now()>=deadline)throw new Error("Unable to confirm plugin process group stopped");await new Promise(resolve7=>setTimeout(resolve7,20))}}var init_pluginProcessTree=__esm({"../client/src/plugins/runtime/pluginProcessTree.ts"(){"use strict";init_dist()}});import{fork}from"node:child_process";async function executeRegisteredPlugin(plugin,invocation,context,options={},dependencies={}){let{signal,onProgress,source,access="public",executionTimeoutMs,cancellationGraceMs=1e3,workerPath,cwd,environment,describe,help,onOutput,model,recovery,migration,expectedRecovery,onExecutorStarted,onExecutorStopped,onDiagnostic,...invokeOptions}=options;for(let[name,value]of[["executionTimeoutMs",executionTimeoutMs],["cancellationGraceMs",cancellationGraceMs]])if(value!==void 0&&(!Number.isSafeInteger(value)||value<1||value>2147483647))throw new Error(`${name} must be a positive integer within the timer range`);signal?.throwIfAborted();let registration=dependencies.registration??findPluginRegistration(plugin);if(!registration)throw new PluginError("PLUGIN_NOT_REGISTERED",`插件未注册: ${plugin}`);let identity=context.invocation??createPluginInvocationIdentity(plugin,source??context.source??"sdk",context.parentInvocation),entryVersion=pluginFileVersion(registration.entry);return new Promise((resolve7,reject)=>{let child=fork(workerPath??PLUGIN_WORKER_PATH,[],{stdio:["ignore",onOutput?"pipe":"ignore","pipe","ipc"],serialization:"advanced",execArgv:[],detached:process.platform!=="win32",...cwd?{cwd}:{},...environment?{env:environment}:{}}),stderr="",outcome,stopping,cleanupError,treeStopRequested=!1,treeCleanup=Promise.resolve(),exited=!1,graceTimer,deadlineTimer,pendingActions=new Map;if(child.stderr?.setEncoding("utf8"),child.stderr?.on("data",chunk=>{stderr=`${stderr}${chunk}`.slice(-65536)}),onOutput)for(let stream of["stdout","stderr"])child[stream]?.on("data",chunk=>{try{onOutput(stream,Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk))}catch(error2){stop(error2 instanceof Error?error2:new Error(String(error2)))}});let send=message=>{if(!(!child.connected||exited))try{child.send(message,error2=>{error2&&!exited&&stop(error2)})}catch(error2){stop(error2 instanceof Error?error2:new Error(String(error2)))}},kill=()=>{if(!child.pid||exited||treeStopRequested)return;treeStopRequested=!0;let pid=child.pid;treeCleanup=Promise.resolve().then(()=>(dependencies.stopProcessTree??stopPluginProcessTree)(pid)).catch(()=>{cleanupError=Object.assign(new Error("Unable to confirm plugin process-tree cleanup"),{code:"PLUGIN_EXECUTOR_CLEANUP_FAILED",details:{executionStopped:!1}}),child.kill("SIGKILL")})},stop=error2=>{stopping||outcome||exited||(stopping=error2,send({type:"cancel",error:serializePluginExecutionError(error2)}),graceTimer=setTimeout(kill,cancellationGraceMs))},abort=()=>stop(Object.assign(new Error("Plugin execution cancelled"),{code:"PLUGIN_EXECUTION_CANCELLED"}));signal?.addEventListener("abort",abort,{once:!0}),signal?.aborted&&abort(),executionTimeoutMs!==void 0&&(deadlineTimer=setTimeout(()=>stop(Object.assign(new Error("Plugin execution deadline exceeded"),{code:"PLUGIN_EXECUTION_DEADLINE"})),executionTimeoutMs)),child.on("message",raw=>{if(!(exited||!raw||typeof raw!="object")){if(raw.type==="result"||raw.type==="error"){if(!outcome&&!stopping)try{outcome=raw.type==="result"?{value:JSON.parse(raw.json).value}:{error:pluginExecutionError(raw.error)},raw.type==="result"&&pendingActions.size&&(outcome={error:Object.assign(new Error("Plugin returned with pending host calls"),{code:"PLUGIN_PENDING_HOST_CALLS",details:{pendingActions:[...pendingActions.values()]}})})}catch(error2){stop(error2 instanceof Error?error2:new Error(String(error2)))}kill();return}if(!(stopping||outcome))if(raw.type==="diagnostic"&&model)try{onDiagnostic?.(raw.metadata)}catch(error2){stop(error2 instanceof Error?error2:new Error(String(error2)))}else if(raw.type==="progress")try{onProgress?.(raw.progress)}catch(error2){stop(error2 instanceof Error?error2:new Error(String(error2)))}else raw.type==="invoke"&&(pendingActions.set(raw.id,raw.action),Promise.resolve().then(()=>{if(stopping||exited)throw stopping??new Error("Plugin execution ended");let options2={...invokeOptions,...raw.options};return raw.stream?context.invoke(raw.action,raw.payload,options2,progress=>{!stopping&&!outcome&&pendingActions.has(raw.id)&&send({type:"invoke-progress",id:raw.id,progress})}):context.invoke(raw.action,raw.payload,options2)}).then(result=>send({type:"response",id:raw.id,result}),error2=>send({type:"response",id:raw.id,error:serializePluginExecutionError(error2)})).finally(()=>pendingActions.delete(raw.id)))}});let cleanup=()=>{exited=!0,signal?.removeEventListener("abort",abort),graceTimer&&clearTimeout(graceTimer),deadlineTimer&&clearTimeout(deadlineTimer)};child.once("error",error2=>{cleanup(),reject(error2)}),child.once("exit",()=>{kill(),child.stderr?.destroy()}),child.once("close",(code,exitSignal)=>{cleanup(),treeCleanup.then(()=>{if(!cleanupError&&treeStopRequested)try{onExecutorStopped?.()}catch(error2){reject(error2);return}cleanupError?reject(cleanupError):stopping?(Object.assign(stopping,{details:{pendingActions:[...pendingActions.values()],executionStopped:!0}}),reject(stopping)):outcome?.error?reject(outcome.error):outcome?resolve7(outcome.value):reject(Object.assign(new Error(stderr.trim()||`Plugin executor exited (${code??exitSignal})`),{code:"PLUGIN_EXECUTOR_EXITED"}))})});try{child.pid&&onExecutorStarted?.({pid:child.pid,processIdentity:processIdentity(child.pid)}),send({type:"start",plugin,invocation,options:invokeOptions,identity,entry:registration.entry,entryVersion,access,describe,help,model,recovery,migration,expectedRecovery,...dependencies.registration?{registration}:{}})}catch(error2){stop(error2 instanceof Error?error2:new Error(String(error2)))}})}var init_pluginExecutor=__esm({"../client/src/plugins/runtime/pluginExecutor.ts"(){"use strict";init_runtimePaths();init_pluginRegistry();init_pluginSource();init_pluginExecution();init_pluginExecutorProtocol();init_pluginProcessTree();init_dist()}});import{existsSync as existsSync3,lstatSync,renameSync as renameSync4}from"node:fs";import{join as join8}from"node:path";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,data){mkdirSync5(dirname5(file),{recursive:!0,mode:448});let temporary=`${file}.${randomUUID5()}.tmp`,descriptor;try{descriptor=openSync4(temporary,"wx",384),writeFileSync5(descriptor,data),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 createHash6}from"node:crypto";import{closeSync as closeSync5,openSync as openSync5,opendirSync,readFileSync as readFileSync8,readSync as readSync2,readdirSync as readdirSync6,rmSync as rmSync7,statSync as statSync6}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_plugin_tasks();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}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(readFileSync8(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=statSync6(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=statSync6(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:createHash6("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(statSync6(file).size!==task.result.bytes)throw new Error("Plugin task result size does not match its record");let offset=index*PLUGIN_TASK_RESULT_CHUNK_BYTES,buffer=Buffer.alloc(Math.min(PLUGIN_TASK_RESULT_CHUNK_BYTES,task.result.bytes-offset)),descriptor=openSync5(file,"r");try{if(readSync2(descriptor,buffer,0,buffer.length,offset)!==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 offset=0;offset<args.length;offset+=CHUNK_CHARACTERS)await evaluate(`void (window[${key2}] = (window[${key2}] || '') + ${JSON.stringify(args.slice(offset,offset+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_browser_action_protocol();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 offset=0;for await(let chunk of source.createReadStream({highWaterMark:CHUNK_BYTES,autoClose:!1}))await client.invoke("__artifact_write",{artifactId:id,offset,base64:chunk.toString("base64")},pinned),offset+=chunk.length;return await client.invoke("__artifact_complete",{artifactId:id,bytes:offset},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 offset=0;if(artifact.bytes===0){let empty=await client.invoke("__artifact_read",{artifactId:artifact.artifactId,offset:0},pinned);if(empty.totalBytes!==0||empty.bytes!==0||empty.base64!=="")throw new Error("Artifact export size mismatch")}for(;offset<artifact.bytes;){let chunk=await client.invoke("__artifact_read",{artifactId:artifact.artifactId,offset},pinned),bytes=Buffer.from(chunk.base64,"base64");if(!bytes.length||bytes.length!==chunk.bytes||chunk.totalBytes!==artifact.bytes||offset+bytes.length>artifact.bytes)throw new Error("Invalid artifact chunk or truncated transfer");await target.write(bytes),offset+=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_dist2=__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(data){let observe=data.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,data,fallback=15){if(action==="send_request"){let transferTimeoutMs=data.bodyFile!==void 0||Array.isArray(data.files)&&data.files.length>0?SEND_REQUEST_FILE_TRANSFER_TIMEOUT_MS:0;return Math.max(fallback,Math.ceil(((data.timeout??3e4)+transferTimeoutMs+5e3)/1e3))}if(action==="send_mtop_request")return Math.max(fallback,Math.ceil(((data.timeout??1e4)+5e3)/1e3));if(action==="page_download")return Math.max(fallback,Math.ceil((Number(data.timeout??3e4)+1e4)/1e3));if(action==="page_wait"){let waitMs=data.time??data.timeout??1e4;return Math.max(fallback,Math.ceil((Number(waitMs)+5e3)/1e3))}if(action==="tab_open"||action==="set_cookie"&&data.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(data.actions)?data.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"?data.observe!==null&&typeof data.observe=="object"&&!Array.isArray(data.observe):data.observe!==!1;if(action==="page_upload"){let actionTimeoutSec=observationEnabled?resolveObservedActionTimeoutSec(data):fallback;return Math.max(fallback,actionTimeoutSec+Math.ceil(PAGE_UPLOAD_FILE_TRANSFER_TIMEOUT_MS/1e3))}return observedAction&&observationEnabled?Math.max(fallback,resolveObservedActionTimeoutSec(data)):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_browser_action_protocol();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_types=__esm({"../provider/dist/types.js"(){"use strict";init_protocol()}});var PROVIDER_ACTIONS,providerActionSet,init_provider_actions=__esm({"../provider/dist/generated/provider-actions.js"(){"use strict";PROVIDER_ACTIONS=["tab_checkpoint","capture_checkpoint","get_requests","get_logs","get_events","set_mock","get_mocks","set_rule","get_rules","send_request","send_mtop_request","browser_release","page_screenshot","tab_open","tab_close","tab_list","tab_group","page_click","page_drag","page_type","page_scroll","page_hover","page_eval","page_runtime","page_inspect","page_snapshot","page_press","page_wait","page_navigate","page_upload","page_frames","set_device_emulation","set_app_profile","set_timezone","get_cookie","set_cookie","request_domain_permission","run_actions","page_download"],providerActionSet=new Set(PROVIDER_ACTIONS)}});var init_provider_runtime=__esm({"../provider/dist/provider-runtime.js"(){"use strict";init_types();init_provider_actions()}});import{existsSync as existsSync5,readFileSync as readFileSync9,renameSync as renameSync7,rmSync as rmSync8,statSync as statSync7,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(readFileSync9(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_types();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_provider_actions();init_provider_runtime();init_registry2();init_types();init_protocol();MAX_REQUEST_BYTES=16*1024*1024}});var init_dist3=__esm({"../provider/dist/index.js"(){"use strict";init_types();init_protocol();init_provider_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(data){if(data===null||typeof data!="object"||Array.isArray(data))return data;let out={};for(let[key2,value]of Object.entries(data))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(`
|
|
609
609
|
`)[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,data){if(browser.transport==="provider"){if(action==="browser_control"||action==="browser_release"&&data[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"&&data[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=data.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,data,transport){return browsers.filter(browser=>isConnectedBrowser(browser)&&(action!=="browser_list"||browser.transport!=="provider")&&!(transport==="cdp"&&browser.transport==="provider")&&supportsAction(browser,action,data))}function resolveLocalActionTarget(action,data,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,data,options.transport)[0];return target?{browser:target.browserId}:void 0}function selectBrowserResult(data){let selector=typeof data.browser=="string"?data.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 current=fs4.lstatSync(SOCKET_PATH);if(!current.isSocket()||current.dev!==identity.dev||current.ino!==identity.ino)return;fs4.unlinkSync(SOCKET_PATH)}catch{}}function invoke(action,data={},options={}){if(action==="__resolve_action_target"){if(!isBrowserCapabilityAction(data.action))return Promise.reject(new Error("Invalid browser capability action"));let target=resolveLocalActionTarget(data.action,data.data??{},options),browser2=target?.browser??(target?.socketPath?listLiveBrowsers().find(item=>item.socketPath===target.socketPath&&supportsAction(item,data.action,data.data??{}))?.browserId:void 0);return Promise.resolve(browser2?{browser:browser2}:null)}if(action==="browser_select_browser")try{return Promise.resolve(selectBrowserResult(data))}catch(error2){return Promise.reject(error2)}let{timeoutSec=resolveActionTimeoutSec(action,data),transport,browser,controlSource="local"}=options;if(action==="__control_session_resolve")return resolveControlSession(data.sessionId,timeoutSec,controlSource);let resolvedData=resolveFileRefs(data);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,data,timeoutSec,transport,selector,controlSource,socketPath){let configuredSocketPath=socketPath??process.env.MEARL_SOCKET_PATH?.trim();if(configuredSocketPath&&(socketPath||!selector))try{return await invokeOnce(action,data,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,data,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,data))throw new Error(`Browser ${describeBrowser(match)} does not support ${action}`);try{return await invokeOnce(action,data,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,data,transport);if(candidates.length>0){let target=candidates[0];try{return await invokeOnce(action,data,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,data,timeoutSec,transport,controlSource,!1)}async function invokeViaLegacy(action,data,timeoutSec,transport,controlSource,isRetry){let legacySocketIdentity=readOwnedLegacySocketIdentity();try{return await invokeOnce(action,data,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)&&!(transport==="cdp"&&browser.transport==="provider")&&supportsAction(browser,action,data));return target?invokeOnce(action,data,timeoutSec,transport,target.socketPath,controlSource,target.transport==="provider"?target.browserId:void 0):invokeViaLegacy(action,data,timeoutSec,transport,controlSource,!0)}throw err instanceof Error&&!isConnError(err)&&!isSocketSandboxError(err)?err:new Error(formatConnectError(err,SOCKET_PATH),{cause:error2})}}async function invokeOnce(action,data,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,version:CLIENT_VERSION,protocolVersion:3,controlSource:toControlSourceWireValue(controlSource)};data[SESSION_OPTION]&&(message.controlSessionId=data[SESSION_OPTION]),transport&&(message.transport=transport),providerTarget&&(message.providerTarget=providerTarget,message.deadline=deadline),socket.write(JSON.stringify(message)+`
|
|
610
610
|
`)}),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(`
|
|
611
|
-
`))!==-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(`
|
|
611
|
+
`))!==-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@2 --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(`
|
|
612
612
|
`):error2.code==="ECONNREFUSED"?[`Socket 连接被拒绝: ${socketPath}`,"已尝试处理 stale socket,但 native host 自动恢复仍失败。请运行 mearl check --local 定位故障。","请确认 Chrome 浏览器正在运行,可尝试在 chrome://extensions 重新加载插件。","",installHint].join(`
|
|
613
613
|
`):error2.code==="EPERM"?[`Socket 连接被执行环境拦截: ${socketPath}`,"当前进程没有访问该 Mearl Unix Socket 的权限(EPERM)。","如果命令运行在 Agent 沙箱中,请为这个 Socket 配置精确的 Unix Socket 白名单后重试。","无需重新安装浏览器插件,也不要使用 sudo 或改写 Socket 权限。"].join(`
|
|
614
614
|
`):[`Socket 连接失败: ${error2.message}`,"",installHint].join(`
|
|
@@ -974,7 +974,7 @@ except BaseException as error:
|
|
|
974
974
|
sys.exit(1)
|
|
975
975
|
`}});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(`
|
|
976
976
|
`))>=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)+`
|
|
977
|
-
`;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 closeSync8,mkdirSync as mkdirSync8,openSync as openSync8}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;mkdirSync8(paths.home,{recursive:!0,mode:448}),rotateLogIfNeeded(paths.log);let log=openSync8(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{closeSync8(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_plugin_tasks();init_pluginTaskWire();init_pluginExecutorProtocol();starting=new Map;PluginHostConnection=class{constructor(configuration={}){this.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 createHash8}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_pluginTasks=__esm({"../client/src/plugins/clients/pluginTasks.ts"(){"use strict";init_pluginHostConnection();init_dist();init_pluginHostPaths();init_unified();init_plugin_tasks();init_pluginExecutorProtocol();PluginTaskClient=class{constructor(configuration={}){this.configuration=configuration;this.connection=configuration.connection??new PluginHostConnection(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||createHash8("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 rmSync11}from"node:fs";import{randomUUID as randomUUID9}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 current=readRecord(paths.lease);if(current&&isProcessOwnerAlive(current)||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(randomUUID9()),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`])rmSync11(file,{recursive:!0,force:!0});current&&removeRecordIfPid(paths.lease,current.pid),process.platform!=="win32"&&rmSync11(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_pluginTasks();init_pluginTaskStore()}});var pluginPages_exports={};__export(pluginPages_exports,{PluginPageClient:()=>PluginPageClient});var PluginPageClient,init_pluginPages=__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))}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_pluginState=__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)}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_pluginModels=__esm({"../client/src/plugins/clients/pluginModels.ts"(){"use strict";init_pluginTasks();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,readPluginPageBundleChunk:()=>readPluginPageBundleChunk,refreshRegisteredPlugin:()=>refreshRegisteredPlugin,registerPlugin:()=>registerPlugin,resolvePluginEntry:()=>resolvePluginEntry,unregisterPlugin:()=>unregisterPlugin});import{readFileSync as readFileSync12}from"node:fs";import{dirname as dirname8,extname as extname2,resolve as resolve6}from"node:path";import{pathToFileURL}from"node:url";function readPluginVersion(entry){try{let manifest=skillManifestPath(resolve6(dirname8(entry),".."));return manifest?readFileSync12(manifest,"utf8").match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1]?.split(/\r?\n/).find(line=>/^version:\s*/.test(line))?.match(/^version:\s*["']?([^"'#\s]+)["']?\s*$/)?.[1]:void 0}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 data;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");data=Object.freeze(parsed)}catch{throw new Error("app.progress data 必须是可 JSON 序列化的对象")}return Object.freeze({message:progress.message,...data?{data}:{}})}function missingPluginEntryMessage(entry,pluginName){let name=pluginName??"<name>";return["插件入口已失效","",`插件:${name}`,`入口:${entry}`,"",`卸载命令:npx @mearl/setup plugin uninstall ${name}`].join(`
|
|
977
|
+
`;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 closeSync8,mkdirSync as mkdirSync8,openSync as openSync8}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;mkdirSync8(paths.home,{recursive:!0,mode:448}),rotateLogIfNeeded(paths.log);let log=openSync8(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{closeSync8(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_plugin_tasks();init_pluginTaskWire();init_pluginExecutorProtocol();starting=new Map;PluginHostConnection=class{constructor(configuration={}){this.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 createHash8}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_pluginTasks=__esm({"../client/src/plugins/clients/pluginTasks.ts"(){"use strict";init_pluginHostConnection();init_dist();init_pluginHostPaths();init_unified();init_plugin_tasks();init_pluginExecutorProtocol();PluginTaskClient=class{constructor(configuration={}){this.configuration=configuration;this.connection=configuration.connection??new PluginHostConnection(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||createHash8("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 rmSync11}from"node:fs";import{randomUUID as randomUUID9}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 current=readRecord(paths.lease);if(current&&isProcessOwnerAlive(current)||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(randomUUID9()),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`])rmSync11(file,{recursive:!0,force:!0});current&&removeRecordIfPid(paths.lease,current.pid),process.platform!=="win32"&&rmSync11(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_pluginTasks();init_pluginTaskStore()}});var pluginPages_exports={};__export(pluginPages_exports,{PluginPageClient:()=>PluginPageClient});var PluginPageClient,init_pluginPages=__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))}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_pluginState=__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)}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_pluginModels=__esm({"../client/src/plugins/clients/pluginModels.ts"(){"use strict";init_pluginTasks();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,readPluginPageBundleChunk:()=>readPluginPageBundleChunk,refreshRegisteredPlugin:()=>refreshRegisteredPlugin,registerPlugin:()=>registerPlugin,resolvePluginEntry:()=>resolvePluginEntry,unregisterPlugin:()=>unregisterPlugin});import{readFileSync as readFileSync12}from"node:fs";import{dirname as dirname8,extname as extname2,resolve as resolve6}from"node:path";import{pathToFileURL}from"node:url";function readPluginVersion(entry){try{let manifest=skillManifestPath(resolve6(dirname8(entry),".."));return manifest?readFileSync12(manifest,"utf8").match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1]?.split(/\r?\n/).find(line=>/^version:\s*/.test(line))?.match(/^version:\s*["']?([^"'#\s]+)["']?\s*$/)?.[1]:void 0}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 data;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");data=Object.freeze(parsed)}catch{throw new Error("app.progress data 必须是可 JSON 序列化的对象")}return Object.freeze({message:progress.message,...data?{data}:{}})}function missingPluginEntryMessage(entry,pluginName){let name=pluginName??"<name>";return["插件入口已失效","",`插件:${name}`,`入口:${entry}`,"",`卸载命令:npx @mearl/setup@2 plugin uninstall ${name}`].join(`
|
|
978
978
|
`)}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,current=findPluginRegistration(name);if(JSON.stringify(current)!==JSON.stringify(registration))return current;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(data,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,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:(data,expectedSequence)=>(assertActive(),app.checkpoint(data,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(`
|
|
979
979
|
`)},help(namespace,name2){let selected=name2?[find(name2)]:publicCommands();if(!selected.length){let location=toolbox?`请在 Mearl ${toolbox.displayMode==="popup"?"Popup":"工具箱"} 中打开「${toolbox.title}」使用。`:"此插件未提供公开 CLI 命令。";return`${namespace} 未提供 CLI 命令。
|
|
980
980
|
${location}`}let examples=selected.flatMap(({examples:examples2})=>examples2);return["用法:",...selected.map(({command,signature})=>` ${namespace} ${signature} ${command.description()}`),...examples.length?["","示例:",...examples.map(example=>` ${namespace} ${example}`)]:[]].join(`
|
|
@@ -984,5 +984,5 @@ ${location}`}let examples=selected.flatMap(({examples:examples2})=>examples2);re
|
|
|
984
984
|
`))!==-1;){let line=this.messageBuffer.slice(0,newlineIndex);if(this.messageBuffer=this.messageBuffer.slice(newlineIndex+1),!!line.trim())try{let message=JSON.parse(line);isHeartbeatMessage(message)||this.handleResponse(message)}catch{}}}handleResponse(response){let pending=this.pendingRequests.get(response.id);pending&&(this.pendingRequests.delete(response.id),clearTimeout(pending.timer),response.versionWarning&&console.warn(response.versionWarning),response.success?pending.resolve(response.data):pending.reject(new Error(response.error||"Unknown error from cloud-server")))}async requestRemote(action,data,timeoutSec,browser,connector,controlSessionId){await this.ensureConnected();let ws=this.ws;if(!ws||ws.readyState!==WebSocket.OPEN)throw new Error("Connection is not open");let id=`${Date.now()}_${Math.random().toString(36).slice(2,10)}`,message={id,action,data,timeoutSec};browser&&(message.browser=browser),connector&&(message.connector=connector),controlSessionId&&(message.controlSessionId=controlSessionId);let serialized=`${JSON.stringify(message)}
|
|
985
985
|
`,bytes=Buffer.byteLength(serialized,"utf8");if(bytes>10485760)throw new Error(`Cloud request exceeds ${10485760} byte message limit (${bytes} bytes)`);return new Promise((resolve7,reject)=>{let timer=setTimeout(()=>{this.pendingRequests.delete(id),reject(new Error(`Request timeout after ${timeoutSec}s`))},timeoutSec*1e3+RESPONSE_GRACE_MS);this.pendingRequests.set(id,{resolve:resolve7,reject,timer}),ws.send(serialized)})}rejectAllPending(error2){for(let pending of this.pendingRequests.values())clearTimeout(pending.timer),pending.reject(error2);this.pendingRequests.clear()}}}});var init_process_env=__esm({"../agent-runtime/dist/process-env.js"(){"use strict";init_dist()}});import fs5 from"node:fs";import{spawn as spawn8}from"node:child_process";import path5 from"node:path";function agentExecutableCandidatePaths({executableNames,configuredPath,env=process.env,extraUserCandidates=[],home,platform=process.platform,processExecPath=process.execPath,readdirSync:readdirSync8}){let pathKey=Object.keys(env).find(key2=>key2.toLowerCase()==="path")||"PATH",pathCandidates=(env[pathKey]||"").split(path5.delimiter).filter(Boolean).flatMap(directory=>executableNames.map(name=>path5.join(directory,name))),commonUserDirectories=[path5.join(home,".local","bin"),path5.join(home,".npm-global","bin"),path5.join(home,".volta","bin"),path5.join(home,".local","share","pnpm"),path5.join(home,"Library","pnpm"),path5.join(home,".bun","bin"),...platform==="win32"&&env.APPDATA?[path5.join(env.APPDATA,"npm")]:[]],systemDirectories=platform==="win32"?[]:["/opt/homebrew/bin","/usr/local/bin","/usr/bin"];return[configuredPath,...executableNames.map(name=>path5.join(path5.dirname(processExecPath),name)),...commonUserDirectories.flatMap(directory=>executableNames.map(name=>path5.join(directory,name))),...extraUserCandidates,...versionManagerExecutableCandidates(home,executableNames,{env,platform,readdirSync:readdirSync8}),...systemDirectories.flatMap(directory=>executableNames.map(name=>path5.join(directory,name))),...pathCandidates].filter(candidate=>typeof candidate=="string")}function executableFileCandidates(candidates){let seen=new Set,executableCandidates=[];for(let candidate of candidates)!candidate||seen.has(candidate)||(seen.add(candidate),isExecutableFile(candidate)&&executableCandidates.push(candidate));return executableCandidates}function probeAgentExecutable(executable,options){return new Promise(resolve7=>{let settled=!1,stderr="",launch=resolveSpawnCommand(executable,options.args),child=spawn8(launch.command,launch.args,{stdio:["ignore","ignore","pipe"],windowsHide:!0,env:prependNodePath()}),finish=error2=>{settled||(settled=!0,clearTimeout(timer),resolve7(error2))},timer=setTimeout(()=>{child.kill("SIGTERM"),finish(options.timeoutError)},options.timeoutMs);child.stderr?.on("data",chunk=>{stderr.length<4e3&&(stderr+=chunk.toString("utf8"))}),child.once("error",error2=>finish(options.startError(error2))),child.once("exit",code=>{if(code===0){finish(null);return}finish(stderr.trim()||options.exitError(code))})})}function isExecutableFile(candidate){try{return fs5.accessSync(candidate,process.platform==="win32"?fs5.constants.F_OK:fs5.constants.X_OK),fs5.statSync(candidate).isFile()}catch{return!1}}var AgentExecutableResolver,init_executable_resolver=__esm({"../agent-runtime/dist/executable-resolver.js"(){"use strict";init_process_env();AgentExecutableResolver=class{options;resolvedExecutable=null;resolutionPromise=null;generation=0;constructor(options){this.options=options}async resolve(){if(this.resolvedExecutable){if(isExecutableFile(this.resolvedExecutable))return{executable:this.resolvedExecutable,error:null};this.resolvedExecutable=null}if(this.resolutionPromise)return this.resolutionPromise;let generation=this.generation,resolution=this.probeCandidates(generation);this.resolutionPromise=resolution;try{return await resolution}finally{this.resolutionPromise===resolution&&(this.resolutionPromise=null)}}reset(){this.generation+=1,this.resolvedExecutable=null,this.resolutionPromise=null}async probeCandidates(generation){let candidates=this.options.candidates();if(candidates.length===0)return{executable:null,error:this.options.notFoundError};let failures=[],recordFailure=(executable,error2)=>{failures.push({executable,error:error2}),this.options.onFailure?.(executable,error2)},select=(executable,version)=>(this.generation===generation&&(this.resolvedExecutable=executable,this.options.onSelection?.(executable,version)),{executable,error:null}),preferredCandidates=this.options.preferred?candidates.filter(executable=>this.options.preferred?.(executable)):[],automaticCandidates=candidates.filter(executable=>!preferredCandidates.includes(executable));for(let executable of preferredCandidates){let error2=await this.options.probe(executable);if(error2===null)return select(executable,null);recordFailure(executable,error2)}let getVersion=this.options.getVersion;if(getVersion){let inspected=await Promise.all(automaticCandidates.map(async(executable,index)=>{let[error2,version]=await Promise.all([this.options.probe(executable),getVersion(executable).catch(()=>null)]);return{executable,error:error2,index,version}})),usable=inspected.filter(candidate=>candidate.error===null);for(let candidate of inspected)candidate.error!==null&&recordFailure(candidate.executable,candidate.error);usable.sort((left,right)=>{if(left.version&&right.version&&this.options.compareVersions){let comparison=this.options.compareVersions(left.version,right.version);if(comparison!==0)return-comparison}else{if(left.version)return-1;if(right.version)return 1}return left.index-right.index});let selected=usable[0];if(selected)return select(selected.executable,selected.version)}else for(let executable of automaticCandidates){let error2=await this.options.probe(executable);if(error2===null)return select(executable,null);recordFailure(executable,error2)}let detail=failures.map(({executable,error:error2})=>{let compactError=error2.replace(/\s+/g," ").trim().slice(0,300);return`${executable}: ${compactError}`}).join("; ");return{executable:null,error:`${this.options.unusableError} ${detail}`}}}}});import fs6 from"node:fs";import os3 from"node:os";import path6 from"node:path";import readline from"node:readline";import{spawn as spawn9}from"node:child_process";function configOverride(value){return Array.isArray(value)?value.map(configOverride):value&&typeof value=="object"?Object.fromEntries(Object.entries(value).filter(([,field])=>field!=null).map(([key2,field])=>[key2,configOverride(field)])):value}function normalizeCodexVersion(value){return typeof value!="string"||value.length>MAX_VERSION_OUTPUT_CHARS?null:CODEX_VERSION_PATTERN.exec(value.trim())?.[1]??null}function parsedCodexVersion(value){let normalized=normalizeCodexVersion(value);if(!normalized)return null;let withoutBuild=normalized.split("+",1)[0],prereleaseSeparator=withoutBuild.indexOf("-"),coreValue=prereleaseSeparator===-1?withoutBuild:withoutBuild.slice(0,prereleaseSeparator),prereleaseValue=prereleaseSeparator===-1?void 0:withoutBuild.slice(prereleaseSeparator+1),core=coreValue?.split(".").map(Number);return core?.length!==3||core.some(component=>!Number.isSafeInteger(component)||component<0)?null:{core,prerelease:prereleaseValue?prereleaseValue.split("."):[]}}function compareCodexVersions(leftValue,rightValue){let left=parsedCodexVersion(leftValue),right=parsedCodexVersion(rightValue);if(!left||!right)return leftValue.localeCompare(rightValue,void 0,{numeric:!0});for(let index=0;index<left.core.length;index+=1){let difference=left.core[index]-right.core[index];if(difference!==0)return difference}if(left.prerelease.length===0||right.prerelease.length===0)return left.prerelease.length===right.prerelease.length?0:left.prerelease.length===0?1:-1;let identifierCount=Math.max(left.prerelease.length,right.prerelease.length);for(let index=0;index<identifierCount;index+=1){let leftIdentifier=left.prerelease[index],rightIdentifier=right.prerelease[index];if(leftIdentifier===void 0||rightIdentifier===void 0)return leftIdentifier===rightIdentifier?0:leftIdentifier===void 0?-1:1;if(leftIdentifier===rightIdentifier)continue;let leftNumeric=/^\d+$/.test(leftIdentifier),rightNumeric=/^\d+$/.test(rightIdentifier);return leftNumeric&&rightNumeric?Number(leftIdentifier)-Number(rightIdentifier):leftNumeric!==rightNumeric?leftNumeric?-1:1:leftIdentifier.localeCompare(rightIdentifier)}return 0}function codexAppExecutableCandidatePaths(options={}){let env=options.env??process.env,home=(options.homedir??os3.homedir)(),platform=options.platform??process.platform;return platform==="darwin"?["/Applications",path6.join(home,"Applications")].flatMap(root=>["ChatGPT.app","Codex.app"].flatMap(app=>{let resources=path6.join(root,app,"Contents","Resources");return[path6.join(resources,"codex-cli","CodexCLI.app","Contents","MacOS","codex"),path6.join(resources,"codex")]})):platform==="win32"?[path6.join(env.LOCALAPPDATA||path6.join(home,"AppData","Local"),"Programs"),env.ProgramFiles,env["ProgramFiles(x86)"]].filter(root=>typeof root=="string"&&root.length>0).flatMap(root=>["ChatGPT","Codex"].map(app=>path6.join(root,app,"resources","codex.exe"))):[]}function isIdeBundledCodexPath(candidate,home){let resolvedCandidates=[path6.resolve(candidate)];try{resolvedCandidates.push(fs6.realpathSync(candidate))}catch{}return[".vscode",".vscode-insiders",".cursor",".windsurf"].some(directory=>{let extensionsRoot=`${path6.join(home,directory,"extensions")}${path6.sep}`;return resolvedCandidates.some(resolved=>resolved.startsWith(extensionsRoot))})}function codexExecutableCandidatePaths(options={}){let env=options.env??process.env,home=(options.homedir??os3.homedir)(),platform=options.platform??process.platform;return agentExecutableCandidatePaths({executableNames:[platform==="win32"?"codex.cmd":"codex"],configuredPath:env.MEARL_CODEX_PATH,env,home,platform,processExecPath:options.processExecPath,readdirSync:options.readdirSync,extraUserCandidates:codexAppExecutableCandidatePaths({env,homedir:()=>home,platform})}).filter(candidate=>candidate===env.MEARL_CODEX_PATH||!isIdeBundledCodexPath(candidate,home))}function codexPackageVersion(executable){let packageFiles=[];try{let resolved=fs6.realpathSync(executable);if(resolved.split(path6.sep).includes("node_modules")){let directory=path6.dirname(resolved);for(let depth=0;depth<6;depth+=1){packageFiles.push(path6.join(directory,"package.json"));let parent=path6.dirname(directory);if(parent===directory)break;directory=parent}}}catch{}/\.(?:cmd|bat)$/i.test(executable)&&packageFiles.push(path6.join(path6.dirname(executable),"node_modules","@openai","codex","package.json"));for(let packageFile of packageFiles)try{let manifest=JSON.parse(fs6.readFileSync(packageFile,"utf8"));if(manifest.name==="@openai/codex"){let version=normalizeCodexVersion(manifest.version);if(version)return version}}catch{}return null}function codexExecutableVersion(executable){let packageVersion=codexPackageVersion(executable);return packageVersion?Promise.resolve(packageVersion):new Promise(resolve7=>{let settled=!1,output="",launch=resolveSpawnCommand(executable,["--version"]),child=spawn9(launch.command,launch.args,{stdio:["ignore","pipe","pipe"],windowsHide:!0,env:prependNodePath()}),finish=version=>{settled||(settled=!0,clearTimeout(timer),resolve7(version))},capture=chunk=>{let remaining=MAX_VERSION_OUTPUT_CHARS-output.length;remaining>0&&(output+=chunk.toString("utf8").slice(0,remaining))};child.stdout.on("data",capture),child.stderr.on("data",capture),child.once("error",()=>finish(null)),child.once("exit",code=>finish(code===0?normalizeCodexVersion(output):null));let timer=setTimeout(()=>{child.kill("SIGTERM"),finish(null)},PROBE_TIMEOUT_MS)})}function codexModelInfo(value){if(typeof value!="string")return;let id=value.trim().slice(0,MAX_MODEL_CHARS);return id?{id}:void 0}function fullAccessWithApprovals(){return{sandbox:"danger-full-access",approvalPolicy:"untrusted"}}function timestampMs(value){if(typeof value=="number"&&Number.isFinite(value))return value<1e12?value*1e3:value;if(typeof value=="string"){let parsed=Date.parse(value);return Number.isNaN(parsed)?void 0:parsed}}function codexSkillScope(value){return value==="user"?"global":value==="repo"?"project":value==="system"||value==="admin"?value:"unknown"}function userMessageText(item){return Array.isArray(item?.content)?item.content.flatMap(part=>part?.type==="text"&&typeof part.text=="string"?[part.text]:[]).join(`
|
|
986
986
|
`):""}function userMessageImageCount(item){return Array.isArray(item?.content)?item.content.filter(part=>part?.type==="image"||part?.type==="localImage").length:0}function threadHistoryEvents(thread){if(!Array.isArray(thread?.turns))return[];let events=[];for(let turn of thread.turns.slice(-100)){if(!turn||typeof turn.id!="string"||!Array.isArray(turn.items))continue;let timestamp=timestampMs(turn.completedAt??turn.startedAt);for(let item of turn.items)if(!(!item||typeof item.id!="string"||typeof item.type!="string")){if(item.type==="userMessage"){let text3=userMessageText(item),imageCount=userMessageImageCount(item);(text3||imageCount>0)&&events.push({kind:"lifecycle",method:"chat/userMessage",params:{text:text3,...imageCount>0?{imageCount}:{}},...timestamp!==void 0?{timestamp}:{}});continue}events.push({kind:"notification",method:"item/completed",params:{turnId:turn.id,item},...timestamp!==void 0?{timestamp}:{}})}}return events}function codexThreadUrl(providerSessionId){if(!/^[a-zA-Z0-9_-]+$/.test(providerSessionId))throw new Error("Codex returned an invalid thread id");return`codex://threads/${providerSessionId}`}function codexWorkspaceUrl(directory){return`codex://threads/new?path=${encodeURIComponent(directory)}`}function externalOpenCommand(url,platform=process.platform){return platform==="darwin"?{command:"open",args:[url]}:platform==="win32"?{command:"cmd.exe",args:["/d","/s","/c","start","",url]}:{command:"xdg-open",args:[url]}}function openExternalUrl(url){let{command,args}=externalOpenCommand(url);return new Promise((resolve7,reject)=>{let child=spawn9(command,args,{stdio:"ignore",windowsHide:!0});child.once("error",error2=>{reject(new Error(`Unable to open Codex App: ${error2.message}`))}),child.once("close",code=>{code===0?resolve7():reject(new Error("Unable to open Codex App. Make sure it is installed."))})})}var REQUEST_TIMEOUT_MS,PROBE_TIMEOUT_MS,MAX_MODEL_CHARS,MAX_VERSION_OUTPUT_CHARS,CODEX_NOT_FOUND_ERROR,CODEX_VERSION_PATTERN,CodexAgentProvider,init_codex_provider=__esm({"../agent-runtime/dist/codex-provider.js"(){"use strict";init_executable_resolver();init_process_env();REQUEST_TIMEOUT_MS=3e4,PROBE_TIMEOUT_MS=5e3,MAX_MODEL_CHARS=256,MAX_VERSION_OUTPUT_CHARS=1024,CODEX_NOT_FOUND_ERROR="Codex was not found. Install @openai/codex, install the Codex App, or set MEARL_CODEX_PATH.",CODEX_VERSION_PATTERN=/^(?:codex-cli\s+)?v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)$/;CodexAgentProvider=class{log;clientVersion;mearlSocketPath;id="codex";process=null;initialized=!1;lifecycle=0;modelRuntime;startPromise=null;nextRequestId=1;pendingRequests=new Map;listeners=new Set;knownThreadIds=new Set;sessionModels=new Map;executableResolver;constructor(log,clientVersion,mearlSocketPath){this.log=log,this.clientVersion=clientVersion,this.mearlSocketPath=mearlSocketPath,this.executableResolver=new AgentExecutableResolver({candidates:()=>this.resolveExecutableCandidates(),probe:executable=>this.probeExecutable(executable),getVersion:executable=>this.readExecutableVersion(executable),compareVersions:compareCodexVersions,preferred:executable=>executable===process.env.MEARL_CODEX_PATH,notFoundError:CODEX_NOT_FOUND_ERROR,unusableError:"Codex candidates were found, but none supports app-server.",onFailure:(executable,error2)=>{this.log("[Codex] Skipping unusable runtime:",executable,error2)},onSelection:(executable,version)=>{this.modelRuntime={executable,...version?{version}:{},selection:executable===process.env.MEARL_CODEX_PATH?"explicit":"automatic",started:!1},this.modelDiagnostic({runtime:this.modelRuntime}),this.log("[Codex] Selected runtime:",executable,version?`(version ${version})`:executable===process.env.MEARL_CODEX_PATH?"(explicitly configured)":"(version unavailable)")}})}async getDescriptor(){let{error:error2}=await this.resolveExecutable();return{id:this.id,name:"Codex",available:error2===null,capabilities:{streaming:!0,resume:!0,listSessions:!0,refreshHistory:!0,imageInput:!0,interrupt:!0,requests:!0},...error2?{error:error2}:{},setup:{installCommand:"npm install -g @openai/codex",loginCommand:"codex login",docsUrl:"https://developers.openai.com/codex/cli"}}}async prepare(){await this.ensureStarted()}async listModels(){await this.ensureStarted();let models=new Map,cursor,cursors=new Set;for(let page=0;page<4;page++){let result=await this.request("model/list",{limit:100,...cursor?{cursor}:{}});if(!Array.isArray(result?.data))throw new Error("Invalid Codex model list");for(let value of result.data){let id=value?.model??value?.id;if(value?.hidden||typeof id!="string"||!id||id.length>256||models.has(id))continue;let efforts=["none","minimal","low","medium","high","xhigh","max"],reasoningEfforts=Array.isArray(value.supportedReasoningEfforts)?efforts.filter(effort=>value.supportedReasoningEfforts.some(option=>option?.reasoningEffort===effort)):void 0;if(models.set(id,{id,displayName:typeof value.displayName=="string"?value.displayName.slice(0,256):id,...typeof value.description=="string"?{description:value.description.slice(0,2e3)}:{},isDefault:value.isDefault===!0,...reasoningEfforts?{reasoningEfforts}:{},...efforts.includes(value.defaultReasoningEffort)?{defaultReasoningEffort:value.defaultReasoningEffort}:{}}),models.size>=200)break}if(!result.nextCursor||models.size>=200)break;if(typeof result.nextCursor!="string"||cursors.has(result.nextCursor))throw new Error("Invalid Codex model cursor");cursor=result.nextCursor,cursors.add(result.nextCursor)}return[...models.values()]}onEvent(listener){return this.listeners.add(listener),()=>this.listeners.delete(listener)}async listSessions(options){await this.ensureStarted();let result=await this.request("thread/list",{cursor:options.cursor??null,limit:options.limit,sortKey:"updated_at",sortDirection:"desc",archived:!1,...options.cwd?{cwd:options.cwd}:{}});return{sessions:Array.isArray(result?.data)?result.data.flatMap(thread=>{if(!thread||typeof thread.id!="string")return[];let title=typeof thread.name=="string"&&thread.name.trim()?thread.name.trim():typeof thread.preview=="string"&&thread.preview.trim()?thread.preview.trim():void 0,cwd=typeof thread.cwd=="string"?thread.cwd:void 0,updatedAt=timestampMs(thread.recencyAt??thread.updatedAt),ephemeral=typeof thread.ephemeral=="boolean"?thread.ephemeral:void 0;return[{providerSessionId:thread.id,...title?{title}:{},...cwd?{cwd}:{},...updatedAt!==void 0?{updatedAt}:{},...ephemeral!==void 0?{ephemeral}:{}}]}):[],...typeof result?.nextCursor=="string"?{nextCursor:result.nextCursor}:{}}}async listSkills(cwd){await this.ensureStarted();let result=await this.request("skills/list",{cwds:[cwd||os3.homedir()],forceReload:!0});return Array.isArray(result?.data)?result.data.flatMap(entry=>Array.isArray(entry?.skills)?entry.skills.flatMap(skill=>!skill||typeof skill.name!="string"||typeof skill.path!="string"?[]:[{name:skill.name,path:skill.path,scope:codexSkillScope(skill.scope)}]):[]):[]}async startSession(options){await this.ensureStarted();let cwd=options.cwd||process.env.MEARL_AGENT_CWD||os3.homedir(),config=options.modelExecution?await this.modelExecutionConfig():void 0,result=await this.request("thread/start",{...config?{config}:{},cwd,...options.modelId?{model:options.modelId}:{},...options.readOnly?{sandbox:"read-only",approvalPolicy:"never"}:fullAccessWithApprovals(),...options.ephemeral!==void 0?{ephemeral:options.ephemeral}:{},...options.developerInstructions?{developerInstructions:options.developerInstructions}:{}}),threadId=result?.thread?.id;if(typeof threadId!="string"||!threadId)throw new Error("Codex did not return a thread id");options.modelExecution&&this.modelDiagnostic({threadRestrictionsAccepted:!0}),this.knownThreadIds.add(threadId);let model=codexModelInfo(result?.model);return model&&this.sessionModels.set(threadId,model),threadId}async modelExecutionConfig(){let config=(await this.request("config/read",{includeLayers:!1}))?.config;if(!config||typeof config!="object")throw new Error("Codex cannot report its effective execution configuration");let home=process.env.CODEX_HOME||path6.join(os3.homedir(),".codex"),store=config.cli_auth_credentials_store;this.modelDiagnostic({configuration:{source:process.env.CODEX_HOME?"environment":"default",home,filePresent:fs6.existsSync(path6.join(home,"config.toml")),...typeof config.model_provider=="string"?{baseProvider:config.model_provider}:{},...typeof store=="string"?{credentialStore:["file","keyring","auto","ephemeral"].includes(store)?store:"unknown"}:{}}});let mcp=config.mcp_servers??{},apps=config.apps??{};if(typeof mcp!="object"||Array.isArray(mcp)||typeof apps!="object"||Array.isArray(apps))throw new Error("Invalid Codex model tool configuration");return configOverride({mcp_servers:Object.fromEntries(Object.keys(mcp).map(name=>[name,{...mcp[name],enabled:!1}])),apps:Object.fromEntries(["_default",...Object.keys(apps)].map(name=>[name,{...apps[name]??{},enabled:!1}])),web_search:"disabled",features:{apps:!1,plugins:!1,hooks:!1,codex_hooks:!1,plugin_hooks:!1,multi_agent:!1,multi_agent_v2:!1,collab:!1,computer_use:!1,browser_use:!1,image_generation:!1,imagegenext:!1,memory_tool:!1,memories:!1,skill_search:!1}})}async resumeSession(providerSessionId,options){await this.ensureStarted();let result=await this.request("thread/resume",{threadId:providerSessionId,...options.cwd?{cwd:options.cwd}:{},...fullAccessWithApprovals(),...options.developerInstructions?{developerInstructions:options.developerInstructions}:{}}),threadId=result?.thread?.id;if(typeof threadId!="string"||!threadId)throw new Error("Codex did not return a resumed thread id");this.knownThreadIds.add(threadId);let model=codexModelInfo(result?.model);return model&&this.sessionModels.set(threadId,model),{providerSessionId:threadId,history:threadHistoryEvents(result.thread),historyComplete:!0}}getSessionModel(providerSessionId){let model=this.sessionModels.get(providerSessionId);return model?{...model}:void 0}async sendMessage(providerSessionId,message,images=[],options={}){await this.ensureStarted();let turnId=(await this.request("turn/start",{threadId:providerSessionId,input:[...message?[{type:"text",text:message,text_elements:[]}]:[],...images.map(image=>({type:"image",url:`data:${image.mimeType};base64,${image.data}`}))],summary:options.reasoningSummary??"detailed",...options.outputSchema?{outputSchema:options.outputSchema}:{},...options.modelExecution?{approvalPolicy:"never",sandboxPolicy:{type:"readOnly",networkAccess:!1}}:{},...options.reasoningEffort?{effort:options.reasoningEffort}:{}}))?.turn?.id;if(typeof turnId!="string"||!turnId)throw new Error("Codex did not return a turn id");return options.modelExecution&&this.modelDiagnostic({turnRestrictionsAccepted:!0}),{turnId}}async interrupt(providerSessionId,turnId){await this.ensureStarted(),await this.request("turn/interrupt",{threadId:providerSessionId,turnId})}async closeSession(providerSessionId){this.knownThreadIds.delete(providerSessionId),this.sessionModels.delete(providerSessionId),this.initialized&&this.request("thread/unsubscribe",{threadId:providerSessionId}).catch(error2=>{this.log("[Codex] Unable to unsubscribe thread:",String(error2))})}openSession(providerSessionId){return openExternalUrl(codexThreadUrl(providerSessionId))}openWorkspace(cwd){return openExternalUrl(codexWorkspaceUrl(cwd))}respondToRequest(requestId,result){this.write({id:requestId,result})}dispose(){this.lifecycle++,this.rejectPending(new Error("Codex provider disposed"));let child=this.process;this.process=null,this.initialized=!1,this.startPromise=null,this.sessionModels.clear(),this.executableResolver.reset(),child&&!child.killed&&child.kill("SIGTERM")}async ensureStarted(){if(this.process&&!this.process.killed&&this.initialized)return;if(this.startPromise)return this.startPromise;this.process&&!this.process.killed&&(this.process.kill("SIGTERM"),this.process=null);let pending=this.start();this.startPromise=pending;try{await pending}catch(error2){throw this.startPromise===pending&&(this.startPromise=null),error2}}async start(){let lifecycle=this.lifecycle,resolution=await this.resolveExecutable();if(lifecycle!==this.lifecycle)throw new Error("Codex provider disposed during startup");if(resolution.executable===null)throw new Error(resolution.error);let{executable}=resolution,launch=resolveSpawnCommand(executable,["app-server","--listen","stdio://"]),childEnv=prependNodePath();this.mearlSocketPath&&(childEnv.MEARL_SOCKET_PATH=this.mearlSocketPath);let child=spawn9(launch.command,launch.args,{stdio:["pipe","pipe","pipe"],windowsHide:!0,env:childEnv});this.process=child,readline.createInterface({input:child.stdout}).on("line",line=>this.handleLine(line)),child.stderr.on("data",chunk=>{let message=chunk.toString("utf8").trim();message&&this.log("[Codex]",message)}),child.once("error",error2=>this.handleExit(child,error2)),child.once("exit",(code,signal)=>{this.handleExit(child,new Error(`Codex app-server exited (code=${String(code)}, signal=${String(signal)})`))});try{await this.request("initialize",{clientInfo:{name:"mearl",title:"Mearl",version:this.clientVersion},capabilities:{experimentalApi:!0}}),this.write({method:"initialized",params:{}}),this.initialized=!0,this.modelRuntime&&(this.modelRuntime={...this.modelRuntime,started:!0},this.modelDiagnostic({runtime:this.modelRuntime}))}catch(error2){let startupError=error2 instanceof Error?error2:new Error(String(error2));throw this.handleExit(child,startupError),child.killed||child.kill("SIGTERM"),startupError}}handleLine(line){let message;try{message=JSON.parse(line)}catch{this.log("[Codex] Invalid JSONL message:",line);return}if(message.method){let isRequest=message.id!==void 0,providerSessionId=this.extractThreadId(message.params);if(!isRequest&&providerSessionId&&message.method==="thread/settings/updated"){let model=codexModelInfo(message.params?.threadSettings?.model);model&&(this.sessionModels.set(providerSessionId,model),this.emit({providerSessionId,kind:"notification",method:"session/model/updated",params:{model}}))}else if(!isRequest&&providerSessionId&&message.method==="model/rerouted"){let model=codexModelInfo(message.params?.toModel);model&&this.emit({providerSessionId,kind:"notification",method:"session/activeModel/updated",params:{model}})}this.emit({providerSessionId,kind:isRequest?"request":message.method==="error"?"error":"notification",method:message.method,params:message.params??{},...isRequest?{requestId:message.id}:{}});return}if(typeof message.id!="number")return;let pending=this.pendingRequests.get(message.id);pending&&(clearTimeout(pending.timer),this.pendingRequests.delete(message.id),message.error?pending.reject(new Error(message.error.message||`Codex error ${message.error.code??""}`)):pending.resolve(message.result))}request(method,params){let id=this.nextRequestId++;return new Promise((resolve7,reject)=>{let timer=setTimeout(()=>{this.pendingRequests.delete(id),reject(new Error(`Codex request timed out: ${method}`))},REQUEST_TIMEOUT_MS);this.pendingRequests.set(id,{resolve:resolve7,reject,timer});try{this.write({id,method,params})}catch(error2){clearTimeout(timer),this.pendingRequests.delete(id),reject(error2 instanceof Error?error2:new Error(String(error2)))}})}write(message){let child=this.process;if(!child||child.killed||!child.stdin.writable)throw new Error("Codex app-server is not running");child.stdin.write(`${JSON.stringify(message)}
|
|
987
|
-
`)}handleExit(child,error2){if(this.process===child){this.process=null,this.initialized=!1,this.startPromise=null,this.executableResolver.reset(),this.rejectPending(error2);for(let providerSessionId of this.knownThreadIds)this.emit({providerSessionId,kind:"error",method:"provider/exited",params:{message:error2.message}})}}rejectPending(error2){for(let[id,pending]of this.pendingRequests)clearTimeout(pending.timer),pending.reject(error2),this.pendingRequests.delete(id)}modelDiagnostic(metadata){this.emit({kind:"notification",method:"provider/model/diagnostic",params:metadata})}emit(event){for(let listener of this.listeners)listener(event)}extractThreadId(params){return[params?.threadId,params?.conversationId,params?.thread?.id,params?.turn?.threadId].find(value=>typeof value=="string")}probeExecutable(executable){return probeAgentExecutable(executable,{args:["app-server","--help"],timeoutMs:PROBE_TIMEOUT_MS,timeoutError:"Timed out while checking Codex app-server availability.",startError:error2=>`Failed to start Codex: ${error2.message}`,exitError:code=>`Codex app-server check exited with code ${String(code)}`})}readExecutableVersion(executable){return codexExecutableVersion(executable)}resolveExecutable(){return this.executableResolver.resolve()}resolveExecutableCandidates(){return executableFileCandidates(codexExecutableCandidatePaths())}}}});import{readFileSync as readFileSync16}from"node:fs";async function runCodexModel(request,context,provider=new CodexAgentProvider(()=>{},JSON.parse(readFileSync16(new URL("../package.json",import.meta.url),"utf8")).version)){if(request.provider!=="codex"||request.mode!=="agent"||request.permissions!=="read-only")throw Object.assign(new Error("Codex supports explicit read-only agent execution; strict tool-free generation is unavailable"),{code:"PLUGIN_MODEL_CAPABILITY_UNSUPPORTED"});context.signal.throwIfAborted();let session,turn,text3,bytes=0,index=0,retries=0,buffered="",streamItem,streamPhase,phases=new Map,flushTimer,flush=()=>{clearTimeout(flushTimer),flushTimer=void 0;try{for(let offset=0;offset<buffered.length;offset+=4e3)streamItem||streamPhase?context.onDelta(buffered.slice(offset,offset+4e3),++index,{...streamItem?{itemId:streamItem}:{},...streamPhase?{phase:streamPhase}:{}}):context.onDelta(buffered.slice(offset,offset+4e3),++index)}catch(error2){fail2(error2)}buffered=""},finish,fail2,completed=new Promise((resolve7,reject)=>{finish=resolve7,fail2=reject});completed.catch(()=>{});let listener=event=>{let params=event.params;event.method==="provider/model/diagnostic"&&context.onDiagnostic?.(params),event.method==="account/updated"&&context.onDiagnostic?.({authentication:["apikey","chatgpt","chatgptAuthTokens"].includes(params?.authMode)?params.authMode:"unknown"}),event.method==="session/model/updated"&&typeof params?.model?.id=="string"&&context.onDiagnostic?.({selectedModel:params.model.id}),event.method==="session/activeModel/updated"&&typeof params?.model?.id=="string"&&context.onDiagnostic?.({activeModel:params.model.id});let relevant=event.providerSessionId===void 0||event.providerSessionId===session;if(event.kind==="request"&&relevant){try{provider.respondToRequest(event.requestId,{error:"Interactive requests are disabled for background model tasks"})}catch{}fail2(Object.assign(new Error("Model requested interactive input"),{code:"PLUGIN_MODEL_INTERACTION_REQUIRED"})),provider.dispose();return}if((event.kind==="error"||event.method==="provider/exited")&&relevant){if(event.method!=="provider/exited"&¶ms?.willRetry===!0){context.onDiagnostic?.({retryCount:++retries});return}fail2(Object.assign(new Error(String(params?.error?.message??params?.message??"Model provider failed")),{code:"PLUGIN_MODEL_PROVIDER_ERROR"})),provider.dispose();return}if(!(!session||event.providerSessionId!==session))if(event.method==="item/started"&¶ms?.item?.type==="agentMessage")flush(),streamItem=params.item.id,streamPhase=params.item.phase,streamItem&&streamPhase&&phases.set(streamItem,streamPhase);else if(event.method==="item/agentMessage/delta"&&typeof params?.delta=="string"){let itemId=params.itemId??streamItem,phase=params.phase??(itemId?phases.get(itemId):streamPhase);if((itemId!==streamItem||phase!==streamPhase)&&flush(),streamItem=itemId,streamPhase=phase,bytes+=Buffer.byteLength(params.delta),bytes>32*1024*1024){fail2(new Error("Model output exceeds 32 MiB"));return}buffered+=params.delta,buffered.length>=16e3?flush():flushTimer??=setTimeout(flush,50)}else if(event.method==="item/completed"&¶ms?.item?.type==="agentMessage"&&typeof params.item.text=="string"){if(flush(),Buffer.byteLength(params.item.text)>32*1024*1024){fail2(new Error("Model output exceeds 32 MiB"));return}(!params.item.phase||params.item.phase==="final_answer")&&(text3=params.item.text)}else event.method==="turn/completed"&&(params?.turn?.status!=="completed"?fail2(Object.assign(new Error(`Model turn ended with ${String(params?.turn?.status)}${params?.turn?.error?.message?`: ${params.turn.error.message}`:""}`),{code:"PLUGIN_MODEL_TURN_FAILED"})):text3===void 0?fail2(new Error("Model returned no final text")):(flush(),finish(text3)))},unsubscribe=provider.onEvent(listener),abort=()=>{fail2(Object.assign(new Error("Model execution cancelled"),{code:"PLUGIN_EXECUTION_CANCELLED"})),session&&turn&&provider.interrupt(session,turn).catch(()=>{}),provider.dispose()};context.signal.addEventListener("abort",abort,{once:!0});try{session=context.conversation?.threadId??await provider.startSession({cwd:context.cwd,ephemeral:!0,readOnly:!0,modelExecution:!0,...request.model?{modelId:request.model}:{},...request.instructions?{developerInstructions:request.instructions}:{}}),context.conversation&&(context.conversation.threadId=session),context.onDiagnostic?.({...provider.getSessionModel(session)?.id?{selectedModel:provider.getSessionModel(session).id}:{}}),context.signal.throwIfAborted();let[,result]=await Promise.all([provider.sendMessage(session,request.prompt,[],{reasoningSummary:"none",reasoningEffort:request.reasoningEffort,outputSchema:request.outputSchema,modelExecution:!0}).then(value=>{turn=value.turnId}),completed]);return context.signal.throwIfAborted(),{text:result,provider:request.provider,mode:request.mode,...provider.getSessionModel(session)?.id?{model:provider.getSessionModel(session).id}:{}}}catch(error2){throw context.conversation&&(delete context.conversation.threadId,provider.dispose()),error2}finally{clearTimeout(flushTimer),context.signal.removeEventListener("abort",abort),unsubscribe(),context.conversation||provider.dispose()}}var init_model=__esm({"../agent-runtime/dist/model.js"(){"use strict";init_codex_provider();init_capabilities()}});import{readFileSync as readFileSync17}from"node:fs";async function listCodexModels(signal,provider=new CodexAgentProvider(()=>{},JSON.parse(readFileSync17(new URL("../package.json",import.meta.url),"utf8")).version)){signal.throwIfAborted();let abort,cancelled=new Promise((_resolve,reject)=>{abort=()=>{provider.dispose(),reject(signal.reason??new Error("Model discovery cancelled"))},signal.addEventListener("abort",abort,{once:!0})});try{return await Promise.race([provider.listModels(),cancelled])}finally{signal.removeEventListener("abort",abort),provider.dispose()}}var init_model_catalog=__esm({"../agent-runtime/dist/model-catalog.js"(){"use strict";init_codex_provider()}});var codex_exports={};__export(codex_exports,{CODEX_MODEL_CAPABILITY:()=>CODEX_MODEL_CAPABILITY,CodexAgentProvider:()=>CodexAgentProvider,codexAppExecutableCandidatePaths:()=>codexAppExecutableCandidatePaths,codexExecutableCandidatePaths:()=>codexExecutableCandidatePaths,codexThreadUrl:()=>codexThreadUrl,codexWorkspaceUrl:()=>codexWorkspaceUrl,compareCodexVersions:()=>compareCodexVersions,externalOpenCommand:()=>externalOpenCommand,listCodexModels:()=>listCodexModels,normalizeCodexVersion:()=>normalizeCodexVersion,runCodexModel:()=>runCodexModel});var init_codex=__esm({"../agent-runtime/dist/codex.js"(){"use strict";init_codex_provider();init_model();init_model_catalog()}});init_runtimePaths();var PLUGIN_HOST_BUILD_ID="025a1d4dd51ebda523c759b5e82c3de635a9016d5680ff189ffc27f60009c405";init_dist();init_plugin_tasks();init_pluginHostPaths();import{randomUUID as randomUUID12}from"node:crypto";import{createServer}from"node:net";import{chmodSync as chmodSync2,rmSync as rmSync13}from"node:fs";init_dist();init_plugin_tasks();init_plugin_pages();import{basename as basename2}from"node:path";var MAX_PLUGIN_DIAGNOSTIC_BYTES=256*1024;function text2(value,maximum=256){if(typeof value!="string")return;let bytes=Buffer.from(value);return bytes.length<=maximum?value:bytes.subarray(0,maximum).toString("utf8").replace(/\uFFFD$/,"")}function normalizeDiagnosticOptions(value){if(!value||typeof value!="object"||Array.isArray(value))throw new Error("Invalid diagnostic options");let input=value;if(Object.keys(input).some(key2=>!["taskId","plugin","instanceId","includeErrorMessages","includeLocalPaths"].includes(key2)))throw new Error("Unknown diagnostic option");if(input.taskId!==void 0&&!isPluginTaskId(input.taskId))throw new Error("Invalid diagnostic task ID");if(input.instanceId!==void 0&&!isPluginPageInstanceId(input.instanceId))throw new Error("Invalid diagnostic instance ID");if(input.plugin!==void 0&&(typeof input.plugin!="string"||!/^[a-z][a-z0-9-]*$/.test(input.plugin)||input.plugin.length>128))throw new Error("Invalid diagnostic plugin");for(let key2 of["includeErrorMessages","includeLocalPaths"])if(input[key2]!==void 0&&typeof input[key2]!="boolean")throw new Error("Invalid diagnostic content option");return{...input.taskId?{taskId:String(input.taskId)}:{},...input.plugin?{plugin:String(input.plugin)}:{},...input.instanceId?{instanceId:String(input.instanceId)}:{},...input.includeErrorMessages!==void 0?{includeErrorMessages:input.includeErrorMessages}:{},...input.includeLocalPaths!==void 0?{includeLocalPaths:input.includeLocalPaths}:{}}}function diagnosticFailure(value,options){if(!value)return;let message=options.includeErrorMessages?text2(value.message,4096):void 0;return{code:text2(value.code)??"PLUGIN_ERROR_UNCLASSIFIED",...message!==void 0?{message,...Buffer.byteLength(value.message??"")>4096?{messageTruncated:!0}:{}}:{}}}function normalizeModelDiagnostic(value,previous={}){let result={...previous};if(!value||typeof value!="object"||Array.isArray(value))return result;let input=value;for(let key2 of["requestedModel","selectedModel","activeModel"])typeof input[key2]=="string"&&(result[key2]=text2(input[key2]));["apikey","chatgpt","chatgptAuthTokens","unknown"].includes(input.authentication)&&(result.authentication=input.authentication);for(let key2 of["threadRestrictionsAccepted","turnRestrictionsAccepted"])typeof input[key2]=="boolean"&&(result[key2]=input[key2]);Number.isSafeInteger(input.retryCount)&&input.retryCount>=0&&(result.retryCount=Math.min(input.retryCount,1e6));let runtime=input.runtime;runtime&&["explicit","automatic"].includes(runtime.selection)&&(result.runtime={...typeof runtime.executable=="string"?{executable:text2(runtime.executable,4096)}:{},...typeof runtime.version=="string"?{version:text2(runtime.version)}:{},selection:runtime.selection,...typeof runtime.started=="boolean"?{started:runtime.started}:{}});let config=input.configuration;return config&&["default","environment"].includes(config.source)&&typeof config.filePresent=="boolean"&&(result.configuration={source:config.source,filePresent:config.filePresent,...typeof config.home=="string"?{home:text2(config.home,4096)}:{},...typeof config.baseProvider=="string"?{baseProvider:text2(config.baseProvider)}:{},...["file","keyring","auto","ephemeral","unknown"].includes(config.credentialStore)?{credentialStore:config.credentialStore}:{}}),result}function diagnosticBinding(value){return{instanceId:value.instanceId,browserSessionId:value.target.browserSessionId,tabId:value.target.tabId,frameId:value.target.frameId,documentId:value.target.documentId}}function modelProjection(record2,options){if(record2.task.kind!=="model")return;let request=record2.task.input.request,metadata=normalizeModelDiagnostic(record2.diagnostics?.model,typeof request?.model=="string"?{requestedModel:text2(request.model)}:{});if(!options.includeLocalPaths&&(metadata.runtime?.executable&&(metadata.runtime={...metadata.runtime,executable:basename2(metadata.runtime.executable)}),metadata.configuration)){let{home:_home,...configuration}=metadata.configuration;metadata.configuration=configuration}return metadata}function taskProjection(record2,options,observedAt){let{task,diagnostics,execution,executorOwner:owner}=record2,events=record2.events.filter(event=>event.type!=="progress"),first=record2.events[0]?.sequence??task.sequence+1,alive=owner&&!diagnostics?.executorStoppedAt?isProcessOwnerAlive(owner):!1,stopped=diagnostics?.executorStoppedAt,state=alive?"observed-alive":stopped?"stop-confirmed":diagnostics?.executorAttempted===!1?"never-started":"unknown",routing=execution?.options;return{taskId:task.taskId,plugin:text2(task.plugin),kind:task.kind??"command",command:text2(task.command),state:task.state,source:task.source,host:{hostId:task.host.hostId,epoch:task.host.epoch},installationId:task.installationId,revision:task.revision,...diagnostics?.invocationId?{invocationId:diagnostics.invocationId}:{},createdAt:task.createdAt,updatedAt:task.updatedAt,sequence:task.sequence,...diagnostics?.page?{page:diagnosticBinding({instanceId:diagnostics.page.instanceId,target:diagnostics.page})}:{},...routing?{routing:{...routing.browser?{browser:text2(routing.browser,1024)}:{},...routing.session?{session:text2(routing.session)}:{},...routing.connector?{connector:text2(routing.connector)}:{},...routing.local!==void 0?{local:routing.local}:{},...routing.transport?{transport:routing.transport}:{}}}:{},...execution?.executionTimeoutMs?{executionTimeoutMs:execution.executionTimeoutMs}:{},executor:{state,observedAt,...owner?{pid:owner.pid}:{},...stopped?{stoppedAt:stopped}:{}},...task.error?{failure:diagnosticFailure(task.error,options)}:{},...task.checkpoint?{checkpoint:{sequence:task.checkpoint.sequence,version:task.checkpoint.version,schemaHash:task.checkpoint.schemaHash,updatedAt:task.checkpoint.updatedAt}}:{},...task.result?{result:{bytes:task.result.bytes,chunks:task.result.chunks,sha256:task.result.sha256}}:{},...task.kind==="model"?{model:modelProjection(record2,options)}:{},metadataIncomplete:!diagnostics||!!diagnostics.updatesDropped||task.kind==="model"&&!diagnostics.model?.runtime,history:{firstRetainedSequence:first,gap:first>1,omitted:Math.max(0,events.length-8),events:events.slice(-8).map(event=>({sequence:event.sequence,time:event.time,state:event.state,type:event.type}))}}}function buildDiagnosticReport(host,scope,options,scan,snapshots){let records=scan.records.filter(record2=>(!scope.plugin||record2.task.plugin===scope.plugin)&&(scope.access!=="toolbox"||record2.access!=="public")&&(!options.plugin||record2.task.plugin===options.plugin)&&(!options.instanceId||record2.diagnostics?.page?.instanceId===options.instanceId)),visible=new Map(records.map(record2=>[record2.task.taskId,record2])),selected=records.sort((a,b)=>b.task.createdAt-a.task.createdAt||a.task.taskId.localeCompare(b.task.taskId));if(options.taskId){if(!visible.has(options.taskId))throw Object.assign(new Error("Plugin task was not found"),{code:"PLUGIN_TASK_NOT_FOUND"});let ids=new Set([options.taskId]),pending=[options.taskId];for(;pending.length;){let id=pending.shift(),item=visible.get(id),candidates=[item.task.parentTaskId,item.task.resumedFrom?.taskId,...records.filter(record2=>record2.task.parentTaskId===id||record2.task.resumedFrom?.taskId===id).map(record2=>record2.task.taskId)];for(let candidate of candidates)candidate&&visible.has(candidate)&&!ids.has(candidate)&&(ids.add(candidate),pending.push(candidate))}selected=[...ids].map(id=>visible.get(id))}let generatedAt=Date.now(),tasks=selected.slice(0,128).map(record2=>taskProjection(record2,options,generatedAt)),pages=snapshots.filter(page=>(!scope.plugin||page.plugin===scope.plugin)&&(!options.plugin||page.plugin===options.plugin)&&(!options.instanceId||page.binding.instanceId===options.instanceId)&&(!options.taskId||tasks.some(task=>task.page?.instanceId===page.binding.instanceId)));for(let task of tasks)task.page&&!pages.some(page=>page.binding.instanceId===task.page.instanceId)&&pages.push({plugin:task.plugin,installationId:task.installationId,revision:task.revision,host:task.host,binding:task.page,observation:"historical-binding"});let report={version:1,generatedAt,host:{hostId:host.hostId,epoch:host.epoch},protocolVersion:5,selection:{...options.taskId?{taskId:options.taskId}:{},...options.plugin?{plugin:options.plugin}:{},...options.instanceId?{instanceId:options.instanceId}:{}},coverage:{scanLimited:scan.limited,tasksOmitted:selected.length-tasks.length,pagesOmitted:Math.max(0,pages.length-64),byteLimited:!1},tasks,pages:pages.slice(0,64),links:[]},links=()=>{let retained=new Set(report.tasks.map(task=>task.taskId));report.links=report.tasks.flatMap(task=>{let record2=visible.get(task.taskId);return[["parent",record2.task.parentTaskId],["resume",record2.task.resumedFrom?.taskId]].flatMap(([kind,from])=>from&&retained.has(from)?[{from,to:task.taskId,kind}]:[])})};for(links();Buffer.byteLength(JSON.stringify(report))>MAX_PLUGIN_DIAGNOSTIC_BYTES;)report.coverage.byteLimited=!0,report.pages.length?(report.pages.pop(),report.coverage.pagesOmitted++):(report.tasks.pop(),report.coverage.tasksOmitted++,links());return report}init_pluginModelDefinition();init_pluginRecoveryDefinition();init_pluginExecution();init_pluginExecutor();init_pluginExecutorProtocol();import{createHash as createHash9,randomUUID as randomUUID10}from"node:crypto";import{EventEmitter}from"node:events";import{isAbsolute as isAbsolute6,join as join16,relative as relative5,sep as sep4}from"node:path";import{realpathSync as realpathSync4,statSync as statSync9}from"node:fs";init_dist();init_pluginHostPaths();init_skillManifest();init_pluginRegistry();import{createHash as createHash5,randomUUID as randomUUID4}from"node:crypto";import{existsSync as existsSync4,mkdirSync as mkdirSync4,readFileSync as readFileSync7,readdirSync as readdirSync5,realpathSync as realpathSync2,renameSync as renameSync5,rmSync as rmSync5,statSync as statSync5,writeFileSync as writeFileSync4}from"node:fs";import{basename as basename3,dirname as dirname4,isAbsolute as isAbsolute3,join as join9,relative as relative2,resolve as resolve3,sep as sep2}from"node:path";var MAX_REVISION_BYTES=64*1024*1024;function within(root,file){let path7=relative2(root,file);return path7===""||!path7.startsWith(`..${sep2}`)&&path7!==".."&&!isAbsolute3(path7)}function sourceRoot(entry){let candidate=dirname4(entry);for(let depth=0;depth<6;depth++){if(skillManifestPath(candidate))return candidate;let parent=dirname4(candidate);if(parent===candidate)break;candidate=parent}return basename3(dirname4(entry))==="scripts"?dirname4(dirname4(entry)):dirname4(entry)}function readSources(root){let files=[],registry=realpathSync2(pluginHostPaths().registry),bytes=0,visit=(directory,ancestors)=>{let real=realpathSync2(directory);if(!within(root,real)||ancestors.has(real))throw new Error("Plugin snapshot has an external or cyclic directory link");let next=new Set([...ancestors,real]);for(let item of readdirSync5(directory,{withFileTypes:!0}).sort((a,b)=>a.name<b.name?-1:a.name>b.name?1:0)){if([".git","__pycache__","node_modules",".DS_Store"].includes(item.name))continue;let file=join9(directory,item.name);if(within(root,registry)&&within(registry,file))continue;let target=realpathSync2(file);if(!within(root,target))throw new Error(`Plugin snapshot resource leaves the Skill directory: ${file}`);let stat2=statSync5(file);if(stat2.isDirectory()){visit(file,next);continue}if(!stat2.isFile())throw new Error(`Unsupported plugin resource: ${file}`);if(bytes+=stat2.size,bytes>MAX_REVISION_BYTES||files.length>=1e4)throw new Error("Plugin source snapshot exceeds 64 MiB or 10000 files");let content=readFileSync7(file);if(bytes+=content.length-stat2.size,bytes>MAX_REVISION_BYTES)throw new Error("Plugin source snapshot exceeds 64 MiB");files.push({path:relative2(root,file).split(sep2).join("/"),hash:createHash5("sha256").update(content).digest("hex"),bytes:content,executable:(stat2.mode&73)!==0})}};return visit(root,new Set),files}function fingerprint(files,entry){return createHash5("sha256").update(JSON.stringify({entry,files:files.map(file=>[file.path,file.hash,file.executable])})).digest("hex")}function capturePluginRevision(plugin){let registration=findPluginRegistration(plugin);if(!registration)throw new PluginError("PLUGIN_NOT_REGISTERED",`插件未注册: ${plugin}`);let entry=realpathSync2(registration.entry),root=realpathSync2(sourceRoot(entry)),paths=pluginHostPaths();mkdirSync4(paths.installations,{recursive:!0,mode:448});let identityFile=join9(paths.installations,`${plugin}.json`),installation=readRecord(identityFile);if(!installation||installation.root!==root){let proposed={pid:process.pid,root,installationId:randomUUID4()};installation?writeRecord(identityFile,proposed):claimRecord(identityFile,proposed),installation=readRecord(identityFile)}if(!installation||installation.root!==root)throw new Error("Plugin installation changed during capture");let relativeEntry=relative2(root,entry).split(sep2).join("/"),files=readSources(root),revision=fingerprint(files,relativeEntry),revisionRoot=join9(paths.revisions,installation.installationId,revision);if(!existsSync4(revisionRoot)){let temporary=`${revisionRoot}.${randomUUID4()}.tmp`;try{mkdirSync4(temporary,{recursive:!0,mode:448});for(let file of files){let destination=resolve3(temporary,file.path);mkdirSync4(dirname4(destination),{recursive:!0,mode:448}),writeFileSync4(destination,file.bytes,{flag:"wx",mode:file.executable?365:292})}if(fingerprint(readSources(root),relativeEntry)!==revision)throw new PluginError("PLUGIN_SOURCE_CHANGED","插件源码在创建快照时发生变化");mkdirSync4(dirname4(revisionRoot),{recursive:!0,mode:448}),renameSync5(temporary,revisionRoot)}finally{rmSync5(temporary,{recursive:!0,force:!0})}}return{plugin,installationId:installation.installationId,revision,originalRoot:root,entry:join9(revisionRoot,relativeEntry),registration:{...registration,entry:join9(revisionRoot,relativeEntry),source:void 0}}}function prunePluginRevisions(references){let directory=pluginHostPaths().revisions;if(existsSync4(directory))for(let installation of readdirSync5(directory,{withFileTypes:!0})){if(!installation.isDirectory())continue;let root=join9(directory,installation.name);for(let revision of readdirSync5(root,{withFileTypes:!0}))revision.isDirectory()&&!references.has(`${installation.name}/${revision.name}`)&&rmSync5(join9(root,revision.name),{recursive:!0,force:!0});readdirSync5(root).length||rmSync5(root,{recursive:!0,force:!0})}}function restorePluginRevision(plugin,installationId,revision,entry){if(!/^[a-f0-9-]{36}$/.test(installationId)||!/^[a-f0-9]{64}$/.test(revision))throw new Error("Invalid recovery revision");let paths=pluginHostPaths(),registration=findPluginRegistration(plugin),installation=readRecord(join9(paths.installations,`${plugin}.json`));if(!registration||!installation||installation.installationId!==installationId||sourceRoot(realpathSync2(registration.entry))!==installation.root)throw new PluginError("PLUGIN_TASK_INSTALLATION_CHANGED","Task installation was removed or replaced");try{let root=realpathSync2(join9(paths.revisions,installationId,revision)),restoredEntry=resolve3(root,entry);if(!entry||isAbsolute3(entry)||!within(root,restoredEntry)||!within(root,realpathSync2(restoredEntry))||fingerprint(readSources(root),entry)!==revision)throw new Error("Snapshot identity changed");return{plugin,installationId,revision,originalRoot:installation.root,entry:restoredEntry,registration:{...registration,entry:restoredEntry,source:void 0}}}catch{throw new PluginError("PLUGIN_TASK_REVISION_UNAVAILABLE","Task source snapshot is missing or has changed")}}init_pluginHostPaths();init_pluginTaskStore();init_plugin_tasks();init_unified();init_commands();function error(code,message){return Object.assign(new Error(message),{code})}function digest(value){return createHash9("sha256").update(JSON.stringify(value,(_key,item)=>item&&typeof item=="object"&&!Array.isArray(item)?Object.fromEntries(Object.entries(item).sort(([a],[b])=>a<b?-1:a>b?1:0)):item)).digest("hex")}function revisionEntry(revision){return relative5(realpathSync4(join16(pluginHostPaths().revisions,revision.installationId,revision.revision)),realpathSync4(revision.entry)).split(sep4).join("/")}var PluginTaskManager=class{constructor(configuration={},services){this.configuration=configuration;this.services=services;this.store=configuration.store??new PluginTaskStore,this.store.recover(this.host.epoch),this.changes.setMaxListeners(0);let concurrency=configuration.concurrency??4;if(!Number.isInteger(configuration.modelConcurrency??2)||(configuration.modelConcurrency??2)<1||(configuration.modelConcurrency??2)>8)throw new Error("Invalid model concurrency");if(!Number.isInteger(concurrency)||concurrency<1||concurrency>32)throw new Error("Invalid plugin task concurrency");let prune=()=>{this.store.prune(),prunePluginRevisions(new Set([...this.revisionPins.keys(),...this.store.list().map(record2=>`${record2.task.installationId}/${record2.task.revision}`)]))};prune(),this.maintenance=setInterval(()=>{try{prune()}catch(error2){console.error("Plugin task retention failed:",error2)}},3600*1e3),this.maintenance.unref()}host=localPluginExecutionHost();store;changes=new EventEmitter;queue=new Map;running=new Map;resuming=new Map;activeChildren=new Map;closed=!1;maintenance;descriptionsRunning=0;descriptions=new Map;descriptionBytes=0;revisionPins=new Map;recoveries=new Map;pinRevision(revision){let key2=`${revision.installationId}/${revision.revision}`;this.revisionPins.set(key2,(this.revisionPins.get(key2)??0)+1);let released=!1;return()=>{if(released)return;released=!0;let count=this.revisionPins.get(key2)??0;count>1?this.revisionPins.set(key2,count-1):this.revisionPins.delete(key2)}}async describeRevision(revision,signal,options={}){if(signal.throwIfAborted(),this.closed)throw new Error("Plugin description budget is exhausted");let key2=`${revision.installationId}/${revision.revision}`,cached=options.cache?this.descriptions.get(key2):void 0;if(cached)return this.descriptions.delete(key2),this.descriptions.set(key2,cached),structuredClone(cached.value);if(this.descriptionsRunning>=8)throw new Error("Plugin description budget is exhausted");this.descriptionsRunning++;try{let value=await(this.configuration.execute??executeRegisteredPlugin)(revision.plugin,{command:"describe",input:{}},{invoke:async()=>{throw new Error("Plugin declarations cannot invoke host actions")}},{describe:!0,signal,source:"runtime",executionTimeoutMs:1e4,workerPath:this.configuration.workerPath},{registration:revision.registration});signal.throwIfAborted();let bytes=Buffer.byteLength(JSON.stringify(value));if(options.cache&&!this.closed&&bytes<=8*1024*1024){let previous=this.descriptions.get(key2);for(previous&&(this.descriptions.delete(key2),this.descriptionBytes-=previous.bytes);this.descriptions.size>=64||this.descriptionBytes+bytes>8*1024*1024;){let oldest=this.descriptions.keys().next().value;this.descriptionBytes-=this.descriptions.get(oldest).bytes,this.descriptions.delete(oldest)}this.descriptions.set(key2,{value:structuredClone(value),bytes}),this.descriptionBytes+=bytes}return value}finally{this.descriptionsRunning--}}authorize(taskId,scope){if(!taskId.startsWith(`pt_${this.host.hostId}_`))throw error("PLUGIN_TASK_WRONG_HOST","Plugin task belongs to another execution host");let record2=this.store.read(taskId);if(scope.plugin&&record2.task.plugin!==scope.plugin||scope.access==="toolbox"&&record2.access==="public")throw error("PLUGIN_TASK_NOT_FOUND","Plugin task was not found");return record2.task}get(taskId,scope){return this.authorize(taskId,scope)}list(scope,activeOnly=!1,cursor,limit=100){if(!Number.isSafeInteger(limit)||limit<1||limit>200||cursor!==void 0&&typeof cursor!="string")throw new Error("Invalid plugin task list page");let candidates=(activeOnly?this.store.listActive():this.store.list()).filter(record2=>scope.access!=="toolbox"||record2.access!=="public").map(record2=>record2.task).filter(task=>(!scope.plugin||task.plugin===scope.plugin)&&(!activeOnly||isPluginTaskActive(task.state))).sort((a,b)=>b.createdAt-a.createdAt||b.taskId.localeCompare(a.taskId)),match=cursor?.match(/^(\d+):(pt_[0-9a-f-]{36}_[0-9a-f-]{36})$/i);if(cursor!==void 0&&(!match||!Number.isSafeInteger(Number(match[1]))))throw new Error("Invalid plugin task list cursor");let remaining=match?candidates.filter(task=>task.createdAt<Number(match[1])||task.createdAt===Number(match[1])&&task.taskId.localeCompare(match[2])<0):candidates,tasks=[],bytes=0;for(let task of remaining){let size=Buffer.byteLength(JSON.stringify(task));if(tasks.length&&(tasks.length>=limit||bytes+size>384*1024))break;tasks.push(task),bytes+=size}let last=tasks.at(-1);return{tasks,...last&&tasks.length<remaining.length?{nextCursor:`${last.createdAt}:${last.taskId}`}:{}}}start(request,bridge,binding=void 0){if(this.closed)throw error("PLUGIN_TASK_HOST_STOPPING","Plugin execution service is stopping");if(request.kind!==void 0&&!["command","model"].includes(request.kind))throw new Error("Invalid task kind");if(request.kind==="model"){let model=normalizePluginModel(request.input?.request);request={...request,command:"model",input:{request:model},executionTimeoutMs:request.executionTimeoutMs??12e4}}if(!/^[a-z][a-z0-9-]*$/.test(request.plugin)||!/^[a-z][a-z0-9-]*$/.test(request.command))throw new Error("Invalid plugin task command");if(bridge.scope.plugin&&request.plugin!==bridge.scope.plugin)throw error("PLUGIN_COMMAND_NOT_ALLOWED","Plugin task is outside the caller scope");if(!request.input||typeof request.input!="object"||Array.isArray(request.input)||Buffer.byteLength(JSON.stringify(request.input))>256*1024)throw new Error("Plugin task input must be an object within 256 KiB");if(!isAbsolute6(request.cwd)||!statSync9(request.cwd).isDirectory())throw new Error("Plugin task cwd must be an absolute directory");if(!request.environment||typeof request.environment!="object"||Array.isArray(request.environment)||Object.entries(request.environment).some(([key2,value])=>!key2||key2.includes("=")||typeof value!="string"||key2.includes("\0")||value.includes("\0"))||Buffer.byteLength(JSON.stringify(request.environment))>512*1024)throw new Error("Invalid plugin task environment");if(!request.options||typeof request.options!="object"||Array.isArray(request.options)||Object.keys(request.options).some(key2=>!["browser","session","connector","local","transport","timeoutSec"].includes(key2)))throw new Error("Invalid plugin task routing options");for(let key2 of["browser","session","connector"])if(request.options[key2]!==void 0&&(typeof request.options[key2]!="string"||!request.options[key2]))throw new Error("Invalid plugin task target");if(request.options.timeoutSec!==void 0&&(!Number.isFinite(request.options.timeoutSec)||request.options.timeoutSec<=0))throw new Error("Invalid plugin task invocation timeout");if(request.options.local!==void 0&&typeof request.options.local!="boolean")throw new Error("Invalid plugin task local routing option");if(request.options.transport!==void 0&&!["extension","cdp"].includes(request.options.transport))throw new Error("Invalid plugin task transport");if(bridge.scope.bridge==="service"&&request.options.browser&&!decodeBrowserTarget(request.options.browser))throw error("PLUGIN_TASK_TARGET_REQUIRED","Background tasks require a stable global browser ID");if(request.executionTimeoutMs!==void 0&&(!Number.isSafeInteger(request.executionTimeoutMs)||request.executionTimeoutMs<1||request.executionTimeoutMs>2147483647))throw new Error("Invalid plugin task execution deadline");if(request.idempotencyKey!==void 0&&(typeof request.idempotencyKey!="string"||!request.idempotencyKey||request.idempotencyKey.length>128))throw new Error("Invalid plugin task idempotency key");let revision=binding?.revision??(this.configuration.captureRevision??capturePluginRevision)(request.plugin);if(bridge.scope.access==="runtime"&&!binding)throw new Error("Runtime task requires a document binding");if(request.kind!=="model"&&bridge.scope.access==="toolbox"&&!revision.registration.toolbox?.page.commands.includes(request.command))throw error("PLUGIN_COMMAND_NOT_ALLOWED","Tool page cannot invoke this plugin command");request={...request,cwd:realpathSync4(request.cwd),input:JSON.parse(JSON.stringify(request.input))};let requestHash=binding?.recoveryRequestHash??digest([request.kind??"command",request.command,request.input,request.cwd,request.options,request.executionTimeoutMs,revision.revision,binding?.recovery,Object.entries(request.environment).sort(([a],[b])=>a<b?-1:a>b?1:0)]),dedupeKey=binding?.recovery?`resume:${binding.recovery.fromTaskId}`:request.idempotencyKey?`explicit:${revision.installationId}:${bridge.scope.access}:${request.idempotencyKey}`:bridge.scope.access==="toolbox"?`active:${digest(bridge.scope.scopeId??bridge.id)}:${revision.installationId}:${requestHash}`:void 0;if(dedupeKey){let existing=this.store.findDedupe(dedupeKey,request.idempotencyKey===void 0&&binding?.recovery===void 0);if(existing){if(existing.requestHash!==requestHash)throw error("PLUGIN_TASK_IDEMPOTENCY_CONFLICT","Idempotency key refers to a different plugin task request");return{taskId:existing.task.taskId,state:existing.task.state,host:existing.task.host,reused:!0}}}if([...this.queue.values()].filter(item=>(item.request.kind??"command")===(request.kind??"command")).length>=128)throw error("PLUGIN_TASK_QUEUE_FULL","Plugin task queue is full");let now=Date.now(),task={taskId:`pt_${this.host.hostId}_${randomUUID10()}`,host:this.host,plugin:request.plugin,installationId:revision.installationId,revision:revision.revision,command:request.command,kind:request.kind??"command",...binding?.parentTaskId?{parentTaskId:binding.parentTaskId}:{},...binding?.recovery?{resumedFrom:{taskId:binding.recovery.fromTaskId,revision:binding.recovery.fromRevision,checkpointSequence:binding.recovery.sourceCheckpoint.sequence,checkpointVersion:binding.recovery.sourceCheckpoint.version},checkpoint:{sequence:0,version:binding.recovery.checkpoint.version,schemaHash:binding.recovery.checkpoint.schemaHash,updatedAt:now}}:{},input:request.input,source:bridge.scope.source,cwd:request.cwd,state:"queued",sequence:0,createdAt:now,updatedAt:now};if(this.store.write({task,events:[],diagnostics:{executorAttempted:!1,...binding?.page?{page:diagnosticBinding({instanceId:binding.page.instanceId,target:binding.page.target})}:{}},access:bridge.scope.access,...dedupeKey?{dedupeKey}:{},requestHash,execution:{entry:revisionEntry(revision),options:JSON.parse(JSON.stringify(request.options)),bridge:bridge.scope.bridge,executionTimeoutMs:request.executionTimeoutMs},...binding?.recovery?{checkpoint:{...structuredClone(binding.recovery.checkpoint),sequence:0,updatedAt:now}}:{}}),this.update(task.taskId,{state:"queued"}),task.parentTaskId){let children=this.activeChildren.get(task.parentTaskId)??new Set;children.add(task.taskId),this.activeChildren.set(task.parentTaskId,children)}return this.queue.set(task.taskId,{request,revision,bridge,controller:new AbortController,guard:binding?.guard,page:binding?.page,recovery:binding?.recovery,parentIdentity:binding?.parentIdentity,documentTarget:binding?.documentTarget}),queueMicrotask(()=>this.drain()),{taskId:task.taskId,state:"queued",host:this.host}}checkpoint(taskId,scope){return this.authorize(taskId,scope),this.store.read(taskId).checkpoint??null}resume(request,bridge){try{if(this.closed)throw error("PLUGIN_TASK_HOST_STOPPING","Plugin execution service is stopping");if(this.authorize(request.taskId,bridge.scope),bridge.scope.access!=="public"||bridge.scope.bridge!=="service")throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Recovery requires public service-owned routing");if(request.revision!==void 0&&!["original","current"].includes(request.revision))throw new Error("Recovery revision must be original or current");if(!request.environment||typeof request.environment!="object"||Array.isArray(request.environment)||Object.entries(request.environment).some(([key2,value])=>!key2||key2.includes("=")||typeof value!="string"||key2.includes("\0")||value.includes("\0"))||Buffer.byteLength(JSON.stringify(request.environment))>512*1024)throw new Error("Invalid plugin task environment");let intent=digest([request.revision??"original",request.environment]),existing=this.recoveries.get(request.taskId);if(existing){if(existing.intent!==intent)throw error("PLUGIN_TASK_IDEMPOTENCY_CONFLICT","A different recovery request is already in progress");return existing.promise.then(result=>({...result,reused:!0}))}let controller=new AbortController,operation={controller,intent,promise:this.resumeAttempt(request,bridge,controller.signal)};return this.recoveries.set(request.taskId,operation),operation.promise.finally(()=>this.recoveries.delete(request.taskId)).catch(()=>{}),operation.promise}catch(cause){return Promise.reject(cause)}}async resumeAttempt(request,bridge,signal){let task=this.authorize(request.taskId,bridge.scope),record2=this.store.read(task.taskId);if(!["error","cancelled","interrupted"].includes(task.state)||task.kind==="model"||task.parentTaskId||record2.access!=="public"||record2.execution?.bridge!=="service"||!record2.checkpoint)throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Recovery requires a terminal root command with a checkpoint and service-owned routing");this.assertExecutionTreeStopped(task.taskId);let source=restorePluginRevision(task.plugin,task.installationId,task.revision,record2.execution.entry),revision=request.revision==="current"?(this.configuration.captureRevision??capturePluginRevision)(task.plugin):source;if(revision.installationId!==task.installationId)throw error("PLUGIN_TASK_INSTALLATION_CHANGED","Recovery cannot change the plugin installation");restorePluginRevision(task.plugin,task.installationId,revision.revision,revisionEntry(revision));let releaseSource=this.pinRevision(source),releaseTarget=this.pinRevision(revision);try{let requestHash=digest([request.revision??"original",task,record2.checkpoint,record2.execution,revision.revision,request.environment]),existing=this.store.findDedupe(`resume:${task.taskId}`);if(existing){if(existing.requestHash!==requestHash)throw error("PLUGIN_TASK_IDEMPOTENCY_CONFLICT","Source task already has a different recovery attempt");return{taskId:existing.task.taskId,state:existing.task.state,host:existing.task.host,reused:!0}}let sourceCommands=(await this.describeRevision(source,signal)).taskRecovery?.commands,sourceDefinition=sourceCommands&&Object.hasOwn(sourceCommands,task.command)?sourceCommands[task.command]:void 0;if(signal.throwIfAborted(),!sourceDefinition||sourceDefinition.checkpointVersion!==record2.checkpoint.version||digest(sourceDefinition.schema)!==record2.checkpoint.schemaHash)throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Checkpoint does not match the retained recovery declaration");let checkpoint=structuredClone(record2.checkpoint),declarationHash=pluginRecoverySignature(sourceDefinition);if(revision.revision!==source.revision){let targetCommands=(await this.describeRevision(revision,signal)).taskRecovery?.commands,target=targetCommands&&Object.hasOwn(targetCommands,task.command)?targetCommands[task.command]:void 0;if(signal.throwIfAborted(),!target)throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Target revision does not declare recovery for the original command");if(declarationHash=pluginRecoverySignature(target),target.checkpointVersion===checkpoint.version){if(digest(target.schema)!==digest(sourceDefinition.schema))throw error("PLUGIN_CHECKPOINT_MIGRATION_UNSUPPORTED","Checkpoint schema changed without a version increment")}else{if(!target.migrateFrom?.some(from=>from.version===checkpoint.version&&digest(from.schema)===digest(sourceDefinition.schema)))throw error("PLUGIN_CHECKPOINT_MIGRATION_UNSUPPORTED","Target revision does not declare this checkpoint format");if(this.descriptionsRunning>=8)throw error("PLUGIN_CHECKPOINT_MIGRATION_BUSY","Checkpoint migration budget is exhausted");this.descriptionsRunning++;let result;try{result=await(this.configuration.execute??executeRegisteredPlugin)(revision.plugin,{command:task.command,input:{checkpoint,fromRevision:source.revision,toRevision:revision.revision}},{invoke:async()=>{throw error("PLUGIN_CHECKPOINT_MIGRATION_HOST_CALL","Checkpoint migration cannot invoke host actions")}},{migration:!0,expectedRecovery:declarationHash,signal,source:"runtime",cwd:task.cwd,environment:request.environment,executionTimeoutMs:1e4,workerPath:this.configuration.workerPath},{registration:revision.registration})}finally{this.descriptionsRunning--}signal.throwIfAborted();let json2=JSON.stringify(result);if(json2===void 0||Buffer.byteLength(json2)>64*1024)throw error("PLUGIN_TASK_CHECKPOINT_INVALID","Migrated checkpoint must be JSON within 64 KiB");checkpoint={...checkpoint,version:target.checkpointVersion,schemaHash:digest(target.schema),data:JSON.parse(json2)}}}return signal.throwIfAborted(),restorePluginRevision(task.plugin,task.installationId,task.revision,record2.execution.entry),restorePluginRevision(task.plugin,task.installationId,revision.revision,revisionEntry(revision)),this.assertExecutionTreeStopped(task.taskId),this.start({plugin:task.plugin,command:task.command,input:task.input,cwd:task.cwd,environment:request.environment,options:record2.execution.options,executionTimeoutMs:record2.execution.executionTimeoutMs},bridge,{revision,recoveryRequestHash:requestHash,recovery:{fromTaskId:task.taskId,fromRevision:source.revision,declarationHash,sourceCheckpoint:{sequence:record2.checkpoint.sequence,version:record2.checkpoint.version,schemaHash:record2.checkpoint.schemaHash,updatedAt:record2.checkpoint.updatedAt},checkpoint}})}finally{releaseTarget(),releaseSource()}}assertExecutionTreeStopped(taskId){let records=this.store.list(),visited=new Set,pending=[taskId];for(;pending.length;){let id=pending.pop();if(visited.has(id))continue;visited.add(id);let record2=records.find(record3=>record3.task.taskId===id);if(!record2)throw error("PLUGIN_TASK_EXECUTION_STILL_ACTIVE","Previous execution ownership record is unavailable");if(pending.push(...records.filter(record3=>record3.task.parentTaskId===id).map(record3=>record3.task.taskId)),isPluginTaskActive(record2.task.state)||pluginTaskExecutionUnconfirmed(record2))throw error("PLUGIN_TASK_EXECUTION_STILL_ACTIVE","Previous execution or descendant cleanup cannot be confirmed")}}cancel(taskId,scope){let task=this.authorize(taskId,scope);if(task.state==="queued")return this.queue.delete(taskId),this.update(taskId,{state:"cancelled",error:{code:"PLUGIN_EXECUTION_CANCELLED",message:"Plugin task cancelled before execution"}});if(task.state==="running"){let updated=this.update(taskId,{state:"cancelling"});return this.running.get(taskId)?.controller.abort(),updated}return task}async events(taskId,scope,cursor,waitMs=0,signal){let task=this.authorize(taskId,scope);if(!Number.isSafeInteger(waitMs)||waitMs<0||waitMs>25e3)throw new Error("Invalid plugin task event wait");let current=this.store.events(taskId,cursor);return waitMs&&cursor===task.sequence&&isPluginTaskActive(task.state)?(await new Promise((resolve7,reject)=>{let finish=()=>{cleanup(),resolve7()},abort=()=>{cleanup(),reject(signal?.reason??new Error("Plugin task subscription closed"))},timer=setTimeout(finish,waitMs),cleanup=()=>{clearTimeout(timer),this.changes.off(taskId,finish),signal?.removeEventListener("abort",abort)};this.changes.once(taskId,finish),signal?.addEventListener("abort",abort,{once:!0}),signal?.aborted?abort():this.store.read(taskId).task.sequence!==cursor&&finish()}),this.store.events(taskId,cursor)):current}resultChunk(taskId,scope,index){return this.authorize(taskId,scope),this.store.resultChunk(taskId,index)}update(taskId,update,type="state"){let task=this.store.update(taskId,update,type);if(!isPluginTaskActive(task.state)&&task.parentTaskId){let children=this.activeChildren.get(task.parentTaskId);children?.delete(taskId),children?.size||this.activeChildren.delete(task.parentTaskId)}return this.changes.emit(taskId),task}drain(){if(!this.closed){for(let[taskId,resume]of this.resuming){let parent=this.running.get(taskId);if(!parent){this.resuming.delete(taskId),resume();continue}let limit=parent.kind==="model"?this.configuration.modelConcurrency??2:this.configuration.concurrency??4;[...this.running.values()].filter(item=>item.kind===parent.kind&&!item.waiting).length<limit&&(this.resuming.delete(taskId),resume())}for(let[taskId,pending]of this.queue){let kind=pending.request.kind??"command",limit=kind==="model"?this.configuration.modelConcurrency??2:this.configuration.concurrency??4;if([...this.running.values()].filter(item=>item.kind===kind&&!item.waiting).length>=limit)continue;this.queue.delete(taskId),this.update(taskId,{state:"running"});let completed=this.execute(taskId,pending).finally(()=>{this.running.delete(taskId),this.drain()});this.running.set(taskId,{controller:pending.controller,completed,kind,waiting:0})}}}async execute(taskId,{request,revision,bridge,controller,guard,page,recovery,parentIdentity,documentTarget}){let client=bridge.scope.bridge==="service"?new UnifiedClient:void 0;try{await guard?.();let identity=Object.freeze({...createPluginInvocationIdentity(request.plugin,bridge.scope.source,parentIdentity),taskId,installationId:revision.installationId,revision:revision.revision,...page?{page}:{},...recovery?{recovery}:{}});{let record2=this.store.read(taskId);record2.diagnostics={...record2.diagnostics,invocationId:identity.invocationId,executorAttempted:!0},this.store.write(record2)}let restrictedTarget=page?.target??documentTarget,diagnosticUpdates=0,recoveryDescription,modelOwner=page?.instanceId??parentIdentity?.taskId,conversation=request.kind==="model"&&request.input.request.conversation;if(conversation&&!modelOwner)throw error("PLUGIN_MODEL_CONVERSATION_SCOPE","Model conversations require a bound document or calling command");let result=await(this.configuration.execute??(conversation&&this.services?.model?this.services.model.bind(this.services,modelOwner):executeRegisteredPlugin))(request.plugin,{command:request.command,input:request.input},{invocation:identity,source:bridge.scope.source,invoke:async(action,payload={},options={},onProgress)=>{if(controller.signal.throwIfAborted(),await guard?.(),controller.signal.throwIfAborted(),action==="plugin_invoke_child"){let name=payload.plugin,invocation=payload.invocation;if(typeof name!="string"||!invocation||typeof invocation.command!="string"||!invocation.input||typeof invocation.input!="object"||Array.isArray(invocation.input))throw new Error("Invalid nested plugin invocation");this.assertRouting(options,request.options);let ancestor=taskId,depth=0;for(;ancestor;){let task=this.store.read(ancestor).task;if(task.plugin===name)throw error("PLUGIN_INVOCATION_CYCLE","Plugin invocation forms a cycle");if(++depth>=16)throw error("PLUGIN_INVOCATION_DEPTH","Plugin invocation depth exceeds 16");ancestor=task.parentTaskId}this.assertChildBudget(taskId);let childRevision=(this.configuration.captureRevision??capturePluginRevision)(name),childBridge={...bridge,scope:{...bridge.scope,access:"public",plugin:name}},child=this.start({...request,plugin:name,kind:"command",command:invocation.command,input:invocation.input,options:{...request.options,...options},idempotencyKey:void 0},childBridge,{revision:childRevision,guard,parentTaskId:taskId,parentIdentity:identity,documentTarget:restrictedTarget});return this.awaitChild(taskId,child.taskId,childBridge.scope,controller.signal)}if(["plugin_state","plugin_model","plugin_page","plugin_task_checkpoint"].includes(action)&&payload.plugin!==request.plugin)throw error("PLUGIN_CALLER_SCOPE_MISMATCH","Plugin-owned services require the executing plugin identity");if(action==="plugin_task_checkpoint"){if(request.kind==="model"||page||bridge.scope.access!=="public"||bridge.scope.bridge!=="service")throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Checkpoints require a command with service-owned routing");let json2=JSON.stringify(payload.data);if(!Number.isSafeInteger(payload.expectedSequence)||payload.expectedSequence<0||json2===void 0||Buffer.byteLength(json2)>64*1024)throw new Error("Checkpoint must have a CAS sequence and fit within 64 KiB");let data=JSON.parse(json2),commands=(await(recoveryDescription??=this.describeRevision(revision,controller.signal))).taskRecovery?.commands,declaration=commands&&Object.hasOwn(commands,request.command)?commands[request.command]:void 0;if(controller.signal.throwIfAborted(),declaration&&payload.expectedDeclaration!==pluginRecoverySignature(declaration))throw error("PLUGIN_RECOVERY_DECLARATION_CHANGED","Checkpoint declaration differs from the executing command");if(!declaration)throw error("PLUGIN_TASK_CHECKPOINT_INVALID","Checkpoint is undeclared");let current=this.store.read(taskId);if(current.task.state!=="running"||payload.expectedSequence!==(current.checkpoint?.sequence??0))throw error("PLUGIN_TASK_CHECKPOINT_CONFLICT","Checkpoint sequence or execution state changed");let checkpoint={data,version:declaration.checkpointVersion,schemaHash:digest(declaration.schema),sequence:payload.expectedSequence+1,updatedAt:Date.now()};return this.store.update(taskId,{checkpoint:{sequence:checkpoint.sequence,version:checkpoint.version,schemaHash:checkpoint.schemaHash,updatedAt:checkpoint.updatedAt}},"checkpoint",{checkpoint}),this.changes.emit(taskId),checkpoint}if(action==="plugin_state"){if(page&&payload.instanceId!==void 0&&payload.instanceId!==page.instanceId)throw error("PLUGIN_PAGE_TARGET_CHANGED","State access cannot change the bound page");if(!this.services)throw new Error("Plugin host services are unavailable");return this.services.state(request.plugin,{...payload,...page?{instanceId:page.instanceId}:{}},bridge,request.options,{revision,signal:controller.signal,expectedTarget:restrictedTarget})}if(action==="plugin_model"){if(payload.operation==="capabilities")return pluginModelCapabilities();if(payload.operation==="list"){if(!this.services)throw new Error("Plugin host services are unavailable");return this.services.listModels(payload.provider)}if(payload.operation!=="run")throw new Error("Plugin commands use model.run; detached model tasks require an explicit task entry");this.assertChildBudget(taskId);let model=normalizePluginModel(payload.request),started=this.start({...request,kind:"model",command:"model",input:{request:model},idempotencyKey:void 0},bridge,{revision,guard,page,parentTaskId:taskId,parentIdentity:identity,documentTarget:restrictedTarget});return this.awaitChild(taskId,started.taskId,bridge.scope,controller.signal,onProgress)}if(action==="plugin_page"){if(!this.services)throw new Error("Plugin host services are unavailable");if(page&&!(payload.operation==="activation"&&payload.enabled===void 0)&&(!["send","get","events"].includes(String(payload.operation))||payload.instanceId!==page.instanceId))throw error("PLUGIN_PAGE_TARGET_CHANGED","Runtime tasks can only access their bound document");return this.services.page(request.plugin,payload,bridge,request.options,{cwd:request.cwd,environment:request.environment,signal:controller.signal,revision,expectedTarget:restrictedTarget,parentIdentity:identity})}if(restrictedTarget&&!["send_request","send_mtop_request","browser_list","connector_list"].includes(action))throw error("PLUGIN_PAGE_TARGET_CHANGED","Runtime tasks must access the document through context.page");if(this.assertRouting(options,request.options),client){if(COMMAND_MAP.has(action)&&!["browser_list","connector_list","agent_chat_run"].includes(action)&&!request.options.browser)throw error("PLUGIN_TASK_TARGET_REQUIRED","Background browser tasks require an explicit browser target");return client.invoke(action,payload,{...request.options,...options})}return bridge.invoke(action,payload,{...request.options,...options})}},{...request.kind==="model"?{model:request.input.request}:{},recovery:recovery!==void 0,onDiagnostic:metadata=>{let record2=this.store.read(taskId);if(++diagnosticUpdates>64){record2.diagnostics?.updatesDropped||(record2.diagnostics={...record2.diagnostics,updatesDropped:!0},this.store.write(record2));return}let model=normalizeModelDiagnostic(metadata,record2.diagnostics?.model);JSON.stringify(model)!==JSON.stringify(record2.diagnostics?.model)&&(this.store.update(taskId,{},"diagnostic",{diagnostics:{...record2.diagnostics,model}}),this.changes.emit(taskId))},onExecutorStopped:()=>{let record2=this.store.read(taskId);record2.diagnostics={...record2.diagnostics,executorStoppedAt:Date.now()},this.store.write(record2)},onExecutorStarted:owner=>{let record2=this.store.read(taskId);record2.executorOwner=owner,this.store.write(record2)},...request.options,cwd:request.cwd,environment:request.environment,signal:controller.signal,source:bridge.scope.source,access:bridge.scope.access,executionTimeoutMs:request.executionTimeoutMs,workerPath:this.configuration.workerPath,onProgress:progress=>{if(Buffer.byteLength(JSON.stringify(progress))>64*1024)throw new Error("Plugin task progress exceeds 64 KiB");this.update(taskId,{progress},"progress")}},{registration:revision.registration});await guard?.(),request.kind==="model"&&(result=validatePluginModelResult(request.input.request,result)),await this.stopChildren(taskId),!page&&request.kind!=="model"&&await this.services?.releaseModels?.(taskId);let reference=this.store.saveResult(taskId,result);this.update(taskId,{state:"success",result:reference})}catch(cause){await this.stopChildren(taskId);let failureCause=cause;if(!page&&request.kind!=="model")try{await this.services?.releaseModels?.(taskId)}catch(cleanupError){failureCause=cleanupError}let failure2=serializePluginExecutionError(failureCause),interrupted=this.closed||failure2.code==="PLUGIN_TASK_BRIDGE_UNAVAILABLE";this.update(taskId,{state:interrupted?"interrupted":failure2.code==="PLUGIN_EXECUTION_CANCELLED"?"cancelled":"error",error:failure2})}finally{client?.disconnect()}}assertRouting(options,bound){if(Object.keys(options).some(key2=>!["browser","connector","session","local","transport","timeoutSec"].includes(key2)))throw error("PLUGIN_TASK_TARGET_CHANGED","Plugin task cannot override internal routing options");for(let key2 of["browser","connector","session","local","transport"])if(options[key2]!==void 0&&options[key2]!==bound[key2])throw error("PLUGIN_TASK_TARGET_CHANGED","Plugin task cannot change its bound browser routing")}assertChildBudget(parent){if(!this.running.get(parent)?.waiting&&[...this.running.values()].filter(item=>item.waiting).length>=32)throw error("PLUGIN_TASK_NESTING_BUDGET","Too many suspended plugin invocations")}async awaitChild(parent,child,scope,signal,onProgress){let running=this.running.get(parent);running&&running.waiting++,queueMicrotask(()=>this.drain());let cancel=()=>this.cancel(child,scope);signal.addEventListener("abort",cancel,{once:!0}),signal.aborted&&cancel();try{return await this.waitResult(child,scope,progress=>{progress!==void 0&&!signal.aborted&&onProgress?.(progress),isPluginTaskActive(this.store.read(parent).task.state)&&this.update(parent,{progress},"progress")})}finally{signal.removeEventListener("abort",cancel),running?.waiting===1&&!signal.aborted&&!this.closed?await new Promise(resolve7=>{let resume=()=>{this.resuming.delete(parent),signal.removeEventListener("abort",resume),running.waiting--,resolve7()};this.resuming.set(parent,resume),signal.addEventListener("abort",resume,{once:!0}),this.drain()}):running&&running.waiting--}}async stopChildren(taskId){let children=[...this.activeChildren.get(taskId)??[]];for(let child of children)this.cancel(child,{access:"public",source:"runtime",bridge:"service"});await Promise.allSettled(children.map(child=>this.running.get(child)?.completed))}async waitResult(taskId,scope,onProgress){let cursor=0;for(;;){let page=await this.events(taskId,scope,cursor,25e3);cursor=page.nextCursor;for(let event of page.events)event.type==="progress"&&onProgress?.(event.progress);if(page.task.state==="success"){let chunks=[];for(let index=0;index<page.task.result.chunks;index++)chunks.push(Buffer.from(this.resultChunk(taskId,scope,index).data,"base64"));let bytes=Buffer.concat(chunks);if(bytes.length!==page.task.result.bytes||createHash9("sha256").update(bytes).digest("hex")!==page.task.result.sha256)throw new Error("Model task result integrity check failed");return JSON.parse(bytes.toString("utf8")).value}if(!isPluginTaskActive(page.task.state))throw Object.assign(pluginExecutionError(page.task.error??{message:"Model task failed"}),{childTaskId:taskId})}}async close(){this.closed=!0,this.descriptions.clear(),this.descriptionBytes=0;for(let resume of[...this.resuming.values()])resume();for(let operation of this.recoveries.values())operation.controller.abort();clearInterval(this.maintenance),await Promise.allSettled([...this.recoveries.values()].map(operation=>operation.promise));for(let taskId of this.queue.keys())this.update(taskId,{state:"interrupted",error:{code:"PLUGIN_TASK_HOST_STOPPED",message:"Plugin execution service stopped"}});this.queue.clear();for(let{controller}of this.running.values())controller.abort();await Promise.allSettled([...[...this.running.values()].map(item=>item.completed)])}};init_pluginStateDefinition();init_pluginRegistry();init_pluginHostPaths();import{readFileSync as readFileSync14}from"node:fs";import{join as join18}from"node:path";init_pluginHostPaths();init_pluginHostFiles();import{createHash as createHash10}from"node:crypto";import{existsSync as existsSync8,readFileSync as readFileSync13,readdirSync as readdirSync7,statSync as statSync10,rmSync as rmSync12}from"node:fs";import{join as join17,sep as sep5}from"node:path";function fail(code,message){throw Object.assign(new Error(message),{code})}function schemaHash(definition){return createHash10("sha256").update(JSON.stringify({schema:definition.schema,storage:definition.storage},(_key,value)=>value&&typeof value=="object"&&!Array.isArray(value)?Object.fromEntries(Object.entries(value).sort(([a],[b])=>a<b?-1:a>b?1:0)):value)).digest("hex")}function key(value){(typeof value!="string"||!value.length||Buffer.byteLength(value)>512||["__proto__","constructor","prototype"].includes(value))&&fail("PLUGIN_STATE_INVALID","Invalid state key")}var PluginStateStore=class{constructor(directory=pluginHostPaths().state,now=Date.now){this.directory=directory;this.now=now}memory=new Map;memoryBytes=0;memoryRevisionFloor=0;clearMemory(){this.memory.clear(),this.memoryBytes=0,this.memoryRevisionFloor=0}valueBytes=new WeakMap;memorySize(document){let bytes=Buffer.byteLength(JSON.stringify({...document,entries:{}})),count=0;for(let[name,entry]of Object.entries(document.entries)){let valueBytes=typeof entry.value=="object"&&entry.value!==null?this.valueBytes.get(entry.value):void 0;valueBytes===void 0&&(valueBytes=Buffer.byteLength(JSON.stringify(entry.value)),typeof entry.value=="object"&&entry.value!==null&&this.valueBytes.set(entry.value,valueBytes)),bytes+=Buffer.byteLength(JSON.stringify(name))+1+Buffer.byteLength(JSON.stringify({...entry,value:null}))-4+valueBytes+(count++?1:0)}return bytes}saveMemory(path7,document,bytes=this.memorySize(document)){let total=this.memoryBytes-(this.memory.get(path7)?.bytes??0)+bytes;(total>128*1024*1024||!this.memory.has(path7)&&this.memory.size>=512)&&fail("PLUGIN_STATE_QUOTA","Memory state exceeds its host limits"),this.memory.set(path7,{document,bytes}),this.memoryBytes=total}path(namespace){(!/^[a-f0-9-]{36}$/i.test(namespace.installationId)||!/^[a-z][a-z0-9-]{0,63}$/.test(namespace.collection))&&fail("PLUGIN_STATE_INVALID","Invalid state namespace");let id=createHash10("sha256").update(namespace.scope).digest("hex");return join17(this.directory,namespace.installationId,`${namespace.collection}-${id}.json`)}read(namespace,definition){try{let file=this.path(namespace),saved=this.memory.get(file);if((definition.storage!=="memory"&&saved||definition.storage==="memory"&&!saved&&existsSync8(file))&&fail("PLUGIN_STATE_STORAGE_CHANGED","Switching between memory and disk state requires a new collection or namespace"),definition.storage==="memory"&&!saved){if(this.memory.size>=512){this.memoryRevisionFloor=Math.max(this.memoryRevisionFloor,...[...this.memory.values()].map(entry=>entry.document.revision))+1;for(let[path7,entry]of this.memory)Object.keys(entry.document.entries).length||(this.memory.delete(path7),this.memoryBytes-=entry.bytes)}throw Object.assign(new Error("Not found"),{code:"ENOENT"})}let document=saved?{...saved.document,entries:{...saved.document.entries}}:JSON.parse(readFileSync13(file,"utf8"));if(!Number.isSafeInteger(document.revision)||!Number.isSafeInteger(document.schemaVersion)||!document.entries||!Array.isArray(document.commits))throw new Error("Invalid state document");if(definition.storage!=="persistent"){let entries=Object.fromEntries(Object.entries(document.entries).filter(([,entry])=>entry.expiresAt===void 0||entry.expiresAt>this.now()));Object.keys(entries).length!==Object.keys(document.entries).length&&(document.entries=entries,document.revision++,definition.storage==="memory"?this.saveMemory(file,document):writePluginHostFile(file,JSON.stringify(document)))}return document}catch(error2){if(error2.code!=="ENOENT")throw error2;return{revision:definition.storage==="memory"?this.memoryRevisionFloor:0,schemaVersion:definition.schemaVersion,schemaHash:schemaHash(definition),storage:definition.storage,scope:namespace.scope,entries:{},commits:[]}}}entry(document,name,now){let entry=Object.hasOwn(document.entries,name)?document.entries[name]:void 0;return entry&&(entry.expiresAt===void 0||entry.expiresAt>now)?{key:name,exists:!0,version:`v:${entry.revision}`,value:document.storage==="memory"?structuredClone(entry.value):entry.value,...entry.expiresAt===void 0?{}:{expiresAt:entry.expiresAt}}:{key:name,exists:!1,version:`a:${document.revision}`}}discardScope(installationId,scope){if(!/^[a-f0-9-]{36}$/i.test(installationId)||!scope.startsWith("instance:pi_"))throw new Error("Only ended instance scopes can be discarded");let directory=join17(this.directory,installationId),memorySuffix=`-${createHash10("sha256").update(scope).digest("hex")}.json`;for(let[path7,entry]of this.memory)path7.startsWith(directory+sep5)&&path7.endsWith(memorySuffix)&&(this.memory.delete(path7),this.memoryBytes-=entry.bytes);let suffix=`-${createHash10("sha256").update(scope).digest("hex")}.json`,names;try{names=readdirSync7(directory)}catch(error2){if(error2.code==="ENOENT")return;throw error2}for(let name of names)name.endsWith(suffix)&&rmSync12(join17(directory,name),{force:!0})}prune(skipFile){for(let[path7,saved]of this.memory){let document={...saved.document,entries:{...saved.document.entries}};for(let[key2,entry]of Object.entries(document.entries))entry.expiresAt!==void 0&&entry.expiresAt<=this.now()&&delete document.entries[key2];Object.keys(document.entries).length!==Object.keys(saved.document.entries).length&&(document.revision++,this.saveMemory(path7,document))}let installations;try{installations=readdirSync7(this.directory)}catch(error2){if(error2.code==="ENOENT")return;throw error2}for(let installation of installations)if(/^[a-f0-9-]{36}$/i.test(installation))for(let name of readdirSync7(join17(this.directory,installation))){if(!name.endsWith(".json"))continue;let file=join17(this.directory,installation,name);if(file!==skipFile)try{let document=JSON.parse(readFileSync13(file,"utf8"));if(document.storage!=="cache")continue;let entries=Object.fromEntries(Object.entries(document.entries).filter(([,value])=>value.expiresAt===void 0||value.expiresAt>this.now()));if(Object.keys(entries).length===Object.keys(document.entries).length)continue;writePluginHostFile(file,JSON.stringify({...document,revision:document.revision+1,entries}))}catch(error2){console.error(`Cannot maintain plugin cache ${file}:`,error2)}}}get(namespace,definition,name){key(name);let document=this.read(namespace,definition);return{...this.entry(document,name,this.now()),revision:document.revision,schemaVersion:document.schemaVersion}}getMany(namespace,definition,names){(!Array.isArray(names)||names.length<1||names.length>128||new Set(names).size!==names.length)&&fail("PLUGIN_STATE_INVALID","Expected 1 to 128 unique state keys"),names.forEach(name=>key(name));let document=this.read(namespace,definition),now=this.now(),entries=names.map(name=>this.entry(document,name,now));return Buffer.byteLength(JSON.stringify(entries))>1024*1024&&fail("PLUGIN_STATE_RESPONSE_TOO_LARGE","State batch exceeds 1 MiB; read fewer keys"),{entries,revision:document.revision,schemaVersion:document.schemaVersion}}list(namespace,definition,input={}){let document=this.read(namespace,definition),limit=input.limit??100;(!Number.isSafeInteger(limit)||limit<1||limit>200||input.cursor!==void 0&&typeof input.cursor!="string")&&fail("PLUGIN_STATE_INVALID","Invalid state list cursor or limit"),input.expectedRevision!==void 0&&input.expectedRevision!==document.revision&&fail("PLUGIN_STATE_CONFLICT","State list changed; restart pagination");let now=this.now(),candidates=Object.keys(document.entries).sort().filter(name=>(input.cursor===void 0||name>input.cursor)&&this.entry(document,name,now).exists),entries=[],bytes=0;for(let name of candidates){let entry=this.entry(document,name,now),size=Buffer.byteLength(JSON.stringify(entry));if(entries.length&&(entries.length>=limit||bytes+size>256*1024))break;entries.push(entry),bytes+=size}return{entries,revision:document.revision,schemaVersion:document.schemaVersion,...entries.length<candidates.length?{nextCursor:entries.at(-1).key}:{}}}transact(namespace,definition,input){(!input||!Array.isArray(input.operations)||input.operations.length>128||Buffer.byteLength(JSON.stringify(input))>512*1024)&&fail("PLUGIN_STATE_INVALID","State transaction exceeds its limits");let document=this.read(namespace,definition),hash=createHash10("sha256").update(JSON.stringify({input,schemaVersion:definition.schemaVersion,schema:definition.schema})).digest("hex");if(input.idempotencyKey!==void 0){(typeof input.idempotencyKey!="string"||!input.idempotencyKey.length||input.idempotencyKey.length>128)&&fail("PLUGIN_STATE_INVALID","Invalid transaction idempotency key");let existing=document.commits.find(item=>item.id===input.idempotencyKey);if(existing)return existing.hash!==hash&&fail("PLUGIN_STATE_IDEMPOTENCY_CONFLICT","Transaction key refers to different data or schema"),{revision:existing.revision,schemaVersion:document.schemaVersion,reused:!0}}input.expectedRevision!==void 0&&input.expectedRevision!==document.revision&&fail("PLUGIN_STATE_CONFLICT","Collection revision changed"),document.schemaVersion===definition.schemaVersion&&document.schemaHash!==schemaHash(definition)&&fail("PLUGIN_STATE_SCHEMA_VERSION_REQUIRED","Changing the state schema or storage policy requires a new schemaVersion"),document.schemaVersion>definition.schemaVersion&&fail("PLUGIN_STATE_SCHEMA_DOWNGRADE","An older plugin revision cannot write a newer state schema"),document.schemaVersion!==definition.schemaVersion&&(input.migrateFromVersion!==document.schemaVersion||input.expectedRevision===void 0)&&fail("PLUGIN_STATE_MIGRATION_REQUIRED","Schema migration requires the previous schema version and collection revision"),input.migrateFromVersion!==void 0&&input.migrateFromVersion!==document.schemaVersion&&fail("PLUGIN_STATE_CONFLICT","State schema changed");let now=this.now(),names=new Set,entries=Object.fromEntries(Object.entries(document.entries).filter(([,value])=>value.expiresAt===void 0||value.expiresAt>now).map(([name,value])=>[name,{...value}])),revision=document.revision+1;if(Number.isSafeInteger(revision)||fail("PLUGIN_STATE_QUOTA","State revision limit reached"),document.storage!==definition.storage)for(let entry of Object.values(entries))entry.revision=revision,definition.storage!=="persistent"?entry.expiresAt=now+(definition.ttlMs??864e5):delete entry.expiresAt;for(let operation of input.operations){if(key(operation.key),(names.has(operation.key)||!["put","delete"].includes(operation.type)||operation.expectedVersion!==this.entry(document,operation.key,now).version)&&fail("PLUGIN_STATE_CONFLICT","State entry version changed or key repeated"),names.add(operation.key),operation.type==="delete"){delete entries[operation.key];continue}let json2=JSON.stringify(operation.value);(json2===void 0||Buffer.byteLength(json2)>256*1024)&&fail("PLUGIN_STATE_INVALID","State value must be JSON within 256 KiB");let ttl=operation.ttlMs??definition.ttlMs??864e5;definition.storage==="persistent"&&operation.ttlMs!==void 0&&fail("PLUGIN_STATE_INVALID","Persistent state does not accept TTL"),(!Number.isSafeInteger(ttl)||ttl<1||ttl>7*864e5)&&fail("PLUGIN_STATE_INVALID","Invalid cache TTL"),entries[operation.key]={revision,value:definition.storage==="memory"?structuredClone(operation.value):operation.value,...definition.storage!=="persistent"?{expiresAt:now+ttl}:{}}}let commit={revision,schemaVersion:definition.schemaVersion,schemaHash:schemaHash(definition),storage:definition.storage,scope:namespace.scope,entries,commits:[...document.commits,...input.idempotencyKey?[{id:input.idempotencyKey,hash,revision}]:[]].slice(-64)},content=definition.storage==="memory"?"":JSON.stringify(commit),bytes=definition.storage==="memory"?this.memorySize(commit):Buffer.byteLength(content);(Object.keys(entries).length>(definition.maxEntries??1024)||bytes>(definition.maxBytes??8*1024*1024))&&fail("PLUGIN_STATE_QUOTA","State collection quota exceeded");let file=this.path(namespace);if(definition.storage==="memory")return this.saveMemory(file,commit,bytes),{revision,schemaVersion:definition.schemaVersion};let total=Buffer.byteLength(content);try{for(let name of readdirSync7(join17(this.directory,namespace.installationId))){let path7=join17(this.directory,namespace.installationId,name);path7!==file&&name.endsWith(".json")&&(total+=statSync10(path7).size)}}catch(error2){if(error2.code!=="ENOENT")throw error2}if(total>64*1024*1024){this.prune(file),total=Buffer.byteLength(content);for(let name of readdirSync7(join17(this.directory,namespace.installationId))){let path7=join17(this.directory,namespace.installationId,name);path7!==file&&name.endsWith(".json")&&(total+=statSync10(path7).size)}}return total>64*1024*1024&&fail("PLUGIN_STATE_QUOTA","Plugin state exceeds 64 MiB"),writePluginHostFile(file,content),{revision,schemaVersion:definition.schemaVersion}}};var PluginStateManager=class{constructor(tasks){this.tasks=tasks;this.store.prune(),this.maintenance=setInterval(()=>{try{this.store.prune()}catch(error2){console.error("Plugin cache maintenance failed:",error2)}},6e4),this.maintenance.unref()}store=new PluginStateStore;descriptions=new Map;pending=new Set;controller=new AbortController;resolveTarget;maintenance;async call(plugin,input,bridge,options={},binding){if(this.controller.signal.throwIfAborted(),binding?.signal?.throwIfAborted(),!/^[a-z][a-z0-9-]*$/.test(plugin)||bridge.scope.plugin&&bridge.scope.plugin!==plugin||!input||typeof input!="object"||Array.isArray(input))throw new Error("Invalid plugin state scope");let revision=binding?.revision??capturePluginRevision(plugin),id=`${revision.installationId}/${revision.revision}`,description=this.descriptions.get(id);if(!description){this.descriptions.size>=64&&this.descriptions.delete(this.descriptions.keys().next().value);let release=this.tasks.pinRevision(revision);description=this.tasks.describeRevision(revision,this.controller.signal,{cache:!0}).finally(release),this.descriptions.set(id,description),this.pending.add(description),description.finally(()=>this.pending.delete(description)).catch(()=>{}),description.catch(()=>{this.descriptions.get(id)===description&&this.descriptions.delete(id)})}let declaration=(await description).stateStorage;this.controller.signal.throwIfAborted(),binding?.signal?.throwIfAborted();let name=String(input.collection??""),definition=declaration&&Object.hasOwn(declaration.collections,name)?declaration.collections[name]:void 0;if(!definition||!/^[a-z][a-z0-9-]{0,63}$/.test(name))throw Object.assign(new Error("Plugin state collection is not declared"),{code:"PLUGIN_STATE_NOT_DECLARED"});if(input.expectedDeclaration!==void 0&&input.expectedDeclaration!==pluginStateSignature(definition))throw Object.assign(new Error("Plugin state declaration changed during command execution"),{code:"PLUGIN_STATE_REVISION_CHANGED"});let scope=definition.scope,assertScope=()=>{};if(definition.scope==="host"&&(scope+=`:${this.tasks.host.hostId}`),definition.scope==="instance"||definition.scope==="browser-session"){if(!this.resolveTarget)throw new Error("Page state scope is unavailable");let resolved=await this.resolveTarget(plugin,input.instanceId,bridge,options),target=resolved.target,expected=binding?.expectedTarget;if(expected&&(target.browserSessionId!==expected.browserSessionId||target.tabId!==expected.tabId||target.frameId!==expected.frameId||target.documentId!==expected.documentId))throw Object.assign(new Error("State access cannot change the inherited document"),{code:"PLUGIN_PAGE_TARGET_CHANGED"});assertScope=resolved.assertActive,scope+=definition.scope==="instance"?`:${String(input.instanceId)}`:`:${target.browserSessionId}`}this.controller.signal.throwIfAborted(),binding?.signal?.throwIfAborted();let installation;try{installation=JSON.parse(readFileSync14(join18(pluginHostPaths().installations,`${plugin}.json`),"utf8"))}catch{}if(!findPluginRegistration(plugin)||installation?.installationId!==revision.installationId)throw Object.assign(new Error("Plugin state installation was removed or replaced"),{code:"PLUGIN_STATE_SCOPE_ENDED"});assertScope();let namespace={installationId:revision.installationId,collection:name,scope};switch(input.operation){case"get":return this.store.get(namespace,definition,input.key);case"get-many":return this.store.getMany(namespace,definition,input.keys);case"list":return this.store.list(namespace,definition,input);case"transact":return this.store.transact(namespace,definition,input.transaction);default:throw new Error("Invalid state operation")}}async close(){clearInterval(this.maintenance),this.controller.abort(),this.descriptions.clear(),this.store.clearMemory(),await Promise.allSettled([...this.pending])}};import{createHash as createHash11,randomUUID as randomUUID11}from"node:crypto";import{EventEmitter as EventEmitter2}from"node:events";import{readFileSync as readFileSync15,statSync as statSync11}from"node:fs";import{isAbsolute as isAbsolute7,join as join19}from"node:path";init_plugin();init_unified();init_pluginHostPaths();init_pluginExecutorProtocol();init_plugin_tasks();init_plugin_pages();function failure(code,message){return Object.assign(new Error(message),{code})}function sameTarget(a,b){return a.browserSessionId===b.browserSessionId&&a.tabId===b.tabId&&a.frameId===b.frameId&&a.documentId===b.documentId}var PluginPageManager=class{constructor(tasks,states,releaseModels){this.tasks=tasks;this.states=states;this.releaseModels=releaseModels;this.heartbeat=setInterval(()=>{this.reconcile()},1e4),this.heartbeat.unref()}instances=new Map;changes=new EventEmitter2().setMaxListeners(0);heartbeat;reconciling=new Set;closed=!1;describing=new Set;async resolveStateTarget(plugin,instanceId,bridge,options){let record2=this.own(plugin,instanceId,bridge);return await this.bind(record2,bridge,options),await this.validate(record2),{target:{...record2.value.target},assertActive:()=>{if(this.closed||record2.value.state!=="active"||this.instances.get(record2.value.instanceId)!==record2)throw failure("PLUGIN_STATE_SCOPE_ENDED","Plugin page state scope has ended")}}}diagnosticSnapshot(){return[...this.instances.values()].map(record2=>({plugin:record2.value.plugin,installationId:record2.value.installationId,revision:record2.value.revision,host:{hostId:record2.value.host.hostId,epoch:record2.value.host.epoch},binding:diagnosticBinding(record2.value),observation:"service-snapshot",state:record2.value.state,lastReceived:record2.lastReceived,queued:record2.deliveries.filter(delivery=>delivery.state==="queued").length,running:record2.running.size}))}async reconcile(){this.closed||await Promise.all([...this.instances.values()].map(async record2=>{let id=record2.value.instanceId;if(!(!record2.ready||record2.value.state!=="active"||this.reconciling.has(id))){this.reconciling.add(id);try{await this.validate(record2)}catch(error2){isPluginPageUnavailable(error2)||await this.invalidate(record2,error2)}finally{this.reconciling.delete(id)}}}))}bounded(promise,milliseconds){return new Promise((resolve7,reject)=>{let timer=setTimeout(()=>reject(failure("PLUGIN_PAGE_TIMEOUT","Plugin page browser request timed out")),milliseconds);promise.then(resolve7,reject).finally(()=>clearTimeout(timer))})}async invoke(record2,data,timeoutSec){let payload={...data,tabId:record2.value.target.tabId,instanceId:record2.value.instanceId,...timeoutSec!==void 0?{timeoutSec}:{}};return record2.client?record2.client.invoke("page_runtime",payload,{...record2.options,...timeoutSec!==void 0?{timeoutSec}:{}}):record2.bridge.invoke("page_runtime",payload,{...record2.options,...timeoutSec!==void 0?{timeoutSec}:{}})}unavailable(record2,error2){return isPluginPageInvalidation(error2)?error2:(record2.unavailable=!0,Date.now()-record2.lastVerifiedAt>=3e4?failure("PLUGIN_PAGE_INVALIDATED","Plugin page host recovery lease expired"):pluginPageUnavailable(error2))}async validate(record2){if(this.closed||record2.value.state!=="active")throw failure("PLUGIN_PAGE_INVALIDATED","Plugin page instance is no longer active");let registration=findPluginRegistration(record2.value.plugin),installation;try{installation=JSON.parse(readFileSync15(join19(pluginHostPaths().installations,`${record2.value.plugin}.json`),"utf8"))}catch{}if(!registration||installation?.installationId!==record2.value.installationId)throw failure("PLUGIN_PAGE_INVALIDATED","Plugin installation was removed or replaced");let bridge=record2.bridge,current;try{current=await this.bounded(this.invoke(record2,{operation:"get"}),8e3)}catch(error2){if(!record2.client&&record2.bridge!==bridge&&!this.closed&&record2.value.state==="active")return this.validate(record2);throw this.unavailable(record2,error2)}if(this.closed||record2.value.state!=="active")throw failure("PLUGIN_PAGE_INVALIDATED","Plugin page instance ended during validation");if(current.state!=="active"||!sameTarget(current.target,record2.value.target)||!matchesPluginPageURL(record2.runtime.matches,current.target.url))throw failure("PLUGIN_PAGE_INVALIDATED","Plugin page document changed");record2.value.target.url=current.target.url,record2.lastVerifiedAt=Date.now();let recovered=record2.unavailable;record2.unavailable=!1,recovered&&queueMicrotask(()=>this.drain(record2))}own(plugin,instanceId,bridge){if(bridge.scope.plugin&&bridge.scope.plugin!==plugin)throw failure("PLUGIN_PAGE_NOT_FOUND","Plugin page instance was not found");if(!isPluginPageInstanceId(instanceId))throw failure("PLUGIN_PAGE_NOT_FOUND","Invalid plugin page instance ID");let record2=this.instances.get(instanceId);if(!record2||record2.value.plugin!==plugin)throw failure("PLUGIN_PAGE_NOT_FOUND","Plugin page instance was not found");return record2}async bind(record2,bridge,options){if(bridge.scope.bridge!=="caller"||!record2.client&&record2.bridge.id===bridge.id||record2.value.state!=="active")return;let current;try{current=await this.bounded(bridge.invoke("page_runtime",{operation:"get",tabId:record2.value.target.tabId,instanceId:record2.value.instanceId},options),8e3)}catch(error2){throw this.unavailable(record2,error2)}if(this.closed||record2.value.state!=="active")throw failure("PLUGIN_PAGE_INVALIDATED","Plugin page instance ended while binding");if(!sameTarget(current.target,record2.value.target))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Plugin page belongs to a different browser document");record2.client||(record2.bridge=bridge,record2.options=options)}append(record2,event){for(record2.history.push({...event,sequence:++record2.value.sequence,time:Date.now()});record2.history.length>256||Buffer.byteLength(JSON.stringify(record2.history))>384*1024;)record2.history.shift();this.changes.emit(record2.value.instanceId)}async waitForChange(record2,cursor,waitMs,signal){!waitMs||cursor!==record2.value.sequence||record2.value.state!=="active"||await new Promise((resolve7,reject)=>{let finish=()=>{cleanup(),resolve7()},abort=()=>{cleanup(),reject(signal?.reason??new Error("Plugin page subscription closed"))},timer=setTimeout(finish,waitMs),cleanup=()=>{clearTimeout(timer),this.changes.off(record2.value.instanceId,finish),signal?.removeEventListener("abort",abort)};this.changes.once(record2.value.instanceId,finish),signal?.addEventListener("abort",abort,{once:!0}),signal?.aborted?abort():(record2.value.sequence!==cursor||record2.value.state!=="active")&&finish()})}async invalidate(record2,error2,state="invalidated"){if(record2.value.state==="active"){record2.value.state=state,record2.value.error=serializePluginExecutionError(error2),this.append(record2,{type:"invalidated",error:record2.value.error});for(let taskId of record2.taskIds)this.tasks.cancel(taskId,{access:"public",source:"runtime",bridge:"service"});for(let delivery of record2.deliveries)delivery.taskId&&this.tasks.cancel(delivery.taskId,{access:"public",source:"runtime",bridge:"service"}),["queued","running"].includes(delivery.state)&&(delivery.state="invalidated");try{this.states.store.discardScope(record2.value.installationId,`instance:${record2.value.instanceId}`)}catch(error3){console.error("Plugin instance cache cleanup failed:",error3)}record2.release();try{await this.releaseModels?.(record2.value.instanceId)}finally{try{await this.bounded(this.invoke(record2,{operation:"detach"}),2e3)}catch{}record2.client?.disconnect()}}}async call(plugin,input,bridge,options={},execution={cwd:process.cwd(),environment:Object.fromEntries(Object.entries(process.env).filter(entry=>entry[1]!==void 0))}){if(this.closed)throw failure("PLUGIN_PAGE_HOST_STOPPING","Plugin page service is stopping");if(execution.signal?.throwIfAborted(),!input||typeof input!="object"||Array.isArray(input))throw new Error("Invalid plugin page request");if(!/^[a-z][a-z0-9-]*$/.test(plugin)||bridge.scope.plugin&&bridge.scope.plugin!==plugin)throw new Error("Invalid plugin page scope");let operation=input.operation;if(execution.expectedTarget&&operation==="activation"&&input.enabled!==void 0)throw failure("PLUGIN_PAGE_TARGET_CHANGED","Document-bound calls cannot configure browser activation");if(operation==="attach"||operation==="activation"){if(Object.keys(options).some(key2=>!["browser","connector","session","local","transport","timeoutSec"].includes(key2)))throw new Error("Invalid plugin page routing options");if(bridge.scope.bridge==="service"&&!decodeBrowserTarget(options.browser))throw new Error("Plugin pages require a stable browser ID");if(!execution||!isAbsolute7(execution.cwd)||!statSync11(execution.cwd).isDirectory()||!execution.environment||Buffer.byteLength(JSON.stringify(execution.environment))>512*1024||Object.values(execution.environment).some(value=>typeof value!="string"))throw new Error("Invalid plugin page execution context")}if(operation==="activation"&&input.enabled!==!0){let client=bridge.scope.bridge==="service"?new UnifiedClient:void 0;try{return client?await client.invoke("page_runtime",{...input,plugin},options):await bridge.invoke("page_runtime",{...input,plugin},options)}finally{client?.disconnect()}}if(operation==="attach"||operation==="activation"){let revision=execution.revision??capturePluginRevision(plugin);if(input.installationId!==void 0&&input.installationId!==revision.installationId)throw failure("PLUGIN_PAGE_INSTALLATION_CHANGED","Page activation belongs to another plugin installation");let release=this.tasks.pinRevision(revision),retained=!1,client=bridge.scope.bridge==="service"?new UnifiedClient:void 0,invoke2=data=>client?client.invoke("page_runtime",data,options):bridge.invoke("page_runtime",data,options);try{let controller=new AbortController,description={controller,promise:this.tasks.describeRevision(revision,execution.signal?AbortSignal.any([controller.signal,execution.signal]):controller.signal,{cache:!0})};this.describing.add(description);let loaded;try{loaded=await description.promise}finally{this.describing.delete(description)}if(this.closed)throw new Error("Plugin page host stopped during attachment");let runtime=loaded.pageRuntime;if(!runtime)throw failure("PLUGIN_PAGE_NOT_DECLARED","Plugin has no pageRuntime declaration");if(operation==="activation")return await invoke2({...input,operation:"activation",plugin,installationId:revision.installationId,allowedMatches:runtime.matches});if(this.instances.size>=256){for(let[id,item]of this.instances)item.value.state!=="active"&&!item.running.size&&this.instances.delete(id);if(this.instances.size>=256)throw new Error("Too many plugin page instances")}let target=await invoke2({operation:"resolve",tabId:input.tabId,frame:input.frame,expectedDocumentId:input.documentId});if(this.closed)throw failure("PLUGIN_PAGE_HOST_STOPPING","Plugin page service is stopping");if(execution.signal?.throwIfAborted(),execution.expectedTarget&&!sameTarget(target,execution.expectedTarget))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Nested plugin must use the inherited document");if(!matchesPluginPageURL(runtime.matches,target.url))throw failure("PLUGIN_PAGE_URL_NOT_ALLOWED","Plugin page URL is outside its declared matches");let existing=[...this.instances.values()].find(item=>item.value.state==="active"&&item.value.plugin===plugin&&sameTarget(item.value.target,target));if(existing&&execution.revision&&existing.value.revision!==revision.revision&&input.replace!==!0)throw failure("PLUGIN_PAGE_REVISION_CHANGED","Existing page instance uses another plugin revision");if(existing&&input.replace!==!0)try{return await existing.initialized,await this.bind(existing,bridge,options),await this.validate(existing),input.activationGeneration===void 0&&await invoke2({operation:"get",instanceId:existing.value.instanceId,tabId:target.tabId,retainManual:!0}),existing.value}catch(error2){if(isPluginPageUnavailable(error2))throw error2;await this.invalidate(existing,error2)}existing&&await this.invalidate(existing,new Error("Plugin page instance replaced"),"detached");let value={instanceId:`pi_${randomUUID11()}`,plugin,installationId:revision.installationId,revision:revision.revision,host:this.tasks.host,target,state:"active",sequence:0,createdAt:Date.now()},initialized,initializationFailed,initialization=new Promise((resolve7,reject)=>{initialized=resolve7,initializationFailed=reject});initialization.catch(()=>{});let record3={initialized:initialization,value,runtime,revision,release,bridge,options:{...options},client,ready:!1,lastVerifiedAt:Date.now(),unavailable:!1,taskIds:new Set,execution:{cwd:execution.cwd,environment:{...execution.environment}},history:[],deliveries:[],lastReceived:0,running:new Set};this.instances.set(value.instanceId,record3);try{let attached=await invoke2({operation:"attach",instance:value,runtime,input:input.input??{},frame:input.frame??"business",activationGeneration:input.activationGeneration});if(!sameTarget(attached.target,target))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Document changed while attaching plugin page");if(await this.validate(record3),execution.signal?.throwIfAborted(),runtime.initialize){let parent=execution.parentIdentity;parent?.taskId&&this.tasks.assertChildBudget(parent.taskId);let{taskId}=this.start(record3,runtime.initialize,{input:input.input??{}},`initialize:${value.instanceId}`,parent),scope={access:"public",source:"runtime",bridge:"service"},cancel2=()=>this.tasks.cancel(taskId,scope);execution.signal?.addEventListener("abort",cancel2,{once:!0}),execution.signal?.aborted&&cancel2();try{parent?.taskId&&execution.signal?await this.tasks.awaitChild(parent.taskId,taskId,scope,execution.signal):await this.wait(record3,taskId)}finally{execution.signal?.removeEventListener("abort",cancel2),record3.taskIds.delete(taskId)}}return execution.signal?.throwIfAborted(),await this.validate(record3),execution.signal?.throwIfAborted(),record3.ready=!0,initialized(),this.drain(record3),retained=!0,value}catch(error2){throw initializationFailed(error2),await this.invalidate(record3,error2),error2}}finally{retained||(release(),client?.disconnect())}}if(operation==="list")return{instances:[...this.instances.values()].filter(item=>item.value.plugin===plugin&&(!execution.expectedTarget||sameTarget(item.value.target,execution.expectedTarget))).map(item=>item.value)};let record2=this.own(plugin,input.instanceId,bridge);if(execution.expectedTarget&&!sameTarget(record2.value.target,execution.expectedTarget))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Nested plugin must use the inherited document");if(execution.revision&&record2.value.revision!==execution.revision.revision)throw failure("PLUGIN_PAGE_REVISION_CHANGED","Page instance uses another plugin revision");if(await this.bind(record2,bridge,options),operation==="detach")return await this.invalidate(record2,new Error("Plugin page instance detached"),"detached"),record2.value;try{record2.value.state==="active"&&await this.validate(record2)}catch(error2){if(isPluginPageUnavailable(error2))throw error2;await this.invalidate(record2,error2)}if(operation==="get")return record2.value;if(operation==="events"){let cursor=input.cursor??0,waitMs=input.waitMs??0;if(!Number.isSafeInteger(cursor)||Number(cursor)<0||Number(cursor)>record2.value.sequence)throw new Error("Invalid plugin page event cursor");if(!Number.isSafeInteger(waitMs)||Number(waitMs)<0||Number(waitMs)>25e3)throw new Error("Invalid plugin page event wait");await this.waitForChange(record2,Number(cursor),Number(waitMs),execution.signal),execution.signal?.throwIfAborted();let first=record2.history[0]?.sequence??record2.value.sequence+1,events=[],bytes=0;for(let event of record2.history)if(event.sequence>Number(cursor)){let size=Buffer.byteLength(JSON.stringify(event));if(events.length&&bytes+size>192*1024)break;events.push(event),bytes+=size}return{instance:record2.value,events,nextCursor:events.at(-1)?.sequence??Number(cursor),firstAvailableSequence:first,gap:Number(cursor)<first-1}}if(operation!=="send")throw new Error("Unknown plugin page operation");let timeoutSec=input.timeoutSec;if(timeoutSec!==void 0&&(typeof timeoutSec!="number"||!Number.isFinite(timeoutSec)||timeoutSec<=0||timeoutSec>300))throw new Error("Page request timeoutSec must be between 0 and 300");await this.validate(record2),execution.signal?.throwIfAborted();let json2=Buffer.from(JSON.stringify(input.message??null));if(json2.length>24*1024*1024)throw new Error("Plugin page message exceeds 24 MiB");let requestId=randomUUID11(),cancel=()=>{this.invoke(record2,{operation:"cancel",requestId}).catch(()=>{})};execution.signal?.addEventListener("abort",cancel,{once:!0});try{let result;if(json2.length<=192*1024)result=await this.invoke(record2,{operation:"send",message:input.message,requestId},timeoutSec);else{let transferId=randomUUID11();try{await this.invoke(record2,{operation:"message_begin",transferId,bytes:json2.length,sha256:createHash11("sha256").update(json2).digest("hex")});for(let offset=0,index=0;offset<json2.length;offset+=192*1024,index++)await this.validate(record2),execution.signal?.throwIfAborted(),await this.invoke(record2,{operation:"message_chunk",transferId,index,data:json2.subarray(offset,offset+192*1024).toString("base64")});await this.validate(record2),execution.signal?.throwIfAborted(),result=await this.invoke(record2,{operation:"message_commit",transferId,requestId},timeoutSec)}finally{await this.invoke(record2,{operation:"message_abort",transferId}).catch(()=>{})}}execution.signal?.throwIfAborted(),await this.validate(record2);let reply=result;if(reply?.kind==="value")return reply.value;if(reply?.kind!=="resource"||!Number.isSafeInteger(reply.bytes)||reply.bytes<1||reply.bytes>24*1024*1024||reply.chunks!==Math.ceil(reply.bytes/(192*1024)))throw new Error("Invalid page response resource");try{let parts=[];for(let index=0;index<reply.chunks;index++){await this.validate(record2);let chunk=await this.invoke(record2,{operation:"result_chunk",resultId:reply.resultId,index});if(chunk.resultId!==reply.resultId||chunk.index!==index||chunk.bytes!==reply.bytes||chunk.sha256!==reply.sha256||chunk.chunks!==reply.chunks)throw new Error("Page response chunk identity mismatch");parts.push(Buffer.from(chunk.data,"base64"))}let bytes=Buffer.concat(parts);if(bytes.length!==reply.bytes||createHash11("sha256").update(bytes).digest("hex")!==reply.sha256)throw new Error("Page response integrity check failed");return await this.validate(record2),JSON.parse(bytes.toString("utf8"))}finally{await this.invoke(record2,{operation:"result_release",resultId:reply.resultId}).catch(()=>{})}}catch(error2){throw cancel(),error2}finally{execution.signal?.removeEventListener("abort",cancel)}}async receive(plugin,input,bridge,options={}){if(input.kind==="sync"){if(this.closed||!/^[a-z][a-z0-9-]*$/.test(plugin)||bridge.scope.plugin&&bridge.scope.plugin!==plugin||!Array.isArray(input.instances)||input.instances.length>32)throw new Error("Invalid page reconciliation request");let session2=await this.bounded(bridge.invoke("page_runtime",{operation:"session"},options),8e3);if(typeof input.browserSessionId!="string"||session2.browserSessionId!==input.browserSessionId)throw failure("PLUGIN_PAGE_TARGET_CHANGED","Page reconciliation came from another browser session");let instances=[];for(let value of input.instances){let proposed=value,record3=this.instances.get(proposed?.instanceId);if(!(!record3||record3.value.plugin!==plugin||record3.value.state!=="active"||!proposed.host||proposed.host.hostId!==this.tasks.host.hostId||proposed.host.epoch!==this.tasks.host.epoch||proposed.installationId!==record3.value.installationId||proposed.revision!==record3.value.revision||!proposed.target||!sameTarget(proposed.target,record3.value.target)||record3.value.target.browserSessionId!==session2.browserSessionId))try{await this.bind(record3,bridge,options),await this.validate(record3),instances.push({instance:record3.value,lastReceived:record3.lastReceived})}catch(error2){if(isPluginPageUnavailable(error2))throw error2;await this.invalidate(record3,error2)}}return{host:this.tasks.host,instances}}let record2=this.own(plugin,input.instanceId,bridge);if((await this.bounded(bridge.invoke("page_runtime",{operation:"session"},options),8e3)).browserSessionId!==record2.value.target.browserSessionId)throw failure("PLUGIN_PAGE_TARGET_CHANGED","Page event came from another browser");if(input.kind==="invalidated"){if(!input.target||!sameTarget(input.target,record2.value.target))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Invalid document invalidation source");return await this.invalidate(record2,new Error("Browser invalidated the document")),{accepted:!0}}let source=await this.bounded(bridge.invoke("page_runtime",{operation:"get",tabId:record2.value.target.tabId,instanceId:record2.value.instanceId},options),8e3);if(!sameTarget(source.target,record2.value.target))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Page event came from another document");if(await this.bind(record2,bridge,options),await this.validate(record2),input.kind==="navigation")return this.append(record2,{type:"navigation",payload:{url:record2.value.target.url}}),{accepted:!0};let sequence=input.eventSequence;if(!Number.isSafeInteger(sequence)||Number(sequence)<1)throw new Error("Invalid page event sequence");let duplicate=record2.deliveries.find(item=>item.sequence===sequence);if(Number(sequence)<=record2.lastReceived)return{accepted:!0,duplicate:!0,taskId:duplicate?.taskId,gap:!duplicate};if(Number(sequence)!==record2.lastReceived+1)throw failure("PLUGIN_PAGE_EVENT_GAP","Page event stream has a gap; reattach the instance");let name=typeof input.name=="string"?input.name:"",binding=record2.runtime.events?.[name];if(!binding||Buffer.byteLength(JSON.stringify(input.payload??null))>32*1024)throw new Error("Page event is not declared or exceeds 32 KiB");let waiting=record2.deliveries.filter(item=>item.name===name&&item.state==="queued");if(binding.policy==="latest")for(let previous of waiting)previous.state="superseded",this.append(record2,{type:"superseded",eventSequence:previous.sequence,name});else if(waiting.length>=(binding.maxPending??32))throw failure("PLUGIN_PAGE_QUEUE_FULL","Page event queue is full");if(record2.deliveries.filter(item=>["queued","running"].includes(item.state)).length>=128)throw failure("PLUGIN_PAGE_QUEUE_FULL","Page event queue is full");let delivery={sequence:Number(sequence),name,payload:input.payload,state:"queued"};for(record2.lastReceived=Number(sequence),record2.deliveries.push(delivery);record2.deliveries.length>256;){let index=record2.deliveries.findIndex(item=>!["queued","running"].includes(item.state));if(index<0)break;record2.deliveries.splice(index,1)}return this.append(record2,{type:"received",eventSequence:delivery.sequence,name,payload:input.payload}),this.drain(record2),{accepted:!0,eventSequence:delivery.sequence,taskId:delivery.taskId}}drain(record2){if(!(this.closed||!record2.ready||record2.unavailable||record2.value.state!=="active"))for(let delivery of record2.deliveries){if(delivery.state!=="queued")continue;let binding=record2.runtime.events[delivery.name];[...record2.running].filter(item=>item.name===delivery.name).length>=(binding.policy==="parallel"?binding.concurrency??2:1)||(delivery.state="running",record2.running.add(delivery),this.execute(record2,delivery).finally(()=>{record2.running.delete(delivery),this.drain(record2)}))}}async execute(record2,delivery){try{await this.validate(record2);let binding=record2.runtime.events[delivery.name],started=this.start(record2,binding.command,{event:{name:delivery.name,payload:delivery.payload,sequence:delivery.sequence}},`page:${record2.value.instanceId}:${delivery.sequence}`);delivery.taskId=started.taskId,this.append(record2,{type:"running",name:delivery.name,eventSequence:delivery.sequence,taskId:started.taskId}),await this.wait(record2,started.taskId),delivery.state="success",this.append(record2,{type:"success",name:delivery.name,eventSequence:delivery.sequence,taskId:started.taskId})}catch(error2){if(isPluginPageUnavailable(error2)&&!delivery.taskId&&record2.value.state==="active"){delivery.state="queued";return}delivery.state="error",this.append(record2,{type:"error",name:delivery.name,eventSequence:delivery.sequence,error:serializePluginExecutionError(error2)})}}start(record2,command,input,key2,parentIdentity){let bridge={get id(){return record2.bridge.id},scope:{...record2.bridge.scope,plugin:record2.value.plugin,access:"runtime",source:"runtime"},invoke:(action,payload,options)=>record2.client?record2.client.invoke(action,payload,options):record2.bridge.invoke(action,payload,options)},started=this.tasks.start({plugin:record2.value.plugin,command,input,cwd:record2.execution.cwd,environment:record2.execution.environment,options:record2.options,idempotencyKey:key2},bridge,{...parentIdentity?.taskId?{parentTaskId:parentIdentity.taskId,parentIdentity}:{},revision:record2.revision,guard:()=>this.validate(record2),page:{instanceId:record2.value.instanceId,target:{...record2.value.target}}});return record2.taskIds.add(started.taskId),started}async wait(record2,taskId){let cursor=0;for(;;){let page=await this.tasks.events(taskId,{access:"public",source:"runtime",bridge:"service"},cursor,25e3);if(cursor=page.nextCursor,!isPluginTaskActive(page.task.state)){if(record2.taskIds.delete(taskId),page.task.state!=="success")throw Object.assign(pluginExecutionError(page.task.error??{message:"Page handler failed"}),{childTaskId:taskId});return}record2.value.state!=="active"&&this.tasks.cancel(taskId,{access:"public",source:"runtime",bridge:"service"})}}async close(){this.closed=!0,clearInterval(this.heartbeat);for(let item of this.describing)item.controller.abort();await Promise.allSettled([...this.describing].map(item=>item.promise)),await Promise.allSettled([...this.instances.values()].map(record2=>this.invalidate(record2,new Error("Plugin page host stopped"))))}};var PluginModelCatalog=class{constructor(discover=async signal=>{let{listCodexModels:listCodexModels2}=await Promise.resolve().then(()=>(init_codex(),codex_exports));return listCodexModels2(signal)},now=Date.now){this.discover=discover;this.now=now}controller=new AbortController;cached;pending;list(provider="codex"){if(this.controller.signal.throwIfAborted(),provider!=="codex")throw new Error("Unsupported model provider");return this.cached?(this.cached.expires<=this.now()&&this.refresh().catch(()=>{}),Promise.resolve(structuredClone(this.cached.value))):this.refresh().then(value=>structuredClone(value))}refresh(){return this.pending??=this.load().catch(error2=>{throw this.cached&&(this.cached.expires=this.now()+3e4),error2}).finally(()=>{this.pending=void 0}),this.pending}async load(){let signal=AbortSignal.any([this.controller.signal,AbortSignal.timeout(15e3)]),value={provider:"codex",models:await this.discover(signal)};return signal.throwIfAborted(),this.cached={value,expires:this.now()+12e4},value}async close(){this.controller.abort(),await this.pending?.catch(()=>{}),this.cached=void 0}};init_runtimePaths();init_dist();init_pluginExecutorProtocol();init_pluginProcessTree();init_pluginModelDefinition();import{fork as fork2}from"node:child_process";import{createHash as createHash12}from"node:crypto";var PluginModelSessions=class{constructor(idleMs=5*6e4,limit=2,workerPath=PLUGIN_MODEL_SESSION_WORKER_PATH){this.idleMs=idleMs;this.limit=limit;this.workerPath=workerPath}sessions=new Map;closing=new Set;closed=!1;execute(owner,...args){let[plugin,,context,options={}]=args,request=options.model,configuration=Object.fromEntries(Object.entries(request).filter(([key3])=>!["prompt","outputSchema"].includes(key3)).sort(([a],[b])=>a.localeCompare(b))),key2=createHash12("sha256").update(JSON.stringify([owner,plugin,context.invocation?.installationId,context.invocation?.revision,options.cwd,Object.entries(options.environment??{}).sort(([a],[b])=>a.localeCompare(b)),configuration])).digest("hex");if(options.signal?.throwIfAborted(),this.closed)return Promise.reject(new Error("Model conversations are closed"));let session=this.sessions.get(key2);if(!session||session.closed){if(this.sessions.size>=this.limit){let idle=[...this.sessions.values()].find(value=>!value.queued);if(!idle)return Promise.reject(new Error("Model conversation limit reached"));this.remove(idle).catch(error2=>console.error("Model conversation cleanup failed:",error2))}let child=fork2(this.workerPath,[],{stdio:["ignore","ignore","pipe","ipc"],serialization:"advanced",execArgv:[],detached:process.platform!=="win32",cwd:options.cwd,env:options.environment}),exited=new Promise(resolve7=>child.once("close",()=>resolve7()));session={owner,child,tail:Promise.resolve(),queued:0,closed:!1,exited};let current2=session;child.on("error",()=>{this.remove(current2).catch(error2=>console.error("Model conversation cleanup failed:",error2))}),child.once("exit",()=>{this.remove(current2).catch(error2=>console.error("Model conversation cleanup failed:",error2))}),this.sessions.set(key2,session)}this.sessions.delete(key2),this.sessions.set(key2,session),clearTimeout(session.timer);let current=session;current.queued++;let started=!1,operation=current.tail.catch(()=>{}).then(async()=>{if(options.signal?.throwIfAborted(),current.closed)throw new Error("Model conversation has ended");return started=!0,this.run(current,args)});if(current.tail=operation.finally(()=>{current.queued--,!current.queued&&!current.closed&&(current.timer=setTimeout(()=>{this.remove(current).catch(error2=>console.error("Model conversation cleanup failed:",error2))},this.idleMs),current.timer.unref())}),current.tail.catch(()=>{}),!options.signal)return operation;let signal=options.signal;return new Promise((resolve7,reject)=>{let abort=()=>{started||reject(Object.assign(new Error("Model execution cancelled"),{code:"PLUGIN_EXECUTION_CANCELLED"}))};signal.addEventListener("abort",abort,{once:!0}),signal.aborted&&abort(),operation.then(resolve7,reject).finally(()=>signal.removeEventListener("abort",abort))})}run(session,args){let options=args[3],request=options.model,child=session.child;return new Promise((resolve7,reject)=>{let finished=!1,stderr="",timer,finish=async(failure2,result)=>{if(!finished){finished=!0,clearTimeout(timer),options.signal?.removeEventListener("abort",abort),child.removeListener("message",message),child.removeListener("error",failed),child.removeListener("exit",exited),child.stderr?.removeListener("data",capture);try{failure2&&await this.remove(session),options.onExecutorStopped?.(),failure2?reject(failure2):resolve7(result)}catch(error2){reject(error2)}}},failed=error2=>{finish(error2)},exited=()=>failed(new Error(stderr||"Model conversation executor exited")),abort=()=>failed(Object.assign(new Error("Model execution cancelled"),{code:"PLUGIN_EXECUTION_CANCELLED"})),capture=chunk=>{stderr=(stderr+chunk.toString()).slice(-65536)},message=event=>{if(!finished)try{event.type==="result"?finish(void 0,validatePluginModelResult(request,event.result)):event.type==="error"?failed(pluginExecutionError(event.error)):event.type==="diagnostic"?options.onDiagnostic?.(event.metadata):event.type==="delta"&&options.onProgress?.({message:"model.delta",data:{delta:event.delta,index:event.index,...event.metadata,partial:!0}})}catch(error2){finish(error2)}};child.on("message",message),child.once("error",failed),child.once("exit",exited),child.stderr?.on("data",capture),options.signal?.addEventListener("abort",abort,{once:!0}),options.executionTimeoutMs&&(timer=setTimeout(()=>failed(Object.assign(new Error("Model execution deadline exceeded"),{code:"PLUGIN_EXECUTION_DEADLINE"})),options.executionTimeoutMs));try{if(options.signal?.aborted){abort();return}child.pid&&options.onExecutorStarted?.({pid:child.pid,processIdentity:processIdentity(child.pid)}),child.send({request},error2=>{error2&&failed(error2)})}catch(error2){finish(error2)}})}remove(session){if(session.closing)return session.closing;session.closed=!0,clearTimeout(session.timer);for(let[key2,value]of this.sessions)value===session&&this.sessions.delete(key2);return session.closing=(session.child.pid?stopPluginProcessTree(session.child.pid):Promise.resolve()).then(()=>{let timer,timeout=new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error("Model conversation did not exit")),2e3)});return Promise.race([session.exited,timeout]).finally(()=>clearTimeout(timer))}).catch(cause=>{throw Object.assign(new Error("Unable to confirm model conversation cleanup",{cause}),{code:"PLUGIN_EXECUTOR_CLEANUP_FAILED",details:{executionStopped:!1}})}),this.closing.add(session),session.closing.finally(()=>this.closing.delete(session)).catch(()=>{}),session.closing}async release(owner){await Promise.all([...new Set([...this.sessions.values(),...this.closing])].filter(value=>value.owner===owner).map(value=>this.remove(value)))}async close(){this.closed=!0,await Promise.all([...new Set([...this.sessions.values(),...this.closing])].map(value=>this.remove(value)))}};import{dirname as dirname9,join as join20}from"node:path";var PluginHost=class{tasks;state;pages;models=new PluginModelCatalog;modelSessions;closing;constructor(configuration={}){this.modelSessions=new PluginModelSessions(void 0,Math.max(2,configuration.modelConcurrency??2),configuration.workerPath?join20(dirname9(configuration.workerPath),"pluginModelSessionWorker.js"):void 0),this.tasks=new PluginTaskManager(configuration,{state:(...args)=>this.state.call(...args),page:(...args)=>this.pages.call(...args),listModels:provider=>this.models.list(provider),model:(owner,...args)=>this.modelSessions.execute(owner,...args),releaseModels:owner=>this.modelSessions.release(owner)}),this.state=new PluginStateManager(this.tasks),this.pages=new PluginPageManager(this.tasks,this.state,owner=>this.modelSessions.release(owner)),this.state.resolveTarget=(...args)=>this.pages.resolveStateTarget(...args)}diagnose(value,scope){let options=normalizeDiagnosticOptions(value);if(scope.plugin&&options.plugin&&scope.plugin!==options.plugin)throw Object.assign(new Error("Plugin task was not found"),{code:"PLUGIN_TASK_NOT_FOUND"});return buildDiagnosticReport(this.tasks.host,scope,options,this.tasks.store.diagnosticRecords(options.taskId),this.pages.diagnosticSnapshot())}close(){return this.closing??=Promise.allSettled([this.pages.close(),this.tasks.close(),this.state.close(),this.models.close(),this.modelSessions.close()]).then(results=>{let failures=results.flatMap(result=>result.status==="rejected"?[result.reason]:[]);if(failures.length)throw new AggregateError(failures,"Plugin host cleanup failed")}),this.closing}};init_pluginTaskWire();init_pluginExecutorProtocol();async function startPluginHostServer(configuration={}){let paths=pluginHostPaths(),existing=readRecord(paths.lease);existing&&!isProcessOwnerAlive(existing)?removeRecordIfPid(paths.lease,existing.pid):existing||removeInvalidRecord(paths.lease);let owner={pid:process.pid,protocolVersion:5,version:CLIENT_VERSION,buildId:PLUGIN_HOST_BUILD_ID,processIdentity:processIdentity(process.pid)};if(!claimRecord(paths.lease,owner))throw Object.assign(new Error("Plugin execution service already has an owner"),{code:"PLUGIN_TASK_HOST_OWNED"});let host;try{host=new PluginHost(configuration)}catch(error2){throw removeRecordIfPid(paths.lease,process.pid),error2}let{tasks:manager,pages,state,models}=host;if(!writeRecordIfPid(paths.lease,process.pid,{...owner,...manager.host,socketPath:paths.socket,startedAt:new Date().toISOString()}))throw new Error("Plugin host lease ownership was lost");let sessions=new Set,closing,server=createServer(socket=>{sessions.add(socket);let disconnected=new AbortController,scope,sequence=0,requests=0,pending=new Map,bridge={id:randomUUID12(),get scope(){if(!scope)throw new Error("Plugin task connection is not initialized");return scope},invoke(action,payload,options){if(socket.destroyed)return Promise.reject(Object.assign(new Error("The browser invocation bridge is unavailable"),{code:"PLUGIN_TASK_BRIDGE_UNAVAILABLE"}));let id=++sequence;return new Promise((resolve7,reject)=>{let timer=setTimeout(()=>{pending.delete(id),reject(Object.assign(new Error("The browser invocation bridge timed out; external operation status is unknown"),{code:"PLUGIN_TASK_BRIDGE_UNAVAILABLE",details:{action}}))},Math.min(Math.max(options.timeoutSec??180,1),3600)*1e3);pending.set(id,{resolve:resolve7,reject,timer}),writePluginTaskMessage(socket,{type:"invoke",id,action,payload,options}).catch(error2=>{pending.delete(id),clearTimeout(timer),reject(error2)})})}},handle=async(method,input)=>{if(method==="hello"){if(scope||input.protocolVersion!==5)throw new Error("Plugin task protocol version mismatch");if(!["public","toolbox","runtime"].includes(String(input.access))||!["service","caller"].includes(String(input.bridge))||!["cli","sdk","mcp","toolbox","runtime"].includes(String(input.source))||input.plugin!==void 0&&(typeof input.plugin!="string"||!/^[a-z][a-z0-9-]*$/.test(input.plugin))||input.scopeId!==void 0&&(typeof input.scopeId!="string"||input.scopeId.length>256)||input.access!=="public"&&(!input.plugin||input.bridge!=="caller"))throw new Error("Invalid plugin task caller scope");return scope={access:input.access,bridge:input.bridge,source:input.source,...input.plugin?{plugin:String(input.plugin)}:{},...input.scopeId?{scopeId:String(input.scopeId)}:{}},{protocolVersion:5,version:CLIENT_VERSION,buildId:PLUGIN_HOST_BUILD_ID,host:manager.host,pid:process.pid}}if(!scope)throw new Error("Plugin task connection requires a handshake");if(method==="status")return{protocolVersion:5,version:CLIENT_VERSION,buildId:PLUGIN_HOST_BUILD_ID,host:manager.host,pid:process.pid};if(method==="diagnose")return host.diagnose(input,scope);if(method==="state.call")return state.call(String(input.plugin),input.input,bridge,input.options);if(method==="model.list")return models.list(input.provider);if(method==="model.capabilities")return(await Promise.resolve().then(()=>(init_pluginModelDefinition(),pluginModelDefinition_exports))).pluginModelCapabilities();if(method==="page.call"){let execution=input.execution;return pages.call(String(input.plugin),input.input,bridge,input.options,execution?{cwd:execution.cwd,environment:execution.environment,signal:disconnected.signal}:void 0)}if(method==="page.event"){if(scope.access!=="runtime")throw new Error("Page events require the browser runtime bridge");return pages.receive(String(input.plugin),input.input,bridge,input.options)}if(method==="start")return manager.start(input,bridge);if(method==="resume")return manager.resume(input,bridge);if(method==="list")return manager.list(scope,input.activeOnly===!0,input.cursor,input.limit);if(method==="shutdown"){if(scope.access!=="public")throw new Error("Tool pages cannot stop the plugin host");return{stopped:!0}}if(typeof input.taskId!="string")throw new Error("Plugin task ID is required");switch(method){case"get":return manager.get(input.taskId,scope);case"checkpoint":return manager.checkpoint(input.taskId,scope);case"cancel":return manager.cancel(input.taskId,scope);case"events":return manager.events(input.taskId,scope,input.cursor,input.waitMs,disconnected.signal);case"result":return manager.resultChunk(input.taskId,scope,input.index);default:throw new Error(`Unknown plugin task method: ${method}`)}};readPluginTaskMessages(socket,message=>{if(message.type==="invoke-result"){let request=pending.get(message.id);if(!request)return;clearTimeout(request.timer),pending.delete(message.id),message.error?request.reject(pluginExecutionError(message.error)):request.resolve(message.result)}else if(message.type==="request"){if(++requests>64||!message.input||typeof message.input!="object"||Array.isArray(message.input)){socket.destroy(new Error("Invalid or excessive plugin task requests"));return}Promise.resolve().then(()=>handle(message.method,message.input)).then(async result=>{await writePluginTaskMessage(socket,{type:"response",id:message.id,result}),message.method==="shutdown"&&setImmediate(()=>{close().catch(()=>{})})},error2=>writePluginTaskMessage(socket,{type:"response",id:message.id,error:serializePluginExecutionError(error2)})).catch(()=>socket.destroy()).finally(()=>{requests--})}else socket.destroy(new Error("Invalid plugin task message direction"))}),socket.on("error",()=>{}),socket.once("close",()=>{disconnected.abort(),sessions.delete(socket);for(let request of pending.values())clearTimeout(request.timer),request.reject(Object.assign(new Error("The invocation bridge disconnected; external operation status may need reconciliation"),{code:"PLUGIN_TASK_BRIDGE_UNAVAILABLE"}));pending.clear()})});async function close(){return closing??=(async()=>{let servicesClosed=host.close();for(let socket of sessions)socket.destroy();await servicesClosed,await new Promise(resolve7=>server.close(()=>resolve7())),removeRecordIfPid(paths.lease,process.pid)&&process.platform!=="win32"&&rmSync13(paths.socket,{force:!0})})(),closing}try{process.platform!=="win32"&&rmSync13(paths.socket,{force:!0}),await new Promise((resolve7,reject)=>{server.once("error",reject),server.listen(paths.socket,()=>{server.off("error",reject),resolve7()})}),process.platform!=="win32"&&chmodSync2(paths.socket,384)}catch(error2){throw removeRecordIfPid(paths.lease,process.pid),await host.close(),error2}return{close,host,socketPath:paths.socket}}try{let server=await startPluginHostServer(),close=()=>{server.close().then(()=>process.exit(0))};process.once("SIGTERM",close),process.once("SIGINT",close)}catch(error2){error2.code!=="PLUGIN_TASK_HOST_OWNED"&&process.stderr.write(`${error2 instanceof Error?error2.stack:String(error2)}
|
|
987
|
+
`)}handleExit(child,error2){if(this.process===child){this.process=null,this.initialized=!1,this.startPromise=null,this.executableResolver.reset(),this.rejectPending(error2);for(let providerSessionId of this.knownThreadIds)this.emit({providerSessionId,kind:"error",method:"provider/exited",params:{message:error2.message}})}}rejectPending(error2){for(let[id,pending]of this.pendingRequests)clearTimeout(pending.timer),pending.reject(error2),this.pendingRequests.delete(id)}modelDiagnostic(metadata){this.emit({kind:"notification",method:"provider/model/diagnostic",params:metadata})}emit(event){for(let listener of this.listeners)listener(event)}extractThreadId(params){return[params?.threadId,params?.conversationId,params?.thread?.id,params?.turn?.threadId].find(value=>typeof value=="string")}probeExecutable(executable){return probeAgentExecutable(executable,{args:["app-server","--help"],timeoutMs:PROBE_TIMEOUT_MS,timeoutError:"Timed out while checking Codex app-server availability.",startError:error2=>`Failed to start Codex: ${error2.message}`,exitError:code=>`Codex app-server check exited with code ${String(code)}`})}readExecutableVersion(executable){return codexExecutableVersion(executable)}resolveExecutable(){return this.executableResolver.resolve()}resolveExecutableCandidates(){return executableFileCandidates(codexExecutableCandidatePaths())}}}});import{readFileSync as readFileSync16}from"node:fs";async function runCodexModel(request,context,provider=new CodexAgentProvider(()=>{},JSON.parse(readFileSync16(new URL("../package.json",import.meta.url),"utf8")).version)){if(request.provider!=="codex"||request.mode!=="agent"||request.permissions!=="read-only")throw Object.assign(new Error("Codex supports explicit read-only agent execution; strict tool-free generation is unavailable"),{code:"PLUGIN_MODEL_CAPABILITY_UNSUPPORTED"});context.signal.throwIfAborted();let session,turn,text3,bytes=0,index=0,retries=0,buffered="",streamItem,streamPhase,phases=new Map,flushTimer,flush=()=>{clearTimeout(flushTimer),flushTimer=void 0;try{for(let offset=0;offset<buffered.length;offset+=4e3)streamItem||streamPhase?context.onDelta(buffered.slice(offset,offset+4e3),++index,{...streamItem?{itemId:streamItem}:{},...streamPhase?{phase:streamPhase}:{}}):context.onDelta(buffered.slice(offset,offset+4e3),++index)}catch(error2){fail2(error2)}buffered=""},finish,fail2,completed=new Promise((resolve7,reject)=>{finish=resolve7,fail2=reject});completed.catch(()=>{});let listener=event=>{let params=event.params;event.method==="provider/model/diagnostic"&&context.onDiagnostic?.(params),event.method==="account/updated"&&context.onDiagnostic?.({authentication:["apikey","chatgpt","chatgptAuthTokens"].includes(params?.authMode)?params.authMode:"unknown"}),event.method==="session/model/updated"&&typeof params?.model?.id=="string"&&context.onDiagnostic?.({selectedModel:params.model.id}),event.method==="session/activeModel/updated"&&typeof params?.model?.id=="string"&&context.onDiagnostic?.({activeModel:params.model.id});let relevant=event.providerSessionId===void 0||event.providerSessionId===session;if(event.kind==="request"&&relevant){try{provider.respondToRequest(event.requestId,{error:"Interactive requests are disabled for background model tasks"})}catch{}fail2(Object.assign(new Error("Model requested interactive input"),{code:"PLUGIN_MODEL_INTERACTION_REQUIRED"})),provider.dispose();return}if((event.kind==="error"||event.method==="provider/exited")&&relevant){if(event.method!=="provider/exited"&¶ms?.willRetry===!0){context.onDiagnostic?.({retryCount:++retries});return}fail2(Object.assign(new Error(String(params?.error?.message??params?.message??"Model provider failed")),{code:"PLUGIN_MODEL_PROVIDER_ERROR"})),provider.dispose();return}if(!(!session||event.providerSessionId!==session))if(event.method==="item/started"&¶ms?.item?.type==="agentMessage")flush(),streamItem=params.item.id,streamPhase=params.item.phase,streamItem&&streamPhase&&phases.set(streamItem,streamPhase);else if(event.method==="item/agentMessage/delta"&&typeof params?.delta=="string"){let itemId=params.itemId??streamItem,phase=params.phase??(itemId?phases.get(itemId):streamPhase);if((itemId!==streamItem||phase!==streamPhase)&&flush(),streamItem=itemId,streamPhase=phase,bytes+=Buffer.byteLength(params.delta),bytes>32*1024*1024){fail2(new Error("Model output exceeds 32 MiB"));return}buffered+=params.delta,buffered.length>=16e3?flush():flushTimer??=setTimeout(flush,50)}else if(event.method==="item/completed"&¶ms?.item?.type==="agentMessage"&&typeof params.item.text=="string"){if(flush(),Buffer.byteLength(params.item.text)>32*1024*1024){fail2(new Error("Model output exceeds 32 MiB"));return}(!params.item.phase||params.item.phase==="final_answer")&&(text3=params.item.text)}else event.method==="turn/completed"&&(params?.turn?.status!=="completed"?fail2(Object.assign(new Error(`Model turn ended with ${String(params?.turn?.status)}${params?.turn?.error?.message?`: ${params.turn.error.message}`:""}`),{code:"PLUGIN_MODEL_TURN_FAILED"})):text3===void 0?fail2(new Error("Model returned no final text")):(flush(),finish(text3)))},unsubscribe=provider.onEvent(listener),abort=()=>{fail2(Object.assign(new Error("Model execution cancelled"),{code:"PLUGIN_EXECUTION_CANCELLED"})),session&&turn&&provider.interrupt(session,turn).catch(()=>{}),provider.dispose()};context.signal.addEventListener("abort",abort,{once:!0});try{session=context.conversation?.threadId??await provider.startSession({cwd:context.cwd,ephemeral:!0,readOnly:!0,modelExecution:!0,...request.model?{modelId:request.model}:{},...request.instructions?{developerInstructions:request.instructions}:{}}),context.conversation&&(context.conversation.threadId=session),context.onDiagnostic?.({...provider.getSessionModel(session)?.id?{selectedModel:provider.getSessionModel(session).id}:{}}),context.signal.throwIfAborted();let[,result]=await Promise.all([provider.sendMessage(session,request.prompt,[],{reasoningSummary:"none",reasoningEffort:request.reasoningEffort,outputSchema:request.outputSchema,modelExecution:!0}).then(value=>{turn=value.turnId}),completed]);return context.signal.throwIfAborted(),{text:result,provider:request.provider,mode:request.mode,...provider.getSessionModel(session)?.id?{model:provider.getSessionModel(session).id}:{}}}catch(error2){throw context.conversation&&(delete context.conversation.threadId,provider.dispose()),error2}finally{clearTimeout(flushTimer),context.signal.removeEventListener("abort",abort),unsubscribe(),context.conversation||provider.dispose()}}var init_model=__esm({"../agent-runtime/dist/model.js"(){"use strict";init_codex_provider();init_capabilities()}});import{readFileSync as readFileSync17}from"node:fs";async function listCodexModels(signal,provider=new CodexAgentProvider(()=>{},JSON.parse(readFileSync17(new URL("../package.json",import.meta.url),"utf8")).version)){signal.throwIfAborted();let abort,cancelled=new Promise((_resolve,reject)=>{abort=()=>{provider.dispose(),reject(signal.reason??new Error("Model discovery cancelled"))},signal.addEventListener("abort",abort,{once:!0})});try{return await Promise.race([provider.listModels(),cancelled])}finally{signal.removeEventListener("abort",abort),provider.dispose()}}var init_model_catalog=__esm({"../agent-runtime/dist/model-catalog.js"(){"use strict";init_codex_provider()}});var codex_exports={};__export(codex_exports,{CODEX_MODEL_CAPABILITY:()=>CODEX_MODEL_CAPABILITY,CodexAgentProvider:()=>CodexAgentProvider,codexAppExecutableCandidatePaths:()=>codexAppExecutableCandidatePaths,codexExecutableCandidatePaths:()=>codexExecutableCandidatePaths,codexThreadUrl:()=>codexThreadUrl,codexWorkspaceUrl:()=>codexWorkspaceUrl,compareCodexVersions:()=>compareCodexVersions,externalOpenCommand:()=>externalOpenCommand,listCodexModels:()=>listCodexModels,normalizeCodexVersion:()=>normalizeCodexVersion,runCodexModel:()=>runCodexModel});var init_codex=__esm({"../agent-runtime/dist/codex.js"(){"use strict";init_codex_provider();init_model();init_model_catalog()}});init_runtimePaths();var PLUGIN_HOST_BUILD_ID="83b778ad78c82ef3cd6d48e2a23b118d36e60b9adceda991f943f9c346dd10fe";init_dist();init_plugin_tasks();init_pluginHostPaths();import{randomUUID as randomUUID12}from"node:crypto";import{createServer}from"node:net";import{chmodSync as chmodSync2,rmSync as rmSync13}from"node:fs";init_dist();init_plugin_tasks();init_plugin_pages();import{basename as basename2}from"node:path";var MAX_PLUGIN_DIAGNOSTIC_BYTES=256*1024;function text2(value,maximum=256){if(typeof value!="string")return;let bytes=Buffer.from(value);return bytes.length<=maximum?value:bytes.subarray(0,maximum).toString("utf8").replace(/\uFFFD$/,"")}function normalizeDiagnosticOptions(value){if(!value||typeof value!="object"||Array.isArray(value))throw new Error("Invalid diagnostic options");let input=value;if(Object.keys(input).some(key2=>!["taskId","plugin","instanceId","includeErrorMessages","includeLocalPaths"].includes(key2)))throw new Error("Unknown diagnostic option");if(input.taskId!==void 0&&!isPluginTaskId(input.taskId))throw new Error("Invalid diagnostic task ID");if(input.instanceId!==void 0&&!isPluginPageInstanceId(input.instanceId))throw new Error("Invalid diagnostic instance ID");if(input.plugin!==void 0&&(typeof input.plugin!="string"||!/^[a-z][a-z0-9-]*$/.test(input.plugin)||input.plugin.length>128))throw new Error("Invalid diagnostic plugin");for(let key2 of["includeErrorMessages","includeLocalPaths"])if(input[key2]!==void 0&&typeof input[key2]!="boolean")throw new Error("Invalid diagnostic content option");return{...input.taskId?{taskId:String(input.taskId)}:{},...input.plugin?{plugin:String(input.plugin)}:{},...input.instanceId?{instanceId:String(input.instanceId)}:{},...input.includeErrorMessages!==void 0?{includeErrorMessages:input.includeErrorMessages}:{},...input.includeLocalPaths!==void 0?{includeLocalPaths:input.includeLocalPaths}:{}}}function diagnosticFailure(value,options){if(!value)return;let message=options.includeErrorMessages?text2(value.message,4096):void 0;return{code:text2(value.code)??"PLUGIN_ERROR_UNCLASSIFIED",...message!==void 0?{message,...Buffer.byteLength(value.message??"")>4096?{messageTruncated:!0}:{}}:{}}}function normalizeModelDiagnostic(value,previous={}){let result={...previous};if(!value||typeof value!="object"||Array.isArray(value))return result;let input=value;for(let key2 of["requestedModel","selectedModel","activeModel"])typeof input[key2]=="string"&&(result[key2]=text2(input[key2]));["apikey","chatgpt","chatgptAuthTokens","unknown"].includes(input.authentication)&&(result.authentication=input.authentication);for(let key2 of["threadRestrictionsAccepted","turnRestrictionsAccepted"])typeof input[key2]=="boolean"&&(result[key2]=input[key2]);Number.isSafeInteger(input.retryCount)&&input.retryCount>=0&&(result.retryCount=Math.min(input.retryCount,1e6));let runtime=input.runtime;runtime&&["explicit","automatic"].includes(runtime.selection)&&(result.runtime={...typeof runtime.executable=="string"?{executable:text2(runtime.executable,4096)}:{},...typeof runtime.version=="string"?{version:text2(runtime.version)}:{},selection:runtime.selection,...typeof runtime.started=="boolean"?{started:runtime.started}:{}});let config=input.configuration;return config&&["default","environment"].includes(config.source)&&typeof config.filePresent=="boolean"&&(result.configuration={source:config.source,filePresent:config.filePresent,...typeof config.home=="string"?{home:text2(config.home,4096)}:{},...typeof config.baseProvider=="string"?{baseProvider:text2(config.baseProvider)}:{},...["file","keyring","auto","ephemeral","unknown"].includes(config.credentialStore)?{credentialStore:config.credentialStore}:{}}),result}function diagnosticBinding(value){return{instanceId:value.instanceId,browserSessionId:value.target.browserSessionId,tabId:value.target.tabId,frameId:value.target.frameId,documentId:value.target.documentId}}function modelProjection(record2,options){if(record2.task.kind!=="model")return;let request=record2.task.input.request,metadata=normalizeModelDiagnostic(record2.diagnostics?.model,typeof request?.model=="string"?{requestedModel:text2(request.model)}:{});if(!options.includeLocalPaths&&(metadata.runtime?.executable&&(metadata.runtime={...metadata.runtime,executable:basename2(metadata.runtime.executable)}),metadata.configuration)){let{home:_home,...configuration}=metadata.configuration;metadata.configuration=configuration}return metadata}function taskProjection(record2,options,observedAt){let{task,diagnostics,execution,executorOwner:owner}=record2,events=record2.events.filter(event=>event.type!=="progress"),first=record2.events[0]?.sequence??task.sequence+1,alive=owner&&!diagnostics?.executorStoppedAt?isProcessOwnerAlive(owner):!1,stopped=diagnostics?.executorStoppedAt,state=alive?"observed-alive":stopped?"stop-confirmed":diagnostics?.executorAttempted===!1?"never-started":"unknown",routing=execution?.options;return{taskId:task.taskId,plugin:text2(task.plugin),kind:task.kind??"command",command:text2(task.command),state:task.state,source:task.source,host:{hostId:task.host.hostId,epoch:task.host.epoch},installationId:task.installationId,revision:task.revision,...diagnostics?.invocationId?{invocationId:diagnostics.invocationId}:{},createdAt:task.createdAt,updatedAt:task.updatedAt,sequence:task.sequence,...diagnostics?.page?{page:diagnosticBinding({instanceId:diagnostics.page.instanceId,target:diagnostics.page})}:{},...routing?{routing:{...routing.browser?{browser:text2(routing.browser,1024)}:{},...routing.session?{session:text2(routing.session)}:{},...routing.connector?{connector:text2(routing.connector)}:{},...routing.local!==void 0?{local:routing.local}:{},...routing.transport?{transport:routing.transport}:{}}}:{},...execution?.executionTimeoutMs?{executionTimeoutMs:execution.executionTimeoutMs}:{},executor:{state,observedAt,...owner?{pid:owner.pid}:{},...stopped?{stoppedAt:stopped}:{}},...task.error?{failure:diagnosticFailure(task.error,options)}:{},...task.checkpoint?{checkpoint:{sequence:task.checkpoint.sequence,version:task.checkpoint.version,schemaHash:task.checkpoint.schemaHash,updatedAt:task.checkpoint.updatedAt}}:{},...task.result?{result:{bytes:task.result.bytes,chunks:task.result.chunks,sha256:task.result.sha256}}:{},...task.kind==="model"?{model:modelProjection(record2,options)}:{},metadataIncomplete:!diagnostics||!!diagnostics.updatesDropped||task.kind==="model"&&!diagnostics.model?.runtime,history:{firstRetainedSequence:first,gap:first>1,omitted:Math.max(0,events.length-8),events:events.slice(-8).map(event=>({sequence:event.sequence,time:event.time,state:event.state,type:event.type}))}}}function buildDiagnosticReport(host,scope,options,scan,snapshots){let records=scan.records.filter(record2=>(!scope.plugin||record2.task.plugin===scope.plugin)&&(scope.access!=="toolbox"||record2.access!=="public")&&(!options.plugin||record2.task.plugin===options.plugin)&&(!options.instanceId||record2.diagnostics?.page?.instanceId===options.instanceId)),visible=new Map(records.map(record2=>[record2.task.taskId,record2])),selected=records.sort((a,b)=>b.task.createdAt-a.task.createdAt||a.task.taskId.localeCompare(b.task.taskId));if(options.taskId){if(!visible.has(options.taskId))throw Object.assign(new Error("Plugin task was not found"),{code:"PLUGIN_TASK_NOT_FOUND"});let ids=new Set([options.taskId]),pending=[options.taskId];for(;pending.length;){let id=pending.shift(),item=visible.get(id),candidates=[item.task.parentTaskId,item.task.resumedFrom?.taskId,...records.filter(record2=>record2.task.parentTaskId===id||record2.task.resumedFrom?.taskId===id).map(record2=>record2.task.taskId)];for(let candidate of candidates)candidate&&visible.has(candidate)&&!ids.has(candidate)&&(ids.add(candidate),pending.push(candidate))}selected=[...ids].map(id=>visible.get(id))}let generatedAt=Date.now(),tasks=selected.slice(0,128).map(record2=>taskProjection(record2,options,generatedAt)),pages=snapshots.filter(page=>(!scope.plugin||page.plugin===scope.plugin)&&(!options.plugin||page.plugin===options.plugin)&&(!options.instanceId||page.binding.instanceId===options.instanceId)&&(!options.taskId||tasks.some(task=>task.page?.instanceId===page.binding.instanceId)));for(let task of tasks)task.page&&!pages.some(page=>page.binding.instanceId===task.page.instanceId)&&pages.push({plugin:task.plugin,installationId:task.installationId,revision:task.revision,host:task.host,binding:task.page,observation:"historical-binding"});let report={version:1,generatedAt,host:{hostId:host.hostId,epoch:host.epoch},protocolVersion:5,selection:{...options.taskId?{taskId:options.taskId}:{},...options.plugin?{plugin:options.plugin}:{},...options.instanceId?{instanceId:options.instanceId}:{}},coverage:{scanLimited:scan.limited,tasksOmitted:selected.length-tasks.length,pagesOmitted:Math.max(0,pages.length-64),byteLimited:!1},tasks,pages:pages.slice(0,64),links:[]},links=()=>{let retained=new Set(report.tasks.map(task=>task.taskId));report.links=report.tasks.flatMap(task=>{let record2=visible.get(task.taskId);return[["parent",record2.task.parentTaskId],["resume",record2.task.resumedFrom?.taskId]].flatMap(([kind,from])=>from&&retained.has(from)?[{from,to:task.taskId,kind}]:[])})};for(links();Buffer.byteLength(JSON.stringify(report))>MAX_PLUGIN_DIAGNOSTIC_BYTES;)report.coverage.byteLimited=!0,report.pages.length?(report.pages.pop(),report.coverage.pagesOmitted++):(report.tasks.pop(),report.coverage.tasksOmitted++,links());return report}init_pluginModelDefinition();init_pluginRecoveryDefinition();init_pluginExecution();init_pluginExecutor();init_pluginExecutorProtocol();import{createHash as createHash9,randomUUID as randomUUID10}from"node:crypto";import{EventEmitter}from"node:events";import{isAbsolute as isAbsolute6,join as join16,relative as relative5,sep as sep4}from"node:path";import{realpathSync as realpathSync4,statSync as statSync9}from"node:fs";init_dist();init_pluginHostPaths();init_skillManifest();init_pluginRegistry();import{createHash as createHash5,randomUUID as randomUUID4}from"node:crypto";import{existsSync as existsSync4,mkdirSync as mkdirSync4,readFileSync as readFileSync7,readdirSync as readdirSync5,realpathSync as realpathSync2,renameSync as renameSync5,rmSync as rmSync5,statSync as statSync5,writeFileSync as writeFileSync4}from"node:fs";import{basename as basename3,dirname as dirname4,isAbsolute as isAbsolute3,join as join9,relative as relative2,resolve as resolve3,sep as sep2}from"node:path";var MAX_REVISION_BYTES=64*1024*1024;function within(root,file){let path7=relative2(root,file);return path7===""||!path7.startsWith(`..${sep2}`)&&path7!==".."&&!isAbsolute3(path7)}function sourceRoot(entry){let candidate=dirname4(entry);for(let depth=0;depth<6;depth++){if(skillManifestPath(candidate))return candidate;let parent=dirname4(candidate);if(parent===candidate)break;candidate=parent}return basename3(dirname4(entry))==="scripts"?dirname4(dirname4(entry)):dirname4(entry)}function readSources(root){let files=[],registry=realpathSync2(pluginHostPaths().registry),bytes=0,visit=(directory,ancestors)=>{let real=realpathSync2(directory);if(!within(root,real)||ancestors.has(real))throw new Error("Plugin snapshot has an external or cyclic directory link");let next=new Set([...ancestors,real]);for(let item of readdirSync5(directory,{withFileTypes:!0}).sort((a,b)=>a.name<b.name?-1:a.name>b.name?1:0)){if([".git","__pycache__","node_modules",".DS_Store"].includes(item.name))continue;let file=join9(directory,item.name);if(within(root,registry)&&within(registry,file))continue;let target=realpathSync2(file);if(!within(root,target))throw new Error(`Plugin snapshot resource leaves the Skill directory: ${file}`);let stat2=statSync5(file);if(stat2.isDirectory()){visit(file,next);continue}if(!stat2.isFile())throw new Error(`Unsupported plugin resource: ${file}`);if(bytes+=stat2.size,bytes>MAX_REVISION_BYTES||files.length>=1e4)throw new Error("Plugin source snapshot exceeds 64 MiB or 10000 files");let content=readFileSync7(file);if(bytes+=content.length-stat2.size,bytes>MAX_REVISION_BYTES)throw new Error("Plugin source snapshot exceeds 64 MiB");files.push({path:relative2(root,file).split(sep2).join("/"),hash:createHash5("sha256").update(content).digest("hex"),bytes:content,executable:(stat2.mode&73)!==0})}};return visit(root,new Set),files}function fingerprint(files,entry){return createHash5("sha256").update(JSON.stringify({entry,files:files.map(file=>[file.path,file.hash,file.executable])})).digest("hex")}function capturePluginRevision(plugin){let registration=findPluginRegistration(plugin);if(!registration)throw new PluginError("PLUGIN_NOT_REGISTERED",`插件未注册: ${plugin}`);let entry=realpathSync2(registration.entry),root=realpathSync2(sourceRoot(entry)),paths=pluginHostPaths();mkdirSync4(paths.installations,{recursive:!0,mode:448});let identityFile=join9(paths.installations,`${plugin}.json`),installation=readRecord(identityFile);if(!installation||installation.root!==root){let proposed={pid:process.pid,root,installationId:randomUUID4()};installation?writeRecord(identityFile,proposed):claimRecord(identityFile,proposed),installation=readRecord(identityFile)}if(!installation||installation.root!==root)throw new Error("Plugin installation changed during capture");let relativeEntry=relative2(root,entry).split(sep2).join("/"),files=readSources(root),revision=fingerprint(files,relativeEntry),revisionRoot=join9(paths.revisions,installation.installationId,revision);if(!existsSync4(revisionRoot)){let temporary=`${revisionRoot}.${randomUUID4()}.tmp`;try{mkdirSync4(temporary,{recursive:!0,mode:448});for(let file of files){let destination=resolve3(temporary,file.path);mkdirSync4(dirname4(destination),{recursive:!0,mode:448}),writeFileSync4(destination,file.bytes,{flag:"wx",mode:file.executable?365:292})}if(fingerprint(readSources(root),relativeEntry)!==revision)throw new PluginError("PLUGIN_SOURCE_CHANGED","插件源码在创建快照时发生变化");mkdirSync4(dirname4(revisionRoot),{recursive:!0,mode:448}),renameSync5(temporary,revisionRoot)}finally{rmSync5(temporary,{recursive:!0,force:!0})}}return{plugin,installationId:installation.installationId,revision,originalRoot:root,entry:join9(revisionRoot,relativeEntry),registration:{...registration,entry:join9(revisionRoot,relativeEntry),source:void 0}}}function prunePluginRevisions(references){let directory=pluginHostPaths().revisions;if(existsSync4(directory))for(let installation of readdirSync5(directory,{withFileTypes:!0})){if(!installation.isDirectory())continue;let root=join9(directory,installation.name);for(let revision of readdirSync5(root,{withFileTypes:!0}))revision.isDirectory()&&!references.has(`${installation.name}/${revision.name}`)&&rmSync5(join9(root,revision.name),{recursive:!0,force:!0});readdirSync5(root).length||rmSync5(root,{recursive:!0,force:!0})}}function restorePluginRevision(plugin,installationId,revision,entry){if(!/^[a-f0-9-]{36}$/.test(installationId)||!/^[a-f0-9]{64}$/.test(revision))throw new Error("Invalid recovery revision");let paths=pluginHostPaths(),registration=findPluginRegistration(plugin),installation=readRecord(join9(paths.installations,`${plugin}.json`));if(!registration||!installation||installation.installationId!==installationId||sourceRoot(realpathSync2(registration.entry))!==installation.root)throw new PluginError("PLUGIN_TASK_INSTALLATION_CHANGED","Task installation was removed or replaced");try{let root=realpathSync2(join9(paths.revisions,installationId,revision)),restoredEntry=resolve3(root,entry);if(!entry||isAbsolute3(entry)||!within(root,restoredEntry)||!within(root,realpathSync2(restoredEntry))||fingerprint(readSources(root),entry)!==revision)throw new Error("Snapshot identity changed");return{plugin,installationId,revision,originalRoot:installation.root,entry:restoredEntry,registration:{...registration,entry:restoredEntry,source:void 0}}}catch{throw new PluginError("PLUGIN_TASK_REVISION_UNAVAILABLE","Task source snapshot is missing or has changed")}}init_pluginHostPaths();init_pluginTaskStore();init_plugin_tasks();init_unified();init_commands();function error(code,message){return Object.assign(new Error(message),{code})}function digest(value){return createHash9("sha256").update(JSON.stringify(value,(_key,item)=>item&&typeof item=="object"&&!Array.isArray(item)?Object.fromEntries(Object.entries(item).sort(([a],[b])=>a<b?-1:a>b?1:0)):item)).digest("hex")}function revisionEntry(revision){return relative5(realpathSync4(join16(pluginHostPaths().revisions,revision.installationId,revision.revision)),realpathSync4(revision.entry)).split(sep4).join("/")}var PluginTaskManager=class{constructor(configuration={},services){this.configuration=configuration;this.services=services;this.store=configuration.store??new PluginTaskStore,this.store.recover(this.host.epoch),this.changes.setMaxListeners(0);let concurrency=configuration.concurrency??4;if(!Number.isInteger(configuration.modelConcurrency??2)||(configuration.modelConcurrency??2)<1||(configuration.modelConcurrency??2)>8)throw new Error("Invalid model concurrency");if(!Number.isInteger(concurrency)||concurrency<1||concurrency>32)throw new Error("Invalid plugin task concurrency");let prune=()=>{this.store.prune(),prunePluginRevisions(new Set([...this.revisionPins.keys(),...this.store.list().map(record2=>`${record2.task.installationId}/${record2.task.revision}`)]))};prune(),this.maintenance=setInterval(()=>{try{prune()}catch(error2){console.error("Plugin task retention failed:",error2)}},3600*1e3),this.maintenance.unref()}host=localPluginExecutionHost();store;changes=new EventEmitter;queue=new Map;running=new Map;resuming=new Map;activeChildren=new Map;closed=!1;maintenance;descriptionsRunning=0;descriptions=new Map;descriptionBytes=0;revisionPins=new Map;recoveries=new Map;pinRevision(revision){let key2=`${revision.installationId}/${revision.revision}`;this.revisionPins.set(key2,(this.revisionPins.get(key2)??0)+1);let released=!1;return()=>{if(released)return;released=!0;let count=this.revisionPins.get(key2)??0;count>1?this.revisionPins.set(key2,count-1):this.revisionPins.delete(key2)}}async describeRevision(revision,signal,options={}){if(signal.throwIfAborted(),this.closed)throw new Error("Plugin description budget is exhausted");let key2=`${revision.installationId}/${revision.revision}`,cached=options.cache?this.descriptions.get(key2):void 0;if(cached)return this.descriptions.delete(key2),this.descriptions.set(key2,cached),structuredClone(cached.value);if(this.descriptionsRunning>=8)throw new Error("Plugin description budget is exhausted");this.descriptionsRunning++;try{let value=await(this.configuration.execute??executeRegisteredPlugin)(revision.plugin,{command:"describe",input:{}},{invoke:async()=>{throw new Error("Plugin declarations cannot invoke host actions")}},{describe:!0,signal,source:"runtime",executionTimeoutMs:1e4,workerPath:this.configuration.workerPath},{registration:revision.registration});signal.throwIfAborted();let bytes=Buffer.byteLength(JSON.stringify(value));if(options.cache&&!this.closed&&bytes<=8*1024*1024){let previous=this.descriptions.get(key2);for(previous&&(this.descriptions.delete(key2),this.descriptionBytes-=previous.bytes);this.descriptions.size>=64||this.descriptionBytes+bytes>8*1024*1024;){let oldest=this.descriptions.keys().next().value;this.descriptionBytes-=this.descriptions.get(oldest).bytes,this.descriptions.delete(oldest)}this.descriptions.set(key2,{value:structuredClone(value),bytes}),this.descriptionBytes+=bytes}return value}finally{this.descriptionsRunning--}}authorize(taskId,scope){if(!taskId.startsWith(`pt_${this.host.hostId}_`))throw error("PLUGIN_TASK_WRONG_HOST","Plugin task belongs to another execution host");let record2=this.store.read(taskId);if(scope.plugin&&record2.task.plugin!==scope.plugin||scope.access==="toolbox"&&record2.access==="public")throw error("PLUGIN_TASK_NOT_FOUND","Plugin task was not found");return record2.task}get(taskId,scope){return this.authorize(taskId,scope)}list(scope,activeOnly=!1,cursor,limit=100){if(!Number.isSafeInteger(limit)||limit<1||limit>200||cursor!==void 0&&typeof cursor!="string")throw new Error("Invalid plugin task list page");let candidates=(activeOnly?this.store.listActive():this.store.list()).filter(record2=>scope.access!=="toolbox"||record2.access!=="public").map(record2=>record2.task).filter(task=>(!scope.plugin||task.plugin===scope.plugin)&&(!activeOnly||isPluginTaskActive(task.state))).sort((a,b)=>b.createdAt-a.createdAt||b.taskId.localeCompare(a.taskId)),match=cursor?.match(/^(\d+):(pt_[0-9a-f-]{36}_[0-9a-f-]{36})$/i);if(cursor!==void 0&&(!match||!Number.isSafeInteger(Number(match[1]))))throw new Error("Invalid plugin task list cursor");let remaining=match?candidates.filter(task=>task.createdAt<Number(match[1])||task.createdAt===Number(match[1])&&task.taskId.localeCompare(match[2])<0):candidates,tasks=[],bytes=0;for(let task of remaining){let size=Buffer.byteLength(JSON.stringify(task));if(tasks.length&&(tasks.length>=limit||bytes+size>384*1024))break;tasks.push(task),bytes+=size}let last=tasks.at(-1);return{tasks,...last&&tasks.length<remaining.length?{nextCursor:`${last.createdAt}:${last.taskId}`}:{}}}start(request,bridge,binding=void 0){if(this.closed)throw error("PLUGIN_TASK_HOST_STOPPING","Plugin execution service is stopping");if(request.kind!==void 0&&!["command","model"].includes(request.kind))throw new Error("Invalid task kind");if(request.kind==="model"){let model=normalizePluginModel(request.input?.request);request={...request,command:"model",input:{request:model},executionTimeoutMs:request.executionTimeoutMs??12e4}}if(!/^[a-z][a-z0-9-]*$/.test(request.plugin)||!/^[a-z][a-z0-9-]*$/.test(request.command))throw new Error("Invalid plugin task command");if(bridge.scope.plugin&&request.plugin!==bridge.scope.plugin)throw error("PLUGIN_COMMAND_NOT_ALLOWED","Plugin task is outside the caller scope");if(!request.input||typeof request.input!="object"||Array.isArray(request.input)||Buffer.byteLength(JSON.stringify(request.input))>256*1024)throw new Error("Plugin task input must be an object within 256 KiB");if(!isAbsolute6(request.cwd)||!statSync9(request.cwd).isDirectory())throw new Error("Plugin task cwd must be an absolute directory");if(!request.environment||typeof request.environment!="object"||Array.isArray(request.environment)||Object.entries(request.environment).some(([key2,value])=>!key2||key2.includes("=")||typeof value!="string"||key2.includes("\0")||value.includes("\0"))||Buffer.byteLength(JSON.stringify(request.environment))>512*1024)throw new Error("Invalid plugin task environment");if(!request.options||typeof request.options!="object"||Array.isArray(request.options)||Object.keys(request.options).some(key2=>!["browser","session","connector","local","transport","timeoutSec"].includes(key2)))throw new Error("Invalid plugin task routing options");for(let key2 of["browser","session","connector"])if(request.options[key2]!==void 0&&(typeof request.options[key2]!="string"||!request.options[key2]))throw new Error("Invalid plugin task target");if(request.options.timeoutSec!==void 0&&(!Number.isFinite(request.options.timeoutSec)||request.options.timeoutSec<=0))throw new Error("Invalid plugin task invocation timeout");if(request.options.local!==void 0&&typeof request.options.local!="boolean")throw new Error("Invalid plugin task local routing option");if(request.options.transport!==void 0&&!["extension","cdp"].includes(request.options.transport))throw new Error("Invalid plugin task transport");if(bridge.scope.bridge==="service"&&request.options.browser&&!decodeBrowserTarget(request.options.browser))throw error("PLUGIN_TASK_TARGET_REQUIRED","Background tasks require a stable global browser ID");if(request.executionTimeoutMs!==void 0&&(!Number.isSafeInteger(request.executionTimeoutMs)||request.executionTimeoutMs<1||request.executionTimeoutMs>2147483647))throw new Error("Invalid plugin task execution deadline");if(request.idempotencyKey!==void 0&&(typeof request.idempotencyKey!="string"||!request.idempotencyKey||request.idempotencyKey.length>128))throw new Error("Invalid plugin task idempotency key");let revision=binding?.revision??(this.configuration.captureRevision??capturePluginRevision)(request.plugin);if(bridge.scope.access==="runtime"&&!binding)throw new Error("Runtime task requires a document binding");if(request.kind!=="model"&&bridge.scope.access==="toolbox"&&!revision.registration.toolbox?.page.commands.includes(request.command))throw error("PLUGIN_COMMAND_NOT_ALLOWED","Tool page cannot invoke this plugin command");request={...request,cwd:realpathSync4(request.cwd),input:JSON.parse(JSON.stringify(request.input))};let requestHash=binding?.recoveryRequestHash??digest([request.kind??"command",request.command,request.input,request.cwd,request.options,request.executionTimeoutMs,revision.revision,binding?.recovery,Object.entries(request.environment).sort(([a],[b])=>a<b?-1:a>b?1:0)]),dedupeKey=binding?.recovery?`resume:${binding.recovery.fromTaskId}`:request.idempotencyKey?`explicit:${revision.installationId}:${bridge.scope.access}:${request.idempotencyKey}`:bridge.scope.access==="toolbox"?`active:${digest(bridge.scope.scopeId??bridge.id)}:${revision.installationId}:${requestHash}`:void 0;if(dedupeKey){let existing=this.store.findDedupe(dedupeKey,request.idempotencyKey===void 0&&binding?.recovery===void 0);if(existing){if(existing.requestHash!==requestHash)throw error("PLUGIN_TASK_IDEMPOTENCY_CONFLICT","Idempotency key refers to a different plugin task request");return{taskId:existing.task.taskId,state:existing.task.state,host:existing.task.host,reused:!0}}}if([...this.queue.values()].filter(item=>(item.request.kind??"command")===(request.kind??"command")).length>=128)throw error("PLUGIN_TASK_QUEUE_FULL","Plugin task queue is full");let now=Date.now(),task={taskId:`pt_${this.host.hostId}_${randomUUID10()}`,host:this.host,plugin:request.plugin,installationId:revision.installationId,revision:revision.revision,command:request.command,kind:request.kind??"command",...binding?.parentTaskId?{parentTaskId:binding.parentTaskId}:{},...binding?.recovery?{resumedFrom:{taskId:binding.recovery.fromTaskId,revision:binding.recovery.fromRevision,checkpointSequence:binding.recovery.sourceCheckpoint.sequence,checkpointVersion:binding.recovery.sourceCheckpoint.version},checkpoint:{sequence:0,version:binding.recovery.checkpoint.version,schemaHash:binding.recovery.checkpoint.schemaHash,updatedAt:now}}:{},input:request.input,source:bridge.scope.source,cwd:request.cwd,state:"queued",sequence:0,createdAt:now,updatedAt:now};if(this.store.write({task,events:[],diagnostics:{executorAttempted:!1,...binding?.page?{page:diagnosticBinding({instanceId:binding.page.instanceId,target:binding.page.target})}:{}},access:bridge.scope.access,...dedupeKey?{dedupeKey}:{},requestHash,execution:{entry:revisionEntry(revision),options:JSON.parse(JSON.stringify(request.options)),bridge:bridge.scope.bridge,executionTimeoutMs:request.executionTimeoutMs},...binding?.recovery?{checkpoint:{...structuredClone(binding.recovery.checkpoint),sequence:0,updatedAt:now}}:{}}),this.update(task.taskId,{state:"queued"}),task.parentTaskId){let children=this.activeChildren.get(task.parentTaskId)??new Set;children.add(task.taskId),this.activeChildren.set(task.parentTaskId,children)}return this.queue.set(task.taskId,{request,revision,bridge,controller:new AbortController,guard:binding?.guard,page:binding?.page,recovery:binding?.recovery,parentIdentity:binding?.parentIdentity,documentTarget:binding?.documentTarget}),queueMicrotask(()=>this.drain()),{taskId:task.taskId,state:"queued",host:this.host}}checkpoint(taskId,scope){return this.authorize(taskId,scope),this.store.read(taskId).checkpoint??null}resume(request,bridge){try{if(this.closed)throw error("PLUGIN_TASK_HOST_STOPPING","Plugin execution service is stopping");if(this.authorize(request.taskId,bridge.scope),bridge.scope.access!=="public"||bridge.scope.bridge!=="service")throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Recovery requires public service-owned routing");if(request.revision!==void 0&&!["original","current"].includes(request.revision))throw new Error("Recovery revision must be original or current");if(!request.environment||typeof request.environment!="object"||Array.isArray(request.environment)||Object.entries(request.environment).some(([key2,value])=>!key2||key2.includes("=")||typeof value!="string"||key2.includes("\0")||value.includes("\0"))||Buffer.byteLength(JSON.stringify(request.environment))>512*1024)throw new Error("Invalid plugin task environment");let intent=digest([request.revision??"original",request.environment]),existing=this.recoveries.get(request.taskId);if(existing){if(existing.intent!==intent)throw error("PLUGIN_TASK_IDEMPOTENCY_CONFLICT","A different recovery request is already in progress");return existing.promise.then(result=>({...result,reused:!0}))}let controller=new AbortController,operation={controller,intent,promise:this.resumeAttempt(request,bridge,controller.signal)};return this.recoveries.set(request.taskId,operation),operation.promise.finally(()=>this.recoveries.delete(request.taskId)).catch(()=>{}),operation.promise}catch(cause){return Promise.reject(cause)}}async resumeAttempt(request,bridge,signal){let task=this.authorize(request.taskId,bridge.scope),record2=this.store.read(task.taskId);if(!["error","cancelled","interrupted"].includes(task.state)||task.kind==="model"||task.parentTaskId||record2.access!=="public"||record2.execution?.bridge!=="service"||!record2.checkpoint)throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Recovery requires a terminal root command with a checkpoint and service-owned routing");this.assertExecutionTreeStopped(task.taskId);let source=restorePluginRevision(task.plugin,task.installationId,task.revision,record2.execution.entry),revision=request.revision==="current"?(this.configuration.captureRevision??capturePluginRevision)(task.plugin):source;if(revision.installationId!==task.installationId)throw error("PLUGIN_TASK_INSTALLATION_CHANGED","Recovery cannot change the plugin installation");restorePluginRevision(task.plugin,task.installationId,revision.revision,revisionEntry(revision));let releaseSource=this.pinRevision(source),releaseTarget=this.pinRevision(revision);try{let requestHash=digest([request.revision??"original",task,record2.checkpoint,record2.execution,revision.revision,request.environment]),existing=this.store.findDedupe(`resume:${task.taskId}`);if(existing){if(existing.requestHash!==requestHash)throw error("PLUGIN_TASK_IDEMPOTENCY_CONFLICT","Source task already has a different recovery attempt");return{taskId:existing.task.taskId,state:existing.task.state,host:existing.task.host,reused:!0}}let sourceCommands=(await this.describeRevision(source,signal)).taskRecovery?.commands,sourceDefinition=sourceCommands&&Object.hasOwn(sourceCommands,task.command)?sourceCommands[task.command]:void 0;if(signal.throwIfAborted(),!sourceDefinition||sourceDefinition.checkpointVersion!==record2.checkpoint.version||digest(sourceDefinition.schema)!==record2.checkpoint.schemaHash)throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Checkpoint does not match the retained recovery declaration");let checkpoint=structuredClone(record2.checkpoint),declarationHash=pluginRecoverySignature(sourceDefinition);if(revision.revision!==source.revision){let targetCommands=(await this.describeRevision(revision,signal)).taskRecovery?.commands,target=targetCommands&&Object.hasOwn(targetCommands,task.command)?targetCommands[task.command]:void 0;if(signal.throwIfAborted(),!target)throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Target revision does not declare recovery for the original command");if(declarationHash=pluginRecoverySignature(target),target.checkpointVersion===checkpoint.version){if(digest(target.schema)!==digest(sourceDefinition.schema))throw error("PLUGIN_CHECKPOINT_MIGRATION_UNSUPPORTED","Checkpoint schema changed without a version increment")}else{if(!target.migrateFrom?.some(from=>from.version===checkpoint.version&&digest(from.schema)===digest(sourceDefinition.schema)))throw error("PLUGIN_CHECKPOINT_MIGRATION_UNSUPPORTED","Target revision does not declare this checkpoint format");if(this.descriptionsRunning>=8)throw error("PLUGIN_CHECKPOINT_MIGRATION_BUSY","Checkpoint migration budget is exhausted");this.descriptionsRunning++;let result;try{result=await(this.configuration.execute??executeRegisteredPlugin)(revision.plugin,{command:task.command,input:{checkpoint,fromRevision:source.revision,toRevision:revision.revision}},{invoke:async()=>{throw error("PLUGIN_CHECKPOINT_MIGRATION_HOST_CALL","Checkpoint migration cannot invoke host actions")}},{migration:!0,expectedRecovery:declarationHash,signal,source:"runtime",cwd:task.cwd,environment:request.environment,executionTimeoutMs:1e4,workerPath:this.configuration.workerPath},{registration:revision.registration})}finally{this.descriptionsRunning--}signal.throwIfAborted();let json2=JSON.stringify(result);if(json2===void 0||Buffer.byteLength(json2)>64*1024)throw error("PLUGIN_TASK_CHECKPOINT_INVALID","Migrated checkpoint must be JSON within 64 KiB");checkpoint={...checkpoint,version:target.checkpointVersion,schemaHash:digest(target.schema),data:JSON.parse(json2)}}}return signal.throwIfAborted(),restorePluginRevision(task.plugin,task.installationId,task.revision,record2.execution.entry),restorePluginRevision(task.plugin,task.installationId,revision.revision,revisionEntry(revision)),this.assertExecutionTreeStopped(task.taskId),this.start({plugin:task.plugin,command:task.command,input:task.input,cwd:task.cwd,environment:request.environment,options:record2.execution.options,executionTimeoutMs:record2.execution.executionTimeoutMs},bridge,{revision,recoveryRequestHash:requestHash,recovery:{fromTaskId:task.taskId,fromRevision:source.revision,declarationHash,sourceCheckpoint:{sequence:record2.checkpoint.sequence,version:record2.checkpoint.version,schemaHash:record2.checkpoint.schemaHash,updatedAt:record2.checkpoint.updatedAt},checkpoint}})}finally{releaseTarget(),releaseSource()}}assertExecutionTreeStopped(taskId){let records=this.store.list(),visited=new Set,pending=[taskId];for(;pending.length;){let id=pending.pop();if(visited.has(id))continue;visited.add(id);let record2=records.find(record3=>record3.task.taskId===id);if(!record2)throw error("PLUGIN_TASK_EXECUTION_STILL_ACTIVE","Previous execution ownership record is unavailable");if(pending.push(...records.filter(record3=>record3.task.parentTaskId===id).map(record3=>record3.task.taskId)),isPluginTaskActive(record2.task.state)||pluginTaskExecutionUnconfirmed(record2))throw error("PLUGIN_TASK_EXECUTION_STILL_ACTIVE","Previous execution or descendant cleanup cannot be confirmed")}}cancel(taskId,scope){let task=this.authorize(taskId,scope);if(task.state==="queued")return this.queue.delete(taskId),this.update(taskId,{state:"cancelled",error:{code:"PLUGIN_EXECUTION_CANCELLED",message:"Plugin task cancelled before execution"}});if(task.state==="running"){let updated=this.update(taskId,{state:"cancelling"});return this.running.get(taskId)?.controller.abort(),updated}return task}async events(taskId,scope,cursor,waitMs=0,signal){let task=this.authorize(taskId,scope);if(!Number.isSafeInteger(waitMs)||waitMs<0||waitMs>25e3)throw new Error("Invalid plugin task event wait");let current=this.store.events(taskId,cursor);return waitMs&&cursor===task.sequence&&isPluginTaskActive(task.state)?(await new Promise((resolve7,reject)=>{let finish=()=>{cleanup(),resolve7()},abort=()=>{cleanup(),reject(signal?.reason??new Error("Plugin task subscription closed"))},timer=setTimeout(finish,waitMs),cleanup=()=>{clearTimeout(timer),this.changes.off(taskId,finish),signal?.removeEventListener("abort",abort)};this.changes.once(taskId,finish),signal?.addEventListener("abort",abort,{once:!0}),signal?.aborted?abort():this.store.read(taskId).task.sequence!==cursor&&finish()}),this.store.events(taskId,cursor)):current}resultChunk(taskId,scope,index){return this.authorize(taskId,scope),this.store.resultChunk(taskId,index)}update(taskId,update,type="state"){let task=this.store.update(taskId,update,type);if(!isPluginTaskActive(task.state)&&task.parentTaskId){let children=this.activeChildren.get(task.parentTaskId);children?.delete(taskId),children?.size||this.activeChildren.delete(task.parentTaskId)}return this.changes.emit(taskId),task}drain(){if(!this.closed){for(let[taskId,resume]of this.resuming){let parent=this.running.get(taskId);if(!parent){this.resuming.delete(taskId),resume();continue}let limit=parent.kind==="model"?this.configuration.modelConcurrency??2:this.configuration.concurrency??4;[...this.running.values()].filter(item=>item.kind===parent.kind&&!item.waiting).length<limit&&(this.resuming.delete(taskId),resume())}for(let[taskId,pending]of this.queue){let kind=pending.request.kind??"command",limit=kind==="model"?this.configuration.modelConcurrency??2:this.configuration.concurrency??4;if([...this.running.values()].filter(item=>item.kind===kind&&!item.waiting).length>=limit)continue;this.queue.delete(taskId),this.update(taskId,{state:"running"});let completed=this.execute(taskId,pending).finally(()=>{this.running.delete(taskId),this.drain()});this.running.set(taskId,{controller:pending.controller,completed,kind,waiting:0})}}}async execute(taskId,{request,revision,bridge,controller,guard,page,recovery,parentIdentity,documentTarget}){let client=bridge.scope.bridge==="service"?new UnifiedClient:void 0;try{await guard?.();let identity=Object.freeze({...createPluginInvocationIdentity(request.plugin,bridge.scope.source,parentIdentity),taskId,installationId:revision.installationId,revision:revision.revision,...page?{page}:{},...recovery?{recovery}:{}});{let record2=this.store.read(taskId);record2.diagnostics={...record2.diagnostics,invocationId:identity.invocationId,executorAttempted:!0},this.store.write(record2)}let restrictedTarget=page?.target??documentTarget,diagnosticUpdates=0,recoveryDescription,modelOwner=page?.instanceId??parentIdentity?.taskId,conversation=request.kind==="model"&&request.input.request.conversation;if(conversation&&!modelOwner)throw error("PLUGIN_MODEL_CONVERSATION_SCOPE","Model conversations require a bound document or calling command");let result=await(this.configuration.execute??(conversation&&this.services?.model?this.services.model.bind(this.services,modelOwner):executeRegisteredPlugin))(request.plugin,{command:request.command,input:request.input},{invocation:identity,source:bridge.scope.source,invoke:async(action,payload={},options={},onProgress)=>{if(controller.signal.throwIfAborted(),await guard?.(),controller.signal.throwIfAborted(),action==="plugin_invoke_child"){let name=payload.plugin,invocation=payload.invocation;if(typeof name!="string"||!invocation||typeof invocation.command!="string"||!invocation.input||typeof invocation.input!="object"||Array.isArray(invocation.input))throw new Error("Invalid nested plugin invocation");this.assertRouting(options,request.options);let ancestor=taskId,depth=0;for(;ancestor;){let task=this.store.read(ancestor).task;if(task.plugin===name)throw error("PLUGIN_INVOCATION_CYCLE","Plugin invocation forms a cycle");if(++depth>=16)throw error("PLUGIN_INVOCATION_DEPTH","Plugin invocation depth exceeds 16");ancestor=task.parentTaskId}this.assertChildBudget(taskId);let childRevision=(this.configuration.captureRevision??capturePluginRevision)(name),childBridge={...bridge,scope:{...bridge.scope,access:"public",plugin:name}},child=this.start({...request,plugin:name,kind:"command",command:invocation.command,input:invocation.input,options:{...request.options,...options},idempotencyKey:void 0},childBridge,{revision:childRevision,guard,parentTaskId:taskId,parentIdentity:identity,documentTarget:restrictedTarget});return this.awaitChild(taskId,child.taskId,childBridge.scope,controller.signal)}if(["plugin_state","plugin_model","plugin_page","plugin_task_checkpoint"].includes(action)&&payload.plugin!==request.plugin)throw error("PLUGIN_CALLER_SCOPE_MISMATCH","Plugin-owned services require the executing plugin identity");if(action==="plugin_task_checkpoint"){if(request.kind==="model"||page||bridge.scope.access!=="public"||bridge.scope.bridge!=="service")throw error("PLUGIN_TASK_RECOVERY_UNAVAILABLE","Checkpoints require a command with service-owned routing");let json2=JSON.stringify(payload.data);if(!Number.isSafeInteger(payload.expectedSequence)||payload.expectedSequence<0||json2===void 0||Buffer.byteLength(json2)>64*1024)throw new Error("Checkpoint must have a CAS sequence and fit within 64 KiB");let data=JSON.parse(json2),commands=(await(recoveryDescription??=this.describeRevision(revision,controller.signal))).taskRecovery?.commands,declaration=commands&&Object.hasOwn(commands,request.command)?commands[request.command]:void 0;if(controller.signal.throwIfAborted(),declaration&&payload.expectedDeclaration!==pluginRecoverySignature(declaration))throw error("PLUGIN_RECOVERY_DECLARATION_CHANGED","Checkpoint declaration differs from the executing command");if(!declaration)throw error("PLUGIN_TASK_CHECKPOINT_INVALID","Checkpoint is undeclared");let current=this.store.read(taskId);if(current.task.state!=="running"||payload.expectedSequence!==(current.checkpoint?.sequence??0))throw error("PLUGIN_TASK_CHECKPOINT_CONFLICT","Checkpoint sequence or execution state changed");let checkpoint={data,version:declaration.checkpointVersion,schemaHash:digest(declaration.schema),sequence:payload.expectedSequence+1,updatedAt:Date.now()};return this.store.update(taskId,{checkpoint:{sequence:checkpoint.sequence,version:checkpoint.version,schemaHash:checkpoint.schemaHash,updatedAt:checkpoint.updatedAt}},"checkpoint",{checkpoint}),this.changes.emit(taskId),checkpoint}if(action==="plugin_state"){if(page&&payload.instanceId!==void 0&&payload.instanceId!==page.instanceId)throw error("PLUGIN_PAGE_TARGET_CHANGED","State access cannot change the bound page");if(!this.services)throw new Error("Plugin host services are unavailable");return this.services.state(request.plugin,{...payload,...page?{instanceId:page.instanceId}:{}},bridge,request.options,{revision,signal:controller.signal,expectedTarget:restrictedTarget})}if(action==="plugin_model"){if(payload.operation==="capabilities")return pluginModelCapabilities();if(payload.operation==="list"){if(!this.services)throw new Error("Plugin host services are unavailable");return this.services.listModels(payload.provider)}if(payload.operation!=="run")throw new Error("Plugin commands use model.run; detached model tasks require an explicit task entry");this.assertChildBudget(taskId);let model=normalizePluginModel(payload.request),started=this.start({...request,kind:"model",command:"model",input:{request:model},idempotencyKey:void 0},bridge,{revision,guard,page,parentTaskId:taskId,parentIdentity:identity,documentTarget:restrictedTarget});return this.awaitChild(taskId,started.taskId,bridge.scope,controller.signal,onProgress)}if(action==="plugin_page"){if(!this.services)throw new Error("Plugin host services are unavailable");if(page&&!(payload.operation==="activation"&&payload.enabled===void 0)&&(!["send","get","events"].includes(String(payload.operation))||payload.instanceId!==page.instanceId))throw error("PLUGIN_PAGE_TARGET_CHANGED","Runtime tasks can only access their bound document");return this.services.page(request.plugin,payload,bridge,request.options,{cwd:request.cwd,environment:request.environment,signal:controller.signal,revision,expectedTarget:restrictedTarget,parentIdentity:identity})}if(restrictedTarget&&!["send_request","send_mtop_request","browser_list","connector_list"].includes(action))throw error("PLUGIN_PAGE_TARGET_CHANGED","Runtime tasks must access the document through context.page");if(this.assertRouting(options,request.options),client){if(COMMAND_MAP.has(action)&&!["browser_list","connector_list","agent_chat_run"].includes(action)&&!request.options.browser)throw error("PLUGIN_TASK_TARGET_REQUIRED","Background browser tasks require an explicit browser target");return client.invoke(action,payload,{...request.options,...options})}return bridge.invoke(action,payload,{...request.options,...options})}},{...request.kind==="model"?{model:request.input.request}:{},recovery:recovery!==void 0,onDiagnostic:metadata=>{let record2=this.store.read(taskId);if(++diagnosticUpdates>64){record2.diagnostics?.updatesDropped||(record2.diagnostics={...record2.diagnostics,updatesDropped:!0},this.store.write(record2));return}let model=normalizeModelDiagnostic(metadata,record2.diagnostics?.model);JSON.stringify(model)!==JSON.stringify(record2.diagnostics?.model)&&(this.store.update(taskId,{},"diagnostic",{diagnostics:{...record2.diagnostics,model}}),this.changes.emit(taskId))},onExecutorStopped:()=>{let record2=this.store.read(taskId);record2.diagnostics={...record2.diagnostics,executorStoppedAt:Date.now()},this.store.write(record2)},onExecutorStarted:owner=>{let record2=this.store.read(taskId);record2.executorOwner=owner,this.store.write(record2)},...request.options,cwd:request.cwd,environment:request.environment,signal:controller.signal,source:bridge.scope.source,access:bridge.scope.access,executionTimeoutMs:request.executionTimeoutMs,workerPath:this.configuration.workerPath,onProgress:progress=>{if(Buffer.byteLength(JSON.stringify(progress))>64*1024)throw new Error("Plugin task progress exceeds 64 KiB");this.update(taskId,{progress},"progress")}},{registration:revision.registration});await guard?.(),request.kind==="model"&&(result=validatePluginModelResult(request.input.request,result)),await this.stopChildren(taskId),!page&&request.kind!=="model"&&await this.services?.releaseModels?.(taskId);let reference=this.store.saveResult(taskId,result);this.update(taskId,{state:"success",result:reference})}catch(cause){await this.stopChildren(taskId);let failureCause=cause;if(!page&&request.kind!=="model")try{await this.services?.releaseModels?.(taskId)}catch(cleanupError){failureCause=cleanupError}let failure2=serializePluginExecutionError(failureCause),interrupted=this.closed||failure2.code==="PLUGIN_TASK_BRIDGE_UNAVAILABLE";this.update(taskId,{state:interrupted?"interrupted":failure2.code==="PLUGIN_EXECUTION_CANCELLED"?"cancelled":"error",error:failure2})}finally{client?.disconnect()}}assertRouting(options,bound){if(Object.keys(options).some(key2=>!["browser","connector","session","local","transport","timeoutSec"].includes(key2)))throw error("PLUGIN_TASK_TARGET_CHANGED","Plugin task cannot override internal routing options");for(let key2 of["browser","connector","session","local","transport"])if(options[key2]!==void 0&&options[key2]!==bound[key2])throw error("PLUGIN_TASK_TARGET_CHANGED","Plugin task cannot change its bound browser routing")}assertChildBudget(parent){if(!this.running.get(parent)?.waiting&&[...this.running.values()].filter(item=>item.waiting).length>=32)throw error("PLUGIN_TASK_NESTING_BUDGET","Too many suspended plugin invocations")}async awaitChild(parent,child,scope,signal,onProgress){let running=this.running.get(parent);running&&running.waiting++,queueMicrotask(()=>this.drain());let cancel=()=>this.cancel(child,scope);signal.addEventListener("abort",cancel,{once:!0}),signal.aborted&&cancel();try{return await this.waitResult(child,scope,progress=>{progress!==void 0&&!signal.aborted&&onProgress?.(progress),isPluginTaskActive(this.store.read(parent).task.state)&&this.update(parent,{progress},"progress")})}finally{signal.removeEventListener("abort",cancel),running?.waiting===1&&!signal.aborted&&!this.closed?await new Promise(resolve7=>{let resume=()=>{this.resuming.delete(parent),signal.removeEventListener("abort",resume),running.waiting--,resolve7()};this.resuming.set(parent,resume),signal.addEventListener("abort",resume,{once:!0}),this.drain()}):running&&running.waiting--}}async stopChildren(taskId){let children=[...this.activeChildren.get(taskId)??[]];for(let child of children)this.cancel(child,{access:"public",source:"runtime",bridge:"service"});await Promise.allSettled(children.map(child=>this.running.get(child)?.completed))}async waitResult(taskId,scope,onProgress){let cursor=0;for(;;){let page=await this.events(taskId,scope,cursor,25e3);cursor=page.nextCursor;for(let event of page.events)event.type==="progress"&&onProgress?.(event.progress);if(page.task.state==="success"){let chunks=[];for(let index=0;index<page.task.result.chunks;index++)chunks.push(Buffer.from(this.resultChunk(taskId,scope,index).data,"base64"));let bytes=Buffer.concat(chunks);if(bytes.length!==page.task.result.bytes||createHash9("sha256").update(bytes).digest("hex")!==page.task.result.sha256)throw new Error("Model task result integrity check failed");return JSON.parse(bytes.toString("utf8")).value}if(!isPluginTaskActive(page.task.state))throw Object.assign(pluginExecutionError(page.task.error??{message:"Model task failed"}),{childTaskId:taskId})}}async close(){this.closed=!0,this.descriptions.clear(),this.descriptionBytes=0;for(let resume of[...this.resuming.values()])resume();for(let operation of this.recoveries.values())operation.controller.abort();clearInterval(this.maintenance),await Promise.allSettled([...this.recoveries.values()].map(operation=>operation.promise));for(let taskId of this.queue.keys())this.update(taskId,{state:"interrupted",error:{code:"PLUGIN_TASK_HOST_STOPPED",message:"Plugin execution service stopped"}});this.queue.clear();for(let{controller}of this.running.values())controller.abort();await Promise.allSettled([...[...this.running.values()].map(item=>item.completed)])}};init_pluginStateDefinition();init_pluginRegistry();init_pluginHostPaths();import{readFileSync as readFileSync14}from"node:fs";import{join as join18}from"node:path";init_pluginHostPaths();init_pluginHostFiles();import{createHash as createHash10}from"node:crypto";import{existsSync as existsSync8,readFileSync as readFileSync13,readdirSync as readdirSync7,statSync as statSync10,rmSync as rmSync12}from"node:fs";import{join as join17,sep as sep5}from"node:path";function fail(code,message){throw Object.assign(new Error(message),{code})}function schemaHash(definition){return createHash10("sha256").update(JSON.stringify({schema:definition.schema,storage:definition.storage},(_key,value)=>value&&typeof value=="object"&&!Array.isArray(value)?Object.fromEntries(Object.entries(value).sort(([a],[b])=>a<b?-1:a>b?1:0)):value)).digest("hex")}function key(value){(typeof value!="string"||!value.length||Buffer.byteLength(value)>512||["__proto__","constructor","prototype"].includes(value))&&fail("PLUGIN_STATE_INVALID","Invalid state key")}var PluginStateStore=class{constructor(directory=pluginHostPaths().state,now=Date.now){this.directory=directory;this.now=now}memory=new Map;memoryBytes=0;memoryRevisionFloor=0;clearMemory(){this.memory.clear(),this.memoryBytes=0,this.memoryRevisionFloor=0}valueBytes=new WeakMap;memorySize(document){let bytes=Buffer.byteLength(JSON.stringify({...document,entries:{}})),count=0;for(let[name,entry]of Object.entries(document.entries)){let valueBytes=typeof entry.value=="object"&&entry.value!==null?this.valueBytes.get(entry.value):void 0;valueBytes===void 0&&(valueBytes=Buffer.byteLength(JSON.stringify(entry.value)),typeof entry.value=="object"&&entry.value!==null&&this.valueBytes.set(entry.value,valueBytes)),bytes+=Buffer.byteLength(JSON.stringify(name))+1+Buffer.byteLength(JSON.stringify({...entry,value:null}))-4+valueBytes+(count++?1:0)}return bytes}saveMemory(path7,document,bytes=this.memorySize(document)){let total=this.memoryBytes-(this.memory.get(path7)?.bytes??0)+bytes;(total>128*1024*1024||!this.memory.has(path7)&&this.memory.size>=512)&&fail("PLUGIN_STATE_QUOTA","Memory state exceeds its host limits"),this.memory.set(path7,{document,bytes}),this.memoryBytes=total}path(namespace){(!/^[a-f0-9-]{36}$/i.test(namespace.installationId)||!/^[a-z][a-z0-9-]{0,63}$/.test(namespace.collection))&&fail("PLUGIN_STATE_INVALID","Invalid state namespace");let id=createHash10("sha256").update(namespace.scope).digest("hex");return join17(this.directory,namespace.installationId,`${namespace.collection}-${id}.json`)}read(namespace,definition){try{let file=this.path(namespace),saved=this.memory.get(file);if((definition.storage!=="memory"&&saved||definition.storage==="memory"&&!saved&&existsSync8(file))&&fail("PLUGIN_STATE_STORAGE_CHANGED","Switching between memory and disk state requires a new collection or namespace"),definition.storage==="memory"&&!saved){if(this.memory.size>=512){this.memoryRevisionFloor=Math.max(this.memoryRevisionFloor,...[...this.memory.values()].map(entry=>entry.document.revision))+1;for(let[path7,entry]of this.memory)Object.keys(entry.document.entries).length||(this.memory.delete(path7),this.memoryBytes-=entry.bytes)}throw Object.assign(new Error("Not found"),{code:"ENOENT"})}let document=saved?{...saved.document,entries:{...saved.document.entries}}:JSON.parse(readFileSync13(file,"utf8"));if(!Number.isSafeInteger(document.revision)||!Number.isSafeInteger(document.schemaVersion)||!document.entries||!Array.isArray(document.commits))throw new Error("Invalid state document");if(definition.storage!=="persistent"){let entries=Object.fromEntries(Object.entries(document.entries).filter(([,entry])=>entry.expiresAt===void 0||entry.expiresAt>this.now()));Object.keys(entries).length!==Object.keys(document.entries).length&&(document.entries=entries,document.revision++,definition.storage==="memory"?this.saveMemory(file,document):writePluginHostFile(file,JSON.stringify(document)))}return document}catch(error2){if(error2.code!=="ENOENT")throw error2;return{revision:definition.storage==="memory"?this.memoryRevisionFloor:0,schemaVersion:definition.schemaVersion,schemaHash:schemaHash(definition),storage:definition.storage,scope:namespace.scope,entries:{},commits:[]}}}entry(document,name,now){let entry=Object.hasOwn(document.entries,name)?document.entries[name]:void 0;return entry&&(entry.expiresAt===void 0||entry.expiresAt>now)?{key:name,exists:!0,version:`v:${entry.revision}`,value:document.storage==="memory"?structuredClone(entry.value):entry.value,...entry.expiresAt===void 0?{}:{expiresAt:entry.expiresAt}}:{key:name,exists:!1,version:`a:${document.revision}`}}discardScope(installationId,scope){if(!/^[a-f0-9-]{36}$/i.test(installationId)||!scope.startsWith("instance:pi_"))throw new Error("Only ended instance scopes can be discarded");let directory=join17(this.directory,installationId),memorySuffix=`-${createHash10("sha256").update(scope).digest("hex")}.json`;for(let[path7,entry]of this.memory)path7.startsWith(directory+sep5)&&path7.endsWith(memorySuffix)&&(this.memory.delete(path7),this.memoryBytes-=entry.bytes);let suffix=`-${createHash10("sha256").update(scope).digest("hex")}.json`,names;try{names=readdirSync7(directory)}catch(error2){if(error2.code==="ENOENT")return;throw error2}for(let name of names)name.endsWith(suffix)&&rmSync12(join17(directory,name),{force:!0})}prune(skipFile){for(let[path7,saved]of this.memory){let document={...saved.document,entries:{...saved.document.entries}};for(let[key2,entry]of Object.entries(document.entries))entry.expiresAt!==void 0&&entry.expiresAt<=this.now()&&delete document.entries[key2];Object.keys(document.entries).length!==Object.keys(saved.document.entries).length&&(document.revision++,this.saveMemory(path7,document))}let installations;try{installations=readdirSync7(this.directory)}catch(error2){if(error2.code==="ENOENT")return;throw error2}for(let installation of installations)if(/^[a-f0-9-]{36}$/i.test(installation))for(let name of readdirSync7(join17(this.directory,installation))){if(!name.endsWith(".json"))continue;let file=join17(this.directory,installation,name);if(file!==skipFile)try{let document=JSON.parse(readFileSync13(file,"utf8"));if(document.storage!=="cache")continue;let entries=Object.fromEntries(Object.entries(document.entries).filter(([,value])=>value.expiresAt===void 0||value.expiresAt>this.now()));if(Object.keys(entries).length===Object.keys(document.entries).length)continue;writePluginHostFile(file,JSON.stringify({...document,revision:document.revision+1,entries}))}catch(error2){console.error(`Cannot maintain plugin cache ${file}:`,error2)}}}get(namespace,definition,name){key(name);let document=this.read(namespace,definition);return{...this.entry(document,name,this.now()),revision:document.revision,schemaVersion:document.schemaVersion}}getMany(namespace,definition,names){(!Array.isArray(names)||names.length<1||names.length>128||new Set(names).size!==names.length)&&fail("PLUGIN_STATE_INVALID","Expected 1 to 128 unique state keys"),names.forEach(name=>key(name));let document=this.read(namespace,definition),now=this.now(),entries=names.map(name=>this.entry(document,name,now));return Buffer.byteLength(JSON.stringify(entries))>1024*1024&&fail("PLUGIN_STATE_RESPONSE_TOO_LARGE","State batch exceeds 1 MiB; read fewer keys"),{entries,revision:document.revision,schemaVersion:document.schemaVersion}}list(namespace,definition,input={}){let document=this.read(namespace,definition),limit=input.limit??100;(!Number.isSafeInteger(limit)||limit<1||limit>200||input.cursor!==void 0&&typeof input.cursor!="string")&&fail("PLUGIN_STATE_INVALID","Invalid state list cursor or limit"),input.expectedRevision!==void 0&&input.expectedRevision!==document.revision&&fail("PLUGIN_STATE_CONFLICT","State list changed; restart pagination");let now=this.now(),candidates=Object.keys(document.entries).sort().filter(name=>(input.cursor===void 0||name>input.cursor)&&this.entry(document,name,now).exists),entries=[],bytes=0;for(let name of candidates){let entry=this.entry(document,name,now),size=Buffer.byteLength(JSON.stringify(entry));if(entries.length&&(entries.length>=limit||bytes+size>256*1024))break;entries.push(entry),bytes+=size}return{entries,revision:document.revision,schemaVersion:document.schemaVersion,...entries.length<candidates.length?{nextCursor:entries.at(-1).key}:{}}}transact(namespace,definition,input){(!input||!Array.isArray(input.operations)||input.operations.length>128||Buffer.byteLength(JSON.stringify(input))>512*1024)&&fail("PLUGIN_STATE_INVALID","State transaction exceeds its limits");let document=this.read(namespace,definition),hash=createHash10("sha256").update(JSON.stringify({input,schemaVersion:definition.schemaVersion,schema:definition.schema})).digest("hex");if(input.idempotencyKey!==void 0){(typeof input.idempotencyKey!="string"||!input.idempotencyKey.length||input.idempotencyKey.length>128)&&fail("PLUGIN_STATE_INVALID","Invalid transaction idempotency key");let existing=document.commits.find(item=>item.id===input.idempotencyKey);if(existing)return existing.hash!==hash&&fail("PLUGIN_STATE_IDEMPOTENCY_CONFLICT","Transaction key refers to different data or schema"),{revision:existing.revision,schemaVersion:document.schemaVersion,reused:!0}}input.expectedRevision!==void 0&&input.expectedRevision!==document.revision&&fail("PLUGIN_STATE_CONFLICT","Collection revision changed"),document.schemaVersion===definition.schemaVersion&&document.schemaHash!==schemaHash(definition)&&fail("PLUGIN_STATE_SCHEMA_VERSION_REQUIRED","Changing the state schema or storage policy requires a new schemaVersion"),document.schemaVersion>definition.schemaVersion&&fail("PLUGIN_STATE_SCHEMA_DOWNGRADE","An older plugin revision cannot write a newer state schema"),document.schemaVersion!==definition.schemaVersion&&(input.migrateFromVersion!==document.schemaVersion||input.expectedRevision===void 0)&&fail("PLUGIN_STATE_MIGRATION_REQUIRED","Schema migration requires the previous schema version and collection revision"),input.migrateFromVersion!==void 0&&input.migrateFromVersion!==document.schemaVersion&&fail("PLUGIN_STATE_CONFLICT","State schema changed");let now=this.now(),names=new Set,entries=Object.fromEntries(Object.entries(document.entries).filter(([,value])=>value.expiresAt===void 0||value.expiresAt>now).map(([name,value])=>[name,{...value}])),revision=document.revision+1;if(Number.isSafeInteger(revision)||fail("PLUGIN_STATE_QUOTA","State revision limit reached"),document.storage!==definition.storage)for(let entry of Object.values(entries))entry.revision=revision,definition.storage!=="persistent"?entry.expiresAt=now+(definition.ttlMs??864e5):delete entry.expiresAt;for(let operation of input.operations){if(key(operation.key),(names.has(operation.key)||!["put","delete"].includes(operation.type)||operation.expectedVersion!==this.entry(document,operation.key,now).version)&&fail("PLUGIN_STATE_CONFLICT","State entry version changed or key repeated"),names.add(operation.key),operation.type==="delete"){delete entries[operation.key];continue}let json2=JSON.stringify(operation.value);(json2===void 0||Buffer.byteLength(json2)>256*1024)&&fail("PLUGIN_STATE_INVALID","State value must be JSON within 256 KiB");let ttl=operation.ttlMs??definition.ttlMs??864e5;definition.storage==="persistent"&&operation.ttlMs!==void 0&&fail("PLUGIN_STATE_INVALID","Persistent state does not accept TTL"),(!Number.isSafeInteger(ttl)||ttl<1||ttl>7*864e5)&&fail("PLUGIN_STATE_INVALID","Invalid cache TTL"),entries[operation.key]={revision,value:definition.storage==="memory"?structuredClone(operation.value):operation.value,...definition.storage!=="persistent"?{expiresAt:now+ttl}:{}}}let commit={revision,schemaVersion:definition.schemaVersion,schemaHash:schemaHash(definition),storage:definition.storage,scope:namespace.scope,entries,commits:[...document.commits,...input.idempotencyKey?[{id:input.idempotencyKey,hash,revision}]:[]].slice(-64)},content=definition.storage==="memory"?"":JSON.stringify(commit),bytes=definition.storage==="memory"?this.memorySize(commit):Buffer.byteLength(content);(Object.keys(entries).length>(definition.maxEntries??1024)||bytes>(definition.maxBytes??8*1024*1024))&&fail("PLUGIN_STATE_QUOTA","State collection quota exceeded");let file=this.path(namespace);if(definition.storage==="memory")return this.saveMemory(file,commit,bytes),{revision,schemaVersion:definition.schemaVersion};let total=Buffer.byteLength(content);try{for(let name of readdirSync7(join17(this.directory,namespace.installationId))){let path7=join17(this.directory,namespace.installationId,name);path7!==file&&name.endsWith(".json")&&(total+=statSync10(path7).size)}}catch(error2){if(error2.code!=="ENOENT")throw error2}if(total>64*1024*1024){this.prune(file),total=Buffer.byteLength(content);for(let name of readdirSync7(join17(this.directory,namespace.installationId))){let path7=join17(this.directory,namespace.installationId,name);path7!==file&&name.endsWith(".json")&&(total+=statSync10(path7).size)}}return total>64*1024*1024&&fail("PLUGIN_STATE_QUOTA","Plugin state exceeds 64 MiB"),writePluginHostFile(file,content),{revision,schemaVersion:definition.schemaVersion}}};var PluginStateManager=class{constructor(tasks){this.tasks=tasks;this.store.prune(),this.maintenance=setInterval(()=>{try{this.store.prune()}catch(error2){console.error("Plugin cache maintenance failed:",error2)}},6e4),this.maintenance.unref()}store=new PluginStateStore;descriptions=new Map;pending=new Set;controller=new AbortController;resolveTarget;maintenance;async call(plugin,input,bridge,options={},binding){if(this.controller.signal.throwIfAborted(),binding?.signal?.throwIfAborted(),!/^[a-z][a-z0-9-]*$/.test(plugin)||bridge.scope.plugin&&bridge.scope.plugin!==plugin||!input||typeof input!="object"||Array.isArray(input))throw new Error("Invalid plugin state scope");let revision=binding?.revision??capturePluginRevision(plugin),id=`${revision.installationId}/${revision.revision}`,description=this.descriptions.get(id);if(!description){this.descriptions.size>=64&&this.descriptions.delete(this.descriptions.keys().next().value);let release=this.tasks.pinRevision(revision);description=this.tasks.describeRevision(revision,this.controller.signal,{cache:!0}).finally(release),this.descriptions.set(id,description),this.pending.add(description),description.finally(()=>this.pending.delete(description)).catch(()=>{}),description.catch(()=>{this.descriptions.get(id)===description&&this.descriptions.delete(id)})}let declaration=(await description).stateStorage;this.controller.signal.throwIfAborted(),binding?.signal?.throwIfAborted();let name=String(input.collection??""),definition=declaration&&Object.hasOwn(declaration.collections,name)?declaration.collections[name]:void 0;if(!definition||!/^[a-z][a-z0-9-]{0,63}$/.test(name))throw Object.assign(new Error("Plugin state collection is not declared"),{code:"PLUGIN_STATE_NOT_DECLARED"});if(input.expectedDeclaration!==void 0&&input.expectedDeclaration!==pluginStateSignature(definition))throw Object.assign(new Error("Plugin state declaration changed during command execution"),{code:"PLUGIN_STATE_REVISION_CHANGED"});let scope=definition.scope,assertScope=()=>{};if(definition.scope==="host"&&(scope+=`:${this.tasks.host.hostId}`),definition.scope==="instance"||definition.scope==="browser-session"){if(!this.resolveTarget)throw new Error("Page state scope is unavailable");let resolved=await this.resolveTarget(plugin,input.instanceId,bridge,options),target=resolved.target,expected=binding?.expectedTarget;if(expected&&(target.browserSessionId!==expected.browserSessionId||target.tabId!==expected.tabId||target.frameId!==expected.frameId||target.documentId!==expected.documentId))throw Object.assign(new Error("State access cannot change the inherited document"),{code:"PLUGIN_PAGE_TARGET_CHANGED"});assertScope=resolved.assertActive,scope+=definition.scope==="instance"?`:${String(input.instanceId)}`:`:${target.browserSessionId}`}this.controller.signal.throwIfAborted(),binding?.signal?.throwIfAborted();let installation;try{installation=JSON.parse(readFileSync14(join18(pluginHostPaths().installations,`${plugin}.json`),"utf8"))}catch{}if(!findPluginRegistration(plugin)||installation?.installationId!==revision.installationId)throw Object.assign(new Error("Plugin state installation was removed or replaced"),{code:"PLUGIN_STATE_SCOPE_ENDED"});assertScope();let namespace={installationId:revision.installationId,collection:name,scope};switch(input.operation){case"get":return this.store.get(namespace,definition,input.key);case"get-many":return this.store.getMany(namespace,definition,input.keys);case"list":return this.store.list(namespace,definition,input);case"transact":return this.store.transact(namespace,definition,input.transaction);default:throw new Error("Invalid state operation")}}async close(){clearInterval(this.maintenance),this.controller.abort(),this.descriptions.clear(),this.store.clearMemory(),await Promise.allSettled([...this.pending])}};import{createHash as createHash11,randomUUID as randomUUID11}from"node:crypto";import{EventEmitter as EventEmitter2}from"node:events";import{readFileSync as readFileSync15,statSync as statSync11}from"node:fs";import{isAbsolute as isAbsolute7,join as join19}from"node:path";init_plugin();init_unified();init_pluginHostPaths();init_pluginExecutorProtocol();init_plugin_tasks();init_plugin_pages();function failure(code,message){return Object.assign(new Error(message),{code})}function sameTarget(a,b){return a.browserSessionId===b.browserSessionId&&a.tabId===b.tabId&&a.frameId===b.frameId&&a.documentId===b.documentId}var PluginPageManager=class{constructor(tasks,states,releaseModels){this.tasks=tasks;this.states=states;this.releaseModels=releaseModels;this.heartbeat=setInterval(()=>{this.reconcile()},1e4),this.heartbeat.unref()}instances=new Map;changes=new EventEmitter2().setMaxListeners(0);heartbeat;reconciling=new Set;closed=!1;describing=new Set;async resolveStateTarget(plugin,instanceId,bridge,options){let record2=this.own(plugin,instanceId,bridge);return await this.bind(record2,bridge,options),await this.validate(record2),{target:{...record2.value.target},assertActive:()=>{if(this.closed||record2.value.state!=="active"||this.instances.get(record2.value.instanceId)!==record2)throw failure("PLUGIN_STATE_SCOPE_ENDED","Plugin page state scope has ended")}}}diagnosticSnapshot(){return[...this.instances.values()].map(record2=>({plugin:record2.value.plugin,installationId:record2.value.installationId,revision:record2.value.revision,host:{hostId:record2.value.host.hostId,epoch:record2.value.host.epoch},binding:diagnosticBinding(record2.value),observation:"service-snapshot",state:record2.value.state,lastReceived:record2.lastReceived,queued:record2.deliveries.filter(delivery=>delivery.state==="queued").length,running:record2.running.size}))}async reconcile(){this.closed||await Promise.all([...this.instances.values()].map(async record2=>{let id=record2.value.instanceId;if(!(!record2.ready||record2.value.state!=="active"||this.reconciling.has(id))){this.reconciling.add(id);try{await this.validate(record2)}catch(error2){isPluginPageUnavailable(error2)||await this.invalidate(record2,error2)}finally{this.reconciling.delete(id)}}}))}bounded(promise,milliseconds){return new Promise((resolve7,reject)=>{let timer=setTimeout(()=>reject(failure("PLUGIN_PAGE_TIMEOUT","Plugin page browser request timed out")),milliseconds);promise.then(resolve7,reject).finally(()=>clearTimeout(timer))})}async invoke(record2,data,timeoutSec){let payload={...data,tabId:record2.value.target.tabId,instanceId:record2.value.instanceId,...timeoutSec!==void 0?{timeoutSec}:{}};return record2.client?record2.client.invoke("page_runtime",payload,{...record2.options,...timeoutSec!==void 0?{timeoutSec}:{}}):record2.bridge.invoke("page_runtime",payload,{...record2.options,...timeoutSec!==void 0?{timeoutSec}:{}})}unavailable(record2,error2){return isPluginPageInvalidation(error2)?error2:(record2.unavailable=!0,Date.now()-record2.lastVerifiedAt>=3e4?failure("PLUGIN_PAGE_INVALIDATED","Plugin page host recovery lease expired"):pluginPageUnavailable(error2))}async validate(record2){if(this.closed||record2.value.state!=="active")throw failure("PLUGIN_PAGE_INVALIDATED","Plugin page instance is no longer active");let registration=findPluginRegistration(record2.value.plugin),installation;try{installation=JSON.parse(readFileSync15(join19(pluginHostPaths().installations,`${record2.value.plugin}.json`),"utf8"))}catch{}if(!registration||installation?.installationId!==record2.value.installationId)throw failure("PLUGIN_PAGE_INVALIDATED","Plugin installation was removed or replaced");let bridge=record2.bridge,current;try{current=await this.bounded(this.invoke(record2,{operation:"get"}),8e3)}catch(error2){if(!record2.client&&record2.bridge!==bridge&&!this.closed&&record2.value.state==="active")return this.validate(record2);throw this.unavailable(record2,error2)}if(this.closed||record2.value.state!=="active")throw failure("PLUGIN_PAGE_INVALIDATED","Plugin page instance ended during validation");if(current.state!=="active"||!sameTarget(current.target,record2.value.target)||!matchesPluginPageURL(record2.runtime.matches,current.target.url))throw failure("PLUGIN_PAGE_INVALIDATED","Plugin page document changed");record2.value.target.url=current.target.url,record2.lastVerifiedAt=Date.now();let recovered=record2.unavailable;record2.unavailable=!1,recovered&&queueMicrotask(()=>this.drain(record2))}own(plugin,instanceId,bridge){if(bridge.scope.plugin&&bridge.scope.plugin!==plugin)throw failure("PLUGIN_PAGE_NOT_FOUND","Plugin page instance was not found");if(!isPluginPageInstanceId(instanceId))throw failure("PLUGIN_PAGE_NOT_FOUND","Invalid plugin page instance ID");let record2=this.instances.get(instanceId);if(!record2||record2.value.plugin!==plugin)throw failure("PLUGIN_PAGE_NOT_FOUND","Plugin page instance was not found");return record2}async bind(record2,bridge,options){if(bridge.scope.bridge!=="caller"||!record2.client&&record2.bridge.id===bridge.id||record2.value.state!=="active")return;let current;try{current=await this.bounded(bridge.invoke("page_runtime",{operation:"get",tabId:record2.value.target.tabId,instanceId:record2.value.instanceId},options),8e3)}catch(error2){throw this.unavailable(record2,error2)}if(this.closed||record2.value.state!=="active")throw failure("PLUGIN_PAGE_INVALIDATED","Plugin page instance ended while binding");if(!sameTarget(current.target,record2.value.target))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Plugin page belongs to a different browser document");record2.client||(record2.bridge=bridge,record2.options=options)}append(record2,event){for(record2.history.push({...event,sequence:++record2.value.sequence,time:Date.now()});record2.history.length>256||Buffer.byteLength(JSON.stringify(record2.history))>384*1024;)record2.history.shift();this.changes.emit(record2.value.instanceId)}async waitForChange(record2,cursor,waitMs,signal){!waitMs||cursor!==record2.value.sequence||record2.value.state!=="active"||await new Promise((resolve7,reject)=>{let finish=()=>{cleanup(),resolve7()},abort=()=>{cleanup(),reject(signal?.reason??new Error("Plugin page subscription closed"))},timer=setTimeout(finish,waitMs),cleanup=()=>{clearTimeout(timer),this.changes.off(record2.value.instanceId,finish),signal?.removeEventListener("abort",abort)};this.changes.once(record2.value.instanceId,finish),signal?.addEventListener("abort",abort,{once:!0}),signal?.aborted?abort():(record2.value.sequence!==cursor||record2.value.state!=="active")&&finish()})}async invalidate(record2,error2,state="invalidated"){if(record2.value.state==="active"){record2.value.state=state,record2.value.error=serializePluginExecutionError(error2),this.append(record2,{type:"invalidated",error:record2.value.error});for(let taskId of record2.taskIds)this.tasks.cancel(taskId,{access:"public",source:"runtime",bridge:"service"});for(let delivery of record2.deliveries)delivery.taskId&&this.tasks.cancel(delivery.taskId,{access:"public",source:"runtime",bridge:"service"}),["queued","running"].includes(delivery.state)&&(delivery.state="invalidated");try{this.states.store.discardScope(record2.value.installationId,`instance:${record2.value.instanceId}`)}catch(error3){console.error("Plugin instance cache cleanup failed:",error3)}record2.release();try{await this.releaseModels?.(record2.value.instanceId)}finally{try{await this.bounded(this.invoke(record2,{operation:"detach"}),2e3)}catch{}record2.client?.disconnect()}}}async call(plugin,input,bridge,options={},execution={cwd:process.cwd(),environment:Object.fromEntries(Object.entries(process.env).filter(entry=>entry[1]!==void 0))}){if(this.closed)throw failure("PLUGIN_PAGE_HOST_STOPPING","Plugin page service is stopping");if(execution.signal?.throwIfAborted(),!input||typeof input!="object"||Array.isArray(input))throw new Error("Invalid plugin page request");if(!/^[a-z][a-z0-9-]*$/.test(plugin)||bridge.scope.plugin&&bridge.scope.plugin!==plugin)throw new Error("Invalid plugin page scope");let operation=input.operation;if(execution.expectedTarget&&operation==="activation"&&input.enabled!==void 0)throw failure("PLUGIN_PAGE_TARGET_CHANGED","Document-bound calls cannot configure browser activation");if(operation==="attach"||operation==="activation"){if(Object.keys(options).some(key2=>!["browser","connector","session","local","transport","timeoutSec"].includes(key2)))throw new Error("Invalid plugin page routing options");if(bridge.scope.bridge==="service"&&!decodeBrowserTarget(options.browser))throw new Error("Plugin pages require a stable browser ID");if(!execution||!isAbsolute7(execution.cwd)||!statSync11(execution.cwd).isDirectory()||!execution.environment||Buffer.byteLength(JSON.stringify(execution.environment))>512*1024||Object.values(execution.environment).some(value=>typeof value!="string"))throw new Error("Invalid plugin page execution context")}if(operation==="activation"&&input.enabled!==!0){let client=bridge.scope.bridge==="service"?new UnifiedClient:void 0;try{return client?await client.invoke("page_runtime",{...input,plugin},options):await bridge.invoke("page_runtime",{...input,plugin},options)}finally{client?.disconnect()}}if(operation==="attach"||operation==="activation"){let revision=execution.revision??capturePluginRevision(plugin);if(input.installationId!==void 0&&input.installationId!==revision.installationId)throw failure("PLUGIN_PAGE_INSTALLATION_CHANGED","Page activation belongs to another plugin installation");let release=this.tasks.pinRevision(revision),retained=!1,client=bridge.scope.bridge==="service"?new UnifiedClient:void 0,invoke2=data=>client?client.invoke("page_runtime",data,options):bridge.invoke("page_runtime",data,options);try{let controller=new AbortController,description={controller,promise:this.tasks.describeRevision(revision,execution.signal?AbortSignal.any([controller.signal,execution.signal]):controller.signal,{cache:!0})};this.describing.add(description);let loaded;try{loaded=await description.promise}finally{this.describing.delete(description)}if(this.closed)throw new Error("Plugin page host stopped during attachment");let runtime=loaded.pageRuntime;if(!runtime)throw failure("PLUGIN_PAGE_NOT_DECLARED","Plugin has no pageRuntime declaration");if(operation==="activation")return await invoke2({...input,operation:"activation",plugin,installationId:revision.installationId,allowedMatches:runtime.matches});if(this.instances.size>=256){for(let[id,item]of this.instances)item.value.state!=="active"&&!item.running.size&&this.instances.delete(id);if(this.instances.size>=256)throw new Error("Too many plugin page instances")}let target=await invoke2({operation:"resolve",tabId:input.tabId,frame:input.frame,expectedDocumentId:input.documentId});if(this.closed)throw failure("PLUGIN_PAGE_HOST_STOPPING","Plugin page service is stopping");if(execution.signal?.throwIfAborted(),execution.expectedTarget&&!sameTarget(target,execution.expectedTarget))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Nested plugin must use the inherited document");if(!matchesPluginPageURL(runtime.matches,target.url))throw failure("PLUGIN_PAGE_URL_NOT_ALLOWED","Plugin page URL is outside its declared matches");let existing=[...this.instances.values()].find(item=>item.value.state==="active"&&item.value.plugin===plugin&&sameTarget(item.value.target,target));if(existing&&execution.revision&&existing.value.revision!==revision.revision&&input.replace!==!0)throw failure("PLUGIN_PAGE_REVISION_CHANGED","Existing page instance uses another plugin revision");if(existing&&input.replace!==!0)try{return await existing.initialized,await this.bind(existing,bridge,options),await this.validate(existing),input.activationGeneration===void 0&&await invoke2({operation:"get",instanceId:existing.value.instanceId,tabId:target.tabId,retainManual:!0}),existing.value}catch(error2){if(isPluginPageUnavailable(error2))throw error2;await this.invalidate(existing,error2)}existing&&await this.invalidate(existing,new Error("Plugin page instance replaced"),"detached");let value={instanceId:`pi_${randomUUID11()}`,plugin,installationId:revision.installationId,revision:revision.revision,host:this.tasks.host,target,state:"active",sequence:0,createdAt:Date.now()},initialized,initializationFailed,initialization=new Promise((resolve7,reject)=>{initialized=resolve7,initializationFailed=reject});initialization.catch(()=>{});let record3={initialized:initialization,value,runtime,revision,release,bridge,options:{...options},client,ready:!1,lastVerifiedAt:Date.now(),unavailable:!1,taskIds:new Set,execution:{cwd:execution.cwd,environment:{...execution.environment}},history:[],deliveries:[],lastReceived:0,running:new Set};this.instances.set(value.instanceId,record3);try{let attached=await invoke2({operation:"attach",instance:value,runtime,input:input.input??{},frame:input.frame??"business",activationGeneration:input.activationGeneration});if(!sameTarget(attached.target,target))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Document changed while attaching plugin page");if(await this.validate(record3),execution.signal?.throwIfAborted(),runtime.initialize){let parent=execution.parentIdentity;parent?.taskId&&this.tasks.assertChildBudget(parent.taskId);let{taskId}=this.start(record3,runtime.initialize,{input:input.input??{}},`initialize:${value.instanceId}`,parent),scope={access:"public",source:"runtime",bridge:"service"},cancel2=()=>this.tasks.cancel(taskId,scope);execution.signal?.addEventListener("abort",cancel2,{once:!0}),execution.signal?.aborted&&cancel2();try{parent?.taskId&&execution.signal?await this.tasks.awaitChild(parent.taskId,taskId,scope,execution.signal):await this.wait(record3,taskId)}finally{execution.signal?.removeEventListener("abort",cancel2),record3.taskIds.delete(taskId)}}return execution.signal?.throwIfAborted(),await this.validate(record3),execution.signal?.throwIfAborted(),record3.ready=!0,initialized(),this.drain(record3),retained=!0,value}catch(error2){throw initializationFailed(error2),await this.invalidate(record3,error2),error2}}finally{retained||(release(),client?.disconnect())}}if(operation==="list")return{instances:[...this.instances.values()].filter(item=>item.value.plugin===plugin&&(!execution.expectedTarget||sameTarget(item.value.target,execution.expectedTarget))).map(item=>item.value)};let record2=this.own(plugin,input.instanceId,bridge);if(execution.expectedTarget&&!sameTarget(record2.value.target,execution.expectedTarget))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Nested plugin must use the inherited document");if(execution.revision&&record2.value.revision!==execution.revision.revision)throw failure("PLUGIN_PAGE_REVISION_CHANGED","Page instance uses another plugin revision");if(await this.bind(record2,bridge,options),operation==="detach")return await this.invalidate(record2,new Error("Plugin page instance detached"),"detached"),record2.value;try{record2.value.state==="active"&&await this.validate(record2)}catch(error2){if(isPluginPageUnavailable(error2))throw error2;await this.invalidate(record2,error2)}if(operation==="get")return record2.value;if(operation==="events"){let cursor=input.cursor??0,waitMs=input.waitMs??0;if(!Number.isSafeInteger(cursor)||Number(cursor)<0||Number(cursor)>record2.value.sequence)throw new Error("Invalid plugin page event cursor");if(!Number.isSafeInteger(waitMs)||Number(waitMs)<0||Number(waitMs)>25e3)throw new Error("Invalid plugin page event wait");await this.waitForChange(record2,Number(cursor),Number(waitMs),execution.signal),execution.signal?.throwIfAborted();let first=record2.history[0]?.sequence??record2.value.sequence+1,events=[],bytes=0;for(let event of record2.history)if(event.sequence>Number(cursor)){let size=Buffer.byteLength(JSON.stringify(event));if(events.length&&bytes+size>192*1024)break;events.push(event),bytes+=size}return{instance:record2.value,events,nextCursor:events.at(-1)?.sequence??Number(cursor),firstAvailableSequence:first,gap:Number(cursor)<first-1}}if(operation!=="send")throw new Error("Unknown plugin page operation");let timeoutSec=input.timeoutSec;if(timeoutSec!==void 0&&(typeof timeoutSec!="number"||!Number.isFinite(timeoutSec)||timeoutSec<=0||timeoutSec>300))throw new Error("Page request timeoutSec must be between 0 and 300");await this.validate(record2),execution.signal?.throwIfAborted();let json2=Buffer.from(JSON.stringify(input.message??null));if(json2.length>24*1024*1024)throw new Error("Plugin page message exceeds 24 MiB");let requestId=randomUUID11(),cancel=()=>{this.invoke(record2,{operation:"cancel",requestId}).catch(()=>{})};execution.signal?.addEventListener("abort",cancel,{once:!0});try{let result;if(json2.length<=192*1024)result=await this.invoke(record2,{operation:"send",message:input.message,requestId},timeoutSec);else{let transferId=randomUUID11();try{await this.invoke(record2,{operation:"message_begin",transferId,bytes:json2.length,sha256:createHash11("sha256").update(json2).digest("hex")});for(let offset=0,index=0;offset<json2.length;offset+=192*1024,index++)await this.validate(record2),execution.signal?.throwIfAborted(),await this.invoke(record2,{operation:"message_chunk",transferId,index,data:json2.subarray(offset,offset+192*1024).toString("base64")});await this.validate(record2),execution.signal?.throwIfAborted(),result=await this.invoke(record2,{operation:"message_commit",transferId,requestId},timeoutSec)}finally{await this.invoke(record2,{operation:"message_abort",transferId}).catch(()=>{})}}execution.signal?.throwIfAborted(),await this.validate(record2);let reply=result;if(reply?.kind==="value")return reply.value;if(reply?.kind!=="resource"||!Number.isSafeInteger(reply.bytes)||reply.bytes<1||reply.bytes>24*1024*1024||reply.chunks!==Math.ceil(reply.bytes/(192*1024)))throw new Error("Invalid page response resource");try{let parts=[];for(let index=0;index<reply.chunks;index++){await this.validate(record2);let chunk=await this.invoke(record2,{operation:"result_chunk",resultId:reply.resultId,index});if(chunk.resultId!==reply.resultId||chunk.index!==index||chunk.bytes!==reply.bytes||chunk.sha256!==reply.sha256||chunk.chunks!==reply.chunks)throw new Error("Page response chunk identity mismatch");parts.push(Buffer.from(chunk.data,"base64"))}let bytes=Buffer.concat(parts);if(bytes.length!==reply.bytes||createHash11("sha256").update(bytes).digest("hex")!==reply.sha256)throw new Error("Page response integrity check failed");return await this.validate(record2),JSON.parse(bytes.toString("utf8"))}finally{await this.invoke(record2,{operation:"result_release",resultId:reply.resultId}).catch(()=>{})}}catch(error2){throw cancel(),error2}finally{execution.signal?.removeEventListener("abort",cancel)}}async receive(plugin,input,bridge,options={}){if(input.kind==="sync"){if(this.closed||!/^[a-z][a-z0-9-]*$/.test(plugin)||bridge.scope.plugin&&bridge.scope.plugin!==plugin||!Array.isArray(input.instances)||input.instances.length>32)throw new Error("Invalid page reconciliation request");let session2=await this.bounded(bridge.invoke("page_runtime",{operation:"session"},options),8e3);if(typeof input.browserSessionId!="string"||session2.browserSessionId!==input.browserSessionId)throw failure("PLUGIN_PAGE_TARGET_CHANGED","Page reconciliation came from another browser session");let instances=[];for(let value of input.instances){let proposed=value,record3=this.instances.get(proposed?.instanceId);if(!(!record3||record3.value.plugin!==plugin||record3.value.state!=="active"||!proposed.host||proposed.host.hostId!==this.tasks.host.hostId||proposed.host.epoch!==this.tasks.host.epoch||proposed.installationId!==record3.value.installationId||proposed.revision!==record3.value.revision||!proposed.target||!sameTarget(proposed.target,record3.value.target)||record3.value.target.browserSessionId!==session2.browserSessionId))try{await this.bind(record3,bridge,options),await this.validate(record3),instances.push({instance:record3.value,lastReceived:record3.lastReceived})}catch(error2){if(isPluginPageUnavailable(error2))throw error2;await this.invalidate(record3,error2)}}return{host:this.tasks.host,instances}}let record2=this.own(plugin,input.instanceId,bridge);if((await this.bounded(bridge.invoke("page_runtime",{operation:"session"},options),8e3)).browserSessionId!==record2.value.target.browserSessionId)throw failure("PLUGIN_PAGE_TARGET_CHANGED","Page event came from another browser");if(input.kind==="invalidated"){if(!input.target||!sameTarget(input.target,record2.value.target))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Invalid document invalidation source");return await this.invalidate(record2,new Error("Browser invalidated the document")),{accepted:!0}}let source=await this.bounded(bridge.invoke("page_runtime",{operation:"get",tabId:record2.value.target.tabId,instanceId:record2.value.instanceId},options),8e3);if(!sameTarget(source.target,record2.value.target))throw failure("PLUGIN_PAGE_TARGET_CHANGED","Page event came from another document");if(await this.bind(record2,bridge,options),await this.validate(record2),input.kind==="navigation")return this.append(record2,{type:"navigation",payload:{url:record2.value.target.url}}),{accepted:!0};let sequence=input.eventSequence;if(!Number.isSafeInteger(sequence)||Number(sequence)<1)throw new Error("Invalid page event sequence");let duplicate=record2.deliveries.find(item=>item.sequence===sequence);if(Number(sequence)<=record2.lastReceived)return{accepted:!0,duplicate:!0,taskId:duplicate?.taskId,gap:!duplicate};if(Number(sequence)!==record2.lastReceived+1)throw failure("PLUGIN_PAGE_EVENT_GAP","Page event stream has a gap; reattach the instance");let name=typeof input.name=="string"?input.name:"",binding=record2.runtime.events?.[name];if(!binding||Buffer.byteLength(JSON.stringify(input.payload??null))>32*1024)throw new Error("Page event is not declared or exceeds 32 KiB");let waiting=record2.deliveries.filter(item=>item.name===name&&item.state==="queued");if(binding.policy==="latest")for(let previous of waiting)previous.state="superseded",this.append(record2,{type:"superseded",eventSequence:previous.sequence,name});else if(waiting.length>=(binding.maxPending??32))throw failure("PLUGIN_PAGE_QUEUE_FULL","Page event queue is full");if(record2.deliveries.filter(item=>["queued","running"].includes(item.state)).length>=128)throw failure("PLUGIN_PAGE_QUEUE_FULL","Page event queue is full");let delivery={sequence:Number(sequence),name,payload:input.payload,state:"queued"};for(record2.lastReceived=Number(sequence),record2.deliveries.push(delivery);record2.deliveries.length>256;){let index=record2.deliveries.findIndex(item=>!["queued","running"].includes(item.state));if(index<0)break;record2.deliveries.splice(index,1)}return this.append(record2,{type:"received",eventSequence:delivery.sequence,name,payload:input.payload}),this.drain(record2),{accepted:!0,eventSequence:delivery.sequence,taskId:delivery.taskId}}drain(record2){if(!(this.closed||!record2.ready||record2.unavailable||record2.value.state!=="active"))for(let delivery of record2.deliveries){if(delivery.state!=="queued")continue;let binding=record2.runtime.events[delivery.name];[...record2.running].filter(item=>item.name===delivery.name).length>=(binding.policy==="parallel"?binding.concurrency??2:1)||(delivery.state="running",record2.running.add(delivery),this.execute(record2,delivery).finally(()=>{record2.running.delete(delivery),this.drain(record2)}))}}async execute(record2,delivery){try{await this.validate(record2);let binding=record2.runtime.events[delivery.name],started=this.start(record2,binding.command,{event:{name:delivery.name,payload:delivery.payload,sequence:delivery.sequence}},`page:${record2.value.instanceId}:${delivery.sequence}`);delivery.taskId=started.taskId,this.append(record2,{type:"running",name:delivery.name,eventSequence:delivery.sequence,taskId:started.taskId}),await this.wait(record2,started.taskId),delivery.state="success",this.append(record2,{type:"success",name:delivery.name,eventSequence:delivery.sequence,taskId:started.taskId})}catch(error2){if(isPluginPageUnavailable(error2)&&!delivery.taskId&&record2.value.state==="active"){delivery.state="queued";return}delivery.state="error",this.append(record2,{type:"error",name:delivery.name,eventSequence:delivery.sequence,error:serializePluginExecutionError(error2)})}}start(record2,command,input,key2,parentIdentity){let bridge={get id(){return record2.bridge.id},scope:{...record2.bridge.scope,plugin:record2.value.plugin,access:"runtime",source:"runtime"},invoke:(action,payload,options)=>record2.client?record2.client.invoke(action,payload,options):record2.bridge.invoke(action,payload,options)},started=this.tasks.start({plugin:record2.value.plugin,command,input,cwd:record2.execution.cwd,environment:record2.execution.environment,options:record2.options,idempotencyKey:key2},bridge,{...parentIdentity?.taskId?{parentTaskId:parentIdentity.taskId,parentIdentity}:{},revision:record2.revision,guard:()=>this.validate(record2),page:{instanceId:record2.value.instanceId,target:{...record2.value.target}}});return record2.taskIds.add(started.taskId),started}async wait(record2,taskId){let cursor=0;for(;;){let page=await this.tasks.events(taskId,{access:"public",source:"runtime",bridge:"service"},cursor,25e3);if(cursor=page.nextCursor,!isPluginTaskActive(page.task.state)){if(record2.taskIds.delete(taskId),page.task.state!=="success")throw Object.assign(pluginExecutionError(page.task.error??{message:"Page handler failed"}),{childTaskId:taskId});return}record2.value.state!=="active"&&this.tasks.cancel(taskId,{access:"public",source:"runtime",bridge:"service"})}}async close(){this.closed=!0,clearInterval(this.heartbeat);for(let item of this.describing)item.controller.abort();await Promise.allSettled([...this.describing].map(item=>item.promise)),await Promise.allSettled([...this.instances.values()].map(record2=>this.invalidate(record2,new Error("Plugin page host stopped"))))}};var PluginModelCatalog=class{constructor(discover=async signal=>{let{listCodexModels:listCodexModels2}=await Promise.resolve().then(()=>(init_codex(),codex_exports));return listCodexModels2(signal)},now=Date.now){this.discover=discover;this.now=now}controller=new AbortController;cached;pending;list(provider="codex"){if(this.controller.signal.throwIfAborted(),provider!=="codex")throw new Error("Unsupported model provider");return this.cached?(this.cached.expires<=this.now()&&this.refresh().catch(()=>{}),Promise.resolve(structuredClone(this.cached.value))):this.refresh().then(value=>structuredClone(value))}refresh(){return this.pending??=this.load().catch(error2=>{throw this.cached&&(this.cached.expires=this.now()+3e4),error2}).finally(()=>{this.pending=void 0}),this.pending}async load(){let signal=AbortSignal.any([this.controller.signal,AbortSignal.timeout(15e3)]),value={provider:"codex",models:await this.discover(signal)};return signal.throwIfAborted(),this.cached={value,expires:this.now()+12e4},value}async close(){this.controller.abort(),await this.pending?.catch(()=>{}),this.cached=void 0}};init_runtimePaths();init_dist();init_pluginExecutorProtocol();init_pluginProcessTree();init_pluginModelDefinition();import{fork as fork2}from"node:child_process";import{createHash as createHash12}from"node:crypto";var PluginModelSessions=class{constructor(idleMs=5*6e4,limit=2,workerPath=PLUGIN_MODEL_SESSION_WORKER_PATH){this.idleMs=idleMs;this.limit=limit;this.workerPath=workerPath}sessions=new Map;closing=new Set;closed=!1;execute(owner,...args){let[plugin,,context,options={}]=args,request=options.model,configuration=Object.fromEntries(Object.entries(request).filter(([key3])=>!["prompt","outputSchema"].includes(key3)).sort(([a],[b])=>a.localeCompare(b))),key2=createHash12("sha256").update(JSON.stringify([owner,plugin,context.invocation?.installationId,context.invocation?.revision,options.cwd,Object.entries(options.environment??{}).sort(([a],[b])=>a.localeCompare(b)),configuration])).digest("hex");if(options.signal?.throwIfAborted(),this.closed)return Promise.reject(new Error("Model conversations are closed"));let session=this.sessions.get(key2);if(!session||session.closed){if(this.sessions.size>=this.limit){let idle=[...this.sessions.values()].find(value=>!value.queued);if(!idle)return Promise.reject(new Error("Model conversation limit reached"));this.remove(idle).catch(error2=>console.error("Model conversation cleanup failed:",error2))}let child=fork2(this.workerPath,[],{stdio:["ignore","ignore","pipe","ipc"],serialization:"advanced",execArgv:[],detached:process.platform!=="win32",cwd:options.cwd,env:options.environment}),exited=new Promise(resolve7=>child.once("close",()=>resolve7()));session={owner,child,tail:Promise.resolve(),queued:0,closed:!1,exited};let current2=session;child.on("error",()=>{this.remove(current2).catch(error2=>console.error("Model conversation cleanup failed:",error2))}),child.once("exit",()=>{this.remove(current2).catch(error2=>console.error("Model conversation cleanup failed:",error2))}),this.sessions.set(key2,session)}this.sessions.delete(key2),this.sessions.set(key2,session),clearTimeout(session.timer);let current=session;current.queued++;let started=!1,operation=current.tail.catch(()=>{}).then(async()=>{if(options.signal?.throwIfAborted(),current.closed)throw new Error("Model conversation has ended");return started=!0,this.run(current,args)});if(current.tail=operation.finally(()=>{current.queued--,!current.queued&&!current.closed&&(current.timer=setTimeout(()=>{this.remove(current).catch(error2=>console.error("Model conversation cleanup failed:",error2))},this.idleMs),current.timer.unref())}),current.tail.catch(()=>{}),!options.signal)return operation;let signal=options.signal;return new Promise((resolve7,reject)=>{let abort=()=>{started||reject(Object.assign(new Error("Model execution cancelled"),{code:"PLUGIN_EXECUTION_CANCELLED"}))};signal.addEventListener("abort",abort,{once:!0}),signal.aborted&&abort(),operation.then(resolve7,reject).finally(()=>signal.removeEventListener("abort",abort))})}run(session,args){let options=args[3],request=options.model,child=session.child;return new Promise((resolve7,reject)=>{let finished=!1,stderr="",timer,finish=async(failure2,result)=>{if(!finished){finished=!0,clearTimeout(timer),options.signal?.removeEventListener("abort",abort),child.removeListener("message",message),child.removeListener("error",failed),child.removeListener("exit",exited),child.stderr?.removeListener("data",capture);try{failure2&&await this.remove(session),options.onExecutorStopped?.(),failure2?reject(failure2):resolve7(result)}catch(error2){reject(error2)}}},failed=error2=>{finish(error2)},exited=()=>failed(new Error(stderr||"Model conversation executor exited")),abort=()=>failed(Object.assign(new Error("Model execution cancelled"),{code:"PLUGIN_EXECUTION_CANCELLED"})),capture=chunk=>{stderr=(stderr+chunk.toString()).slice(-65536)},message=event=>{if(!finished)try{event.type==="result"?finish(void 0,validatePluginModelResult(request,event.result)):event.type==="error"?failed(pluginExecutionError(event.error)):event.type==="diagnostic"?options.onDiagnostic?.(event.metadata):event.type==="delta"&&options.onProgress?.({message:"model.delta",data:{delta:event.delta,index:event.index,...event.metadata,partial:!0}})}catch(error2){finish(error2)}};child.on("message",message),child.once("error",failed),child.once("exit",exited),child.stderr?.on("data",capture),options.signal?.addEventListener("abort",abort,{once:!0}),options.executionTimeoutMs&&(timer=setTimeout(()=>failed(Object.assign(new Error("Model execution deadline exceeded"),{code:"PLUGIN_EXECUTION_DEADLINE"})),options.executionTimeoutMs));try{if(options.signal?.aborted){abort();return}child.pid&&options.onExecutorStarted?.({pid:child.pid,processIdentity:processIdentity(child.pid)}),child.send({request},error2=>{error2&&failed(error2)})}catch(error2){finish(error2)}})}remove(session){if(session.closing)return session.closing;session.closed=!0,clearTimeout(session.timer);for(let[key2,value]of this.sessions)value===session&&this.sessions.delete(key2);return session.closing=(session.child.pid?stopPluginProcessTree(session.child.pid):Promise.resolve()).then(()=>{let timer,timeout=new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error("Model conversation did not exit")),2e3)});return Promise.race([session.exited,timeout]).finally(()=>clearTimeout(timer))}).catch(cause=>{throw Object.assign(new Error("Unable to confirm model conversation cleanup",{cause}),{code:"PLUGIN_EXECUTOR_CLEANUP_FAILED",details:{executionStopped:!1}})}),this.closing.add(session),session.closing.finally(()=>this.closing.delete(session)).catch(()=>{}),session.closing}async release(owner){await Promise.all([...new Set([...this.sessions.values(),...this.closing])].filter(value=>value.owner===owner).map(value=>this.remove(value)))}async close(){this.closed=!0,await Promise.all([...new Set([...this.sessions.values(),...this.closing])].map(value=>this.remove(value)))}};import{dirname as dirname9,join as join20}from"node:path";var PluginHost=class{tasks;state;pages;models=new PluginModelCatalog;modelSessions;closing;constructor(configuration={}){this.modelSessions=new PluginModelSessions(void 0,Math.max(2,configuration.modelConcurrency??2),configuration.workerPath?join20(dirname9(configuration.workerPath),"pluginModelSessionWorker.js"):void 0),this.tasks=new PluginTaskManager(configuration,{state:(...args)=>this.state.call(...args),page:(...args)=>this.pages.call(...args),listModels:provider=>this.models.list(provider),model:(owner,...args)=>this.modelSessions.execute(owner,...args),releaseModels:owner=>this.modelSessions.release(owner)}),this.state=new PluginStateManager(this.tasks),this.pages=new PluginPageManager(this.tasks,this.state,owner=>this.modelSessions.release(owner)),this.state.resolveTarget=(...args)=>this.pages.resolveStateTarget(...args)}diagnose(value,scope){let options=normalizeDiagnosticOptions(value);if(scope.plugin&&options.plugin&&scope.plugin!==options.plugin)throw Object.assign(new Error("Plugin task was not found"),{code:"PLUGIN_TASK_NOT_FOUND"});return buildDiagnosticReport(this.tasks.host,scope,options,this.tasks.store.diagnosticRecords(options.taskId),this.pages.diagnosticSnapshot())}close(){return this.closing??=Promise.allSettled([this.pages.close(),this.tasks.close(),this.state.close(),this.models.close(),this.modelSessions.close()]).then(results=>{let failures=results.flatMap(result=>result.status==="rejected"?[result.reason]:[]);if(failures.length)throw new AggregateError(failures,"Plugin host cleanup failed")}),this.closing}};init_pluginTaskWire();init_pluginExecutorProtocol();async function startPluginHostServer(configuration={}){let paths=pluginHostPaths(),existing=readRecord(paths.lease);existing&&!isProcessOwnerAlive(existing)?removeRecordIfPid(paths.lease,existing.pid):existing||removeInvalidRecord(paths.lease);let owner={pid:process.pid,protocolVersion:5,version:CLIENT_VERSION,buildId:PLUGIN_HOST_BUILD_ID,processIdentity:processIdentity(process.pid)};if(!claimRecord(paths.lease,owner))throw Object.assign(new Error("Plugin execution service already has an owner"),{code:"PLUGIN_TASK_HOST_OWNED"});let host;try{host=new PluginHost(configuration)}catch(error2){throw removeRecordIfPid(paths.lease,process.pid),error2}let{tasks:manager,pages,state,models}=host;if(!writeRecordIfPid(paths.lease,process.pid,{...owner,...manager.host,socketPath:paths.socket,startedAt:new Date().toISOString()}))throw new Error("Plugin host lease ownership was lost");let sessions=new Set,closing,server=createServer(socket=>{sessions.add(socket);let disconnected=new AbortController,scope,sequence=0,requests=0,pending=new Map,bridge={id:randomUUID12(),get scope(){if(!scope)throw new Error("Plugin task connection is not initialized");return scope},invoke(action,payload,options){if(socket.destroyed)return Promise.reject(Object.assign(new Error("The browser invocation bridge is unavailable"),{code:"PLUGIN_TASK_BRIDGE_UNAVAILABLE"}));let id=++sequence;return new Promise((resolve7,reject)=>{let timer=setTimeout(()=>{pending.delete(id),reject(Object.assign(new Error("The browser invocation bridge timed out; external operation status is unknown"),{code:"PLUGIN_TASK_BRIDGE_UNAVAILABLE",details:{action}}))},Math.min(Math.max(options.timeoutSec??180,1),3600)*1e3);pending.set(id,{resolve:resolve7,reject,timer}),writePluginTaskMessage(socket,{type:"invoke",id,action,payload,options}).catch(error2=>{pending.delete(id),clearTimeout(timer),reject(error2)})})}},handle=async(method,input)=>{if(method==="hello"){if(scope||input.protocolVersion!==5)throw new Error("Plugin task protocol version mismatch");if(!["public","toolbox","runtime"].includes(String(input.access))||!["service","caller"].includes(String(input.bridge))||!["cli","sdk","mcp","toolbox","runtime"].includes(String(input.source))||input.plugin!==void 0&&(typeof input.plugin!="string"||!/^[a-z][a-z0-9-]*$/.test(input.plugin))||input.scopeId!==void 0&&(typeof input.scopeId!="string"||input.scopeId.length>256)||input.access!=="public"&&(!input.plugin||input.bridge!=="caller"))throw new Error("Invalid plugin task caller scope");return scope={access:input.access,bridge:input.bridge,source:input.source,...input.plugin?{plugin:String(input.plugin)}:{},...input.scopeId?{scopeId:String(input.scopeId)}:{}},{protocolVersion:5,version:CLIENT_VERSION,buildId:PLUGIN_HOST_BUILD_ID,host:manager.host,pid:process.pid}}if(!scope)throw new Error("Plugin task connection requires a handshake");if(method==="status")return{protocolVersion:5,version:CLIENT_VERSION,buildId:PLUGIN_HOST_BUILD_ID,host:manager.host,pid:process.pid};if(method==="diagnose")return host.diagnose(input,scope);if(method==="state.call")return state.call(String(input.plugin),input.input,bridge,input.options);if(method==="model.list")return models.list(input.provider);if(method==="model.capabilities")return(await Promise.resolve().then(()=>(init_pluginModelDefinition(),pluginModelDefinition_exports))).pluginModelCapabilities();if(method==="page.call"){let execution=input.execution;return pages.call(String(input.plugin),input.input,bridge,input.options,execution?{cwd:execution.cwd,environment:execution.environment,signal:disconnected.signal}:void 0)}if(method==="page.event"){if(scope.access!=="runtime")throw new Error("Page events require the browser runtime bridge");return pages.receive(String(input.plugin),input.input,bridge,input.options)}if(method==="start")return manager.start(input,bridge);if(method==="resume")return manager.resume(input,bridge);if(method==="list")return manager.list(scope,input.activeOnly===!0,input.cursor,input.limit);if(method==="shutdown"){if(scope.access!=="public")throw new Error("Tool pages cannot stop the plugin host");return{stopped:!0}}if(typeof input.taskId!="string")throw new Error("Plugin task ID is required");switch(method){case"get":return manager.get(input.taskId,scope);case"checkpoint":return manager.checkpoint(input.taskId,scope);case"cancel":return manager.cancel(input.taskId,scope);case"events":return manager.events(input.taskId,scope,input.cursor,input.waitMs,disconnected.signal);case"result":return manager.resultChunk(input.taskId,scope,input.index);default:throw new Error(`Unknown plugin task method: ${method}`)}};readPluginTaskMessages(socket,message=>{if(message.type==="invoke-result"){let request=pending.get(message.id);if(!request)return;clearTimeout(request.timer),pending.delete(message.id),message.error?request.reject(pluginExecutionError(message.error)):request.resolve(message.result)}else if(message.type==="request"){if(++requests>64||!message.input||typeof message.input!="object"||Array.isArray(message.input)){socket.destroy(new Error("Invalid or excessive plugin task requests"));return}Promise.resolve().then(()=>handle(message.method,message.input)).then(async result=>{await writePluginTaskMessage(socket,{type:"response",id:message.id,result}),message.method==="shutdown"&&setImmediate(()=>{close().catch(()=>{})})},error2=>writePluginTaskMessage(socket,{type:"response",id:message.id,error:serializePluginExecutionError(error2)})).catch(()=>socket.destroy()).finally(()=>{requests--})}else socket.destroy(new Error("Invalid plugin task message direction"))}),socket.on("error",()=>{}),socket.once("close",()=>{disconnected.abort(),sessions.delete(socket);for(let request of pending.values())clearTimeout(request.timer),request.reject(Object.assign(new Error("The invocation bridge disconnected; external operation status may need reconciliation"),{code:"PLUGIN_TASK_BRIDGE_UNAVAILABLE"}));pending.clear()})});async function close(){return closing??=(async()=>{let servicesClosed=host.close();for(let socket of sessions)socket.destroy();await servicesClosed,await new Promise(resolve7=>server.close(()=>resolve7())),removeRecordIfPid(paths.lease,process.pid)&&process.platform!=="win32"&&rmSync13(paths.socket,{force:!0})})(),closing}try{process.platform!=="win32"&&rmSync13(paths.socket,{force:!0}),await new Promise((resolve7,reject)=>{server.once("error",reject),server.listen(paths.socket,()=>{server.off("error",reject),resolve7()})}),process.platform!=="win32"&&chmodSync2(paths.socket,384)}catch(error2){throw removeRecordIfPid(paths.lease,process.pid),await host.close(),error2}return{close,host,socketPath:paths.socket}}try{let server=await startPluginHostServer(),close=()=>{server.close().then(()=>process.exit(0))};process.once("SIGTERM",close),process.once("SIGINT",close)}catch(error2){error2.code!=="PLUGIN_TASK_HOST_OWNED"&&process.stderr.write(`${error2 instanceof Error?error2.stack:String(error2)}
|
|
988
988
|
`),process.exitCode=error2.code==="PLUGIN_TASK_HOST_OWNED"?0:1}
|