tnyma-bridge 0.1.0 → 0.1.1
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 +16 -3
- package/bundle/index.js +101 -34
- package/bundle/postinstall.js +54 -3
- package/bundle/watchdog.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,14 +22,27 @@ http://127.0.0.1:18788
|
|
|
22
22
|
|
|
23
23
|
The page shows the QR code and pairing code. Scan the QR code or enter the pairing code in Tnyma to bind this computer.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
The installer also registers a per-user watchdog so the bridge is restarted if it exits:
|
|
26
|
+
|
|
27
|
+
- macOS: LaunchAgent at `~/Library/LaunchAgents/ai.tnyma.bridge.plist`
|
|
28
|
+
- Linux: systemd user service at `~/.config/systemd/user/tnyma-bridge.service`
|
|
29
|
+
- Windows: Task Scheduler task named `tnyma-bridge`
|
|
30
|
+
|
|
31
|
+
Manage the watchdog:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
tnyma-bridge service status
|
|
35
|
+
tnyma-bridge service restart
|
|
36
|
+
tnyma-bridge service stop
|
|
37
|
+
tnyma-bridge service install
|
|
38
|
+
```
|
|
26
39
|
|
|
27
40
|
## Troubleshooting
|
|
28
41
|
|
|
29
42
|
If OpenClaw is not detected, start OpenClaw first and then restart `tnyma-bridge`.
|
|
30
43
|
|
|
31
|
-
If the
|
|
44
|
+
If the platform service cannot be registered, start the detached watchdog manually:
|
|
32
45
|
|
|
33
46
|
```bash
|
|
34
|
-
tnyma-bridge
|
|
47
|
+
tnyma-bridge service fallback-start
|
|
35
48
|
```
|
package/bundle/index.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
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
|
|
4
|
-
`);return/<<<EXTERNAL_UNTRUSTED_CONTENT|UNTRUSTED channel metadata \(|Source:\s+/.test(n)}function
|
|
5
|
-
`),r=[],o=!1,
|
|
6
|
-
`).replace(/^\n+/,"").replace(/\n+$/,"").replace(
|
|
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
|
|
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
|
|
11
|
-
mode: ${
|
|
12
|
-
agents: ${r.agents.join(", ")}`);n.push(` ${r.id}["${
|
|
2
|
+
import{realpathSync as Ei}from"fs";import Td from"path";import{fileURLToPath as xi}from"url";function $n(){return{path:null,hash:null,valid:!0,lastLoadedAt:null,snapshot:null}}function jn(e){return e.trim().replace(/\[(\d+)\]/g,".$1").replace(/^\/+|\/+$/g,"").replace(/\//g,".").replace(/\.+/g,".")}function qn(e,t=""){let n=[];for(let[r,o]of Object.entries(e)){let s=t?`${t}.${r}`:r;n.push(s),o&&typeof o=="object"&&!Array.isArray(o)&&n.push(...qn(o,s))}return n}function Hn(e){let t=e.map(jn).filter(Boolean);return{allowedRoots:t,allows(n){let r=jn(n);return t.some(o=>r===o||r.startsWith(`${o}.`))}}}function rt(e,t){return qn(e).filter(n=>!t.allows(n))}function Gn(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:$n(),capabilities:{},health:{lastSuccessAt:null,lastError:null},data:{agents:{},models:{},channels:{},sessions:{}}}}function Oi(e){return e.trim().replace(/\[(\d+)\]/g,".$1").replace(/\//g,".").split(".").map(t=>t.trim()).filter(Boolean)}function ce(e,t){let n=e;for(let r of Oi(t)){if(!n||typeof n!="object")return;n=n[r]}return n}function P(e,t){return{ok:!0,data:e,meta:t}}function Wn(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 Fn(e){let t=e.pairCode.trim(),n=e.pairCodeExpiresAt.trim(),r=Wn({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 ot(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 Jn(e){return{path:e.config.path,hash:e.config.hash,valid:e.config.valid,lastLoadedAt:e.config.lastLoadedAt,snapshot:e.config.snapshot}}function st(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 de(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function De(e){return Array.isArray(e)?e:[]}function Z(e){return typeof e=="string"&&e.trim()?e.trim():null}function Bi(e){return typeof e=="boolean"?e:null}function Mi(e){let t=de(e);return t?Object.keys(t).length:0}function Ni(e,t){let n=de(e);if(!n)return 0;let r=0;for(let[o,s]of Object.entries(n)){if(o==="defaults")continue;let i=de(s);if(!i)continue;let a=!1;(Z(i.defaultTo)===t||Z(i.agentId)===t)&&(a=!0);let l=De(i.bindings);a||(a=l.some(c=>{let d=de(c);return d?Z(d.agentId)===t:!1})),a&&(r+=1)}return r}function Li(e,t){return De(e).filter(n=>{let r=de(n);return r?Z(r.agentId)===t:!1}).length}function Ui(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 $i(e){let t=Object.values(e.data.agents).find(n=>n.isDefault);return t?{id:t.id,name:t.name}:null}function Vn(e){let t=e.config.snapshot??null,n=De(ce(t,"agents.list")),r=De(ce(t,"agents.defaults.skills")),o=Z(ce(t,"agents.defaults.workspace")),s=Z(ce(t,"agents.defaults.model.primary")),i=ce(t,"channels"),a=ce(t,"bindings"),l=$i(e),c=new Map;for(let d of n){let p=de(d),h=Z(p?.id);p&&h&&c.set(h,p)}return Object.values(e.data.agents).map(d=>{let p=c.get(d.id),h=Z(p?.name),m=Z(p?.workspace)??o,f=Z(de(p?.model)?.primary)??d.modelId??s,w=De(p?.skills),S=de(p?.tools),_=De(de(p?.subagents)?.allowAgents).map(H=>Z(H)).filter(H=>!!H),O=d.isDefault??Bi(p?.default)??!1;return{id:d.id,name:h??d.name,kind:_.length>0?"team":"agent",masterName:!O&&l&&l.id!==d.id?l.name:null,status:Ui(e,d.id),primaryModel:f,workspace:m,skillsCount:w.length>0?w.length:r.length,channelCount:Ni(i,d.id)+Li(a,d.id),toolCount:Mi(S),teamMemberCount:_.length,teamMemberIds:_,isDefault:O}}).sort((d,p)=>d.id.localeCompare(p.id))}function Qt(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function zn(e){return Array.isArray(e)?e:[]}function Oe(e){return typeof e=="string"&&e.trim()?e.trim():null}function ji(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function qi(e){return typeof e=="boolean"?e:null}function Kn(e){return zn(e).map(t=>Oe(t)).filter(t=>!!t)}function Qn(e){let t=e.config.snapshot??null,n=Qt(ce(t,"models.providers")),r=new Map,o=new Map;for(let s of Object.values(e.data.models)){let i=`${s.provider}:${s.id}`;o.set(i,{id:s.id,provider:s.provider,name:s.name??s.id,contextWindow:s.contextWindow??null,supportsReasoning:s.supportsReasoning,inputModes:s.inputModes,configured:!1,available:!0});let a=r.get(s.provider)??{id:s.provider,baseUrl:null,api:null,auth:null,configuredModelCount:0,runtimeModelCount:0};a.runtimeModelCount+=1,r.set(s.provider,a)}for(let[s,i]of Object.entries(n??{})){let a=Qt(i)??{},l=r.get(s)??{id:s,baseUrl:null,api:null,auth:null,configuredModelCount:0,runtimeModelCount:0};l.baseUrl=Oe(a.baseUrl),l.api=Oe(a.api),l.auth=Oe(a.auth);let c=zn(a.models);l.configuredModelCount=c.length,r.set(s,l);for(let d of c){let p=Qt(d),h=Oe(p?.id);if(!h)continue;let m=`${s}:${h}`,f=o.get(m);o.set(m,{id:h,provider:s,name:Oe(p?.name)??f?.name??h,contextWindow:ji(p?.contextWindow)??f?.contextWindow??null,supportsReasoning:qi(p?.reasoning)??f?.supportsReasoning??void 0,inputModes:Kn(p?.input).length?Kn(p?.input):f?.inputModes,configured:!0,available:f?.available??!1})}}return{providers:[...r.values()].sort((s,i)=>s.id.localeCompare(i.id)),models:[...o.values()].sort((s,i)=>s.provider!==i.provider?s.provider.localeCompare(i.provider):s.id.localeCompare(i.id))}}function Yn(e,t){return e.data.sessions[t]??null}function it(e){return Object.values(e.data.agents).sort((t,n)=>t.id.localeCompare(n.id))}function at(e){return Object.values(e.data.channels).sort((t,n)=>t.id.localeCompare(n.id))}function lt(e){return Object.values(e.data.models).sort((t,n)=>t.id.localeCompare(n.id))}function ct(e){return Object.values(e.data.sessions).sort((t,n)=>t.id.localeCompare(n.id))}function Xn(e){return{bridge:ot(e),pairing:st(e),config:Jn(e),capabilities:{...e.capabilities},health:{...e.health},agents:it(e),models:lt(e),channels:at(e),sessions:ct(e)}}var dt=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 ut=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 s=o.id?.trim()??"";s&&r.push({id:s,name:o.name?.trim()||o.identity?.name?.trim()||s,isDefault:n===s,modelId:o.model?.primary?.trim()||null,updatedAt:null})}return r}};import{promises as Be}from"fs";import fe from"path";import Hi from"os";var Gi=3600*1e3;function Me(){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||Hi.homedir();return fe.join(n,".openclaw","credentials")}function tr(e){return e.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"")}function Wi(e){return e.trim().toLowerCase().replace(/[^a-z0-9_-]/g,"")}function Zn(e){return fe.join(Me(),`${tr(e)}-pairing.json`)}function er(e,t){let n=tr(e);if(!t)return fe.join(Me(),`${n}-allowFrom.json`);let r=Wi(t);return r?fe.join(Me(),`${n}-${r}-allowFrom.json`):fe.join(Me(),`${n}-allowFrom.json`)}async function _e(e,t){try{let n=await Be.readFile(e,"utf8");return JSON.parse(n)}catch(n){if(n?.code==="ENOENT")return t;throw n}}async function gt(e,t){await Be.mkdir(fe.dirname(e),{recursive:!0});let n=`${e}.tmp-${Date.now()}`;await Be.writeFile(n,JSON.stringify(t,null,2),"utf8"),await Be.rename(n,e)}function Fi(e){if(!e||typeof e!="object")return!1;let t=e;return t.version===1&&Array.isArray(t.requests)}function Yt(e,t){return e.filter(n=>{let r=Date.parse(n.lastSeenAt??n.createdAt);return Number.isFinite(r)?t-r<=Gi:!0})}var pt=class{async listPending(){let t=Me(),n;try{n=await Be.readdir(t)}catch(s){if(s?.code==="ENOENT")return[];throw s}let r=Date.now(),o=[];for(let s of n){let i=/^([a-z0-9_-]+)-pairing\.json$/i.exec(s);if(!i)continue;let a=i[1].toLowerCase(),l=await _e(fe.join(t,s),null);if(Fi(l))for(let c of Yt(l.requests,r))o.push({channel:a,...c})}return o}async listApproved(){let t=Me(),n;try{n=await Be.readdir(t)}catch(o){if(o?.code==="ENOENT")return[];throw o}let r=[];for(let o of n){let s=/^([a-z0-9_-]+?)(?:-([a-z0-9_-]+))?-allowFrom\.json$/i.exec(o);if(!s)continue;let i=s[1].toLowerCase(),a=s[2]?.toLowerCase(),l=await _e(fe.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:i,...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=er(n,t.accountId),s=await _e(o,{version:1,allowFrom:[]}),i=s.allowFrom.filter(a=>a!==r);return i.length===s.allowFrom.length?{removed:!1}:(await gt(o,{version:1,allowFrom:i}),{removed:!0})}async approve(t){let n=t.channel.toLowerCase(),r=t.code.trim().toUpperCase();if(!r)return{error:"not_found"};let o=Zn(n),s=await _e(o,{version:1,requests:[]}),i=Yt(s.requests,Date.now()),a=i.findIndex(h=>h.code.toUpperCase()===r);if(a<0)return{error:"not_found"};let l=i[a];i.splice(a,1),await gt(o,{version:1,requests:i});let c=l.meta?.accountId,d=er(n,c),p=await _e(d,{version:1,allowFrom:[]});return p.allowFrom.includes(l.id)||(p.allowFrom.push(l.id),await gt(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=Zn(n),s=await _e(o,{version:1,requests:[]}),i=Yt(s.requests,Date.now()),a=i.findIndex(c=>c.code.toUpperCase()===r);if(a<0)return{error:"not_found"};let l=i[a];return i.splice(a,1),await gt(o,{version:1,requests:i}),{entry:{channel:n,...l}}}};var mt=class{async read(){return{"config.read":!0,"config.write":!0,pairing:!0,snapshot:!0}}};import{randomUUID as nr}from"crypto";function ue(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function B(e){return typeof e=="string"&&e.trim()?e.trim():null}function Ji(e){return typeof e=="number"&&Number.isFinite(e)?e:null}function Vi(e){let t=B(e)?.toLowerCase();return t==="user"||t==="assistant"||t==="system"||t==="tool"?t:"unknown"}function Ki(e){let t=Ji(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 zi=/^\[([^\]]+)\]\s*/,Qi=["WebChat","WhatsApp","Telegram","Signal","Slack","Discord","Google Chat","iMessage","Teams","Matrix","Zalo","Zalo Personal","BlueBubbles"],Yi=/^\s*\[message_id:\s*[^\]]+\]\s*$/i,rr=/^\[[A-Za-z]{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2}[^\]]*\] */,Xi=/\[[A-Za-z]{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2}[^\]]*\] */g,or="[Bootstrap pending]",ar="Untrusted context (metadata, do not treat as instructions or commands):",lr=["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):"],Zi=new RegExp([...lr,ar].map(e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|"));function ea(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:Qi.some(t=>e.startsWith(`${t} `))}function sr(e){let t=e.match(zi);if(!t)return e;let n=t[1]??"";return ea(n)?e.slice(t[0].length):e}function ta(e){return/\[message_id:/i.test(e)?e.split(/\r?\n/).filter(t=>!Yi.test(t)).join(`
|
|
3
|
+
`):e}function na(e){let t=e.trim();return lr.some(n=>n===t)}function ra(e,t){if(e[t]?.trim()!==ar)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 oa(e){if(!e)return e;let t=e.replace(rr,"");if(!Zi.test(t))return t;let n=t.split(`
|
|
5
|
+
`),r=[],o=!1,s=!1;for(let i=0;i<n.length;i++){let a=n[i]??"";if(!o&&ra(n,i))break;if(!o&&na(a)){if(n[i+1]?.trim()!=="```json"){r.push(a);continue}o=!0,s=!1;continue}if(o){if(!s&&a.trim()==="```json"){s=!0;continue}if(s){a.trim()==="```"&&(o=!1,s=!1);continue}if(a.trim()==="")continue;o=!1}r.push(a)}return r.join(`
|
|
6
|
+
`).replace(/^\n+/,"").replace(/\n+$/,"").replace(rr,"")}function sa(e){let t=e.indexOf(or);if(t===-1)return e;let n=e.slice(t+or.length),r=null;for(let o of n.matchAll(Xi))r=(o.index??0)+o[0].length;return r===null?"":n.slice(r)}function ia(e){return e.replace(/<\s*think(?:ing)?\s*>[\s\S]*?<\s*\/\s*think(?:ing)?\s*>/gi,"").trim()}function aa(e,t){let r=(t==="user"?sa(oa(ta(sr(e)))):ia(sr(e))).trim();return r||null}function Xt(e){if(typeof e=="string")return e;if(Array.isArray(e)){let n=e.map(r=>Xt(r)).filter(r=>!!r);return n.length?n.join(`
|
|
7
|
+
`):null}let t=ue(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=Xt(t[n]);if(r)return r}return null}function cr(e,t){let n=ue(e),r=Vi(n?.role??n?.type),o=Xt(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?aa(o,r):null,createdAt:Ki(n?.createdAt??n?.timestamp??n?.time)}}function la(e){let t=ue(e);return{messages:(Array.isArray(t?.messages)?t.messages:[]).map((o,s)=>cr(o,s)).filter(o=>!!o.text?.trim()),raw:e}}function ir(e){let t=cr(e,0);return t.text?.trim()?t:null}function ca(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 ft=class{constructor(t){this.transport=t}transport;async ensureSession(t){try{let r=ue(await this.transport.request("sessions.resolve",{key:t.key}));return{key:B(r?.key)??t.key,raw:r}}catch(r){if(!ca(r))throw r}let n=ue(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()||nr(),r=ue(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()||nr(),r=!1,o=null,s=null,i=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=ue(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 O=ir(f.message);O?.text&&d({type:"delta",runId:w,text:O.text,...O.createdAt?{createdAt:O.createdAt}:{}});return}if(_==="final"||_==="aborted"){let O=ir(f.message);O&&d({type:"message",runId:w,message:O}),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}),i=()=>t.signal?.removeEventListener("abort",m))}s=setTimeout(()=>{p("timeout"),a(l)},t.streamTimeoutMs??5*6e4);try{let m=ue(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(),s&&clearTimeout(s),i?.()}}async waitForRun(t){let n=ue(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 la(n)}};import{randomUUID as da}from"crypto";var dr=3,ua=["operator.admin"];function ur(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function ga(e){return ur(e)?.type==="res"}function pa(e){return ur(e)?.type==="event"}function ma(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 fa(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},ht=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(s),n())},s=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(i){this.log("warn",`OpenClaw gateway close handshake failed: ${Ve(i)}`),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((s,i)=>{let a=setTimeout(()=>{i(new U(`OpenClaw gateway connect timeout after ${this.options.connectTimeoutMs??5e3}ms`,"connect_timeout"))},this.options.connectTimeoutMs??5e3);t.addEventListener("open",()=>{clearTimeout(a),s()},{once:!0}),t.addEventListener("error",()=>{clearTimeout(a),i(new U("OpenClaw gateway failed to open","connect_error"))},{once:!0})});let r=ma(this.options.auth),o=await this.sendRequest(t,"connect",{minProtocol:dr,maxProtocol:dr,client:{id:this.options.client?.id??"gateway-client",version:this.options.client?.version??"0.1.1",platform:this.options.client?.platform??process.platform,mode:this.options.client?.mode??"backend"},role:this.options.role??"operator",scopes:this.options.scopes??ua,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=da(),s=JSON.stringify({type:"req",id:o,method:n,params:r}),i=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(s)}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}: ${Ve(a)}`,"send_failed"))}return i}async handleMessage(t){let n;try{n=JSON.parse(await fa(t))}catch(o){this.log("warn",`OpenClaw gateway dropped invalid JSON frame: ${Ve(o)}`);return}if(pa(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(s){this.log("error",`OpenClaw gateway event listener failed: ${Ve(s)}`)}return}if(!ga(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: ${Ve(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 Ve(e){return e instanceof Error?e.message:String(e)}function Ne(e){return typeof e=="string"?e:null}function pr(e,t){return typeof e=="boolean"?e:t}function Zt(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function Ce(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function ha(e){return{path:Ne(e.path),hash:Ne(e.hash),valid:pr(e.valid,!0),lastLoadedAt:Ne(e.lastLoadedAt),snapshot:Zt(e.snapshot)??Zt(e.config)}}function gr(e){let t=Ce(e.restart);return{path:Ne(e.path)??"",hash:Ne(e.hash),config:Zt(e.config)??{},restart:Object.keys(t).length?{scheduled:pr(t.scheduled,!0),delayMs:typeof t.delayMs=="number"?t.delayMs:void 0,coalesced:typeof t.coalesced=="boolean"?t.coalesced:void 0,reason:Ne(t.reason)??void 0}:void 0}}function ya(e){if(!(e instanceof U))return null;let n=Ce(e.details).retryAfterMs;return typeof n!="number"||!Number.isFinite(n)?null:Math.max(0,n)}async function wa(e){await new Promise(t=>setTimeout(t,e))}var ba=18e4,Sa=3;function Ca(e){return e instanceof U&&/config changed since last load|re-run config\.get/i.test(e.message)}var yt=class{constructor(t){this.transport=t}transport;async getSnapshot(){let t=Ce(await this.transport.request("config.get",{}));return ha(t)}async getSchema(){return Ce(await this.transport.request("config.schema",{}))}async lookupSchema(t){return Ce(await this.transport.request("config.schema.lookup",{path:t}))}async patchConfig(t){let n=Ce(await this.requestConfigWriteWithRateLimitRetry("config.patch",{baseHash:t.baseHash,raw:JSON.stringify(t.patch),note:t.note}));return gr(n)}async applyConfig(t){let n=Ce(await this.requestConfigWriteWithRateLimitRetry("config.apply",{baseHash:t.baseHash,raw:JSON.stringify(t.config),note:t.note}));return gr(n)}async requestConfigWriteWithRateLimitRetry(t,n){let r=0,o=0,s={...n};for(;;)try{return await this.transport.request(t,s)}catch(i){let a=ya(i);if(a!=null){let l=a+250;if(r+l>ba)throw i;r+=l,await wa(l);continue}if(Ca(i)){if(t==="config.patch"&&o<Sa){o+=1;let l=await this.getSnapshot();s.baseHash=l.hash??"";continue}throw new en(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:i instanceof Error?i:void 0})}throw i}}},en=class extends Error{code="config_conflict";constructor(t,n){super(t,n),this.name="ConfigConflictError"}};var wt=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()??"",s=r.provider?.trim()??"";!o||!s||n.push({id:o,provider:s,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 bt=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 ka(t.snapshot)}async listSessions(){return this.options.sessionReader.list()}};function mr(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function Ke(e){return typeof e=="string"&&e.trim()?e.trim():null}function fr(e){return typeof e=="boolean"?e:null}function Ra(e){if(typeof e=="number"&&Number.isFinite(e))return new Date(e).toISOString();let t=Ke(e);if(!t)return null;let n=Date.parse(t);return Number.isFinite(n)?new Date(n).toISOString():t}function Pa(e){let t=fr(e.healthy);if(t!==null)return t;let n=Ke(e.status)?.toLowerCase();return n?["healthy","connected","online","ready"].includes(n)?!0:["error","failed","offline","disabled"].includes(n)?!1:null:null}function ka(e){let t=mr(e?.channels);return t?Object.entries(t).flatMap(([n,r])=>{if(n==="defaults")return[];let o=mr(r);if(!o)return[];let s=Ke(o.id)??n;return[{id:s,name:Ke(o.name)??Ke(o.label)??s,enabled:o.disabled===!0?!1:fr(o.enabled)??!0,healthy:Pa(o),updatedAt:Ra(o.updatedAt??o.updated_at)}]}).sort((n,r)=>n.id.localeCompare(r.id)):[]}function Aa(e){return typeof e=="number"&&Number.isFinite(e)?new Date(e).toISOString():null}function va(e){return e==="running"?"active":"idle"}var St=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 s=Aa(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:va(r.status),lastMessageAt:s,updatedAt:s})}return n}};var Ct=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 hr(e,t){return{...e,data:{...e.data,agents:Object.fromEntries(t.map(n=>[n.id,n]))}}}function yr(e,t){return{...e,bridge:{...e.bridge,...t}}}function wr(e,t){return{...e,data:{...e.data,channels:Object.fromEntries(t.map(n=>[n.id,n]))}}}function br(e,t){return{...e,data:{...e.data,models:Object.fromEntries(t.map(n=>[n.id,n]))}}}function Sr(e,t){return{...e,data:{...e.data,sessions:Object.fromEntries(t.map(n=>[n.id,n]))}}}var Le=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 Rt=class extends Le{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 Br,hostname as Ma}from"os";import J from"path";import Or from"fs";import{randomUUID as Cr}from"crypto";import{mkdirSync as Ea,readFileSync as Rr,writeFileSync as Pr,existsSync as kr,renameSync as Ar,unlinkSync as vr}from"fs";import Ia from"path";function Ir(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=Ia.join(e,"bridge-id");if(kr(r)){let a=Rr(r,"utf8").trim();if(Er(a))return a}Ea(e,{recursive:!0});let o=Cr(),s=r+".tmp";try{Pr(s,o+`
|
|
8
|
+
`,{encoding:"utf8",mode:384,flag:"wx"});try{return Ar(s,r),o}catch(a){try{vr(s)}catch(l){let c=l?.code}throw a}}catch(a){if(a?.code!=="EEXIST")throw a}if(!kr(r)){try{vr(s)}catch(l){if(l?.code!=="ENOENT")throw l}let a=Cr();return Pr(s,a+`
|
|
9
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),Ar(s,r),a}let i=Rr(r,"utf8").trim();if(!Er(i))throw new Error(`Race detected writing bridge-id, and winner's file is invalid: ${i.slice(0,40)}`);return i}function Er(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 Tr}from"child_process";import{existsSync as xa}from"fs";import{homedir as Ta,platform as Da}from"os";import nn from"path";var Dr=2e3,tn=null;function rn(){if(tn!==null)return tn.value;let e=null;try{switch(Da()){case"darwin":e=Oa();break;case"linux":e=_a();break;default:e=null}}catch{e=null}return tn={value:e},e}function Oa(){let e=nn.join(Ta(),"Library/LaunchAgents/ai.openclaw.gateway.plist");if(!xa(e))return null;let t;try{t=Tr("/usr/bin/plutil",["-convert","json","-o","-",e],{encoding:"utf8",timeout:Dr})}catch{return null}let n;try{n=JSON.parse(t)}catch{return null}if(!Ba(n))return null;let r=n.WorkingDirectory;return typeof r=="string"&&r.trim()?nn.resolve(r.trim()):null}function _a(){let e=xr(["--user"]);return e||xr([])}function xr(e){let t;try{t=Tr("systemctl",[...e,"show","ai.openclaw.gateway","--property=WorkingDirectory","--no-pager"],{encoding:"utf8",timeout:Dr})}catch{return null}let n=t.match(/^WorkingDirectory=(.+)$/m);if(!n)return null;let r=n[1].trim();return!r||r==="(null)"||r==="/"?null:nn.resolve(r)}function Ba(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ue(e,t){let n=Number.parseInt(e??"",10);return Number.isFinite(n)?n:t}function Mr(e=process.env){let t=Ma(),n=La(e),r=ze(e),o=on(e,r),s=Lr(e,{hostHome:r,stateDir:o}),i=qa(e,r);return Ga(r),Wa(o,r),{bridgeId:Ir(o,{envOverride:e.BRIDGE_ID}),nodeId:e.SOURCE_NODE_ID??t,upstreamKind:e.UPSTREAM_KIND??"openclaw",openClawConfigPath:s,openClawStateDir:o,openClawHostHome:r,openClawDefaultWorkspaceDir:i,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.1",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:Ue(e.OPENCLAW_REQUEST_TIMEOUT_MS,1e4),openClawConnectTimeoutMs:Ue(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:Ue(e.PAIRING_REQUEST_TIMEOUT_MS,1e4),pairingPrintAsciiQr:_r(e.PAIRING_PRINT_ASCII_QR,!0),bindUrlBase:$a(e.TNYMA_BIND_URL_BASE??"http://localhost:21100"),localUiEnabled:_r(e.TNYMA_BRIDGE_LOCAL_UI_ENABLED,!0),localUiHost:e.TNYMA_BRIDGE_LOCAL_UI_HOST?.trim()||"127.0.0.1",localUiPort:Ue(e.TNYMA_BRIDGE_LOCAL_UI_PORT,18788),localUiPublicUrl:Q(e.TNYMA_BRIDGE_LOCAL_UI_PUBLIC_URL),localUiOpenMode:Na(e.TNYMA_BRIDGE_OPEN_ON_START),collectionIntervalMs:Ue(e.BRIDGE_COLLECTION_INTERVAL_MS,15e3),configSyncIntervalMs:Ue(e.BRIDGE_CONFIG_SYNC_INTERVAL_MS,3e4)}}function Na(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 _r(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 La(e){let t=Nr(e.OPENCLAW_GATEWAY_AUTH_MODE),n=Q(e.OPENCLAW_GATEWAY_TOKEN),r=Q(e.OPENCLAW_GATEWAY_PASSWORD),o=ja(e),s=n??o.token,i=r??o.password,a=t??o.authMode??Ua(s,i);if(a==="token")return{mode:a,token:s,password:null};if(a==="password")return{mode:a,token:null,password:i};if(s&&i)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:s,password:i}}function Ua(e,t){return e&&t?null:e?"token":t?"password":null}function Nr(e){let t=e?.trim().toLowerCase();return t==="token"||t==="password"?t:null}function Q(e){let t=e?.trim();return t||null}function $a(e){return e.trim().replace(/\/+$/,"")}function ja(e){let t=ze(e),n=on(e,t),r=Lr(e,{hostHome:t,stateDir:n});if(!r||!Or.existsSync(r))return{authMode:null,token:null,password:null};try{let s=JSON.parse(Or.readFileSync(r,"utf8")).gateway?.auth;return{authMode:Nr(typeof s?.mode=="string"?s.mode:void 0),token:typeof s?.token=="string"?Q(s.token):null,password:typeof s?.password=="string"?Q(s.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 ze(e){let t=Q(e.HOME)??Q(e.USERPROFILE)??Br(),n=Q(e.OPENCLAW_HOST_HOME)??Q(e.OPENCLAW_HOME);if(n)return J.resolve(sn(n,t));let r=rn();return r?J.dirname(r):J.resolve(t)}function on(e,t=ze(e)){let n=Q(e.OPENCLAW_STATE_DIR);if(n)return J.resolve(sn(n,t));let r=rn();return r||J.join(t,".openclaw")}function Lr(e,t={}){let n=Q(e.OPENCLAW_CONFIG_PATH),r=t.hostHome??ze(e);if(n)return J.resolve(sn(n,r));let o=t.stateDir??on(e,r);return J.join(o,"openclaw.json")}function qa(e,t=ze(e)){let n=Q(e.OPENCLAW_PROFILE);return n&&n.toLowerCase()!=="default"?J.join(t,".openclaw",`workspace-${n}`):J.join(t,".openclaw","workspace")}function sn(e,t=Br()){return e==="~"?t:e.startsWith("~/")?J.join(t,e.slice(2)):e}function Ha(e){return e instanceof Error?e.message:String(e)}var Ur=["/etc","/var/run","/proc","/sys","/dev","/boot","/lib","/usr/lib","/usr/bin","/sbin"];function Ga(e){if(!J.isAbsolute(e))throw new Error(`OPENCLAW_HOST_HOME must resolve to an absolute path; got ${e}`);for(let t of Ur)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(!J.isAbsolute(e))throw new Error(`OPENCLAW_STATE_DIR must resolve to an absolute path; got ${e}`);for(let r of Ur)if(e===r||e.startsWith(`${r}/`))throw new Error(`OPENCLAW_STATE_DIR=${e} is on the system path denylist`);if(J.relative(t,e).startsWith("..")&&!["/root/.openclaw","/home","/Users"].some(s=>e===s||e.startsWith(`${s}/`)))throw new Error(`OPENCLAW_STATE_DIR=${e} is outside OPENCLAW_HOST_HOME=${t} and not an allowed container mount`)}async function $r(e){return e.listAgents()}async function jr(e){return{bridge:{...e.bridge,status:"online"},health:{...e.health,lastSuccessAt:new Date().toISOString(),lastError:null}}}async function qr(e){return e.listChannels()}async function Hr(e){return e.listModels()}async function Gr(e){return e.listSessions()}async function kt(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 At=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(`${Fa(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)}),s=await o.json();if(!o.ok||!s.ok){let i=s.ok?`Pairing request failed (${o.status})`:s.error.message;throw new Error(i)}return s.data}};function Fa(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 Ja}from"crypto";var Va=3e4,Wr=0;async function vt(e,t={}){let n=Ka(e.nodeConfig.pairingServerUrl),r=e.store.get(),o=t.displayName?.trim()||e.nodeConfig.pairingDisplayName,s=r.raw?.pairing?.relaySecret??null,i,a=s;if(s&&r.pairing.serverUrl===n&&r.pairing.bridgeId===e.nodeConfig.bridgeId)try{i=await e.pairingPort.refreshAccessCode({bridgeId:e.nodeConfig.bridgeId,relaySecret:s,displayName:o})}catch(m){let f=Date.now();if(f-Wr<Va)throw m;Wr=f;let w={bridgeId:e.nodeConfig.bridgeId,displayName:o,preferredRegion:t.preferredRegion,relaySecret:s},S=await e.pairingPort.registerPairing(w);i=S,a=S.relaySecret}else{let m=s??za(),f={bridgeId:e.nodeConfig.bridgeId,displayName:o,preferredRegion:t.preferredRegion,relaySecret:m},w=await e.pairingPort.registerPairing(f);i=w,a=w.relaySecret}let l=i.pairCode??i.accessCode,c=i.pairCodeExpiresAt??i.accessCodeExpiresAt,d=Fn({serverUrl:n,bridgeId:i.bridgeId,pairCode:l,pairCodeExpiresAt:c,displayName:i.displayName}),p=d.find(m=>m.kind==="qr")?.payload??null,h={serverUrl:n,bridgeId:i.bridgeId,pairCode:l,pairCodeExpiresAt:c,accessCode:i.accessCode,accessCodeExpiresAt:i.accessCodeExpiresAt,displayName:i.displayName,region:i.region,relayUrl:i.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:i.bridgeId,pairCode:l,pairCodeExpiresAt:c,accessCode:i.accessCode,accessCodeExpiresAt:i.accessCodeExpiresAt,displayName:i.displayName,region:i.region,relayUrl:i.relayUrl,qrPayload:p,methods:d,protocolVersion:2,supportsBootstrap:!0},raw:{...m.raw,pairing:{relaySecret:a}}})),h}function Ka(e){let t=e?.trim();if(!t)throw new Error("pairing server URL is not configured");return t}function za(){return`brs_${Ja(24).toString("hex")}`}function Et(){return Date.now()}function Qa(e){return/gateway (connection|transport|failed|closed|not connected|timeout|unavailable)|failed to open|connect timeout/i.test(e)}async function Ya(e){await new Promise(t=>setTimeout(t,e))}var It=class{block=null;current(t=Et()){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=Et();for(;;){let r=this.current();if(!r)return;let o=t-(Et()-n);if(o<=0)return;await Ya(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);Qa(n)&&this.blockFor("upstream_restarting","OpenClaw gateway is not ready",5e3)}blockFor(t,n,r){let o=Et()+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 $(e){return e instanceof y}function Fr(e){return e instanceof Error?e.message:String(e)}var Xa=40,Za=8e3;function Yr(e){return e.trim().toLowerCase().replace(/[^a-z0-9_-]+/g,"-").replace(/^-+/,"").replace(/-+$/,"").slice(0,64)||"main"}function Jr(e){return typeof e=="string"&&e.trim()?e.trim():null}function Vr(e,t=Xa){return typeof e=="number"&&Number.isFinite(e)?Math.max(1,Math.min(200,Math.trunc(e))):t}function Kr(e){return typeof e=="number"&&Number.isFinite(e)?Math.max(0,Math.min(6e4,Math.trunc(e))):Za}function Xr(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 xt(e){let t=Xr(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 an(e,t){let n=Xr(e),r=t?.trim();if(!r)return xt(e);let o=r.toLowerCase(),s=n.find(i=>i.id.toLowerCase()===o||i.name.toLowerCase()===o);if(!s)throw new y(`agent not found: ${r}`,"agent_not_found");return s}function el(e){let t=e.name.trim();return`@${t&&!/\s/.test(t)&&!t.startsWith("@")?t:e.id}`}function ln(e){return`agent:${Yr(e.id)}:tnyma-app:v3:direct`}function cn(e){return`agent:${Yr(e.id)}:tnyma-app:v3:team`}function zr(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=el(t);return{deliveredMessage:`${r} ${n}`.trim(),mention:r}}function tl(e,t){return e==="ok"?"completed":e==="timeout"?"timeout":t==="failed"?"failed":"started"}var Qr=6e4,nl=1024,Tt=class{constructor(t){this.options=t}options;idempotency=new Map;async send(t,n){let r=Jr(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<Qr)return S.result}let s=this.options.store.get(),i=n.agentId?an(s,n.agentId):null,a=xt(s),l=t==="direct"?i??a:a,c=t==="direct"?ln(l):cn(l),d=t==="direct"?{deliveredMessage:r,mention:null}:zr(r,i),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:Kr(n.waitTimeoutMs)})).status??null);let f=await this.readHistoryForSession({kind:t,sessionKey:p.key,targetAgent:t==="direct"?l:i,request:{limit:Vr(n.historyLimit)}}),w={conversationKind:t,sessionKey:p.key,targetAgentId:t==="direct"?l.id:i?.id??null,targetAgentName:t==="direct"?l.name:i?.name??null,mention:d.mention,sentMessage:r,deliveredMessage:d.deliveredMessage,runId:h.runId??null,status:tl(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,s]of this.idempotency)r-s.at>=Qr&&this.idempotency.delete(o);if(this.idempotency.size>=nl){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?an(r,n.agentId):null,s=xt(r),i=t==="direct"?o??s:s,a=t==="direct"?ln(i):cn(i),l=await this.options.chatPort.ensureSession({key:a,agentId:i.id,label:t==="direct"?`Tnyma App Direct v3 - ${i.name}`:"Tnyma App Team Chat v3"});return this.readHistoryForSession({kind:t,sessionKey:l.key,targetAgent:t==="direct"?i:o,request:n})}async stream(t,n,r,o){let s=Jr(n.message);if(!s)throw new y("message is required");let i=this.options.store.get(),a=n.agentId?an(i,n.agentId):null,l=xt(i),c=t==="direct"?a??l:l,d=t==="direct"?ln(c):cn(c),p=t==="direct"?{deliveredMessage:s,mention:null}:zr(s,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:Kr(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:Vr(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 rl="https://clawhub.ai";function he(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function V(e){return typeof e=="string"?e.trim():""}function dn(e){return V(e)||null}function ge(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 Dt(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 ol(e){let t=he(e);if(!t)return[];let n=[t.data,t.items,t.results];for(let r of n){if(Array.isArray(r))return r.map(s=>he(s)).filter(s=>!!s);let o=he(r);if(o){for(let s of[o.items,o.results,o.data])if(Array.isArray(s))return s.map(i=>he(i)).filter(i=>!!i)}}return[]}function sl(e){let n=he(e.package)??e,r=V(n.slug)||V(n.name)||V(n.id);if(!r)return null;let o=he(n.latestVersion)??he(n.latest_version),s=he(n.owner);return{slug:r,displayName:V(n.displayName)||V(n.display_name)||r,summary:V(n.summary),version:V(o?.version)||V(n.version)||V(n.latestVersionString)||"",downloads:ge(n.downloads)??ge(n.download_count)??ge(n.current_installs)??ge(n.all_time_installs),stars:ge(n.stars)??ge(n.star_count),versionCount:ge(n.versions)??ge(n.version_count)??ge(n.versions_count),ownerHandle:V(n.ownerHandle)||V(n.owner_handle)||V(s?.handle),ownerImageUrl:dn(n.ownerImageUrl)||dn(n.owner_image)||dn(s?.image),updatedAt:Dt(n.updatedAt)||Dt(n.updated_at)||Dt(o?.createdAt)||Dt(o?.created_at)}}var $e=class{baseUrl;timeoutMs;constructor(t={}){this.baseUrl=(t.baseUrl??process.env.OPENCLAW_CLAWHUB_URL??process.env.CLAWHUB_URL??rl).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 s=await fetch(o,{headers:{accept:"application/json"},signal:n.signal});if(!s.ok)throw new Error(`ClawHub request failed (${s.status})`);let i=await s.json();return ol(i).map(sl).filter(a=>!!a)}finally{clearTimeout(r)}}};import so from"os";import Re from"fs/promises";import D from"path";function _t(e){return{builtIn:e.builtIn,global:e.global,spec:e.spec}}function M(e){return typeof e=="string"?e.trim():""}function K(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function Zr(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 il(e){return e==null||!Number.isFinite(e)?"\u2014":`${e} v`}function ee(e){return[...new Set([...e].filter(Boolean))]}function Pe(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function eo(e){return Array.isArray(e)?ee(e.map(M)):[]}function to(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function al(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 ll(e){return e==="bundled"||e==="global"||e==="personal"||e==="extra"}function no(e){let t=D.basename(M(e.baseDir)),r=[K(t),K(M(e.skillKey)),K(M(e.name))].filter(Boolean)[0]??"unknown-skill";return{id:r,slug:r}}function cl(e,t){let n=ee([K(t.slug)]);for(let r of n){let o=e.get(r);if(o)return o}}function io(e){return Object.values(e.data.agents).map(t=>t.id.trim()).filter(Boolean).sort()}function dl(e){let t=Pe(e.config.snapshot),n=Pe(t.agents),r=Array.isArray(n.list)?n.list:[];return ee([...io(e),...r.map(o=>M(Pe(o).id))]).sort()}function ro(e,t){let n=new Set([t.id,t.slug,t.name].map(K).filter(Boolean));return e.some(r=>n.has(K(r)))}function ul(e,t){let n=Pe(e.config.snapshot),r=Pe(n.agents),o=Pe(r.defaults),s=to(o,"skills"),i=eo(o.skills),a=Array.isArray(r.list)?r.list.map(d=>Pe(d)):[],l=new Map;for(let d of a){let p=M(d.id);p&&l.set(p,d)}let c=dl(e);for(let d of t.values()){let p=!s||ro(i,d),h=new Set;for(let m of c){let f=l.get(m);((f?to(f,"skills"):!1)&&f?ro(eo(f.skills),d):p)&&h.add(m)}d.globalVisible=p,d.installedAgentKeys=h,d.globalEnabled=d.installed&&d.globalEnabled}}function gl(){return D.join(so.homedir(),".agents","skills")}function pl(e,t){return{id:t?.id??K(e.slug),slug:K(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:Zr(e.downloads),stars:e.stars==null?"\u2014":`\u2605 ${Zr(e.stars)}`,versions:il(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?ee(t.installedAgentKeys):[],hasUpdate:!1,latestVersion:e.version||null,installLocations:t?.installLocations??[]}}function ml(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:ee(e.installedAgentKeys),hasUpdate:!1,latestVersion:null,installLocations:e.installLocations}}function fl(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 Ot=class{constructor(t){this.options=t;this.clawHubClient=t.clawHubClient??new $e}options;clawHubClient;async read(t,n={}){let r=await this.collectLocalStatus(t),o=new Map,s=null,i=null,a={};for(let[f,w]of r.entries()){w.workspaceDir&&f&&(a[f]=w.workspaceDir),!s&&w.managedSkillsDir&&(s=w.managedSkillsDir);for(let S of w.skills??[]){let{id:_,slug:O}=no(S),H=al(M(S.source)),N=o.get(_)??{id:_,slug:O,name:M(S.name)||O,summary:M(S.description),source:H==="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)||O,N.summary=N.summary||M(S.description),N.globalVisible=N.globalVisible||ll(H),N.globalEnabled=N.globalEnabled||S.disabled!==!0,N.needsSetup=N.needsSetup||S.disabled!==!0&&S.eligible===!1,H==="agent"&&f&&N.installedAgentKeys.add(f),N.installLocations.push({source:M(S.source)||"unknown",scope:H,path:M(S.baseDir)||M(S.filePath),agentId:f||null,enabled:S.disabled!==!0,eligible:S.eligible!==!1}),!i&&H==="bundled"&&M(S.baseDir)&&(i=D.dirname(M(S.baseDir))),o.set(_,N)}}ul(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=cl(o,f);w&&d.add(w.id),c.push(pl(f,w))}for(let[f,w]of o.entries()){if(d.has(f))continue;let S=ml(w);fl(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:s??"~/.openclaw/skills",personalSkillsDir:gl(),bundledSkillsDir:i,agentWorkspaceDirs:a}}}async collectLocalStatus(t){let n=io(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 s=>[s,await this.options.skillsClient.status(s)]));for(let[s,i]of o)r.set(s,i);return r}async installedSkillSlugs(t){let n=await this.collectLocalStatus(t),r=new Set;for(let o of n.values())for(let s of o.skills??[]){let{slug:i}=no(s);i&&r.add(i)}return[...r].sort()}async installClawHubSkill(t){return this.options.skillsClient.installClawHubSkill(t)}async promoteClawHubSkillToManaged(t){let n=K(t.slug),r=wl(t.catalog),o=D.join(r,n);if(!Cl(o,n,r))throw new Error(`Unsafe managed skill target path: ${o}`);let s=bl({slug:n,catalog:t.catalog,gateway:t.gateway}),i=await Sl(s),a=await ao(o);if(!i){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(!Rl(i,n,t.catalog))throw new Error(`Unsafe installed skill source path: ${i}`);if(D.resolve(i)===D.resolve(o))return{targetPath:o,sourcePath:i,promoted:!1,alreadyPresent:!0,removedWorkspacePaths:[]};if(await Re.mkdir(r,{recursive:!0}),a){if(!t.force)return await Re.rm(i,{recursive:!0,force:!0}),{targetPath:o,sourcePath:i,promoted:!1,alreadyPresent:!0,removedWorkspacePaths:[i]};await Re.rm(o,{recursive:!0,force:!0})}return await Re.cp(i,o,{recursive:!0,force:!0}),await Re.rm(i,{recursive:!0,force:!0}),{targetPath:o,sourcePath:i,promoted:!0,alreadyPresent:!1,removedWorkspacePaths:[i]}}async updateClawHubSkill(t){return this.options.skillsClient.updateClawHubSkill(t)}async removeSkillInstallations(t,n){let r=[],o=[],s=K(t),i=yl(n);for(let a of n.items)if(hl(a,s))for(let l of a.installLocations){let c=M(l.path);if(!c||l.scope==="bundled"){c&&o.push(c);continue}let p=je(c).find(h=>lo(h,s,i));if(!p){o.push(c);continue}await Re.rm(p,{recursive:!0,force:!0}),r.push(c)}return{removedPaths:ee(r).sort(),skippedPaths:ee(o).sort()}}};function hl(e,t){return[e.slug,e.id,e.name].some(n=>K(n)===t)}function yl(e){return ee([M(e.locations.managedSkillsDir),M(e.locations.personalSkillsDir),...Object.values(e.locations.agentWorkspaceDirs).flatMap(t=>[D.join(t,"skills"),D.join(t,".agents","skills")])]).flatMap(t=>je(t))}function wl(e){let t=M(e.locations.managedSkillsDir)||"~/.openclaw/skills";return D.resolve(je(Qe(t))[0]??Qe(t))}function Qe(e){let t=e.trim();return t==="~"?oo():t.startsWith("~/")?D.join(oo(),t.slice(2)):t}function oo(){return M(process.env.OPENCLAW_HOST_HOME)||so.homedir()}function bl(e){let t=M(e.gateway.targetDir);return ee([...je(Qe(t)),...Object.values(e.catalog.locations.agentWorkspaceDirs).flatMap(n=>je(D.join(Qe(n),"skills",e.slug)))])}async function Sl(e){for(let t of e)if(await ao(t))return t;return null}async function ao(e){if(!e)return!1;try{return(await Re.stat(e)).isDirectory()}catch{return!1}}function je(e){let t=e.replace(/\/+$/,""),n=[t],r=M(process.env.OPENCLAW_HOST_HOME);return r&&t.startsWith(`${r}/.openclaw`)&&n.push(D.join("/root/.openclaw",t.slice(`${r}/.openclaw`.length))),r&&t.startsWith("/root/.openclaw")&&n.push(D.join(r,".openclaw",t.slice(15))),ee(n)}function Cl(e,t,n){if(!D.isAbsolute(e)||!t)return!1;let r=D.resolve(e),o=D.resolve(n);return K(D.basename(r))===t&&r.startsWith(`${o}${D.sep}`)}function Rl(e,t,n){let r=ee(Object.values(n.locations.agentWorkspaceDirs).flatMap(o=>[D.join(o,"skills"),D.join(o,".agents","skills")])).flatMap(o=>je(Qe(o)));return lo(e,t,r)}function lo(e,t,n){if(!D.isAbsolute(e)||!t)return!1;let r=D.resolve(e);return K(D.basename(r))!==t?!1:n.some(o=>{if(!o||!D.isAbsolute(o))return!1;let s=D.resolve(o);return r===s||r.startsWith(`${s}${D.sep}`)})}import{existsSync as co,mkdirSync as Pl,readFileSync as kl,renameSync as Al,writeFileSync as vl}from"fs";import El from"path";var un=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()}},Bt=class{constructor(t,n){this.filePath=t;let r=this.mergePersisted(n);this.inner=new un(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(!co(this.filePath))return null;try{let t=kl(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=El.dirname(this.filePath);try{co(r)||Pl(r,{recursive:!0,mode:448});let o=`${this.filePath}.tmp`;vl(o,JSON.stringify(n,null,2),{mode:384}),Al(o,this.filePath)}catch(o){let s=o instanceof Error?o.message:String(o);process.stderr.write(`[bridge-store] failed to persist pairing slice to ${this.filePath}: ${s}
|
|
10
|
+
`)}}};import Kc from"path";function Il(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 xl(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 s=e[o],i=n[o];if(s.kind==="literal"){if(s.value!==i)return null}else try{r[s.name]=decodeURIComponent(i)}catch{return null}}return r}var gn=class{routes=[];register(t,n,r,o){this.routes.push({method:t,segments:Il(n),handler:r,remoteAllowed:o.remoteAllowed})}async dispatch(t,n,r={}){let o=r.source??"local";for(let s of this.routes){if(s.method!==n.method)continue;let i=xl(s.segments,n.path);if(!i)continue;if(o==="relay"&&!s.remoteAllowed)return{status:403,body:{ok:!1,error:{code:"forbidden",message:"endpoint not exposed over relay"}}};let a={...n,query:{...n.query??{},...i}};return s.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 pn(){return new gn}var mn=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(s=>{o=s}),await r,this.pending-=1,this.running=!0;try{return await n()}finally{this.running=!1,o()}}},Y=new mn;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 X(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 X(Fr(e),$(e)?e.code:"bad_request")}function fn(e,t="not_found"){return{status:404,headers:{"content-type":"application/json; charset=utf-8"},body:{ok:!1,error:{code:t,message:e}}}}function hn(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function Mt(e){return typeof e=="string"&&e.trim()?e.trim():null}function Ye(e){if(!e)return;let t=Number(e);return Number.isFinite(t)?t:void 0}var uo=async e=>{let t=e.store.get();return R(P(ot(t),I(e)))},go=async e=>R(P(e.store.get().capabilities,I(e))),po=async e=>R(P(Xn(e.store.get()),I(e))),mo=async e=>R(P(it(e.store.get()),I(e))),fo=async e=>R(P(Vn(e.store.get()),I(e))),ho=async e=>R(P(lt(e.store.get()),I(e))),yo=async e=>R(P(Qn(e.store.get()),I(e))),wo=async e=>R(P(at(e.store.get()),I(e))),bo=async e=>R(P(ct(e.store.get()),I(e))),So=async(e,t)=>{let n=t.query?.sessionId;if(!n)return fn("Session id required");let r=Yn(e.store.get(),n);return r?R(P(r,I(e))):fn(`Session ${n} not found`,"session_not_found")},Co=async e=>{let t=e.operationGate.current(),n=Y.status();return R(P({...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)))},Ro=async e=>R(P(st(e.store.get()),I(e))),Po=async e=>{let t=e.store.get();return R(P({qrPayload:t.pairing.qrPayload??null},I(e)))};import ko from"qrcode";async function Nt(e){let t=e.trim();if(!t)throw new Error("pairing QR payload is empty; register pairing before requesting qr.png");return ko.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 ko.toString(t,{type:"terminal",small:!0,errorCorrectionLevel:"L",margin:0})}function Tl(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 vo=async(e,t)=>{let n=Tl(t.body),r=await vt({store:e.store,pairingPort:e.pairingPort,nodeConfig:e.nodeConfig},n);return R(P(r,I(e)))},Eo=async e=>{let t=e.store.get().pairing.qrPayload??"";if(!t)return X("No active pairing payload","no_pairing");let n=await Nt(t);return{status:200,headers:{"content-type":"image/png","content-length":String(n.byteLength),"content-encoding":"base64"},body:n.toString("base64")}};function yn(e){return!e||typeof e!="object"?null:e}var Io=async e=>{let t=await e.pairingClient.listPending();return R(P({items:t},I(e)))},xo=async(e,t)=>{let n=yn(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 s=await e.pairingClient.approve({channel:r,code:o});return"error"in s?A(new y(`pairing code not found: ${o}`)):R(P(s,I(e)))}catch(s){if($(s))return A(s);throw s}},To=async e=>{let t=await e.pairingClient.listApproved();return R(P({items:t},I(e)))},Do=async(e,t)=>{let n=yn(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 s=await e.pairingClient.revoke({channel:r,id:o,accountId:n.accountId?.trim()||void 0});return R(P(s,I(e)))}catch(s){if($(s))return A(s);throw s}},Oo=async(e,t)=>{let n=yn(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 s=await e.pairingClient.reject({channel:r,code:o});return"error"in s?A(new y(`pairing code not found: ${o}`)):R(P(s,I(e)))}catch(s){if($(s))return A(s);throw s}};async function _o(e,t){let n=rt(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(s=>({...s,meta:{...s.meta,collectedAt:o},config:{path:r.path,hash:r.hash??s.config.hash,valid:!0,lastLoadedAt:o,snapshot:r.config},health:{lastSuccessAt:o,lastError:null}})),r}async function Bo(e){return e.upstreamConfigPort.getSchema()}async function Mo(e,t){return e.upstreamConfigPort.lookupSchema(t)}async function qe(e,t){let n=rt(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(s=>({...s,meta:{...s.meta,collectedAt:o},config:{path:r.path,hash:r.hash??s.config.hash,valid:!0,lastLoadedAt:o,snapshot:r.config},health:{lastSuccessAt:o,lastError:null}})),r}function Lt(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function No(e,t){if(e!==void 0){if(typeof e!="string")throw new y(`${t} must be a string`);return e}}function Lo(e,t){if(typeof e!="string"||!e.trim())throw new y(`${t} is required`);return e}function Uo(e){if(!Lt(e))throw new y("Patch request body must be an object");if(!Lt(e.patch))throw new y("patch must be an object");return{baseHash:Lo(e.baseHash,"baseHash"),patch:e.patch,note:No(e.note,"note")}}function $o(e){if(!Lt(e))throw new y("Apply request body must be an object");if(!Lt(e.config))throw new y("config must be an object");return{baseHash:Lo(e.baseHash,"baseHash"),config:e.config,note:No(e.note,"note")}}var jo=async e=>{let t=await kt({nodeConfig:e.nodeConfig,store:e.store,upstreamConfigPort:e.upstreamConfigPort});return R(P(t,I(e)))},qo=async e=>{let t=await Bo({upstreamConfigPort:e.upstreamConfigPort});return R(P(t,I(e)))},Ho=async(e,t)=>{let n=t.query?.path;if(!n)return X("path query parameter is required");let r=await Mo({upstreamConfigPort:e.upstreamConfigPort},n);return R(P(r,I(e)))},Go=async(e,t)=>{let n;try{n=Uo(t.body)}catch(r){return A(r)}return Y.run("config:patch",async()=>{let r;try{r=await qe({store:e.store,upstreamConfigPort:e.upstreamConfigPort,writePolicy:e.configWritePolicy,operationGate:e.operationGate},n)}catch(o){if($(o))return A(o);throw o}return R(P(r,I(e)))})},Wo=async(e,t)=>{let n;try{n=$o(t.body)}catch(r){return A(r)}return Y.run("config:apply",async()=>{let r;try{r=await _o({store:e.store,upstreamConfigPort:e.upstreamConfigPort,writePolicy:e.configWritePolicy,operationGate:e.operationGate},n)}catch(o){if($(o))return A(o);throw o}return R(P(r,I(e)))})};import{chmod as _l,mkdir as $t,readFile as Zo,readdir as Bl,rm as Xe,writeFile as kn}from"fs/promises";import{homedir as jt,tmpdir as Ml}from"os";import E from"path";var Fo="<!-- TEAM-FLOW:start managed-by=tnyma-web -->",wn="<!-- TEAM-FLOW:end -->";function Dl(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n")}function Jo(e){let t=new Map;for(let n of e)for(let r of n.agents)t.set(r,n.id);return t}function Vo(e,t){return e.mode==="serial"||e.mode==="parallel"?e.mode:t<=1||(e.waitFor??[]).some(r=>r.endsWith(":*"))?"serial":"parallel"}function Ko(e){let t=Jo(e),n=["flowchart TD"];e.forEach((r,o)=>{let s=Dl(`${r.label}
|
|
11
|
+
mode: ${Vo(r,o)}
|
|
12
|
+
agents: ${r.agents.join(", ")}`);n.push(` ${r.id}["${s}"]`)});for(let r of e){let o=r.waitFor??[];if(o.length===0)continue;let s=new Set;for(let i of o)i.endsWith(":*")?s.add(i.slice(0,-2)):t.has(i)&&s.add(t.get(i));for(let i of s)i!==r.id&&n.push(` ${i} --> ${r.id}`)}return`${n.join(`
|
|
13
13
|
`)}
|
|
14
|
-
`}function
|
|
14
|
+
`}function zo(e){let t=Jo(e),n=[];return e.forEach((r,o)=>{let s=o+1,i=`mode: ${Vo(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(`${s}. **${r.label}** \u2014 ${i}; ${a}${c}`)}),`${n.join(`
|
|
15
15
|
`)}
|
|
16
|
-
`}function
|
|
16
|
+
`}function Ol(e){let t=0;if(e.startsWith(`---
|
|
17
17
|
`)){let n=e.indexOf(`
|
|
18
18
|
---`,4);n!==-1&&(t=n+4,e[t]===`
|
|
19
19
|
`&&(t+=1))}for(;e[t]===`
|
|
20
20
|
`;)t+=1;if(e.startsWith("# ",t)){let n=e.indexOf(`
|
|
21
21
|
`,t);for(t=n===-1?e.length:n+1;e[t]===`
|
|
22
|
-
`;)t+=1}return t}function
|
|
23
|
-
`),o=`${
|
|
22
|
+
`;)t+=1}return t}function Qo(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=`${Fo}
|
|
24
24
|
${r}
|
|
25
|
-
${
|
|
25
|
+
${wn}`,s=e.indexOf(Fo),i=e.indexOf(wn),a;if(s>=0&&i>s){let l=e.slice(0,s),c=e.slice(i+wn.length);a=`${l}${o}${c}`}else{let l=Ol(e),c=e.slice(0,l),d=e.slice(l),p=c.length>0&&!c.endsWith(`
|
|
26
26
|
|
|
27
27
|
`)?c.endsWith(`
|
|
28
28
|
`)?`
|
|
@@ -36,23 +36,23 @@ ${sn}`,i=e.indexOf(Io),s=e.indexOf(sn),a;if(i>=0&&s>i){let l=e.slice(0,i),c=e.sl
|
|
|
36
36
|
`)?"":`
|
|
37
37
|
`;a=`${c}${p}${o}${h}${d}`}return a.endsWith(`
|
|
38
38
|
`)?a:`${a}
|
|
39
|
-
`}var
|
|
39
|
+
`}var Nl=/^[a-z0-9][a-z0-9_-]{0,63}$/,Ll=new Set(["main"]);function te(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 ye(e,t){let n=q(e,t);if(!n)throw new y(`${t} is required`);return n}function Sn(e,t){if(e!=null){if(typeof e!="boolean")throw new y(`${t} must be a boolean`);return e}}function ke(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 Ul(e,t="id"){let n=ye(e,t).toLowerCase();if(!Nl.test(n)||Ll.has(n))throw new y(`${t} is not a valid agent id`,"invalid_agent_id");return n}function $l(e){if(e==null)return;if(typeof e=="string")return{primary:e.trim()};if(!te(e))throw new y("model must be a string or object");let t=q(e.primary,"model.primary"),n=ke(e.fallbacks,"model.fallbacks");return t||n?{...t?{primary:t}:{},...n?{fallbacks:n}:{}}:void 0}function jl(e){if(e==null)return;if(!te(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 ql(e){if(e==null)return;if(!te(e))throw new y("tools must be an object");let t=q(e.profile,"tools.profile"),n=ke(e.allow,"tools.allow"),r=ke(e.alsoAllow,"tools.alsoAllow"),o=ke(e.deny,"tools.deny"),s=Sn(te(e.elevated)?e.elevated.enabled:void 0,"tools.elevated.enabled");return{...t?{profile:t}:{},...n?{allow:n}:{},...r?{alsoAllow:r}:{},...o?{deny:o}:{},...s!==void 0?{elevated:{enabled:s}}:{}}}function qt(e,t="agent"){if(!te(e))throw new y(`${t} must be an object`);let n=Ul(e.id,`${t}.id`),r=ye(e.name,`${t}.name`),o=ye(e.workspace,`${t}.workspace`);return{id:n,name:r,workspace:o,agentDir:q(e.agentDir,`${t}.agentDir`),copyPortableAuthProfiles:Sn(e.copyPortableAuthProfiles,`${t}.copyPortableAuthProfiles`),model:$l(e.model),runtimeId:q(e.runtimeId,`${t}.runtimeId`),thinkingDefault:q(e.thinkingDefault,`${t}.thinkingDefault`),reasoningDefault:q(e.reasoningDefault,`${t}.reasoningDefault`),skills:ke(e.skills,`${t}.skills`),memorySearchEnabled:Sn(e.memorySearchEnabled,`${t}.memorySearchEnabled`),sandbox:jl(e.sandbox),tools:ql(e.tools),files:Hl(e.files,`${t}.files`),channelBinding:es(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(!te(n))throw new y(`${t}.${r} must be an object`);return{path:ye(n.path,`${t}.${r}.path`),content:ye(n.content,`${t}.${r}.content`)}})}}function es(e,t){if(e==null)return;if(!te(e))throw new y(`${t} must be an object`);let n=ye(e.channel,`${t}.channel`),r=q(e.accountId,`${t}.accountId`);return{channel:n,...r?{accountId:r}:{},comment:q(e.comment,`${t}.comment`),auth:te(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 Ht(e){if(!te(e))throw new y("team request body must be an object");let t=qt(e.controller,"controller"),r=(Array.isArray(e.members)?e.members:[]).map((i,a)=>qt(i,`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 i of r){if(i.id===t.id||o.has(i.id))throw new y("team agent ids must be unique","duplicate_agent_id");o.add(i.id)}let s=Gl(e.flow,"flow");return Wl(s,t,r),{controller:t,members:r,flow:s,channelBinding:es(e.channelBinding,"channelBinding")}}function Gl(e,t){if(e!=null){if(!Array.isArray(e))throw new y(`${t} must be an array`);return e.map((n,r)=>{if(!te(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:ye(n.id,`${t}.${r}.id`),label:ye(n.label,`${t}.${r}.label`),mode:o,note:q(n.note,`${t}.${r}.note`),agents:ke(n.agents,`${t}.${r}.agents`)??[],waitFor:ke(n.waitFor,`${t}.${r}.waitFor`)}})}}function Wl(e,t,n){if(!e||e.length===0)return;let r=new Set(e.map(s=>s.id)),o=new Set([t.id,...n.map(s=>s.id)]);for(let s of e){for(let i of s.agents)if(!o.has(i))throw new y(`flow entry ${s.id} references unknown agent ${i}`,"unknown_agent_in_flow");for(let i of s.waitFor??[])if(i.endsWith(":*")){let a=i.slice(0,-2);if(!r.has(a))throw new y(`flow waitFor ${i} references unknown step ${a}`,"unknown_step_in_waitfor")}else if(!o.has(i))throw new y(`flow waitFor ${i} references unknown agent ${i}`,"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 Rn(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 Fl(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 Pn(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:Fl(t.auth.secretRef,"channelBinding.auth.secretRef"),s=r??o;if(!n&&!s)return null;let i=t.accountId??e;return{feishu:{enabled:!0,defaultAccount:i,accounts:{[i]:{enabled:!0,name:i,...n?{appId:n}:{},...s?{appSecret:s}:{},domain:"feishu",groupPolicy:"open"}}}}}function ts(e){let t=[Rn(e.id,e.channelBinding)].filter(Boolean),n=Pn(e.id,e.channelBinding);return{agents:{list:[Cn(e)]},...t.length?{bindings:t}:{},...n?{channels:n}:{}}}function Jl(e){return!(e instanceof U)||e.code!=="INVALID_REQUEST"?null:new y(e.message,"openclaw_invalid_request")}function Vl(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 Kl=[1e3,2e3,3e3,5e3,8e3,13e3,21e3];function zl(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 Ql(e){await new Promise(t=>setTimeout(t,e))}async function bn(e,t){for(let n=0;;n+=1)try{return await t()}catch(r){let o=Kl[n];if(o===void 0||!zl(r))throw r;e.operationGate.blockForError(r),await Ql(o)}}function ns(e){let t=e.members.map(l=>l.id),n=e.controller.channelBinding??e.channelBinding,r=[],o=[],s=Rn(e.controller.id,n);s&&r.push(s);let i=Pn(e.controller.id,n);i&&o.push(i);for(let l of e.members){let c=l.channelBinding;if(!c)continue;let d=Rn(l.id,c);d&&r.push(d);let p=Pn(l.id,c);p&&o.push(p)}let a=Yl(o);return{agents:{list:[Cn(e.controller,t),...e.members.map(l=>Cn(l))]},...r.length?{bindings:r}:{},...a?{channels:a}:{}}}function Yl(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 s=t[r]??{},i=o;for(let[a,l]of Object.entries(i))if(a==="accounts"&&l&&typeof l=="object"&&!Array.isArray(l)){let c=s.accounts??{};s.accounts={...c,...l}}else s[a]===void 0&&(s[a]=l);t[r]=s}return t}function rs(e,t=[]){return{patch:e,fileWrites:t?.map(n=>n.path)??[]}}function oe(e){return e==="~"?jt():e.startsWith("~/")?E.join(jt(),e.slice(2)):e}var Xl=["/etc","/var/run","/proc","/sys","/dev","/boot","/lib","/usr/lib","/usr/bin","/sbin","/root"];function os(e,t){let n=E.resolve(oe(e));for(let s of Xl)if(n===s||n.startsWith(`${s}/`))throw new y(`${t} is on the system path denylist: ${s}`,"invalid_agent_path");let r=[jt(),"/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(Ml())),!r.some(s=>s?n===s||n.startsWith(`${s}/`):!1))throw new y(`${t} must live under the user's home or OpenClaw state dir`,"invalid_agent_path")}function ss(e,t){os(e,"workspace");let n=E.resolve(oe(e)),r=E.resolve(E.isAbsolute(oe(t))?oe(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 is(e){let t=[];for(let n of e){n.agentDir&&(os(n.agentDir,"agentDir"),await $t(E.resolve(oe(n.agentDir)),{recursive:!0,mode:448}));for(let r of n.files??[]){let o=ss(n.workspace,r.path);await $t(E.dirname(o),{recursive:!0}),await kn(o,r.content.endsWith(`
|
|
40
40
|
`)?r.content:`${r.content}
|
|
41
|
-
`,"utf8"),t.push(o)}}return t}function
|
|
42
|
-
`,{encoding:"utf8",mode:384}),await
|
|
43
|
-
`}let o=
|
|
41
|
+
`,"utf8"),t.push(o)}}return t}function as(e,t){if(!e)return!1;let n=t.toLowerCase(),r=T(e.agents);return(Array.isArray(r?.list)?r.list:[]).some(s=>{let i=T(s);return W(i?.id)?.toLowerCase()===n})}function An(e,t){if(!e)return null;let n=T(e.agents),r=Array.isArray(n?.list)?n.list:[];for(let o of r){let s=T(o);if(W(s?.id)?.toLowerCase()!==t)continue;let i=W(s?.agentDir);return i?E.resolve(oe(i)):null}return null}function Zl(e,t){if(t.agentDir)return E.resolve(oe(t.agentDir));let n=An(e.snapshot,t.id);return n||E.join(Ze(e),"agents",t.id,"agent")}function ec(e){return An(e.snapshot,"main")??E.join(Ze(e),"agents","main","agent")}function tc(e){let t=T(e);if(!t||t.copyToAgents===!1)return!1;let n=W(t.type)??W(t.mode);return n==="oauth"?t.copyToAgents===!0:n==="api_key"||n==="token"}function nc(e){let t=T(e.profiles);if(!t)return null;let n=Object.fromEntries(Object.entries(t).filter(([,r])=>tc(r)));return Object.keys(n).length===0?null:{version:typeof e.version=="number"&&Number.isFinite(e.version)?e.version:1,profiles:n}}async function Yo(e,t){let n;try{n=await Zo(e,"utf8")}catch(r){if(r.code==="ENOENT")return null;throw r}try{let r=JSON.parse(n);if(!te(r))throw new Error("expected object");return r}catch{throw new y(`${t} auth-profiles.json is invalid`,"invalid_auth_profiles")}}function rc(e,t){return t.copyPortableAuthProfiles===!1?!1:!as(e.snapshot,t.id)}function oc(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 ls(e,t){let n=t.filter(a=>rc(e,a));if(n.length===0)return[];let r=E.join(ec(e),"auth-profiles.json"),o=await Yo(r,"main");if(!o)return[];let s=nc(o);if(!s)return[];let i=[];for(let a of n){let l=Zl(e,a),c=E.join(l,"auth-profiles.json");if(E.resolve(c)===E.resolve(r))continue;let d=await Yo(c,a.id),p=oc(s,d);await $t(l,{recursive:!0,mode:448}),await kn(c,`${JSON.stringify(p,null,2)}
|
|
42
|
+
`,{encoding:"utf8",mode:384}),await _l(c,384).catch(()=>{}),i.push(c)}return i}function sc(e){let t=e.replace(/[\r\n ]+/g," ").trim();return t.length>0?t:"Team Controller"}async function ic(e,t){if(!t||t.length===0)return null;let n=ss(e.workspace,"AGENTS.md"),r="";try{r=await Zo(n,"utf8")}catch(a){if(a.code!=="ENOENT")throw a;r=`# ${sc(e.name)}
|
|
43
|
+
`}let o=Ko(t),s=zo(t),i=Qo(r,o,s);return await $t(E.dirname(n),{recursive:!0}),await kn(n,i,"utf8"),n}async function vn(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 qe({store:e.store,upstreamConfigPort:e.upstreamConfigPort,writePolicy:e.configWritePolicy,operationGate:e.operationGate},{baseHash:r.hash,patch:t,note:n})}var cs=async(e,t)=>{try{let n=qt(t.body);return R(P(rs(ts(n),n.files)))}catch(n){return A(n)}},ds=async(e,t)=>{let n;try{n=qt(t.body)}catch(r){return A(r)}return Y.run(`agent:create:${n.id}`,async()=>{try{let r=await e.upstreamConfigPort.getSnapshot(),o=ts(n),s=await vn(e,o,`tnyma-web:create-agent:${n.id}`),i=await is([n]);i.push(...await ls(r,[n])),await e.refreshData();let a={agentIds:[n.id],patch:o,config:s,fileWrites:i};return R(P(a,I(e)))}catch(r){if($(r))return A(r);throw r}})},us=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 Y.run(`agent:delete:${n}`,async()=>{try{await e.operationGate.waitUntilReady();let o=await bn(e,()=>e.upstreamConfigPort.getSnapshot()),s=o.snapshot??e.store.get().config.snapshot,i=n.toLowerCase(),a=as(s,i),l=cc(s,i),c=hc(s,i),d=l.patch||c?{...l.patch??{},...c??{}}:null,p={ok:!0,agentId:n,removedBindings:0};if(a)try{p=await bn(e,()=>e.agentClient.deleteAgent({agentId:n,deleteFiles:r}))}catch(h){if(!Vl(h))throw h}d&&(await bn(e,()=>vn(e,d,`tnyma-web:delete-agent-cleanup:${n}`)),await dc(o,l.removedAccounts)),r&&(a?await mc(o,i):await fc(o,i));try{await e.refreshData()}catch(h){e.operationGate.blockForError(h)}return R(P(p,I(e)))}catch(o){if($(o))return A(o);let s=Jl(o);if(s)return A(s);throw o}})};function T(e){return!e||typeof e!="object"||Array.isArray(e)?null:e}function W(e){return typeof e=="string"&&e.trim()?e.trim():null}function Ut(e){if(!Array.isArray(e))return null;let t=[];for(let n of e)typeof n=="string"&&t.push(n);return t}function Xo(e){return JSON.stringify([e.channel,e.accountId])}function gs(e){let t=T(e.match);if(!t)return null;let n=W(t.channel),r=W(t.accountId);return!n||!r?null:{channel:n,accountId:r}}function ac(e,t){if(!e)return[];let n=new Map,r=Array.isArray(e.bindings)?e.bindings:[];for(let s of r){let i=T(s);if(!i||W(i.agentId)?.toLowerCase()!==t)continue;let l=gs(i);l&&n.set(Xo(l),l)}let o=T(e.channels);if(o)for(let[s,i]of Object.entries(o)){let a=T(i),l=T(a?.accounts);if(l)for(let c of Object.keys(l)){if(c.toLowerCase()!==t)continue;let d={channel:s,accountId:c};n.set(Xo(d),d)}}return[...n.values()]}function lc(e,t,n){let r=Array.isArray(e.bindings)?e.bindings:[];for(let o of r){let s=T(o);if(!s)continue;let i=W(s.agentId)?.toLowerCase();if(!i||i===t)continue;let a=gs(s);if(a?.channel===n.channel&&a.accountId===n.accountId)return!0}return!1}function cc(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 i of ac(e,t)){if(lc(e,t,i))continue;let a=T(n[i.channel]);if(!a)continue;let l=T(a.accounts),c=!!l&&Object.prototype.hasOwnProperty.call(l,i.accountId),d=a.defaultAccount===i.accountId;if(!c&&!d)continue;let p=r.get(i.channel)??new Set;p.add(i.accountId),r.set(i.channel,p),o.push(i)}let s={};for(let[i,a]of r.entries()){let l=T(n[i]);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=W(l.defaultAccount);f&&a.has(f)&&(c.defaultAccount=h[0]??null),Object.keys(c).length>0&&(s[i]=c)}return Object.keys(s).length===0?{patch:null,removedAccounts:[]}:{patch:{channels:s},removedAccounts:o}}function Ze(e){let t=T(e.runtimePaths),n=W(t?.stateDir);if(n)return n;let r=W(e.path);return r?E.dirname(r):E.join(jt(),".openclaw")}async function dc(e,t){if(t.length===0)return;let n=Ze(e);await Promise.all(t.filter(r=>r.channel==="feishu").map(r=>Xe(E.join(n,"credentials",`feishu-${r.accountId}-allowFrom.json`),{force:!0})))}var uc=new Set([".DS_Store",".localized","Thumbs.db","desktop.ini"]);function gc(e,t){let n=E.relative(e,t);return n===""||!!n&&!n.startsWith("..")&&!E.isAbsolute(n)}function pc(e,t,n){if(!e)return!1;let r=T(e.agents),o=Array.isArray(r?.list)?r.list:[];for(let s of o){let i=T(s);if(!i)continue;let a=W(i.id)?.toLowerCase();if(!(!a||a===t))for(let l of["agentDir","workspace"]){let c=W(i[l]);if(c&&gc(n,E.resolve(oe(c))))return!0}}return!1}async function mc(e,t){let n=E.resolve(oe(Ze(e))),r=E.join(n,"agents",t),o=E.join(r,"agent"),s=An(e.snapshot,t)??o;if(E.resolve(s)!==o||pc(e.snapshot,t,r))return;let i;try{i=await Bl(r,{withFileTypes:!0})}catch(l){if(l.code==="ENOENT")return;throw l}i.every(l=>l.isFile()&&uc.has(l.name))&&await Xe(r,{recursive:!0,force:!0})}async function fc(e,t){let n=E.resolve(oe(Ze(e)));await Promise.all([Xe(E.join(n,"agents",t),{recursive:!0,force:!0}),Xe(E.join(n,"workspace",t),{recursive:!0,force:!0}),Xe(E.join(n,"agent-workspaces",t),{recursive:!0,force:!0})])}function hc(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=Ut(d.allowAgents),h=Ut(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 s=null,i=T(n.defaults);if(i){let a=T(i.subagents);if(a){let l=Ut(a.allowAgents),c=Ut(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&&(s={subagents:d})}}return r.length===0&&!s?null:{agents:{...r.length>0?{list:r}:{},...s?{defaults:s}:{}}}}var ps=async(e,t)=>{try{let n=Ht(t.body),r=[n.controller,...n.members].flatMap(o=>o.files??[]);return R(P(rs(ns(n),r)))}catch(n){return A(n)}};async function En(e,t,n={}){return Y.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(),s=ns(t),i=await vn(e,s,`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 is([t.controller,...t.members]),c=[...l],d=await ls(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 ic(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:s,config:i,fileWrites:c}})}var ms=async(e,t)=>{let n;try{n=Ht(t.body)}catch(r){return A(r)}try{let r=await En(e,n);return R(P(r,I(e)))}catch(r){if($(r))return A(r);throw r}};function fs(e){if(!e||typeof e!="object"||Array.isArray(e))throw new Error("request body must be an object");let t=e,n=Mt(t.message);if(!n)throw new Error("message is required");return{agentId:Mt(t.agentId),message:n,waitForReply:t.waitForReply===!0,waitTimeoutMs:hn(t.waitTimeoutMs),historyLimit:hn(t.historyLimit),idempotencyKey:Mt(t.idempotencyKey)??void 0}}function hs(e){return async(t,n)=>{let r;try{r=fs(n.body)}catch(i){return X(i instanceof Error?i.message:String(i))}let o=r.idempotencyKey??n.id??void 0,s;try{s=await t.chatService.send(e,{...r,idempotencyKey:o})}catch(i){if($(i))return A(i);throw i}return R(P(s,I(t)))}}function ys(e){return async(t,n)=>{let r;try{r=await t.chatService.history(e,{agentId:n.query?.agentId??null,limit:Ye(n.query?.limit),maxChars:Ye(n.query?.maxChars)})}catch(o){if($(o))return A(o);throw o}return R(P(r,I(t)))}}function ws(e){return async(t,n)=>{let r;try{r=fs(n.body)}catch(m){return X(m instanceof Error?m.message:String(m))}let o=new AbortController,s=[],i=null,a=!1,l=m=>{if(!a){if(i){i({value:m,done:!1}),i=null;return}s.push(m)}},c=()=>{a||(a=!0,i&&(i({value:void 0,done:!0}),i=null))},d=setInterval(()=>{a||l(`: ping
|
|
44
44
|
|
|
45
45
|
`)},15e3),p=m=>{l(`data: ${JSON.stringify(m)}
|
|
46
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
|
|
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 s.length>0?Promise.resolve({value:s.shift(),done:!1}):a?Promise.resolve({value:void 0,done:!0}):new Promise(m=>{i=m})},return(){return o.abort(),c(),Promise.resolve({value:void 0,done:!0})}}}}}}}var bs=hs("direct"),Ss=hs("group"),Cs=ys("direct"),Rs=ys("group"),Ps=ws("direct"),ks=ws("group");function Wt(e){let t=e.store.get();return{collectedAt:t.meta.collectedAt,schemaVersion:t.meta.schemaVersion}}async function As(e,t={}){let n=await e.skillCatalogService.read(e.store.get(),t);return P(_t(n),Wt(e))}async function vs(e,t){let n=xn(t.slug),r=t.scope??"global",o=xs(t.agentIds);if(r==="agents"&&o.length===0)throw new y("agentIds are required when installing a skill to agents");let s=e.store.get(),i=await e.skillCatalogService.installedSkillSlugs(s),a=await e.skillCatalogService.read(s),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 yc(e,{slug:n,fallbackCatalog:a,gateway:l,force:t.force,originalError:c}):null,p=await Ms(e,bc(s.config.snapshot,{slug:n,scope:r,agentIds:o,existingSkillSlugs:i}),`Install ClawHub skill ${n}`),h=await Dn(e,a,{type:"install",slug:n,scope:r,agentIds:o});return P({slug:n,message:r==="global"?`Installed ${n} globally`:`Installed ${n} to agents`,gateway:{...Ae(l),...c?{gatewayInstallError:wc(c)}:{},...d?{globalInstall:d}:{}},config:Ns(p),catalog:h},Wt(e))}async function yc(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 Es(e,t){let n=xn(t.slug),r=await e.skillCatalogService.read(e.store.get()),o=await e.skillCatalogService.updateClawHubSkill({slug:n}),s=await Dn(e,r,{type:"update",slug:n});return P({slug:n,message:`Updated ${n}`,gateway:Ae(o),config:null,catalog:s},Wt(e))}async function Is(e,t){let n=xn(t.slug),r=t.scope??"all",o=xs(t.agentIds);if(r==="agents"&&o.length===0)throw new y("agentIds are required when uninstalling a skill from agents");let s=e.store.get(),i=await e.skillCatalogService.installedSkillSlugs(s),a=await e.skillCatalogService.read(s),l=await Ms(e,Sc(s.config.snapshot,{slug:n,scope:r,agentIds:o,existingSkillSlugs:i}),`Uninstall ClawHub skill ${n}`),c=await e.skillCatalogService.removeSkillInstallations(n,a),d=await Dn(e,a,{type:"uninstall",slug:n,scope:r,agentIds:o});return P({slug:n,message:`Removed ${n} assignment`,gateway:Ae(c),config:Ns(l),catalog:d},Wt(e))}function xn(e){let t=typeof e=="string"?e.trim():"";if(!t)throw new y("skill slug is required");return t}function xs(e){return Array.isArray(e)?He(e.map(t=>typeof t=="string"?t.trim():"")):[]}function Ae(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function Ts(e){return Array.isArray(e)?He(e.map(t=>typeof t=="string"?t.trim():"")):[]}function wc(e){return e instanceof Error?e.message:String(e)}function Ds(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function He(e){return[...new Set(e.filter(Boolean))]}function Os(e,t){let n=Ae(e),r=Ae(n.agents),o=Ae(r.defaults),s=Ds(o,"skills")?Ts(o.skills):t,i=Array.isArray(r.list)?r.list.map(a=>({...Ae(a)})):[];return{defaults:He(s),agents:i}}function bc(e,t){let n=Os(e,t.existingSkillSlugs);if(t.scope==="global")return In({defaults:_s(n.defaults,t.slug),agents:[]});let r=Tn(n.defaults,t.slug);return In({defaults:r,agents:t.agentIds.map(o=>Bs(n.agents,o,t.slug,r,"add"))})}function Sc(e,t){let n=Os(e,t.existingSkillSlugs),r=t.scope==="agents"?n.defaults:Tn(n.defaults,t.slug),o=t.scope==="agents"?t.agentIds:t.scope==="all"?He([...n.agents.map(s=>typeof s.id=="string"?s.id:""),...t.agentIds]):[];return In({defaults:r,agents:o.map(s=>Bs(n.agents,s,t.slug,r,"remove"))})}function In(e){return{agents:{defaults:{skills:e.defaults},...e.agents.length>0?{list:e.agents}:{}}}}function _s(e,t){return He([...e,t])}function Tn(e,t){let n=Gt(t);return e.filter(r=>Gt(r)!==n)}function Gt(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}async function Dn(e,t,n){try{let r=await e.skillCatalogService.read(e.store.get());return _t(r)}catch{return _t(Cc(t,n))}}function Cc(e,t){let n=r=>Rc(r,t.slug)?Pc(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 Rc(e,t){let n=Gt(t);return[e.slug,e.id,e.name].some(r=>Gt(r)===n)}function Pc(e,t){if(t.type==="update")return{...e,version:e.latestVersion??e.version,hasUpdate:!1};if(t.type==="install"){let o=t.scope==="agents"?He([...e.installedAgentKeys,...t.agentIds]):e.installedAgentKeys,s=t.scope==="global"?!0:e.globalVisible;return{...e,installed:!0,globalVisible:s,globalEnabled:s?!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 Bs(e,t,n,r,o){let s=e.find(a=>typeof a.id=="string"&&a.id===t)??{id:t},i=Ds(s,"skills")?Ts(s.skills):r;return{...s,id:t,skills:o==="add"?_s(i,n):Tn(i,n)}}async function Ms(e,t,n){return qe({store:e.store,upstreamConfigPort:e.upstreamConfigPort,writePolicy:e.configWritePolicy,operationGate:e.operationGate},{baseHash:e.store.get().config.hash??"",patch:t,note:n})}function Ns(e){return{path:e.path,hash:e.hash??null,restart:e.restart??null}}var Ls=async(e,t)=>{let n=await As(e,{query:t.query?.query??null,limit:Ye(t.query?.limit)});return R(n)},Us=async(e,t)=>{let n=t.body;return n?Y.run(`skill:install:${n.slug??"unknown"}`,async()=>{let r;try{r=await vs(e,n)}catch(o){if($(o))return A(o);throw o}return R(r)}):X("Install request body is required")},$s=async(e,t)=>{let n=t.body;if(!n)return X("Update request body is required");let r;try{r=await Es(e,n)}catch(o){if($(o))return A(o);throw o}return R(r)},js=async(e,t)=>{let n=t.body;return n?Y.run(`skill:uninstall:${n.slug??"unknown"}`,async()=>{let r;try{r=await Is(e,n)}catch(o){if($(o))return A(o);throw o}return R(r)}):X("Uninstall request body is required")};import{spawn as kc}from"child_process";import{randomBytes as Ac}from"crypto";import{existsSync as vc}from"fs";import{homedir as Ec}from"os";import Ge from"path";import{fileURLToPath as Ic}from"url";var xc=15e3,Tc=3e3,Dc=1800*1e3,qs=Ge.dirname(Ic(import.meta.url)),Oc=(()=>{let e=[Ge.resolve(qs,"./feishu-bot-creator/feishu_bot_creator.py"),Ge.resolve(qs,"../../../tools/feishu-bot-creator/feishu_bot_creator.py")];return e.find(t=>vc(t))??e[1]})(),se=null,et=null,Hs=800,_c=600*1e3;function Gs(){return se!==null}function Ws(e="client_canceled"){return se?(se.abort(e),!0):!1}function Fs(e={}){let t=(e.sessionId??"").trim();if(se)return t&&t===se.sessionId?{ok:!0,response:Bc(se)}:{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&&et?.sessionId===t){if(Date.now()-et.completedAt<=_c)return{ok:!0,response:Mc(et)};et=null}let n=e.platform==="lark"?"lark":"feishu",r=(e.accountId??"").trim()||"default",o=t||jc(),s=process.env.FEISHU_BOT_SCRIPT_PATH||Oc,i=process.env.FEISHU_BOT_PYTHON_BIN||"python3",a=[s,"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(k=>({name:k.name,account_id:k.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??Ge.join(Ec(),".openclaw"),p=Ge.join(d,"openclaw.json"),h=Ge.join(d,"credentials",`feishu-${r}-allowFrom.json`),m=kc(i,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,O=null,H=(C,k)=>{if(!C.closed){if(C.pending){C.pending({value:k,done:!1}),C.pending=null;return}C.queue.push(k)}},N=C=>{if(!S){f.push(C),f.length>Hs&&f.splice(0,f.length-Hs);for(let k of w)H(k,C)}},F=()=>{if(!S){S=!0,et={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,k)=>{N($c(C,k))},me=setInterval(()=>{if(!S)for(let C of w)H(C,`: ping ${Date.now()}
|
|
48
48
|
|
|
49
|
-
`)},
|
|
50
|
-
`);for(let
|
|
51
|
-
`)}),m.on("error",C=>{L("finish",{action:"finish",level:"error",step:"spawn",message:`Failed to spawn ${
|
|
52
|
-
`))!==-1;){let o=n.slice(0,r).trimEnd();n=n.slice(r+1),o&&t.push(o)}return{lines:t,remainder:n}}function
|
|
49
|
+
`)},xc),ae=!1,b=()=>{clearInterval(me),clearTimeout(j),se&&se.sessionId===o&&(se=null)},x=(C="SIGTERM")=>{if(!ae){ae=!0;try{m.killed||m.kill(C)}catch{}setTimeout(()=>{if(!m.killed)try{m.kill("SIGKILL")}catch{}},Tc).unref()}},z=C=>{S||(L("finish",{action:"finish",level:"error",step:"canceled",message:C}),x("SIGTERM"),F())},j=setTimeout(()=>{S||z("session_timeout")},Dc);j.unref();let le="";m.stdout.setEncoding("utf8"),m.stdout.on("data",C=>{le+=C;let k=Uc(le);le=k.remainder;for(let Se of k.lines){let Te=null;try{Te=JSON.parse(Se)}catch{L("log",{action:"log",level:"warn",step:"raw_stdout",message:Se});continue}xe(Te)}}),m.stderr.setEncoding("utf8"),m.stderr.on("data",C=>{let k=String(C).trim();if(k){process.stderr.write(`[feishu-script-stderr] ${k}
|
|
50
|
+
`);for(let Se of k.split(/\r?\n/))Se&&L("log",{action:"log",level:"warn",step:"script_stderr",message:Se.slice(0,1e3)})}}),m.stdout.on("data",C=>{let k=String(C).trim();k&&process.stderr.write(`[feishu-script-stdout] ${k.slice(0,4e3)}
|
|
51
|
+
`)}),m.on("error",C=>{L("finish",{action:"finish",level:"error",step:"spawn",message:`Failed to spawn ${i}: ${C.message}`}),b(),F()}),m.on("exit",(C,k)=>{if(S||C!==0&&!ae&&L("finish",{action:"finish",level:"error",step:"exit",message:`script exited code=${C??"?"} signal=${k??"-"}`}),_){O={code:C,signal:k,killed:ae};return}b(),F()});function xe(C){if(C.action==="finish"&&(C.level??"success")==="success"&&e.onScriptFinish){let Se=Vs(C);_=!0,Promise.resolve().then(()=>e.onScriptFinish(Se,L)).catch(Te=>{let Di=Te instanceof Error?Te.message:String(Te);L("finish",{action:"finish",level:"error",step:"post_process",message:`post-script processing failed: ${Di}`})}).finally(()=>{_=!1,O&&(O=null,b(),F())});return}let k=Lc(C);L(k.eventName,k.payload)}se={sessionId:o,startedAt:Date.now(),child:m,frames:f,subscribers:w,abort:z},L("session_started",{action:"session_started",level:"info",step:"session",message:"Feishu bind session started",sessionId:o,platform:n,accountId:r});let zt={[Symbol.asyncIterator](){return Js({sessionId:o,frames:f,subscribers:w},S)}};return{ok:!0,response:{status:200,headers:On(),stream:zt}}}function Js(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 Bc(e){return{status:200,headers:On(),stream:{[Symbol.asyncIterator](){return Js(e)}}}}function Mc(e){return{status:200,headers:On(),stream:{async*[Symbol.asyncIterator](){for(let t of e.frames)yield t}}}}function On(){return{"content-type":"text/event-stream; charset=utf-8","cache-control":"no-cache, no-transform",connection:"keep-alive","x-accel-buffering":"no"}}function Vs(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 Nc(e){if(!("app_secret"in e))return e;let{app_secret:t,...n}=e;return n}function Lc(e){let t=String(e.action??"");return t==="finish"?{eventName:"finish",payload:Vs(e)}:t==="log"?{eventName:"log",payload:Nc(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 Uc(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 $c(e,t){return`event: ${e}
|
|
53
53
|
data: ${JSON.stringify(t)}
|
|
54
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>
|
|
55
|
+
`}function jc(){return`feishu-bind-${Date.now().toString(36)}-${Ac(8).toString("hex")}`}import{randomBytes as qc}from"crypto";var zs=300*1e3,Ks=32,ve=new Map;function Qs(){return Date.now()}function Hc(){return`tt_${qc(8).toString("hex")}`}function _n(){let e=Qs()-zs;for(let[t,n]of ve)n.createdAt<e&&ve.delete(t)}function Ys(e){if(_n(),ve.size>=Ks)throw new Error(`team-bind ticket store is at capacity (${Ks}); retry shortly`);let t=Hc(),n=Qs();return ve.set(t,{team:e,createdAt:n}),{ticketId:t,expiresAt:new Date(n+zs).toISOString()}}function Xs(e){return _n(),ve.get(e)?.team??null}function Zs(e){_n();let t=ve.get(e);return t?(ve.delete(e),t.team):null}var ei=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(),s=null;if(o&&(s=Xs(o),!s))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 i=(n.accountId??"").trim()||"default",l=s?async(h,m)=>{let f=o?Zs(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:"",O=typeof w.open_id=="string"?w.open_id:"",H=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 H){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(i,{appId:S,appSecret:b,botName:_,openId:O})}let me=b=>{let x=b.channelBinding?.accountId?.trim()||b.id,z=L.get(x),j=z?.appId??"",le=z?.appSecret??"",xe=j||le?{...j?{appId:j}:{},...le?{appSecret:le}:{}}:void 0;return{...b,channelBinding:{channel:"feishu",accountId:x,...xe?{auth:xe}:{},comment:b.channelBinding?.comment??`tnyma-web:${b.id}`}}},ae={...f,controller:me(f.controller),members:f.members.map(b=>me(b))};try{let b=await En(e,ae,{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:O,...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=s?.controller.name,d=s?[{name:s.controller.name||s.controller.id,accountId:s.controller.channelBinding?.accountId?.trim()||s.controller.id},...s.members.map(h=>({name:h.name||h.id,accountId:h.channelBinding?.accountId?.trim()||h.id}))]:void 0,p=Fs({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}}}},ti=async()=>{let e=Gs(),t=Ws("client_requested_cancel");return R({ok:!0,data:{canceled:t,hadActiveSession:e}})},ni=async(e,t)=>{let n;try{n=Ht(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=Ys(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},We={remoteAllowed:!1};function ri(e){e.register("GET","/v1/health",Co,v),e.register("GET","/v1/bridge",uo,v),e.register("GET","/v1/capabilities",go,v),e.register("GET","/v1/snapshot",po,v),e.register("GET","/v1/agents",mo,v),e.register("GET","/v1/agents/roster",fo,v),e.register("GET","/v1/models",ho,v),e.register("GET","/v1/models/catalog",yo,v),e.register("GET","/v1/channels",wo,v),e.register("GET","/v1/sessions",bo,v),e.register("GET","/v1/sessions/:sessionId",So,v),e.register("POST","/v1/agents",ds,v),e.register("POST","/v1/agents/preview",cs,v),e.register("POST","/v1/agent-teams",ms,v),e.register("DELETE","/v1/agents/:agentId",us,v),e.register("POST","/v1/agent-teams/preview",ps,v),e.register("GET","/v1/pairing/current",Ro,v),e.register("GET","/v1/pairing/qr-payload",Po,v),e.register("GET","/v1/pairing/qr.png",Eo,v),e.register("POST","/v1/pairing/register",vo,We),e.register("GET","/v1/pairing/pending",Io,v),e.register("GET","/v1/pairing/approved",To,v),e.register("POST","/v1/pairing/approve",xo,v),e.register("POST","/v1/pairing/reject",Oo,v),e.register("POST","/v1/pairing/revoke",Do,v),e.register("GET","/v1/config",jo,v),e.register("GET","/v1/config/schema",qo,v),e.register("GET","/v1/config/schema/lookup",Ho,v),e.register("POST","/v1/config/patch",Go,We),e.register("POST","/v1/config/apply",Wo,We),e.register("POST","/v1/chat/direct/messages",bs,v),e.register("POST","/v1/chat/group/messages",Ss,v),e.register("POST","/v1/chat/direct/messages/stream",Ps,v),e.register("POST","/v1/chat/group/messages/stream",ks,v),e.register("GET","/v1/chat/direct/messages",Cs,v),e.register("GET","/v1/chat/group/messages",Rs,v),e.register("GET","/v1/skills/catalog",Ls,v),e.register("POST","/v1/skills/install",Us,We),e.register("POST","/v1/skills/update",$s,We),e.register("POST","/v1/skills/uninstall",js,We),e.register("GET","/v1/integrations/feishu/bind/stream",ei,v),e.register("DELETE","/v1/integrations/feishu/bind",ti,v),e.register("POST","/v1/integrations/feishu/team-bind/prepare",ni,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 ne=class extends Error{constructor(n,r,o,s){super(n);this.code=r;this.retryable=o;this.retryAfterSec=s;this.name="DiscoveryError"}code;retryable;retryAfterSec};function oi(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 s=new AbortController,i=setTimeout(()=>s.abort(),n),a;try{a=await t(o.toString(),{method:"GET",headers:{Authorization:`Bearer ${r.bridgeToken}`,Accept:"application/json"},signal:s.signal})}catch(p){throw p.name==="AbortError"?new ne("Discovery request timed out","timeout",!0):new ne(p instanceof Error?p.message:String(p),"network_error",!0)}finally{clearTimeout(i)}let l=await a.text(),c;try{c=l?JSON.parse(l):null}catch{throw new ne(`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 ne(m,h,!f,p.retryAfterSec)}let d=c?.data??c;if(!d||typeof d.relayUrl!="string"||typeof d.sessionToken!="string")throw new ne("Discovery response missing relayUrl or sessionToken","invalid_response",!1);return d}}}var Gc=[1e3,2e3,5e3,1e4,3e4,6e4];async function si(e,t,n={}){let r=n.backoffMs??Gc,o=0;for(;;){if(n.abortSignal?.aborted)throw new ne("Aborted","aborted",!1);try{return await e.fetch(t)}catch(s){let i=s instanceof ne?s:new ne(s instanceof Error?s.message:String(s),"unknown",!0);if(n.onAttemptFailure?.(i,o),!i.retryable)throw i;let a=i.retryAfterSec?i.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 ne("Aborted","aborted",!1))},{once:!0})})}}}import Ft from"ws";function ii(e){return typeof e.stream<"u"}var ai=[1e3,2e3,5e3,1e4,3e4,6e4],Jt=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: ${tt(t)}`),this.discovery=null;let n=ai[Math.min(this.attempt,ai.length-1)];this.attempt+=1,this.setState({status:"reconnecting",backoffMs:n,attempt:this.attempt}),await Fc(n)}}openSocket(t){return new Promise((n,r)=>{this.setState({status:"connecting",relayUrl:t.relayUrl});let o=new Ft(t.relayUrl,{headers:{"user-agent":`tnyma-bridge/${this.options.clientVersion??"0.1.1"}`},maxPayload:4*1024*1024});this.socket=o;let s=!1;o.once("open",()=>{let i={kind:"hello",protocolVersion:1,bridgeId:this.options.bridgeId,sessionToken:t.sessionToken,clientVersion:this.options.clientVersion,platform:this.options.platform};this.sendEnvelope(i)}),o.on("message",i=>{let a=Jc(i.toString());if(!a){this.options.onLog?.("warn","relay: dropped malformed frame");return}if(a.kind==="welcome"){s=!0,this.attempt=0,this.handleWelcome(a),n();return}this.handleIncomingEnvelope(a)}),o.on("close",(i,a)=>{let l=a.toString()||"close";this.options.onLog?.("info",`relay socket closed code=${i} reason=${l}`),this.clearTimers(),this.socket=null,!this.stopped&&(s?(this.discovery=null,this.runConnectLoop()):r(new Error(`Relay handshake failed: ${l}`)))}),o.on("error",i=>{s?this.options.onLog?.("warn",`relay socket error: ${tt(i)}`):r(i)})})}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: ${tt(o)}`),this.sendEnvelope({kind:"response",id:t.id,status:500,body:{ok:!1,error:{code:"internal_error",message:"internal error"}}});return}if(ii(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!==Ft.OPEN)return;this.sendEnvelope({kind:"stream-end",id:t.id,trailers:r.trailers?.()})}catch(o){this.options.onLog?.("error",`stream dispatch failed: ${tt(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!==Ft.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!==Ft.OPEN))try{this.socket.send(JSON.stringify(t))}catch(n){this.options.onLog?.("warn",`relay send failed: ${tt(n)}`)}}setState(t){this.state=t,this.options.onState?.(t)}};function Fc(e){return new Promise(t=>{setTimeout(t,e).unref?.()})}function tt(e){return e instanceof Error?e.message:String(e)}function Jc(e){let t;try{t=JSON.parse(e)}catch{return null}if(!Vc(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 Vc(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}async function ci(e=Mr(),t={}){let n=new mt,r=new Date().toISOString(),o=e.openClawStateDir,s=Kc.join(o,"bridge-state.json"),i=new Bt(s,Gn({bridgeId:e.bridgeId,nodeId:e.nodeId,upstreamKind:e.upstreamKind,hostName:e.nodeId,now:r}));i.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 ht({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 yt(a),c=new bt({configPort:l,agentReader:new ut(a),modelReader:new wt(a),sessionReader:new St(a)}),d=new At({serverUrl:e.pairingRegisterUrl,timeoutMs:e.pairingRequestTimeoutMs}),p=new It,h=Hn(e.configWriteAllowedRoots),m=new Tt({store:i,chatPort:new ft(a)}),f=new $e,w=new Ot({skillsClient:new Ct(a),clawHubClient:f}),S=await n.read();i.update(b=>({...b,capabilities:S}));let _={nodeConfig:e,store:i,pairingPort:d,upstreamConfigPort:l,configWritePolicy:h,chatService:m,skillCatalogService:w,agentClient:new dt(a),pairingClient:new pt,operationGate:p,refreshData:async()=>{await me()}},O=pn();ri(O);let H=oi({url:e.discoveryUrl}),N=()=>e.bridgeToken??i.get().raw?.pairing?.relaySecret??"",F=new Jt({bridgeId:e.bridgeId,apiContext:_,router:O,clientVersion:e.clientVersion,platform:process.platform,discover:()=>si(H,{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 Le({intervalMs:e.collectionIntervalMs,onError:b=>{t.onLog?.("error",`collection loop failed: ${li(b)}`)},task:async()=>{await me()}}),configSyncLoop:new Rt({intervalMs:e.configSyncIntervalMs,onError:b=>{t.onLog?.("error",`config sync loop failed: ${li(b)}`)},task:async()=>{await ae()}})});async function me(){let b=new Date().toISOString();try{let x=i.get(),[z,j,le,xe,zt]=await Promise.all([jr(x),$r(c),Hr(c),qr(c),Gr(c)]);i.update(C=>{let k=yr(C,z.bridge??{status:"online"});return k=hr(k,j),k=br(k,le),k=wr(k,xe),k=Sr(k,zt),{...k,meta:{...k.meta,collectedAt:b},health:{...k.health,lastSuccessAt:b,lastError:null}}}),p.markReady(),t.onHealth?.({status:"online",lastError:null,lastSuccessAt:b})}catch(x){let z=x instanceof Error?x.message:String(x);p.blockForError(x),i.update(j=>({...j,meta:{...j.meta,collectedAt:b},bridge:{...j.bridge,status:"degraded"},health:{...j.health,lastError:z}})),t.onHealth?.({status:"degraded",lastError:z,lastSuccessAt:null})}finally{L.markCollection(b)}}async function ae(){let b=new Date().toISOString();try{await kt({nodeConfig:e,store:i,upstreamConfigPort:l}),p.markReady(),L.markConfigSync(b)}catch(x){let z=x instanceof Error?x.message:String(x);p.blockForError(x),i.update(j=>({...j,bridge:{...j.bridge,status:"degraded"},health:{...j.health,lastError:z}})),L.markConfigSync(b)}}return{config:e,store:i,runtime:L,apiContext:_,router:O,relayClient:F,registerPairing(b={}){return vt({store:i,pairingPort:d,nodeConfig:e},b)},async start(){L.start(),me(),ae(),F.start()},async stop(){F.stop(),L.stop(),await a.close()}}}function li(e){return e instanceof Error?e.message:String(e)}function zc(){return process.parentPort??null}var di=1;function ui(){let e=zc();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 hi,timingSafeEqual as Qc}from"crypto";import gi from"fs";import{createServer as Yc}from"http";import pi from"path";import Xc from"open";var mi=new Set(["localhost","127.0.0.1","::1","[::1]"]);function Zc(e){let t=hi(32).toString("base64url"),n=pi.join(e.openClawStateDir,"bridge-bind-ui.token");try{gi.mkdirSync(pi.dirname(n),{recursive:!0}),gi.writeFileSync(n,t,{mode:384})}catch{}return{token:t,tokenFilePath:n}}function ed(e,t){let n=new Map;return{check(r){let o=Date.now(),s=n.get(r)??[];for(;s.length>0&&s[0]<o-t;)s.shift();return s.length>=e?(n.set(r,s),!1):(s.push(o),n.set(r,s),!0)}}}async function yi(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&&!fi(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&&fi(t)?t:"127.0.0.1":t||"127.0.0.1",o=Zc(e.config),s=ed(30,6e4),i=hi(16).toString("base64"),a=Yc((c,d)=>{td({...e,auth:o,limiter:s,cspNonce:i,loopbackOnly:n},c,d)});await ud(a,e.config.localUiPort,r);let l=ad(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:()=>gd(a),open:()=>dd(e.config,l,e.logger)}}async function td(e,t,n){try{if(!nd(t.headers.host)){pe(n,e.cspNonce,421,{ok:!1,error:{code:"untrusted_host"}});return}if(e.loopbackOnly&&!rd(t)){pe(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==="/"){pd(n,e.cspNonce,md(e.cspNonce,e.auth.token,e.config));return}if(t.method==="GET"&&r.pathname==="/health"){pe(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"){pe(n,e.cspNonce,403,{ok:!1,error:{code:"csrf_blocked"}});return}if(!od(t,e.auth.token)){pe(n,e.cspNonce,401,{ok:!1,error:{code:"unauthorized"}});return}if(!e.limiter.check(r.pathname)){pe(n,e.cspNonce,429,{ok:!1,error:{code:"rate_limited"}});return}}if(t.method==="GET"&&r.pathname==="/api/pairing"){let o=await e.registerPairing();pe(n,e.cspNonce,200,{ok:!0,data:await sd(e.config,o)});return}pe(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"}),pe(n,e.cspNonce,500,{ok:!1,error:{code:"local_bind_page_error"}})}}function nd(e){if(!e)return!1;let t=e.split(":")[0].toLowerCase().trim();return mi.has(t)||mi.has(e.toLowerCase())}function fi(e){let t=e.trim().toLowerCase();return t==="127.0.0.1"||t==="localhost"||t==="::1"||t==="[::1]"}function rd(e){let t=e.socket.remoteAddress??"";return t==="127.0.0.1"||t==="::1"||t==="::ffff:127.0.0.1"}function od(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 Qc(Buffer.from(o),Buffer.from(t))}catch{return!1}}async function sd(e,t){let n=t.bridgeId??e.bridgeId,r=t.pairCode??t.accessCode??null,o=t.pairCodeExpiresAt??t.accessCodeExpiresAt??null,s=wi(e.bindUrlBase,n),i=await id(s);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:s,qrImageUrl:i,registered:!0}}async function id(e){return`data:image/png;base64,${(await Nt(e)).toString("base64")}`}function wi(e,t){return`${e.replace(/\/+$/,"")}/bind?bridgeId=${encodeURIComponent(t)}`}function ad(e,t,n){if(e.localUiPublicUrl)return e.localUiPublicUrl.replace(/\/+$/,"");let r=t.address(),o=typeof r=="object"&&r?r.port:e.localUiPort,s=ld(n)?"127.0.0.1":n;return`http://${cd(s)}:${o}`}function ld(e){return e==="0.0.0.0"||e==="::"||e===""}function cd(e){return e.includes(":")&&!e.startsWith("[")?`[${e}]`:e}async function dd(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 Xc(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 ud(e,t,n){return new Promise((r,o)=>{let s=a=>{e.off("listening",i),o(a)},i=()=>{e.off("error",s),r()};e.once("error",s),e.once("listening",i),e.listen(t,n)})}function gd(e){return new Promise((t,n)=>{if(!e.listening){t();return}e.close(r=>{if(r){n(r);return}t()})})}function pe(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),...bi(t)}),e.end(o)}function pd(e,t,n){e.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-store","content-length":Buffer.byteLength(n),...bi(t)}),e.end(n)}function bi(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 md(e,t,n){let r=wi(n.bindUrlBase,n.bridgeId);return`<!doctype html>
|
|
56
56
|
<html lang="en">
|
|
57
57
|
<head>
|
|
58
58
|
<meta charset="utf-8">
|
|
@@ -190,13 +190,80 @@ data: ${JSON.stringify(t)}
|
|
|
190
190
|
void refresh();
|
|
191
191
|
</script>
|
|
192
192
|
</body>
|
|
193
|
-
</html>`}async function
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
</html>`}import{spawn as fd}from"child_process";import{constants as hd}from"fs";import{access as yd,chmod as Mn,mkdir as Nn,rm as Si,writeFile as Pi}from"fs/promises";import{homedir as wd,platform as re,userInfo as bd}from"os";import ie from"path";import{promisify as Sd}from"util";import{execFile as Cd}from"child_process";var Rd=Sd(Cd),Ie="ai.tnyma.bridge",G="tnyma-bridge",Je="http://127.0.0.1:18788";async function Ln(e){let t=nt(e),n=Kt(t);switch(await vi(n.logDir),re()){case"darwin":return vd(t,n);case"linux":return Ed(t,n);case"win32":return Id(t,n);default:return{platform:re(),supported:!1,installed:!1,started:!1,servicePath:null,message:`unsupported platform: ${re()}`}}}async function Un(e){let t=nt(e),n=Kt(t);switch(re()){case"darwin":return await we("launchctl",["bootout",Fe(),n.launchAgentPath]),await Si(n.launchAgentPath,{force:!0}),{platform:"darwin",supported:!0,installed:!1,started:!1,servicePath:n.launchAgentPath,message:"macOS LaunchAgent removed"};case"linux":return await we("systemctl",["--user","disable","--now",`${G}.service`]),await Si(n.systemdUnitPath,{force:!0}),await we("systemctl",["--user","daemon-reload"]),{platform:"linux",supported:!0,installed:!1,started:!1,servicePath:n.systemdUnitPath,message:"systemd user service removed"};case"win32":return await we("schtasks.exe",["/End","/TN",G]),await we("schtasks.exe",["/Delete","/F","/TN",G]),{platform:"win32",supported:!0,installed:!1,started:!1,servicePath:G,message:"Windows scheduled task removed"};default:return{platform:re(),supported:!1,installed:!1,started:!1,servicePath:null,message:`unsupported platform: ${re()}`}}}async function ki(e){let t=nt(e),n=Kt(t);switch(re()){case"darwin":{let r=await Ci(n.launchAgentPath),o=await Bn("launchctl",["print",`${Fe()}/${Ie}`]);return{platform:"darwin",supported:!0,installed:r,running:o,servicePath:n.launchAgentPath,message:o?"LaunchAgent is loaded":"LaunchAgent is not loaded"}}case"linux":{let r=await Ci(n.systemdUnitPath),o=await Bn("systemctl",["--user","is-active","--quiet",`${G}.service`]);return{platform:"linux",supported:!0,installed:r,running:o,servicePath:n.systemdUnitPath,message:o?"systemd user service is active":"systemd user service is not active"}}case"win32":{let r=await Bn("schtasks.exe",["/Query","/TN",G]);return{platform:"win32",supported:!0,installed:r,running:r?null:!1,servicePath:G,message:r?"Windows scheduled task exists":"Windows scheduled task is not installed"}}default:return{platform:re(),supported:!1,installed:!1,running:!1,servicePath:null,message:`unsupported platform: ${re()}`}}}async function Ai(e){let t=nt(e),n=Kt(t);return await vi(n.logDir),await xd()?{platform:re(),supported:!0,installed:!1,started:!0,servicePath:null,message:`bridge already running at ${Je}`}:(fd(t.nodePath,[n.watchdogEntry],{detached:!0,stdio:"ignore",env:{...process.env,TNYMA_BRIDGE_SUPERVISOR:"detached-watchdog"}}).unref(),{platform:re(),supported:!0,installed:!1,started:!0,servicePath:null,message:`watchdog started without a platform service; logs: ${n.watchdogLogPath}`})}function Kt(e){let t=nt(e),n=ie.join(t.homeDir,".tnyma-bridge");return{bundleDir:t.bundleDir,bridgeEntry:ie.join(t.bundleDir,"index.js"),watchdogEntry:ie.join(t.bundleDir,"watchdog.js"),logDir:n,bridgeLogPath:ie.join(n,"bridge.log"),watchdogLogPath:ie.join(n,"watchdog.log"),watchdogErrorLogPath:ie.join(n,"watchdog.err.log"),launchAgentPath:ie.join(t.homeDir,"Library","LaunchAgents",`${Ie}.plist`),systemdUnitPath:ie.join(t.homeDir,".config","systemd","user",`${G}.service`)}}function Pd(e){return`<?xml version="1.0" encoding="UTF-8"?>
|
|
194
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
195
|
+
<plist version="1.0">
|
|
196
|
+
<dict>
|
|
197
|
+
<key>Label</key>
|
|
198
|
+
<string>${Ee(e.label)}</string>
|
|
199
|
+
<key>ProgramArguments</key>
|
|
200
|
+
<array>
|
|
201
|
+
<string>${Ee(e.nodePath)}</string>
|
|
202
|
+
<string>${Ee(e.watchdogPath)}</string>
|
|
203
|
+
</array>
|
|
204
|
+
<key>RunAtLoad</key>
|
|
205
|
+
<true/>
|
|
206
|
+
<key>KeepAlive</key>
|
|
207
|
+
<true/>
|
|
208
|
+
<key>WorkingDirectory</key>
|
|
209
|
+
<string>${Ee(e.homeDir)}</string>
|
|
210
|
+
<key>StandardOutPath</key>
|
|
211
|
+
<string>${Ee(e.stdoutPath)}</string>
|
|
212
|
+
<key>StandardErrorPath</key>
|
|
213
|
+
<string>${Ee(e.stderrPath)}</string>
|
|
214
|
+
<key>EnvironmentVariables</key>
|
|
215
|
+
<dict>
|
|
216
|
+
<key>PATH</key>
|
|
217
|
+
<string>${Ee(e.env?.PATH??process.env.PATH??"")}</string>
|
|
218
|
+
<key>TNYMA_BRIDGE_SUPERVISOR</key>
|
|
219
|
+
<string>launchd</string>
|
|
220
|
+
</dict>
|
|
221
|
+
</dict>
|
|
222
|
+
</plist>
|
|
223
|
+
`}function kd(e){let t=e.env?.PATH??process.env.PATH??"";return`[Unit]
|
|
224
|
+
Description=Tnyma Bridge watchdog
|
|
225
|
+
After=network-online.target
|
|
226
|
+
Wants=network-online.target
|
|
227
|
+
|
|
228
|
+
[Service]
|
|
229
|
+
Type=simple
|
|
230
|
+
ExecStart=${Vt(e.nodePath)} ${Vt(e.watchdogPath)}
|
|
231
|
+
WorkingDirectory=${Vt(e.homeDir)}
|
|
232
|
+
Restart=always
|
|
233
|
+
RestartSec=5
|
|
234
|
+
Environment=${Vt(`PATH=${t}`)}
|
|
235
|
+
Environment=TNYMA_BRIDGE_SUPERVISOR=systemd
|
|
236
|
+
SyslogIdentifier=tnyma-bridge
|
|
237
|
+
|
|
238
|
+
[Install]
|
|
239
|
+
WantedBy=default.target
|
|
240
|
+
`}function Ad(e){return`${Ri(e.nodePath)} ${Ri(e.watchdogPath)}`}async function vd(e,t){return await Nn(ie.dirname(t.launchAgentPath),{recursive:!0}),await Pi(t.launchAgentPath,Pd({label:Ie,taskName:G,nodePath:e.nodePath,watchdogPath:t.watchdogEntry,logDir:t.logDir,stdoutPath:t.watchdogLogPath,stderrPath:t.watchdogErrorLogPath,homeDir:e.homeDir,env:e.env}),"utf8"),await Mn(t.launchAgentPath,420).catch(()=>{}),await we("launchctl",["bootout",Fe(),t.launchAgentPath]),await be("launchctl",["bootstrap",Fe(),t.launchAgentPath]),await we("launchctl",["enable",`${Fe()}/${Ie}`]),await be("launchctl",["kickstart","-k",`${Fe()}/${Ie}`]),{platform:"darwin",supported:!0,installed:!0,started:!0,servicePath:t.launchAgentPath,message:`macOS LaunchAgent installed at ${t.launchAgentPath}`}}async function Ed(e,t){return await be("systemctl",["--user","--version"]),await Nn(ie.dirname(t.systemdUnitPath),{recursive:!0}),await Pi(t.systemdUnitPath,kd({label:Ie,taskName:G,nodePath:e.nodePath,watchdogPath:t.watchdogEntry,logDir:t.logDir,stdoutPath:t.watchdogLogPath,stderrPath:t.watchdogErrorLogPath,homeDir:e.homeDir,env:e.env}),"utf8"),await Mn(t.systemdUnitPath,420).catch(()=>{}),await be("systemctl",["--user","daemon-reload"]),await be("systemctl",["--user","enable","--now",`${G}.service`]),{platform:"linux",supported:!0,installed:!0,started:!0,servicePath:t.systemdUnitPath,message:`systemd user service installed at ${t.systemdUnitPath}`}}async function Id(e,t){let n=Ad({label:Ie,taskName:G,nodePath:e.nodePath,watchdogPath:t.watchdogEntry,logDir:t.logDir,stdoutPath:t.watchdogLogPath,stderrPath:t.watchdogErrorLogPath,homeDir:e.homeDir,env:e.env});return await be("schtasks.exe",["/Create","/F","/TN",G,"/SC","ONLOGON","/TR",n,"/RL","LIMITED"]),await we("schtasks.exe",["/Run","/TN",G]),{platform:"win32",supported:!0,installed:!0,started:!0,servicePath:G,message:"Windows scheduled task installed"}}function nt(e){return{bundleDir:e.bundleDir,nodePath:e.nodePath??process.execPath,homeDir:e.homeDir??wd(),env:e.env??process.env}}async function vi(e){await Nn(e,{recursive:!0,mode:448}),await Mn(e,448).catch(()=>{})}async function xd(){try{return(await fetch(`${Je}/health`,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}async function Ci(e){try{return await yd(e,hd.F_OK),!0}catch{return!1}}async function Bn(e,t){try{return await be(e,t),!0}catch{return!1}}async function be(e,t){let n=await Rd(e,t,{windowsHide:!0,timeout:15e3,maxBuffer:1048576});return{stdout:String(n.stdout??""),stderr:String(n.stderr??"")}}async function we(e,t){try{await be(e,t)}catch{}}function Fe(){return`gui/${typeof process.getuid=="function"?process.getuid():bd().uid}`}function Ee(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function Vt(e){return`"${e.replaceAll("\\","\\\\").replaceAll('"','\\"').replaceAll("$","\\$")}"`}function Ri(e){return`"${e.replaceAll('"','\\"')}"`}async function Dd(){if(await Od())return;let e=ui(),t=await ci(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}
|
|
241
|
+
`)}});await Bd(t);let n=await Nd(t);await t.start(),process.stdout.write(`bridge-node started (bridgeId=${t.config.bridgeId})
|
|
242
|
+
`),await Md(t),await n?.open(),e.send({type:"ready",bridgeId:t.config.bridgeId,protocolVersion:di}),e.attached?await Ld(t,e,n):await Ud(t,n)}async function Od(){let[e,t="status"]=process.argv.slice(2);if(e!=="service")return!1;let n=Td.dirname(xi(import.meta.url));switch(t){case"install":case"start":{let r=await Ln({bundleDir:n});return process.stdout.write(`${r.message}
|
|
243
|
+
`),process.stdout.write(`Local page: ${Je}
|
|
244
|
+
`),!0}case"fallback-start":{let r=await Ai({bundleDir:n});return process.stdout.write(`${r.message}
|
|
245
|
+
`),process.stdout.write(`Local page: ${Je}
|
|
246
|
+
`),!0}case"uninstall":case"stop":{let r=await Un({bundleDir:n});return process.stdout.write(`${r.message}
|
|
247
|
+
`),!0}case"restart":{await Un({bundleDir:n});let r=await Ln({bundleDir:n});return process.stdout.write(`${r.message}
|
|
248
|
+
`),process.stdout.write(`Local page: ${Je}
|
|
249
|
+
`),!0}case"status":{let r=await ki({bundleDir:n});return process.stdout.write([`platform: ${r.platform}`,`supported: ${r.supported}`,`installed: ${r.installed}`,`running: ${r.running??"unknown"}`,`servicePath: ${r.servicePath??"-"}`,`message: ${r.message}`].join(`
|
|
250
|
+
`)),process.stdout.write(`
|
|
251
|
+
`),!0}case"help":case"--help":case"-h":return Ii(),!0;default:return process.stderr.write(`Unknown service command: ${t}
|
|
252
|
+
`),Ii(),process.exitCode=1,!0}}function Ii(){process.stdout.write(`Usage: tnyma-bridge service <command>
|
|
253
|
+
|
|
254
|
+
Commands:
|
|
255
|
+
install Install and start the platform watchdog service
|
|
256
|
+
start Alias for install
|
|
257
|
+
stop Stop and remove the watchdog service
|
|
258
|
+
uninstall Alias for stop
|
|
259
|
+
restart Reinstall and restart the watchdog service
|
|
260
|
+
status Print watchdog service status
|
|
261
|
+
fallback-start Start a detached watchdog without platform service registration
|
|
262
|
+
`)}var _d=typeof process.argv[1]=="string"&&Ei(xi(import.meta.url))===Ei(process.argv[1]);_d&&Dd().catch(e=>{let t=e instanceof Error?e.stack??e.message:String(e);process.stderr.write(`${t}
|
|
263
|
+
`),process.exitCode=1});async function Bd(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
264
|
`:`[bridge] pairing registration succeeded; relaySecret generated and persisted
|
|
198
265
|
`)}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
|
|
266
|
+
`)}}async function Md(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
267
|
Scan this QR with your app to pair, or enter the pair code manually:
|
|
201
268
|
|
|
202
269
|
`),process.stdout.write(n),process.stdout.write(`
|
|
@@ -204,11 +271,11 @@ Scan this QR with your app to pair, or enter the pair code manually:
|
|
|
204
271
|
`),process.stdout.write(`Pairing server: ${t.serverUrl??""}
|
|
205
272
|
`),process.stdout.write(`Bridge ID: ${t.bridgeId??e.config.bridgeId}
|
|
206
273
|
`),process.stdout.write(`
|
|
207
|
-
`)}async function
|
|
274
|
+
`)}async function Nd(e){try{return await yi({config:e.config,registerPairing:t=>e.registerPairing(t),logger:{info:t=>process.stdout.write(`${t}
|
|
208
275
|
`),warn:t=>process.stderr.write(`${t}
|
|
209
276
|
`)}})}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
|
|
277
|
+
`),null}}async function Ld(e,t,n){await new Promise((r,o)=>{let s=!1,i=()=>{s||(s=!0,Ti(e,n).then(()=>r(),a=>o(a)))};t.onMessage(async a=>{if(a.type==="shutdown"){i();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",i),process.once("SIGINT",i)})}async function Ud(e,t){await new Promise((n,r)=>{let o=!1,s=()=>{process.off("SIGINT",a),process.off("SIGTERM",l)},i=c=>{if(o){process.stderr.write(`
|
|
211
278
|
Received ${c} again, forcing exit.
|
|
212
279
|
`),process.exit(130);return}o=!0,process.stdout.write(`
|
|
213
280
|
Received ${c}, shutting down bridge-node...
|
|
214
|
-
`),
|
|
281
|
+
`),Ti(e,t).then(()=>{s(),n()},d=>{s(),r(d)})},a=()=>{i("SIGINT")},l=()=>{i("SIGTERM")};process.on("SIGINT",a),process.on("SIGTERM",l)})}async function Ti(e,t){await t?.stop(),await e.stop()}
|
package/bundle/postinstall.js
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import g from"path";import{fileURLToPath as A}from"url";import{spawn as W}from"child_process";import{constants as G}from"fs";import{access as V,chmod as m,mkdir as p,rm as Y,writeFile as w}from"fs/promises";import{homedir as L,platform as c,userInfo as E}from"os";import a from"path";import{promisify as x}from"util";import{execFile as O}from"child_process";var R=x(O),o="ai.tnyma.bridge",n="tnyma-bridge",d="http://127.0.0.1:18788";async function y(t){let e=P(t),r=D(e);switch(await b(r.logDir),c()){case"darwin":return I(e,r);case"linux":return _(e,r);case"win32":return B(e,r);default:return{platform:c(),supported:!1,installed:!1,started:!1,servicePath:null,message:`unsupported platform: ${c()}`}}}async function S(t){let e=P(t),r=D(e);return await b(r.logDir),await C()?{platform:c(),supported:!0,installed:!1,started:!0,servicePath:null,message:`bridge already running at ${d}`}:(W(e.nodePath,[r.watchdogEntry],{detached:!0,stdio:"ignore",env:{...process.env,TNYMA_BRIDGE_SUPERVISOR:"detached-watchdog"}}).unref(),{platform:c(),supported:!0,installed:!1,started:!0,servicePath:null,message:`watchdog started without a platform service; logs: ${r.watchdogLogPath}`})}function D(t){let e=P(t),r=a.join(e.homeDir,".tnyma-bridge");return{bundleDir:e.bundleDir,bridgeEntry:a.join(e.bundleDir,"index.js"),watchdogEntry:a.join(e.bundleDir,"watchdog.js"),logDir:r,bridgeLogPath:a.join(r,"bridge.log"),watchdogLogPath:a.join(r,"watchdog.log"),watchdogErrorLogPath:a.join(r,"watchdog.err.log"),launchAgentPath:a.join(e.homeDir,"Library","LaunchAgents",`${o}.plist`),systemdUnitPath:a.join(e.homeDir,".config","systemd","user",`${n}.service`)}}function T(t){return`<?xml version="1.0" encoding="UTF-8"?>
|
|
3
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4
|
+
<plist version="1.0">
|
|
5
|
+
<dict>
|
|
6
|
+
<key>Label</key>
|
|
7
|
+
<string>${s(t.label)}</string>
|
|
8
|
+
<key>ProgramArguments</key>
|
|
9
|
+
<array>
|
|
10
|
+
<string>${s(t.nodePath)}</string>
|
|
11
|
+
<string>${s(t.watchdogPath)}</string>
|
|
12
|
+
</array>
|
|
13
|
+
<key>RunAtLoad</key>
|
|
14
|
+
<true/>
|
|
15
|
+
<key>KeepAlive</key>
|
|
16
|
+
<true/>
|
|
17
|
+
<key>WorkingDirectory</key>
|
|
18
|
+
<string>${s(t.homeDir)}</string>
|
|
19
|
+
<key>StandardOutPath</key>
|
|
20
|
+
<string>${s(t.stdoutPath)}</string>
|
|
21
|
+
<key>StandardErrorPath</key>
|
|
22
|
+
<string>${s(t.stderrPath)}</string>
|
|
23
|
+
<key>EnvironmentVariables</key>
|
|
24
|
+
<dict>
|
|
25
|
+
<key>PATH</key>
|
|
26
|
+
<string>${s(t.env?.PATH??process.env.PATH??"")}</string>
|
|
27
|
+
<key>TNYMA_BRIDGE_SUPERVISOR</key>
|
|
28
|
+
<string>launchd</string>
|
|
29
|
+
</dict>
|
|
30
|
+
</dict>
|
|
31
|
+
</plist>
|
|
32
|
+
`}function N(t){let e=t.env?.PATH??process.env.PATH??"";return`[Unit]
|
|
33
|
+
Description=Tnyma Bridge watchdog
|
|
34
|
+
After=network-online.target
|
|
35
|
+
Wants=network-online.target
|
|
36
|
+
|
|
37
|
+
[Service]
|
|
38
|
+
Type=simple
|
|
39
|
+
ExecStart=${u(t.nodePath)} ${u(t.watchdogPath)}
|
|
40
|
+
WorkingDirectory=${u(t.homeDir)}
|
|
41
|
+
Restart=always
|
|
42
|
+
RestartSec=5
|
|
43
|
+
Environment=${u(`PATH=${e}`)}
|
|
44
|
+
Environment=TNYMA_BRIDGE_SUPERVISOR=systemd
|
|
45
|
+
SyslogIdentifier=tnyma-bridge
|
|
46
|
+
|
|
47
|
+
[Install]
|
|
48
|
+
WantedBy=default.target
|
|
49
|
+
`}function U(t){return`${v(t.nodePath)} ${v(t.watchdogPath)}`}async function I(t,e){return await p(a.dirname(e.launchAgentPath),{recursive:!0}),await w(e.launchAgentPath,T({label:o,taskName:n,nodePath:t.nodePath,watchdogPath:e.watchdogEntry,logDir:e.logDir,stdoutPath:e.watchdogLogPath,stderrPath:e.watchdogErrorLogPath,homeDir:t.homeDir,env:t.env}),"utf8"),await m(e.launchAgentPath,420).catch(()=>{}),await h("launchctl",["bootout",l(),e.launchAgentPath]),await i("launchctl",["bootstrap",l(),e.launchAgentPath]),await h("launchctl",["enable",`${l()}/${o}`]),await i("launchctl",["kickstart","-k",`${l()}/${o}`]),{platform:"darwin",supported:!0,installed:!0,started:!0,servicePath:e.launchAgentPath,message:`macOS LaunchAgent installed at ${e.launchAgentPath}`}}async function _(t,e){return await i("systemctl",["--user","--version"]),await p(a.dirname(e.systemdUnitPath),{recursive:!0}),await w(e.systemdUnitPath,N({label:o,taskName:n,nodePath:t.nodePath,watchdogPath:e.watchdogEntry,logDir:e.logDir,stdoutPath:e.watchdogLogPath,stderrPath:e.watchdogErrorLogPath,homeDir:t.homeDir,env:t.env}),"utf8"),await m(e.systemdUnitPath,420).catch(()=>{}),await i("systemctl",["--user","daemon-reload"]),await i("systemctl",["--user","enable","--now",`${n}.service`]),{platform:"linux",supported:!0,installed:!0,started:!0,servicePath:e.systemdUnitPath,message:`systemd user service installed at ${e.systemdUnitPath}`}}async function B(t,e){let r=U({label:o,taskName:n,nodePath:t.nodePath,watchdogPath:e.watchdogEntry,logDir:e.logDir,stdoutPath:e.watchdogLogPath,stderrPath:e.watchdogErrorLogPath,homeDir:t.homeDir,env:t.env});return await i("schtasks.exe",["/Create","/F","/TN",n,"/SC","ONLOGON","/TR",r,"/RL","LIMITED"]),await h("schtasks.exe",["/Run","/TN",n]),{platform:"win32",supported:!0,installed:!0,started:!0,servicePath:n,message:"Windows scheduled task installed"}}function P(t){return{bundleDir:t.bundleDir,nodePath:t.nodePath??process.execPath,homeDir:t.homeDir??L(),env:t.env??process.env}}async function b(t){await p(t,{recursive:!0,mode:448}),await m(t,448).catch(()=>{})}async function C(){try{return(await fetch(`${d}/health`,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}async function i(t,e){let r=await R(t,e,{windowsHide:!0,timeout:15e3,maxBuffer:1048576});return{stdout:String(r.stdout??""),stderr:String(r.stderr??"")}}async function h(t,e){try{await i(t,e)}catch{}}function l(){return`gui/${typeof process.getuid=="function"?process.getuid():E().uid}`}function s(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function u(t){return`"${t.replaceAll("\\","\\\\").replaceAll('"','\\"').replaceAll("$","\\$")}"`}function v(t){return`"${t.replaceAll('"','\\"')}"`}async function j(){if(process.env.TNYMA_BRIDGE_SKIP_AUTOSTART==="1"||H())return;let t=g.dirname(A(import.meta.url));try{let r=await y({bundleDir:t});if(r.installed&&r.started){process.stdout.write(`[tnyma-bridge] ${r.message}
|
|
50
|
+
`),process.stdout.write(`[tnyma-bridge] local page: ${d}
|
|
51
|
+
`);return}process.stdout.write(`[tnyma-bridge] service install skipped: ${r.message}
|
|
52
|
+
`)}catch(r){let f=r instanceof Error?r.message:String(r);process.stderr.write(`[tnyma-bridge] service install failed: ${f}
|
|
53
|
+
`)}let e=await S({bundleDir:t});process.stdout.write(`[tnyma-bridge] ${e.message}
|
|
54
|
+
`),process.stdout.write(`[tnyma-bridge] local page: ${d}
|
|
55
|
+
`)}function H(){return A(import.meta.url).includes(`${g.sep}apps${g.sep}bridge-node${g.sep}`)}j().catch(t=>{let e=t instanceof Error?t.message:String(t);process.stderr.write(`[tnyma-bridge] autostart skipped: ${e}
|
|
5
56
|
`)});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{spawn as h}from"child_process";import{closeSync as S,mkdirSync as f,openSync as _,writeFileSync as M}from"fs";import{homedir as T}from"os";import s from"path";import{setTimeout as g}from"timers/promises";import{fileURLToPath as y}from"url";var m="http://127.0.0.1:18788",I=`${m}/health`,r=1e3,E=3e4,R=5e3,A=3e4,D=s.dirname(y(import.meta.url)),u=s.join(D,"index.js"),c=s.join(T(),".tnyma-bridge"),L=s.join(c,"bridge.log"),b=s.join(c,"watchdog.log"),i=!1,o=null;async function P(){f(c,{recursive:!0,mode:448}),n(`watchdog started (bridge=${u})`),process.on("SIGINT",()=>d("SIGINT")),process.on("SIGTERM",()=>d("SIGTERM"));let t=r;for(;!i;){if(await p()){n(`bridge already healthy at ${m}; waiting for it to exit`),await v(),t=r;continue}let e=await $();if(i)break;e.uptimeMs>=A&&(t=r),n(`bridge exited code=${e.code??"null"} signal=${e.signal??"null"} uptimeMs=${e.uptimeMs}; restarting in ${t}ms`),await g(t),t=Math.min(t*2,E)}n("watchdog stopped")}async function $(){let t=Date.now(),e=_(L,"a",384);try{o=h(process.execPath,[u],{stdio:["ignore",e,e],env:{...process.env,TNYMA_BRIDGE_SUPERVISOR:process.env.TNYMA_BRIDGE_SUPERVISOR??"watchdog",TNYMA_BRIDGE_WATCHDOG_CHILD:"1"}})}finally{S(e)}return n(`started bridge pid=${o.pid??"unknown"}`),await new Promise(l=>{o?.once("error",a=>{n(`bridge spawn error: ${a.message}`),o=null,l({code:1,signal:null,uptimeMs:Date.now()-t})}),o?.once("exit",(a,w)=>{o=null,l({code:a,signal:w,uptimeMs:Date.now()-t})})})}async function v(){for(;!i&&await p();)await g(R)}async function p(){try{return(await fetch(I,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}function d(t){i||(i=!0,n(`received ${t}; stopping watchdog`),o&&!o.killed&&o.kill(t))}function n(t){let e=`${new Date().toISOString()} [watchdog] ${t}
|
|
3
|
+
`;M(b,e,{flag:"a",mode:384})}P().catch(t=>{let e=t instanceof Error?t.stack??t.message:String(t);n(`fatal: ${e}`),process.exitCode=1});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tnyma-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
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
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|