@orcha-ai/runtime-bridge 0.1.47 → 0.1.48
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/README.md
CHANGED
|
@@ -80,7 +80,7 @@ Version 0.1.44 routes replies, native `message` tool sends, and background notif
|
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
82
|
npm run pack:local
|
|
83
|
-
npm publish ./dist/orcha-ai-runtime-bridge-0.1.
|
|
83
|
+
npm publish ./dist/orcha-ai-runtime-bridge-0.1.48.tgz --access public --registry=https://registry.npmjs.org
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
After updating the npm package on a device, run `orcha-runtime-bridge reload --url ...` to switch the existing background process to the installed version. `reload` and `restart` also install/update the bundled plugin for OpenClaw targets saved on that device, using the target's executable and environment. Bridge stops before `openclaw plugins install <bundled-path> --force` and `openclaw gateway restart`; it starts again only after Gateway health succeeds and any configured MCP binding endpoint passes native Channel protocol preflight. Targets sharing an executable and environment update together. Installation/restart/health failures stop the command with an error and leave Bridge stopped; fix the reported problem and rerun `reload`. Package/executable preflight failures leave the existing Bridge running. The earlier manually downloaded foreground process must be stopped before connecting with npm to avoid duplicate connections.
|
|
@@ -97,6 +97,10 @@ To check reply classification against the real SDK without installing or startin
|
|
|
97
97
|
|
|
98
98
|
The OpenClaw channel sets the host's `suppressToolErrorWarnings` reply option to prevent synthesized tool-error warnings from becoming extra chat messages. Tool failures remain in the execution progress, and terminal error replies, delivery failures, timeouts, and empty-result checks keep their existing handling. Update the bundled plugin on the OpenClaw host and restart its Gateway for this change to take effect; publishing npm or deploying Core alone does not update an installed Gateway plugin.
|
|
99
99
|
|
|
100
|
+
Native reply previews use the host's `onPartialReply` callback. Growing snapshots emit `agent_message_chunk`; revised or cleared snapshots emit `agent_message_snapshot` with the complete preview in `content.text`, projected by Core as the existing `message_replace` event. Assistant message boundaries separate preview paragraphs. Block streaming is disabled so previews cannot consume final IM delivery. The final Bridge result remains a delivery receipt; only the IM sender persists the actual reply and attachments. This event addition requires the updated Core, Bridge and bundled plugin together; protocol version numbers are unchanged, with no database migration or new environment variable. Existing installed plugins need an explicit update and Gateway restart.
|
|
101
|
+
|
|
102
|
+
Version 0.1.48 includes these native reply previews. Deploy Core with commit `3c165340` or later before upgrading connected devices, then reload Bridge to update its bundled Gateway plugin.
|
|
103
|
+
|
|
100
104
|
## Long chats and independent topics (0.1.45)
|
|
101
105
|
|
|
102
106
|
Bridge and the bundled OpenClaw plugin are versioned together. Version 0.1.45 accepts an explicit `taskContextId: null` for long chats and independent topic conversations. Missing or invalid task identifiers still fail validation. Native session keys include the conversation ID, so separate conversations remain isolated even when both have no embedded task ID. External proactive messages and background results use the existing long chat unless bound to an existing run.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{spawn as dc}from"node:child_process";import{closeSync as fc,existsSync as Er,mkdirSync as pc,openSync as hc,readFileSync as yr,rmSync as mc,statSync as us,writeFileSync as hs}from"node:fs";import{dirname as ms,resolve as ht}from"node:path";import{fileURLToPath as gc}from"node:url";import{createHash as Ur}from"node:crypto";import{mkdir as bs,readFile as Ms,writeFile as Us,rename as Ps}from"node:fs/promises";import{join as br}from"node:path";import{homedir as Ls,hostname as ks}from"node:os";import{execFile as Os}from"node:child_process";import{access as Is,realpath as Cs}from"node:fs/promises";import{constants as xs}from"node:fs";import{delimiter as As,join as xr,isAbsolute as Ar}from"node:path";import{homedir as Cr}from"node:os";async function pe(e,t=process.env){let r=process.platform==="win32"?[".exe",".cmd",".bat",""]:[""];for(let n of(t.PATH||"").split(As).filter(Ar))for(let s of r){let i=xr(n,`${e}${s}`);try{return await Is(i,xs.X_OK),i}catch{}}return null}function Ns(e,t){return new Promise(r=>Os(e,t,{timeout:8e3,maxBuffer:1024*1024,windowsHide:!0},(n,s)=>r({code:n?n.code:0,stdout:String(s||"")})))}async function he(e){let t=e==="~"?Cr():e.startsWith("~/")?xr(Cr(),e.slice(2)):e;if(!Ar(t))throw new Error("\u5DE5\u4F5C\u76EE\u5F55\u5FC5\u987B\u662F\u8BBE\u5907\u4E0A\u7684\u7EDD\u5BF9\u8DEF\u5F84\u6216 ~/ \u5F00\u5934\u7684\u8DEF\u5F84");let r=await Cs(t),{stat:n}=await import("node:fs/promises");if(!(await n(r)).isDirectory())throw new Error("\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6\u5939");return r}async function Nr({find:e=pe,run:t=Ns,cwd:r=process.cwd()}={}){return(await Promise.all(["codex","claude","openclaw"].map(async s=>{let i=await e(s);if(!i)return[];let o={codex:"Codex",claude:"Claude Code",openclaw:"OpenClaw"}[s],a=await t(i,["--version"]),u=a.code===0&&a.stdout.match(/\d+\.\d+\.\d+[^\s]*/)?.[0]||"",d={id:s,name:o,framework:s,version:u,workspace:r,managed:!1};if(a.code!==0)return[{...d,status:"error",message:"\u7A0B\u5E8F\u65E0\u6CD5\u542F\u52A8\uFF0C\u8BF7\u5728\u672C\u673A\u68C0\u67E5\u5B89\u88C5"}];if(s==="openclaw"){let h=await t(i,["agents","list","--json"]),m;try{m=JSON.parse(h.stdout)}catch{}if(h.code!==0||!Array.isArray(m))return[{...d,status:"error",message:"\u65E0\u6CD5\u8BFB\u53D6 OpenClaw \u667A\u80FD\u4F53\uFF0C\u8BF7\u68C0\u67E5\u672C\u673A\u914D\u7F6E"}];let y=await t(i,["health","--json"]);return m.filter(S=>typeof S.id=="string"&&typeof S.workspace=="string").slice(0,32).map(S=>({...d,id:`openclaw:${S.id}`,name:S.name||S.identityName||S.id,workspace:S.workspace,managed:!0,localAgentId:S.id,command:i,status:y.code===0?"ready":"needs_setup",message:y.code===0?"":"\u8BF7\u5148\u5728\u672C\u673A\u542F\u52A8\u5E76\u914D\u7F6E OpenClaw Gateway"}))}let f=await t(i,s==="codex"?["login","status"]:["auth","status"]),p=f.code===0?"ready":f.code===1?"needs_login":"error";return[{...d,command:i,status:p,message:p==="needs_login"?`\u8BF7\u5728\u672C\u673A\u8FD0\u884C ${s} ${s==="codex"?"login":"auth login"}`:p==="error"?"\u767B\u5F55\u72B6\u6001\u68C0\u6D4B\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 CLI \u7248\u672C":""}]}))).flat()}async function Mr(e,t){let r=`${e}.${process.pid}.tmp`;await Us(r,JSON.stringify(t),{mode:384}),await Ps(r,e)}function ge(e){let t=new URL(e);if(t.username||t.password||t.search||t.hash||t.protocol!=="https:"&&!(t.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)))throw new Error("\u8FDE\u63A5\u5730\u5740\u5FC5\u987B\u4F7F\u7528 HTTPS\uFF1B\u4EC5\u672C\u673A\u5F00\u53D1\u5141\u8BB8 HTTP");let r=br(Ls(),".orcha","devices"),n=br(r,Ur("sha256").update(t.toString()).digest("hex").slice(0,20));return{address:t,root:r,profile:`${n}.json`,pidFile:`${n}.pid`,logFile:`${n}.log`}}async function bt(e,{url:t,pairCode:r}){let{address:n,root:s,profile:i}=ge(t||e.orchaUrl);await bs(s,{recursive:!0,mode:448});let o;if(r){let a=await fetch(new URL("api/agent-devices/pairings/exchange",`${n.toString().replace(/\/+$/,"")}/`),{method:"POST",redirect:"error",headers:{"content-type":"application/json"},signal:AbortSignal.timeout(15e3),body:JSON.stringify({code:r,name:ks()})}),u=await a.json();if(!a.ok||!u.success)throw new Error(u.message||u.detail||"\u8BBE\u5907\u8FDE\u63A5\u5931\u8D25");o={...u.data,targets:{}},await Mr(i,o)}else try{o=JSON.parse(await Ms(i,"utf8"))}catch{throw new Error("\u6CA1\u6709\u5DF2\u8FDE\u63A5\u8BBE\u5907\uFF0C\u8BF7\u4ECE\u667A\u80FD\u4F53\u9875\u9762\u751F\u6210\u8FDE\u63A5\u7801")}e.orchaUrl=n.toString().replace(/\/+$/,""),e.mode="node",e.nodeId=o.nodeCode,e.nodeName=o.nodeName,e.nodeToken=o.token,e.targets=o.targets,e.defaultTargetCode="",e.deviceConnection=new me(e,o,async()=>Mr(i,o))}var me=class{constructor(t,r,n,s=Nr){this.config=t,this.profile=r,this.persist=n,this.discover=s}async candidates(){return{candidates:(await this.discover()).map(({command:r,localAgentId:n,...s})=>s)}}async configure(t,r){if(!Array.isArray(t.selections)||!t.selections.length||t.selections.length>32)throw new Error("\u6DFB\u52A0\u9879\u65E0\u6548");let n=new Map((await this.discover()).map(u=>[u.id,u])),s={...r.targets},i=[],o=new Set;for(let u of t.selections){let d=n.get(u.candidateId);if(!d||d.status!=="ready"||o.has(d.id))throw new Error("\u53D1\u73B0\u7ED3\u679C\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u68C0\u6D4B");o.add(d.id);let f=String(u.name||"").trim();if(!f||f.length>128)throw new Error("\u667A\u80FD\u4F53\u540D\u79F0\u65E0\u6548");let p=await he(String(u.workspace||""));if(d.managed&&p!==await he(d.workspace))throw new Error("\u5DF2\u6709\u667A\u80FD\u4F53\u5FC5\u987B\u6CBF\u7528\u672C\u673A\u5DE5\u4F5C\u76EE\u5F55");let h=`${this.config.nodeId}:${Ur("sha256").update(d.id).digest("hex").slice(0,20)}`;if([...r.sessions.values()].some(m=>m.targetCode===h))throw new Error("\u6B64\u667A\u80FD\u4F53\u4ECD\u6709\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u7ED3\u675F\u4F1A\u8BDD\u518D\u4FEE\u6539\u63A5\u5165");s[h]={name:f,workspace:p,command:d.command,type:d.managed?"openclaw":"cli",...d.managed?{openclawAgentId:d.localAgentId}:d.framework==="codex"?{terminalProtocol:"codex",args:["exec","--skip-git-repo-check","--json"],promptMode:"stdin",outputMode:"json-lines",captureLastMessage:!0}:{terminalProtocol:"claude",args:["-p","--output-format","json","--no-session-persistence"],promptMode:"args",outputMode:"json-lines"}},i.push({candidateId:d.id,name:f,workspace:p,targetCode:h,framework:d.framework})}let a=this.profile.targets;this.profile.targets=s;try{await this.persist()}catch(u){throw this.profile.targets=a,u}return r.targets=s,this.config.targets=s,r.notifyCapacityChanged(),{agents:i}}};import{execFile as Ws}from"node:child_process";import{constants as Xs}from"node:fs";import{access as Br,readFile as jr,realpath as Ys}from"node:fs/promises";import{join as _e}from"node:path";import{promisify as Qs}from"node:util";var V="0.1.47";function yt(){return new Date().toISOString()}function P(e,t=2){return String(e).padStart(t,"0")}function Pr(e){let t=e>=0?"+":"-",r=Math.abs(e);return`${t}${P(Math.floor(r/60))}:${P(r%60)}`}function vs(e){let t=-e.getTimezoneOffset();return[`${e.getFullYear()}-${P(e.getMonth()+1)}-${P(e.getDate())}`,`T${P(e.getHours())}:${P(e.getMinutes())}:${P(e.getSeconds())}.${P(e.getMilliseconds(),3)}`,Pr(t)].join("")}function Ds(e,t){let r=new Intl.DateTimeFormat("en-US",{timeZone:t,hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}),n={};for(let s of r.formatToParts(e))s.type!=="literal"&&(n[s.type]=Number(s.value));return n}function Hs(e,t){let r=Ds(e,t),n=Date.UTC(r.year,r.month-1,r.day,r.hour,r.minute,r.second,e.getMilliseconds()),s=Math.round((n-e.getTime())/6e4);return[`${r.year}-${P(r.month)}-${P(r.day)}`,`T${P(r.hour)}:${P(r.minute)}:${P(r.second)}.${P(e.getMilliseconds(),3)}`,Pr(s)].join("")}function $s(e=new Date){let t=c(process.env.ORCHA_RUNTIME_LOG_TIMEZONE||process.env.TZ||"Asia/Shanghai");try{return Hs(e,t)}catch{return vs(e)}}function l(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function c(e){return String(e??"").trim()}function T(e,t){let r=Number.parseInt(String(e??""),10);return Number.isFinite(r)?r:t}function A(e){return Array.isArray(e)?e.map(String):[]}function tt(e,t,r=process.cwd()){let n=l(e)?e:{};return c(n.cwd)||c(n.workspace)||c(t)||c(r)||process.cwd()}var Mt="[redacted]",Fs=/(authorization|api[_-]?key|credential|password|secret|token)/i;function J(e){return Fs.test(c(e))}function ct(e,t=[]){let r=String(e??"");for(let n of t){let s=String(n??"");s.length>=4&&(r=r.split(s).join(Mt))}return r.replace(/((?:authorization|api[_-]?key|credential|password|secret|token)\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi,`$1${Mt}`).replace(/([?&](?:api[_-]?key|credential|password|secret|token)=)[^&\s]+/gi,`$1${Mt}`)}function Z(e,t=new WeakSet){if(e==null)return e;if(typeof e=="bigint")return e.toString();if(typeof e!="object")return e;if(e instanceof Date)return e.toISOString();if(e instanceof Error){if(t.has(e))return"[circular]";t.add(e);let s={name:e.name,message:e.message};e.stack&&(s.stack=e.stack);for(let i of Object.keys(e))s[i]=Z(e[i],t);return t.delete(e),s}if(t.has(e))return"[circular]";if(t.add(e),Array.isArray(e)){let s=e.map(i=>Z(i,t));return t.delete(e),s}let r={},n=J(e.name||e.key);for(let[s,i]of Object.entries(e))r[s]=J(s)||n&&s.toLowerCase()==="value"?Mt:Z(i,t);return t.delete(e),r}function Lr(e,t=4e3){let r=JSON.stringify(Z(e))??String(e);return!Number.isFinite(t)||r.length<=t?r:`${r.slice(0,t)}...`}function g(e,t,r,n={}){let s={ts:$s(),level:e,module:t,msg:r,...l(n)?Z(n):{}};console.error(JSON.stringify(s))}var et="application/x-orcha-channel-events-v2+ndjson";function M(e,t){return Object.assign(new Error(t||(["run_timeout","RUNTIME_NODE_TIMEOUT"].includes(e)?"OpenClaw \u539F\u751F IM \u6267\u884C\u8D85\u65F6\uFF0C\u4EFB\u52A1\u5DF2\u7ED3\u675F\uFF0C\u8BF7\u91CD\u8BD5\u3002":e==="OPENCLAW_CHANNEL_PROTOCOL_MISMATCH"?"OpenClaw \u63D2\u4EF6\u4E0E Bridge \u7684\u539F\u751F IM \u534F\u8BAE\u4E0D\u517C\u5BB9\uFF0C\u8BF7\u914D\u5957\u5347\u7EA7\u3002":`OpenClaw \u539F\u751F IM \u6267\u884C\u5931\u8D25\uFF08${e}\uFF09`)),{code:e})}function kr(e,t){let r=e.cause?.code||e.code;return t?.reason?.name==="TimeoutError"||e.name==="TimeoutError"||["UND_ERR_HEADERS_TIMEOUT","UND_ERR_BODY_TIMEOUT","UND_ERR_CONNECT_TIMEOUT","ETIMEDOUT"].includes(r)?Object.assign(M("RUNTIME_NODE_TIMEOUT","OpenClaw \u8FDE\u63A5\u6216\u7B49\u5F85\u54CD\u5E94\u8D85\u65F6\uFF0C\u4EFB\u52A1\u5DF2\u7ED3\u675F\uFF0C\u8BF7\u91CD\u8BD5\u3002"),{detail:{causeCode:r||"REQUEST_TIMEOUT"}}):e}async function vr(e,t){if(e.headers.get("content-type")!==et)throw await e.body?.cancel(),M("OPENCLAW_CHANNEL_PROTOCOL_MISMATCH");let r=Buffer.alloc(0);for await(let n of e.body){r=Buffer.concat([r,n]);let s;for(;(s=r.indexOf(10))>=0;){if(s>2097152)throw M("OPENCLAW_CHANNEL_FRAME_TOO_LARGE");let i=r.subarray(0,s).toString("utf8");r=r.subarray(s+1);let o;try{o=JSON.parse(i)}catch{throw M("OPENCLAW_CHANNEL_INVALID_FRAME")}if(o.type!=="heartbeat")if(o.type==="update"&&o.payload?.update&&o.payload?._meta?.channelUpdateId)await t(o.payload);else{if(o.type==="result"&&o.result?.stopReason)return o.result;throw o.type==="error"&&typeof o.code=="string"?M(o.code):M("OPENCLAW_CHANNEL_INVALID_FRAME")}}if(r.length>2097152)throw M("OPENCLAW_CHANNEL_FRAME_TOO_LARGE")}throw M("OPENCLAW_CHANNEL_STREAM_INTERRUPTED","OpenClaw \u8FC7\u7A0B\u8FDE\u63A5\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u4EFB\u52A1\u7EC8\u6001\uFF0C\u8BF7\u68C0\u67E5\u8282\u70B9\u8FDE\u63A5\u540E\u91CD\u8BD5\u3002")}import{createServer as js}from"node:http";import{randomBytes as qs}from"node:crypto";var Ut={controlTools:["find_capabilities_and_skills","describe_capability","inspect_result","execute_capability"],residentTools:["search_orcha_docs","knowledge_search"]};var Gs=Object.freeze([...Ut.controlTools,...Ut.residentTools]),Ks=new Set(Gs),zs=8*1024*1024;function Pt(e,t){if(!Array.isArray(e)||e.length!==1)throw new Error("one task-scoped Orcha MCP server is required");let r=e[0],n=new URL(r.url);if(r.name!=="orcha"||r.type!=="http"||!["http:","https:"].includes(n.protocol)||n.username||n.password||n.hash||t&&n.href!==new URL(t).href)throw new Error("Orcha MCP server does not match the trusted binding");let s=Object.fromEntries((r.headers||[]).map(({name:i,value:o})=>[i.toLowerCase(),o]));if(!String(s.authorization||"").startsWith("Bearer "))throw new Error("Orcha MCP task credentials are missing");return{url:n.href,headers:{...s,"content-type":"application/json",accept:"application/json"}}}async function Hr(e){let t=0,r=[];for await(let n of e){if(t+=n.length,t>zs)throw new Error("MCP payload exceeds the size limit");r.push(Buffer.from(n))}return JSON.parse(Buffer.concat(r).toString("utf8"))}async function $r(e,t,r){let n=await fetch(e.url,{method:"POST",headers:e.headers,body:JSON.stringify(t),redirect:"error",signal:AbortSignal.any([AbortSignal.timeout(36e5),...r?[r]:[]])});if(!n.ok)throw new Error(`Orcha MCP request failed (${n.status})`);if(n.status===202||n.status===204)return null;let s=await Hr(n.body);if(!s||s.jsonrpc!=="2.0"||s.id!==t.id)throw new Error("Orcha MCP response identity mismatch");return s}async function Dr(e,t){let r=await $r(e,{jsonrpc:"2.0",id:"orcha-bind-tools",method:"tools/list"},t),n=r?.result?.tools;if(r?.error){let s=Object.entries(e.headers).filter(([a])=>J(a)).flatMap(([,a])=>[a,String(a).replace(/^Bearer\s+/i,"")]),i=typeof r.error.message=="string"?ct(r.error.message,s).slice(0,512):"",o=Number.isInteger(r.error.code)?` (${r.error.code})`:"";throw new Error(`Orcha MCP tool discovery failed${o}${i?`: ${i}`:""}`)}return Js(n)}function Js(e){if(!Array.isArray(e)||e.some(t=>!t||!Ks.has(t.name)||t.inputSchema?.type!=="object"||typeof t.description!="string")||new Set(e.map(t=>t.name)).size!==e.length||e.length&&Ut.controlTools.some(t=>!e.some(r=>r.name===t)))throw new Error("Orcha MCP returned an unsupported or invalid authorized tool catalog");return e}function Vs(e,t){let r=s=>Array.isArray(s)?s.map(r):s&&typeof s=="object"?Object.fromEntries(Object.keys(s).sort().map(i=>[i,r(s[i])])):s,n=s=>s.map(({name:i,inputSchema:o})=>({name:i,inputSchema:o})).sort((i,o)=>i.name.localeCompare(o.name));return JSON.stringify(r(n(e)))===JSON.stringify(r(n(t)))}async function Fr(e){let t=Pt(e),r=new AbortController,n=await Dr(t,r.signal),s=qs(32).toString("hex"),i=js(async(o,a)=>{if(o.url!=="/mcp"||o.headers.authorization!==`Bearer ${s}`){a.writeHead(403).end();return}if(o.method!=="POST"){a.writeHead(405).end();return}try{let u=await Hr(o);if(!u||u.jsonrpc!=="2.0"||typeof u.method!="string")throw new Error("Invalid MCP request");if(u.method==="tools/call"&&!n.some(f=>f.name===u.params?.name))throw new Error("Tool is not authorized in this task binding");let d=await $r(t,u,r.signal);a.writeHead(d===null?202:200,{"content-type":"application/json"}),a.end(d===null?"":JSON.stringify(d))}catch{a.writeHead(502,{"content-type":"application/json"}).end(JSON.stringify({error:"The task-scoped Orcha MCP connection failed"}))}});return await new Promise((o,a)=>{i.once("error",a),i.listen(0,"127.0.0.1",o)}),{url:`http://127.0.0.1:${i.address().port}/mcp`,token:s,async refresh(o){let a=Pt(o,t.url);if(!Vs(n,await Dr(a,r.signal)))throw new Error("The frozen MCP tool catalog changed; start a new task");t=a},async close(){r.abort(),i.closeAllConnections(),await new Promise(o=>i.close(o))}}}var W=class{constructor(t,r){this.target=t,this.identity=r,this.bound=!1}async request(t,r,n=!1,s="mcp-bindings",i=3e4,o=null){let a=c(this.target.mcpBridgeUrl),u=c(this.target.mcpBridgeTokenEnv),d=this.target.env?.[u]||process.env[u];if(!a||!u||!d)throw Object.assign(new Error("OpenClaw requires the Orcha tool plugin, mcpBridgeUrl and mcpBridgeTokenEnv"),{code:"OPENCLAW_MCP_NOT_CONFIGURED"});let f=new URL(a);if(!["http:","https:"].includes(f.protocol)||f.username||f.password||f.pathname!=="/plugins/orcha-runtime/mcp-bindings"||f.search||f.hash)throw new Error("OpenClaw MCP binding URL must be the trusted Gateway plugin route");if(!["mcp-bindings","deliveries","background"].includes(s))throw new Error("Unsupported Orcha plugin route");f.pathname=`/plugins/orcha-runtime/${s}`;let p=AbortSignal.timeout(i);try{let h=await fetch(f,{method:t,redirect:"error",signal:p,headers:{authorization:`Bearer ${d}`,"content-type":"application/json",...o?{accept:et}:{},...n?{"if-match":this.identity.runId}:{}},...r!==void 0?{body:JSON.stringify(r)}:{}});if(t==="GET"&&s==="deliveries"&&[404,405].includes(h.status))throw await h.body?.cancel(),M("OPENCLAW_CHANNEL_PROTOCOL_MISMATCH");if(h.ok&&o)return await vr(h,o);let m=await h.json();if(!h.ok)throw M(m.error||"OPENCLAW_MCP_BINDING_FAILED");return m}catch(h){throw kr(h,p)}}async bind(t,r=!1){Pt(t),await this.request("PUT",{...this.identity,mcpServer:t[0]},r),this.bound=!0}async close(){this.bound&&(await this.request("DELETE",{runId:this.identity.runId,sessionKey:this.identity.sessionKey}),this.bound=!1)}};var Zs=Qs(Ws),ti=12e4;async function qr(e,t){let r=new Map,n=[];for(let s of Object.values(e)){if(s.type!=="openclaw")continue;if(s.mcpBridgeUrl&&(n.push(s),!["localhost","127.0.0.1","[::1]"].includes(new URL(s.mcpBridgeUrl).hostname))){process.stderr.write(`\u8FDC\u7A0B OpenClaw Gateway \u7684\u914D\u5957\u63D2\u4EF6\u9700\u5728\u6240\u5728\u4E3B\u673A\u66F4\u65B0\uFF1Breload \u5C06\u68C0\u67E5\u5176\u534F\u8BAE\u3002
|
|
2
|
+
import{spawn as dc}from"node:child_process";import{closeSync as fc,existsSync as Er,mkdirSync as pc,openSync as hc,readFileSync as yr,rmSync as mc,statSync as us,writeFileSync as hs}from"node:fs";import{dirname as ms,resolve as ht}from"node:path";import{fileURLToPath as gc}from"node:url";import{createHash as Ur}from"node:crypto";import{mkdir as bs,readFile as Ms,writeFile as Us,rename as Ps}from"node:fs/promises";import{join as br}from"node:path";import{homedir as Ls,hostname as ks}from"node:os";import{execFile as Os}from"node:child_process";import{access as Is,realpath as Cs}from"node:fs/promises";import{constants as xs}from"node:fs";import{delimiter as As,join as xr,isAbsolute as Ar}from"node:path";import{homedir as Cr}from"node:os";async function pe(e,t=process.env){let r=process.platform==="win32"?[".exe",".cmd",".bat",""]:[""];for(let n of(t.PATH||"").split(As).filter(Ar))for(let s of r){let i=xr(n,`${e}${s}`);try{return await Is(i,xs.X_OK),i}catch{}}return null}function Ns(e,t){return new Promise(r=>Os(e,t,{timeout:8e3,maxBuffer:1024*1024,windowsHide:!0},(n,s)=>r({code:n?n.code:0,stdout:String(s||"")})))}async function he(e){let t=e==="~"?Cr():e.startsWith("~/")?xr(Cr(),e.slice(2)):e;if(!Ar(t))throw new Error("\u5DE5\u4F5C\u76EE\u5F55\u5FC5\u987B\u662F\u8BBE\u5907\u4E0A\u7684\u7EDD\u5BF9\u8DEF\u5F84\u6216 ~/ \u5F00\u5934\u7684\u8DEF\u5F84");let r=await Cs(t),{stat:n}=await import("node:fs/promises");if(!(await n(r)).isDirectory())throw new Error("\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6\u5939");return r}async function Nr({find:e=pe,run:t=Ns,cwd:r=process.cwd()}={}){return(await Promise.all(["codex","claude","openclaw"].map(async s=>{let i=await e(s);if(!i)return[];let o={codex:"Codex",claude:"Claude Code",openclaw:"OpenClaw"}[s],a=await t(i,["--version"]),u=a.code===0&&a.stdout.match(/\d+\.\d+\.\d+[^\s]*/)?.[0]||"",d={id:s,name:o,framework:s,version:u,workspace:r,managed:!1};if(a.code!==0)return[{...d,status:"error",message:"\u7A0B\u5E8F\u65E0\u6CD5\u542F\u52A8\uFF0C\u8BF7\u5728\u672C\u673A\u68C0\u67E5\u5B89\u88C5"}];if(s==="openclaw"){let h=await t(i,["agents","list","--json"]),m;try{m=JSON.parse(h.stdout)}catch{}if(h.code!==0||!Array.isArray(m))return[{...d,status:"error",message:"\u65E0\u6CD5\u8BFB\u53D6 OpenClaw \u667A\u80FD\u4F53\uFF0C\u8BF7\u68C0\u67E5\u672C\u673A\u914D\u7F6E"}];let y=await t(i,["health","--json"]);return m.filter(S=>typeof S.id=="string"&&typeof S.workspace=="string").slice(0,32).map(S=>({...d,id:`openclaw:${S.id}`,name:S.name||S.identityName||S.id,workspace:S.workspace,managed:!0,localAgentId:S.id,command:i,status:y.code===0?"ready":"needs_setup",message:y.code===0?"":"\u8BF7\u5148\u5728\u672C\u673A\u542F\u52A8\u5E76\u914D\u7F6E OpenClaw Gateway"}))}let f=await t(i,s==="codex"?["login","status"]:["auth","status"]),p=f.code===0?"ready":f.code===1?"needs_login":"error";return[{...d,command:i,status:p,message:p==="needs_login"?`\u8BF7\u5728\u672C\u673A\u8FD0\u884C ${s} ${s==="codex"?"login":"auth login"}`:p==="error"?"\u767B\u5F55\u72B6\u6001\u68C0\u6D4B\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 CLI \u7248\u672C":""}]}))).flat()}async function Mr(e,t){let r=`${e}.${process.pid}.tmp`;await Us(r,JSON.stringify(t),{mode:384}),await Ps(r,e)}function ge(e){let t=new URL(e);if(t.username||t.password||t.search||t.hash||t.protocol!=="https:"&&!(t.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)))throw new Error("\u8FDE\u63A5\u5730\u5740\u5FC5\u987B\u4F7F\u7528 HTTPS\uFF1B\u4EC5\u672C\u673A\u5F00\u53D1\u5141\u8BB8 HTTP");let r=br(Ls(),".orcha","devices"),n=br(r,Ur("sha256").update(t.toString()).digest("hex").slice(0,20));return{address:t,root:r,profile:`${n}.json`,pidFile:`${n}.pid`,logFile:`${n}.log`}}async function bt(e,{url:t,pairCode:r}){let{address:n,root:s,profile:i}=ge(t||e.orchaUrl);await bs(s,{recursive:!0,mode:448});let o;if(r){let a=await fetch(new URL("api/agent-devices/pairings/exchange",`${n.toString().replace(/\/+$/,"")}/`),{method:"POST",redirect:"error",headers:{"content-type":"application/json"},signal:AbortSignal.timeout(15e3),body:JSON.stringify({code:r,name:ks()})}),u=await a.json();if(!a.ok||!u.success)throw new Error(u.message||u.detail||"\u8BBE\u5907\u8FDE\u63A5\u5931\u8D25");o={...u.data,targets:{}},await Mr(i,o)}else try{o=JSON.parse(await Ms(i,"utf8"))}catch{throw new Error("\u6CA1\u6709\u5DF2\u8FDE\u63A5\u8BBE\u5907\uFF0C\u8BF7\u4ECE\u667A\u80FD\u4F53\u9875\u9762\u751F\u6210\u8FDE\u63A5\u7801")}e.orchaUrl=n.toString().replace(/\/+$/,""),e.mode="node",e.nodeId=o.nodeCode,e.nodeName=o.nodeName,e.nodeToken=o.token,e.targets=o.targets,e.defaultTargetCode="",e.deviceConnection=new me(e,o,async()=>Mr(i,o))}var me=class{constructor(t,r,n,s=Nr){this.config=t,this.profile=r,this.persist=n,this.discover=s}async candidates(){return{candidates:(await this.discover()).map(({command:r,localAgentId:n,...s})=>s)}}async configure(t,r){if(!Array.isArray(t.selections)||!t.selections.length||t.selections.length>32)throw new Error("\u6DFB\u52A0\u9879\u65E0\u6548");let n=new Map((await this.discover()).map(u=>[u.id,u])),s={...r.targets},i=[],o=new Set;for(let u of t.selections){let d=n.get(u.candidateId);if(!d||d.status!=="ready"||o.has(d.id))throw new Error("\u53D1\u73B0\u7ED3\u679C\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u68C0\u6D4B");o.add(d.id);let f=String(u.name||"").trim();if(!f||f.length>128)throw new Error("\u667A\u80FD\u4F53\u540D\u79F0\u65E0\u6548");let p=await he(String(u.workspace||""));if(d.managed&&p!==await he(d.workspace))throw new Error("\u5DF2\u6709\u667A\u80FD\u4F53\u5FC5\u987B\u6CBF\u7528\u672C\u673A\u5DE5\u4F5C\u76EE\u5F55");let h=`${this.config.nodeId}:${Ur("sha256").update(d.id).digest("hex").slice(0,20)}`;if([...r.sessions.values()].some(m=>m.targetCode===h))throw new Error("\u6B64\u667A\u80FD\u4F53\u4ECD\u6709\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u7ED3\u675F\u4F1A\u8BDD\u518D\u4FEE\u6539\u63A5\u5165");s[h]={name:f,workspace:p,command:d.command,type:d.managed?"openclaw":"cli",...d.managed?{openclawAgentId:d.localAgentId}:d.framework==="codex"?{terminalProtocol:"codex",args:["exec","--skip-git-repo-check","--json"],promptMode:"stdin",outputMode:"json-lines",captureLastMessage:!0}:{terminalProtocol:"claude",args:["-p","--output-format","json","--no-session-persistence"],promptMode:"args",outputMode:"json-lines"}},i.push({candidateId:d.id,name:f,workspace:p,targetCode:h,framework:d.framework})}let a=this.profile.targets;this.profile.targets=s;try{await this.persist()}catch(u){throw this.profile.targets=a,u}return r.targets=s,this.config.targets=s,r.notifyCapacityChanged(),{agents:i}}};import{execFile as Ws}from"node:child_process";import{constants as Xs}from"node:fs";import{access as Br,readFile as jr,realpath as Ys}from"node:fs/promises";import{join as _e}from"node:path";import{promisify as Qs}from"node:util";var V="0.1.48";function yt(){return new Date().toISOString()}function P(e,t=2){return String(e).padStart(t,"0")}function Pr(e){let t=e>=0?"+":"-",r=Math.abs(e);return`${t}${P(Math.floor(r/60))}:${P(r%60)}`}function vs(e){let t=-e.getTimezoneOffset();return[`${e.getFullYear()}-${P(e.getMonth()+1)}-${P(e.getDate())}`,`T${P(e.getHours())}:${P(e.getMinutes())}:${P(e.getSeconds())}.${P(e.getMilliseconds(),3)}`,Pr(t)].join("")}function Ds(e,t){let r=new Intl.DateTimeFormat("en-US",{timeZone:t,hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}),n={};for(let s of r.formatToParts(e))s.type!=="literal"&&(n[s.type]=Number(s.value));return n}function Hs(e,t){let r=Ds(e,t),n=Date.UTC(r.year,r.month-1,r.day,r.hour,r.minute,r.second,e.getMilliseconds()),s=Math.round((n-e.getTime())/6e4);return[`${r.year}-${P(r.month)}-${P(r.day)}`,`T${P(r.hour)}:${P(r.minute)}:${P(r.second)}.${P(e.getMilliseconds(),3)}`,Pr(s)].join("")}function $s(e=new Date){let t=c(process.env.ORCHA_RUNTIME_LOG_TIMEZONE||process.env.TZ||"Asia/Shanghai");try{return Hs(e,t)}catch{return vs(e)}}function l(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function c(e){return String(e??"").trim()}function T(e,t){let r=Number.parseInt(String(e??""),10);return Number.isFinite(r)?r:t}function A(e){return Array.isArray(e)?e.map(String):[]}function tt(e,t,r=process.cwd()){let n=l(e)?e:{};return c(n.cwd)||c(n.workspace)||c(t)||c(r)||process.cwd()}var Mt="[redacted]",Fs=/(authorization|api[_-]?key|credential|password|secret|token)/i;function J(e){return Fs.test(c(e))}function ct(e,t=[]){let r=String(e??"");for(let n of t){let s=String(n??"");s.length>=4&&(r=r.split(s).join(Mt))}return r.replace(/((?:authorization|api[_-]?key|credential|password|secret|token)\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi,`$1${Mt}`).replace(/([?&](?:api[_-]?key|credential|password|secret|token)=)[^&\s]+/gi,`$1${Mt}`)}function Z(e,t=new WeakSet){if(e==null)return e;if(typeof e=="bigint")return e.toString();if(typeof e!="object")return e;if(e instanceof Date)return e.toISOString();if(e instanceof Error){if(t.has(e))return"[circular]";t.add(e);let s={name:e.name,message:e.message};e.stack&&(s.stack=e.stack);for(let i of Object.keys(e))s[i]=Z(e[i],t);return t.delete(e),s}if(t.has(e))return"[circular]";if(t.add(e),Array.isArray(e)){let s=e.map(i=>Z(i,t));return t.delete(e),s}let r={},n=J(e.name||e.key);for(let[s,i]of Object.entries(e))r[s]=J(s)||n&&s.toLowerCase()==="value"?Mt:Z(i,t);return t.delete(e),r}function Lr(e,t=4e3){let r=JSON.stringify(Z(e))??String(e);return!Number.isFinite(t)||r.length<=t?r:`${r.slice(0,t)}...`}function g(e,t,r,n={}){let s={ts:$s(),level:e,module:t,msg:r,...l(n)?Z(n):{}};console.error(JSON.stringify(s))}var et="application/x-orcha-channel-events-v2+ndjson";function M(e,t){return Object.assign(new Error(t||(["run_timeout","RUNTIME_NODE_TIMEOUT"].includes(e)?"OpenClaw \u539F\u751F IM \u6267\u884C\u8D85\u65F6\uFF0C\u4EFB\u52A1\u5DF2\u7ED3\u675F\uFF0C\u8BF7\u91CD\u8BD5\u3002":e==="OPENCLAW_CHANNEL_PROTOCOL_MISMATCH"?"OpenClaw \u63D2\u4EF6\u4E0E Bridge \u7684\u539F\u751F IM \u534F\u8BAE\u4E0D\u517C\u5BB9\uFF0C\u8BF7\u914D\u5957\u5347\u7EA7\u3002":`OpenClaw \u539F\u751F IM \u6267\u884C\u5931\u8D25\uFF08${e}\uFF09`)),{code:e})}function kr(e,t){let r=e.cause?.code||e.code;return t?.reason?.name==="TimeoutError"||e.name==="TimeoutError"||["UND_ERR_HEADERS_TIMEOUT","UND_ERR_BODY_TIMEOUT","UND_ERR_CONNECT_TIMEOUT","ETIMEDOUT"].includes(r)?Object.assign(M("RUNTIME_NODE_TIMEOUT","OpenClaw \u8FDE\u63A5\u6216\u7B49\u5F85\u54CD\u5E94\u8D85\u65F6\uFF0C\u4EFB\u52A1\u5DF2\u7ED3\u675F\uFF0C\u8BF7\u91CD\u8BD5\u3002"),{detail:{causeCode:r||"REQUEST_TIMEOUT"}}):e}async function vr(e,t){if(e.headers.get("content-type")!==et)throw await e.body?.cancel(),M("OPENCLAW_CHANNEL_PROTOCOL_MISMATCH");let r=Buffer.alloc(0);for await(let n of e.body){r=Buffer.concat([r,n]);let s;for(;(s=r.indexOf(10))>=0;){if(s>2097152)throw M("OPENCLAW_CHANNEL_FRAME_TOO_LARGE");let i=r.subarray(0,s).toString("utf8");r=r.subarray(s+1);let o;try{o=JSON.parse(i)}catch{throw M("OPENCLAW_CHANNEL_INVALID_FRAME")}if(o.type!=="heartbeat")if(o.type==="update"&&o.payload?.update&&o.payload?._meta?.channelUpdateId)await t(o.payload);else{if(o.type==="result"&&o.result?.stopReason)return o.result;throw o.type==="error"&&typeof o.code=="string"?M(o.code):M("OPENCLAW_CHANNEL_INVALID_FRAME")}}if(r.length>2097152)throw M("OPENCLAW_CHANNEL_FRAME_TOO_LARGE")}throw M("OPENCLAW_CHANNEL_STREAM_INTERRUPTED","OpenClaw \u8FC7\u7A0B\u8FDE\u63A5\u4E2D\u65AD\uFF0C\u672A\u6536\u5230\u4EFB\u52A1\u7EC8\u6001\uFF0C\u8BF7\u68C0\u67E5\u8282\u70B9\u8FDE\u63A5\u540E\u91CD\u8BD5\u3002")}import{createServer as js}from"node:http";import{randomBytes as qs}from"node:crypto";var Ut={controlTools:["find_capabilities_and_skills","describe_capability","inspect_result","execute_capability"],residentTools:["search_orcha_docs","knowledge_search"]};var Gs=Object.freeze([...Ut.controlTools,...Ut.residentTools]),Ks=new Set(Gs),zs=8*1024*1024;function Pt(e,t){if(!Array.isArray(e)||e.length!==1)throw new Error("one task-scoped Orcha MCP server is required");let r=e[0],n=new URL(r.url);if(r.name!=="orcha"||r.type!=="http"||!["http:","https:"].includes(n.protocol)||n.username||n.password||n.hash||t&&n.href!==new URL(t).href)throw new Error("Orcha MCP server does not match the trusted binding");let s=Object.fromEntries((r.headers||[]).map(({name:i,value:o})=>[i.toLowerCase(),o]));if(!String(s.authorization||"").startsWith("Bearer "))throw new Error("Orcha MCP task credentials are missing");return{url:n.href,headers:{...s,"content-type":"application/json",accept:"application/json"}}}async function Hr(e){let t=0,r=[];for await(let n of e){if(t+=n.length,t>zs)throw new Error("MCP payload exceeds the size limit");r.push(Buffer.from(n))}return JSON.parse(Buffer.concat(r).toString("utf8"))}async function $r(e,t,r){let n=await fetch(e.url,{method:"POST",headers:e.headers,body:JSON.stringify(t),redirect:"error",signal:AbortSignal.any([AbortSignal.timeout(36e5),...r?[r]:[]])});if(!n.ok)throw new Error(`Orcha MCP request failed (${n.status})`);if(n.status===202||n.status===204)return null;let s=await Hr(n.body);if(!s||s.jsonrpc!=="2.0"||s.id!==t.id)throw new Error("Orcha MCP response identity mismatch");return s}async function Dr(e,t){let r=await $r(e,{jsonrpc:"2.0",id:"orcha-bind-tools",method:"tools/list"},t),n=r?.result?.tools;if(r?.error){let s=Object.entries(e.headers).filter(([a])=>J(a)).flatMap(([,a])=>[a,String(a).replace(/^Bearer\s+/i,"")]),i=typeof r.error.message=="string"?ct(r.error.message,s).slice(0,512):"",o=Number.isInteger(r.error.code)?` (${r.error.code})`:"";throw new Error(`Orcha MCP tool discovery failed${o}${i?`: ${i}`:""}`)}return Js(n)}function Js(e){if(!Array.isArray(e)||e.some(t=>!t||!Ks.has(t.name)||t.inputSchema?.type!=="object"||typeof t.description!="string")||new Set(e.map(t=>t.name)).size!==e.length||e.length&&Ut.controlTools.some(t=>!e.some(r=>r.name===t)))throw new Error("Orcha MCP returned an unsupported or invalid authorized tool catalog");return e}function Vs(e,t){let r=s=>Array.isArray(s)?s.map(r):s&&typeof s=="object"?Object.fromEntries(Object.keys(s).sort().map(i=>[i,r(s[i])])):s,n=s=>s.map(({name:i,inputSchema:o})=>({name:i,inputSchema:o})).sort((i,o)=>i.name.localeCompare(o.name));return JSON.stringify(r(n(e)))===JSON.stringify(r(n(t)))}async function Fr(e){let t=Pt(e),r=new AbortController,n=await Dr(t,r.signal),s=qs(32).toString("hex"),i=js(async(o,a)=>{if(o.url!=="/mcp"||o.headers.authorization!==`Bearer ${s}`){a.writeHead(403).end();return}if(o.method!=="POST"){a.writeHead(405).end();return}try{let u=await Hr(o);if(!u||u.jsonrpc!=="2.0"||typeof u.method!="string")throw new Error("Invalid MCP request");if(u.method==="tools/call"&&!n.some(f=>f.name===u.params?.name))throw new Error("Tool is not authorized in this task binding");let d=await $r(t,u,r.signal);a.writeHead(d===null?202:200,{"content-type":"application/json"}),a.end(d===null?"":JSON.stringify(d))}catch{a.writeHead(502,{"content-type":"application/json"}).end(JSON.stringify({error:"The task-scoped Orcha MCP connection failed"}))}});return await new Promise((o,a)=>{i.once("error",a),i.listen(0,"127.0.0.1",o)}),{url:`http://127.0.0.1:${i.address().port}/mcp`,token:s,async refresh(o){let a=Pt(o,t.url);if(!Vs(n,await Dr(a,r.signal)))throw new Error("The frozen MCP tool catalog changed; start a new task");t=a},async close(){r.abort(),i.closeAllConnections(),await new Promise(o=>i.close(o))}}}var W=class{constructor(t,r){this.target=t,this.identity=r,this.bound=!1}async request(t,r,n=!1,s="mcp-bindings",i=3e4,o=null){let a=c(this.target.mcpBridgeUrl),u=c(this.target.mcpBridgeTokenEnv),d=this.target.env?.[u]||process.env[u];if(!a||!u||!d)throw Object.assign(new Error("OpenClaw requires the Orcha tool plugin, mcpBridgeUrl and mcpBridgeTokenEnv"),{code:"OPENCLAW_MCP_NOT_CONFIGURED"});let f=new URL(a);if(!["http:","https:"].includes(f.protocol)||f.username||f.password||f.pathname!=="/plugins/orcha-runtime/mcp-bindings"||f.search||f.hash)throw new Error("OpenClaw MCP binding URL must be the trusted Gateway plugin route");if(!["mcp-bindings","deliveries","background"].includes(s))throw new Error("Unsupported Orcha plugin route");f.pathname=`/plugins/orcha-runtime/${s}`;let p=AbortSignal.timeout(i);try{let h=await fetch(f,{method:t,redirect:"error",signal:p,headers:{authorization:`Bearer ${d}`,"content-type":"application/json",...o?{accept:et}:{},...n?{"if-match":this.identity.runId}:{}},...r!==void 0?{body:JSON.stringify(r)}:{}});if(t==="GET"&&s==="deliveries"&&[404,405].includes(h.status))throw await h.body?.cancel(),M("OPENCLAW_CHANNEL_PROTOCOL_MISMATCH");if(h.ok&&o)return await vr(h,o);let m=await h.json();if(!h.ok)throw M(m.error||"OPENCLAW_MCP_BINDING_FAILED");return m}catch(h){throw kr(h,p)}}async bind(t,r=!1){Pt(t),await this.request("PUT",{...this.identity,mcpServer:t[0]},r),this.bound=!0}async close(){this.bound&&(await this.request("DELETE",{runId:this.identity.runId,sessionKey:this.identity.sessionKey}),this.bound=!1)}};var Zs=Qs(Ws),ti=12e4;async function qr(e,t){let r=new Map,n=[];for(let s of Object.values(e)){if(s.type!=="openclaw")continue;if(s.mcpBridgeUrl&&(n.push(s),!["localhost","127.0.0.1","[::1]"].includes(new URL(s.mcpBridgeUrl).hostname))){process.stderr.write(`\u8FDC\u7A0B OpenClaw Gateway \u7684\u914D\u5957\u63D2\u4EF6\u9700\u5728\u6240\u5728\u4E3B\u673A\u66F4\u65B0\uFF1Breload \u5C06\u68C0\u67E5\u5176\u534F\u8BAE\u3002
|
|
3
3
|
`);continue}let i={...process.env,...s.env},o=c(s.command)||"openclaw",a=o.includes("/")?o:await pe(o,i);if(!a)throw new Error("\u627E\u4E0D\u5230\u5DF2\u63A5\u5165\u7684 OpenClaw\uFF0C\u8BF7\u68C0\u67E5\u672C\u673A\u5B89\u88C5\u540E\u91CD\u8BD5 reload");let u=await Ys(a);await Br(u,Xs.X_OK);let d=JSON.stringify([u,Object.entries(i).sort(([f],[p])=>f.localeCompare(p))]);r.has(d)||r.set(d,{command:u,env:i})}if(r.size)try{let s=JSON.parse(await jr(_e(t,"openclaw.plugin.json"),"utf8")),i=JSON.parse(await jr(_e(t,"package.json"),"utf8"));if(await Br(_e(t,"index.js")),s.id!=="orcha-runtime"||i.version!==V)throw new Error("\u63D2\u4EF6\u6807\u8BC6\u6216\u7248\u672C\u4E0D\u5339\u914D")}catch(s){throw new Error("\u914D\u5957 OpenClaw \u63D2\u4EF6\u7F3A\u5931\u3001\u635F\u574F\u6216\u7248\u672C\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5 Bridge\uFF1B\u6E90\u7801\u8FD0\u884C\u8BF7\u5148 npm run build",{cause:s})}return async()=>{for(let s of r.values()){process.stderr.write(`\u6B63\u5728\u5B89\u88C5\u914D\u5957 OpenClaw \u63D2\u4EF6\u5E76\u91CD\u542F Gateway\u2026
|
|
4
4
|
`),await ye(s,["plugins","install",t,"--force"]),await ye(s,["gateway","restart"]);let i=await ye(s,["health","--json"]);if(JSON.parse(i).ok!==!0)throw new Error("OpenClaw Gateway \u5065\u5EB7\u68C0\u67E5\u5931\u8D25\uFF0CBridge \u672A\u542F\u52A8\uFF0C\u8BF7\u4FEE\u590D\u540E\u91CD\u8BD5 reload");process.stderr.write(`OpenClaw \u914D\u5957\u63D2\u4EF6\u5DF2\u66F4\u65B0\uFF0CGateway \u5065\u5EB7\u68C0\u67E5\u901A\u8FC7\u3002
|
|
5
5
|
`)}for(let s of n)if((await new W(s,{}).request("GET",void 0,!1,"deliveries")).streamType!==et)throw M("OPENCLAW_CHANNEL_PROTOCOL_MISMATCH")}}async function ye(e,t){try{let{stdout:r}=await Zs(e.command,t,{env:e.env,timeout:ti,killSignal:"SIGKILL",maxBuffer:1048576,windowsHide:!0});return r}catch(r){let n=Object.entries(e.env).filter(([i])=>J(i)).map(([,i])=>i),s=ct(r.stderr||r.message,n).trim();throw new Error(`OpenClaw ${t.slice(0,2).join(" ")} \u5931\u8D25\uFF0CBridge \u672A\u542F\u52A8\uFF0C\u8BF7\u4FEE\u590D\u540E\u91CD\u8BD5 reload\uFF1A${s}`)}}import{createHash as ca,randomUUID as te}from"node:crypto";function Lt(e){return Array.isArray(e)?e.map(t=>l(t)?t.type==="text"?c(t.text):t.type==="file"?c(t.uri):t.type==="image"?c(t.uri):"":"").filter(Boolean).join(`
|
|
@@ -17,7 +17,7 @@ ${s}`:s;xn(t,r,n,d,s)&&(o=1,a=1)}return s&&i&&(t.finalTextSeen=!0),On(t,Sn(e,t.a
|
|
|
17
17
|
... [truncated ${n} characters]`}function lr(e){if(!l(e))return"";let t=l(e.update)?e.update:e,n=(l(t.content)?t.content:{}).text??t.text;return n==null?"":String(n)}function ur(e){if(!l(e))return"";let t=l(e.update)?e.update:e;return c(t.sessionUpdate)}function dr(e){if(!Array.isArray(e))return"";let t=!1,r=[],n=[],s=[],i=()=>{r.length>0&&(n.push(r),r=[])};for(let a of e){let u=ur(a),d=lr(a);if(!u){d&&s.push(d);continue}if(t=!0,u==="agent_message_chunk"){d&&r.push(d);continue}(u==="agent_thought_chunk"||u==="user_message_chunk"||u==="tool_call"||u==="tool_call_update"||u==="plan")&&i()}if(i(),!t)return ft(s);let o=n[n.length-1]||[];return ft(o)}function Pa(e){return Array.isArray(e)?e.map(lr).filter(t=>t!==""):[]}function ee(e){return Array.isArray(e)?e.some(r=>ur(r))?e.filter(r=>_a.has(ur(r))).map(lr).filter(r=>r!==""):Pa(e):[]}function La(e){if(!l(e))return null;let t=l(e.update)?e.update:e;return c(t.sessionUpdate)!=="runtime_event"?null:l(t.event)?t.event:null}function ka(e){if(!l(e))return"";let t=l(e.payload)?e.payload:{},r=c(e.type),n=c(t.type);if(r==="event_msg"){if(n==="task_complete")return c(t.last_agent_message||t.message||t.output);if(n==="agent_message"){let s=c(t.phase||t.status||t.stage).toLowerCase();if(["final_answer","final","complete","completed","done"].includes(s))return c(t.message||t.text||t.content)}}return""}function va(e){if(!Array.isArray(e))return"";for(let t=e.length-1;t>=0;t-=1){let r=ka(La(e[t]));if(r)return r}return""}function ft(e){let t="";for(let r of e){let n=String(r||"");if(n)if(!t)t=n;else if(n.length>t.length&&n.startsWith(t))t=n;else{let s=Ln(n)&&!/[\r\n]$/.test(t)?`
|
|
18
18
|
`:"";t+=`${s}${n}`}}return t.trim()}function Da(e){let t=String(e||"").trim();if(!t)return"";let r=t.match(/(?:^|\n)#{1,3}\s+\S/);return!r||r.index===0?t:t.slice(r.index+(t[r.index]===`
|
|
19
19
|
`?1:0)).trim()}function Kn(e){let t=String(e||"").trim();if(!t)return null;try{let r=JSON.parse(t);if(l(r)&&(Yn(r)||k(r._artifacts)||k(r.artifacts)||k(r._decision)||k(r._meta)))return r}catch{}return null}function At(e){try{return je(e)}catch{return null}}function Zt(...e){let t=null;for(let r of e)try{if(je(r))return}catch(n){t||=n}if(t)throw t}function ir(e){let t=String(e||"").trim();if(!t)return null;let r=At(t);if(r)return r;let n=[...t.matchAll(/```(?:json)?\s*([\s\S]*?)```/gi)];for(let s=n.length-1;s>=0;s-=1){let i=Kn(n[s][1]);if(i)return i}if(!t.endsWith("}"))return null;for(let s=t.lastIndexOf("{");s>=0;s=t.lastIndexOf("{",s-1)){let i=Kn(t.slice(s));if(i)return i}return null}function Ha(e){let t=At(dr(e));if(t)return t;let r=ee(e);for(let n=r.length-1;n>=0;n-=1){let s=At(r[n]);if(s)return s}return At(ft(r))}function Yn(e){if(!l(e))return!1;if(k(e.output))return!0;for(let t of["content","text","message","summary"])if(c(e[t]))return!0;return!!(Vn(e.status)||c(e.error)||c(e.errorMessage))}function j(e){let t=l(e)?{...e}:{};return(!Array.isArray(t._artifacts)||t._artifacts.length===0)&&Array.isArray(t.artifacts)&&t.artifacts.length>0&&(t._artifacts=t.artifacts),delete t.artifacts,t}function $a(e){return l(e)?typeof(l(e._decision)?e._decision:l(e.decision)?e.decision:e).passed=="boolean":!1}function Qn(e){return l(e)?l(e._decision)&&typeof e._decision.passed=="boolean"?!0:$a(e.output):!1}function or(e,t,r){let n=l(e)?{...e}:{};if(!k(n.output)){for(let s of["summary","message","content","text"])if(c(n[s])){n.output=c(n[s]);break}}return!k(n.output)&&c(r)&&(n.output=c(r)),{...n,_meta:{...l(n._meta)?n._meta:{},runtimeOutputSource:t,requiredRuntimeResult:!0,finalMessagePreview:c(r).slice(0,1e3)}}}var Fa=["dataUrl","dataURL","contentDataUrl","contentDataURL","contentBase64","base64","data"];function zn(e){if(!l(e))return"";let t=l(e.content)?e.content:{},r=l(e.metadata)?e.metadata:{};return c(e.fieldKey||e.field_key||t.fieldKey||r.fieldKey)}function Ba(e,t){let r=l(e.content)?e.content:{},n=l(t.content)?t.content:{},s={...e,...t,content:{...r,...n},metadata:{...l(e.metadata)?e.metadata:{},...l(t.metadata)?t.metadata:{}}};for(let i of Fa)k(e[i])&&(s[i]=e[i]),k(r[i])&&(s.content[i]=r[i]);return s}function Zn(e,t){let r=Array.isArray(e)?e.filter(l):[],n=Array.isArray(t)?t.filter(l):[];if(!r.length)return n;if(!n.length)return r;let s=[...r],i=new Map;for(let a=0;a<r.length;a+=1){let u=zn(r[a]),d=i.get(u)||[];d.push(a),i.set(u,d)}let o=new Map;for(let a of n){let u=zn(a),d=o.get(u)||0;o.set(u,d+1);let f=i.get(u)?.[d];f===void 0?s.push(a):s[f]=Ba(s[f],a)}return s}function G(e,t,r){let n={...e,...t,_meta:{...l(e._meta)?e._meta:{},...l(t._meta)?t._meta:{},runtimeOutputSource:r}};return(Array.isArray(e._artifacts)||Array.isArray(t._artifacts))&&(n._artifacts=Zn(e._artifacts,t._artifacts)),n}function ts(e,t,r={}){let n=c(r.promptKind),s=r.requiresRuntimeResult===!0,i=s||n==="dispatch",o=j(e),a=typeof o.output=="string"?At(o.output):null;if(a)return G(o,j(a),"result_block");let u=va(t),d=u?ir(u):null,f=i?Ha(t):null;if(Yn(o))return Vn(o.status)||c(o.error)||c(o.errorMessage)?o:n==="dispatch"&&d?G(o,j(d),"final_event"):n==="dispatch"&&f?G(o,j(f),"updates"):!s||Qn(o)?o:d?G(o,j(d),"final_event"):f?G(o,j(f),"updates"):(Zt(o.output,u,ft(ee(t))),or(o,"result",o.output));if(d)return G(o,j(d),"final_event");if(f)return G(o,j(f),"updates");if(u)return s?(Zt(u,ft(ee(t))),or(o,"final_event",u)):{...o,output:u,_meta:{...l(o._meta)?o._meta:{},runtimeOutputSource:"final_event"}};let p=dr(t),h=ir(p);if(h)return G(o,j(h),"updates");let m=Da(p);if(!m)return s&&Zt(ft(ee(t))),o;let y=ir(m);return y?G(o,j(y),"updates"):s?(Zt(p,m),or(o,"updates",m)):{...o,output:m,_meta:{...l(o._meta)?o._meta:{},runtimeOutputSource:"updates"}}}function fr(e){return l(e)?c(e.stopReason||e.stop_reason).toLowerCase():""}function es(e){return ga.has(fr(e))}function ja(e,t,r){if(r.promptKind!=="dispatch"||r.requiresRuntimeResult!==!0||!es(e))return!1;try{return!Qn(ts(e,t,r))}catch{return!0}}function qa(){return["Your previous final response reached the output limit before the Runtime Result protocol was complete.","Re-emit the complete final Runtime Result from scratch using the conclusion you already reached.","Do not repeat analysis, process notes, tool calls, or task work.",`Keep the OUTPUT report within ${Jn} Unicode characters.`,`Output ${De}, ${He}, ${$e}, and ${Fe} on their own lines.`,"The CONTROL block must be valid JSON containing _decision and must be fully closed.","Return only the two protocol blocks."].join(`
|
|
20
|
-
`)}function Ga(e,t){let r=l(t)?{...t}:{},n=Zn(e?._artifacts,t?._artifacts);return n.length>0&&(r._artifacts=n),r}function Ka(e,t){return{...e,_meta:{...l(e?._meta)?e._meta:{},runtimeProtocolRetry:{attempted:!0,recovered:!0,reason:fr(t)||"max_tokens"}}}}function za(e,t){let r=typeof e?.output=="string"?e.output:dr(t),n=new L("runtime_output_truncated","Runtime Result \u8FBE\u5230\u6700\u5927\u8F93\u51FA\u957F\u5EA6\uFF0C\u7D27\u51D1\u91CD\u8BD5\u540E\u4ECD\u672A\u5B8C\u6574\u8FD4\u56DE",r);return n.detail={...n.detail,stopReason:fr(e)||"max_tokens",retryAttempted:!0},n}var re=class{constructor(t){this.config=t,this.targets=t.targets||{},this.agents=new Map,this.sessions=new Map,this.startingSessions=new Set,this.targetReservations=new Map,this.runtimeSessionIndex=new Map,this.pendingPermissions=new Map,this.operations=new Map,this.startedAt=yt(),this.sandboxCapabilities={dispatchable:t.sandboxRequired!==!0,enforced:!1,backend:t.sandboxRequired?"unavailable":"direct",daemonStatus:t.sandboxRequired?"pending":"not_required",policySchemaVersions:[1],runtimePolicyUpdates:t.sandboxRequired===!0,workspaceSync:!1,egressProxy:!1},this.cleanupTimer=null,this.onCapacityChanged=null,this.startSessionCleanup()}async start(){try{this.sandboxCapabilities=await Bn(this.config)}catch(t){this.sandboxCapabilities={...this.sandboxCapabilities,dispatchable:!1,enforced:!1,backend:"unavailable",daemonStatus:"unavailable",reason:c(t?.code||t?.message||t)||"sandbox_probe_failed"},g("error","runtime-bridge","runtime node sandbox probe failed; node will not accept work",{reason:this.sandboxCapabilities.reason})}return this.sandboxCapabilities}assertDispatchable(){if(this.config.sandboxRequired&&!(this.sandboxCapabilities.dispatchable===!0&&this.sandboxCapabilities.enforced===!0))throw Object.assign(new Error("runtime node strong sandbox is unavailable"),{code:"RUNTIME_NODE_SANDBOX_UNAVAILABLE"})}operationIdentity(t,r,n){let s=l(t.goalContract)?ze(t.goalContract):c(t.goalContractHash),i=ca("sha256").update(q({prompt:Array.isArray(t.prompt)?t.prompt:[],mcpServers:Ea(t.mcpServers),metadata:l(t.metadata)?t.metadata:{}})).digest("hex");return{agentId:r,runId:c(t.runId),sessionId:n,targetCode:c(t.targetCode||t.framework),workspace:c(t.workspace),goalContractHash:s,requestHash:i}}assertOperationIdentity(t,r,n,s){let i=this.operationIdentity(r,n,s),o=Object.keys(t.identity).find(a=>t.identity[a]!==i[a]);if(o)throw Object.assign(new Error(`runtime operation correlation conflict: ${t.correlationId}`),{code:"RUNTIME_OPERATION_CORRELATION_CONFLICT",field:o})}createOperation(t,r,n,s){let i={correlationId:t,agentId:n,sessionId:s,identity:this.operationIdentity(r,n,s),sandboxPolicy:l(r.sandboxPolicy)?structuredClone(r.sandboxPolicy):null,status:"running",updates:[],listeners:new Set,waiters:[],response:null,error:null,mcpServers:r.mcpServers,createdAtMs:Date.now(),completedAtMs:null};return this.operations.set(t,i),i}async attachOperation(t,r={}){let n=()=>{if(r.onUpdate)for(let s of t.updates)r.onUpdate(s)};if(t.status==="completed")return n(),t.response;if(t.status==="failed")throw n(),t.error;r.onUpdate&&t.listeners.add(r.onUpdate);try{return n(),await new Promise((s,i)=>t.waiters.push({resolve:s,reject:i}))}finally{r.onUpdate&&t.listeners.delete(r.onUpdate)}}completeOperation(t,r){if(t.status==="running"){t.status="completed",t.response=r,t.mcpServers=null,t.completedAtMs=Date.now();for(let n of t.waiters.splice(0))n.resolve(r);t.listeners.clear()}}promptResponse(t,r,n,s,i,o=s){let a=l(t.metadata)?t.metadata:{},u=l(a._runtimeBridge)?a._runtimeBridge:{},d=ts(i,o,u),f=Dn(t.goalContract,s,t.goalContractHash);return{sessionId:n,runtimeSessionId:r.runtimeSessionId,result:d,updates:s,...f?{verification:f}:{}}}failOperation(t,r){if(t.status!=="running")return;let n=r instanceof Error?r:Object.assign(new Error(String(r||"runtime operation failed")),{code:"RUNTIME_NODE_ERROR"});t.status="failed",t.error=n,t.mcpServers=null,t.completedAtMs=Date.now();for(let s of t.waiters.splice(0))s.reject(n);t.listeners.clear()}cleanupOperations(t=Date.now()){let r=X(this.config.operationTtlMs,pa);for(let[n,s]of this.operations)s.status!=="running"&&t-(s.completedAtMs||s.createdAtMs)>r&&this.operations.delete(n)}targetFor(t){let r=c(t)||this.config.defaultTargetCode,n=this.targets[r];if(!r||!l(n)){let s=new Error(`runtime target is not configured: ${r||"<empty>"}`);throw s.code="RUNTIME_TARGET_NOT_FOUND",s}return{code:r,target:n}}targetIdleTtlMs(t){let n=K(t)==="openclaw"?X(this.config.openClawSessionIdleTtlMs,ha):X(this.config.sessionIdleTtlMs,la);return X(t?.sessionIdleTtlMs??t?.idleTtlMs,n)}targetMaxSessions(t){let n=K(t)==="openclaw"?X(this.config.openClawMaxSessions,ma):X(this.config.maxSessionsPerTarget,fa);return X(t?.maxSessions,n)}targetReservationCount(t){return Number(this.targetReservations.get(t)||0)}notifyCapacityChanged(){try{this.onCapacityChanged?.()}catch(t){g("warn","runtime-bridge","capacity heartbeat callback failed",{error:String(t)})}}targetCapacitySnapshot(t=!0){return Object.entries(this.targets).map(([r,n])=>{let s=[...this.sessions.values()].filter(f=>f.targetCode===r),i=s.filter(f=>f.busy).length,o=s.length-i,a=this.targetReservationCount(r),u=this.targetMaxSessions(n),d=t?Math.max(0,u-i-a):0;return{targetCode:r,targetName:c(n?.name||n?.targetName||r),targetType:c(n?.type||"echo"),status:t?d>0?"available":"busy":"unavailable",maxSessions:u,busySessions:i,idleSessions:o,reservedSessions:a,availableSessions:d}})}targetReleaseAfterPrompt(t,r={}){let n=K(t),s=l(r?._runtimeBridge)?r._runtimeBridge:{},i=ar(s);return i!==void 0?xt(i,!1):xt(t?.releaseAfterPrompt??t?.disposeAfterPrompt,n==="openclaw")}promptReleaseAfterPrompt(t,r={}){if(this.config.sandboxRequired)return!0;let n=l(r?._runtimeBridge)?r._runtimeBridge:{},s=ar(n);return s!==void 0?xt(s,t.releaseAfterPrompt):t.releaseAfterPrompt}startSessionCleanup(){let t=X(this.config.sessionCleanupIntervalMs,da);t<=0||(this.cleanupTimer=setInterval(()=>{this.cleanupExpiredSessions().catch(r=>{g("warn","runtime-bridge","runtime session cleanup failed",{error:String(r)})})},t),this.cleanupTimer.unref?.())}sessionCountForTarget(t){let r=0;for(let n of this.sessions.values())n.targetCode===t&&(r+=1);return r}idleTargetSessions(t){return[...this.sessions.entries()].filter(([,r])=>r.targetCode===t&&!r.busy).sort((r,n)=>(r[1].lastUsedAtMs||0)-(n[1].lastUsedAtMs||0))}async closeSession(t,r="session_closed"){let n=this.sessions.get(t);if(!n)return!1;try{await n.runtime?.stop?.(r)}catch(s){g("warn","runtime-bridge","runtime session stop failed",{sessionId:n.sessionId,runtimeSessionId:n.runtimeSessionId,targetCode:n.targetCode,runtimeKind:n.runtimeKind,error:String(s)})}return this.sessions.delete(t),this.runtimeSessionIndex.delete(`${n.agentId}:${n.runtimeSessionId}`),this.notifyCapacityChanged(),g("info","runtime-bridge","runtime session closed",{sessionId:n.sessionId,runtimeSessionId:n.runtimeSessionId,targetCode:n.targetCode,runtimeKind:n.runtimeKind,reason:r}),!0}async closeAllSessions(t="bridge_shutdown"){for(let r of[...this.sessions.keys()])await this.closeSession(r,t)}async stop(t="bridge_stop"){this.cleanupTimer&&clearInterval(this.cleanupTimer),this.cleanupTimer=null,await this.closeAllSessions(t)}async cleanupExpiredSessions(t=Date.now()){for(let[r,n]of[...this.sessions.entries()])n.busy||t-(n.lastUsedAtMs||n.createdAtMs||t)>n.idleTtlMs&&await this.closeSession(r,"idle_timeout")}async reserveTargetSlot(t,r){let n=this.targetMaxSessions(r);for(;this.sessionCountForTarget(t)+this.targetReservationCount(t)>=n;){let s=this.idleTargetSessions(t);if(!s.length){let i=new Error(`runtime target session limit reached: ${t}`);throw i.code="RUNTIME_TARGET_SESSION_LIMIT",i}await this.closeSession(s[0][0],"target_session_limit")}this.targetReservations.set(t,this.targetReservationCount(t)+1),this.notifyCapacityChanged()}releaseTargetSlot(t){let r=Math.max(0,this.targetReservationCount(t)-1);r>0?this.targetReservations.set(t,r):this.targetReservations.delete(t),this.notifyCapacityChanged()}async createAgent(t){let r=c(t.agentId);if(!r)throw Object.assign(new Error("agentId is required"),{status:400,code:"BAD_REQUEST"});let{code:n,target:s}=this.targetFor(t.targetCode||t.framework),i=this.agents.get(r);if(i&&i.targetCode!==n)throw Object.assign(new Error(`runtime agent target conflict: ${r}`),{code:"RUNTIME_AGENT_TARGET_CONFLICT"});let o=this.config.sandboxRequired?await Qe(this.config,s,t.workspace):c(t.workspace),a={agentId:r,targetCode:n,framework:c(t.framework||n),workspace:o,metadata:l(t.metadata)?t.metadata:{},createdAt:yt(),runtimeKind:K(s)};return this.agents.set(r,a),{agent:a}}runtimeFor(t,r,n={}){let{target:s}=this.targetFor(t.targetCode),i=K(s),o={onPermissionRequest:u=>n.source==="conversation_delegation"?{outcome:"denied",reason:"External group member permission continuation is not supported"}:this.requestPermission(u)},a={sessionPromptTimeoutMs:this.config.sessionPromptTimeoutMs,initializeTimeoutMs:this.config.initializeTimeoutMs,sandboxRequired:this.config.sandboxRequired,runId:n.runId,source:n.source,cloudSessionId:r,orchaUrl:this.config.orchaUrl,attachOnly:n.attachOnly,sandboxPolicy:n.sandboxPolicy,...this.config.sandboxRequired?{prepareLaunch:u=>jn({config:this.config,target:s,...u,runId:n.runId,agentId:t.agentId,sessionId:r,sandboxPolicy:n.sandboxPolicy})}:{}};return i==="stdio-acp"||i==="acp"?new ut(t,s,o,a):i==="openclaw"?new Bt(t,s,r,o,a):i==="cli"?new Jt(t,s,a):new kt(t)}async prompt(t,r={}){if(this.assertDispatchable(),this.config.sandboxRequired){let a=c(t.runId);if(!a)throw Object.assign(new Error("runId is required"),{code:"BAD_REQUEST"});t={...t,runId:a,sandboxPolicy:nt(t.sandboxPolicy)}}let n=c(t.agentId),s=c(t.sessionId)||te(),i=c(t.correlationId);if(!n)throw Object.assign(new Error("agentId is required"),{status:400,code:"BAD_REQUEST"});if(this.cleanupOperations(),i){let a=this.operations.get(i);if(a){if(this.assertOperationIdentity(a,t,n,s),a.status==="running"&&(["conversation_delegation","channel_message"].includes(t.metadata?.source)||t.metadata?.source==="ai_chat_runtime_node"&&t.framework==="codex")){a.mcpServers=t.mcpServers;let u=this.sessions.get(`${n}:${s}`);if(u){if(!u.runtime.refreshMcpServers)throw Object.assign(new Error("This runtime cannot refresh task MCP credentials"),{code:"unsupported_dynamic_mcp"});await u.runtime.refreshMcpServers(t.mcpServers)}}return this.attachOperation(a,r)}if(t.attachOnly===!0&&!(t.metadata?.source==="channel_message"&&this.targetFor(t.targetCode||t.framework).target.type==="openclaw"))throw Object.assign(new Error(`runtime operation is no longer available: ${i}`),{code:"RUNTIME_OPERATION_NOT_FOUND"})}else if(t.attachOnly===!0)throw Object.assign(new Error("correlationId is required for attachOnly"),{code:"BAD_REQUEST"});let o=i?this.createOperation(i,t,n,s):null;if(o){let a=this.attachOperation(o,r);return(async()=>{try{await this._promptOwned(t,{},o,n,s)}catch(u){let d=o.status==="completed";this.failOperation(o,u),d&&g("warn","runtime-bridge","runtime cleanup failed after operation completion",{correlationId:o.correlationId,error:String(u)})}})(),await a}return await this._promptOwned(t,r,o,n,s)}async _promptOwned(t,r,n,s,i){let o=c(t.runId),a=this.config.sandboxRequired?nt(n?.sandboxPolicy||t.sandboxPolicy):null,u=this.agents.get(s),d=this.targetFor(t.targetCode||t.framework||u?.targetCode);if(u&&u.targetCode!==d.code)throw Object.assign(new Error(`runtime agent target conflict: ${s}`),{code:"RUNTIME_AGENT_TARGET_CONFLICT"});if(K(d.target)==="cli"&&Ra(t.goalContract))throw Object.assign(new Error("CLI runtimes do not support per-run dynamic MCP required by plugin_operation"),{code:"unsupported_dynamic_mcp"});let f=u||(await this.createAgent({agentId:s,targetCode:d.code,framework:t.framework,workspace:t.workspace,metadata:t.metadata})).agent;if(!this.config.sandboxRequired){let _=c(t.workspace);_&&_!==f.workspace&&(f.workspace=_)}let p=`${s}:${i}`;await this.cleanupExpiredSessions();let h=this.sessions.get(p),m=null;if(h&&h.targetCode!==d.code)throw Object.assign(new Error(`runtime session target conflict: ${i}`),{code:"RUNTIME_SESSION_TARGET_CONFLICT"});if(h&&this.config.sandboxRequired&&h.runId!==o)throw Object.assign(new Error(`runtime session cannot be shared across runs: ${i}`),{code:"RUNTIME_SESSION_RUN_CONFLICT"});if(!h){if(this.startingSessions.has(p))throw Object.assign(new Error(`runtime session is already starting: ${i}`),{code:"RUNTIME_SESSION_BUSY"});this.startingSessions.add(p);let{code:_,target:w}=d,E;try{await this.reserveTargetSlot(_,w),m=_;let O=this.runtimeFor(f,i,{runId:o,sandboxPolicy:a,source:t.metadata?.source,attachOnly:t.attachOnly});E=O;let U=n?.mcpServers||t.mcpServers||[],v=await O.sessionNew({cwd:tt(w,f.workspace),mcpServers:U,metadata:l(t.metadata)?t.metadata:{}});if(n&&n.status!=="running")throw Object.assign(new Error("Runtime operation was cancelled before prompt dispatch"),{code:"RUNTIME_OPERATION_CANCELLED"});let D=Date.now();h={agentId:s,sessionId:i,runtime:O,targetCode:_,runtimeKind:K(w),runId:o,sandboxPolicy:a?structuredClone(a):null,runtimeSessionId:c(l(v)?v.sessionId:"")||i,idleTtlMs:this.targetIdleTtlMs(w),releaseAfterPrompt:this.config.sandboxRequired?!0:this.targetReleaseAfterPrompt(w,t.metadata),createdAtMs:D,lastUsedAtMs:D,busy:!1,createdAt:yt()},this.sessions.set(p,h),this.runtimeSessionIndex.set(`${s}:${h.runtimeSessionId}`,h),n?.mcpServers!==U&&n?.mcpServers&&O.refreshMcpServers&&await O.refreshMcpServers(n.mcpServers)}catch(O){throw this.sessions.get(p)?.runtime===E?await this.closeSession(p,"session_start_failed"):await E?.stop("session_start_failed"),m&&(this.releaseTargetSlot(m),m=null),O}finally{this.startingSessions.delete(p)}}if(h.busy){let _=new Error(`runtime session is already running: ${i}`);throw _.code="RUNTIME_SESSION_BUSY",_}h.busy=!0,m&&this.releaseTargetSlot(m),this.notifyCapacityChanged(),h.operationCorrelationId=n?.correlationId||null,h.lastUsedAtMs=Date.now();let y=n?.updates||[],S=_=>{let w=Sa(_,i,h);if(y.push(w),n)for(let E of n.listeners)try{E?.(w)}catch(O){g("warn","runtime-bridge","runtime operation update listener failed",{correlationId:n.correlationId,error:String(O)})}else r.onUpdate?.(w)},N=_=>{if(!(!n||n.status!=="running"||!l(_))){if(_.status==="failed"){let w=_.error;if(w instanceof Error){this.failOperation(n,w);return}let E=l(w)?w:{},O=Object.assign(new Error(c(E.message||_.message)||"runtime terminal failure"),{code:c(E.code||_.code)||"RUNTIME_TERMINAL_FAILED"});this.failOperation(n,O);return}this.completeOperation(n,this.promptResponse(t,h,i,y,l(_.result)?_.result:{stopReason:"end_turn"}))}};try{let _=vn(t.goalContract),w=Array.isArray(t.prompt)?[...t.prompt]:[];_&&w.unshift({type:"text",text:_,metadata:{source:"orcha_goal_contract"}});let E=l(t.metadata)?t.metadata:{},O=l(E._runtimeBridge)?E._runtimeBridge:{},U=await h.runtime.sessionPrompt({sessionId:h.runtimeSessionId,prompt:w,metadata:E},S,N),v=y,D=null;if(Wn({type:h.runtimeKind})&&ja(U,y,O)){D=U;let z=y.length,I=await h.runtime.sessionPrompt({sessionId:h.runtimeSessionId,prompt:[{type:"text",text:qa()}],metadata:E,_meta:{thinking:"off"}},S,N);U=Ga(D,I),v=y.slice(z)}let Y;try{Y=this.promptResponse(t,h,i,y,U,v)}catch(z){throw D&&es(U)?za(U,v):z}return D&&(Y.result=Ka(Y.result,D)),n&&this.completeOperation(n,Y),Y}finally{h.busy=!1,this.notifyCapacityChanged(),h.operationCorrelationId===n?.correlationId&&(h.operationCorrelationId=null),h.lastUsedAtMs=Date.now(),this.promptReleaseAfterPrompt(h,l(t.metadata)?t.metadata:{})&&await this.closeSession(p,"release_after_prompt"),n&&(n.mcpServers=null)}}async updatePolicy(t){if(this.assertDispatchable(),!this.config.sandboxRequired)throw Object.assign(new Error("runtime sandbox policy updates require an enforced sandbox"),{code:"RUNTIME_NODE_SANDBOX_UNAVAILABLE"});let r=c(t.agentId),n=c(t.runId),s=c(t.sessionId),i=c(t.correlationId);if(!r||!n||!s||!i)throw Object.assign(new Error("agentId, runId, sessionId and correlationId are required"),{code:"BAD_REQUEST"});let o=nt(t.sandboxPolicy),a=this.operations.get(i);if(a&&(a.agentId!==r||a.sessionId!==s||a.identity.runId!==n))throw Object.assign(new Error(`runtime operation policy identity conflict: ${i}`),{code:"RUNTIME_OPERATION_CORRELATION_CONFLICT"});let u=`${r}:${s}`,d=this.sessions.get(u);if(d&&d.runId!==n)throw Object.assign(new Error(`runtime session policy identity conflict: ${s}`),{code:"RUNTIME_SESSION_RUN_CONFLICT"});let f=d?.sandboxPolicy||a?.sandboxPolicy;if(!f)throw Object.assign(new Error(`runtime operation is no longer available: ${i}`),{code:"RUNTIME_OPERATION_NOT_FOUND"});let p=Number(f.policyRevision),h=Number(o.policyRevision);if(h<p)throw Object.assign(new Error("runtime sandbox policy revision is stale"),{code:"RUNTIME_POLICY_REVISION_CONFLICT",currentPolicyRevision:p});if(h===p){if(q(f)!==q(o))throw Object.assign(new Error("runtime sandbox policy revision payload conflicts"),{code:"RUNTIME_POLICY_REVISION_CONFLICT",currentPolicyRevision:p});return{updated:!1,idempotent:!0,policyRevision:p,accessMode:f.accessMode}}let m=Fn(f,o);a&&(a.sandboxPolicy=structuredClone(o)),d&&(d.sandboxPolicy=structuredClone(o));let y=!1;return m&&d?.busy===!0&&(await d.runtime.sessionCancel({sessionId:d.runtimeSessionId}),y=await this.closeSession(u,"sandbox_policy_downgrade")),{updated:!0,policyRevision:h,accessMode:o.accessMode,activeCommandCancelled:y,deferredToNextLaunch:!m&&d?.busy===!0}}async cancel(t){let r=c(t.agentId),n=c(t.sessionId),s=c(t.correlationId);if(s){let a=this.operations.get(s);if(!a||a.status!=="running")return{cancelled:!1,correlationId:s,reason:"operation_not_running"};if(a.agentId!==r||a.sessionId!==n)throw Object.assign(new Error(`runtime operation cancellation identity conflict: ${s}`),{code:"RUNTIME_OPERATION_CORRELATION_CONFLICT"});let u=this.sessions.get(`${r}:${n}`);return!u||u.busy!==!0||u.operationCorrelationId!==s?(this.failOperation(a,Object.assign(new Error("Runtime operation cancelled before dispatch"),{code:"RUNTIME_OPERATION_CANCELLED"})),{cancelled:!0,correlationId:s,reason:"cancelled_before_dispatch"}):(await u.runtime.sessionCancel({sessionId:u.runtimeSessionId}),await this.closeSession(`${r}:${n}`,"cancelled"),{cancelled:!0,correlationId:s})}let i=`${r}:${n}`,o=this.sessions.get(i);return o?(await o.runtime.sessionCancel({sessionId:o.runtimeSessionId}),await this.closeSession(i,"cancelled"),{cancelled:!0}):{cancelled:!1}}async dispatch(t,r={}){let n=c(t.agentId),s=c(t.taskId),i=c(t.executionId);if(!n||!s)throw Object.assign(new Error("agentId and taskId are required"),{status:400,code:"BAD_REQUEST"});let o=c(t.sessionId)||`dispatch-${i||s}`,a=c(t.instructions),u=Ua(t),d=Xn(t),{target:f}=this.targetFor(t.targetCode||t.framework),p=[{type:"text",text:["Task dispatch received.",`taskId: ${s}`,i?`executionId: ${i}`:"",a?`instructions: ${a}`:"",u?"orchaContext:":"",u,u?"Use orchaContext as the source of truth for upstream outputs, review reports, artifacts, and workflow metadata.":"","Follow the execution protocol in instructions exactly.","When embedding a generated artifact in final Markdown, use the Orcha artifact placeholder as the link target: orcha-artifact://<fieldKey>/<index>.","Example: use  for the first generated image artifact.","Do not use local filesystem paths, file:// URLs, or data: URLs in final Markdown; Orcha replaces artifact placeholders after ingestion.",d?`For decision results, put the raw report between ${De} and ${He}.`:"",d?`Then put a JSON object containing _decision between ${$e} and ${Fe}.`:"",d?"Do not JSON-escape the report. Keep output out of the CONTROL JSON, and put every protocol marker on its own line.":"",d?`Keep the report within ${Jn} Unicode characters so the CONTROL block fits before the output limit.`:"","Return only the final answer required by that protocol.","Do not include process notes, logs, or explanations."].filter(Boolean).join(`
|
|
20
|
+
`)}function Ga(e,t){let r=l(t)?{...t}:{},n=Zn(e?._artifacts,t?._artifacts);return n.length>0&&(r._artifacts=n),r}function Ka(e,t){return{...e,_meta:{...l(e?._meta)?e._meta:{},runtimeProtocolRetry:{attempted:!0,recovered:!0,reason:fr(t)||"max_tokens"}}}}function za(e,t){let r=typeof e?.output=="string"?e.output:dr(t),n=new L("runtime_output_truncated","Runtime Result \u8FBE\u5230\u6700\u5927\u8F93\u51FA\u957F\u5EA6\uFF0C\u7D27\u51D1\u91CD\u8BD5\u540E\u4ECD\u672A\u5B8C\u6574\u8FD4\u56DE",r);return n.detail={...n.detail,stopReason:fr(e)||"max_tokens",retryAttempted:!0},n}var re=class{constructor(t){this.config=t,this.targets=t.targets||{},this.agents=new Map,this.sessions=new Map,this.startingSessions=new Set,this.targetReservations=new Map,this.runtimeSessionIndex=new Map,this.pendingPermissions=new Map,this.operations=new Map,this.startedAt=yt(),this.sandboxCapabilities={dispatchable:t.sandboxRequired!==!0,enforced:!1,backend:t.sandboxRequired?"unavailable":"direct",daemonStatus:t.sandboxRequired?"pending":"not_required",policySchemaVersions:[1],runtimePolicyUpdates:t.sandboxRequired===!0,workspaceSync:!1,egressProxy:!1},this.cleanupTimer=null,this.onCapacityChanged=null,this.startSessionCleanup()}async start(){try{this.sandboxCapabilities=await Bn(this.config)}catch(t){this.sandboxCapabilities={...this.sandboxCapabilities,dispatchable:!1,enforced:!1,backend:"unavailable",daemonStatus:"unavailable",reason:c(t?.code||t?.message||t)||"sandbox_probe_failed"},g("error","runtime-bridge","runtime node sandbox probe failed; node will not accept work",{reason:this.sandboxCapabilities.reason})}return this.sandboxCapabilities}assertDispatchable(){if(this.config.sandboxRequired&&!(this.sandboxCapabilities.dispatchable===!0&&this.sandboxCapabilities.enforced===!0))throw Object.assign(new Error("runtime node strong sandbox is unavailable"),{code:"RUNTIME_NODE_SANDBOX_UNAVAILABLE"})}operationIdentity(t,r,n){let s=l(t.goalContract)?ze(t.goalContract):c(t.goalContractHash),i=ca("sha256").update(q({prompt:Array.isArray(t.prompt)?t.prompt:[],mcpServers:Ea(t.mcpServers),metadata:l(t.metadata)?t.metadata:{}})).digest("hex");return{agentId:r,runId:c(t.runId),sessionId:n,targetCode:c(t.targetCode||t.framework),workspace:c(t.workspace),goalContractHash:s,requestHash:i}}assertOperationIdentity(t,r,n,s){let i=this.operationIdentity(r,n,s),o=Object.keys(t.identity).find(a=>t.identity[a]!==i[a]);if(o)throw Object.assign(new Error(`runtime operation correlation conflict: ${t.correlationId}`),{code:"RUNTIME_OPERATION_CORRELATION_CONFLICT",field:o})}createOperation(t,r,n,s){let i={correlationId:t,agentId:n,sessionId:s,identity:this.operationIdentity(r,n,s),sandboxPolicy:l(r.sandboxPolicy)?structuredClone(r.sandboxPolicy):null,status:"running",updates:[],listeners:new Set,waiters:[],response:null,error:null,mcpServers:r.mcpServers,createdAtMs:Date.now(),completedAtMs:null};return this.operations.set(t,i),i}async attachOperation(t,r={}){let n=()=>{if(r.onUpdate)for(let s of t.updates)r.onUpdate(s)};if(t.status==="completed")return n(),t.response;if(t.status==="failed")throw n(),t.error;r.onUpdate&&t.listeners.add(r.onUpdate);try{return n(),await new Promise((s,i)=>t.waiters.push({resolve:s,reject:i}))}finally{r.onUpdate&&t.listeners.delete(r.onUpdate)}}completeOperation(t,r){if(t.status==="running"){t.status="completed",t.response=r,t.mcpServers=null,t.completedAtMs=Date.now();for(let n of t.waiters.splice(0))n.resolve(r);t.listeners.clear()}}promptResponse(t,r,n,s,i,o=s){let a=l(t.metadata)?t.metadata:{},u=l(a._runtimeBridge)?a._runtimeBridge:{},d=a.source==="channel_message"?i:ts(i,o,u),f=Dn(t.goalContract,s,t.goalContractHash);return{sessionId:n,runtimeSessionId:r.runtimeSessionId,result:d,updates:s,...f?{verification:f}:{}}}failOperation(t,r){if(t.status!=="running")return;let n=r instanceof Error?r:Object.assign(new Error(String(r||"runtime operation failed")),{code:"RUNTIME_NODE_ERROR"});t.status="failed",t.error=n,t.mcpServers=null,t.completedAtMs=Date.now();for(let s of t.waiters.splice(0))s.reject(n);t.listeners.clear()}cleanupOperations(t=Date.now()){let r=X(this.config.operationTtlMs,pa);for(let[n,s]of this.operations)s.status!=="running"&&t-(s.completedAtMs||s.createdAtMs)>r&&this.operations.delete(n)}targetFor(t){let r=c(t)||this.config.defaultTargetCode,n=this.targets[r];if(!r||!l(n)){let s=new Error(`runtime target is not configured: ${r||"<empty>"}`);throw s.code="RUNTIME_TARGET_NOT_FOUND",s}return{code:r,target:n}}targetIdleTtlMs(t){let n=K(t)==="openclaw"?X(this.config.openClawSessionIdleTtlMs,ha):X(this.config.sessionIdleTtlMs,la);return X(t?.sessionIdleTtlMs??t?.idleTtlMs,n)}targetMaxSessions(t){let n=K(t)==="openclaw"?X(this.config.openClawMaxSessions,ma):X(this.config.maxSessionsPerTarget,fa);return X(t?.maxSessions,n)}targetReservationCount(t){return Number(this.targetReservations.get(t)||0)}notifyCapacityChanged(){try{this.onCapacityChanged?.()}catch(t){g("warn","runtime-bridge","capacity heartbeat callback failed",{error:String(t)})}}targetCapacitySnapshot(t=!0){return Object.entries(this.targets).map(([r,n])=>{let s=[...this.sessions.values()].filter(f=>f.targetCode===r),i=s.filter(f=>f.busy).length,o=s.length-i,a=this.targetReservationCount(r),u=this.targetMaxSessions(n),d=t?Math.max(0,u-i-a):0;return{targetCode:r,targetName:c(n?.name||n?.targetName||r),targetType:c(n?.type||"echo"),status:t?d>0?"available":"busy":"unavailable",maxSessions:u,busySessions:i,idleSessions:o,reservedSessions:a,availableSessions:d}})}targetReleaseAfterPrompt(t,r={}){let n=K(t),s=l(r?._runtimeBridge)?r._runtimeBridge:{},i=ar(s);return i!==void 0?xt(i,!1):xt(t?.releaseAfterPrompt??t?.disposeAfterPrompt,n==="openclaw")}promptReleaseAfterPrompt(t,r={}){if(this.config.sandboxRequired)return!0;let n=l(r?._runtimeBridge)?r._runtimeBridge:{},s=ar(n);return s!==void 0?xt(s,t.releaseAfterPrompt):t.releaseAfterPrompt}startSessionCleanup(){let t=X(this.config.sessionCleanupIntervalMs,da);t<=0||(this.cleanupTimer=setInterval(()=>{this.cleanupExpiredSessions().catch(r=>{g("warn","runtime-bridge","runtime session cleanup failed",{error:String(r)})})},t),this.cleanupTimer.unref?.())}sessionCountForTarget(t){let r=0;for(let n of this.sessions.values())n.targetCode===t&&(r+=1);return r}idleTargetSessions(t){return[...this.sessions.entries()].filter(([,r])=>r.targetCode===t&&!r.busy).sort((r,n)=>(r[1].lastUsedAtMs||0)-(n[1].lastUsedAtMs||0))}async closeSession(t,r="session_closed"){let n=this.sessions.get(t);if(!n)return!1;try{await n.runtime?.stop?.(r)}catch(s){g("warn","runtime-bridge","runtime session stop failed",{sessionId:n.sessionId,runtimeSessionId:n.runtimeSessionId,targetCode:n.targetCode,runtimeKind:n.runtimeKind,error:String(s)})}return this.sessions.delete(t),this.runtimeSessionIndex.delete(`${n.agentId}:${n.runtimeSessionId}`),this.notifyCapacityChanged(),g("info","runtime-bridge","runtime session closed",{sessionId:n.sessionId,runtimeSessionId:n.runtimeSessionId,targetCode:n.targetCode,runtimeKind:n.runtimeKind,reason:r}),!0}async closeAllSessions(t="bridge_shutdown"){for(let r of[...this.sessions.keys()])await this.closeSession(r,t)}async stop(t="bridge_stop"){this.cleanupTimer&&clearInterval(this.cleanupTimer),this.cleanupTimer=null,await this.closeAllSessions(t)}async cleanupExpiredSessions(t=Date.now()){for(let[r,n]of[...this.sessions.entries()])n.busy||t-(n.lastUsedAtMs||n.createdAtMs||t)>n.idleTtlMs&&await this.closeSession(r,"idle_timeout")}async reserveTargetSlot(t,r){let n=this.targetMaxSessions(r);for(;this.sessionCountForTarget(t)+this.targetReservationCount(t)>=n;){let s=this.idleTargetSessions(t);if(!s.length){let i=new Error(`runtime target session limit reached: ${t}`);throw i.code="RUNTIME_TARGET_SESSION_LIMIT",i}await this.closeSession(s[0][0],"target_session_limit")}this.targetReservations.set(t,this.targetReservationCount(t)+1),this.notifyCapacityChanged()}releaseTargetSlot(t){let r=Math.max(0,this.targetReservationCount(t)-1);r>0?this.targetReservations.set(t,r):this.targetReservations.delete(t),this.notifyCapacityChanged()}async createAgent(t){let r=c(t.agentId);if(!r)throw Object.assign(new Error("agentId is required"),{status:400,code:"BAD_REQUEST"});let{code:n,target:s}=this.targetFor(t.targetCode||t.framework),i=this.agents.get(r);if(i&&i.targetCode!==n)throw Object.assign(new Error(`runtime agent target conflict: ${r}`),{code:"RUNTIME_AGENT_TARGET_CONFLICT"});let o=this.config.sandboxRequired?await Qe(this.config,s,t.workspace):c(t.workspace),a={agentId:r,targetCode:n,framework:c(t.framework||n),workspace:o,metadata:l(t.metadata)?t.metadata:{},createdAt:yt(),runtimeKind:K(s)};return this.agents.set(r,a),{agent:a}}runtimeFor(t,r,n={}){let{target:s}=this.targetFor(t.targetCode),i=K(s),o={onPermissionRequest:u=>n.source==="conversation_delegation"?{outcome:"denied",reason:"External group member permission continuation is not supported"}:this.requestPermission(u)},a={sessionPromptTimeoutMs:this.config.sessionPromptTimeoutMs,initializeTimeoutMs:this.config.initializeTimeoutMs,sandboxRequired:this.config.sandboxRequired,runId:n.runId,source:n.source,cloudSessionId:r,orchaUrl:this.config.orchaUrl,attachOnly:n.attachOnly,sandboxPolicy:n.sandboxPolicy,...this.config.sandboxRequired?{prepareLaunch:u=>jn({config:this.config,target:s,...u,runId:n.runId,agentId:t.agentId,sessionId:r,sandboxPolicy:n.sandboxPolicy})}:{}};return i==="stdio-acp"||i==="acp"?new ut(t,s,o,a):i==="openclaw"?new Bt(t,s,r,o,a):i==="cli"?new Jt(t,s,a):new kt(t)}async prompt(t,r={}){if(this.assertDispatchable(),this.config.sandboxRequired){let a=c(t.runId);if(!a)throw Object.assign(new Error("runId is required"),{code:"BAD_REQUEST"});t={...t,runId:a,sandboxPolicy:nt(t.sandboxPolicy)}}let n=c(t.agentId),s=c(t.sessionId)||te(),i=c(t.correlationId);if(!n)throw Object.assign(new Error("agentId is required"),{status:400,code:"BAD_REQUEST"});if(this.cleanupOperations(),i){let a=this.operations.get(i);if(a){if(this.assertOperationIdentity(a,t,n,s),a.status==="running"&&(["conversation_delegation","channel_message"].includes(t.metadata?.source)||t.metadata?.source==="ai_chat_runtime_node"&&t.framework==="codex")){a.mcpServers=t.mcpServers;let u=this.sessions.get(`${n}:${s}`);if(u){if(!u.runtime.refreshMcpServers)throw Object.assign(new Error("This runtime cannot refresh task MCP credentials"),{code:"unsupported_dynamic_mcp"});await u.runtime.refreshMcpServers(t.mcpServers)}}return this.attachOperation(a,r)}if(t.attachOnly===!0&&!(t.metadata?.source==="channel_message"&&this.targetFor(t.targetCode||t.framework).target.type==="openclaw"))throw Object.assign(new Error(`runtime operation is no longer available: ${i}`),{code:"RUNTIME_OPERATION_NOT_FOUND"})}else if(t.attachOnly===!0)throw Object.assign(new Error("correlationId is required for attachOnly"),{code:"BAD_REQUEST"});let o=i?this.createOperation(i,t,n,s):null;if(o){let a=this.attachOperation(o,r);return(async()=>{try{await this._promptOwned(t,{},o,n,s)}catch(u){let d=o.status==="completed";this.failOperation(o,u),d&&g("warn","runtime-bridge","runtime cleanup failed after operation completion",{correlationId:o.correlationId,error:String(u)})}})(),await a}return await this._promptOwned(t,r,o,n,s)}async _promptOwned(t,r,n,s,i){let o=c(t.runId),a=this.config.sandboxRequired?nt(n?.sandboxPolicy||t.sandboxPolicy):null,u=this.agents.get(s),d=this.targetFor(t.targetCode||t.framework||u?.targetCode);if(u&&u.targetCode!==d.code)throw Object.assign(new Error(`runtime agent target conflict: ${s}`),{code:"RUNTIME_AGENT_TARGET_CONFLICT"});if(K(d.target)==="cli"&&Ra(t.goalContract))throw Object.assign(new Error("CLI runtimes do not support per-run dynamic MCP required by plugin_operation"),{code:"unsupported_dynamic_mcp"});let f=u||(await this.createAgent({agentId:s,targetCode:d.code,framework:t.framework,workspace:t.workspace,metadata:t.metadata})).agent;if(!this.config.sandboxRequired){let _=c(t.workspace);_&&_!==f.workspace&&(f.workspace=_)}let p=`${s}:${i}`;await this.cleanupExpiredSessions();let h=this.sessions.get(p),m=null;if(h&&h.targetCode!==d.code)throw Object.assign(new Error(`runtime session target conflict: ${i}`),{code:"RUNTIME_SESSION_TARGET_CONFLICT"});if(h&&this.config.sandboxRequired&&h.runId!==o)throw Object.assign(new Error(`runtime session cannot be shared across runs: ${i}`),{code:"RUNTIME_SESSION_RUN_CONFLICT"});if(!h){if(this.startingSessions.has(p))throw Object.assign(new Error(`runtime session is already starting: ${i}`),{code:"RUNTIME_SESSION_BUSY"});this.startingSessions.add(p);let{code:_,target:w}=d,E;try{await this.reserveTargetSlot(_,w),m=_;let O=this.runtimeFor(f,i,{runId:o,sandboxPolicy:a,source:t.metadata?.source,attachOnly:t.attachOnly});E=O;let U=n?.mcpServers||t.mcpServers||[],v=await O.sessionNew({cwd:tt(w,f.workspace),mcpServers:U,metadata:l(t.metadata)?t.metadata:{}});if(n&&n.status!=="running")throw Object.assign(new Error("Runtime operation was cancelled before prompt dispatch"),{code:"RUNTIME_OPERATION_CANCELLED"});let D=Date.now();h={agentId:s,sessionId:i,runtime:O,targetCode:_,runtimeKind:K(w),runId:o,sandboxPolicy:a?structuredClone(a):null,runtimeSessionId:c(l(v)?v.sessionId:"")||i,idleTtlMs:this.targetIdleTtlMs(w),releaseAfterPrompt:this.config.sandboxRequired?!0:this.targetReleaseAfterPrompt(w,t.metadata),createdAtMs:D,lastUsedAtMs:D,busy:!1,createdAt:yt()},this.sessions.set(p,h),this.runtimeSessionIndex.set(`${s}:${h.runtimeSessionId}`,h),n?.mcpServers!==U&&n?.mcpServers&&O.refreshMcpServers&&await O.refreshMcpServers(n.mcpServers)}catch(O){throw this.sessions.get(p)?.runtime===E?await this.closeSession(p,"session_start_failed"):await E?.stop("session_start_failed"),m&&(this.releaseTargetSlot(m),m=null),O}finally{this.startingSessions.delete(p)}}if(h.busy){let _=new Error(`runtime session is already running: ${i}`);throw _.code="RUNTIME_SESSION_BUSY",_}h.busy=!0,m&&this.releaseTargetSlot(m),this.notifyCapacityChanged(),h.operationCorrelationId=n?.correlationId||null,h.lastUsedAtMs=Date.now();let y=n?.updates||[],S=_=>{let w=Sa(_,i,h);if(y.push(w),n)for(let E of n.listeners)try{E?.(w)}catch(O){g("warn","runtime-bridge","runtime operation update listener failed",{correlationId:n.correlationId,error:String(O)})}else r.onUpdate?.(w)},N=_=>{if(!(!n||n.status!=="running"||!l(_))){if(_.status==="failed"){let w=_.error;if(w instanceof Error){this.failOperation(n,w);return}let E=l(w)?w:{},O=Object.assign(new Error(c(E.message||_.message)||"runtime terminal failure"),{code:c(E.code||_.code)||"RUNTIME_TERMINAL_FAILED"});this.failOperation(n,O);return}this.completeOperation(n,this.promptResponse(t,h,i,y,l(_.result)?_.result:{stopReason:"end_turn"}))}};try{let _=vn(t.goalContract),w=Array.isArray(t.prompt)?[...t.prompt]:[];_&&w.unshift({type:"text",text:_,metadata:{source:"orcha_goal_contract"}});let E=l(t.metadata)?t.metadata:{},O=l(E._runtimeBridge)?E._runtimeBridge:{},U=await h.runtime.sessionPrompt({sessionId:h.runtimeSessionId,prompt:w,metadata:E},S,N),v=y,D=null;if(Wn({type:h.runtimeKind})&&ja(U,y,O)){D=U;let z=y.length,I=await h.runtime.sessionPrompt({sessionId:h.runtimeSessionId,prompt:[{type:"text",text:qa()}],metadata:E,_meta:{thinking:"off"}},S,N);U=Ga(D,I),v=y.slice(z)}let Y;try{Y=this.promptResponse(t,h,i,y,U,v)}catch(z){throw D&&es(U)?za(U,v):z}return D&&(Y.result=Ka(Y.result,D)),n&&this.completeOperation(n,Y),Y}finally{h.busy=!1,this.notifyCapacityChanged(),h.operationCorrelationId===n?.correlationId&&(h.operationCorrelationId=null),h.lastUsedAtMs=Date.now(),this.promptReleaseAfterPrompt(h,l(t.metadata)?t.metadata:{})&&await this.closeSession(p,"release_after_prompt"),n&&(n.mcpServers=null)}}async updatePolicy(t){if(this.assertDispatchable(),!this.config.sandboxRequired)throw Object.assign(new Error("runtime sandbox policy updates require an enforced sandbox"),{code:"RUNTIME_NODE_SANDBOX_UNAVAILABLE"});let r=c(t.agentId),n=c(t.runId),s=c(t.sessionId),i=c(t.correlationId);if(!r||!n||!s||!i)throw Object.assign(new Error("agentId, runId, sessionId and correlationId are required"),{code:"BAD_REQUEST"});let o=nt(t.sandboxPolicy),a=this.operations.get(i);if(a&&(a.agentId!==r||a.sessionId!==s||a.identity.runId!==n))throw Object.assign(new Error(`runtime operation policy identity conflict: ${i}`),{code:"RUNTIME_OPERATION_CORRELATION_CONFLICT"});let u=`${r}:${s}`,d=this.sessions.get(u);if(d&&d.runId!==n)throw Object.assign(new Error(`runtime session policy identity conflict: ${s}`),{code:"RUNTIME_SESSION_RUN_CONFLICT"});let f=d?.sandboxPolicy||a?.sandboxPolicy;if(!f)throw Object.assign(new Error(`runtime operation is no longer available: ${i}`),{code:"RUNTIME_OPERATION_NOT_FOUND"});let p=Number(f.policyRevision),h=Number(o.policyRevision);if(h<p)throw Object.assign(new Error("runtime sandbox policy revision is stale"),{code:"RUNTIME_POLICY_REVISION_CONFLICT",currentPolicyRevision:p});if(h===p){if(q(f)!==q(o))throw Object.assign(new Error("runtime sandbox policy revision payload conflicts"),{code:"RUNTIME_POLICY_REVISION_CONFLICT",currentPolicyRevision:p});return{updated:!1,idempotent:!0,policyRevision:p,accessMode:f.accessMode}}let m=Fn(f,o);a&&(a.sandboxPolicy=structuredClone(o)),d&&(d.sandboxPolicy=structuredClone(o));let y=!1;return m&&d?.busy===!0&&(await d.runtime.sessionCancel({sessionId:d.runtimeSessionId}),y=await this.closeSession(u,"sandbox_policy_downgrade")),{updated:!0,policyRevision:h,accessMode:o.accessMode,activeCommandCancelled:y,deferredToNextLaunch:!m&&d?.busy===!0}}async cancel(t){let r=c(t.agentId),n=c(t.sessionId),s=c(t.correlationId);if(s){let a=this.operations.get(s);if(!a||a.status!=="running")return{cancelled:!1,correlationId:s,reason:"operation_not_running"};if(a.agentId!==r||a.sessionId!==n)throw Object.assign(new Error(`runtime operation cancellation identity conflict: ${s}`),{code:"RUNTIME_OPERATION_CORRELATION_CONFLICT"});let u=this.sessions.get(`${r}:${n}`);return!u||u.busy!==!0||u.operationCorrelationId!==s?(this.failOperation(a,Object.assign(new Error("Runtime operation cancelled before dispatch"),{code:"RUNTIME_OPERATION_CANCELLED"})),{cancelled:!0,correlationId:s,reason:"cancelled_before_dispatch"}):(await u.runtime.sessionCancel({sessionId:u.runtimeSessionId}),await this.closeSession(`${r}:${n}`,"cancelled"),{cancelled:!0,correlationId:s})}let i=`${r}:${n}`,o=this.sessions.get(i);return o?(await o.runtime.sessionCancel({sessionId:o.runtimeSessionId}),await this.closeSession(i,"cancelled"),{cancelled:!0}):{cancelled:!1}}async dispatch(t,r={}){let n=c(t.agentId),s=c(t.taskId),i=c(t.executionId);if(!n||!s)throw Object.assign(new Error("agentId and taskId are required"),{status:400,code:"BAD_REQUEST"});let o=c(t.sessionId)||`dispatch-${i||s}`,a=c(t.instructions),u=Ua(t),d=Xn(t),{target:f}=this.targetFor(t.targetCode||t.framework),p=[{type:"text",text:["Task dispatch received.",`taskId: ${s}`,i?`executionId: ${i}`:"",a?`instructions: ${a}`:"",u?"orchaContext:":"",u,u?"Use orchaContext as the source of truth for upstream outputs, review reports, artifacts, and workflow metadata.":"","Follow the execution protocol in instructions exactly.","When embedding a generated artifact in final Markdown, use the Orcha artifact placeholder as the link target: orcha-artifact://<fieldKey>/<index>.","Example: use  for the first generated image artifact.","Do not use local filesystem paths, file:// URLs, or data: URLs in final Markdown; Orcha replaces artifact placeholders after ingestion.",d?`For decision results, put the raw report between ${De} and ${He}.`:"",d?`Then put a JSON object containing _decision between ${$e} and ${Fe}.`:"",d?"Do not JSON-escape the report. Keep output out of the CONTROL JSON, and put every protocol marker on its own line.":"",d?`Keep the report within ${Jn} Unicode characters so the CONTROL block fits before the output limit.`:"","Return only the final answer required by that protocol.","Do not include process notes, logs, or explanations."].filter(Boolean).join(`
|
|
21
21
|
`)}];return this.prompt({...t,agentId:n,sessionId:o,prompt:p,metadata:Ma(t,f)},r)}async requestPermission({agent:t,runtimeSessionId:r,request:n,emitUpdate:s}){let i=te(),a=this.runtimeSessionIndex.get(`${t.agentId}:${r}`)?.sessionId||r||te(),u={permissionId:i,agentId:t.agentId,sessionId:a,runtimeSessionId:r,request:l(n)?n:{},status:"pending",createdAt:yt()};return s?.({sessionId:r,update:{sessionUpdate:"permission_request",permission:u}}),await new Promise(d=>{let f=T(n?.timeoutMs,this.config.permissionRequestTimeoutMs),p=setTimeout(()=>{this.pendingPermissions.delete(i),d({permissionId:i,outcome:"denied",response:{reason:"permission request timed out"}})},f);this.pendingPermissions.set(i,{timer:p,resolve:d,permission:u})})}async respondPermission(t){let r=c(t.permissionId),n=this.pendingPermissions.get(r),s=c(t.outcome||"approved")||"approved";return n?(clearTimeout(n.timer),this.pendingPermissions.delete(r),n.resolve({permissionId:r,outcome:s,response:l(t.response)?t.response:{},userId:c(t.userId),metadata:l(t.metadata)?t.metadata:{}}),{accepted:!0,permissionId:r,outcome:s}):{accepted:!1,permissionId:r,outcome:s}}health(){return{status:this.config.sandboxRequired&&!this.sandboxCapabilities.dispatchable?"unavailable":"ok",bridgeVersion:V,startedAt:this.startedAt,agents:[...this.agents.values()].map(t=>({agentId:t.agentId,targetCode:t.targetCode,framework:t.framework,runtimeKind:t.runtimeKind})),sessions:this.sessions.size,pendingPermissions:this.pendingPermissions.size,targets:Object.keys(this.targets),sandboxCapabilities:this.sandboxCapabilities}}};import Ja from"node:http";import Va from"node:https";import{randomBytes as rs}from"node:crypto";var pt=1,st=3,Wa=1e4,ne=class{constructor(t,r={}){this.url=t,this.readyState=0,this.socket=null,this.request=null,this.buffer=Buffer.alloc(0),this.listeners=new Map,this.connectTimeoutMs=Xa(r.connectTimeoutMs,Wa),this.connectTimer=null,this.connect()}addEventListener(t,r){let n=this.listeners.get(t)||new Set;n.add(r),this.listeners.set(t,n)}emit(t,r={}){let n=this.listeners.get(t);if(n)for(let s of n)s(r)}connect(){let t=new URL(this.url),r=t.protocol==="wss:"?Va:Ja,n=rs(16).toString("base64"),s=r.request({protocol:t.protocol==="wss:"?"https:":"http:",hostname:t.hostname,port:t.port||(t.protocol==="wss:"?443:80),path:`${t.pathname}${t.search}`,method:"GET",headers:{Connection:"Upgrade",Upgrade:"websocket","Sec-WebSocket-Key":n,"Sec-WebSocket-Version":"13"}});this.request=s,this.startConnectTimer(s),s.on("upgrade",(i,o,a)=>{if(this.clearConnectTimer(),this.readyState===st){o.destroy();return}this.socket=o,this.readyState=pt,o.on("data",u=>this.onData(u)),o.on("close",()=>this.onClose()),o.on("error",u=>{this.readyState!==st&&this.emit("error",{error:u,message:u.message,type:"error"}),this.onClose()}),a?.length&&this.onData(a),this.emit("open")}),s.on("response",i=>{this.clearConnectTimer(),this.emit("error",{message:`websocket upgrade failed: HTTP ${i.statusCode}`,type:"error"}),i.resume(),this.onClose()}),s.on("error",i=>{this.clearConnectTimer(),this.readyState!==st&&this.emit("error",{error:i,message:i.message,type:"error"}),this.onClose()}),s.end()}startConnectTimer(t){this.connectTimeoutMs<=0||(this.connectTimer=setTimeout(()=>{if(this.readyState===pt||this.readyState===st)return;let r=`websocket connect timeout after ${this.connectTimeoutMs}ms`;this.emit("error",{message:r,type:"timeout"}),this.onClose(),t.destroy(new Error(r))},this.connectTimeoutMs),this.connectTimer.unref?.())}clearConnectTimer(){this.connectTimer&&(clearTimeout(this.connectTimer),this.connectTimer=null)}send(t){!this.socket||this.readyState!==pt||this.socket.write(pr(1,Buffer.from(String(t),"utf8")))}close(){if(this.readyState!==st){if(this.clearConnectTimer(),!this.socket){this.request?.destroy(),this.onClose();return}try{this.readyState===pt&&this.socket.write(pr(8,Buffer.alloc(0))),this.socket.end()}catch{this.socket.destroy()}this.onClose()}}onClose(){this.readyState!==st&&(this.clearConnectTimer(),this.readyState=st,this.request=null,this.emit("close"))}onData(t){for(this.buffer=Buffer.concat([this.buffer,t]);this.buffer.length>=2;){let r=this.buffer[0],n=this.buffer[1],s=r&15,i=(n&128)!==0,o=n&127,a=2;if(o===126){if(this.buffer.length<a+2)return;o=this.buffer.readUInt16BE(a),a+=2}else if(o===127){if(this.buffer.length<a+8)return;let f=this.buffer.readUInt32BE(a),p=this.buffer.readUInt32BE(a+4);o=f*2**32+p,a+=8}let u=a;if(i&&(a+=4),this.buffer.length<a+o)return;let d=this.buffer.subarray(a,a+o);if(i){let f=this.buffer.subarray(u,u+4);d=Buffer.from(d.map((p,h)=>p^f[h%4]))}this.buffer=this.buffer.subarray(a+o),s===1?this.emit("message",{data:d.toString("utf8")}):s===8?this.close():s===9&&this.socket?.write(pr(10,d))}}};function Xa(e,t){let r=Number.parseInt(String(e??""),10);return Number.isFinite(r)&&r>=0?r:t}function pr(e,t){let r=t.length,n=2;r>=126&&r<=65535?n+=2:r>65535&&(n+=8);let s=rs(4),i=Buffer.alloc(n+4+r);i[0]=128|e;let o=2;r<126?i[1]=128|r:r<=65535?(i[1]=254,i.writeUInt16BE(r,o),o+=2):(i[1]=255,i.writeUInt32BE(Math.floor(r/2**32),o),i.writeUInt32BE(r>>>0,o+4),o+=8),s.copy(i,o),o+=4;for(let a=0;a<r;a+=1)i[o+a]=t[a]^s[a%4];return i}function Ya(e){let t=new URL(e);if(t.protocol==="http:")t.protocol="ws:";else if(t.protocol==="https:")t.protocol="wss:";else if(t.protocol!=="ws:"&&t.protocol!=="wss:")throw new Error(`unsupported ORCHA_RUNTIME_ORCHA_URL protocol: ${t.protocol}`);return t.pathname=`${t.pathname.replace(/\/+$/,"")}/api/agent-runtime-bridge/ws`,t.search="",t.toString()}function Qa(e,t=!0){return Object.entries(e||{}).map(([r,n])=>({targetCode:r,targetName:c(n?.name||n?.targetName||r),targetType:c(n?.type||"echo"),status:t?"available":"unavailable"}))}var Za=Object.freeze(["health","agents/create","agents/dispatch","sessions/prompt","sessions/policy","sessions/cancel","permissions/respond"]),se=class{constructor(t,r){this.config=t,this.bridge=r,this.ws=null,this.stopped=!1,this.connected=!1,this.reconnectTimer=null,this.heartbeatTimer=null,this.capacityHeartbeatTimer=null,this.bridge.onCapacityChanged=()=>this.scheduleCapacityHeartbeat()}sandboxCapabilities(){return this.bridge.sandboxCapabilities||{dispatchable:this.config.sandboxRequired!==!0,enforced:!1,backend:"development_direct",daemonStatus:"development_only",policySchemaVersions:[1],runtimePolicyUpdates:!0,workspaceSync:!1,egressProxy:!1}}supportedMethods(){return[...Za,...this.config.deviceConnection?["devices/discover","devices/configure"]:[]]}runtimeTargets(){let t=this.sandboxCapabilities().dispatchable===!0;return typeof this.bridge.targetCapacitySnapshot=="function"?this.bridge.targetCapacitySnapshot(t):Qa(this.config.targets,t)}start(){this.connect()}stop(){this.stopped=!0,this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null,this.capacityHeartbeatTimer&&clearTimeout(this.capacityHeartbeatTimer),this.capacityHeartbeatTimer=null,this.bridge.onCapacityChanged=null,this.ws?.close()}connect(){if(this.stopped)return;let t=Ya(this.config.orchaUrl),r=new ne(t,{connectTimeoutMs:this.config.connectTimeoutMs});this.ws=r,r.addEventListener("open",()=>{if(this.ws!==r||this.stopped){r.close();return}this.authenticate(r)}),r.addEventListener("message",n=>this.handleMessage(n.data,r)),r.addEventListener("close",()=>this.scheduleReconnect("close",r)),r.addEventListener("error",n=>{g("warn","orcha-client","runtime node websocket error",{error:String(n?.message||n?.type||n)})})}authenticate(t=this.ws){this.send({type:"runtime_node.authenticate",token:this.config.nodeToken,nodeId:this.config.nodeId,nodeName:this.config.nodeName,version:V,protocolVersion:"orcha-runtime-node.v2",defaultTargetCode:this.config.defaultTargetCode,supportedMethods:this.supportedMethods(),sandboxCapabilities:this.sandboxCapabilities(),targets:this.runtimeTargets()},t)}scheduleReconnect(t,r=this.ws){r&&this.ws!==r||(this.connected=!1,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null,this.capacityHeartbeatTimer&&clearTimeout(this.capacityHeartbeatTimer),this.capacityHeartbeatTimer=null,r&&this.ws===r&&(this.ws=null),!this.stopped&&(this.reconnectTimer||(g("info","orcha-client","runtime node disconnected; reconnect scheduled",{reason:t,reconnectMs:this.config.reconnectMs}),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},this.config.reconnectMs))))}async handleMessage(t,r=this.ws){if(r&&this.ws!==r)return;let n;try{n=JSON.parse(String(t))}catch(s){g("warn","orcha-client","invalid Orcha websocket frame",{error:String(s)});return}if(l(n)){if(n.type==="runtime_node.authenticated"){if(r&&this.ws!==r)return;this.connected=!0,this.startHeartbeat(r),g("info","orcha-client","runtime node authenticated",{nodeId:this.config.nodeId,connectionId:n.connectionId});return}if(n.type==="orcha.runtime.request"){await this.handleRequest(n);return}n.type==="runtime_node.ping"&&this.send({type:"runtime_node.pong",ts:new Date().toISOString(),supportedMethods:this.supportedMethods(),sandboxCapabilities:this.sandboxCapabilities(),targets:this.runtimeTargets()})}}startHeartbeat(t){this.heartbeatTimer&&clearInterval(this.heartbeatTimer);let r=Math.max(1e3,Number(this.config.heartbeatIntervalMs||15e3)),n=()=>this.sendCapacityHeartbeat(t);n(),this.heartbeatTimer=setInterval(n,r),this.heartbeatTimer.unref?.()}scheduleCapacityHeartbeat(){if(!this.connected||this.capacityHeartbeatTimer)return;let t=Math.max(25,Math.min(1e3,Number(this.config.capacityHeartbeatDebounceMs||100)));this.capacityHeartbeatTimer=setTimeout(()=>{this.capacityHeartbeatTimer=null,this.sendCapacityHeartbeat()},t),this.capacityHeartbeatTimer.unref?.()}sendCapacityHeartbeat(t=this.ws){return this.connected?this.send({type:"runtime_node.heartbeat",ts:new Date().toISOString(),supportedMethods:this.supportedMethods(),sandboxCapabilities:this.sandboxCapabilities(),targets:this.runtimeTargets()},t):!1}async handleRequest(t){let r=c(t.id),n=c(t.method),s=l(t.params)?t.params:{};try{let i=await this.invoke(n,s,r);this.send({type:"orcha.runtime.response",id:r,ok:!0,result:i})}catch(i){this.send({type:"orcha.runtime.response",id:r,ok:!1,error:{code:i.code||"RUNTIME_NODE_ERROR",message:i.message||String(i),...i.detail===void 0?{}:{detail:i.detail}}})}}async invoke(t,r,n){if(t==="devices/discover"&&this.config.deviceConnection)return this.config.deviceConnection.candidates();if(t==="devices/configure"&&this.config.deviceConnection)return this.config.deviceConnection.configure(r,this.bridge);if(t==="health")return this.bridge.health();if(t==="agents/create")return this.bridge.createAgent(r);if(t==="agents/dispatch")return this.bridge.dispatch(r,{onUpdate:s=>this.sendUpdate(n,s)});if(t==="sessions/prompt")return this.bridge.prompt(r,{onUpdate:s=>this.sendUpdate(n,s)});if(t==="sessions/policy")return this.bridge.updatePolicy(r);if(t==="sessions/cancel")return this.bridge.cancel(r);if(t==="permissions/respond")return this.bridge.respondPermission(r);throw Object.assign(new Error(`unsupported Orcha runtime method: ${t}`),{code:"METHOD_NOT_FOUND"})}sendUpdate(t,r){this.send({type:"orcha.runtime.event",requestId:t,eventType:"runtime.update",payload:r})}send(t,r=this.ws){return!r||r!==this.ws||r.readyState!==pt?!1:(r.send(JSON.stringify(t)),!0)}};import{existsSync as ns,readFileSync as tc}from"node:fs";import{dirname as ec,resolve as ie}from"node:path";import{fileURLToPath as rc}from"node:url";var nc=/^[A-Za-z_][A-Za-z0-9_]*$/;function ss(e=process.env,t=process.cwd()){let r=c(e.ORCHA_RUNTIME_ENV_FILE),n=r?ie(t,r):ie(t,".env");if(!ns(n))return r&&g("warn","config","runtime env file not found",{path:n}),{loaded:!1,path:n,keys:[]};let s=[],i=tc(n,"utf8");for(let o of i.split(/\r?\n/)){let a=oc(o);if(!a)continue;let[u,d]=a;Object.prototype.hasOwnProperty.call(e,u)||(e[u]=d,s.push(u))}return g("info","config","runtime env file loaded",{path:n,keyCount:s.length}),{loaded:!0,path:n,keys:s}}function is(e=process.env){let t=c(e.ORCHA_RUNTIME_MODE||(e.ORCHA_RUNTIME_ORCHA_URL?"node":"server")),r=c(e.ORCHA_RUNTIME_BRIDGE_HOST||"127.0.0.1"),n=!1,s=ec(rc(import.meta.url)),i=ie(s,"sandbox","broker.js"),o=ie(s,"orcha-runtime-sandbox-broker.js"),a=ns(i)?i:o,u=c(e.ORCHA_RUNTIME_SANDBOX_BROKER_COMMAND);return{mode:t,host:r,port:T(e.ORCHA_RUNTIME_BRIDGE_PORT,5588),token:e.ORCHA_RUNTIME_BRIDGE_TOKEN||"",orchaUrl:c(e.ORCHA_RUNTIME_ORCHA_URL),nodeToken:e.ORCHA_RUNTIME_NODE_TOKEN||e.ORCHA_RUNTIME_BRIDGE_TOKEN||"",nodeId:c(e.ORCHA_RUNTIME_NODE_ID||e.HOSTNAME||"local-runtime-node"),nodeName:c(e.ORCHA_RUNTIME_NODE_NAME||e.ORCHA_RUNTIME_NODE_ID||e.HOSTNAME||"Local Runtime Node"),reconnectMs:T(e.ORCHA_RUNTIME_RECONNECT_MS,3e3),connectTimeoutMs:T(e.ORCHA_RUNTIME_CONNECT_TIMEOUT_MS||e.ORCHA_RUNTIME_WS_CONNECT_TIMEOUT_MS,1e4),defaultTargetCode:e.ORCHA_RUNTIME_DEFAULT_TARGET||"echo",requestLimitBytes:T(e.ORCHA_RUNTIME_REQUEST_LIMIT_BYTES,1048576),initializeTimeoutMs:T(e.ORCHA_RUNTIME_INITIALIZE_TIMEOUT_MS,3e4),sessionPromptTimeoutMs:T(e.ORCHA_RUNTIME_SESSION_PROMPT_TIMEOUT_MS,36e5),permissionRequestTimeoutMs:T(e.ORCHA_RUNTIME_PERMISSION_TIMEOUT_MS,3e5),sessionIdleTtlMs:T(e.ORCHA_RUNTIME_SESSION_IDLE_TTL_MS,6e5),sessionCleanupIntervalMs:T(e.ORCHA_RUNTIME_SESSION_CLEANUP_INTERVAL_MS,6e4),maxSessionsPerTarget:T(e.ORCHA_RUNTIME_MAX_SESSIONS_PER_TARGET,8),openClawSessionIdleTtlMs:T(e.ORCHA_RUNTIME_OPENCLAW_SESSION_IDLE_TTL_MS,6e4),openClawMaxSessions:T(e.ORCHA_RUNTIME_OPENCLAW_MAX_SESSIONS,1),heartbeatIntervalMs:T(e.ORCHA_RUNTIME_HEARTBEAT_INTERVAL_MS,15e3),sandboxRequired:n,sandboxBrokerCommand:u||process.execPath,sandboxBrokerArguments:hr(e.ORCHA_RUNTIME_SANDBOX_BROKER_ARGUMENTS,"ORCHA_RUNTIME_SANDBOX_BROKER_ARGUMENTS",u?[]:[a]),sandboxProbeTimeoutMs:T(e.ORCHA_RUNTIME_SANDBOX_PROBE_TIMEOUT_MS,1e4),sandboxTempRoot:c(e.ORCHA_RUNTIME_SANDBOX_TEMP_ROOT),trustedWorkspaceRoots:hr(e.ORCHA_RUNTIME_TRUSTED_WORKSPACE_ROOTS,"ORCHA_RUNTIME_TRUSTED_WORKSPACE_ROOTS"),trustedToolchainRoots:hr(e.ORCHA_RUNTIME_TRUSTED_TOOLCHAIN_ROOTS,"ORCHA_RUNTIME_TRUSTED_TOOLCHAIN_ROOTS"),egressProxyRequired:ic(e.ORCHA_RUNTIME_EGRESS_PROXY_REQUIRED,t==="node"),egressProxySocket:c(e.ORCHA_RUNTIME_EGRESS_PROXY_SOCKET),egressProxyToken:c(e.ORCHA_RUNTIME_EGRESS_PROXY_TOKEN),egressGrantMaxTtlMs:T(e.ORCHA_RUNTIME_EGRESS_GRANT_MAX_TTL_MS,9e5),targets:sc(e.ORCHA_RUNTIME_TARGETS_JSON||"")}}function sc(e){if(!c(e))return{};try{let t=JSON.parse(e);if(l(t))return t}catch(t){g("warn","config","invalid ORCHA_RUNTIME_TARGETS_JSON",{error:String(t)})}return{}}function ic(e,t){let r=c(e).toLowerCase();if(!r)return t;if(["true","1","yes","on"].includes(r))return!0;if(["false","0","no","off"].includes(r))return!1;throw new Error(`invalid boolean configuration value: ${r}`)}function hr(e,t,r=[]){let n=c(e);if(!n)return[...r];let s;try{s=JSON.parse(n)}catch{throw new Error(`${t} must be a JSON string array`)}if(!Array.isArray(s)||s.some(i=>typeof i!="string"))throw new Error(`${t} must be a JSON string array`);return s.map(i=>i.trim()).filter(Boolean)}function oc(e){let t=e.trim();if(!t||t.startsWith("#"))return null;let r=t.startsWith("export ")?t.slice(7).trimStart():t,n=r.indexOf("=");if(n<=0)return null;let s=r.slice(0,n).trim();if(!nc.test(s))return null;let i=ac(r.slice(n+1).trim());return[s,i]}function ac(e){if(!e)return"";let t=e[0];if((t==='"'||t==="'")&&e.endsWith(t)){let r=e.slice(1,-1);return t==='"'?cc(r):r}return uc(e).trim()}function cc(e){return e.replaceAll("\\n",`
|
|
22
22
|
`).replaceAll("\\r","\r").replaceAll("\\t"," ").replaceAll('\\"','"').replaceAll("\\\\","\\")}function uc(e){let t=!1,r=!1;for(let n=0;n<e.length;n+=1){let s=e[n],i=e[n-1];if(s==="'"&&!r)t=!t;else if(s==='"'&&!t&&i!=="\\")r=!r;else if(s==="#"&&!t&&!r&&/\s/.test(i||""))return e.slice(0,n)}return e}import{createServer as lc}from"node:http";function oe(e,t,r){let n=JSON.stringify(r);e.writeHead(t,{"content-type":"application/json; charset=utf-8","content-length":Buffer.byteLength(n)}),e.end(n)}function it(e={}){return{ok:!0,...e}}function mr(e,t,r){return{ok:!1,error:{code:e,message:t,...r===void 0?{}:{detail:r}}}}async function os(e,t){let r=[],n=0;for await(let i of e){if(n+=i.length,n>t)throw Object.assign(new Error("request body too large"),{status:413,code:"REQUEST_TOO_LARGE"});r.push(i)}if(r.length===0)return{};let s=Buffer.concat(r).toString("utf8");if(!s.trim())return{};try{let i=JSON.parse(s);if(!l(i))throw Object.assign(new Error("JSON body must be an object"),{status:400,code:"BAD_JSON"});return i}catch(i){throw i.status?i:Object.assign(new Error(`invalid JSON body: ${i.message}`),{status:400,code:"BAD_JSON"})}}function as(e,t){if(!t)return;if((e.headers.authorization||"")!==`Bearer ${t}`)throw Object.assign(new Error("runtime bridge token is invalid"),{status:401,code:"UNAUTHORIZED"})}function cs(e,t){let r={"GET /health":async()=>it(t.health()),"POST /agents/create":async n=>it(await t.createAgent(n)),"POST /agents/dispatch":async n=>it(await t.dispatch(n)),"POST /sessions/prompt":async n=>it(await t.prompt(n)),"POST /sessions/cancel":async n=>it(await t.cancel(n)),"POST /permissions/respond":async n=>it(await t.respondPermission(n))};return lc(async(n,s)=>{let i=new URL(n.url||"/",`http://${n.headers.host||`${e.host}:${e.port}`}`),o=`${n.method||"GET"} ${i.pathname}`,a=r[o];if(!a){oe(s,404,mr("NOT_FOUND",`No route for ${o}`));return}try{as(n,e.token);let u=n.method==="GET"?{}:await os(n,e.requestLimitBytes);oe(s,200,await a(u))}catch(u){let d=u.status||500,f=u.code||"BRIDGE_INTERNAL";oe(s,d,mr(f,u.message||String(u),u.detail))}})}var gs=gc(import.meta.url),_c=".orcha-runtime-bridge.pid",yc=".orcha-runtime-bridge.log",Tc=3,wc=36e5,{command:Ec,options:_s}=Oc(process.argv.slice(2));Ic(_s);ss();var Rc=is();await ys(Ec,Rc,_s);async function ys(e,t,r){if(r.url){let s=ge(r.url);r.pidFile||(process.env.ORCHA_RUNTIME_PID_FILE=s.pidFile),r.logFile||(process.env.ORCHA_RUNTIME_LOG_FILE=s.logFile)}let n=()=>["connect","--url",t.orchaUrl,"--foreground"];switch(e){case"connect":if(r.pairCode&&ot(ce(ae())))throw new Error("\u8BBE\u5907\u8FDE\u63A5\u4ECD\u5728\u540E\u53F0\u8FD0\u884C\uFF0C\u8BF7\u5148\u6267\u884C stop --url \u540E\u91CD\u65B0\u914D\u5BF9");await bt(t,r),r.foreground?await gr(t):_r(n());return;case"run":case"foreground":r.url&&await bt(t,r),await gr(t);return;case"start":if(r.url)return ys("connect",t,r);if(r.foreground){await gr(t);return}_r();return;case"reload":case"restart":{r.url&&await bt(t,{url:r.url});let s=ht(ms(gs),".."),i=ht(s,...Er(ht(s,"src","index.js"))?["dist","package"]:[],"openclaw-plugin"),o=await qr(t.targets,i);await ds({quiet:!0}),await o(),_r(r.url?n():void 0);return}case"stop":await ds();return;case"status":Sc();return;case"help":case"--help":case"-h":Nc();return;default:throw new Error(`Unknown command: ${e}`)}}async function gr(e){Cc();let t=new re(e);if(await t.start(),e.mode==="server"){let n=cs(e,t);ls({bridge:t,server:n}),n.listen(e.port,e.host,()=>{g("info","runtime-bridge","Orcha runtime bridge HTTP server started",{host:e.host,port:e.port,targets:Object.keys(t.targets)})});return}if(!e.orchaUrl)throw new Error("ORCHA_RUNTIME_ORCHA_URL is required in runtime node mode");let r=new se(e,t);ls({bridge:t,client:r}),r.start(),g("info","runtime-bridge","Orcha runtime node started",{nodeId:e.nodeId,nodeName:e.nodeName,orchaUrl:e.orchaUrl,targets:Object.keys(t.targets)})}function ls({bridge:e,client:t,server:r}){let n=!1,s=i=>{if(n)return;n=!0;let o=Number(e?.sessions?.size||0)>0;g("info","runtime-bridge","runtime bridge shutting down",{signal:i});try{t?.stop?.()}catch(a){g("warn","runtime-bridge","runtime client stop failed",{error:String(a)})}Promise.resolve(e?.stop?.(`process_${String(i||"shutdown").toLowerCase()}`)).catch(a=>{g("warn","runtime-bridge","runtime bridge stop failed",{error:String(a)})}).finally(()=>{try{r?.close?.()}catch(a){g("warn","runtime-bridge","runtime server close failed",{error:String(a)})}}),setTimeout(()=>process.exit(0),o?8500:200)};process.once("SIGTERM",()=>s("SIGTERM")),process.once("SIGINT",()=>s("SIGINT"))}function _r(e=["run"]){let t=ae(),r=Ts(),n=ce(t);if(n&&ot(n)){console.log(JSON.stringify({status:"already_running",pid:n,pidFile:t,logFile:r}));return}n&&wr(t),ps(t),ps(r),Tr(r);let s=hc(r,"a"),i={...process.env,ORCHA_RUNTIME_PID_FILE:t,ORCHA_RUNTIME_LOG_FILE:r},o=dc(process.execPath,[gs,...e,"--pid-file",t,"--log-file",r],{cwd:process.cwd(),detached:!0,env:i,stdio:["ignore",s,s]});o.unref(),fc(s),hs(t,`${o.pid}
|
|
23
23
|
`),console.log(JSON.stringify({status:"started",pid:o.pid,pidFile:t,logFile:r}))}async function ds({quiet:e=!1}={}){let t=ae(),r=ce(t);if(!r){e||console.log(JSON.stringify({status:"not_running",pidFile:t}));return}if(!ot(r)){wr(t),e||console.log(JSON.stringify({status:"not_running",pid:r,pidFile:t}));return}process.kill(r,"SIGTERM"),!await fs(r,1e4)&&ot(r)&&(process.kill(r,"SIGKILL"),await fs(r,3e3)),wr(t),e||console.log(JSON.stringify({status:"stopped",pid:r,pidFile:t}))}function Sc(){let e=ae(),t=Ts(),r=ce(e),n=!!(r&&ot(r));console.log(JSON.stringify({status:n?"running":"not_running",running:n,pid:n?r:null,pidFile:e,logFile:t}))}function Oc(e){let t=[],r={};for(let s=0;s<e.length;s+=1){let i=e[s];i==="--url"?r.url=e[++s]:i==="--pair-code"?r.pairCode=e[++s]:i==="--daemon"?r.daemon=!0:i==="--foreground"?r.foreground=!0:i==="--env-file"?r.envFile=e[++s]:i==="--pid-file"?r.pidFile=e[++s]:i==="--log-file"?r.logFile=e[++s]:t.push(i)}return{command:t[0]||"run",options:r}}function Ic(e){e.envFile&&(process.env.ORCHA_RUNTIME_ENV_FILE=e.envFile),e.pidFile&&(process.env.ORCHA_RUNTIME_PID_FILE=e.pidFile),e.logFile&&(process.env.ORCHA_RUNTIME_LOG_FILE=e.logFile)}function ae(){return ht(process.cwd(),process.env.ORCHA_RUNTIME_PID_FILE||_c)}function Ts(){return ht(process.cwd(),process.env.ORCHA_RUNTIME_LOG_FILE||yc)}function Cc(){let e=process.env.ORCHA_RUNTIME_LOG_FILE;if(!e)return;let t=ws();if(t<=0)return;let r=ht(process.cwd(),e);Tr(r,t);let n=Ac();n<=0||setInterval(()=>Tr(r,t),n).unref()}function Tr(e,t=ws()){if(t<=0||!Er(e))return;let r=0,n="";try{if(r=us(e).size,r<=0)return;n=yr(e,"utf8")}catch(d){g("warn","runtime-bridge","runtime log cleanup skipped",{logFile:e,error:String(d)});return}let s=Date.now()-t*24*60*60*1e3,i=n.split(/\r?\n/),o=[],a=0;for(let d of i)d&&(xc(d,s)?o.push(d):a+=1);if(a===0)return;let u="";try{us(e).size>r&&(u=yr(e,"utf8").slice(n.length));let f=[...o,...u.split(/\r?\n/).filter(Boolean)].join(`
|
package/openclaw-plugin/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import{getPluginRuntimeGatewayRequestScope as Be}from"openclaw/plugin-sdk/plugin-runtime";import{loadOutboundMediaFromUrl as je}from"openclaw/plugin-sdk/outbound-media";function Oe(e){return String(e??"").trim()}var V="[redacted]",Te=/(authorization|api[_-]?key|credential|password|secret|token)/i;function ae(e){return Te.test(Oe(e))}function ie(e,n=[]){let o=String(e??"");for(let f of n){let c=String(f??"");c.length>=4&&(o=o.split(c).join(V))}return o.replace(/((?:authorization|api[_-]?key|credential|password|secret|token)\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi,`$1${V}`).replace(/([?&](?:api[_-]?key|credential|password|secret|token)=)[^&\s]+/gi,`$1${V}`)}var F={controlTools:["find_capabilities_and_skills","describe_capability","inspect_result","execute_capability"],residentTools:["search_orcha_docs","knowledge_search"]};var G=Object.freeze([...F.controlTools,...F.residentTools]),Se=new Set(G),Ne=8*1024*1024;function se(e,n){if(!Array.isArray(e)||e.length!==1)throw new Error("one task-scoped Orcha MCP server is required");let o=e[0],f=new URL(o.url);if(o.name!=="orcha"||o.type!=="http"||!["http:","https:"].includes(f.protocol)||f.username||f.password||f.hash||n&&f.href!==new URL(n).href)throw new Error("Orcha MCP server does not match the trusted binding");let c=Object.fromEntries((o.headers||[]).map(({name:l,value:m})=>[l.toLowerCase(),m]));if(!String(c.authorization||"").startsWith("Bearer "))throw new Error("Orcha MCP task credentials are missing");return{url:f.href,headers:{...c,"content-type":"application/json",accept:"application/json"}}}async function H(e){let n=0,o=[];for await(let f of e){if(n+=f.length,n>Ne)throw new Error("MCP payload exceeds the size limit");o.push(Buffer.from(f))}return JSON.parse(Buffer.concat(o).toString("utf8"))}async function Q(e,n,o){let f=await fetch(e.url,{method:"POST",headers:e.headers,body:JSON.stringify(n),redirect:"error",signal:AbortSignal.any([AbortSignal.timeout(36e5),...o?[o]:[]])});if(!f.ok)throw new Error(`Orcha MCP request failed (${f.status})`);if(f.status===202||f.status===204)return null;let c=await H(f.body);if(!c||c.jsonrpc!=="2.0"||c.id!==n.id)throw new Error("Orcha MCP response identity mismatch");return c}async function ce(e,n){let o=await Q(e,{jsonrpc:"2.0",id:"orcha-bind-tools",method:"tools/list"},n),f=o?.result?.tools;if(o?.error){let c=Object.entries(e.headers).filter(([s])=>ae(s)).flatMap(([,s])=>[s,String(s).replace(/^Bearer\s+/i,"")]),l=typeof o.error.message=="string"?ie(o.error.message,c).slice(0,512):"",m=Number.isInteger(o.error.code)?` (${o.error.code})`:"";throw new Error(`Orcha MCP tool discovery failed${m}${l?`: ${l}`:""}`)}return be(f)}function be(e){if(!Array.isArray(e)||e.some(n=>!n||!Se.has(n.name)||n.inputSchema?.type!=="object"||typeof n.description!="string")||new Set(e.map(n=>n.name)).size!==e.length||e.length&&F.controlTools.some(n=>!e.some(o=>o.name===n)))throw new Error("Orcha MCP returned an unsupported or invalid authorized tool catalog");return e}function de(e,n){let o=c=>Array.isArray(c)?c.map(o):c&&typeof c=="object"?Object.fromEntries(Object.keys(c).sort().map(l=>[l,o(c[l])])):c,f=c=>c.map(({name:l,inputSchema:m})=>({name:l,inputSchema:m})).sort((l,m)=>l.name.localeCompare(m.name));return JSON.stringify(o(f(e)))===JSON.stringify(o(f(n)))}var Z=e=>Object.assign(new Error(e),{status:409});function le(e){let n=new Map,o=e.pluginConfig?.allowedOrchaMcpUrl;if(!o)throw new Error("Orcha plugin requires allowedOrchaMcpUrl");function f(m,s=n.get(m)){!s||n.get(m)!==s||(s.lifetime.abort(),clearTimeout(s.timer),n.delete(m))}function c(m){let s=n.get(m);return s&&s.deadline<=Date.now()?(f(m,s),null):s}async function l(m,{refresh:s=!1}={}){let{runId:u,sessionKey:T,agentCode:P,expiresAt:x,mcpServer:t}=m,h=Date.parse(x);if(typeof u!="string"||!u||typeof T!="string"||!T||typeof P!="string"||!P||!T.startsWith(`agent:${P}:`)||!Number.isFinite(h)||h<=Date.now()||h>Date.now()+24*36e5)throw new Error("invalid_binding_scope");let w=c(T);if(s&&!w||w&&(w.runId!==u||w.agentCode!==P||w.deadline!==h))throw Z("original_binding_unavailable_or_changed");let r=se([t],o),d=w?.lifetime||new AbortController,a=await ce(r,d.signal);if(c(T)!==w)throw Z("binding_changed");if(w){if(!de(w.tools,a))throw Z("frozen_tool_catalog_changed");w.upstream=r}else{let i={runId:u,agentCode:P,deadline:h,upstream:r,tools:a,lifetime:d};i.timer=setTimeout(()=>f(T,i),h-Date.now()),i.timer.unref?.(),n.set(T,i)}}return e.registerTool(m=>{let s=c(m.sessionKey);return!s||m.agentId!==s.agentCode?null:s.tools.map(u=>({name:`orcha_${u.name}`,label:`Orcha ${u.name}`,description:u.description,parameters:u.inputSchema,async execute(T,P,x){if(c(m.sessionKey)!==s||m.agentId!==s.agentCode||!T||!s.tools.some(h=>h.name===u.name))throw new Error("Orcha task binding expired or changed");let t=await Q(s.upstream,{jsonrpc:"2.0",id:T,method:"tools/call",params:{name:u.name,arguments:P}},AbortSignal.any([s.lifetime.signal,...x?[x]:[]]));if(t?.error||!t?.result)throw new Error("Orcha rejected this task-scoped tool call");return{content:t.result.content,details:t.result.structuredContent,isError:t.result.isError===!0}}}))},{names:G.map(m=>`orcha_${m}`)}),{bind:l,active:c,release:f,allowedUrl:o}}import{createHash as Me}from"node:crypto";import{mkdirSync as Le,readFileSync as Pe,renameSync as xe,writeFileSync as ke}from"node:fs";import{join as me}from"node:path";import{createHash as Ce}from"node:crypto";var ue=e=>Ce("sha256").update(JSON.stringify(e)).digest("hex"),D=e=>Object.assign(new Error(e),{code:e}),q=e=>{let n=String(e||"").replace(/^orcha:/,"");return/^[1-9]\d{0,18}$/.test(n)?n:void 0},pe={normalizeTarget:q,targetResolver:{looksLikeId:e=>!!q(e),hint:"Orcha session ID"}};function fe({core:e,load:n,save:o,loadMedia:f,activeRun:c}){let l=new Map,m=(t,h)=>ue([t,h]),s=t=>({channel:"orcha",messageId:t.messageId,chatId:t.sessionId,receipt:{primaryPlatformMessageId:t.messageId,platformMessageIds:[t.messageId],parts:[{platformMessageId:t.messageId,kind:"unknown",index:0}],sentAt:Date.now()}});async function u(t,h,w={}){return await w.onPlatformSendDispatch?.(),w.signal?.throwIfAborted(),s(await e(t,"/messages",h.payload,{cfg:w.cfg,signal:w.signal}))}async function T(t){t.signal?.throwIfAborted();let h=q(t.to);if(!h)throw D("invalid_conversation");if(!t.deliveryQueueId)throw D("OPENCLAW_DURABLE_DELIVERY_ID_REQUIRED");let w=t.run&&t.run.delivery.bindingId===t.accountId&&t.run.delivery.sessionId===h?t.run:null,r=t.replyToId||w?.delivery.messageId||null,d=t.payload||{text:t.text||"",mediaUrl:t.mediaUrl},a=d.mediaUrls||(d.mediaUrl?[d.mediaUrl]:[]),i=m(t.accountId,t.deliveryQueueId),p=ue([h,d.text||"",a,r,w?.runId||null]),I=n(i,"sends");if(I&&I.requestHash!==p)throw D("channel_send_conflict");let E=l.get(i);if(E){if(E.requestHash!==p)throw D("channel_send_conflict");return E.promise}let O=(async()=>{let g=I;if(!g){let y=[];for(let L of a){let N=await f(L,{maxBytes:33554432,mediaAccess:t.mediaAccess,mediaReadFile:t.mediaReadFile,mediaLocalRoots:t.mediaLocalRoots}),S=new FormData;S.set("sessionId",h),S.set("file",new Blob([N.buffer],{type:N.contentType||"application/octet-stream"}),N.fileName||"attachment"),y.push((await e(t.accountId,"/files",S,{cfg:t.cfg,signal:t.signal})).inputPart)}g={requestHash:p,payload:{sourceMessageId:`openclaw:${i}`,sessionId:h,content:d.text||"",inputParts:y,...r?{replyToMessageId:r}:{},...w?{agentRunId:w.runId}:{}}},o(i,g,"sends")}let A=await u(t.accountId,g,t);return w&&!w.messageIds.includes(A.messageId)&&w.messageIds.push(A.messageId),A})().finally(()=>l.delete(i));return l.set(i,{requestHash:p,promise:O}),O}return{send:T,message:{send:{text:T,media:T,payload:T},durableFinal:{capabilities:{text:!0,media:!0,payload:!0,replyTo:!0,reconcileUnknownSend:!0},reconcileUnknownSendKinds:{text:!0,media:!0,payload:!0},async reconcileUnknownSend(t){let h=n(m(t.accountId,t.queueId),"sends");if(!h)return{status:"not_sent"};if(h.payload.sessionId!==String(t.to).replace(/^orcha:/,""))throw D("channel_send_conflict");let w=await u(t.accountId,h,t);return{status:"sent",receipt:w.receipt,messageId:w.messageId}}}},actions:{describeMessageTool:()=>({actions:["send"],mediaSourceParams:{send:["media"]}}),supportsAction:({action:t})=>t==="send",isToolDeliveryAction:({args:t})=>t.action==="send",extractToolSend:({args:t})=>typeof(t.to||t.target)=="string"?{to:t.to||t.target,accountId:t.accountId}:null,async handleAction(t){if(t.action!=="send")throw D("unsupported_channel_action");let h=t.params;if(t.dryRun)return{content:[{type:"text",text:"Orcha send dry run"}],details:{dryRun:!0}};let w=c(t.sessionKey),r=await T({cfg:t.cfg,accountId:t.accountId,to:h.to||h.target,text:h.message,mediaUrl:h.media,replyToId:h.replyTo,deliveryQueueId:h.idempotencyKey,mediaAccess:t.mediaAccess,mediaLocalRoots:t.mediaLocalRoots,mediaReadFile:t.mediaReadFile,run:w,signal:w?.controller.signal});return{content:[{type:"text",text:JSON.stringify(r)}],details:r}}}}}var J="application/x-orcha-channel-events-v2+ndjson";function W(e,n){return Object.assign(new Error(n||(["run_timeout","RUNTIME_NODE_TIMEOUT"].includes(e)?"OpenClaw \u539F\u751F IM \u6267\u884C\u8D85\u65F6\uFF0C\u4EFB\u52A1\u5DF2\u7ED3\u675F\uFF0C\u8BF7\u91CD\u8BD5\u3002":e==="OPENCLAW_CHANNEL_PROTOCOL_MISMATCH"?"OpenClaw \u63D2\u4EF6\u4E0E Bridge \u7684\u539F\u751F IM \u534F\u8BAE\u4E0D\u517C\u5BB9\uFF0C\u8BF7\u914D\u5957\u5347\u7EA7\u3002":`OpenClaw \u539F\u751F IM \u6267\u884C\u5931\u8D25\uFF08${e}\uFF09`)),{code:e})}function X(e,n){let o=e.cause?.code||e.code;return n?.reason?.name==="TimeoutError"||e.name==="TimeoutError"||["UND_ERR_HEADERS_TIMEOUT","UND_ERR_BODY_TIMEOUT","UND_ERR_CONNECT_TIMEOUT","ETIMEDOUT"].includes(o)?Object.assign(W("RUNTIME_NODE_TIMEOUT","OpenClaw \u8FDE\u63A5\u6216\u7B49\u5F85\u54CD\u5E94\u8D85\u65F6\uFF0C\u4EFB\u52A1\u5DF2\u7ED3\u675F\uFF0C\u8BF7\u91CD\u8BD5\u3002"),{detail:{causeCode:o||"REQUEST_TIMEOUT"}}):e}var ge=e=>Me("sha256").update(JSON.stringify(e)).digest("hex"),R=(e,n=409)=>Object.assign(new Error(e),{status:n,code:e}),z=e=>typeof e=="string"&&/^[1-9]\d{0,18}$/.test(e),$e=e=>new Promise(n=>setTimeout(n,e));function he(e,n,o){return`agent:${e}:orcha:task:${ge([o,n.bindingId,n.bindingRevision,n.sessionId,n.taskContextId,n.memberId,n.historyFromSeq,n.runtimePolicyRevision])}`}function ye(e,n,o,f,c){let l=new Map,m=()=>e.config;function s(r,d=m()){let a=d?.channels?.orcha?.accounts?.[r];if(!z(r)||!a||a.enabled===!1||!a.serviceToken||!a.agentId)throw R("orcha_channel_account_unavailable",403);return{...a,accountId:r}}async function u(r,d,a,{cfg:i,signal:p,method:I="POST"}={}){let E=s(r,i),O=new URL(`/api/agent-channels${d}`,n.allowedUrl),g=a instanceof FormData,A=AbortSignal.any([AbortSignal.timeout(3e4),...p?[p]:[]]);try{let y=await fetch(O,{method:I,redirect:"error",signal:A,headers:{authorization:`Bearer ${E.serviceToken}`,...g?{}:{"content-type":"application/json"}},...a!==void 0?{body:g?a:JSON.stringify(a)}:{}}),L=await H(y.body);if(!y.ok||L.success!==!0)throw R(`orcha_channel_request_${y.status}`,y.status);return L.data}catch(y){throw X(y,A)}}function T(r,d="runs"){if(!/^[a-zA-Z0-9_-]{1,128}$/.test(r))throw R("invalid_record_identity",422);let a=me(e.runtime.state.resolveStateDir(),"plugins","orcha-runtime",d);return Le(a,{recursive:!0,mode:448}),me(a,`${r}.json`)}function P(r,d){try{return JSON.parse(Pe(T(r,d),"utf8"))}catch(a){if(a.code==="ENOENT")return null;throw a}}function x(r,d,a){let i=T(r,a),p=`${i}.pending`;ke(p,JSON.stringify(d),{mode:384}),xe(p,i)}async function t(r,d=!1,a){let i=s(r.delivery?.bindingId),{runId:p,delivery:I}=r;if(!z(p)||!z(I.sessionId)||I.taskContextId!==null&&!z(I.taskContextId)||I.mode!=="channel"||r.agentCode!==i.agentId)throw R("invalid_channel_delivery",422);if(!Array.isArray(r.prompt)||r.prompt.some(b=>!["text","image","file"].includes(b.type)||(b.type==="text"?typeof b.text!="string":typeof b.uri!="string")))throw R("invalid_channel_prompt",422);let E=ge({agentCode:r.agentCode,delivery:I,prompt:r.prompt}),O=P(p);if(O&&O.requestHash!==E)throw R("channel_delivery_conflict");if(O?.status==="completed")return O.result;if(O?.status==="failed")throw R(O.error);let g=l.get(p),A=O?.expiresAt||r.expiresAt,y=he(i.agentId,I,n.allowedUrl),L=()=>{let b=n.active(y);b?.runId===p&&n.release(y,b)},N=O?.hostRunId||`orcha-${p}`;if(O&&!g||r.attachOnly&&!g&&!O)throw R("RUNTIME_OPERATION_NOT_FOUND");if(g){try{await n.bind({runId:p,sessionKey:y,agentCode:i.agentId,expiresAt:A,mcpServer:r.mcpServer},{refresh:!0})}catch(b){if(l.get(p)!==g)return await g.promise;throw b}if(d)return{refreshed:!0};g.listeners.add(a);try{for(let b of g.updates)a?.(b);return await g.promise}finally{g.listeners.delete(a)}}if(d)throw R("RUNTIME_OPERATION_NOT_FOUND");let S=new AbortController,M={runId:p,delivery:I,messageIds:[],controller:S,promise:null,listeners:new Set(a?[a]:[]),updates:[],bytes:0},v=b=>{let k={type:"update",payload:{sessionId:y,update:b,_meta:{channelUpdateId:`${N}:${M.updates.length+1}`}}},U=Buffer.byteLength(JSON.stringify(k));if(U>2097152||M.bytes+U>16777216){S.abort(W("OPENCLAW_CHANNEL_PROGRESS_LIMIT"));return}M.bytes+=U,M.updates.push(k);for(let K of M.listeners)K?.(k)};return l.set(p,M),M.promise=(async()=>{await n.bind({runId:p,sessionKey:y,agentCode:i.agentId,expiresAt:A,mcpServer:r.mcpServer}),x(p,{requestHash:E,status:"running",sessionKey:y,hostRunId:N,expiresAt:A});let b=0,k,U,K=setTimeout(()=>S.abort(R("run_timeout")),Date.parse(A)-Date.now());try{let B=e.runtime.channel.reply;if(typeof B?.dispatchReplyWithBufferedBlockDispatcher!="function"||typeof e.runtime.events?.onAgentEvent!="function")throw R("OPENCLAW_CHANNEL_RUNTIME_UNAVAILABLE");U=e.runtime.events.onAgentEvent(_=>{let C=_.data;_.runId!==N||_.stream!=="tool"||!C?.toolCallId||C.hideFromChannelProgress||!["start","update","result"].includes(C.phase)||v({sessionUpdate:C.phase==="start"?"tool_call":"tool_call_update",toolCallId:C.toolCallId,title:C.name,status:C.phase==="result"?C.isError?"failed":"completed":"in_progress",...C.args?{rawInput:C.args}:{},...C.result!==void 0?{rawOutput:C.result}:{}})});let $="",te=_=>{if(_.requiresReasoningProgressOptIn||typeof _.text!="string")return;let C=_.text.startsWith($)?_.text.slice($.length):_.text;$=_.text,C&&v({sessionUpdate:"agent_thought_chunk",content:{type:"text",text:C}})},j=r.prompt.filter(_=>_.type==="text").map(_=>_.text).join(`
|
|
1
|
+
import{getPluginRuntimeGatewayRequestScope as Je}from"openclaw/plugin-sdk/plugin-runtime";import{loadOutboundMediaFromUrl as We}from"openclaw/plugin-sdk/outbound-media";function Se(e){return String(e??"").trim()}var G="[redacted]",Ne=/(authorization|api[_-]?key|credential|password|secret|token)/i;function ie(e){return Ne.test(Se(e))}function ce(e,n=[]){let o=String(e??"");for(let f of n){let c=String(f??"");c.length>=4&&(o=o.split(c).join(G))}return o.replace(/((?:authorization|api[_-]?key|credential|password|secret|token)\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi,`$1${G}`).replace(/([?&](?:api[_-]?key|credential|password|secret|token)=)[^&\s]+/gi,`$1${G}`)}var W={controlTools:["find_capabilities_and_skills","describe_capability","inspect_result","execute_capability"],residentTools:["search_orcha_docs","knowledge_search"]};var Q=Object.freeze([...W.controlTools,...W.residentTools]),Ce=new Set(Q),Re=8*1024*1024;function de(e,n){if(!Array.isArray(e)||e.length!==1)throw new Error("one task-scoped Orcha MCP server is required");let o=e[0],f=new URL(o.url);if(o.name!=="orcha"||o.type!=="http"||!["http:","https:"].includes(f.protocol)||f.username||f.password||f.hash||n&&f.href!==new URL(n).href)throw new Error("Orcha MCP server does not match the trusted binding");let c=Object.fromEntries((o.headers||[]).map(({name:l,value:m})=>[l.toLowerCase(),m]));if(!String(c.authorization||"").startsWith("Bearer "))throw new Error("Orcha MCP task credentials are missing");return{url:f.href,headers:{...c,"content-type":"application/json",accept:"application/json"}}}async function v(e){let n=0,o=[];for await(let f of e){if(n+=f.length,n>Re)throw new Error("MCP payload exceeds the size limit");o.push(Buffer.from(f))}return JSON.parse(Buffer.concat(o).toString("utf8"))}async function Z(e,n,o){let f=await fetch(e.url,{method:"POST",headers:e.headers,body:JSON.stringify(n),redirect:"error",signal:AbortSignal.any([AbortSignal.timeout(36e5),...o?[o]:[]])});if(!f.ok)throw new Error(`Orcha MCP request failed (${f.status})`);if(f.status===202||f.status===204)return null;let c=await v(f.body);if(!c||c.jsonrpc!=="2.0"||c.id!==n.id)throw new Error("Orcha MCP response identity mismatch");return c}async function le(e,n){let o=await Z(e,{jsonrpc:"2.0",id:"orcha-bind-tools",method:"tools/list"},n),f=o?.result?.tools;if(o?.error){let c=Object.entries(e.headers).filter(([i])=>ie(i)).flatMap(([,i])=>[i,String(i).replace(/^Bearer\s+/i,"")]),l=typeof o.error.message=="string"?ce(o.error.message,c).slice(0,512):"",m=Number.isInteger(o.error.code)?` (${o.error.code})`:"";throw new Error(`Orcha MCP tool discovery failed${m}${l?`: ${l}`:""}`)}return Me(f)}function Me(e){if(!Array.isArray(e)||e.some(n=>!n||!Ce.has(n.name)||n.inputSchema?.type!=="object"||typeof n.description!="string")||new Set(e.map(n=>n.name)).size!==e.length||e.length&&W.controlTools.some(n=>!e.some(o=>o.name===n)))throw new Error("Orcha MCP returned an unsupported or invalid authorized tool catalog");return e}function ue(e,n){let o=c=>Array.isArray(c)?c.map(o):c&&typeof c=="object"?Object.fromEntries(Object.keys(c).sort().map(l=>[l,o(c[l])])):c,f=c=>c.map(({name:l,inputSchema:m})=>({name:l,inputSchema:m})).sort((l,m)=>l.name.localeCompare(m.name));return JSON.stringify(o(f(e)))===JSON.stringify(o(f(n)))}var q=e=>Object.assign(new Error(e),{status:409});function pe(e){let n=new Map,o=e.pluginConfig?.allowedOrchaMcpUrl;if(!o)throw new Error("Orcha plugin requires allowedOrchaMcpUrl");function f(m,i=n.get(m)){!i||n.get(m)!==i||(i.lifetime.abort(),clearTimeout(i.timer),n.delete(m))}function c(m){let i=n.get(m);return i&&i.deadline<=Date.now()?(f(m,i),null):i}async function l(m,{refresh:i=!1}={}){let{runId:u,sessionKey:O,agentCode:L,expiresAt:x,mcpServer:t}=m,h=Date.parse(x);if(typeof u!="string"||!u||typeof O!="string"||!O||typeof L!="string"||!L||!O.startsWith(`agent:${L}:`)||!Number.isFinite(h)||h<=Date.now()||h>Date.now()+24*36e5)throw new Error("invalid_binding_scope");let I=c(O);if(i&&!I||I&&(I.runId!==u||I.agentCode!==L||I.deadline!==h))throw q("original_binding_unavailable_or_changed");let r=de([t],o),d=I?.lifetime||new AbortController,a=await le(r,d.signal);if(c(O)!==I)throw q("binding_changed");if(I){if(!ue(I.tools,a))throw q("frozen_tool_catalog_changed");I.upstream=r}else{let s={runId:u,agentCode:L,deadline:h,upstream:r,tools:a,lifetime:d};s.timer=setTimeout(()=>f(O,s),h-Date.now()),s.timer.unref?.(),n.set(O,s)}}return e.registerTool(m=>{let i=c(m.sessionKey);return!i||m.agentId!==i.agentCode?null:i.tools.map(u=>({name:`orcha_${u.name}`,label:`Orcha ${u.name}`,description:u.description,parameters:u.inputSchema,async execute(O,L,x){if(c(m.sessionKey)!==i||m.agentId!==i.agentCode||!O||!i.tools.some(h=>h.name===u.name))throw new Error("Orcha task binding expired or changed");let t=await Z(i.upstream,{jsonrpc:"2.0",id:O,method:"tools/call",params:{name:u.name,arguments:L}},AbortSignal.any([i.lifetime.signal,...x?[x]:[]]));if(t?.error||!t?.result)throw new Error("Orcha rejected this task-scoped tool call");return{content:t.result.content,details:t.result.structuredContent,isError:t.result.isError===!0}}}))},{names:Q.map(m=>`orcha_${m}`)}),{bind:l,active:c,release:f,allowedUrl:o}}import{createHash as xe}from"node:crypto";import{mkdirSync as ke,readFileSync as Ue,renameSync as $e,writeFileSync as He}from"node:fs";import{join as ge}from"node:path";import{createHash as Pe}from"node:crypto";var fe=e=>Pe("sha256").update(JSON.stringify(e)).digest("hex"),B=e=>Object.assign(new Error(e),{code:e}),X=e=>{let n=String(e||"").replace(/^orcha:/,"");return/^[1-9]\d{0,18}$/.test(n)?n:void 0},me={normalizeTarget:X,targetResolver:{looksLikeId:e=>!!X(e),hint:"Orcha session ID"}};function he({core:e,load:n,save:o,loadMedia:f,activeRun:c}){let l=new Map,m=(t,h)=>fe([t,h]),i=t=>({channel:"orcha",messageId:t.messageId,chatId:t.sessionId,receipt:{primaryPlatformMessageId:t.messageId,platformMessageIds:[t.messageId],parts:[{platformMessageId:t.messageId,kind:"unknown",index:0}],sentAt:Date.now()}});async function u(t,h,I={}){return await I.onPlatformSendDispatch?.(),I.signal?.throwIfAborted(),i(await e(t,"/messages",h.payload,{cfg:I.cfg,signal:I.signal}))}async function O(t){t.signal?.throwIfAborted();let h=X(t.to);if(!h)throw B("invalid_conversation");if(!t.deliveryQueueId)throw B("OPENCLAW_DURABLE_DELIVERY_ID_REQUIRED");let I=t.run&&t.run.delivery.bindingId===t.accountId&&t.run.delivery.sessionId===h?t.run:null,r=t.replyToId||I?.delivery.messageId||null,d=t.payload||{text:t.text||"",mediaUrl:t.mediaUrl},a=d.mediaUrls||(d.mediaUrl?[d.mediaUrl]:[]),s=m(t.accountId,t.deliveryQueueId),p=fe([h,d.text||"",a,r,I?.runId||null]),_=n(s,"sends");if(_&&_.requestHash!==p)throw B("channel_send_conflict");let E=l.get(s);if(E){if(E.requestHash!==p)throw B("channel_send_conflict");return E.promise}let T=(async()=>{let y=_;if(!y){let w=[];for(let P of a){let b=await f(P,{maxBytes:33554432,mediaAccess:t.mediaAccess,mediaReadFile:t.mediaReadFile,mediaLocalRoots:t.mediaLocalRoots}),N=new FormData;N.set("sessionId",h),N.set("file",new Blob([b.buffer],{type:b.contentType||"application/octet-stream"}),b.fileName||"attachment"),w.push((await e(t.accountId,"/files",N,{cfg:t.cfg,signal:t.signal})).inputPart)}y={requestHash:p,payload:{sourceMessageId:`openclaw:${s}`,sessionId:h,content:d.text||"",inputParts:w,...r?{replyToMessageId:r}:{},...I?{agentRunId:I.runId}:{}}},o(s,y,"sends")}let S=await u(t.accountId,y,t);return I&&!I.messageIds.includes(S.messageId)&&I.messageIds.push(S.messageId),S})().finally(()=>l.delete(s));return l.set(s,{requestHash:p,promise:T}),T}return{send:O,message:{send:{text:O,media:O,payload:O},durableFinal:{capabilities:{text:!0,media:!0,payload:!0,replyTo:!0,reconcileUnknownSend:!0},reconcileUnknownSendKinds:{text:!0,media:!0,payload:!0},async reconcileUnknownSend(t){let h=n(m(t.accountId,t.queueId),"sends");if(!h)return{status:"not_sent"};if(h.payload.sessionId!==String(t.to).replace(/^orcha:/,""))throw B("channel_send_conflict");let I=await u(t.accountId,h,t);return{status:"sent",receipt:I.receipt,messageId:I.messageId}}}},actions:{describeMessageTool:()=>({actions:["send"],mediaSourceParams:{send:["media"]}}),supportsAction:({action:t})=>t==="send",isToolDeliveryAction:({args:t})=>t.action==="send",extractToolSend:({args:t})=>typeof(t.to||t.target)=="string"?{to:t.to||t.target,accountId:t.accountId}:null,async handleAction(t){if(t.action!=="send")throw B("unsupported_channel_action");let h=t.params;if(t.dryRun)return{content:[{type:"text",text:"Orcha send dry run"}],details:{dryRun:!0}};let I=c(t.sessionKey),r=await O({cfg:t.cfg,accountId:t.accountId,to:h.to||h.target,text:h.message,mediaUrl:h.media,replyToId:h.replyTo,deliveryQueueId:h.idempotencyKey,mediaAccess:t.mediaAccess,mediaLocalRoots:t.mediaLocalRoots,mediaReadFile:t.mediaReadFile,run:I,signal:I?.controller.signal});return{content:[{type:"text",text:JSON.stringify(r)}],details:r}}}}}var z="application/x-orcha-channel-events-v2+ndjson";function K(e,n){return Object.assign(new Error(n||(["run_timeout","RUNTIME_NODE_TIMEOUT"].includes(e)?"OpenClaw \u539F\u751F IM \u6267\u884C\u8D85\u65F6\uFF0C\u4EFB\u52A1\u5DF2\u7ED3\u675F\uFF0C\u8BF7\u91CD\u8BD5\u3002":e==="OPENCLAW_CHANNEL_PROTOCOL_MISMATCH"?"OpenClaw \u63D2\u4EF6\u4E0E Bridge \u7684\u539F\u751F IM \u534F\u8BAE\u4E0D\u517C\u5BB9\uFF0C\u8BF7\u914D\u5957\u5347\u7EA7\u3002":`OpenClaw \u539F\u751F IM \u6267\u884C\u5931\u8D25\uFF08${e}\uFF09`)),{code:e})}function ee(e,n){let o=e.cause?.code||e.code;return n?.reason?.name==="TimeoutError"||e.name==="TimeoutError"||["UND_ERR_HEADERS_TIMEOUT","UND_ERR_BODY_TIMEOUT","UND_ERR_CONNECT_TIMEOUT","ETIMEDOUT"].includes(o)?Object.assign(K("RUNTIME_NODE_TIMEOUT","OpenClaw \u8FDE\u63A5\u6216\u7B49\u5F85\u54CD\u5E94\u8D85\u65F6\uFF0C\u4EFB\u52A1\u5DF2\u7ED3\u675F\uFF0C\u8BF7\u91CD\u8BD5\u3002"),{detail:{causeCode:o||"REQUEST_TIMEOUT"}}):e}var we=e=>xe("sha256").update(JSON.stringify(e)).digest("hex"),R=(e,n=409)=>Object.assign(new Error(e),{status:n,code:e}),Y=e=>typeof e=="string"&&/^[1-9]\d{0,18}$/.test(e),De=e=>new Promise(n=>setTimeout(n,e));function ye(e,n,o){return`agent:${e}:orcha:task:${we([o,n.bindingId,n.bindingRevision,n.sessionId,n.taskContextId,n.memberId,n.historyFromSeq,n.runtimePolicyRevision])}`}function Ie(e,n,o,f,c){let l=new Map,m=()=>e.config;function i(r,d=m()){let a=d?.channels?.orcha?.accounts?.[r];if(!Y(r)||!a||a.enabled===!1||!a.serviceToken||!a.agentId)throw R("orcha_channel_account_unavailable",403);return{...a,accountId:r}}async function u(r,d,a,{cfg:s,signal:p,method:_="POST"}={}){let E=i(r,s),T=new URL(`/api/agent-channels${d}`,n.allowedUrl),y=a instanceof FormData,S=AbortSignal.any([AbortSignal.timeout(3e4),...p?[p]:[]]);try{let w=await fetch(T,{method:_,redirect:"error",signal:S,headers:{authorization:`Bearer ${E.serviceToken}`,...y?{}:{"content-type":"application/json"}},...a!==void 0?{body:y?a:JSON.stringify(a)}:{}}),P=await v(w.body);if(!w.ok||P.success!==!0)throw R(`orcha_channel_request_${w.status}`,w.status);return P.data}catch(w){throw ee(w,S)}}function O(r,d="runs"){if(!/^[a-zA-Z0-9_-]{1,128}$/.test(r))throw R("invalid_record_identity",422);let a=ge(e.runtime.state.resolveStateDir(),"plugins","orcha-runtime",d);return ke(a,{recursive:!0,mode:448}),ge(a,`${r}.json`)}function L(r,d){try{return JSON.parse(Ue(O(r,d),"utf8"))}catch(a){if(a.code==="ENOENT")return null;throw a}}function x(r,d,a){let s=O(r,a),p=`${s}.pending`;He(p,JSON.stringify(d),{mode:384}),$e(p,s)}async function t(r,d=!1,a){let s=i(r.delivery?.bindingId),{runId:p,delivery:_}=r;if(!Y(p)||!Y(_.sessionId)||_.taskContextId!==null&&!Y(_.taskContextId)||_.mode!=="channel"||r.agentCode!==s.agentId)throw R("invalid_channel_delivery",422);if(!Array.isArray(r.prompt)||r.prompt.some(C=>!["text","image","file"].includes(C.type)||(C.type==="text"?typeof C.text!="string":typeof C.uri!="string")))throw R("invalid_channel_prompt",422);let E=we({agentCode:r.agentCode,delivery:_,prompt:r.prompt}),T=L(p);if(T&&T.requestHash!==E)throw R("channel_delivery_conflict");if(T?.status==="completed")return T.result;if(T?.status==="failed")throw R(T.error);let y=l.get(p),S=T?.expiresAt||r.expiresAt,w=ye(s.agentId,_,n.allowedUrl),P=()=>{let C=n.active(w);C?.runId===p&&n.release(w,C)},b=T?.hostRunId||`orcha-${p}`;if(T&&!y||r.attachOnly&&!y&&!T)throw R("RUNTIME_OPERATION_NOT_FOUND");if(y){try{await n.bind({runId:p,sessionKey:w,agentCode:s.agentId,expiresAt:S,mcpServer:r.mcpServer},{refresh:!0})}catch(C){if(l.get(p)!==y)return await y.promise;throw C}if(d)return{refreshed:!0};y.listeners.add(a);try{for(let C of y.updates)a?.(C);return await y.promise}finally{y.listeners.delete(a)}}if(d)throw R("RUNTIME_OPERATION_NOT_FOUND");let N=new AbortController,M={runId:p,delivery:_,messageIds:[],controller:N,promise:null,listeners:new Set(a?[a]:[]),updates:[],bytes:0},$=C=>{let k={type:"update",payload:{sessionId:w,update:C,_meta:{channelUpdateId:`${b}:${M.updates.length+1}`}}},H=Buffer.byteLength(JSON.stringify(k));if(H>2097152||M.bytes+H>16777216){N.abort(K("OPENCLAW_CHANNEL_PROGRESS_LIMIT"));return}M.bytes+=H,M.updates.push(k);for(let V of M.listeners)V?.(k)};return l.set(p,M),M.promise=(async()=>{await n.bind({runId:p,sessionKey:w,agentCode:s.agentId,expiresAt:S,mcpServer:r.mcpServer}),x(p,{requestHash:E,status:"running",sessionKey:w,hostRunId:b,expiresAt:S});let C=0,k,H,V=setTimeout(()=>N.abort(R("run_timeout")),Date.parse(S)-Date.now());try{let F=e.runtime.channel.reply;if(typeof F?.dispatchReplyWithBufferedBlockDispatcher!="function"||typeof e.runtime.events?.onAgentEvent!="function")throw R("OPENCLAW_CHANNEL_RUNTIME_UNAVAILABLE");H=e.runtime.events.onAgentEvent(g=>{let A=g.data;g.runId!==b||g.stream!=="tool"||!A?.toolCallId||A.hideFromChannelProgress||!["start","update","result"].includes(A.phase)||$({sessionUpdate:A.phase==="start"?"tool_call":"tool_call_update",toolCallId:A.toolCallId,title:A.name,status:A.phase==="result"?A.isError?"failed":"completed":"in_progress",...A.args?{rawInput:A.args}:{},...A.result!==void 0?{rawOutput:A.result}:{}})});let U="",ne="",j="",Te=g=>{if(typeof g.text!="string")return;let A=[ne,g.text].filter(Boolean).join(`
|
|
2
2
|
|
|
3
|
-
`)
|
|
4
|
-
|
|
5
|
-
`)
|
|
3
|
+
`);if(A===j)return;let D=A.startsWith(j);$({sessionUpdate:D?"agent_message_chunk":"agent_message_snapshot",content:{type:"text",text:D?A.slice(j.length):A}}),j=A},re=g=>{if(g.requiresReasoningProgressOptIn||typeof g.text!="string")return;let A=g.text.startsWith(U)?g.text.slice(U.length):g.text;U=g.text,A&&$({sessionUpdate:"agent_thought_chunk",content:{type:"text",text:A}})},J=r.prompt.filter(g=>g.type==="text").map(g=>g.text).join(`
|
|
4
|
+
|
|
5
|
+
`),oe=r.prompt.filter(g=>g.type!=="text"),Oe={Body:J,BodyForAgent:J,BodyForCommands:J,RawBody:J,From:`orcha:${_.memberId}`,To:`orcha:${_.sessionId}`,SessionKey:w,AccountId:_.bindingId,Provider:"orcha",Surface:"orcha",ChatType:"direct",OriginatingChannel:"orcha",OriginatingTo:_.sessionId,MessageSid:_.messageId||p,MediaUrls:oe.map(g=>g.uri),MediaTypes:oe.map(g=>g.mimeType||"application/octet-stream"),CommandAuthorized:!1,WasMentioned:!0};if(await F.dispatchReplyWithBufferedBlockDispatcher({ctx:Oe,cfg:m(),replyOptions:{runId:b,abortSignal:N.signal,suppressTyping:!0,suppressDefaultToolProgressMessages:!0,suppressToolErrorWarnings:!0,disableBlockStreaming:!0,reasoningPayloadsEnabled:!0,commentaryPayloadsEnabled:!0,onAssistantMessageStart:()=>{U="",ne=j},onPartialReply:Te,onReasoningStream:re},dispatcherOptions:{deliver:async(g,A)=>{if(N.signal.throwIfAborted(),g.isReasoning&&re(g),g.isCommentary&&g.text&&$({sessionUpdate:"agent_thought_chunk",content:{type:"text",text:g.text}}),A.kind==="final"&&f(g)){let D=m(),se=D?.agents?.list?.find(Ae=>Ae.id===s.agentId)?.workspace||D?.agents?.defaults?.workspace;await h.send({cfg:D,accountId:_.bindingId,to:_.sessionId,payload:g,run:M,deliveryQueueId:`${b}:final:${++C}`,signal:N.signal,mediaLocalRoots:se?[se]:void 0})}},onError:g=>{k??=g}}}),k)throw k;if(N.signal.throwIfAborted(),!M.messageIds.length)throw R("runtime_node_empty_result");let ae={stopReason:"end_turn",messageIds:M.messageIds,sessionId:w};return x(p,{requestHash:E,status:"completed",sessionKey:w,hostRunId:b,expiresAt:S,result:ae}),ae}catch(F){let U=N.signal.aborted?N.signal.reason:ee(F,N.signal);throw x(p,{requestHash:E,status:"failed",sessionKey:w,hostRunId:b,expiresAt:S,error:U.code||"channel_host_failed"}),U}finally{H?.(),clearTimeout(V),P(),l.delete(p)}})().catch(C=>{throw P(),l.delete(p),C}),M.promise}let h=he({core:u,load:L,save:x,loadMedia:c,activeRun:r=>l.get(n.active(r)?.runId)});e.registerChannel({plugin:{id:"orcha",meta:{id:"orcha",label:"Orcha",selectionLabel:"Orcha",docsPath:"/channels/orcha",blurb:"Enterprise people and Agent conversations"},capabilities:{chatTypes:["direct","group"],media:!0},config:{listAccountIds:r=>Object.keys(r?.channels?.orcha?.accounts||{}),inspectAccount:(r,d)=>{let a=r?.channels?.orcha?.accounts?.[d];return{enabled:!!(a&&a.enabled!==!1),configured:!!(a?.serviceToken&&a?.agentId),tokenStatus:a?.serviceToken?"available":"missing"}},resolveAccount:(r,d)=>i(d,r),isEnabled:r=>r.enabled!==!1,isConfigured:r=>!!(r.serviceToken&&r.agentId)},outbound:{deliveryMode:"direct",textChunkLimit:1e5},message:h.message,actions:h.actions,messaging:me}});async function I(r){let{accountId:d,grantId:a,externalInvocationId:s}=r;i(d);let p=await u(d,"/runs",{grantId:a,externalInvocationId:s,input:r.input||{}}),_=p.agentRunId;if(["completed","failed","cancelled","expired"].includes(p.status))return p;let E;do E=await u(d,`/runs/${_}/claim`,{}),E.attemptId||await De(Math.max(1,E.retryAfterSeconds||2)*1e3);while(!E.attemptId);let T=ye(i(d).agentId,E.delivery,n.allowedUrl),y,S=!1,w=setInterval(async()=>{if(!S){S=!0;try{let N=await u(d,`/runs/${_}/heartbeat`,{attemptId:E.attemptId});n.active(T)&&await n.bind({runId:_,sessionKey:T,agentCode:i(d).agentId,expiresAt:E.expiresAt,mcpServer:N.mcpServer},{refresh:!0})}catch(N){y=N,l.get(_)?.controller.abort(N),n.release(T)}finally{S=!1}}},Math.max(1,E.heartbeatIntervalSeconds)*1e3),P,b;try{let N;try{N=await t({runId:_,agentCode:i(d).agentId,delivery:E.delivery,prompt:[{type:"text",text:JSON.stringify({task:E.prompt,eventInput:E.input})}],mcpServer:E.mcpServer,expiresAt:E.expiresAt,attachOnly:E.recovery})}catch($){b=$}if(y)throw y;P=b?{attemptId:E.attemptId,errorMessage:K(b.code||"channel_host_failed").message,status:"failed"}:{attemptId:E.attemptId,messageIds:N.messageIds,status:"completed"};let M=await u(d,`/runs/${_}/complete`,P);if(b)throw b;return M}finally{clearInterval(w),n.release(T)}}for(let[r,d]of[["deliveries",t],["background",I]])e.registerHttpRoute({path:`/plugins/orcha-runtime/${r}`,auth:"gateway",match:"exact",async handler(a,s){let p,_=Promise.resolve(),E=0,T=y=>{if(s.destroyed||s.writableEnded)return;let S=JSON.stringify(y)+`
|
|
6
|
+
`,w=Buffer.byteLength(S);if(w>2097152||E+s.writableLength+w>2097152){s.destroy();return}return E+=w,_=_.then(async()=>{s.destroyed||s.writableEnded||(await o(),s.write(S))}).catch(P=>{!s.destroyed&&!s.writableEnded&&s.end(JSON.stringify({type:"error",code:P.code||"OPENCLAW_CHANNEL_AUTH_FAILED"})+`
|
|
7
|
+
`)}).finally(()=>{E-=w}),_};try{if(r==="deliveries"&&a.method==="GET"){await o(),s.writeHead(200,{"content-type":"application/json"}).end(JSON.stringify({streamType:z}));return}if(!["POST","PATCH","DELETE"].includes(a.method))throw R("method_not_allowed",405);let y=await v(a);if(await o(),r==="deliveries"&&a.method==="POST"){if(a.headers.accept!==z)throw R("OPENCLAW_CHANNEL_PROTOCOL_MISMATCH",409);s.writeHead(200,{"content-type":z,"cache-control":"no-store","x-accel-buffering":"no"}),T({type:"heartbeat"}),p=setInterval(()=>T({type:"heartbeat"}),15e3),s.once("close",()=>clearInterval(p));let w=await t(y,!1,T);await o(),await T({type:"result",result:w}),s.end();return}if(a.method==="DELETE"){let w=l.get(y.runId);w?.controller.abort(R("RUNTIME_OPERATION_CANCELLED")),s.writeHead(200,{"content-type":"application/json"}).end(JSON.stringify({cancelled:!!w}));return}if(a.method==="PATCH"&&r!=="deliveries")throw R("method_not_allowed",405);let S=await d(y,a.method==="PATCH");await o(),s.writeHead(200,{"content-type":"application/json"}).end(JSON.stringify(S))}catch(y){s.headersSent?(await T({type:"error",code:y.code||"channel_host_failed"}),s.end()):s.writableEnded||s.writeHead(y.status||500,{"content-type":"application/json"}).end(JSON.stringify({error:y.code||y.message}))}finally{clearInterval(p)}}})}function _e(e,n,o){return{id:"orcha-runtime",name:"Orcha Channel and authorized tools",register(f){let c=pe(f);f.registerHttpRoute({path:"/plugins/orcha-runtime/mcp-bindings",auth:"gateway",match:"exact",async handler(l,m){let i=(u,O)=>{m.writeHead(u,{"content-type":"application/json"}),m.end(JSON.stringify(O))};try{if(l.method==="GET"){i(200,{ready:!0});return}if(!["PUT","DELETE"].includes(l.method)){i(405,{error:"method_not_allowed"});return}let u=await v(l);if(await e(),typeof u.runId!="string"||!u.runId||typeof u.sessionKey!="string"||!u.sessionKey){i(422,{error:"invalid_binding_identity"});return}let O=c.active(u.sessionKey);if(l.headers["if-match"]&&l.headers["if-match"]!==u.runId){i(409,{error:"binding_run_conflict"});return}if(O&&O.runId!==u.runId){i(409,{error:"session_already_bound"});return}l.method==="DELETE"?c.release(u.sessionKey):await c.bind(u,{refresh:!!l.headers["if-match"]}),i(200,{[l.method==="DELETE"?"released":"bound"]:!0})}catch(u){m.writableEnded||i(u.status||422,{error:u.message})}}}),Ie(f,c,e,n,o)}}}import{isFastModeAutoProgressPayload as ve,isReasoningReplyPayload as Be,isReplyPayloadNonTerminalToolErrorWarning as je,isReplyPayloadTtsSupplement as Fe}from"openclaw/plugin-sdk/reply-payload";function Ee(e){return!Be(e)&&!e.isCommentary&&!e.isCompactionNotice&&!e.isFallbackNotice&&!e.isStatusNotice&&!ve(e)&&!je(e)&&!Fe(e)}var It=_e(async()=>{await Je()?.revalidate?.()},Ee,We);export{It as default};
|