tnyma-bridge 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/bundle/feishu-bot-creator/README.md +66 -0
- package/bundle/feishu-bot-creator/assets/tnyma-ai-avatar.png +0 -0
- package/bundle/feishu-bot-creator/feishu_bot_creator.py +2063 -0
- package/bundle/index.js +214 -0
- package/bundle/postinstall.js +5 -0
- package/package.json +50 -0
package/bundle/index.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{realpathSync as rs}from"fs";import{fileURLToPath as Hc}from"url";function Rn(){return{path:null,hash:null,valid:!0,lastLoadedAt:null,snapshot:null}}function kn(e){return e.trim().replace(/\[(\d+)\]/g,".$1").replace(/^\/+|\/+$/g,"").replace(/\//g,".").replace(/\.+/g,".")}function Pn(e,t=""){let n=[];for(let[r,o]of Object.entries(e)){let i=t?`${t}.${r}`:r;n.push(i),o&&typeof o=="object"&&!Array.isArray(o)&&n.push(...Pn(o,i))}return n}function An(e){let t=e.map(kn).filter(Boolean);return{allowedRoots:t,allows(n){let r=kn(n);return t.some(o=>r===o||r.startsWith(`${o}.`))}}}function Ve(e,t){return Pn(e).filter(n=>!t.allows(n))}function vn(e){return{meta:{schemaVersion:"1.0.0",collectedAt:e.now??new Date().toISOString(),sourceVersion:null},source:{upstreamKind:e.upstreamKind,nodeId:e.nodeId,hostName:e.hostName??null,machineId:e.machineId??null},bridge:{bridgeId:e.bridgeId,status:"offline",relayEnabled:!1,relayConnected:!1},pairing:{serverUrl:null,bridgeId:e.bridgeId,pairCode:null,pairCodeExpiresAt:null,accessCode:null,accessCodeExpiresAt:null,displayName:null,region:null,relayUrl:null,qrPayload:null,methods:[],protocolVersion:2,supportsBootstrap:!0},config:Rn(),capabilities:{},health:{lastSuccessAt:null,lastError:null},data:{agents:{},models:{},channels:{},sessions:{}}}}function ss(e){return e.trim().replace(/\[(\d+)\]/g,".$1").replace(/\//g,".").split(".").map(t=>t.trim()).filter(Boolean)}function se(e,t){let n=e;for(let r of ss(t)){if(!n||typeof n!="object")return;n=n[r]}return n}function k(e,t){return{ok:!0,data:e,meta:t}}function En(e){let t={v:2,k:"cp",s:e.serverUrl.trim(),g:"_",a:e.pairCode.trim(),rb:1,pv:2,sb:!0};return JSON.stringify(t)}function In(e){let t=e.pairCode.trim(),n=e.pairCodeExpiresAt.trim(),r=En({serverUrl:e.serverUrl,bridgeId:e.bridgeId,pairCode:t,displayName:e.displayName});return[{kind:"code",pairCode:t,expiresAt:n,charset:"A-Z0-9",length:6},{kind:"qr",payload:r,encoding:"compact-v2",pairCode:t,expiresAt:n}]}function Ke(e){return{bridgeId:e.bridge.bridgeId,nodeId:e.source.nodeId,upstreamKind:e.source.upstreamKind,machineId:e.source.machineId??null,hostName:e.source.hostName??null,status:e.bridge.status,relayEnabled:e.bridge.relayEnabled,relayConnected:e.bridge.relayConnected,collectedAt:e.meta.collectedAt}}function xn(e){return{path:e.config.path,hash:e.config.hash,valid:e.config.valid,lastLoadedAt:e.config.lastLoadedAt,snapshot:e.config.snapshot}}function ze(e){return{serverUrl:e.pairing.serverUrl??null,bridgeId:e.pairing.bridgeId??e.bridge.bridgeId,pairCode:e.pairing.pairCode??e.pairing.accessCode??null,pairCodeExpiresAt:e.pairing.pairCodeExpiresAt??e.pairing.accessCodeExpiresAt??null,accessCode:e.pairing.accessCode??null,accessCodeExpiresAt:e.pairing.accessCodeExpiresAt??null,displayName:e.pairing.displayName??null,region:e.pairing.region??null,relayUrl:e.pairing.relayUrl??null,qrPayload:e.pairing.qrPayload??null,methods:e.pairing.methods??[],protocolVersion:e.pairing.protocolVersion??2,supportsBootstrap:e.pairing.supportsBootstrap??!0}}function ae(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function Pe(e){return Array.isArray(e)?e:[]}function X(e){return typeof e=="string"&&e.trim()?e.trim():null}function ls(e){return typeof e=="boolean"?e:null}function cs(e){let t=ae(e);return t?Object.keys(t).length:0}function ds(e,t){let n=ae(e);if(!n)return 0;let r=0;for(let[o,i]of Object.entries(n)){if(o==="defaults")continue;let s=ae(i);if(!s)continue;let a=!1;(X(s.defaultTo)===t||X(s.agentId)===t)&&(a=!0);let l=Pe(s.bindings);a||(a=l.some(c=>{let d=ae(c);return d?X(d.agentId)===t:!1})),a&&(r+=1)}return r}function us(e,t){return Pe(e).filter(n=>{let r=ae(n);return r?X(r.agentId)===t:!1}).length}function gs(e,t){return e.bridge.status!=="online"?"offline":Object.values(e.data.sessions).some(r=>r.status!=="active"?!1:r.agentId===t?!0:r.id.startsWith(`agent:${t}:`))?"working":e.health.lastSuccessAt?"active":"unknown"}function ps(e){let t=Object.values(e.data.agents).find(n=>n.isDefault);return t?{id:t.id,name:t.name}:null}function Tn(e){let t=e.config.snapshot??null,n=Pe(se(t,"agents.list")),r=Pe(se(t,"agents.defaults.skills")),o=X(se(t,"agents.defaults.workspace")),i=X(se(t,"agents.defaults.model.primary")),s=se(t,"channels"),a=se(t,"bindings"),l=ps(e),c=new Map;for(let d of n){let p=ae(d),h=X(p?.id);p&&h&&c.set(h,p)}return Object.values(e.data.agents).map(d=>{let p=c.get(d.id),h=X(p?.name),m=X(p?.workspace)??o,f=X(ae(p?.model)?.primary)??d.modelId??i,w=Pe(p?.skills),S=ae(p?.tools),_=Pe(ae(p?.subagents)?.allowAgents).map(G=>X(G)).filter(G=>!!G),D=d.isDefault??ls(p?.default)??!1;return{id:d.id,name:h??d.name,kind:_.length>0?"team":"agent",masterName:!D&&l&&l.id!==d.id?l.name:null,status:gs(e,d.id),primaryModel:f,workspace:m,skillsCount:w.length>0?w.length:r.length,channelCount:ds(s,d.id)+us(a,d.id),toolCount:cs(S),teamMemberCount:_.length,teamMemberIds:_,isDefault:D}}).sort((d,p)=>d.id.localeCompare(p.id))}function Ut(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function Dn(e){return Array.isArray(e)?e:[]}function Ae(e){return typeof e=="string"&&e.trim()?e.trim():null}function ms(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function fs(e){return typeof e=="boolean"?e:null}function On(e){return Dn(e).map(t=>Ae(t)).filter(t=>!!t)}function _n(e){let t=e.config.snapshot??null,n=Ut(se(t,"models.providers")),r=new Map,o=new Map;for(let i of Object.values(e.data.models)){let s=`${i.provider}:${i.id}`;o.set(s,{id:i.id,provider:i.provider,name:i.name??i.id,contextWindow:i.contextWindow??null,supportsReasoning:i.supportsReasoning,inputModes:i.inputModes,configured:!1,available:!0});let a=r.get(i.provider)??{id:i.provider,baseUrl:null,api:null,auth:null,configuredModelCount:0,runtimeModelCount:0};a.runtimeModelCount+=1,r.set(i.provider,a)}for(let[i,s]of Object.entries(n??{})){let a=Ut(s)??{},l=r.get(i)??{id:i,baseUrl:null,api:null,auth:null,configuredModelCount:0,runtimeModelCount:0};l.baseUrl=Ae(a.baseUrl),l.api=Ae(a.api),l.auth=Ae(a.auth);let c=Dn(a.models);l.configuredModelCount=c.length,r.set(i,l);for(let d of c){let p=Ut(d),h=Ae(p?.id);if(!h)continue;let m=`${i}:${h}`,f=o.get(m);o.set(m,{id:h,provider:i,name:Ae(p?.name)??f?.name??h,contextWindow:ms(p?.contextWindow)??f?.contextWindow??null,supportsReasoning:fs(p?.reasoning)??f?.supportsReasoning??void 0,inputModes:On(p?.input).length?On(p?.input):f?.inputModes,configured:!0,available:f?.available??!1})}}return{providers:[...r.values()].sort((i,s)=>i.id.localeCompare(s.id)),models:[...o.values()].sort((i,s)=>i.provider!==s.provider?i.provider.localeCompare(s.provider):i.id.localeCompare(s.id))}}function Bn(e,t){return e.data.sessions[t]??null}function Qe(e){return Object.values(e.data.agents).sort((t,n)=>t.id.localeCompare(n.id))}function Ye(e){return Object.values(e.data.channels).sort((t,n)=>t.id.localeCompare(n.id))}function Xe(e){return Object.values(e.data.models).sort((t,n)=>t.id.localeCompare(n.id))}function Ze(e){return Object.values(e.data.sessions).sort((t,n)=>t.id.localeCompare(n.id))}function Mn(e){return{bridge:Ke(e),pairing:ze(e),config:xn(e),capabilities:{...e.capabilities},health:{...e.health},agents:Qe(e),models:Xe(e),channels:Ye(e),sessions:Ze(e)}}var et=class{constructor(t){this.transport=t}transport;async deleteAgent(t){return await this.transport.request("agents.delete",{agentId:t.agentId,deleteFiles:t.deleteFiles??!1})??{ok:!0,agentId:t.agentId}}};var tt=class{constructor(t){this.transport=t}transport;async list(){let t=await this.transport.request("agents.list",{})??{},n=t.defaultId??null,r=[];for(let o of t.agents??[]){let i=o.id?.trim()??"";i&&r.push({id:i,name:o.name?.trim()||o.identity?.name?.trim()||i,isDefault:n===i,modelId:o.model?.primary?.trim()||null,updatedAt:null})}return r}};import{promises as Ee}from"fs";import ge from"path";import hs from"os";var ys=3600*1e3;function Ie(){let e=typeof globalThis<"u"?globalThis.process?.env:void 0,t=e?.OPENCLAW_PAIRING_CREDENTIALS_DIR;if(t&&t.trim())return t.trim();let n=e?.OPENCLAW_HOST_HOME||hs.homedir();return ge.join(n,".openclaw","credentials")}function Un(e){return e.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"")}function ws(e){return e.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"")}function Nn(e){return ge.join(Ie(),`${Un(e)}-pairing.json`)}function Ln(e,t){let n=Un(e);if(!t)return ge.join(Ie(),`${n}-allowFrom.json`);let r=ws(t);return r?ge.join(Ie(),`${n}-${r}-allowFrom.json`):ge.join(Ie(),`${n}-allowFrom.json`)}async function ve(e,t){try{let n=await Ee.readFile(e,"utf8");return JSON.parse(n)}catch(n){if(n?.code==="ENOENT")return t;throw n}}async function nt(e,t){await Ee.mkdir(ge.dirname(e),{recursive:!0});let n=`${e}.tmp-${Date.now()}`;await Ee.writeFile(n,JSON.stringify(t,null,2),"utf8"),await Ee.rename(n,e)}function bs(e){if(!e||typeof e!="object")return!1;let t=e;return t.version===1&&Array.isArray(t.requests)}function jt(e,t){return e.filter(n=>{let r=Date.parse(n.lastSeenAt??n.createdAt);return Number.isFinite(r)?t-r<=ys:!0})}var rt=class{async listPending(){let t=Ie(),n;try{n=await Ee.readdir(t)}catch(i){if(i?.code==="ENOENT")return[];throw i}let r=Date.now(),o=[];for(let i of n){let s=/^([a-z0-9_-]+)-pairing\.json$/i.exec(i);if(!s)continue;let a=s[1].toLowerCase(),l=await ve(ge.join(t,i),null);if(bs(l))for(let c of jt(l.requests,r))o.push({channel:a,...c})}return o}async listApproved(){let t=Ie(),n;try{n=await Ee.readdir(t)}catch(o){if(o?.code==="ENOENT")return[];throw o}let r=[];for(let o of n){let i=/^([a-z0-9_-]+?)(?:-([a-z0-9_-]+))?-allowFrom\.json$/i.exec(o);if(!i)continue;let s=i[1].toLowerCase(),a=i[2]?.toLowerCase(),l=await ve(ge.join(t,o),null);if(!l||typeof l!="object")continue;let c=l,d=Array.isArray(c.allowFrom)?c.allowFrom:[];for(let p of d)typeof p=="string"&&p.trim()&&r.push({channel:s,...a?{accountId:a}:{},id:p})}return r}async revoke(t){let n=t.channel.toLowerCase(),r=t.id.trim();if(!r)return{removed:!1};let o=Ln(n,t.accountId),i=await ve(o,{version:1,allowFrom:[]}),s=i.allowFrom.filter(a=>a!==r);return s.length===i.allowFrom.length?{removed:!1}:(await nt(o,{version:1,allowFrom:s}),{removed:!0})}async approve(t){let n=t.channel.toLowerCase(),r=t.code.trim().toUpperCase();if(!r)return{error:"not_found"};let o=Nn(n),i=await ve(o,{version:1,requests:[]}),s=jt(i.requests,Date.now()),a=s.findIndex(h=>h.code.toUpperCase()===r);if(a<0)return{error:"not_found"};let l=s[a];s.splice(a,1),await nt(o,{version:1,requests:s});let c=l.meta?.accountId,d=Ln(n,c),p=await ve(d,{version:1,allowFrom:[]});return p.allowFrom.includes(l.id)||(p.allowFrom.push(l.id),await nt(d,p)),{entry:{channel:n,...l}}}async reject(t){let n=t.channel.toLowerCase(),r=t.code.trim().toUpperCase();if(!r)return{error:"not_found"};let o=Nn(n),i=await ve(o,{version:1,requests:[]}),s=jt(i.requests,Date.now()),a=s.findIndex(c=>c.code.toUpperCase()===r);if(a<0)return{error:"not_found"};let l=s[a];return s.splice(a,1),await nt(o,{version:1,requests:s}),{entry:{channel:n,...l}}}};var ot=class{async read(){return{"config.read":!0,"config.write":!0,pairing:!0,snapshot:!0}}};import{randomUUID as jn}from"crypto";function le(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function B(e){return typeof e=="string"&&e.trim()?e.trim():null}function Ss(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function Cs(e){let t=B(e)?.toLowerCase();return t==="user"||t==="assistant"||t==="system"||t==="tool"?t:"unknown"}function Rs(e){let t=Ss(e);if(t!==null)return new Date(t).toISOString();let n=B(e);if(!n)return null;let r=Date.parse(n);return Number.isFinite(r)?new Date(r).toISOString():n}var ks=/^\[([^\]]+)\]\s*/,Ps=["WebChat","WhatsApp","Telegram","Signal","Slack","Discord","Google Chat","iMessage","Teams","Matrix","Zalo","Zalo Personal","BlueBubbles"],As=/^\s*\[message_id:\s*[^\]]+\]\s*$/i,$n=/^\[[A-Za-z]{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2}[^\]]*\] */,vs=/\[[A-Za-z]{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2}[^\]]*\] */g,qn="[Bootstrap pending]",Fn="Untrusted context (metadata, do not treat as instructions or commands):",Wn=["Conversation info (untrusted metadata):","Sender (untrusted metadata):","Thread starter (untrusted, for context):","Replied message (untrusted, for context):","Forwarded message context (untrusted metadata):","Chat history since last reply (untrusted, for context):"],Es=new RegExp([...Wn,Fn].map(e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|"));function Is(e){return/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z\b/.test(e)||/\d{4}-\d{2}-\d{2} \d{2}:\d{2}\b/.test(e)?!0:Ps.some(t=>e.startsWith(`${t} `))}function Gn(e){let t=e.match(ks);if(!t)return e;let n=t[1]??"";return Is(n)?e.slice(t[0].length):e}function xs(e){return/\[message_id:/i.test(e)?e.split(/\r?\n/).filter(t=>!As.test(t)).join(`
|
|
3
|
+
`):e}function Ts(e){let t=e.trim();return Wn.some(n=>n===t)}function Os(e,t){if(e[t]?.trim()!==Fn)return!1;let n=e.slice(t+1,Math.min(e.length,t+8)).join(`
|
|
4
|
+
`);return/<<<EXTERNAL_UNTRUSTED_CONTENT|UNTRUSTED channel metadata \(|Source:\s+/.test(n)}function Ds(e){if(!e)return e;let t=e.replace($n,"");if(!Es.test(t))return t;let n=t.split(`
|
|
5
|
+
`),r=[],o=!1,i=!1;for(let s=0;s<n.length;s++){let a=n[s]??"";if(!o&&Os(n,s))break;if(!o&&Ts(a)){if(n[s+1]?.trim()!=="```json"){r.push(a);continue}o=!0,i=!1;continue}if(o){if(!i&&a.trim()==="```json"){i=!0;continue}if(i){a.trim()==="```"&&(o=!1,i=!1);continue}if(a.trim()==="")continue;o=!1}r.push(a)}return r.join(`
|
|
6
|
+
`).replace(/^\n+/,"").replace(/\n+$/,"").replace($n,"")}function _s(e){let t=e.indexOf(qn);if(t===-1)return e;let n=e.slice(t+qn.length),r=null;for(let o of n.matchAll(vs))r=(o.index??0)+o[0].length;return r===null?"":n.slice(r)}function Bs(e){return e.replace(/<\s*think(?:ing)?\s*>[\s\S]*?<\s*\/\s*think(?:ing)?\s*>/gi,"").trim()}function Ms(e,t){let r=(t==="user"?_s(Ds(xs(Gn(e)))):Bs(Gn(e))).trim();return r||null}function $t(e){if(typeof e=="string")return e;if(Array.isArray(e)){let n=e.map(r=>$t(r)).filter(r=>!!r);return n.length?n.join(`
|
|
7
|
+
`):null}let t=le(e);if(!t)return null;for(let n of["text","message","delta"]){let r=B(t[n]);if(r)return r}for(let n of["content","parts","blocks"]){let r=$t(t[n]);if(r)return r}return null}function Jn(e,t){let n=le(e),r=Cs(n?.role??n?.type),o=$t(e);return{id:B(n?.id)??B(n?.messageId)??B(n?.runId)??`history-${t}`,role:r,authorName:B(n?.authorName)??B(n?.author)??B(n?.name)??B(n?.senderName),text:o?Ms(o,r):null,createdAt:Rs(n?.createdAt??n?.timestamp??n?.time)}}function Ns(e){let t=le(e);return{messages:(Array.isArray(t?.messages)?t.messages:[]).map((o,i)=>Jn(o,i)).filter(o=>!!o.text?.trim()),raw:e}}function Hn(e){let t=Jn(e,0);return t.text?.trim()?t:null}function Ls(e){let t=e instanceof Error?e.message:String(e);return/no session found|session not found|unknown session|not found/i.test(t)}var it=class{constructor(t){this.transport=t}transport;async ensureSession(t){try{let r=le(await this.transport.request("sessions.resolve",{key:t.key}));return{key:B(r?.key)??t.key,raw:r}}catch(r){if(!Ls(r))throw r}let n=le(await this.transport.request("sessions.create",{key:t.key,agentId:t.agentId,...t.label?{label:t.label}:{}}));return{key:B(n?.key)??t.key,sessionId:B(n?.sessionId),raw:n}}async sendMessage(t){let n=t.idempotencyKey?.trim()||jn(),r=le(await this.transport.request("chat.send",{sessionKey:t.sessionKey,message:t.message,idempotencyKey:n,...typeof t.timeoutMs=="number"?{timeoutMs:t.timeoutMs}:{}}));return{runId:B(r?.runId)??n,status:B(r?.status),raw:r}}async streamMessage(t){let n=t.idempotencyKey?.trim()||jn(),r=!1,o=null,i=null,s=null,a=m=>{r||(r=!0,m())},l=()=>{},c=new Promise(m=>{l=m}),d=m=>{Promise.resolve(t.emit(m)).catch(f=>{o??=f,a(l)})},p=(m,f)=>{d({type:"done",runId:n,status:m,...f?{errorMessage:f}:{}})},h=this.transport.subscribe(m=>{if(r||m.event!=="chat")return;let f=le(m.payload);if(!f)return;let w=B(f.runId),S=B(f.sessionKey);if(w!==n||S!==t.sessionKey)return;let _=B(f.state);if(_==="delta"){let D=Hn(f.message);D?.text&&d({type:"delta",runId:w,text:D.text,...D.createdAt?{createdAt:D.createdAt}:{}});return}if(_==="final"||_==="aborted"){let D=Hn(f.message);D&&d({type:"message",runId:w,message:D}),p(_==="final"?"completed":"aborted",B(f.errorMessage)??void 0),a(l);return}_==="error"&&(p("error",B(f.errorMessage)??void 0),a(l))});if(t.signal){let m=()=>{p("timeout"),a(l)};t.signal.aborted?m():(t.signal.addEventListener("abort",m,{once:!0}),s=()=>t.signal?.removeEventListener("abort",m))}i=setTimeout(()=>{p("timeout"),a(l)},t.streamTimeoutMs??5*6e4);try{let m=le(await this.transport.request("chat.send",{sessionKey:t.sessionKey,message:t.message,idempotencyKey:n,...typeof t.timeoutMs=="number"?{timeoutMs:t.timeoutMs}:{}})),f={runId:B(m?.runId)??n,status:B(m?.status),raw:m};if(await t.emit({type:"sent",runId:f.runId,status:f.status}),await c,o)throw o;return f}finally{h(),i&&clearTimeout(i),s?.()}}async waitForRun(t){let n=le(await this.transport.request("agent.wait",{runId:t.runId,timeoutMs:t.timeoutMs}));return{status:B(n?.status),raw:n}}async readHistory(t){let n=await this.transport.request("chat.history",{sessionKey:t.sessionKey,...typeof t.limit=="number"?{limit:t.limit}:{},...typeof t.maxChars=="number"?{maxChars:t.maxChars}:{}});return Ns(n)}};import{randomUUID as Us}from"crypto";var Vn=3,js=["operator.admin"];function Kn(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function $s(e){return Kn(e)?.type==="res"}function qs(e){return Kn(e)?.type==="event"}function Gs(e){if(!e)return;let t=Object.fromEntries(Object.entries(e).filter(n=>typeof n[1]=="string"));return Object.keys(t).length>0?t:void 0}async function Hs(e){return typeof e=="string"?e:e instanceof ArrayBuffer?Buffer.from(e).toString("utf8"):ArrayBuffer.isView(e)?Buffer.from(e.buffer,e.byteOffset,e.byteLength).toString("utf8"):e instanceof Blob?await e.text():String(e)}var U=class extends Error{constructor(n,r,o){super(n);this.code=r;this.details=o;this.name="OpenClawGatewayTransportError"}code;details},st=class{constructor(t){this.options=t}options;ws=null;connectPromise=null;pending=new Map;listeners=new Set;lastConnectChallenge=null;closed=!1;reconnectTimer=null;heartbeatTimer=null;reconnectAttempt=0;reconnectBackoffMs=[1e3,2e3,5e3,1e4,3e4,6e4];heartbeatIntervalMs=3e4;heartbeatTimeoutMs=1e4;lastPongAt=0;async request(t,n={}){await this.ensureConnected();let r=this.ws;if(!r||r.readyState!==WebSocket.OPEN)throw new U("OpenClaw gateway is not connected");return this.sendRequest(r,t,n)}async close(){this.closed=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.stopHeartbeat(),this.connectPromise=null;let t=this.ws;this.ws=null,this.flushPending(new U("OpenClaw gateway transport closed during shutdown","transport_closed")),t&&t.readyState!==WebSocket.CLOSED&&await new Promise(n=>{let r=!1,o=()=>{r||(r=!0,clearTimeout(i),n())},i=setTimeout(o,Math.min(this.options.connectTimeoutMs??5e3,1e3));t.addEventListener("close",o,{once:!0}),t.addEventListener("error",o,{once:!0});try{t.close(1e3,"tnyma-bridge shutdown")}catch(s){this.log("warn",`OpenClaw gateway close handshake failed: ${Ue(s)}`),o()}})}subscribe(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}async ensureConnected(){if(this.closed)throw new U("OpenClaw gateway transport is closed");if(this.ws?.readyState!==WebSocket.OPEN){if(this.connectPromise)return this.connectPromise;this.connectPromise=this.connect();try{await this.connectPromise}finally{this.connectPromise=null}}}async connect(){let t=new WebSocket(this.options.url);this.ws=t,this.lastConnectChallenge=null;let n=!1;t.addEventListener("message",r=>{this.handleMessage(r.data)}),t.addEventListener("close",()=>{this.flushPending(new U("OpenClaw gateway connection closed","connection_closed")),this.ws=null,this.stopHeartbeat(),this.closed||this.scheduleReconnect()}),t.addEventListener("error",()=>{t.readyState!==WebSocket.OPEN&&this.flushPending(new U("OpenClaw gateway connection failed","connection_error"))});try{await new Promise((i,s)=>{let a=setTimeout(()=>{s(new U(`OpenClaw gateway connect timeout after ${this.options.connectTimeoutMs??5e3}ms`,"connect_timeout"))},this.options.connectTimeoutMs??5e3);t.addEventListener("open",()=>{clearTimeout(a),i()},{once:!0}),t.addEventListener("error",()=>{clearTimeout(a),s(new U("OpenClaw gateway failed to open","connect_error"))},{once:!0})});let r=Gs(this.options.auth),o=await this.sendRequest(t,"connect",{minProtocol:Vn,maxProtocol:Vn,client:{id:this.options.client?.id??"gateway-client",version:this.options.client?.version??"0.1.0",platform:this.options.client?.platform??process.platform,mode:this.options.client?.mode??"backend"},role:this.options.role??"operator",scopes:this.options.scopes??js,caps:[],commands:[],permissions:{},...r?{auth:r}:{},...this.options.locale?{locale:this.options.locale}:{},...this.options.userAgent?{userAgent:this.options.userAgent}:{}});if(o.type!=="hello-ok")throw new U("OpenClaw gateway connect succeeded without hello-ok payload","invalid_handshake",o);n=!0}finally{if(!n){this.ws=null,this.flushPending(new U("OpenClaw gateway connection failed during handshake","connect_error")),this.stopHeartbeat();try{t.close(1002,"handshake failed")}catch{}}}this.startHeartbeat()}async sendRequest(t,n,r){let o=Us(),i=JSON.stringify({type:"req",id:o,method:n,params:r}),s=new Promise((a,l)=>{let c=setTimeout(()=>{this.pending.delete(o),l(new U(`OpenClaw gateway request timeout for ${n}`,"request_timeout"))},this.options.requestTimeoutMs??1e4);this.pending.set(o,{resolve:d=>a(d),reject:l,timeout:c})});try{t.send(i)}catch(a){let l=this.pending.get(o);this.pending.delete(o),l?.timeout&&clearTimeout(l.timeout),l?.reject(new U(`OpenClaw gateway send failed for ${n}: ${Ue(a)}`,"send_failed"))}return s}async handleMessage(t){let n;try{n=JSON.parse(await Hs(t))}catch(o){this.log("warn",`OpenClaw gateway dropped invalid JSON frame: ${Ue(o)}`);return}if(qs(n)){if(n.event==="connect.challenge"&&(this.lastConnectChallenge=n.payload??null),n.event)for(let o of this.listeners)try{o({event:n.event,payload:n.payload,seq:n.seq})}catch(i){this.log("error",`OpenClaw gateway event listener failed: ${Ue(i)}`)}return}if(!$s(n)||!n.id){this.log("warn","OpenClaw gateway dropped frame without a response id");return}let r=this.pending.get(n.id);if(!r){this.log("warn",`OpenClaw gateway received response for unknown request id ${n.id}`);return}if(this.pending.delete(n.id),r.timeout&&clearTimeout(r.timeout),n.ok){r.resolve(n.payload);return}r.reject(new U(n.error?.message??"OpenClaw gateway request failed",n.error?.code,n.error?.details))}flushPending(t){for(let[n,r]of this.pending.entries())this.pending.delete(n),r.timeout&&clearTimeout(r.timeout),r.reject(t)}log(t,n){this.options.onLog?.(t,n)}scheduleReconnect(){if(this.closed||this.reconnectTimer)return;let t=this.reconnectBackoffMs[Math.min(this.reconnectAttempt,this.reconnectBackoffMs.length-1)];this.reconnectAttempt+=1,this.log("warn",`OpenClaw gateway disconnected; reconnect attempt ${this.reconnectAttempt} in ${t}ms`),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,!this.closed&&this.ensureConnected().then(()=>{this.reconnectAttempt=0,this.log("info","OpenClaw gateway reconnected")}).catch(n=>{this.log("warn",`OpenClaw gateway reconnect failed: ${Ue(n)}`),this.scheduleReconnect()})},t),this.reconnectTimer.unref?.()}startHeartbeat(){this.stopHeartbeat(),this.lastPongAt=Date.now(),this.heartbeatTimer=setInterval(()=>{let t=this.ws;if(!(this.closed||!t||t.readyState!==WebSocket.OPEN)){if(Date.now()-this.lastPongAt>this.heartbeatIntervalMs+this.heartbeatTimeoutMs){this.log("warn","OpenClaw gateway heartbeat timed out; force-closing");try{t.close(1001,"heartbeat timeout")}catch{}return}this.sendRequest(t,"ping",{}).then(()=>{this.lastPongAt=Date.now()}).catch(()=>{})}},this.heartbeatIntervalMs),this.heartbeatTimer.unref?.()}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null)}};function Ue(e){return e instanceof Error?e.message:String(e)}function xe(e){return typeof e=="string"?e:null}function Qn(e,t){return typeof e=="boolean"?e:t}function qt(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function he(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function Fs(e){return{path:xe(e.path),hash:xe(e.hash),valid:Qn(e.valid,!0),lastLoadedAt:xe(e.lastLoadedAt),snapshot:qt(e.snapshot)??qt(e.config)}}function zn(e){let t=he(e.restart);return{path:xe(e.path)??"",hash:xe(e.hash),config:qt(e.config)??{},restart:Object.keys(t).length?{scheduled:Qn(t.scheduled,!0),delayMs:typeof t.delayMs=="number"?t.delayMs:void 0,coalesced:typeof t.coalesced=="boolean"?t.coalesced:void 0,reason:xe(t.reason)??void 0}:void 0}}function Ws(e){if(!(e instanceof U))return null;let n=he(e.details).retryAfterMs;return typeof n!="number"||!Number.isFinite(n)?null:Math.max(0,n)}async function Js(e){await new Promise(t=>setTimeout(t,e))}var Vs=18e4,Ks=3;function zs(e){return e instanceof U&&/config changed since last load|re-run config\.get/i.test(e.message)}var at=class{constructor(t){this.transport=t}transport;async getSnapshot(){let t=he(await this.transport.request("config.get",{}));return Fs(t)}async getSchema(){return he(await this.transport.request("config.schema",{}))}async lookupSchema(t){return he(await this.transport.request("config.schema.lookup",{path:t}))}async patchConfig(t){let n=he(await this.requestConfigWriteWithRateLimitRetry("config.patch",{baseHash:t.baseHash,raw:JSON.stringify(t.patch),note:t.note}));return zn(n)}async applyConfig(t){let n=he(await this.requestConfigWriteWithRateLimitRetry("config.apply",{baseHash:t.baseHash,raw:JSON.stringify(t.config),note:t.note}));return zn(n)}async requestConfigWriteWithRateLimitRetry(t,n){let r=0,o=0,i={...n};for(;;)try{return await this.transport.request(t,i)}catch(s){let a=Ws(s);if(a!=null){let l=a+250;if(r+l>Vs)throw s;r+=l,await Js(l);continue}if(zs(s)){if(t==="config.patch"&&o<Ks){o+=1;let l=await this.getSnapshot();i.baseHash=l.hash??"";continue}throw new Gt(t==="config.apply"?"config.apply rejected: the snapshot changed since baseHash; refetch and re-merge":"config.patch rejected: snapshot still moving after retries; refetch and re-merge",{cause:s instanceof Error?s:void 0})}throw s}}},Gt=class extends Error{code="config_conflict";constructor(t,n){super(t,n),this.name="ConfigConflictError"}};var lt=class{constructor(t){this.transport=t}transport;async list(){let t=await this.transport.request("models.list",{})??{},n=[];for(let r of t.models??[]){let o=r.id?.trim()??"",i=r.provider?.trim()??"";!o||!i||n.push({id:o,provider:i,name:r.name?.trim()||o,contextWindow:typeof r.contextWindow=="number"?r.contextWindow:null,supportsReasoning:typeof r.reasoning=="boolean"?r.reasoning:void 0,inputModes:Array.isArray(r.input)?[...r.input]:void 0})}return n}};var ct=class{constructor(t){this.options=t}options;getConfigPort(){return this.options.configPort}async collectBridge(t){return{bridge:{...t.bridge,status:"online"}}}async listAgents(){return this.options.agentReader.list()}async listModels(){return this.options.modelReader.list()}async listChannels(){let t=await this.options.configPort.getSnapshot();return Xs(t.snapshot)}async listSessions(){return this.options.sessionReader.list()}};function Yn(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function je(e){return typeof e=="string"&&e.trim()?e.trim():null}function Xn(e){return typeof e=="boolean"?e:null}function Qs(e){if(typeof e=="number"&&Number.isFinite(e))return new Date(e).toISOString();let t=je(e);if(!t)return null;let n=Date.parse(t);return Number.isFinite(n)?new Date(n).toISOString():t}function Ys(e){let t=Xn(e.healthy);if(t!==null)return t;let n=je(e.status)?.toLowerCase();return n?["healthy","connected","online","ready"].includes(n)?!0:["error","failed","offline","disabled"].includes(n)?!1:null:null}function Xs(e){let t=Yn(e?.channels);return t?Object.entries(t).flatMap(([n,r])=>{if(n==="defaults")return[];let o=Yn(r);if(!o)return[];let i=je(o.id)??n;return[{id:i,name:je(o.name)??je(o.label)??i,enabled:o.disabled===!0?!1:Xn(o.enabled)??!0,healthy:Ys(o),updatedAt:Qs(o.updatedAt??o.updated_at)}]}).sort((n,r)=>n.id.localeCompare(r.id)):[]}function Zs(e){return typeof e=="number"&&Number.isFinite(e)?new Date(e).toISOString():null}function ea(e){return e==="running"?"active":"idle"}var dt=class{constructor(t){this.transport=t}transport;async list(){let t=await this.transport.request("sessions.list",{})??{},n=[];for(let r of t.sessions??[]){let o=r.key?.trim()??"";if(!o)continue;let i=Zs(r.updatedAt);n.push({id:o,agentId:r.spawnedBy?.trim()||null,title:r.derivedTitle?.trim()||r.displayName?.trim()||r.label?.trim()||r.subject?.trim()||null,status:ea(r.status),lastMessageAt:i,updatedAt:i})}return n}};var ut=class{constructor(t){this.transport=t}transport;async status(t){return await this.transport.request("skills.status",t?{agentId:t}:{})??{}}async installClawHubSkill(t){return await this.transport.request("skills.install",{source:"clawhub",slug:t.slug,...t.version?{version:t.version}:{},...t.force?{force:!0}:{}})??{}}async updateClawHubSkill(t){return await this.transport.request("skills.update",{source:"clawhub",slug:t.slug})??{}}};function Zn(e,t){return{...e,data:{...e.data,agents:Object.fromEntries(t.map(n=>[n.id,n]))}}}function er(e,t){return{...e,bridge:{...e.bridge,...t}}}function tr(e,t){return{...e,data:{...e.data,channels:Object.fromEntries(t.map(n=>[n.id,n]))}}}function nr(e,t){return{...e,data:{...e.data,models:Object.fromEntries(t.map(n=>[n.id,n]))}}}function rr(e,t){return{...e,data:{...e.data,sessions:Object.fromEntries(t.map(n=>[n.id,n]))}}}var Te=class{constructor(t){this.options=t}options;timer=null;running=!1;start(){this.timer||(this.timer=setInterval(()=>{this.runScheduledTask()},this.options.intervalMs))}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}async tick(){await this.options.task()}async runScheduledTask(){if(!this.running){this.running=!0;try{await this.options.task()}catch(t){this.options.onError?.(t)}finally{this.running=!1}}}};var gt=class extends Te{constructor(t){super(t)}};var pt=class{constructor(t){this.options=t}options;snapshot={running:!1,lastCollectionAt:null,lastConfigSyncAt:null};start(){this.snapshot.running||(this.snapshot.running=!0,this.options.collectionLoop.start(),this.options.configSyncLoop.start())}stop(){this.snapshot.running&&(this.snapshot.running=!1,this.options.collectionLoop.stop(),this.options.configSyncLoop.stop())}markCollection(t=new Date().toISOString()){this.snapshot.lastCollectionAt=t}markConfigSync(t=new Date().toISOString()){this.snapshot.lastConfigSyncAt=t}getSnapshot(){return{...this.snapshot}}};import{homedir as yr,hostname as ca}from"os";import W from"path";import fr from"fs";import{randomUUID as or}from"crypto";import{mkdirSync as ta,readFileSync as ir,writeFileSync as sr,existsSync as ar,renameSync as lr,unlinkSync as cr}from"fs";import na from"path";function ur(e,t={}){let n=t.envOverride?.trim();if(n)return console.warn(`[bridge-id] Using BRIDGE_ID env override "${n}" instead of persisted UUID. Intended for testing/migration only. Unset BRIDGE_ID to use the auto-generated UUID.`),n;let r=na.join(e,"bridge-id");if(ar(r)){let a=ir(r,"utf8").trim();if(dr(a))return a}ta(e,{recursive:!0});let o=or(),i=r+".tmp";try{sr(i,o+`
|
|
8
|
+
`,{encoding:"utf8",mode:384,flag:"wx"});try{return lr(i,r),o}catch(a){try{cr(i)}catch(l){let c=l?.code}throw a}}catch(a){if(a?.code!=="EEXIST")throw a}if(!ar(r)){try{cr(i)}catch(l){if(l?.code!=="ENOENT")throw l}let a=or();return sr(i,a+`
|
|
9
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),lr(i,r),a}let s=ir(r,"utf8").trim();if(!dr(s))throw new Error(`Race detected writing bridge-id, and winner's file is invalid: ${s.slice(0,40)}`);return s}function dr(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(e)}import{execFileSync as pr}from"child_process";import{existsSync as ra}from"fs";import{homedir as oa,platform as ia}from"os";import Ft from"path";var mr=2e3,Ht=null;function Wt(){if(Ht!==null)return Ht.value;let e=null;try{switch(ia()){case"darwin":e=sa();break;case"linux":e=aa();break;default:e=null}}catch{e=null}return Ht={value:e},e}function sa(){let e=Ft.join(oa(),"Library/LaunchAgents/ai.openclaw.gateway.plist");if(!ra(e))return null;let t;try{t=pr("/usr/bin/plutil",["-convert","json","-o","-",e],{encoding:"utf8",timeout:mr})}catch{return null}let n;try{n=JSON.parse(t)}catch{return null}if(!la(n))return null;let r=n.WorkingDirectory;return typeof r=="string"&&r.trim()?Ft.resolve(r.trim()):null}function aa(){let e=gr(["--user"]);return e||gr([])}function gr(e){let t;try{t=pr("systemctl",[...e,"show","ai.openclaw.gateway","--property=WorkingDirectory","--no-pager"],{encoding:"utf8",timeout:mr})}catch{return null}let n=t.match(/^WorkingDirectory=(.+)$/m);if(!n)return null;let r=n[1].trim();return!r||r==="(null)"||r==="/"?null:Ft.resolve(r)}function la(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Oe(e,t){let n=Number.parseInt(e??"",10);return Number.isFinite(n)?n:t}function wr(e=process.env){let t=ca(),n=ua(e),r=$e(e),o=Jt(e,r),i=Sr(e,{hostHome:r,stateDir:o}),s=fa(e,r);return ya(r),wa(o,r),{bridgeId:ur(o,{envOverride:e.BRIDGE_ID}),nodeId:e.SOURCE_NODE_ID??t,upstreamKind:e.UPSTREAM_KIND??"openclaw",openClawConfigPath:i,openClawStateDir:o,openClawHostHome:r,openClawDefaultWorkspaceDir:s,bridgeToken:e.BRIDGE_TOKEN?.trim()||null,discoveryUrl:e.TNYMA_DISCOVERY_URL?.trim()||"https://api.tnyma.com/v1/bridge/discover",clientVersion:e.BRIDGE_CLIENT_VERSION?.trim()||"0.1.0",configWriteAllowedRoots:(e.CONFIG_WRITE_ALLOWED_ROOTS??"gateway,agents,bindings,channels,models").split(",").map(a=>a.trim()).filter(Boolean),openClawGatewayUrl:e.OPENCLAW_GATEWAY_URL??"ws://127.0.0.1:18789",openClawGatewayAuthMode:n.mode,openClawGatewayToken:n.token,openClawGatewayPassword:n.password,openClawGatewayDeviceToken:e.OPENCLAW_GATEWAY_DEVICE_TOKEN??null,openClawRequestTimeoutMs:Oe(e.OPENCLAW_REQUEST_TIMEOUT_MS,1e4),openClawConnectTimeoutMs:Oe(e.OPENCLAW_CONNECT_TIMEOUT_MS,5e3),pairingServerUrl:e.PAIRING_SERVER_URL??e.BRIDGE_REGISTRY_URL??"https://api.tnyma.com",pairingRegisterUrl:e.PAIRING_REGISTER_URL??e.PAIRING_SERVER_URL??e.BRIDGE_REGISTRY_URL??"https://api.tnyma.com",pairingDisplayName:e.PAIRING_DISPLAY_NAME??`OpenClaw ${t}`,pairingRequestTimeoutMs:Oe(e.PAIRING_REQUEST_TIMEOUT_MS,1e4),pairingPrintAsciiQr:hr(e.PAIRING_PRINT_ASCII_QR,!0),bindUrlBase:pa(e.TNYMA_BIND_URL_BASE??"http://localhost:21100"),localUiEnabled:hr(e.TNYMA_BRIDGE_LOCAL_UI_ENABLED,!0),localUiHost:e.TNYMA_BRIDGE_LOCAL_UI_HOST?.trim()||"127.0.0.1",localUiPort:Oe(e.TNYMA_BRIDGE_LOCAL_UI_PORT,18788),localUiPublicUrl:z(e.TNYMA_BRIDGE_LOCAL_UI_PUBLIC_URL),localUiOpenMode:da(e.TNYMA_BRIDGE_OPEN_ON_START),collectionIntervalMs:Oe(e.BRIDGE_COLLECTION_INTERVAL_MS,15e3),configSyncIntervalMs:Oe(e.BRIDGE_CONFIG_SYNC_INTERVAL_MS,3e4)}}function da(e){switch(e?.trim().toLowerCase()){case"0":case"false":case"never":case"off":return"never";case"1":case"true":case"always":case"on":return"always";default:return"auto"}}function hr(e,t){if(!e)return t;switch(e.toLowerCase()){case"1":case"true":case"yes":case"on":return!0;case"0":case"false":case"no":case"off":return!1;default:return t}}function ua(e){let t=br(e.OPENCLAW_GATEWAY_AUTH_MODE),n=z(e.OPENCLAW_GATEWAY_TOKEN),r=z(e.OPENCLAW_GATEWAY_PASSWORD),o=ma(e),i=n??o.token,s=r??o.password,a=t??o.authMode??ga(i,s);if(a==="token")return{mode:a,token:i,password:null};if(a==="password")return{mode:a,token:null,password:s};if(i&&s)throw new Error("OpenClaw gateway auth is ambiguous: both token and password are configured, but OPENCLAW_GATEWAY_AUTH_MODE is unset.");return{mode:null,token:i,password:s}}function ga(e,t){return e&&t?null:e?"token":t?"password":null}function br(e){let t=e?.trim().toLowerCase();return t==="token"||t==="password"?t:null}function z(e){let t=e?.trim();return t||null}function pa(e){return e.trim().replace(/\/+$/,"")}function ma(e){let t=$e(e),n=Jt(e,t),r=Sr(e,{hostHome:t,stateDir:n});if(!r||!fr.existsSync(r))return{authMode:null,token:null,password:null};try{let i=JSON.parse(fr.readFileSync(r,"utf8")).gateway?.auth;return{authMode:br(typeof i?.mode=="string"?i.mode:void 0),token:typeof i?.token=="string"?z(i.token):null,password:typeof i?.password=="string"?z(i.password):null}}catch(o){return process.emitWarning(`Failed to read OpenClaw gateway config from ${r}: ${ha(o)}`,{code:"TNYMA_BRIDGE_CONFIG_READ_FAILED"}),{authMode:null,token:null,password:null}}}function $e(e){let t=z(e.HOME)??z(e.USERPROFILE)??yr(),n=z(e.OPENCLAW_HOST_HOME)??z(e.OPENCLAW_HOME);if(n)return W.resolve(Vt(n,t));let r=Wt();return r?W.dirname(r):W.resolve(t)}function Jt(e,t=$e(e)){let n=z(e.OPENCLAW_STATE_DIR);if(n)return W.resolve(Vt(n,t));let r=Wt();return r||W.join(t,".openclaw")}function Sr(e,t={}){let n=z(e.OPENCLAW_CONFIG_PATH),r=t.hostHome??$e(e);if(n)return W.resolve(Vt(n,r));let o=t.stateDir??Jt(e,r);return W.join(o,"openclaw.json")}function fa(e,t=$e(e)){let n=z(e.OPENCLAW_PROFILE);return n&&n.toLowerCase()!=="default"?W.join(t,".openclaw",`workspace-${n}`):W.join(t,".openclaw","workspace")}function Vt(e,t=yr()){return e==="~"?t:e.startsWith("~/")?W.join(t,e.slice(2)):e}function ha(e){return e instanceof Error?e.message:String(e)}var Cr=["/etc","/var/run","/proc","/sys","/dev","/boot","/lib","/usr/lib","/usr/bin","/sbin"];function ya(e){if(!W.isAbsolute(e))throw new Error(`OPENCLAW_HOST_HOME must resolve to an absolute path; got ${e}`);for(let t of Cr)if(e===t||e.startsWith(`${t}/`))throw new Error(`OPENCLAW_HOST_HOME=${e} is on the system path denylist; choose a user-owned directory`)}function wa(e,t){if(!W.isAbsolute(e))throw new Error(`OPENCLAW_STATE_DIR must resolve to an absolute path; got ${e}`);for(let r of Cr)if(e===r||e.startsWith(`${r}/`))throw new Error(`OPENCLAW_STATE_DIR=${e} is on the system path denylist`);if(W.relative(t,e).startsWith("..")&&!["/root/.openclaw","/home","/Users"].some(i=>e===i||e.startsWith(`${i}/`)))throw new Error(`OPENCLAW_STATE_DIR=${e} is outside OPENCLAW_HOST_HOME=${t} and not an allowed container mount`)}async function Rr(e){return e.listAgents()}async function kr(e){return{bridge:{...e.bridge,status:"online"},health:{...e.health,lastSuccessAt:new Date().toISOString(),lastError:null}}}async function Pr(e){return e.listChannels()}async function Ar(e){return e.listModels()}async function vr(e){return e.listSessions()}async function mt(e){let t=await e.upstreamConfigPort.getSnapshot(),n=new Date().toISOString(),r={configPath:t.path??e.nodeConfig.openClawConfigPath,defaultWorkspaceDir:e.nodeConfig.openClawDefaultWorkspaceDir,stateDir:e.nodeConfig.openClawStateDir,hostHome:e.nodeConfig.openClawHostHome};return e.store.update(o=>({...o,meta:{...o.meta,collectedAt:n},config:{path:t.path,hash:t.hash,valid:t.valid,lastLoadedAt:t.lastLoadedAt??n,snapshot:t.snapshot},health:{lastSuccessAt:n,lastError:null}})),{...t,runtimePaths:r}}var ft=class{serverUrl;timeoutMs;fetchImpl;constructor(t){this.serverUrl=t.serverUrl?.trim()?t.serverUrl.trim():null,this.timeoutMs=t.timeoutMs??1e4,this.fetchImpl=t.fetchImpl??fetch}async registerPairing(t){let n=t.relaySecret?.trim()??"";if(!n)throw new Error("PairingHttpClient: relaySecret is required for bridge registration.");return this.postJson("/v1/pair/register",t,n)}async refreshAccessCode(t){return this.postJson("/v1/pair/access-code",t,t.relaySecret)}async postJson(t,n,r){let o=await this.fetchImpl(`${ba(this.serverUrl)}${t}`,{method:"POST",headers:{accept:"application/json","content-type":"application/json",authorization:`Bearer ${r}`},body:JSON.stringify(n),signal:AbortSignal.timeout(this.timeoutMs)}),i=await o.json();if(!o.ok||!i.ok){let s=i.ok?`Pairing request failed (${o.status})`:i.error.message;throw new Error(s)}return i.data}};function ba(e){let t=e?.trim()??"";if(!t)throw new Error("pairing server URL is not configured");return t.startsWith("http://")||t.startsWith("https://")?t.replace(/\/+$/,""):t.startsWith("ws://")?`http://${t.slice(5)}`.replace(/\/+$/,""):t.startsWith("wss://")?`https://${t.slice(6)}`.replace(/\/+$/,""):`https://${t}`.replace(/\/+$/,"")}import{randomBytes as Sa}from"crypto";var Ca=3e4,Er=0;async function ht(e,t={}){let n=Ra(e.nodeConfig.pairingServerUrl),r=e.store.get(),o=t.displayName?.trim()||e.nodeConfig.pairingDisplayName,i=r.raw?.pairing?.relaySecret??null,s,a=i;if(i&&r.pairing.serverUrl===n&&r.pairing.bridgeId===e.nodeConfig.bridgeId)try{s=await e.pairingPort.refreshAccessCode({bridgeId:e.nodeConfig.bridgeId,relaySecret:i,displayName:o})}catch(m){let f=Date.now();if(f-Er<Ca)throw m;Er=f;let w={bridgeId:e.nodeConfig.bridgeId,displayName:o,preferredRegion:t.preferredRegion,relaySecret:i},S=await e.pairingPort.registerPairing(w);s=S,a=S.relaySecret}else{let m=i??ka(),f={bridgeId:e.nodeConfig.bridgeId,displayName:o,preferredRegion:t.preferredRegion,relaySecret:m},w=await e.pairingPort.registerPairing(f);s=w,a=w.relaySecret}let l=s.pairCode??s.accessCode,c=s.pairCodeExpiresAt??s.accessCodeExpiresAt,d=In({serverUrl:n,bridgeId:s.bridgeId,pairCode:l,pairCodeExpiresAt:c,displayName:s.displayName}),p=d.find(m=>m.kind==="qr")?.payload??null,h={serverUrl:n,bridgeId:s.bridgeId,pairCode:l,pairCodeExpiresAt:c,accessCode:s.accessCode,accessCodeExpiresAt:s.accessCodeExpiresAt,displayName:s.displayName,region:s.region,relayUrl:s.relayUrl,qrPayload:p,methods:d,protocolVersion:2,supportsBootstrap:!0};return e.store.update(m=>({...m,meta:{...m.meta,collectedAt:new Date().toISOString()},pairing:{serverUrl:n,bridgeId:s.bridgeId,pairCode:l,pairCodeExpiresAt:c,accessCode:s.accessCode,accessCodeExpiresAt:s.accessCodeExpiresAt,displayName:s.displayName,region:s.region,relayUrl:s.relayUrl,qrPayload:p,methods:d,protocolVersion:2,supportsBootstrap:!0},raw:{...m.raw,pairing:{relaySecret:a}}})),h}function Ra(e){let t=e?.trim();if(!t)throw new Error("pairing server URL is not configured");return t}function ka(){return`brs_${Sa(24).toString("hex")}`}function yt(){return Date.now()}function Pa(e){return/gateway (connection|transport|failed|closed|not connected|timeout|unavailable)|failed to open|connect timeout/i.test(e)}async function Aa(e){await new Promise(t=>setTimeout(t,e))}var wt=class{block=null;current(t=yt()){let n=this.block;return n?n.untilMs<=t?(this.block=null,null):{reason:n.reason,message:n.message,until:new Date(n.untilMs).toISOString(),retryAfterMs:Math.max(0,n.untilMs-t)}:null}markReady(){this.block=null}async waitUntilReady(t=55e3){let n=yt();for(;;){let r=this.current();if(!r)return;let o=t-(yt()-n);if(o<=0)return;await Aa(Math.min(r.retryAfterMs,o,1e3))}}blockForRestart(t){if(t?.scheduled!==!0)return;let n=Math.max(2e4,(t.delayMs??0)+1e4);this.blockFor("upstream_restarting","OpenClaw gateway is restarting",n)}blockForError(t){let n=t instanceof Error?t.message:String(t);Pa(n)&&this.blockFor("upstream_restarting","OpenClaw gateway is not ready",5e3)}blockFor(t,n,r){let o=yt()+r;this.block&&this.block.untilMs>o||(this.block={reason:t,message:n,untilMs:o})}};var y=class extends Error{constructor(n,r="bad_request"){super(n);this.code=r;this.name="UserInputError"}code};function j(e){return e instanceof y}function Ir(e){return e instanceof Error?e.message:String(e)}var va=40,Ea=8e3;function Br(e){return e.trim().toLowerCase().replace(/[^a-z0-9_-]+/g,"-").replace(/^-+/,"").replace(/-+$/,"").slice(0,64)||"main"}function xr(e){return typeof e=="string"&&e.trim()?e.trim():null}function Tr(e,t=va){return typeof e=="number"&&Number.isFinite(e)?Math.max(1,Math.min(200,Math.trunc(e))):t}function Or(e){return typeof e=="number"&&Number.isFinite(e)?Math.max(0,Math.min(6e4,Math.trunc(e))):Ea}function Mr(e){return Object.values(e.data.agents).map(t=>({id:t.id.trim(),name:t.name.trim()||t.id.trim()})).filter(t=>t.id)}function bt(e){let t=Mr(e);return Object.values(e.data.agents).map(n=>({id:n.id.trim(),name:n.name.trim()||n.id.trim(),isDefault:n.isDefault===!0})).find(n=>n.id&&n.isDefault)??t[0]??{id:"main",name:"main"}}function Kt(e,t){let n=Mr(e),r=t?.trim();if(!r)return bt(e);let o=r.toLowerCase(),i=n.find(s=>s.id.toLowerCase()===o||s.name.toLowerCase()===o);if(!i)throw new y(`agent not found: ${r}`,"agent_not_found");return i}function Ia(e){let t=e.name.trim();return`@${t&&!/\s/.test(t)&&!t.startsWith("@")?t:e.id}`}function zt(e){return`agent:${Br(e.id)}:tnyma-app:v3:direct`}function Qt(e){return`agent:${Br(e.id)}:tnyma-app:v3:team`}function Dr(e,t){let n=e.trim();if(/^@\S+/.test(n))return{deliveredMessage:n,mention:n.match(/^@\S+/)?.[0]??null};if(!t)throw new y("agentId is required for group chat unless message starts with @AgentName");let r=Ia(t);return{deliveredMessage:`${r} ${n}`.trim(),mention:r}}function xa(e,t){return e==="ok"?"completed":e==="timeout"?"timeout":t==="failed"?"failed":"started"}var _r=6e4,Ta=1024,St=class{constructor(t){this.options=t}options;idempotency=new Map;async send(t,n){let r=xr(n.message);if(!r)throw new y("message is required");let o=typeof n.idempotencyKey=="string"&&n.idempotencyKey.trim()?`${t}|${n.idempotencyKey.trim()}`:null;if(o){let S=this.idempotency.get(o);if(S&&Date.now()-S.at<_r)return S.result}let i=this.options.store.get(),s=n.agentId?Kt(i,n.agentId):null,a=bt(i),l=t==="direct"?s??a:a,c=t==="direct"?zt(l):Qt(l),d=t==="direct"?{deliveredMessage:r,mention:null}:Dr(r,s),p=await this.options.chatPort.ensureSession({key:c,agentId:l.id,label:t==="direct"?`Tnyma App Direct v3 - ${l.name}`:"Tnyma App Team Chat v3"}),h=await this.options.chatPort.sendMessage({sessionKey:p.key,message:d.deliveredMessage}),m=null;n.waitForReply===!0&&h.runId&&(m=(await this.options.chatPort.waitForRun({runId:h.runId,timeoutMs:Or(n.waitTimeoutMs)})).status??null);let f=await this.readHistoryForSession({kind:t,sessionKey:p.key,targetAgent:t==="direct"?l:s,request:{limit:Tr(n.historyLimit)}}),w={conversationKind:t,sessionKey:p.key,targetAgentId:t==="direct"?l.id:s?.id??null,targetAgentName:t==="direct"?l.name:s?.name??null,mention:d.mention,sentMessage:r,deliveredMessage:d.deliveredMessage,runId:h.runId??null,status:xa(m,h.status),waitStatus:m,messages:f.messages};return o&&this.rememberIdempotent(o,w),w}rememberIdempotent(t,n){let r=Date.now();for(let[o,i]of this.idempotency)r-i.at>=_r&&this.idempotency.delete(o);if(this.idempotency.size>=Ta){let o=this.idempotency.keys().next().value;o&&this.idempotency.delete(o)}this.idempotency.set(t,{at:r,result:n})}async history(t,n){let r=this.options.store.get(),o=n.agentId?Kt(r,n.agentId):null,i=bt(r),s=t==="direct"?o??i:i,a=t==="direct"?zt(s):Qt(s),l=await this.options.chatPort.ensureSession({key:a,agentId:s.id,label:t==="direct"?`Tnyma App Direct v3 - ${s.name}`:"Tnyma App Team Chat v3"});return this.readHistoryForSession({kind:t,sessionKey:l.key,targetAgent:t==="direct"?s:o,request:n})}async stream(t,n,r,o){let i=xr(n.message);if(!i)throw new y("message is required");let s=this.options.store.get(),a=n.agentId?Kt(s,n.agentId):null,l=bt(s),c=t==="direct"?a??l:l,d=t==="direct"?zt(c):Qt(c),p=t==="direct"?{deliveredMessage:i,mention:null}:Dr(i,a),h=await this.options.chatPort.ensureSession({key:d,agentId:c.id,label:t==="direct"?`Tnyma App Direct v3 - ${c.name}`:"Tnyma App Team Chat v3"});if(this.options.chatPort.streamMessage){await this.options.chatPort.streamMessage({sessionKey:h.key,message:p.deliveredMessage,signal:o,streamTimeoutMs:Or(n.waitTimeoutMs||5*6e4),emit:r});return}let m=await this.options.chatPort.sendMessage({sessionKey:h.key,message:p.deliveredMessage});await r({type:"sent",runId:m.runId??null,status:m.status??null}),await r({type:"done",runId:m.runId??null,status:"timeout"})}async readHistoryForSession(t){let n=await this.options.chatPort.readHistory({sessionKey:t.sessionKey,limit:Tr(t.request.limit),maxChars:t.request.maxChars});return{conversationKind:t.kind,sessionKey:t.sessionKey,targetAgentId:t.targetAgent?.id??null,targetAgentName:t.targetAgent?.name??null,messages:n.messages}}};var Oa="https://clawhub.ai";function pe(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function J(e){return typeof e=="string"?e.trim():""}function Yt(e){return J(e)||null}function ce(e){if(typeof e=="number"&&Number.isFinite(e))return e;if(typeof e=="string"){let t=Number(e);return Number.isFinite(t)?t:null}return null}function Ct(e){if(typeof e=="number"&&Number.isFinite(e))return e;if(typeof e=="string"){let t=Date.parse(e);if(Number.isFinite(t))return t;let n=Number(e);if(Number.isFinite(n))return n}return null}function Da(e){let t=pe(e);if(!t)return[];let n=[t.data,t.items,t.results];for(let r of n){if(Array.isArray(r))return r.map(i=>pe(i)).filter(i=>!!i);let o=pe(r);if(o){for(let i of[o.items,o.results,o.data])if(Array.isArray(i))return i.map(s=>pe(s)).filter(s=>!!s)}}return[]}function _a(e){let n=pe(e.package)??e,r=J(n.slug)||J(n.name)||J(n.id);if(!r)return null;let o=pe(n.latestVersion)??pe(n.latest_version),i=pe(n.owner);return{slug:r,displayName:J(n.displayName)||J(n.display_name)||r,summary:J(n.summary),version:J(o?.version)||J(n.version)||J(n.latestVersionString)||"",downloads:ce(n.downloads)??ce(n.download_count)??ce(n.current_installs)??ce(n.all_time_installs),stars:ce(n.stars)??ce(n.star_count),versionCount:ce(n.versions)??ce(n.version_count)??ce(n.versions_count),ownerHandle:J(n.ownerHandle)||J(n.owner_handle)||J(i?.handle),ownerImageUrl:Yt(n.ownerImageUrl)||Yt(n.owner_image)||Yt(i?.image),updatedAt:Ct(n.updatedAt)||Ct(n.updated_at)||Ct(o?.createdAt)||Ct(o?.created_at)}}var De=class{baseUrl;timeoutMs;constructor(t={}){this.baseUrl=(t.baseUrl??process.env.OPENCLAW_CLAWHUB_URL??process.env.CLAWHUB_URL??Oa).replace(/\/+$/,""),this.timeoutMs=t.timeoutMs??3e4}async list(t={}){let n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs);try{let o=new URL(t.query?.trim()?"/api/v1/search":"/api/v1/skills",`${this.baseUrl}/`);t.query?.trim()?o.searchParams.set("q",t.query.trim()):o.searchParams.set("sort","downloads"),o.searchParams.set("limit",String(t.limit??100));let i=await fetch(o,{headers:{accept:"application/json"},signal:n.signal});if(!i.ok)throw new Error(`ClawHub request failed (${i.status})`);let s=await i.json();return Da(s).map(_a).filter(a=>!!a)}finally{clearTimeout(r)}}};import Gr from"os";import ye from"fs/promises";import O from"path";function kt(e){return{builtIn:e.builtIn,global:e.global,spec:e.spec}}function M(e){return typeof e=="string"?e.trim():""}function V(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function Nr(e){return e==null||!Number.isFinite(e)?"\u2014":e>=1e6?`${(e/1e6).toFixed(e>=1e7?0:1).replace(/\.0$/,"")}m`:e>=1e3?`${(e/1e3).toFixed(e>=1e5?0:1).replace(/\.0$/,"")}k`:String(e)}function Ba(e){return e==null||!Number.isFinite(e)?"\u2014":`${e} v`}function Z(e){return[...new Set([...e].filter(Boolean))]}function we(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function Lr(e){return Array.isArray(e)?Z(e.map(M)):[]}function Ur(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function Ma(e){switch(e){case"openclaw-bundled":return"bundled";case"openclaw-managed":return"global";case"agents-skills-personal":return"personal";case"agents-skills-project":case"openclaw-workspace":return"agent";default:return"extra"}}function Na(e){return e==="bundled"||e==="global"||e==="personal"||e==="extra"}function jr(e){let t=O.basename(M(e.baseDir)),r=[V(t),V(M(e.skillKey)),V(M(e.name))].filter(Boolean)[0]??"unknown-skill";return{id:r,slug:r}}function La(e,t){let n=Z([V(t.slug)]);for(let r of n){let o=e.get(r);if(o)return o}}function Hr(e){return Object.values(e.data.agents).map(t=>t.id.trim()).filter(Boolean).sort()}function Ua(e){let t=we(e.config.snapshot),n=we(t.agents),r=Array.isArray(n.list)?n.list:[];return Z([...Hr(e),...r.map(o=>M(we(o).id))]).sort()}function $r(e,t){let n=new Set([t.id,t.slug,t.name].map(V).filter(Boolean));return e.some(r=>n.has(V(r)))}function ja(e,t){let n=we(e.config.snapshot),r=we(n.agents),o=we(r.defaults),i=Ur(o,"skills"),s=Lr(o.skills),a=Array.isArray(r.list)?r.list.map(d=>we(d)):[],l=new Map;for(let d of a){let p=M(d.id);p&&l.set(p,d)}let c=Ua(e);for(let d of t.values()){let p=!i||$r(s,d),h=new Set;for(let m of c){let f=l.get(m);((f?Ur(f,"skills"):!1)&&f?$r(Lr(f.skills),d):p)&&h.add(m)}d.globalVisible=p,d.installedAgentKeys=h,d.globalEnabled=d.installed&&d.globalEnabled}}function $a(){return O.join(Gr.homedir(),".agents","skills")}function qa(e,t){return{id:t?.id??V(e.slug),slug:V(e.slug),name:e.displayName||t?.name||e.slug,version:e.version||"\u2014",summary:e.summary||t?.summary||"",author:e.ownerHandle?`@${e.ownerHandle.replace(/^@+/,"")}`:"@clawhub",authorAvatarUrl:e.ownerImageUrl,downloads:Nr(e.downloads),stars:e.stars==null?"\u2014":`\u2605 ${Nr(e.stars)}`,versions:Ba(e.versionCount),installed:t?.installed??!1,globalVisible:t?.globalVisible??!1,globalEnabled:t?.globalEnabled??!1,supportsEnableToggle:!1,source:t?.source??"market",needsSetup:t?.needsSetup??!1,installedAgentKeys:t?Z(t.installedAgentKeys):[],hasUpdate:!1,latestVersion:e.version||null,installLocations:t?.installLocations??[]}}function Ga(e){let t=e.source==="builtIn";return{id:e.id,slug:e.slug,name:e.name,version:t?"bundled":"local",summary:e.summary,author:t?"@openclaw":"@local",authorAvatarUrl:null,downloads:"\u2014",stars:"\u2014",versions:"\u2014",installed:!0,globalVisible:e.globalVisible,globalEnabled:e.globalEnabled,supportsEnableToggle:!1,source:e.source,needsSetup:e.needsSetup,installedAgentKeys:Z(e.installedAgentKeys),hasUpdate:!1,latestVersion:null,installLocations:e.installLocations}}function Ha(e,t){let n=t.trim().toLowerCase();return n?[e.name,e.summary,e.author,e.slug].some(r=>r.toLowerCase().includes(n)):!0}var Rt=class{constructor(t){this.options=t;this.clawHubClient=t.clawHubClient??new De}options;clawHubClient;async read(t,n={}){let r=await this.collectLocalStatus(t),o=new Map,i=null,s=null,a={};for(let[f,w]of r.entries()){w.workspaceDir&&f&&(a[f]=w.workspaceDir),!i&&w.managedSkillsDir&&(i=w.managedSkillsDir);for(let S of w.skills??[]){let{id:_,slug:D}=jr(S),G=Ma(M(S.source)),N=o.get(_)??{id:_,slug:D,name:M(S.name)||D,summary:M(S.description),source:G==="bundled"?"builtIn":"market",installed:!1,globalVisible:!1,globalEnabled:!1,needsSetup:!1,installedAgentKeys:new Set,installLocations:[]};N.installed=!0,N.name=N.name||M(S.name)||D,N.summary=N.summary||M(S.description),N.globalVisible=N.globalVisible||Na(G),N.globalEnabled=N.globalEnabled||S.disabled!==!0,N.needsSetup=N.needsSetup||S.disabled!==!0&&S.eligible===!1,G==="agent"&&f&&N.installedAgentKeys.add(f),N.installLocations.push({source:M(S.source)||"unknown",scope:G,path:M(S.baseDir)||M(S.filePath),agentId:f||null,enabled:S.disabled!==!0,eligible:S.eligible!==!1}),!s&&G==="bundled"&&M(S.baseDir)&&(s=O.dirname(M(S.baseDir))),o.set(_,N)}}ja(t,o);let l=await this.clawHubClient.list({query:n.query,limit:n.limit}),c=[],d=new Set;for(let f of l){let w=La(o,f);w&&d.add(w.id),c.push(qa(f,w))}for(let[f,w]of o.entries()){if(d.has(f))continue;let S=Ga(w);Ha(S,n.query??"")&&c.push(S)}let p=[],h=[],m=[];for(let f of c){if(f.source==="builtIn"){p.push(f);continue}f.installed&&(f.globalVisible?h.push(f):f.installedAgentKeys.length>0&&m.push(f))}return{builtIn:p,global:h,spec:m,items:c,locations:{clawhubBaseUrl:this.clawHubClient.baseUrl,managedSkillsDir:i??"~/.openclaw/skills",personalSkillsDir:$a(),bundledSkillsDir:s,agentWorkspaceDirs:a}}}async collectLocalStatus(t){let n=Hr(t),r=new Map;if(r.set("",await this.options.skillsClient.status()),n.length===0)return r;let o=await Promise.all(n.map(async i=>[i,await this.options.skillsClient.status(i)]));for(let[i,s]of o)r.set(i,s);return r}async installedSkillSlugs(t){let n=await this.collectLocalStatus(t),r=new Set;for(let o of n.values())for(let i of o.skills??[]){let{slug:s}=jr(i);s&&r.add(s)}return[...r].sort()}async installClawHubSkill(t){return this.options.skillsClient.installClawHubSkill(t)}async promoteClawHubSkillToManaged(t){let n=V(t.slug),r=Ja(t.catalog),o=O.join(r,n);if(!za(o,n,r))throw new Error(`Unsafe managed skill target path: ${o}`);let i=Va({slug:n,catalog:t.catalog,gateway:t.gateway}),s=await Ka(i),a=await Fr(o);if(!s){if(a)return{targetPath:o,sourcePath:null,promoted:!1,alreadyPresent:!0,removedWorkspacePaths:[]};throw new Error(`Installed skill ${t.slug} was not found after gateway install`)}if(!Qa(s,n,t.catalog))throw new Error(`Unsafe installed skill source path: ${s}`);if(O.resolve(s)===O.resolve(o))return{targetPath:o,sourcePath:s,promoted:!1,alreadyPresent:!0,removedWorkspacePaths:[]};if(await ye.mkdir(r,{recursive:!0}),a){if(!t.force)return await ye.rm(s,{recursive:!0,force:!0}),{targetPath:o,sourcePath:s,promoted:!1,alreadyPresent:!0,removedWorkspacePaths:[s]};await ye.rm(o,{recursive:!0,force:!0})}return await ye.cp(s,o,{recursive:!0,force:!0}),await ye.rm(s,{recursive:!0,force:!0}),{targetPath:o,sourcePath:s,promoted:!0,alreadyPresent:!1,removedWorkspacePaths:[s]}}async updateClawHubSkill(t){return this.options.skillsClient.updateClawHubSkill(t)}async removeSkillInstallations(t,n){let r=[],o=[],i=V(t),s=Wa(n);for(let a of n.items)if(Fa(a,i))for(let l of a.installLocations){let c=M(l.path);if(!c||l.scope==="bundled"){c&&o.push(c);continue}let p=_e(c).find(h=>Wr(h,i,s));if(!p){o.push(c);continue}await ye.rm(p,{recursive:!0,force:!0}),r.push(c)}return{removedPaths:Z(r).sort(),skippedPaths:Z(o).sort()}}};function Fa(e,t){return[e.slug,e.id,e.name].some(n=>V(n)===t)}function Wa(e){return Z([M(e.locations.managedSkillsDir),M(e.locations.personalSkillsDir),...Object.values(e.locations.agentWorkspaceDirs).flatMap(t=>[O.join(t,"skills"),O.join(t,".agents","skills")])]).flatMap(t=>_e(t))}function Ja(e){let t=M(e.locations.managedSkillsDir)||"~/.openclaw/skills";return O.resolve(_e(qe(t))[0]??qe(t))}function qe(e){let t=e.trim();return t==="~"?qr():t.startsWith("~/")?O.join(qr(),t.slice(2)):t}function qr(){return M(process.env.OPENCLAW_HOST_HOME)||Gr.homedir()}function Va(e){let t=M(e.gateway.targetDir);return Z([..._e(qe(t)),...Object.values(e.catalog.locations.agentWorkspaceDirs).flatMap(n=>_e(O.join(qe(n),"skills",e.slug)))])}async function Ka(e){for(let t of e)if(await Fr(t))return t;return null}async function Fr(e){if(!e)return!1;try{return(await ye.stat(e)).isDirectory()}catch{return!1}}function _e(e){let t=e.replace(/\/+$/,""),n=[t],r=M(process.env.OPENCLAW_HOST_HOME);return r&&t.startsWith(`${r}/.openclaw`)&&n.push(O.join("/root/.openclaw",t.slice(`${r}/.openclaw`.length))),r&&t.startsWith("/root/.openclaw")&&n.push(O.join(r,".openclaw",t.slice(15))),Z(n)}function za(e,t,n){if(!O.isAbsolute(e)||!t)return!1;let r=O.resolve(e),o=O.resolve(n);return V(O.basename(r))===t&&r.startsWith(`${o}${O.sep}`)}function Qa(e,t,n){let r=Z(Object.values(n.locations.agentWorkspaceDirs).flatMap(o=>[O.join(o,"skills"),O.join(o,".agents","skills")])).flatMap(o=>_e(qe(o)));return Wr(e,t,r)}function Wr(e,t,n){if(!O.isAbsolute(e)||!t)return!1;let r=O.resolve(e);return V(O.basename(r))!==t?!1:n.some(o=>{if(!o||!O.isAbsolute(o))return!1;let i=O.resolve(o);return r===i||r.startsWith(`${i}${O.sep}`)})}import{existsSync as Jr,mkdirSync as Ya,readFileSync as Xa,renameSync as Za,writeFileSync as el}from"fs";import tl from"path";var Xt=class{constructor(t){this.document=t}document;get(){return structuredClone(this.document)}replace(t){this.document=structuredClone(t)}update(t){let n=t(this.get());return this.replace(n),this.get()}},Pt=class{constructor(t,n){this.filePath=t;let r=this.mergePersisted(n);this.inner=new Xt(r)}filePath;inner;get(){return this.inner.get()}replace(t){this.inner.replace(t),this.persist()}update(t){let n=this.inner.update(t);return this.persist(),n}mergePersisted(t){let n=this.readSlice();return n?{...t,pairing:{...t.pairing,...n.pairing??{}},raw:{...t.raw??{},pairing:n.relaySecret?{relaySecret:n.relaySecret}:t.raw?.pairing??void 0}}:t}readSlice(){if(!Jr(this.filePath))return null;try{let t=Xa(this.filePath,"utf8");return JSON.parse(t)}catch{return null}}persist(){let t=this.inner.get(),n={pairing:t.pairing,relaySecret:t.raw?.pairing?.relaySecret??null},r=tl.dirname(this.filePath);try{Jr(r)||Ya(r,{recursive:!0,mode:448});let o=`${this.filePath}.tmp`;el(o,JSON.stringify(n,null,2),{mode:384}),Za(o,this.filePath)}catch(o){let i=o instanceof Error?o.message:String(o);process.stderr.write(`[bridge-store] failed to persist pairing slice to ${this.filePath}: ${i}
|
|
10
|
+
`)}}};import Rc from"path";function nl(e){if(!e.startsWith("/"))throw new Error(`Route path must start with '/': ${e}`);return e.slice(1).split("/").filter(t=>t.length>0).map(t=>t.startsWith(":")?{kind:"param",name:t.slice(1)}:{kind:"literal",value:t})}function rl(e,t){let n=t.split("/").filter(o=>o.length>0);if(n.length!==e.length)return null;let r={};for(let o=0;o<e.length;o+=1){let i=e[o],s=n[o];if(i.kind==="literal"){if(i.value!==s)return null}else try{r[i.name]=decodeURIComponent(s)}catch{return null}}return r}var Zt=class{routes=[];register(t,n,r,o){this.routes.push({method:t,segments:nl(n),handler:r,remoteAllowed:o.remoteAllowed})}async dispatch(t,n,r={}){let o=r.source??"local";for(let i of this.routes){if(i.method!==n.method)continue;let s=rl(i.segments,n.path);if(!s)continue;if(o==="relay"&&!i.remoteAllowed)return{status:403,body:{ok:!1,error:{code:"forbidden",message:"endpoint not exposed over relay"}}};let a={...n,query:{...n.query??{},...s}};return i.handler(t,a)}return{status:404,body:{ok:!1,error:{code:"not_found",message:`No route for ${n.method} ${n.path}`}}}}inspectRoutes(){return this.routes.map(t=>({method:t.method,path:"/"+t.segments.map(n=>n.kind==="literal"?n.value:":"+n.name).join("/"),remoteAllowed:t.remoteAllowed}))}};function en(){return new Zt}var tn=class{tail=Promise.resolve();running=!1;pending=0;status(){return{running:this.running,pending:this.pending}}async run(t,n){this.pending+=1;let r=this.tail,o;this.tail=new Promise(i=>{o=i}),await r,this.pending-=1,this.running=!0;try{return await n()}finally{this.running=!1,o()}}},Q=new tn;function R(e){return{status:200,headers:{"content-type":"application/json; charset=utf-8"},body:e}}function I(e){let t=e.store.get();return{collectedAt:t.meta.collectedAt,schemaVersion:t.meta.schemaVersion}}function Y(e,t="bad_request"){return{status:400,headers:{"content-type":"application/json; charset=utf-8"},body:{ok:!1,error:{code:t,message:e}}}}function A(e){return Y(Ir(e),j(e)?e.code:"bad_request")}function nn(e,t="not_found"){return{status:404,headers:{"content-type":"application/json; charset=utf-8"},body:{ok:!1,error:{code:t,message:e}}}}function rn(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function At(e){return typeof e=="string"&&e.trim()?e.trim():null}function Ge(e){if(!e)return;let t=Number(e);return Number.isFinite(t)?t:void 0}var Vr=async e=>{let t=e.store.get();return R(k(Ke(t),I(e)))},Kr=async e=>R(k(e.store.get().capabilities,I(e))),zr=async e=>R(k(Mn(e.store.get()),I(e))),Qr=async e=>R(k(Qe(e.store.get()),I(e))),Yr=async e=>R(k(Tn(e.store.get()),I(e))),Xr=async e=>R(k(Xe(e.store.get()),I(e))),Zr=async e=>R(k(_n(e.store.get()),I(e))),eo=async e=>R(k(Ye(e.store.get()),I(e))),to=async e=>R(k(Ze(e.store.get()),I(e))),no=async(e,t)=>{let n=t.query?.sessionId;if(!n)return nn("Session id required");let r=Bn(e.store.get(),n);return r?R(k(r,I(e))):nn(`Session ${n} not found`,"session_not_found")},ro=async e=>{let t=e.operationGate.current(),n=Q.status();return R(k({...e.store.get().health,configMutationRunning:n.running,configMutationPending:n.pending,operationsBlocked:t!=null,operationBlockReason:t?.reason??null,operationBlockMessage:t?.message??null,operationBlockUntil:t?.until??null,operationBlockRetryAfterMs:t?.retryAfterMs??null},I(e)))},oo=async e=>R(k(ze(e.store.get()),I(e))),io=async e=>{let t=e.store.get();return R(k({qrPayload:t.pairing.qrPayload??null},I(e)))};import so from"qrcode";async function vt(e){let t=e.trim();if(!t)throw new Error("pairing QR payload is empty; register pairing before requesting qr.png");return so.toBuffer(t,{type:"png",errorCorrectionLevel:"M",margin:2,width:512,color:{dark:"#111827",light:"#FFFFFFFF"}})}async function ao(e){let t=e.trim();if(!t)throw new Error("pairing QR payload is empty; register pairing before rendering terminal QR");return so.toString(t,{type:"terminal",small:!0,errorCorrectionLevel:"L",margin:0})}function ol(e){if(!e||typeof e!="object"||Array.isArray(e))return{};let t=e,n={};return typeof t.displayName=="string"&&t.displayName.trim()&&(n.displayName=t.displayName.trim()),n}var lo=async(e,t)=>{let n=ol(t.body),r=await ht({store:e.store,pairingPort:e.pairingPort,nodeConfig:e.nodeConfig},n);return R(k(r,I(e)))},co=async e=>{let t=e.store.get().pairing.qrPayload??"";if(!t)return Y("No active pairing payload","no_pairing");let n=await vt(t);return{status:200,headers:{"content-type":"image/png","content-length":String(n.byteLength),"content-encoding":"base64"},body:n.toString("base64")}};function on(e){return!e||typeof e!="object"?null:e}var uo=async e=>{let t=await e.pairingClient.listPending();return R(k({items:t},I(e)))},go=async(e,t)=>{let n=on(t.body);if(!n)return A(new y("body is required"));let r=n.channel?.trim(),o=n.code?.trim();if(!r)return A(new y("channel is required"));if(!o)return A(new y("code is required"));try{let i=await e.pairingClient.approve({channel:r,code:o});return"error"in i?A(new y(`pairing code not found: ${o}`)):R(k(i,I(e)))}catch(i){if(j(i))return A(i);throw i}},po=async e=>{let t=await e.pairingClient.listApproved();return R(k({items:t},I(e)))},mo=async(e,t)=>{let n=on(t.body);if(!n)return A(new y("body is required"));let r=n.channel?.trim(),o=n.id?.trim();if(!r)return A(new y("channel is required"));if(!o)return A(new y("id is required"));try{let i=await e.pairingClient.revoke({channel:r,id:o,accountId:n.accountId?.trim()||void 0});return R(k(i,I(e)))}catch(i){if(j(i))return A(i);throw i}},fo=async(e,t)=>{let n=on(t.body);if(!n)return A(new y("body is required"));let r=n.channel?.trim(),o=n.code?.trim();if(!r)return A(new y("channel is required"));if(!o)return A(new y("code is required"));try{let i=await e.pairingClient.reject({channel:r,code:o});return"error"in i?A(new y(`pairing code not found: ${o}`)):R(k(i,I(e)))}catch(i){if(j(i))return A(i);throw i}};async function ho(e,t){let n=Ve(t.config,e.writePolicy);if(n.length>0)throw new y(`config apply contains disallowed paths: ${n.join(", ")}`,"config_path_denied");let r=await e.upstreamConfigPort.applyConfig(t);e.operationGate?.blockForRestart(r.restart);let o=new Date().toISOString();return e.store.update(i=>({...i,meta:{...i.meta,collectedAt:o},config:{path:r.path,hash:r.hash??i.config.hash,valid:!0,lastLoadedAt:o,snapshot:r.config},health:{lastSuccessAt:o,lastError:null}})),r}async function yo(e){return e.upstreamConfigPort.getSchema()}async function wo(e,t){return e.upstreamConfigPort.lookupSchema(t)}async function Be(e,t){let n=Ve(t.patch,e.writePolicy);if(n.length>0)throw new y(`config patch contains disallowed paths: ${n.join(", ")}`,"config_path_denied");let r=await e.upstreamConfigPort.patchConfig(t);e.operationGate?.blockForRestart(r.restart);let o=new Date().toISOString();return e.store.update(i=>({...i,meta:{...i.meta,collectedAt:o},config:{path:r.path,hash:r.hash??i.config.hash,valid:!0,lastLoadedAt:o,snapshot:r.config},health:{lastSuccessAt:o,lastError:null}})),r}function Et(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function bo(e,t){if(e!==void 0){if(typeof e!="string")throw new y(`${t} must be a string`);return e}}function So(e,t){if(typeof e!="string"||!e.trim())throw new y(`${t} is required`);return e}function Co(e){if(!Et(e))throw new y("Patch request body must be an object");if(!Et(e.patch))throw new y("patch must be an object");return{baseHash:So(e.baseHash,"baseHash"),patch:e.patch,note:bo(e.note,"note")}}function Ro(e){if(!Et(e))throw new y("Apply request body must be an object");if(!Et(e.config))throw new y("config must be an object");return{baseHash:So(e.baseHash,"baseHash"),config:e.config,note:bo(e.note,"note")}}var ko=async e=>{let t=await mt({nodeConfig:e.nodeConfig,store:e.store,upstreamConfigPort:e.upstreamConfigPort});return R(k(t,I(e)))},Po=async e=>{let t=await yo({upstreamConfigPort:e.upstreamConfigPort});return R(k(t,I(e)))},Ao=async(e,t)=>{let n=t.query?.path;if(!n)return Y("path query parameter is required");let r=await wo({upstreamConfigPort:e.upstreamConfigPort},n);return R(k(r,I(e)))},vo=async(e,t)=>{let n;try{n=Co(t.body)}catch(r){return A(r)}return Q.run("config:patch",async()=>{let r;try{r=await Be({store:e.store,upstreamConfigPort:e.upstreamConfigPort,writePolicy:e.configWritePolicy,operationGate:e.operationGate},n)}catch(o){if(j(o))return A(o);throw o}return R(k(r,I(e)))})},Eo=async(e,t)=>{let n;try{n=Ro(t.body)}catch(r){return A(r)}return Q.run("config:apply",async()=>{let r;try{r=await ho({store:e.store,upstreamConfigPort:e.upstreamConfigPort,writePolicy:e.configWritePolicy,operationGate:e.operationGate},n)}catch(o){if(j(o))return A(o);throw o}return R(k(r,I(e)))})};import{chmod as al,mkdir as xt,readFile as No,readdir as ll,rm as He,writeFile as gn}from"fs/promises";import{homedir as Tt,tmpdir as cl}from"os";import E from"path";var Io="<!-- TEAM-FLOW:start managed-by=tnyma-web -->",sn="<!-- TEAM-FLOW:end -->";function il(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n")}function xo(e){let t=new Map;for(let n of e)for(let r of n.agents)t.set(r,n.id);return t}function To(e,t){return e.mode==="serial"||e.mode==="parallel"?e.mode:t<=1||(e.waitFor??[]).some(r=>r.endsWith(":*"))?"serial":"parallel"}function Oo(e){let t=xo(e),n=["flowchart TD"];e.forEach((r,o)=>{let i=il(`${r.label}
|
|
11
|
+
mode: ${To(r,o)}
|
|
12
|
+
agents: ${r.agents.join(", ")}`);n.push(` ${r.id}["${i}"]`)});for(let r of e){let o=r.waitFor??[];if(o.length===0)continue;let i=new Set;for(let s of o)s.endsWith(":*")?i.add(s.slice(0,-2)):t.has(s)&&i.add(t.get(s));for(let s of i)s!==r.id&&n.push(` ${s} --> ${r.id}`)}return`${n.join(`
|
|
13
|
+
`)}
|
|
14
|
+
`}function Do(e){let t=xo(e),n=[];return e.forEach((r,o)=>{let i=o+1,s=`mode: ${To(r,o)}`,a=`agents: ${r.agents.join(", ")}`,l=r.waitFor??[],c="";if(l.length>0){let d=l.filter(w=>w.endsWith(":*")),p=l.filter(w=>!w.endsWith(":*")),h=d.map(w=>`all of ${w.slice(0,-2)}`),m=p.map(w=>{let S=t.get(w);return S?`${w} (from ${S})`:w}),f=[...h,...m];h.length===1&&m.length===0?c=` \u2014 waits for ${h[0]}`:c=` \u2014 waits for: ${f.join(", ")}`}n.push(`${i}. **${r.label}** \u2014 ${s}; ${a}${c}`)}),`${n.join(`
|
|
15
|
+
`)}
|
|
16
|
+
`}function sl(e){let t=0;if(e.startsWith(`---
|
|
17
|
+
`)){let n=e.indexOf(`
|
|
18
|
+
---`,4);n!==-1&&(t=n+4,e[t]===`
|
|
19
|
+
`&&(t+=1))}for(;e[t]===`
|
|
20
|
+
`;)t+=1;if(e.startsWith("# ",t)){let n=e.indexOf(`
|
|
21
|
+
`,t);for(t=n===-1?e.length:n+1;e[t]===`
|
|
22
|
+
`;)t+=1}return t}function _o(e,t,n){let r=["## Team Flow (managed)","","### Execution Rules","","- `mode: serial`: start this step only after all listed upstream step agents have finished.","- `mode: parallel`: start this step after the explicitly listed predecessor agent(s) have finished; do not wait for unrelated agents in earlier steps.","- Agents listed in the same step may run concurrently. Collect their outputs before unblocking any downstream serial step.","","### Steps","",n.trimEnd(),"","```mermaid",t.trimEnd(),"```","","_This block is regenerated whenever the team config changes. Edits inside the start/end markers will be lost._"].join(`
|
|
23
|
+
`),o=`${Io}
|
|
24
|
+
${r}
|
|
25
|
+
${sn}`,i=e.indexOf(Io),s=e.indexOf(sn),a;if(i>=0&&s>i){let l=e.slice(0,i),c=e.slice(s+sn.length);a=`${l}${o}${c}`}else{let l=sl(e),c=e.slice(0,l),d=e.slice(l),p=c.length>0&&!c.endsWith(`
|
|
26
|
+
|
|
27
|
+
`)?c.endsWith(`
|
|
28
|
+
`)?`
|
|
29
|
+
`:`
|
|
30
|
+
|
|
31
|
+
`:"",h=d.length>0&&!d.startsWith(`
|
|
32
|
+
`)?`
|
|
33
|
+
|
|
34
|
+
`:d.startsWith(`
|
|
35
|
+
|
|
36
|
+
`)?"":`
|
|
37
|
+
`;a=`${c}${p}${o}${h}${d}`}return a.endsWith(`
|
|
38
|
+
`)?a:`${a}
|
|
39
|
+
`}var dl=/^[a-z0-9][a-z0-9_-]{0,63}$/,ul=new Set(["main"]);function ee(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function q(e,t){if(e==null)return;if(typeof e!="string")throw new y(`${t} must be a string`);return e.trim()||void 0}function me(e,t){let n=q(e,t);if(!n)throw new y(`${t} is required`);return n}function ln(e,t){if(e!=null){if(typeof e!="boolean")throw new y(`${t} must be a boolean`);return e}}function be(e,t){if(e==null)return;if(!Array.isArray(e))throw new y(`${t} must be an array`);let n=e.map(r=>{if(typeof r!="string")throw new y(`${t} items must be strings`);return r.trim()}).filter(Boolean);return n.length?n:void 0}function gl(e,t="id"){let n=me(e,t).toLowerCase();if(!dl.test(n)||ul.has(n))throw new y(`${t} is not a valid agent id`,"invalid_agent_id");return n}function pl(e){if(e==null)return;if(typeof e=="string")return{primary:e.trim()};if(!ee(e))throw new y("model must be a string or object");let t=q(e.primary,"model.primary"),n=be(e.fallbacks,"model.fallbacks");return t||n?{...t?{primary:t}:{},...n?{fallbacks:n}:{}}:void 0}function ml(e){if(e==null)return;if(!ee(e))throw new y("sandbox must be an object");let t=q(e.mode,"sandbox.mode"),n=q(e.workspaceAccess,"sandbox.workspaceAccess"),r=q(e.sessionToolsVisibility,"sandbox.sessionToolsVisibility");return{...t?{mode:t}:{},...n?{workspaceAccess:n}:{},...r?{sessionToolsVisibility:r}:{}}}function fl(e){if(e==null)return;if(!ee(e))throw new y("tools must be an object");let t=q(e.profile,"tools.profile"),n=be(e.allow,"tools.allow"),r=be(e.alsoAllow,"tools.alsoAllow"),o=be(e.deny,"tools.deny"),i=ln(ee(e.elevated)?e.elevated.enabled:void 0,"tools.elevated.enabled");return{...t?{profile:t}:{},...n?{allow:n}:{},...r?{alsoAllow:r}:{},...o?{deny:o}:{},...i!==void 0?{elevated:{enabled:i}}:{}}}function Ot(e,t="agent"){if(!ee(e))throw new y(`${t} must be an object`);let n=gl(e.id,`${t}.id`),r=me(e.name,`${t}.name`),o=me(e.workspace,`${t}.workspace`);return{id:n,name:r,workspace:o,agentDir:q(e.agentDir,`${t}.agentDir`),copyPortableAuthProfiles:ln(e.copyPortableAuthProfiles,`${t}.copyPortableAuthProfiles`),model:pl(e.model),runtimeId:q(e.runtimeId,`${t}.runtimeId`),thinkingDefault:q(e.thinkingDefault,`${t}.thinkingDefault`),reasoningDefault:q(e.reasoningDefault,`${t}.reasoningDefault`),skills:be(e.skills,`${t}.skills`),memorySearchEnabled:ln(e.memorySearchEnabled,`${t}.memorySearchEnabled`),sandbox:ml(e.sandbox),tools:fl(e.tools),files:hl(e.files,`${t}.files`),channelBinding:Lo(e.channelBinding,`${t}.channelBinding`)}}function hl(e,t){if(e!=null){if(!Array.isArray(e))throw new y(`${t} must be an array`);return e.map((n,r)=>{if(!ee(n))throw new y(`${t}.${r} must be an object`);return{path:me(n.path,`${t}.${r}.path`),content:me(n.content,`${t}.${r}.content`)}})}}function Lo(e,t){if(e==null)return;if(!ee(e))throw new y(`${t} must be an object`);let n=me(e.channel,`${t}.channel`),r=q(e.accountId,`${t}.accountId`);return{channel:n,...r?{accountId:r}:{},comment:q(e.comment,`${t}.comment`),auth:ee(e.auth)?{appId:q(e.auth.appId,`${t}.auth.appId`),secretRef:q(e.auth.secretRef,`${t}.auth.secretRef`),appSecret:q(e.auth.appSecret,`${t}.auth.appSecret`)}:void 0}}function Dt(e){if(!ee(e))throw new y("team request body must be an object");let t=Ot(e.controller,"controller"),r=(Array.isArray(e.members)?e.members:[]).map((s,a)=>Ot(s,`members.${a}`));if(r.length===0)throw new y("members must contain at least one agent","invalid_agent_team");let o=new Set;for(let s of r){if(s.id===t.id||o.has(s.id))throw new y("team agent ids must be unique","duplicate_agent_id");o.add(s.id)}let i=yl(e.flow,"flow");return wl(i,t,r),{controller:t,members:r,flow:i,channelBinding:Lo(e.channelBinding,"channelBinding")}}function yl(e,t){if(e!=null){if(!Array.isArray(e))throw new y(`${t} must be an array`);return e.map((n,r)=>{if(!ee(n))throw new y(`${t}.${r} must be an object`);let o=q(n.mode,`${t}.${r}.mode`);if(o!==void 0&&o!=="serial"&&o!=="parallel")throw new y(`${t}.${r}.mode must be serial or parallel`,"invalid_team_flow_mode");return{id:me(n.id,`${t}.${r}.id`),label:me(n.label,`${t}.${r}.label`),mode:o,note:q(n.note,`${t}.${r}.note`),agents:be(n.agents,`${t}.${r}.agents`)??[],waitFor:be(n.waitFor,`${t}.${r}.waitFor`)}})}}function wl(e,t,n){if(!e||e.length===0)return;let r=new Set(e.map(i=>i.id)),o=new Set([t.id,...n.map(i=>i.id)]);for(let i of e){for(let s of i.agents)if(!o.has(s))throw new y(`flow entry ${i.id} references unknown agent ${s}`,"unknown_agent_in_flow");for(let s of i.waitFor??[])if(s.endsWith(":*")){let a=s.slice(0,-2);if(!r.has(a))throw new y(`flow waitFor ${s} references unknown step ${a}`,"unknown_step_in_waitfor")}else if(!o.has(s))throw new y(`flow waitFor ${s} references unknown agent ${s}`,"unknown_agent_in_waitfor")}}function cn(e,t){return{id:e.id,name:e.name,workspace:e.workspace,...e.agentDir?{agentDir:e.agentDir}:{},...e.model?{model:e.model}:{},...e.runtimeId?{agentRuntime:{id:e.runtimeId}}:{},...e.thinkingDefault?{thinkingDefault:e.thinkingDefault}:{},...e.reasoningDefault?{reasoningDefault:e.reasoningDefault}:{},...e.skills?{skills:e.skills}:{},...e.memorySearchEnabled!==void 0?{memorySearch:{enabled:e.memorySearchEnabled}}:{},...e.sandbox?{sandbox:e.sandbox}:{},...e.tools?{tools:e.tools}:{},...t?.length?{subagents:{allowAgents:t,requireAgentId:!0,...e.model?{model:e.model}:{}}}:{}}}function dn(e,t){if(!t)return null;let n=t.accountId??e;return{type:"route",agentId:e,comment:t.comment??`tnyma-web:${e}`,match:{channel:t.channel,accountId:n}}}function bl(e,t){if(!e)return;let n=/^env:([A-Z][A-Z0-9_]{0,127})$/.exec(e);if(n)return{source:"env",provider:"env",id:n[1]};let r=/^file:(.+)$/.exec(e);if(r?.[1]?.trim())return{source:"file",provider:"file",id:r[1].trim()};let o=/^exec:([a-z][a-z0-9_-]{0,63})$/.exec(e);if(o)return{source:"exec",provider:"exec",id:o[1]};throw new y(`${t} must use env:NAME, file:path, or exec:id`,"invalid_secret_ref")}function un(e,t){if(!t?.auth||t.channel!=="feishu")return null;let n=t.auth.appId,r=t.auth.appSecret?.trim()||void 0,o=r?void 0:bl(t.auth.secretRef,"channelBinding.auth.secretRef"),i=r??o;if(!n&&!i)return null;let s=t.accountId??e;return{feishu:{enabled:!0,defaultAccount:s,accounts:{[s]:{enabled:!0,name:s,...n?{appId:n}:{},...i?{appSecret:i}:{},domain:"feishu",groupPolicy:"open"}}}}}function Uo(e){let t=[dn(e.id,e.channelBinding)].filter(Boolean),n=un(e.id,e.channelBinding);return{agents:{list:[cn(e)]},...t.length?{bindings:t}:{},...n?{channels:n}:{}}}function Sl(e){return!(e instanceof U)||e.code!=="INVALID_REQUEST"?null:new y(e.message,"openclaw_invalid_request")}function Cl(e){return e instanceof U&&e.code==="INVALID_REQUEST"&&/agent\b.*not found|not found\b.*agent|agent\b.*does not exist/i.test(e.message)}var Rl=[1e3,2e3,3e3,5e3,8e3,13e3,21e3];function kl(e){return!(e instanceof U)||e.code==="INVALID_REQUEST"?!1:["transport_unavailable","transport_closed","connection_closed","connect_timeout","request_timeout","send_failed"].includes(e.code??"")?!0:/gateway (connection|transport|failed|closed|not connected|timeout|unavailable)|connect timeout/i.test(e.message)}async function Pl(e){await new Promise(t=>setTimeout(t,e))}async function an(e,t){for(let n=0;;n+=1)try{return await t()}catch(r){let o=Rl[n];if(o===void 0||!kl(r))throw r;e.operationGate.blockForError(r),await Pl(o)}}function jo(e){let t=e.members.map(l=>l.id),n=e.controller.channelBinding??e.channelBinding,r=[],o=[],i=dn(e.controller.id,n);i&&r.push(i);let s=un(e.controller.id,n);s&&o.push(s);for(let l of e.members){let c=l.channelBinding;if(!c)continue;let d=dn(l.id,c);d&&r.push(d);let p=un(l.id,c);p&&o.push(p)}let a=Al(o);return{agents:{list:[cn(e.controller,t),...e.members.map(l=>cn(l))]},...r.length?{bindings:r}:{},...a?{channels:a}:{}}}function Al(e){if(e.length===0)return null;let t={};for(let n of e)for(let[r,o]of Object.entries(n)){if(!o||typeof o!="object"||Array.isArray(o))continue;let i=t[r]??{},s=o;for(let[a,l]of Object.entries(s))if(a==="accounts"&&l&&typeof l=="object"&&!Array.isArray(l)){let c=i.accounts??{};i.accounts={...c,...l}}else i[a]===void 0&&(i[a]=l);t[r]=i}return t}function $o(e,t=[]){return{patch:e,fileWrites:t?.map(n=>n.path)??[]}}function ne(e){return e==="~"?Tt():e.startsWith("~/")?E.join(Tt(),e.slice(2)):e}var vl=["/etc","/var/run","/proc","/sys","/dev","/boot","/lib","/usr/lib","/usr/bin","/sbin","/root"];function qo(e,t){let n=E.resolve(ne(e));for(let i of vl)if(n===i||n.startsWith(`${i}/`))throw new y(`${t} is on the system path denylist: ${i}`,"invalid_agent_path");let r=[Tt(),"/home","/Users","/home/node"];if(process.env.OPENCLAW_HOST_HOME&&r.push(E.resolve(process.env.OPENCLAW_HOST_HOME)),process.env.OPENCLAW_STATE_DIR?r.push(E.resolve(process.env.OPENCLAW_STATE_DIR)):r.push(E.resolve(cl())),!r.some(i=>i?n===i||n.startsWith(`${i}/`):!1))throw new y(`${t} must live under the user's home or OpenClaw state dir`,"invalid_agent_path")}function Go(e,t){qo(e,"workspace");let n=E.resolve(ne(e)),r=E.resolve(E.isAbsolute(ne(t))?ne(t):E.join(n,t)),o=E.relative(n,r);if(o.startsWith("..")||E.isAbsolute(o))throw new y("file path must stay inside the agent workspace","invalid_workspace_file_path");return r}async function Ho(e){let t=[];for(let n of e){n.agentDir&&(qo(n.agentDir,"agentDir"),await xt(E.resolve(ne(n.agentDir)),{recursive:!0,mode:448}));for(let r of n.files??[]){let o=Go(n.workspace,r.path);await xt(E.dirname(o),{recursive:!0}),await gn(o,r.content.endsWith(`
|
|
40
|
+
`)?r.content:`${r.content}
|
|
41
|
+
`,"utf8"),t.push(o)}}return t}function Fo(e,t){if(!e)return!1;let n=t.toLowerCase(),r=T(e.agents);return(Array.isArray(r?.list)?r.list:[]).some(i=>{let s=T(i);return H(s?.id)?.toLowerCase()===n})}function pn(e,t){if(!e)return null;let n=T(e.agents),r=Array.isArray(n?.list)?n.list:[];for(let o of r){let i=T(o);if(H(i?.id)?.toLowerCase()!==t)continue;let s=H(i?.agentDir);return s?E.resolve(ne(s)):null}return null}function El(e,t){if(t.agentDir)return E.resolve(ne(t.agentDir));let n=pn(e.snapshot,t.id);return n||E.join(Fe(e),"agents",t.id,"agent")}function Il(e){return pn(e.snapshot,"main")??E.join(Fe(e),"agents","main","agent")}function xl(e){let t=T(e);if(!t||t.copyToAgents===!1)return!1;let n=H(t.type)??H(t.mode);return n==="oauth"?t.copyToAgents===!0:n==="api_key"||n==="token"}function Tl(e){let t=T(e.profiles);if(!t)return null;let n=Object.fromEntries(Object.entries(t).filter(([,r])=>xl(r)));return Object.keys(n).length===0?null:{version:typeof e.version=="number"&&Number.isFinite(e.version)?e.version:1,profiles:n}}async function Bo(e,t){let n;try{n=await No(e,"utf8")}catch(r){if(r.code==="ENOENT")return null;throw r}try{let r=JSON.parse(n);if(!ee(r))throw new Error("expected object");return r}catch{throw new y(`${t} auth-profiles.json is invalid`,"invalid_auth_profiles")}}function Ol(e,t){return t.copyPortableAuthProfiles===!1?!1:!Fo(e.snapshot,t.id)}function Dl(e,t){let n=T(e.profiles)??{},r=T(t?.profiles)??{};return{version:Math.max(typeof e.version=="number"?e.version:1,typeof t?.version=="number"?t.version:1),profiles:{...n,...r}}}async function Wo(e,t){let n=t.filter(a=>Ol(e,a));if(n.length===0)return[];let r=E.join(Il(e),"auth-profiles.json"),o=await Bo(r,"main");if(!o)return[];let i=Tl(o);if(!i)return[];let s=[];for(let a of n){let l=El(e,a),c=E.join(l,"auth-profiles.json");if(E.resolve(c)===E.resolve(r))continue;let d=await Bo(c,a.id),p=Dl(i,d);await xt(l,{recursive:!0,mode:448}),await gn(c,`${JSON.stringify(p,null,2)}
|
|
42
|
+
`,{encoding:"utf8",mode:384}),await al(c,384).catch(()=>{}),s.push(c)}return s}function _l(e){let t=e.replace(/[\r\n ]+/g," ").trim();return t.length>0?t:"Team Controller"}async function Bl(e,t){if(!t||t.length===0)return null;let n=Go(e.workspace,"AGENTS.md"),r="";try{r=await No(n,"utf8")}catch(a){if(a.code!=="ENOENT")throw a;r=`# ${_l(e.name)}
|
|
43
|
+
`}let o=Oo(t),i=Do(t),s=_o(r,o,i);return await xt(E.dirname(n),{recursive:!0}),await gn(n,s,"utf8"),n}async function mn(e,t,n){let r=await e.upstreamConfigPort.getSnapshot();if(!r.hash)throw new y("Current OpenClaw config hash is unavailable","config_hash_unavailable");return Be({store:e.store,upstreamConfigPort:e.upstreamConfigPort,writePolicy:e.configWritePolicy,operationGate:e.operationGate},{baseHash:r.hash,patch:t,note:n})}var Jo=async(e,t)=>{try{let n=Ot(t.body);return R(k($o(Uo(n),n.files)))}catch(n){return A(n)}},Vo=async(e,t)=>{let n;try{n=Ot(t.body)}catch(r){return A(r)}return Q.run(`agent:create:${n.id}`,async()=>{try{let r=await e.upstreamConfigPort.getSnapshot(),o=Uo(n),i=await mn(e,o,`tnyma-web:create-agent:${n.id}`),s=await Ho([n]);s.push(...await Wo(r,[n])),await e.refreshData();let a={agentIds:[n.id],patch:o,config:i,fileWrites:s};return R(k(a,I(e)))}catch(r){if(j(r))return A(r);throw r}})},Ko=async(e,t)=>{let n=t.query?.agentId?.trim();if(!n)return A(new y("agentId is required"));let r=t.query?.deleteFiles==="true";return Q.run(`agent:delete:${n}`,async()=>{try{await e.operationGate.waitUntilReady();let o=await an(e,()=>e.upstreamConfigPort.getSnapshot()),i=o.snapshot??e.store.get().config.snapshot,s=n.toLowerCase(),a=Fo(i,s),l=Ll(i,s),c=Fl(i,s),d=l.patch||c?{...l.patch??{},...c??{}}:null,p={ok:!0,agentId:n,removedBindings:0};if(a)try{p=await an(e,()=>e.agentClient.deleteAgent({agentId:n,deleteFiles:r}))}catch(h){if(!Cl(h))throw h}d&&(await an(e,()=>mn(e,d,`tnyma-web:delete-agent-cleanup:${n}`)),await Ul(o,l.removedAccounts)),r&&(a?await Gl(o,s):await Hl(o,s));try{await e.refreshData()}catch(h){e.operationGate.blockForError(h)}return R(k(p,I(e)))}catch(o){if(j(o))return A(o);let i=Sl(o);if(i)return A(i);throw o}})};function T(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function H(e){return typeof e=="string"&&e.trim()?e.trim():null}function It(e){if(!Array.isArray(e))return null;let t=[];for(let n of e)typeof n=="string"&&t.push(n);return t}function Mo(e){return JSON.stringify([e.channel,e.accountId])}function zo(e){let t=T(e.match);if(!t)return null;let n=H(t.channel),r=H(t.accountId);return!n||!r?null:{channel:n,accountId:r}}function Ml(e,t){if(!e)return[];let n=new Map,r=Array.isArray(e.bindings)?e.bindings:[];for(let i of r){let s=T(i);if(!s||H(s.agentId)?.toLowerCase()!==t)continue;let l=zo(s);l&&n.set(Mo(l),l)}let o=T(e.channels);if(o)for(let[i,s]of Object.entries(o)){let a=T(s),l=T(a?.accounts);if(l)for(let c of Object.keys(l)){if(c.toLowerCase()!==t)continue;let d={channel:i,accountId:c};n.set(Mo(d),d)}}return[...n.values()]}function Nl(e,t,n){let r=Array.isArray(e.bindings)?e.bindings:[];for(let o of r){let i=T(o);if(!i)continue;let s=H(i.agentId)?.toLowerCase();if(!s||s===t)continue;let a=zo(i);if(a?.channel===n.channel&&a.accountId===n.accountId)return!0}return!1}function Ll(e,t){if(!e)return{patch:null,removedAccounts:[]};let n=T(e.channels);if(!n)return{patch:null,removedAccounts:[]};let r=new Map,o=[];for(let s of Ml(e,t)){if(Nl(e,t,s))continue;let a=T(n[s.channel]);if(!a)continue;let l=T(a.accounts),c=!!l&&Object.prototype.hasOwnProperty.call(l,s.accountId),d=a.defaultAccount===s.accountId;if(!c&&!d)continue;let p=r.get(s.channel)??new Set;p.add(s.accountId),r.set(s.channel,p),o.push(s)}let i={};for(let[s,a]of r.entries()){let l=T(n[s]);if(!l)continue;let c={},d=T(l.accounts),p=d?Object.keys(d):[],h=p.filter(w=>!a.has(w)),m=p.filter(w=>a.has(w));m.length>0&&(c.accounts=h.length>0?Object.fromEntries(m.map(w=>[w,null])):null);let f=H(l.defaultAccount);f&&a.has(f)&&(c.defaultAccount=h[0]??null),Object.keys(c).length>0&&(i[s]=c)}return Object.keys(i).length===0?{patch:null,removedAccounts:[]}:{patch:{channels:i},removedAccounts:o}}function Fe(e){let t=T(e.runtimePaths),n=H(t?.stateDir);if(n)return n;let r=H(e.path);return r?E.dirname(r):E.join(Tt(),".openclaw")}async function Ul(e,t){if(t.length===0)return;let n=Fe(e);await Promise.all(t.filter(r=>r.channel==="feishu").map(r=>He(E.join(n,"credentials",`feishu-${r.accountId}-allowFrom.json`),{force:!0})))}var jl=new Set([".DS_Store",".localized","Thumbs.db","desktop.ini"]);function $l(e,t){let n=E.relative(e,t);return n===""||!!n&&!n.startsWith("..")&&!E.isAbsolute(n)}function ql(e,t,n){if(!e)return!1;let r=T(e.agents),o=Array.isArray(r?.list)?r.list:[];for(let i of o){let s=T(i);if(!s)continue;let a=H(s.id)?.toLowerCase();if(!(!a||a===t))for(let l of["agentDir","workspace"]){let c=H(s[l]);if(c&&$l(n,E.resolve(ne(c))))return!0}}return!1}async function Gl(e,t){let n=E.resolve(ne(Fe(e))),r=E.join(n,"agents",t),o=E.join(r,"agent"),i=pn(e.snapshot,t)??o;if(E.resolve(i)!==o||ql(e.snapshot,t,r))return;let s;try{s=await ll(r,{withFileTypes:!0})}catch(l){if(l.code==="ENOENT")return;throw l}s.every(l=>l.isFile()&&jl.has(l.name))&&await He(r,{recursive:!0,force:!0})}async function Hl(e,t){let n=E.resolve(ne(Fe(e)));await Promise.all([He(E.join(n,"agents",t),{recursive:!0,force:!0}),He(E.join(n,"workspace",t),{recursive:!0,force:!0}),He(E.join(n,"agent-workspaces",t),{recursive:!0,force:!0})])}function Fl(e,t){if(!e)return null;let n=T(e.agents);if(!n)return null;let r=[],o=Array.isArray(n.list)?n.list:[];for(let a of o){let l=T(a);if(!l)continue;let c=typeof l.id=="string"?l.id:null;if(!c||c===t)continue;let d=T(l.subagents);if(!d)continue;let p=It(d.allowAgents),h=It(d.denyAgents),m={};p&&p.includes(t)&&(m.allowAgents=p.filter(f=>f!==t)),h&&h.includes(t)&&(m.denyAgents=h.filter(f=>f!==t)),Object.keys(m).length>0&&r.push({id:c,subagents:m})}let i=null,s=T(n.defaults);if(s){let a=T(s.subagents);if(a){let l=It(a.allowAgents),c=It(a.denyAgents),d={};l&&l.includes(t)&&(d.allowAgents=l.filter(p=>p!==t)),c&&c.includes(t)&&(d.denyAgents=c.filter(p=>p!==t)),Object.keys(d).length>0&&(i={subagents:d})}}return r.length===0&&!i?null:{agents:{...r.length>0?{list:r}:{},...i?{defaults:i}:{}}}}var Qo=async(e,t)=>{try{let n=Dt(t.body),r=[n.controller,...n.members].flatMap(o=>o.files??[]);return R(k($o(jo(n),r)))}catch(n){return A(n)}};async function fn(e,t,n={}){return Q.run(`agent-team:create:${t.controller.id}`,async()=>{let{onProgress:r}=n;r?.({action:"progress",level:"info",step:"team_register/start",message:`\u5F00\u59CB\u6CE8\u518C\u56E2\u961F\uFF081 \u4E3B\u63A7 + ${t.members.length} \u6210\u5458\uFF09...`});let o=await e.upstreamConfigPort.getSnapshot(),i=jo(t),s=await mn(e,i,`tnyma-web:create-agent-team:${t.controller.id}`),a=[t.controller.id,...t.members.map(h=>h.id)];r?.({action:"progress",level:"success",step:"team_register/openclaw",message:`\u5DF2\u5199\u5165 openclaw.json\uFF08agents=${a.length}, bindings=${t.channelBinding||t.controller.channelBinding?1:0}\uFF09`});let l=await Ho([t.controller,...t.members]),c=[...l],d=await Wo(o,[t.controller,...t.members]);c.push(...d),l.length>0&&r?.({action:"progress",level:"success",step:"team_register/files",message:`\u5199\u5165 agent \u5DE5\u4F5C\u533A\u6587\u4EF6 ${l.length} \u4E2A`}),d.length>0&&r?.({action:"progress",level:"success",step:"team_register/auth_profiles",message:`\u590D\u5236\u53EF\u5171\u4EAB auth profile ${d.length} \u4EFD`});let p=null;try{let h=await Bl(t.controller,t.flow);h&&(c.push(h),r?.({action:"progress",level:"success",step:"team_register/agents_md",message:`\u5DF2\u5199\u5165 AGENTS.md\uFF08${h}\uFF09`}))}catch(h){p=h instanceof Error?h.message:String(h),r?.({action:"progress",level:"warn",step:"team_register/agents_md",message:`AGENTS.md \u5199\u5165\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD agent \u6CE8\u518C\uFF09: ${p}`})}return await e.refreshData(),p&&console.warn(`[agents-config] team-flow AGENTS.md write failed for controller ${t.controller.id}: ${p}`),{agentIds:a,patch:i,config:s,fileWrites:c}})}var Yo=async(e,t)=>{let n;try{n=Dt(t.body)}catch(r){return A(r)}try{let r=await fn(e,n);return R(k(r,I(e)))}catch(r){if(j(r))return A(r);throw r}};function Xo(e){if(!e||typeof e!="object"||Array.isArray(e))throw new Error("request body must be an object");let t=e,n=At(t.message);if(!n)throw new Error("message is required");return{agentId:At(t.agentId),message:n,waitForReply:t.waitForReply===!0,waitTimeoutMs:rn(t.waitTimeoutMs),historyLimit:rn(t.historyLimit),idempotencyKey:At(t.idempotencyKey)??void 0}}function Zo(e){return async(t,n)=>{let r;try{r=Xo(n.body)}catch(s){return Y(s instanceof Error?s.message:String(s))}let o=r.idempotencyKey??n.id??void 0,i;try{i=await t.chatService.send(e,{...r,idempotencyKey:o})}catch(s){if(j(s))return A(s);throw s}return R(k(i,I(t)))}}function ei(e){return async(t,n)=>{let r;try{r=await t.chatService.history(e,{agentId:n.query?.agentId??null,limit:Ge(n.query?.limit),maxChars:Ge(n.query?.maxChars)})}catch(o){if(j(o))return A(o);throw o}return R(k(r,I(t)))}}function ti(e){return async(t,n)=>{let r;try{r=Xo(n.body)}catch(m){return Y(m instanceof Error?m.message:String(m))}let o=new AbortController,i=[],s=null,a=!1,l=m=>{if(!a){if(s){s({value:m,done:!1}),s=null;return}i.push(m)}},c=()=>{a||(a=!0,s&&(s({value:void 0,done:!0}),s=null))},d=setInterval(()=>{a||l(`: ping
|
|
44
|
+
|
|
45
|
+
`)},15e3),p=m=>{l(`data: ${JSON.stringify(m)}
|
|
46
|
+
|
|
47
|
+
`)};return(async()=>{try{await t.chatService.stream(e,r,p,o.signal)}catch(m){p({type:"done",status:"error",errorMessage:m instanceof Error?m.message:String(m)})}finally{clearInterval(d),c()}})(),{status:200,headers:{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-cache, no-transform",connection:"keep-alive","x-accel-buffering":"no"},stream:{[Symbol.asyncIterator](){return{next(){return i.length>0?Promise.resolve({value:i.shift(),done:!1}):a?Promise.resolve({value:void 0,done:!0}):new Promise(m=>{s=m})},return(){return o.abort(),c(),Promise.resolve({value:void 0,done:!0})}}}}}}}var ni=Zo("direct"),ri=Zo("group"),oi=ei("direct"),ii=ei("group"),si=ti("direct"),ai=ti("group");function Bt(e){let t=e.store.get();return{collectedAt:t.meta.collectedAt,schemaVersion:t.meta.schemaVersion}}async function li(e,t={}){let n=await e.skillCatalogService.read(e.store.get(),t);return k(kt(n),Bt(e))}async function ci(e,t){let n=yn(t.slug),r=t.scope??"global",o=gi(t.agentIds);if(r==="agents"&&o.length===0)throw new y("agentIds are required when installing a skill to agents");let i=e.store.get(),s=await e.skillCatalogService.installedSkillSlugs(i),a=await e.skillCatalogService.read(i),l={},c=null;try{l=await e.skillCatalogService.installClawHubSkill({slug:n,version:t.version,force:t.force})}catch(m){if(r!=="global")throw m;c=m}let d=r==="global"?await Wl(e,{slug:n,fallbackCatalog:a,gateway:l,force:t.force,originalError:c}):null,p=await wi(e,Vl(i.config.snapshot,{slug:n,scope:r,agentIds:o,existingSkillSlugs:s}),`Install ClawHub skill ${n}`),h=await bn(e,a,{type:"install",slug:n,scope:r,agentIds:o});return k({slug:n,message:r==="global"?`Installed ${n} globally`:`Installed ${n} to agents`,gateway:{...Se(l),...c?{gatewayInstallError:Jl(c)}:{},...d?{globalInstall:d}:{}},config:bi(p),catalog:h},Bt(e))}async function Wl(e,t){try{return await e.skillCatalogService.promoteClawHubSkillToManaged({slug:t.slug,catalog:t.fallbackCatalog,gateway:t.gateway,force:t.force})}catch(n){throw t.originalError?t.originalError:n}}async function di(e,t){let n=yn(t.slug),r=await e.skillCatalogService.read(e.store.get()),o=await e.skillCatalogService.updateClawHubSkill({slug:n}),i=await bn(e,r,{type:"update",slug:n});return k({slug:n,message:`Updated ${n}`,gateway:Se(o),config:null,catalog:i},Bt(e))}async function ui(e,t){let n=yn(t.slug),r=t.scope??"all",o=gi(t.agentIds);if(r==="agents"&&o.length===0)throw new y("agentIds are required when uninstalling a skill from agents");let i=e.store.get(),s=await e.skillCatalogService.installedSkillSlugs(i),a=await e.skillCatalogService.read(i),l=await wi(e,Kl(i.config.snapshot,{slug:n,scope:r,agentIds:o,existingSkillSlugs:s}),`Uninstall ClawHub skill ${n}`),c=await e.skillCatalogService.removeSkillInstallations(n,a),d=await bn(e,a,{type:"uninstall",slug:n,scope:r,agentIds:o});return k({slug:n,message:`Removed ${n} assignment`,gateway:Se(c),config:bi(l),catalog:d},Bt(e))}function yn(e){let t=typeof e=="string"?e.trim():"";if(!t)throw new y("skill slug is required");return t}function gi(e){return Array.isArray(e)?Me(e.map(t=>typeof t=="string"?t.trim():"")):[]}function Se(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function pi(e){return Array.isArray(e)?Me(e.map(t=>typeof t=="string"?t.trim():"")):[]}function Jl(e){return e instanceof Error?e.message:String(e)}function mi(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function Me(e){return[...new Set(e.filter(Boolean))]}function fi(e,t){let n=Se(e),r=Se(n.agents),o=Se(r.defaults),i=mi(o,"skills")?pi(o.skills):t,s=Array.isArray(r.list)?r.list.map(a=>({...Se(a)})):[];return{defaults:Me(i),agents:s}}function Vl(e,t){let n=fi(e,t.existingSkillSlugs);if(t.scope==="global")return hn({defaults:hi(n.defaults,t.slug),agents:[]});let r=wn(n.defaults,t.slug);return hn({defaults:r,agents:t.agentIds.map(o=>yi(n.agents,o,t.slug,r,"add"))})}function Kl(e,t){let n=fi(e,t.existingSkillSlugs),r=t.scope==="agents"?n.defaults:wn(n.defaults,t.slug),o=t.scope==="agents"?t.agentIds:t.scope==="all"?Me([...n.agents.map(i=>typeof i.id=="string"?i.id:""),...t.agentIds]):[];return hn({defaults:r,agents:o.map(i=>yi(n.agents,i,t.slug,r,"remove"))})}function hn(e){return{agents:{defaults:{skills:e.defaults},...e.agents.length>0?{list:e.agents}:{}}}}function hi(e,t){return Me([...e,t])}function wn(e,t){let n=_t(t);return e.filter(r=>_t(r)!==n)}function _t(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}async function bn(e,t,n){try{let r=await e.skillCatalogService.read(e.store.get());return kt(r)}catch{return kt(zl(t,n))}}function zl(e,t){let n=r=>Ql(r,t.slug)?Yl(r,t):r;return{...e,builtIn:e.builtIn.map(n),global:e.global.map(n),spec:e.spec.map(n),items:e.items.map(n)}}function Ql(e,t){let n=_t(t);return[e.slug,e.id,e.name].some(r=>_t(r)===n)}function Yl(e,t){if(t.type==="update")return{...e,version:e.latestVersion??e.version,hasUpdate:!1};if(t.type==="install"){let o=t.scope==="agents"?Me([...e.installedAgentKeys,...t.agentIds]):e.installedAgentKeys,i=t.scope==="global"?!0:e.globalVisible;return{...e,installed:!0,globalVisible:i,globalEnabled:i?!0:e.globalEnabled,installedAgentKeys:o}}let n=t.scope==="agents"?e.installedAgentKeys.filter(o=>!t.agentIds.includes(o)):t.scope==="all"?[]:e.installedAgentKeys,r=t.scope==="agents"?e.globalVisible:!1;return{...e,installed:r||n.length>0,globalVisible:r,globalEnabled:r&&e.globalEnabled,installedAgentKeys:n}}function yi(e,t,n,r,o){let i=e.find(a=>typeof a.id=="string"&&a.id===t)??{id:t},s=mi(i,"skills")?pi(i.skills):r;return{...i,id:t,skills:o==="add"?hi(s,n):wn(s,n)}}async function wi(e,t,n){return Be({store:e.store,upstreamConfigPort:e.upstreamConfigPort,writePolicy:e.configWritePolicy,operationGate:e.operationGate},{baseHash:e.store.get().config.hash??"",patch:t,note:n})}function bi(e){return{path:e.path,hash:e.hash??null,restart:e.restart??null}}var Si=async(e,t)=>{let n=await li(e,{query:t.query?.query??null,limit:Ge(t.query?.limit)});return R(n)},Ci=async(e,t)=>{let n=t.body;return n?Q.run(`skill:install:${n.slug??"unknown"}`,async()=>{let r;try{r=await ci(e,n)}catch(o){if(j(o))return A(o);throw o}return R(r)}):Y("Install request body is required")},Ri=async(e,t)=>{let n=t.body;if(!n)return Y("Update request body is required");let r;try{r=await di(e,n)}catch(o){if(j(o))return A(o);throw o}return R(r)},ki=async(e,t)=>{let n=t.body;return n?Q.run(`skill:uninstall:${n.slug??"unknown"}`,async()=>{let r;try{r=await ui(e,n)}catch(o){if(j(o))return A(o);throw o}return R(r)}):Y("Uninstall request body is required")};import{spawn as Xl}from"child_process";import{randomBytes as Zl}from"crypto";import{existsSync as ec}from"fs";import{homedir as tc}from"os";import Ne from"path";import{fileURLToPath as nc}from"url";var rc=15e3,oc=3e3,ic=1800*1e3,Pi=Ne.dirname(nc(import.meta.url)),sc=(()=>{let e=[Ne.resolve(Pi,"./feishu-bot-creator/feishu_bot_creator.py"),Ne.resolve(Pi,"../../../tools/feishu-bot-creator/feishu_bot_creator.py")];return e.find(t=>ec(t))??e[1]})(),re=null,We=null,Ai=800,ac=600*1e3;function vi(){return re!==null}function Ei(e="client_canceled"){return re?(re.abort(e),!0):!1}function Ii(e={}){let t=(e.sessionId??"").trim();if(re)return t&&t===re.sessionId?{ok:!0,response:lc(re)}:{ok:!1,code:"conflict_session_busy",message:"Another Feishu bind session is already running on this bridge. Cancel it first via DELETE /v1/integrations/feishu/bind."};if(t&&We?.sessionId===t){if(Date.now()-We.completedAt<=ac)return{ok:!0,response:cc(We)};We=null}let n=e.platform==="lark"?"lark":"feishu",r=(e.accountId??"").trim()||"default",o=t||mc(),i=process.env.FEISHU_BOT_SCRIPT_PATH||sc,s=process.env.FEISHU_BOT_PYTHON_BIN||"python3",a=[i,"create","--platform",n];e.avatarUrl&&a.push("--avatar-url",e.avatarUrl),e.greeting&&a.push("--greeting",e.greeting);let l=(e.botName??"").trim();if(l&&a.push("--bot-name",l),e.botSpecs&&e.botSpecs.length>0){let C=e.botSpecs.map(P=>({name:P.name,account_id:P.accountId}));a.push("--bots-json",JSON.stringify(C))}let c={...process.env};for(let C of["http_proxy","HTTP_PROXY","https_proxy","HTTPS_PROXY","all_proxy","ALL_PROXY","ftp_proxy","FTP_PROXY"])delete c[C];c.NO_PROXY="127.0.0.1,localhost,::1",c.no_proxy=c.NO_PROXY;let d=process.env.OPENCLAW_STATE_DIR??Ne.join(tc(),".openclaw"),p=Ne.join(d,"openclaw.json"),h=Ne.join(d,"credentials",`feishu-${r}-allowFrom.json`),m=Xl(s,a,{env:{FEISHU_BOT_OPENCLAW_CONFIG:p,FEISHU_BOT_OPENCLAW_ALLOW_FROM:h,...c,FEISHU_BOT_ACCOUNT_ID:r,PYTHONUNBUFFERED:"1"},stdio:["ignore","pipe","pipe"]}),f=[],w=new Set,S=!1,_=!1,D=null,G=(C,P)=>{if(!C.closed){if(C.pending){C.pending({value:P,done:!1}),C.pending=null;return}C.queue.push(P)}},N=C=>{if(!S){f.push(C),f.length>Ai&&f.splice(0,f.length-Ai);for(let P of w)G(P,C)}},F=()=>{if(!S){S=!0,We={sessionId:o,completedAt:Date.now(),frames:f.slice()};for(let C of w)C.closed=!0,C.pending&&(C.pending({value:void 0,done:!0}),C.pending=null);w.clear()}},L=(C,P)=>{N(pc(C,P))},ue=setInterval(()=>{if(!S)for(let C of w)G(C,`: ping ${Date.now()}
|
|
48
|
+
|
|
49
|
+
`)},rc),oe=!1,b=()=>{clearInterval(ue),clearTimeout($),re&&re.sessionId===o&&(re=null)},x=(C="SIGTERM")=>{if(!oe){oe=!0;try{m.killed||m.kill(C)}catch{}setTimeout(()=>{if(!m.killed)try{m.kill("SIGKILL")}catch{}},oc).unref()}},K=C=>{S||(L("finish",{action:"finish",level:"error",step:"canceled",message:C}),x("SIGTERM"),F())},$=setTimeout(()=>{S||K("session_timeout")},ic);$.unref();let ie="";m.stdout.setEncoding("utf8"),m.stdout.on("data",C=>{ie+=C;let P=gc(ie);ie=P.remainder;for(let fe of P.lines){let ke=null;try{ke=JSON.parse(fe)}catch{L("log",{action:"log",level:"warn",step:"raw_stdout",message:fe});continue}Re(ke)}}),m.stderr.setEncoding("utf8"),m.stderr.on("data",C=>{let P=String(C).trim();if(P){process.stderr.write(`[feishu-script-stderr] ${P}
|
|
50
|
+
`);for(let fe of P.split(/\r?\n/))fe&&L("log",{action:"log",level:"warn",step:"script_stderr",message:fe.slice(0,1e3)})}}),m.stdout.on("data",C=>{let P=String(C).trim();P&&process.stderr.write(`[feishu-script-stdout] ${P.slice(0,4e3)}
|
|
51
|
+
`)}),m.on("error",C=>{L("finish",{action:"finish",level:"error",step:"spawn",message:`Failed to spawn ${s}: ${C.message}`}),b(),F()}),m.on("exit",(C,P)=>{if(S||C!==0&&!oe&&L("finish",{action:"finish",level:"error",step:"exit",message:`script exited code=${C??"?"} signal=${P??"-"}`}),_){D={code:C,signal:P,killed:oe};return}b(),F()});function Re(C){if(C.action==="finish"&&(C.level??"success")==="success"&&e.onScriptFinish){let fe=Ti(C);_=!0,Promise.resolve().then(()=>e.onScriptFinish(fe,L)).catch(ke=>{let is=ke instanceof Error?ke.message:String(ke);L("finish",{action:"finish",level:"error",step:"post_process",message:`post-script processing failed: ${is}`})}).finally(()=>{_=!1,D&&(D=null,b(),F())});return}let P=uc(C);L(P.eventName,P.payload)}re={sessionId:o,startedAt:Date.now(),child:m,frames:f,subscribers:w,abort:K},L("session_started",{action:"session_started",level:"info",step:"session",message:"Feishu bind session started",sessionId:o,platform:n,accountId:r});let Lt={[Symbol.asyncIterator](){return xi({sessionId:o,frames:f,subscribers:w},S)}};return{ok:!0,response:{status:200,headers:Sn(),stream:Lt}}}function xi(e,t=!1){let n={queue:e.frames.slice(),pending:null,closed:t};return t||e.subscribers.add(n),{next(){return n.queue.length>0?Promise.resolve({value:n.queue.shift(),done:!1}):n.closed?Promise.resolve({value:void 0,done:!0}):new Promise(r=>{n.pending=r})},return(){return n.closed=!0,e.subscribers.delete(n),n.pending&&(n.pending({value:void 0,done:!0}),n.pending=null),Promise.resolve({value:void 0,done:!0})}}}function lc(e){return{status:200,headers:Sn(),stream:{[Symbol.asyncIterator](){return xi(e)}}}}function cc(e){return{status:200,headers:Sn(),stream:{async*[Symbol.asyncIterator](){for(let t of e.frames)yield t}}}}function Sn(){return{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-cache, no-transform",connection:"keep-alive","x-accel-buffering":"no"}}function Ti(e){let t=e.data;if(!t||typeof t!="object")return e;let n={...t};return"app_secret"in n&&delete n.app_secret,{...e,data:n}}function dc(e){if(!("app_secret"in e))return e;let{app_secret:t,...n}=e;return n}function uc(e){let t=String(e.action??"");return t==="finish"?{eventName:"finish",payload:Ti(e)}:t==="log"?{eventName:"log",payload:dc(e)}:t==="progress"?{eventName:"progress",payload:e}:t==="show_qrcode"?{eventName:"show_qrcode",payload:e}:t==="write_config"?{eventName:"progress",payload:{action:"progress",level:"info",step:e.step??"config",message:typeof e.message=="string"?e.message:"writing config"}}:{eventName:t||"event",payload:e}}function gc(e){let t=[],n=e,r;for(;(r=n.indexOf(`
|
|
52
|
+
`))!==-1;){let o=n.slice(0,r).trimEnd();n=n.slice(r+1),o&&t.push(o)}return{lines:t,remainder:n}}function pc(e,t){return`event: ${e}
|
|
53
|
+
data: ${JSON.stringify(t)}
|
|
54
|
+
|
|
55
|
+
`}function mc(){return`feishu-bind-${Date.now().toString(36)}-${Zl(8).toString("hex")}`}import{randomBytes as fc}from"crypto";var Di=300*1e3,Oi=32,Ce=new Map;function _i(){return Date.now()}function hc(){return`tt_${fc(8).toString("hex")}`}function Cn(){let e=_i()-Di;for(let[t,n]of Ce)n.createdAt<e&&Ce.delete(t)}function Bi(e){if(Cn(),Ce.size>=Oi)throw new Error(`team-bind ticket store is at capacity (${Oi}); retry shortly`);let t=hc(),n=_i();return Ce.set(t,{team:e,createdAt:n}),{ticketId:t,expiresAt:new Date(n+Di).toISOString()}}function Mi(e){return Cn(),Ce.get(e)?.team??null}function Ni(e){Cn();let t=Ce.get(e);return t?(Ce.delete(e),t.team):null}var Li=async(e,t)=>{let n=t.query??{},r=(n.platform??"feishu").toLowerCase();if(r!=="feishu"&&r!=="lark")return{status:400,headers:{"content-type":"application/json; charset=utf-8"},body:{ok:!1,error:{code:"invalid_platform",message:`platform must be 'feishu' or 'lark', got: ${r}`}}};let o=(n.teamTicket??"").trim(),i=null;if(o&&(i=Mi(o),!i))return{status:410,headers:{"content-type":"application/json; charset=utf-8"},body:{ok:!1,error:{code:"team_ticket_expired",message:"team-bind ticket is invalid or expired; re-prepare the team payload"}}};let s=(n.accountId??"").trim()||"default",l=i?async(h,m)=>{let f=o?Ni(o):null;if(!f){m("finish",{action:"finish",level:"error",step:"team_ticket",message:"team-bind ticket disappeared between session-start and script-finish; aborting team registration"});return}let w=h.data??{},S=typeof w.app_id=="string"?w.app_id:"",_=typeof w.bot_name=="string"?w.bot_name:"",D=typeof w.open_id=="string"?w.open_id:"",G=Array.isArray(w.bots)?w.bots:[],N=w.team_group,F=N&&typeof N=="object"?N:void 0,L=new Map;for(let b of G){let x=typeof b.account_id=="string"?b.account_id:"";x&&L.set(x,{appId:typeof b.app_id=="string"?b.app_id:"",appSecret:typeof b.app_secret=="string"?b.app_secret:"",botName:typeof b.bot_name=="string"?b.bot_name:"",openId:typeof b.open_id=="string"?b.open_id:""})}if(L.size===0&&S){let b=typeof w.app_secret=="string"?w.app_secret:"";L.set(s,{appId:S,appSecret:b,botName:_,openId:D})}let ue=b=>{let x=b.channelBinding?.accountId?.trim()||b.id,K=L.get(x),$=K?.appId??"",ie=K?.appSecret??"",Re=$||ie?{...$?{appId:$}:{},...ie?{appSecret:ie}:{}}:void 0;return{...b,channelBinding:{channel:"feishu",accountId:x,...Re?{auth:Re}:{},comment:b.channelBinding?.comment??`tnyma-web:${b.id}`}}},oe={...f,controller:ue(f.controller),members:f.members.map(b=>ue(b))};try{let b=await fn(e,oe,{onProgress:x=>{m(x.action,{action:x.action,level:x.level,step:x.step,message:x.message,...x.data?{data:x.data}:{}})}});m("finish",{action:"finish",level:"success",step:"all",message:`\u56E2\u961F ${f.controller.id} \u521B\u5EFA\u5B8C\u6210\uFF08${b.agentIds.length} \u4E2A agent\uFF09`,data:{app_id:S,bot_name:_,open_id:D,...F?{team_group:F}:{},team:{controllerId:f.controller.id,agentIds:b.agentIds,fileWrites:b.fileWrites,...F?{group:F}:{}}}})}catch(b){let x=b instanceof Error?b.message:String(b);m("finish",{action:"finish",level:"error",step:"team_register",message:`\u56E2\u961F\u6CE8\u518C\u5931\u8D25: ${x}`,data:{app_id:S}})}}:void 0,c=i?.controller.name,d=i?[{name:i.controller.name||i.controller.id,accountId:i.controller.channelBinding?.accountId?.trim()||i.controller.id},...i.members.map(h=>({name:h.name||h.id,accountId:h.channelBinding?.accountId?.trim()||h.id}))]:void 0,p=Ii({platform:r,accountId:n.accountId,avatarUrl:n.avatarUrl,greeting:n.greeting,sessionId:n.sessionId,...c?{botName:c}:{},...d?{botSpecs:d}:{},...l?{onScriptFinish:l}:{}});return p.ok?p.response:{status:409,headers:{"content-type":"application/json; charset=utf-8"},body:{ok:!1,error:{code:p.code,message:p.message}}}},Ui=async()=>{let e=vi(),t=Ei("client_requested_cancel");return R({ok:!0,data:{canceled:t,hadActiveSession:e}})},ji=async(e,t)=>{let n;try{n=Dt(t.body)}catch(r){if(r instanceof Error)return{status:400,headers:{"content-type":"application/json; charset=utf-8"},body:{ok:!1,error:{code:"invalid_team_payload",message:r.message}}};throw r}try{let r=Bi(n);return R({ok:!0,data:r})}catch(r){return{status:503,headers:{"content-type":"application/json; charset=utf-8"},body:{ok:!1,error:{code:"ticket_capacity",message:r instanceof Error?r.message:String(r)}}}}};var v={remoteAllowed:!0},Le={remoteAllowed:!1};function $i(e){e.register("GET","/v1/health",ro,v),e.register("GET","/v1/bridge",Vr,v),e.register("GET","/v1/capabilities",Kr,v),e.register("GET","/v1/snapshot",zr,v),e.register("GET","/v1/agents",Qr,v),e.register("GET","/v1/agents/roster",Yr,v),e.register("GET","/v1/models",Xr,v),e.register("GET","/v1/models/catalog",Zr,v),e.register("GET","/v1/channels",eo,v),e.register("GET","/v1/sessions",to,v),e.register("GET","/v1/sessions/:sessionId",no,v),e.register("POST","/v1/agents",Vo,v),e.register("POST","/v1/agents/preview",Jo,v),e.register("POST","/v1/agent-teams",Yo,v),e.register("DELETE","/v1/agents/:agentId",Ko,v),e.register("POST","/v1/agent-teams/preview",Qo,v),e.register("GET","/v1/pairing/current",oo,v),e.register("GET","/v1/pairing/qr-payload",io,v),e.register("GET","/v1/pairing/qr.png",co,v),e.register("POST","/v1/pairing/register",lo,Le),e.register("GET","/v1/pairing/pending",uo,v),e.register("GET","/v1/pairing/approved",po,v),e.register("POST","/v1/pairing/approve",go,v),e.register("POST","/v1/pairing/reject",fo,v),e.register("POST","/v1/pairing/revoke",mo,v),e.register("GET","/v1/config",ko,v),e.register("GET","/v1/config/schema",Po,v),e.register("GET","/v1/config/schema/lookup",Ao,v),e.register("POST","/v1/config/patch",vo,Le),e.register("POST","/v1/config/apply",Eo,Le),e.register("POST","/v1/chat/direct/messages",ni,v),e.register("POST","/v1/chat/group/messages",ri,v),e.register("POST","/v1/chat/direct/messages/stream",si,v),e.register("POST","/v1/chat/group/messages/stream",ai,v),e.register("GET","/v1/chat/direct/messages",oi,v),e.register("GET","/v1/chat/group/messages",ii,v),e.register("GET","/v1/skills/catalog",Si,v),e.register("POST","/v1/skills/install",Ci,Le),e.register("POST","/v1/skills/update",Ri,Le),e.register("POST","/v1/skills/uninstall",ki,Le),e.register("GET","/v1/integrations/feishu/bind/stream",Li,v),e.register("DELETE","/v1/integrations/feishu/bind",Ui,v),e.register("POST","/v1/integrations/feishu/team-bind/prepare",ji,v);let t=43,n=e.inspectRoutes().length;if(n!==t)throw new Error(`bridge-node: route count mismatch \u2014 registerAllHandlers produced ${n} routes but EXPECTED_ROUTE_COUNT is ${t}. Update register.ts and bump the constant in the same change so the next reviewer sees the count moved.`)}var te=class extends Error{constructor(n,r,o,i){super(n);this.code=r;this.retryable=o;this.retryAfterSec=i;this.name="DiscoveryError"}code;retryable;retryAfterSec};function qi(e){let t=e.fetchImpl??globalThis.fetch.bind(globalThis),n=e.timeoutMs??1e4;return{async fetch(r){let o=new URL(e.url);o.searchParams.set("bridgeId",r.bridgeId),r.clientVersion&&o.searchParams.set("clientVersion",r.clientVersion),r.platform&&o.searchParams.set("platform",r.platform);let i=new AbortController,s=setTimeout(()=>i.abort(),n),a;try{a=await t(o.toString(),{method:"GET",headers:{Authorization:`Bearer ${r.bridgeToken}`,Accept:"application/json"},signal:i.signal})}catch(p){throw p.name==="AbortError"?new te("Discovery request timed out","timeout",!0):new te(p instanceof Error?p.message:String(p),"network_error",!0)}finally{clearTimeout(s)}let l=await a.text(),c;try{c=l?JSON.parse(l):null}catch{throw new te(`Invalid JSON from discovery (status=${a.status})`,"invalid_response",a.status>=500)}if(!a.ok){let p=c?.error??{},h=p.code??`http_${a.status}`,m=p.message??`HTTP ${a.status}`,f=a.status===401||a.status===410;throw new te(m,h,!f,p.retryAfterSec)}let d=c?.data??c;if(!d||typeof d.relayUrl!="string"||typeof d.sessionToken!="string")throw new te("Discovery response missing relayUrl or sessionToken","invalid_response",!1);return d}}}var yc=[1e3,2e3,5e3,1e4,3e4,6e4];async function Gi(e,t,n={}){let r=n.backoffMs??yc,o=0;for(;;){if(n.abortSignal?.aborted)throw new te("Aborted","aborted",!1);try{return await e.fetch(t)}catch(i){let s=i instanceof te?i:new te(i instanceof Error?i.message:String(i),"unknown",!0);if(n.onAttemptFailure?.(s,o),!s.retryable)throw s;let a=s.retryAfterSec?s.retryAfterSec*1e3:r[Math.min(o,r.length-1)];o+=1,await new Promise((l,c)=>{let d=setTimeout(l,a);n.abortSignal?.addEventListener("abort",()=>{clearTimeout(d),c(new te("Aborted","aborted",!1))},{once:!0})})}}}import Mt from"ws";function Hi(e){return typeof e.stream<"u"}var Fi=[1e3,2e3,5e3,1e4,3e4,6e4],Nt=class{constructor(t){this.options=t}options;socket=null;discovery=null;heartbeatTimer=null;heartbeatExpectedAt=0;rediscoverTimer=null;state={status:"stopped"};stopped=!1;attempt=0;start(){this.stopped||this.runConnectLoop()}stop(){this.stopped=!0,this.clearTimers(),this.socket?.close(1e3,"client-stop"),this.socket=null,this.setState({status:"stopped"})}getState(){return this.state}async runConnectLoop(){for(;!this.stopped;)try{this.discovery||(this.setState({status:"discovering"}),this.discovery=await this.options.discover(),this.scheduleRediscover()),await this.openSocket(this.discovery);return}catch(t){this.options.onLog?.("warn",`relay connect failed: ${Je(t)}`),this.discovery=null;let n=Fi[Math.min(this.attempt,Fi.length-1)];this.attempt+=1,this.setState({status:"reconnecting",backoffMs:n,attempt:this.attempt}),await bc(n)}}openSocket(t){return new Promise((n,r)=>{this.setState({status:"connecting",relayUrl:t.relayUrl});let o=new Mt(t.relayUrl,{headers:{"user-agent":"tnyma-bridge/0.1.0"},maxPayload:4*1024*1024});this.socket=o;let i=!1;o.once("open",()=>{let s={kind:"hello",protocolVersion:1,bridgeId:this.options.bridgeId,sessionToken:t.sessionToken,clientVersion:this.options.clientVersion,platform:this.options.platform};this.sendEnvelope(s)}),o.on("message",s=>{let a=Sc(s.toString());if(!a){this.options.onLog?.("warn","relay: dropped malformed frame");return}if(a.kind==="welcome"){i=!0,this.attempt=0,this.handleWelcome(a),n();return}this.handleIncomingEnvelope(a)}),o.on("close",(s,a)=>{let l=a.toString()||"close";this.options.onLog?.("info",`relay socket closed code=${s} reason=${l}`),this.clearTimers(),this.socket=null,!this.stopped&&(i?(this.discovery=null,this.runConnectLoop()):r(new Error(`Relay handshake failed: ${l}`)))}),o.on("error",s=>{i?this.options.onLog?.("warn",`relay socket error: ${Je(s)}`):r(s)})})}handleWelcome(t){this.setState({status:"connected",relayUrl:this.discovery?.relayUrl??"",sessionId:t.sessionId}),this.startHeartbeat(t.heartbeatIntervalSec)}handleIncomingEnvelope(t){switch(t.kind){case"ping":this.sendEnvelope({kind:"pong",ts:t.ts});return;case"pong":this.heartbeatExpectedAt=Date.now();return;case"request":this.dispatchRequest(t);return;case"error":this.options.onLog?.("error",`relay error: ${t.code} ${t.message}`),t.fatal&&(this.setState({status:"fatal",reason:`${t.code}: ${t.message}`}),this.stopped=!0,this.socket?.close(4e3,t.code));return;default:this.options.onLog?.("warn",`relay: ignored envelope kind=${t.kind}`)}}async dispatchRequest(t){let n={method:t.method.toUpperCase(),path:t.path,query:t.query,headers:t.headers,body:t.body,id:t.id},r;try{r=await this.options.router.dispatch(this.options.apiContext,n,{source:"relay"})}catch(o){this.options.onLog?.("error",`dispatch failed: ${Je(o)}`),this.sendEnvelope({kind:"response",id:t.id,status:500,body:{ok:!1,error:{code:"internal_error",message:"internal error"}}});return}if(Hi(r)){this.sendEnvelope({kind:"stream-start",id:t.id,status:r.status,headers:r.headers});try{for await(let o of r.stream)if(this.sendEnvelope({kind:"stream-chunk",id:t.id,data:o}),this.socket?.readyState!==Mt.OPEN)return;this.sendEnvelope({kind:"stream-end",id:t.id,trailers:r.trailers?.()})}catch(o){this.options.onLog?.("error",`stream dispatch failed: ${Je(o)}`),this.sendEnvelope({kind:"stream-end",id:t.id,error:{code:"stream_failed",message:"stream failed"}})}}else this.sendEnvelope({kind:"response",id:t.id,status:r.status,headers:r.headers,body:r.body})}startHeartbeat(t){this.clearTimers();let n=Math.max(5e3,t*1e3);this.heartbeatExpectedAt=Date.now(),this.heartbeatTimer=setInterval(()=>{if(!this.socket||this.socket.readyState!==Mt.OPEN)return;if(Date.now()-this.heartbeatExpectedAt>n*2){this.options.onLog?.("warn","relay heartbeat timeout, reconnecting"),this.socket.close(4e3,"heartbeat-timeout");return}this.sendEnvelope({kind:"ping",ts:Date.now()})},n),this.heartbeatTimer.unref?.()}scheduleRediscover(){if(!this.discovery)return;this.rediscoverTimer&&clearTimeout(this.rediscoverTimer);let t=Math.max(6e4,(this.discovery.rediscoverAfterSec??3600)*1e3);this.rediscoverTimer=setTimeout(()=>{this.options.onLog?.("info","relay: scheduled rediscover firing"),this.discovery=null,this.socket?.close(4001,"rediscover")},t),this.rediscoverTimer.unref?.()}clearTimers(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.rediscoverTimer&&(clearTimeout(this.rediscoverTimer),this.rediscoverTimer=null)}sendEnvelope(t){if(!(!this.socket||this.socket.readyState!==Mt.OPEN))try{this.socket.send(JSON.stringify(t))}catch(n){this.options.onLog?.("warn",`relay send failed: ${Je(n)}`)}}setState(t){this.state=t,this.options.onState?.(t)}};function bc(e){return new Promise(t=>{setTimeout(t,e).unref?.()})}function Je(e){return e instanceof Error?e.message:String(e)}function Sc(e){let t;try{t=JSON.parse(e)}catch{return null}if(!Cc(t))return null;let n=t.kind;if(typeof n!="string"||n.length===0)return null;switch(n){case"welcome":return typeof t.sessionId!="string"||typeof t.heartbeatIntervalSec!="number"?null:t;case"ping":case"pong":return t;case"request":return typeof t.id!="string"||t.id.length===0||typeof t.method!="string"||t.method.length===0||typeof t.path!="string"||t.path.length===0?null:t;case"error":return typeof t.code!="string"?null:t;default:return null}}function Cc(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}async function Ji(e=wr(),t={}){let n=new ot,r=new Date().toISOString(),o=e.openClawStateDir,i=Rc.join(o,"bridge-state.json"),s=new Pt(i,vn({bridgeId:e.bridgeId,nodeId:e.nodeId,upstreamKind:e.upstreamKind,hostName:e.nodeId,now:r}));s.update(b=>({...b,bridge:{...b.bridge,status:"online"},pairing:{...b.pairing,serverUrl:e.pairingServerUrl??null,displayName:e.pairingDisplayName},capabilities:b.capabilities,health:{lastSuccessAt:r,lastError:null}}));let a=new st({url:e.openClawGatewayUrl,auth:{token:e.openClawGatewayToken??null,password:e.openClawGatewayPassword??null,deviceToken:e.openClawGatewayDeviceToken??null},client:{id:"gateway-client",version:e.clientVersion,platform:process.platform,mode:"backend"},connectTimeoutMs:e.openClawConnectTimeoutMs,requestTimeoutMs:e.openClawRequestTimeoutMs,scopes:["operator.admin"],userAgent:`tnyma-bridge/${e.clientVersion}`,onLog:t.onLog}),l=new at(a),c=new ct({configPort:l,agentReader:new tt(a),modelReader:new lt(a),sessionReader:new dt(a)}),d=new ft({serverUrl:e.pairingRegisterUrl,timeoutMs:e.pairingRequestTimeoutMs}),p=new wt,h=An(e.configWriteAllowedRoots),m=new St({store:s,chatPort:new it(a)}),f=new De,w=new Rt({skillsClient:new ut(a),clawHubClient:f}),S=await n.read();s.update(b=>({...b,capabilities:S}));let _={nodeConfig:e,store:s,pairingPort:d,upstreamConfigPort:l,configWritePolicy:h,chatService:m,skillCatalogService:w,agentClient:new et(a),pairingClient:new rt,operationGate:p,refreshData:async()=>{await ue()}},D=en();$i(D);let G=qi({url:e.discoveryUrl}),N=()=>e.bridgeToken??s.get().raw?.pairing?.relaySecret??"",F=new Nt({bridgeId:e.bridgeId,apiContext:_,router:D,clientVersion:e.clientVersion,platform:process.platform,discover:()=>Gi(G,{bridgeId:e.bridgeId,bridgeToken:N(),clientVersion:e.clientVersion,platform:process.platform},{onAttemptFailure:(b,x)=>{t.onLog?.("warn",`discovery attempt ${x+1} failed: ${b.code} ${b.message}`)}}),onState:b=>{t.onLog?.("info",`relay state: ${b.status}`)},onLog:t.onLog}),L=new pt({collectionLoop:new Te({intervalMs:e.collectionIntervalMs,onError:b=>{t.onLog?.("error",`collection loop failed: ${Wi(b)}`)},task:async()=>{await ue()}}),configSyncLoop:new gt({intervalMs:e.configSyncIntervalMs,onError:b=>{t.onLog?.("error",`config sync loop failed: ${Wi(b)}`)},task:async()=>{await oe()}})});async function ue(){let b=new Date().toISOString();try{let x=s.get(),[K,$,ie,Re,Lt]=await Promise.all([kr(x),Rr(c),Ar(c),Pr(c),vr(c)]);s.update(C=>{let P=er(C,K.bridge??{status:"online"});return P=Zn(P,$),P=nr(P,ie),P=tr(P,Re),P=rr(P,Lt),{...P,meta:{...P.meta,collectedAt:b},health:{...P.health,lastSuccessAt:b,lastError:null}}}),p.markReady(),t.onHealth?.({status:"online",lastError:null,lastSuccessAt:b})}catch(x){let K=x instanceof Error?x.message:String(x);p.blockForError(x),s.update($=>({...$,meta:{...$.meta,collectedAt:b},bridge:{...$.bridge,status:"degraded"},health:{...$.health,lastError:K}})),t.onHealth?.({status:"degraded",lastError:K,lastSuccessAt:null})}finally{L.markCollection(b)}}async function oe(){let b=new Date().toISOString();try{await mt({nodeConfig:e,store:s,upstreamConfigPort:l}),p.markReady(),L.markConfigSync(b)}catch(x){let K=x instanceof Error?x.message:String(x);p.blockForError(x),s.update($=>({...$,bridge:{...$.bridge,status:"degraded"},health:{...$.health,lastError:K}})),L.markConfigSync(b)}}return{config:e,store:s,runtime:L,apiContext:_,router:D,relayClient:F,registerPairing(b={}){return ht({store:s,pairingPort:d,nodeConfig:e},b)},async start(){L.start(),ue(),oe(),F.start()},async stop(){F.stop(),L.stop(),await a.close()}}}function Wi(e){return e instanceof Error?e.message:String(e)}function kc(){return process.parentPort??null}var Vi=1;function Ki(){let e=kc();return e?(e.start?.(),{attached:!0,send(t){try{e.postMessage(t)}catch{}},onMessage(t){let n=r=>{let o=r.data;!o||typeof o!="object"||typeof o.type!="string"||t(o)};return e.on("message",n),()=>e.off("message",n)}}):{attached:!1,send:()=>{},onMessage:()=>()=>{}}}import{randomBytes as Zi,timingSafeEqual as Pc}from"crypto";import zi from"fs";import{createServer as Ac}from"http";import Qi from"path";import vc from"open";var Yi=new Set(["localhost","127.0.0.1","::1","[::1]"]);function Ec(e){let t=Zi(32).toString("base64url"),n=Qi.join(e.openClawStateDir,"bridge-bind-ui.token");try{zi.mkdirSync(Qi.dirname(n),{recursive:!0}),zi.writeFileSync(n,t,{mode:384})}catch{}return{token:t,tokenFilePath:n}}function Ic(e,t){let n=new Map;return{check(r){let o=Date.now(),i=n.get(r)??[];for(;i.length>0&&i[0]<o-t;)i.shift();return i.length>=e?(n.set(r,i),!1):(i.push(o),n.set(r,i),!0)}}}async function es(e){if(!e.config.localUiEnabled)return e.logger?.info?.("[bridge] local bind page disabled"),null;let t=e.config.localUiHost.trim(),n=process.env.TNYMA_BRIDGE_ALLOW_NON_LOOPBACK?.trim()!=="1";n&&t&&!Xi(t)&&e.logger?.warn?.(`[bridge] refusing to bind local UI to ${t}; forcing 127.0.0.1 (set TNYMA_BRIDGE_ALLOW_NON_LOOPBACK=1 to override)`);let r=n?t&&Xi(t)?t:"127.0.0.1":t||"127.0.0.1",o=Ec(e.config),i=Ic(30,6e4),s=Zi(16).toString("base64"),a=Ac((c,d)=>{xc({...e,auth:o,limiter:i,cspNonce:s,loopbackOnly:n},c,d)});await jc(a,e.config.localUiPort,r);let l=Mc(e.config,a,r);return e.logger?.info?.(`[bridge] local bind page: ${l}`),e.logger?.info?.(`[bridge] local bind token at: ${o.tokenFilePath}`),{url:l,stop:()=>$c(a),open:()=>Uc(e.config,l,e.logger)}}async function xc(e,t,n){try{if(!Tc(t.headers.host)){de(n,e.cspNonce,421,{ok:!1,error:{code:"untrusted_host"}});return}if(e.loopbackOnly&&!Oc(t)){de(n,e.cspNonce,403,{ok:!1,error:{code:"loopback_only"}});return}let r=new URL(t.url??"/","http://127.0.0.1");if(t.method==="GET"&&r.pathname==="/"){qc(n,e.cspNonce,Gc(e.cspNonce,e.auth.token,e.config));return}if(t.method==="GET"&&r.pathname==="/health"){de(n,e.cspNonce,200,{ok:!0,data:{status:"ok",bridgeId:e.config.bridgeId}});return}if(r.pathname.startsWith("/api/")){let o=(t.headers["sec-fetch-site"]??"").toString();if(o&&o!=="same-origin"){de(n,e.cspNonce,403,{ok:!1,error:{code:"csrf_blocked"}});return}if(!Dc(t,e.auth.token)){de(n,e.cspNonce,401,{ok:!1,error:{code:"unauthorized"}});return}if(!e.limiter.check(r.pathname)){de(n,e.cspNonce,429,{ok:!1,error:{code:"rate_limited"}});return}}if(t.method==="GET"&&r.pathname==="/api/pairing"){let o=await e.registerPairing();de(n,e.cspNonce,200,{ok:!0,data:await _c(e.config,o)});return}de(n,e.cspNonce,404,{ok:!1,error:{code:"not_found"}})}catch(r){process.emitWarning(r instanceof Error?r:new Error(String(r)),{code:"TNYMA_BIND_UI_ERROR"}),de(n,e.cspNonce,500,{ok:!1,error:{code:"local_bind_page_error"}})}}function Tc(e){if(!e)return!1;let t=e.split(":")[0].toLowerCase().trim();return Yi.has(t)||Yi.has(e.toLowerCase())}function Xi(e){let t=e.trim().toLowerCase();return t==="127.0.0.1"||t==="localhost"||t==="::1"||t==="[::1]"}function Oc(e){let t=e.socket.remoteAddress??"";return t==="127.0.0.1"||t==="::1"||t==="::ffff:127.0.0.1"}function Dc(e,t){let n=(e.headers.authorization??"").toString(),r="bearer ";if(!n.toLowerCase().startsWith(r))return!1;let o=n.slice(r.length).trim();if(o.length!==t.length)return!1;try{return Pc(Buffer.from(o),Buffer.from(t))}catch{return!1}}async function _c(e,t){let n=t.bridgeId??e.bridgeId,r=t.pairCode??t.accessCode??null,o=t.pairCodeExpiresAt??t.accessCodeExpiresAt??null,i=ts(e.bindUrlBase,n),s=await Bc(i);return{bridgeId:n,pairCode:r,expiresAt:o,displayName:t.displayName??e.pairingDisplayName,region:t.region??null,relayUrl:t.relayUrl??null,serverUrl:t.serverUrl??e.pairingServerUrl??null,bindUrl:i,qrImageUrl:s,registered:!0}}async function Bc(e){return`data:image/png;base64,${(await vt(e)).toString("base64")}`}function ts(e,t){return`${e.replace(/\/+$/,"")}/bind?bridgeId=${encodeURIComponent(t)}`}function Mc(e,t,n){if(e.localUiPublicUrl)return e.localUiPublicUrl.replace(/\/+$/,"");let r=t.address(),o=typeof r=="object"&&r?r.port:e.localUiPort,i=Nc(n)?"127.0.0.1":n;return`http://${Lc(i)}:${o}`}function Nc(e){return e==="0.0.0.0"||e==="::"||e===""}function Lc(e){return e.includes(":")&&!e.startsWith("[")?`[${e}]`:e}async function Uc(e,t,n){if(e.localUiOpenMode==="never"){n?.info?.(`[bridge] auto-open disabled; open ${t}`);return}if(e.localUiOpenMode==="auto"&&!process.stdout.isTTY){n?.info?.(`[bridge] non-TTY environment; open ${t}`);return}if(!/^https?:\/\//i.test(t)){n?.warn?.(`[bridge] refusing to open non-http URL ${t}`);return}try{await vc(t),n?.info?.(`[bridge] opened ${t} in default browser`)}catch(r){let o=r instanceof Error?r.message:String(r);n?.warn?.(`[bridge] failed to open browser: ${o}; open ${t}`)}}function jc(e,t,n){return new Promise((r,o)=>{let i=a=>{e.off("listening",s),o(a)},s=()=>{e.off("error",i),r()};e.once("error",i),e.once("listening",s),e.listen(t,n)})}function $c(e){return new Promise((t,n)=>{if(!e.listening){t();return}e.close(r=>{if(r){n(r);return}t()})})}function de(e,t,n,r){let o=JSON.stringify(r);e.writeHead(n,{"content-type":"application/json; charset=utf-8","cache-control":"no-store","content-length":Buffer.byteLength(o),...ns(t)}),e.end(o)}function qc(e,t,n){e.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-store","content-length":Buffer.byteLength(n),...ns(t)}),e.end(n)}function ns(e){return{"x-content-type-options":"nosniff","referrer-policy":"no-referrer","x-frame-options":"DENY","content-security-policy":`default-src 'none'; img-src data:; script-src 'nonce-${e}'; style-src 'nonce-${e}'; connect-src 'self'; base-uri 'none'; frame-ancestors 'none'`}}function Gc(e,t,n){let r=ts(n.bindUrlBase,n.bridgeId);return`<!doctype html>
|
|
56
|
+
<html lang="en">
|
|
57
|
+
<head>
|
|
58
|
+
<meta charset="utf-8">
|
|
59
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
60
|
+
<title>Tnyma Bridge Binding</title>
|
|
61
|
+
<style nonce="${e}">
|
|
62
|
+
:root { color-scheme: dark; --bg: #090a0d; --panel: #121419; --panel-2: #181b22; --line: #2a2f3a; --text: #f5f7fb; --muted: #98a1b3; --accent: #ff435f; --green: #26a85f; }
|
|
63
|
+
* { box-sizing: border-box; }
|
|
64
|
+
body { margin: 0; min-height: 100vh; font-family: ui-sans-serif, system-ui, sans-serif; background: var(--bg); color: var(--text); display: grid; place-items: center; padding: 28px; }
|
|
65
|
+
main { width: min(980px, 100%); display: grid; grid-template-columns: minmax(280px, 380px) minmax(0, 1fr); gap: 18px; }
|
|
66
|
+
section { border: 1px solid var(--line); background: var(--panel); border-radius: 16px; padding: 20px; }
|
|
67
|
+
.qr-card { display: grid; gap: 16px; place-items: center; }
|
|
68
|
+
.qr { width: min(320px, 100%); aspect-ratio: 1; display: grid; place-items: center; border-radius: 12px; background: #fff; padding: 12px; }
|
|
69
|
+
.qr img { width: 100%; height: 100%; object-fit: contain; display: block; }
|
|
70
|
+
.code { width: 100%; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; }
|
|
71
|
+
.digit { min-height: 54px; display: grid; place-items: center; border: 1px solid var(--line); border-radius: 10px; background: var(--panel-2); font: 800 28px/1 ui-monospace, monospace; }
|
|
72
|
+
.details { display: grid; gap: 14px; }
|
|
73
|
+
h1 { margin: 0 0 6px; font-size: clamp(28px, 5vw, 44px); line-height: 1; }
|
|
74
|
+
p { margin: 0; color: var(--muted); line-height: 1.6; }
|
|
75
|
+
dl { margin: 0; display: grid; gap: 10px; }
|
|
76
|
+
.row { display: grid; grid-template-columns: 130px minmax(0, 1fr); gap: 12px; align-items: start; border-top: 1px solid var(--line); padding-top: 10px; }
|
|
77
|
+
dt { color: var(--muted); font-size: 12px; font-weight: 700; text-transform: uppercase; }
|
|
78
|
+
dd { margin: 0; min-width: 0; overflow-wrap: anywhere; font-family: ui-monospace, monospace; font-size: 13px; }
|
|
79
|
+
.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
|
|
80
|
+
button, a.button { min-height: 40px; border: 1px solid var(--line); border-radius: 10px; padding: 0 14px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; background: var(--panel-2); color: var(--text); font-weight: 700; text-decoration: none; cursor: pointer; }
|
|
81
|
+
a.primary { border-color: transparent; background: var(--accent); color: white; }
|
|
82
|
+
.status { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-size: 13px; font-weight: 700; }
|
|
83
|
+
.dot { width: 9px; height: 9px; border-radius: 999px; background: var(--green); box-shadow: 0 0 0 5px rgb(38 168 95 / 13%); }
|
|
84
|
+
.error { color: #ffb4bf; border: 1px solid rgb(255 67 95 / 32%); background: rgb(255 67 95 / 8%); border-radius: 10px; padding: 10px 12px; display: none; }
|
|
85
|
+
@media (max-width: 760px) { body { padding: 14px; } main { grid-template-columns: 1fr; } .row { grid-template-columns: 1fr; gap: 5px; } }
|
|
86
|
+
</style>
|
|
87
|
+
</head>
|
|
88
|
+
<body>
|
|
89
|
+
<main>
|
|
90
|
+
<section class="qr-card">
|
|
91
|
+
<div class="qr" id="qr-box"><span style="color:#111">Loading...</span></div>
|
|
92
|
+
<div class="code" id="code"></div>
|
|
93
|
+
<div class="status"><span class="dot"></span><span id="status">Preparing pairing code</span></div>
|
|
94
|
+
</section>
|
|
95
|
+
<section class="details">
|
|
96
|
+
<div>
|
|
97
|
+
<h1>Tnyma Bridge</h1>
|
|
98
|
+
<p>Use this local page to claim the bridge from the signed-in console account.</p>
|
|
99
|
+
</div>
|
|
100
|
+
<div class="error" id="error"></div>
|
|
101
|
+
<dl>
|
|
102
|
+
<div class="row"><dt>Bridge ID</dt><dd id="bridge-id">-</dd></div>
|
|
103
|
+
<div class="row"><dt>Expires</dt><dd id="expires">-</dd></div>
|
|
104
|
+
<div class="row"><dt>Console URL</dt><dd id="bind-url">-</dd></div>
|
|
105
|
+
<div class="row"><dt>Relay</dt><dd id="relay-url">-</dd></div>
|
|
106
|
+
</dl>
|
|
107
|
+
<div class="actions">
|
|
108
|
+
<a class="button primary" id="open-console" href="#" target="_blank" rel="noopener noreferrer">Open Console</a>
|
|
109
|
+
<button type="button" id="refresh">Refresh Code</button>
|
|
110
|
+
</div>
|
|
111
|
+
</section>
|
|
112
|
+
</main>
|
|
113
|
+
<script nonce="${e}">
|
|
114
|
+
const BIND_TOKEN = ${JSON.stringify(t)};
|
|
115
|
+
const INITIAL_BIND_URL = ${JSON.stringify(r)};
|
|
116
|
+
const BRIDGE_ID = ${JSON.stringify(n.bridgeId)};
|
|
117
|
+
const state = { expiresAt: null };
|
|
118
|
+
const $ = (id) => document.getElementById(id);
|
|
119
|
+
const setText = (id, value) => { $(id).textContent = value || "-"; };
|
|
120
|
+
function withReturnUrl(url) {
|
|
121
|
+
try {
|
|
122
|
+
const parsed = new URL(url);
|
|
123
|
+
parsed.searchParams.set("returnUrl", window.location.origin + window.location.pathname);
|
|
124
|
+
return parsed.toString();
|
|
125
|
+
} catch {
|
|
126
|
+
return url;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function renderCode(code) {
|
|
130
|
+
const clean = (code || "").padEnd(6, " ");
|
|
131
|
+
const container = $("code");
|
|
132
|
+
container.replaceChildren();
|
|
133
|
+
for (const raw of clean.slice(0, 6).split("")) {
|
|
134
|
+
const span = document.createElement("span");
|
|
135
|
+
span.className = "digit";
|
|
136
|
+
span.textContent = raw.trim() || "-";
|
|
137
|
+
container.appendChild(span);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function updateCountdown() {
|
|
141
|
+
if (!state.expiresAt) return;
|
|
142
|
+
const remaining = Math.max(0, Math.floor((Date.parse(state.expiresAt) - Date.now()) / 1000));
|
|
143
|
+
const minutes = String(Math.floor(remaining / 60));
|
|
144
|
+
const seconds = String(remaining % 60).padStart(2, "0");
|
|
145
|
+
$("status").textContent = remaining > 0 ? "Pairing code active: " + minutes + ":" + seconds : "Pairing code expired";
|
|
146
|
+
}
|
|
147
|
+
async function refresh() {
|
|
148
|
+
$("error").style.display = "none";
|
|
149
|
+
$("status").textContent = "Refreshing pairing code";
|
|
150
|
+
try {
|
|
151
|
+
const response = await fetch("/api/pairing", {
|
|
152
|
+
cache: "no-store",
|
|
153
|
+
credentials: "omit",
|
|
154
|
+
headers: { "authorization": "Bearer " + BIND_TOKEN },
|
|
155
|
+
});
|
|
156
|
+
const payload = await response.json();
|
|
157
|
+
if (!response.ok || !payload.ok) throw new Error(payload.error && payload.error.code ? payload.error.code : ("Failed (" + response.status + ")"));
|
|
158
|
+
const data = payload.data;
|
|
159
|
+
state.expiresAt = data.expiresAt || null;
|
|
160
|
+
setText("bridge-id", data.bridgeId);
|
|
161
|
+
setText("expires", data.expiresAt);
|
|
162
|
+
setText("bind-url", data.bindUrl);
|
|
163
|
+
setText("relay-url", data.relayUrl);
|
|
164
|
+
renderCode(data.pairCode);
|
|
165
|
+
const qrBox = $("qr-box");
|
|
166
|
+
qrBox.replaceChildren();
|
|
167
|
+
if (typeof data.qrImageUrl === "string" && data.qrImageUrl.startsWith("data:image/")) {
|
|
168
|
+
const img = document.createElement("img");
|
|
169
|
+
img.alt = "Console bind QR";
|
|
170
|
+
img.src = data.qrImageUrl;
|
|
171
|
+
qrBox.appendChild(img);
|
|
172
|
+
}
|
|
173
|
+
$("open-console").href = withReturnUrl(data.bindUrl);
|
|
174
|
+
if (!data.registered) {
|
|
175
|
+
$("status").textContent = "Open Console to finish binding";
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
updateCountdown();
|
|
179
|
+
} catch (error) {
|
|
180
|
+
$("error").textContent = error instanceof Error ? error.message : String(error);
|
|
181
|
+
$("error").style.display = "block";
|
|
182
|
+
$("status").textContent = "Pairing unavailable";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
setText("bridge-id", BRIDGE_ID);
|
|
186
|
+
setText("bind-url", INITIAL_BIND_URL);
|
|
187
|
+
$("open-console").href = withReturnUrl(INITIAL_BIND_URL);
|
|
188
|
+
$("refresh").addEventListener("click", refresh);
|
|
189
|
+
setInterval(updateCountdown, 1000);
|
|
190
|
+
void refresh();
|
|
191
|
+
</script>
|
|
192
|
+
</body>
|
|
193
|
+
</html>`}async function Fc(){let e=Ki(),t=await Ji(void 0,{onHealth:r=>{e.send({type:"health",status:r.status,lastError:r.lastError,lastSuccessAt:r.lastSuccessAt})},onLog:(r,o)=>{e.send({type:"log",level:r,message:o}),r==="error"&&process.stderr.write(`[bridge] ${o}
|
|
194
|
+
`)}});await Jc(t);let n=await Kc(t);await t.start(),process.stdout.write(`bridge-node started (bridgeId=${t.config.bridgeId})
|
|
195
|
+
`),await Vc(t),await n?.open(),e.send({type:"ready",bridgeId:t.config.bridgeId,protocolVersion:Vi}),e.attached?await zc(t,e,n):await Qc(t,n)}var Wc=typeof process.argv[1]=="string"&&rs(Hc(import.meta.url))===rs(process.argv[1]);Wc&&Fc().catch(e=>{let t=e instanceof Error?e.stack??e.message:String(e);process.stderr.write(`${t}
|
|
196
|
+
`),process.exitCode=1});async function Jc(e){if(!e.config.pairingServerUrl)return;let t=e.store.get().raw?.pairing?.relaySecret??null;try{await e.registerPairing(),process.stdout.write(t?`[bridge] pairing registration refreshed; relaySecret persisted to store
|
|
197
|
+
`:`[bridge] pairing registration succeeded; relaySecret generated and persisted
|
|
198
|
+
`)}catch(n){let r=n instanceof Error?n.message:String(n);process.stderr.write(`[bridge] auto-register failed (will retry on next restart): ${r}
|
|
199
|
+
`)}}async function Vc(e){if(!e.config.pairingPrintAsciiQr||!e.config.pairingServerUrl||!process.stdout.isTTY)return;let t=await e.registerPairing();if(!t.qrPayload)return;let n=await ao(t.qrPayload);process.stdout.write(`
|
|
200
|
+
Scan this QR with your app to pair, or enter the pair code manually:
|
|
201
|
+
|
|
202
|
+
`),process.stdout.write(n),process.stdout.write(`
|
|
203
|
+
`),(t.pairCode??t.accessCode)&&process.stdout.write(`Pair code: ${t.pairCode??t.accessCode}
|
|
204
|
+
`),process.stdout.write(`Pairing server: ${t.serverUrl??""}
|
|
205
|
+
`),process.stdout.write(`Bridge ID: ${t.bridgeId??e.config.bridgeId}
|
|
206
|
+
`),process.stdout.write(`
|
|
207
|
+
`)}async function Kc(e){try{return await es({config:e.config,registerPairing:t=>e.registerPairing(t),logger:{info:t=>process.stdout.write(`${t}
|
|
208
|
+
`),warn:t=>process.stderr.write(`${t}
|
|
209
|
+
`)}})}catch(t){let n=t instanceof Error?t.message:String(t);return process.stderr.write(`[bridge] local bind page failed to start: ${n}
|
|
210
|
+
`),null}}async function zc(e,t,n){await new Promise((r,o)=>{let i=!1,s=()=>{i||(i=!0,os(e,n).then(()=>r(),a=>o(a)))};t.onMessage(async a=>{if(a.type==="shutdown"){s();return}if(a.type==="start-pairing")try{let l=await e.registerPairing(a.label?{displayName:a.label}:{});t.send({type:"pairing-state",pairing:l})}catch(l){t.send({type:"log",level:"error",message:`start-pairing failed: ${l instanceof Error?l.message:String(l)}`})}}),process.once("SIGTERM",s),process.once("SIGINT",s)})}async function Qc(e,t){await new Promise((n,r)=>{let o=!1,i=()=>{process.off("SIGINT",a),process.off("SIGTERM",l)},s=c=>{if(o){process.stderr.write(`
|
|
211
|
+
Received ${c} again, forcing exit.
|
|
212
|
+
`),process.exit(130);return}o=!0,process.stdout.write(`
|
|
213
|
+
Received ${c}, shutting down bridge-node...
|
|
214
|
+
`),os(e,t).then(()=>{i(),n()},d=>{i(),r(d)})},a=()=>{s("SIGINT")},l=()=>{s("SIGTERM")};process.on("SIGINT",a),process.on("SIGTERM",l)})}async function os(e,t){await t?.stop(),await e.stop()}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{openSync as a,closeSync as c,mkdirSync as d}from"fs";import{homedir as p}from"os";import e from"path";import{spawn as m}from"child_process";import{fileURLToPath as s}from"url";var o="http://127.0.0.1:18788";async function u(){if(process.env.TNYMA_BRIDGE_SKIP_AUTOSTART==="1"||l())return;if(await g()){process.stdout.write(`[tnyma-bridge] already running at ${o}
|
|
3
|
+
`);return}let t=e.dirname(s(import.meta.url)),r=e.join(t,"index.js"),i=e.join(p(),".tnyma-bridge");d(i,{recursive:!0,mode:448});let n=a(e.join(i,"bridge.log"),"a",384);try{m(process.execPath,[r],{detached:!0,stdio:["ignore",n,n],env:process.env}).unref(),process.stdout.write(`[tnyma-bridge] started at ${o}
|
|
4
|
+
`)}finally{c(n)}}function l(){return s(import.meta.url).includes(`${e.sep}apps${e.sep}bridge-node${e.sep}`)}async function g(){try{return(await fetch(`${o}/health`,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}u().catch(t=>{let r=t instanceof Error?t.message:String(t);process.stderr.write(`[tnyma-bridge] autostart skipped: ${r}
|
|
5
|
+
`)});
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tnyma-bridge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local bridge daemon that links OpenClaw to the tnyma-cloud relay. Install globally and run `tnyma-bridge` to pair this machine with the tnyma console.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "bundle/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"tnyma-bridge": "bundle/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bundle"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=20"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"tnyma",
|
|
19
|
+
"openclaw",
|
|
20
|
+
"bridge",
|
|
21
|
+
"cli"
|
|
22
|
+
],
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/tnyma/tnyma-bridge.git"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "pnpm build:tsc && pnpm build:bundle",
|
|
29
|
+
"build:bundle": "tsup",
|
|
30
|
+
"build:tsc": "tsc -b tsconfig.json --force",
|
|
31
|
+
"pair:info": "node scripts/print-pair-info.mjs",
|
|
32
|
+
"start": "node bundle/index.js",
|
|
33
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
34
|
+
"postinstall": "node bundle/postinstall.js",
|
|
35
|
+
"prepublishOnly": "pnpm build"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"open": "^11.0.0",
|
|
39
|
+
"qrcode": "^1.5.4",
|
|
40
|
+
"ws": "^8.18.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@tnyma/bridge-adapter-openclaw": "workspace:*",
|
|
44
|
+
"@tnyma/bridge-core": "workspace:*",
|
|
45
|
+
"@tnyma/bridge-runtime": "workspace:*",
|
|
46
|
+
"@tnyma/relay-shared": "workspace:*",
|
|
47
|
+
"@types/ws": "^8.5.13",
|
|
48
|
+
"tsup": "^8.3.0"
|
|
49
|
+
}
|
|
50
|
+
}
|