badgerclaw 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +81 -74
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
`)}async function Sr(e){if(process.env.BADGERCLAW_NO_VERSION_CHECK)return;let t=
|
|
4
|
-
`);if(o.length<2)return null;let r=o[1].split(/\s+/),s=parseInt(r[3]||"",10);return Number.isFinite(s)?Math.floor(s/1024/1024):null}function
|
|
5
|
-
`,{mode:384});try{
|
|
6
|
-
`,{mode:384});try{
|
|
7
|
-
`,{mode:384})}function
|
|
2
|
+
"use strict";var si=Object.create;var qt=Object.defineProperty;var ai=Object.getOwnPropertyDescriptor;var ii=Object.getOwnPropertyNames;var li=Object.getPrototypeOf,ci=Object.prototype.hasOwnProperty;var V=(e,t)=>()=>(e&&(t=e(e=0)),t);var ui=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Bn=(e,t)=>{for(var n in t)qt(e,n,{get:t[n],enumerable:!0})},tr=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ii(t))!ci.call(e,r)&&r!==n&&qt(e,r,{get:()=>t[r],enumerable:!(o=ai(t,r))||o.enumerable});return e};var m=(e,t,n)=>(n=e!=null?si(li(e)):{},tr(t||!e||!e.__esModule?qt(n,"default",{value:e,enumerable:!0}):n,e)),nr=e=>tr(qt({},"__esModule",{value:!0}),e);function $(){try{let e=pt.default.readFileSync(Fn,"utf-8");return JSON.parse(e)}catch{return null}}function Vt(e){pt.default.mkdirSync(ar,{recursive:!0}),pt.default.writeFileSync(Fn,JSON.stringify(e,null,2),{mode:384})}function gt(){try{pt.default.unlinkSync(Fn)}catch{}}function Kt(){let e=$();return e?new Date(e.expires_at)>new Date:!1}function Wt(e){let t=e.match(/^@?([^:]+)/);return t?t[1]:e}var pt,Hn,sr,ar,Fn,H=V(()=>{"use strict";pt=m(require("fs")),Hn=m(require("path")),sr=m(require("os")),ar=Hn.default.join(sr.default.homedir(),".badgerclaw"),Fn=Hn.default.join(ar,"auth.json")});function ft(){return ir.default.createHash("sha256").update(`${se.default.hostname()}-${se.default.platform()}-${se.default.arch()}`).digest("hex").slice(0,16)}function ae(){let e=$();if(e?.instance_id)return e.instance_id;let t=ft();return`openclaw-${se.default.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${t}`}function Yt(){return{hostname:se.default.hostname(),os:se.default.platform(),arch:se.default.arch(),uptimeSeconds:Math.floor(se.default.uptime()),memFreeMb:Math.floor(se.default.freemem()/1024/1024)}}var se,ir,Ae=V(()=>{"use strict";se=m(require("os")),ir=m(require("crypto"));H()});var lr,N,cr,ie=V(()=>{"use strict";lr=process.env.BADGERCLAW_ENV==="local",N=process.env.BADGERCLAW_API_URL??(lr?"http://localhost:8000":"https://api.badger.signout.io"),cr=process.env.BADGERCLAW_AUTH_URL??(lr?"http://localhost:5500":"https://badgerclaw.ai")});function dr(){return ht}function di(e){if(!e)return null;let t=e.split(".");if(t.length!==3)return null;try{let n=t[1].replace(/-/g,"+").replace(/_/g,"/"),o=n.length%4===0?n:n+"=".repeat(4-n.length%4),r=JSON.parse(Buffer.from(o,"base64").toString("utf8"));return(typeof r?.sub=="string"?r.sub:null)||null}catch{return null}}async function Gn(){let e=$();if(!e?.refresh_token||!e?.email)return ht=e?e.refresh_token?"no email in auth.json \u2014 re-run `badgerclaw login`":"no refresh_token in auth.json \u2014 re-run `badgerclaw login`":"not logged in (auth.json missing)",null;let t=di(e.access_token);try{let n=await yt.default.post(`${zt}/api/v1/user/token/refresh`,{refresh_token:e.refresh_token,email:e.email,...t?{cognito_username:t}:{}}),o=n.data?.result??n.data,r=o?.access_token;if(!r)return ht="server returned no access_token",null;let s=o.expires_in||3600,a=new Date(Date.now()+s*1e3).toISOString();return Vt({...e,access_token:r,expires_at:a}),ht=null,r}catch(n){let o=n?.response?.status,r=n?.response?.data?.errors?.[0]||n?.response?.data?.detail||n?.message||"unknown error";ht=o?`HTTP ${o}: ${r}`:`network error: ${r}`;let s=n?.response?.data?.errors?.[0]||n?.response?.data?.detail||n?.message||"";if(o===403&&ur.test(s))throw gt(),new Ve(s);return null}}function qn(e){return e.interceptors.response.use(t=>(t.data&&typeof t.data=="object"&&"result"in t.data&&"errors"in t.data&&(t.data=t.data.result),t),t=>{let n=t.response?.data?.errors;return n?.length&&(t.message=n[0]),Promise.reject(t)}),e}function mr(e){return e.interceptors.response.use(void 0,t=>{if(t.response?.status===403){let o=t.message||t.response?.data?.errors?.[0]||t.response?.data?.detail||"";if(ur.test(o))return gt(),Promise.reject(new Ve(o))}return Promise.reject(t)}),e}function pr(e){return e.interceptors.response.use(void 0,async t=>{let n=t.config;if(t.response?.status!==401||n._retried)return Promise.reject(t);n._retried=!0,Jt||(Jt=Gn().finally(()=>{Jt=null}));let o=await Jt;return o?(n.headers.Authorization=`Bearer ${o}`,e.request(n)):Promise.reject(t)}),e}function B(){let e=$(),t={"Content-Type":"application/json"};e&&(t.Authorization=`Bearer ${e.access_token}`);let n=yt.default.create({baseURL:zt,headers:t});return qn(n),pr(n),mr(n),n}function gr(){return qn(yt.default.create({baseURL:zt,headers:{"Content-Type":"application/json"}}))}function bt(e){let t=yt.default.create({baseURL:zt,headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`}});return qn(t),pr(t),mr(t),t}var yt,zt,Ve,ur,Jt,ht,Ee=V(()=>{"use strict";yt=m(require("axios"));H();ie();zt=N,Ve=class extends Error{constructor(n){super(n);this.code="DEACTIVATED";this.name="DeactivatedError"}},ur=/account\s+has\s+been\s+(deleted|deactivated)/i,Jt=null,ht=null});function br(){try{let e=we.default.statSync(Pe);return Date.now()-e.mtimeMs>hr}catch{return!0}}function Vn(){try{return we.default.mkdirSync(Kn.default.dirname(Pe),{recursive:!0}),we.default.writeFileSync(Pe,String(process.pid),{flag:"wx"}),!0}catch{if(br())try{return we.default.unlinkSync(Pe),we.default.writeFileSync(Pe,String(process.pid),{flag:"wx"}),!0}catch{return!1}return!1}}function wr(){try{we.default.unlinkSync(Pe)}catch{}}async function _r(e){let t=!1;for(let n=0;n<mi;n++){if(Vn()){t=!0;break}await new Promise(o=>setTimeout(o,yr))}t||console.warn("[config-lock] Could not acquire lock after timeout \u2014 proceeding without lock");try{return await e()}finally{t&&wr()}}function Oe(e){let t=Vn();if(!t&&br()){try{we.default.unlinkSync(Pe)}catch{}t=Vn()}try{return e()}finally{t&&wr()}}var we,fr,Kn,Pe,hr,yr,mi,wt=V(()=>{"use strict";we=m(require("fs")),fr=m(require("os")),Kn=m(require("path")),Pe=Kn.default.join(fr.default.homedir(),".openclaw","openclaw.json.lock"),hr=1e4,yr=50,mi=Math.ceil(hr/yr)});var _e=ui((Cu,pi)=>{pi.exports={name:"badgerclaw",version:"0.3.6",engines:{node:">=18"},description:"BadgerClaw CLI \u2014 one-click bot provisioning",main:"dist/index.js",bin:{badgerclaw:"./dist/index.js"},files:["dist/","scripts/","assets/","README.md"],scripts:{build:"node build.mjs","verify-dist-version":`node -e "const v=require('./package.json').version; const d=require('fs').readFileSync('./dist/index.js','utf8'); if(!d.includes('version:\\"'+v+'\\"')){console.error('dist/index.js is stale \u2014 rebuild before publish (package.json='+v+')'); process.exit(1);} console.log('dist/index.js OK \u2014 version '+v);"`,prepublishOnly:"rm -rf dist && npm run build && npm run verify-dist-version",preuninstall:"node scripts/preuninstall.cjs",test:"tsx --test 'tests/**/*.test.ts'"},keywords:[],author:"",license:"ISC",dependencies:{"@anthropic-ai/sdk":"^0.105.0","@modelcontextprotocol/sdk":"^1.29.0",axios:"^1.6.0",chalk:"^4.1.2",commander:"^12.0.0",eventsource:"^1.1.2",open:"^8.4.2",ora:"^5.4.1"},devDependencies:{"@types/eventsource":"^1.1.15","@types/node":"^20.0.0",esbuild:"^0.28.0",tsx:"^4.22.0",typescript:"^5.3.0"}}});function Xt(e=bi){try{return Jn.default.readdirSync(e,{withFileTypes:!0}).filter(t=>t.isDirectory()&&t.name.startsWith("badgerclaw-connect-")).map(t=>de.default.join(e,t.name))}catch{return[]}}function wi(){return[...Xt().map(t=>de.default.join(t,"node_modules/@badgerclaw/connect/package.json")),de.default.join(yi,"package.json"),de.default.join(vr,"node_modules/@badgerclaw/connect/package.json"),de.default.join(vr,"package.json")]}function zn(){return"0.3.6"}function Zt(){for(let e of wi())try{let t=Jn.default.readFileSync(e,"utf-8"),n=JSON.parse(t);if(n.version&&(!n.name||n.name==="@badgerclaw/connect"||n.name==="badgerclaw")||n.version)return n.version}catch{}return null}function Yn(e){let t=e.trim(),n=t.match(/\d+(?:\.\d+){1,3}/);return n?n[0]:t}function Y(){if((0,Wn.spawnSync)("which",["openclaw"],{encoding:"utf-8"}).status!==0)return null;let t=(0,Wn.spawnSync)("openclaw",["--version"],{encoding:"utf-8",timeout:3e3});return t.status!==0||!t.stdout?null:Yn(t.stdout)||null}function _i(e,t){let n=e.split(".").map(s=>parseInt(s,10)||0),o=t.split(".").map(s=>parseInt(s,10)||0),r=Math.max(n.length,o.length);for(let s=0;s<r;s++){let a=(n[s]??0)-(o[s]??0);if(a!==0)return a<0?-1:1}return 0}function kr(e,t=Y()){return t?_i(Yn(t),Yn(e))>=0:!1}async function vi(){let e=new AbortController,t=setTimeout(()=>e.abort(),fi);try{let n=await fetch(gi,{signal:e.signal});if(!n.ok)return null;let o=await n.json(),r=o.result&&typeof o.result=="object"?o.result:o;return{cli:r.cli??"unknown",plugin:r.plugin??"unknown",supported_openclaw:r.supported_openclaw??"unknown"}}catch{return null}finally{clearTimeout(t)}}function ki(e){let t=[];if(e.cli&&e.cli!=="unknown"){let o=zn();o!==e.cli&&t.push({component:"cli",current:o,approved:e.cli})}if(!(Y()!==null))return t;if(e.plugin&&e.plugin!=="unknown"){let o=Zt();(o===null||o!==e.plugin)&&t.push({component:"plugin",current:o??"not installed",approved:e.plugin})}if(e.supported_openclaw&&e.supported_openclaw!=="unknown"){let o=Y();(o===null||o!==e.supported_openclaw)&&t.push({component:"openclaw",current:o??"not installed",approved:e.supported_openclaw})}return t}function Si(e){return e[2]}function Ri(e){return!e||e.startsWith("-")?!0:hi.has(e)}function Ci(e){let t=[];t.push(""),t.push(ve.default.red.bold("\u2717 Unsupported versions detected")),t.push("");for(let n of e){let o=n.component.padEnd(8);t.push(` ${o} ${ve.default.yellow(n.current)} \u2192 ${ve.default.green(n.approved)} ${ve.default.dim("(supported)")}`)}return t.push(""),t.push("This command is blocked until your installation matches the supported versions."),t.push(""),t.push(ve.default.green(" To fix, run:")),t.push(ve.default.green.bold(" badgerclaw setup")),t.push(""),t.push(ve.default.dim(" (bypass temporarily: BADGERCLAW_NO_VERSION_CHECK=1 badgerclaw <cmd>)")),t.push(""),t.join(`
|
|
3
|
+
`)}async function Sr(e){if(process.env.BADGERCLAW_NO_VERSION_CHECK)return;let t=Si(e);if(Ri(t))return;let n=await vi();if(!n)return;let o=ki(n);o.length!==0&&(process.stderr.write(Ci(o)),process.exit(1))}var Jn,Qt,de,Wn,ve,gi,fi,hi,vr,yi,bi,Ke=V(()=>{"use strict";Jn=m(require("fs")),Qt=m(require("os")),de=m(require("path")),Wn=require("child_process"),ve=m(require("chalk"));ie();gi=`${N}/api/v1/dashboard/versions/latest`,fi=2500,hi=new Set(["setup","logout","help","--version","-V","--help","-h","heartbeat","watch","autopair","benchmark"]),vr=de.default.join(Qt.default.homedir(),".openclaw/extensions/badgerclaw"),yi=de.default.join(Qt.default.homedir(),".openclaw/npm/node_modules/@badgerclaw/connect"),bi=de.default.join(Qt.default.homedir(),".openclaw/npm/projects")});var Ir={};Bn(Ir,{HERMES_IMAGE_DEFAULT:()=>We,detectCapabilities:()=>ke,detectDiskFreeGb:()=>$r,detectDocker:()=>en,detectHermes:()=>tn,detectOpenClaw:()=>xr});function en(){if((0,_t.spawnSync)("which",["docker"],{encoding:"utf-8"}).status!==0)return{installed:!1};let t=(0,_t.spawnSync)("docker",["version","--format","{{.Server.Version}}"],{encoding:"utf-8",timeout:3e3});return t.status!==0||!t.stdout.trim()?{installed:!1}:{installed:!0,version:t.stdout.trim()}}function xr(){let e=Y();return e?{installed:!0,version:e}:{installed:!1}}function tn(e){if(!e.installed)return{installed:!1};let t=process.env.HERMES_IMAGE||We;return(0,_t.spawnSync)("docker",["image","inspect","--format","{{.RepoDigests}}",t],{encoding:"utf-8",timeout:3e3}).status!==0?{installed:!1}:{installed:!0,image:t}}function $r(){let e=Cr.default.join(Qn.default.homedir(),".badgerclaw"),t=Rr.default.existsSync(e)?e:Qn.default.homedir();if(process.platform==="win32")return null;let n=(0,_t.spawnSync)("df",["-Pk",t],{encoding:"utf-8",timeout:3e3});if(n.status!==0||!n.stdout)return null;let o=n.stdout.trim().split(`
|
|
4
|
+
`);if(o.length<2)return null;let r=o[1].split(/\s+/),s=parseInt(r[3]||"",10);return Number.isFinite(s)?Math.floor(s/1024/1024):null}function ke(){let e=en();return{openclaw:xr(),hermes:tn(e),docker:e,disk_free_gb:$r(),ports_in_use:[]}}var _t,Rr,Qn,Cr,We,Le=V(()=>{"use strict";_t=require("child_process"),Rr=m(require("fs")),Qn=m(require("os")),Cr=m(require("path"));Ke();We="hermes-agent-bc:bundled"});function Tr(e){return e?Zn:Ii}function nn(e){return F.default.join(xi,`bot-${e}`)}function Mr(e){C.default.existsSync(le)||C.default.mkdirSync(le,{recursive:!0,mode:448}),C.default.writeFileSync(on,JSON.stringify(e,null,2)+`
|
|
5
|
+
`,{mode:384});try{C.default.rmSync(rn,{force:!0})}catch{}}function Ar(){if(!C.default.existsSync(on))return null;try{let e=JSON.parse(C.default.readFileSync(on,"utf-8"));return e&&e.provider==="ollama"?e:null}catch{return null}}function Ai(e){return e==="gemini"||e==="anthropic"||e==="openrouter"}function Dr(e){C.default.existsSync(le)||C.default.mkdirSync(le,{recursive:!0,mode:448}),C.default.writeFileSync(rn,JSON.stringify(e,null,2)+`
|
|
6
|
+
`,{mode:384});try{C.default.rmSync(on,{force:!0})}catch{}}function Ei(){if(!C.default.existsSync(rn))return null;try{let e=JSON.parse(C.default.readFileSync(rn,"utf-8"));return e&&Ai(e.provider)&&typeof e.apiKey=="string"&&e.apiKey?{provider:e.provider,apiKey:e.apiKey,model:e.model||void 0}:null}catch{return null}}function eo(e){return`badgerclaw-hermes-bot-${e}`}function me(){if(!C.default.existsSync(Xn))return{hermes_bots:{}};try{let e=C.default.readFileSync(Xn,"utf-8"),t=JSON.parse(e);return{hermes_bots:t.hermes_bots&&typeof t.hermes_bots=="object"?t.hermes_bots:{}}}catch{return{hermes_bots:{}}}}function Nr(e){C.default.existsSync(le)||C.default.mkdirSync(le,{recursive:!0,mode:448}),C.default.writeFileSync(Xn,JSON.stringify(e,null,2)+`
|
|
7
|
+
`,{mode:384})}function Pi(e,t=$i){let n=new Set(Object.values(e.hermes_bots).map(r=>r.port)),o=t;for(;n.has(o)||!Oi(o);)if(o+=1,o-t>1e3)throw new Error(`Could not find a free port near ${t} after 1000 tries \u2014 host is unusually busy`);return o}function Oi(e){let t=Or.default.createServer(),n=!1;try{t.listen(e,"127.0.0.1"),n=!0}catch{n=!1}finally{try{t.close()}catch{}}return n}function Ye(e=process.env){let t=e.BADGERCLAW_LLM_PROVIDER?.toLowerCase(),n=e.BADGERCLAW_LLM_API_KEY;if(t==="ollama"){let a=Ar();return{provider:"ollama",apiKey:"",baseUrl:e.BADGERCLAW_LLM_BASE_URL||a?.base_url||void 0,model:e.BADGERCLAW_LLM_MODEL||a?.model||void 0}}if(t&&n){if(t!=="gemini"&&t!=="anthropic"&&t!=="openrouter")throw new Error(`BADGERCLAW_LLM_PROVIDER must be one of: gemini, anthropic, openrouter, ollama. Got '${t}'.`);return{provider:t,apiKey:n}}let o=Ar();if(o)return{provider:"ollama",apiKey:"",baseUrl:o.base_url||void 0,model:o.model||void 0};let r=Ei();if(r)return{provider:r.provider,apiKey:r.apiKey,model:r.model||void 0};let s=e.GOOGLE_API_KEY||e.GEMINI_API_KEY;if(s)return{provider:"gemini",apiKey:s};if(e.ANTHROPIC_API_KEY)return{provider:"anthropic",apiKey:e.ANTHROPIC_API_KEY};if(e.OPENROUTER_API_KEY)return{provider:"openrouter",apiKey:e.OPENROUTER_API_KEY}}function kt(e){switch(e){case"gemini":return"gemini-2.5-flash";case"anthropic":return"claude-sonnet-4-6";case"openrouter":return"anthropic/claude-sonnet-4-6";case"ollama":return"qwen2.5:3b"}}function Li(e){switch(e){case"gemini":return"GOOGLE_API_KEY";case"anthropic":return"ANTHROPIC_API_KEY";case"openrouter":return"OPENROUTER_API_KEY";case"ollama":return"OPENAI_API_KEY"}}function Ti(e,t){let n=[`# Generated by badgerclaw CLI for bot ${e.botId}.`,"# Edit at your own risk; subsequent pair operations may overwrite.","",`MATRIX_HOMESERVER=${e.matrixHomeserver}`,`MATRIX_ACCESS_TOKEN=${e.matrixAccessToken}`,`MATRIX_USER_ID=${e.matrixUserId}`,`MATRIX_DEVICE_ID=${e.matrixDeviceId}`,"MATRIX_ENCRYPTION=true",`MATRIX_ALLOWED_USERS=${e.ownerMatrixUserId}`];return e.homeRoomId&&n.push(`MATRIX_HOME_ROOM=${e.homeRoomId}`),e.llm&&(n.push(""),e.llm.provider==="ollama"?(n.push("# Local Ollama (OpenAI-compatible) \u2014 no real API key needed."),n.push(`OPENAI_BASE_URL=${e.llm.baseUrl||Zn}`),n.push("OPENAI_API_KEY=ollama")):(n.push("# LLM provider \u2014 set by the operator's environment at pair time."),n.push("# Hermes reads this directly; model selection lives in config.yaml."),n.push(`${Li(e.llm.provider)}=${e.llm.apiKey}`))),n.push("","# OpenAI-compatible API surface \u2014 localhost only by default so the","# CLI's daemon can health-check the container without exposing it","# beyond the host loopback.","API_SERVER_ENABLED=true","API_SERVER_HOST=127.0.0.1",`API_SERVER_PORT=${t}`,`API_SERVER_KEY=${(0,Pr.randomBytes)(24).toString("hex")}`),n.join(`
|
|
8
8
|
`)+`
|
|
9
|
-
`}function
|
|
10
|
-
`)}function
|
|
11
|
-
`)}function
|
|
12
|
-
|
|
9
|
+
`}function Mi(e){let t=[`# Hermes agent config for BadgerClaw bot ${e.botId}.`,"# Generated by badgerclaw CLI; secrets live in the sibling .env file.",""];if(e.llm){let n=e.llm.model||kt(e.llm.provider);t.push("model:",` default: ${n}`,` provider: ${e.llm.provider}`),e.llm.provider==="ollama"&&t.push(` base_url: ${e.llm.baseUrl||Zn}`," ollama_num_ctx: 65536"),t.push("")}return t.push("agent:",` name: ${e.botName||"BadgerClaw bot"}`,"terminal:"," backend: local","subagents:"," max_spawn_depth: 1","cron:"," enabled: false",""),t.join(`
|
|
10
|
+
`)}function Di(e){return[`# ${e.botName||"BadgerClaw bot"}`,"","You are a helpful assistant connected to the BadgerClaw platform via Matrix.","Respond concisely. Honour @mentions. Decline to act on rooms you weren't added to.",""].join(`
|
|
11
|
+
`)}function jr(e,t){let n=nn(e.botId);C.default.mkdirSync(n,{recursive:!0,mode:448});let o=F.default.join(n,".env"),r=F.default.join(n,"config.yaml"),s=F.default.join(n,"SOUL.md");return C.default.writeFileSync(o,Ti(e,t),{mode:384}),C.default.writeFileSync(r,Mi(e),{mode:384}),C.default.existsSync(s)||C.default.writeFileSync(s,Di(e),{mode:384}),{dataDir:n,envPath:o,configPath:r,soulPath:s}}function St(e){let t=e.run(["network","inspect","bridge","--format","{{range .IPAM.Config}}{{.Subnet}} {{end}}"]);return t.status===0&&!!(t.stdout||"").trim()}function sn(e={}){let t=e.env??process.env;if(t.BADGERCLAW_HERMES_NETWORK==="host")return!0;if(t.BADGERCLAW_HERMES_NETWORK==="bridge")return!1;let n=e.platform??process.platform;return e.localOllama&&n==="linux"?!0:!(e.bridgeUsable??(()=>St(vt)))()}function Br(e){return!!e&&e.provider==="ollama"&&!e.baseUrl}function Ur(e){let t=process.getuid?.()??0,n=process.getgid?.()??0,o=["run","-d","--name",e.containerName,"--restart","unless-stopped","-v",`${e.dataDir}:/opt/data`];return e.hostNetwork??process.env.BADGERCLAW_HERMES_NETWORK==="host"?o.push("--network","host"):o.push("-p",`127.0.0.1:${e.port}:${e.port}`),e.addHostGateway&&o.push("--add-host","host.docker.internal:host-gateway"),t>0&&o.push("-e",`HERMES_UID=${t}`),n>0&&o.push("-e",`HERMES_GID=${n}`),o.push("--env-file",`${e.dataDir}/.env`,e.image,"gateway","run"),o}function Hr(e,t={}){let n=t.docker??vt,o=e.image||process.env.HERMES_IMAGE||We;e.llm||console.warn(`[hermes] no LLM provider configured for bot ${e.botId}. Set BADGERCLAW_LLM_PROVIDER + BADGERCLAW_LLM_API_KEY (or one of GOOGLE_API_KEY / ANTHROPIC_API_KEY / OPENROUTER_API_KEY) and re-pair, or the bot will join rooms but be unable to reply.`);let r=me(),s=r.hermes_bots[e.botId],a=s?.port??Pi(r,t.portBase),i=s?.container_name??eo(e.botId),c=Br(e.llm),l=t.skipDocker?!1:sn({bridgeUsable:()=>St(n),localOllama:c});if(e.llm&&e.llm.provider==="ollama"&&!e.llm.baseUrl&&(e={...e,llm:{...e.llm,baseUrl:Tr(l)}}),jr(e,a),!t.skipDocker){n.run(["rm","-f",i]);let p=(e.llm?.baseUrl??"").includes("host.docker.internal"),_=Ur({containerName:i,dataDir:nn(e.botId),port:a,image:o,hostNetwork:l,addHostGateway:p}),R=n.run(_);if(R.status!==0)throw new Error(`docker run failed (exit ${R.status}): ${R.stderr||R.stdout}`)}let u={bot_id:e.botId,runtime:"hermes",container_name:i,port:a,data_dir:nn(e.botId),image:o,started_at:new Date().toISOString()};return r.hermes_bots[e.botId]=u,Nr(r),u}function Ni(e){let t={};for(let n of e.split(`
|
|
12
|
+
`)){let o=n.trim();if(!o||o.startsWith("#"))continue;let r=o.indexOf("=");r<=0||(t[o.slice(0,r).trim()]=o.slice(r+1))}return t}function ji(e,t,n,o,r){let s=t.MATRIX_HOMESERVER,a=t.MATRIX_ACCESS_TOKEN,i=t.MATRIX_USER_ID,c=t.MATRIX_DEVICE_ID,l=t.MATRIX_ALLOWED_USERS;if(!s||!a||!i||!c||!l)return null;let u=l.trim(),p=n&&n.match(/^\s*name:\s*(.+)$/m)?.[1]?.trim()||void 0;return{botId:e,matrixHomeserver:s,matrixAccessToken:a,matrixUserId:i,matrixDeviceId:c,ownerMatrixUserId:u,homeRoomId:t.MATRIX_HOME_ROOM||void 0,image:o,botName:p,llm:r}}function Bi(e,t,n={}){let o=n.docker??vt,r=me(),s=r.hermes_bots[e];if(!s)return{bot_id:e,rerendered:!1,restarted:!1,skipped:"not in state.json"};if(!t)return{bot_id:e,rerendered:!1,restarted:!1,skipped:"no LLM configured"};let a=F.default.join(s.data_dir,".env");if(!C.default.existsSync(a))return{bot_id:e,rerendered:!1,restarted:!1,skipped:"no .env on disk"};let i=Ni(C.default.readFileSync(a,"utf-8")),c=F.default.join(s.data_dir,"config.yaml"),l=C.default.existsSync(c)?C.default.readFileSync(c,"utf-8"):null,u=ji(e,i,l,s.image,t);if(!u)return{bot_id:e,rerendered:!1,restarted:!1,skipped:"incomplete Matrix credentials in .env"};if(!n.skipDocker){let b=o.run(["inspect","--format","{{.State.Status}}",s.container_name]),g=b.status===0?b.stdout.trim():"missing";if(g==="exited"||g==="dead"||g==="created"||g==="paused")return{bot_id:e,rerendered:!1,restarted:!1,skipped:`container ${g} \u2014 left as-is (start it, then re-provision to apply)`}}let p=Br(u.llm),_=n.skipDocker?!1:sn({bridgeUsable:()=>St(o),localOllama:p});u.llm&&u.llm.provider==="ollama"&&!u.llm.baseUrl&&(u={...u,llm:{...u.llm,baseUrl:Tr(_)}});let R=i.MATRIX_RECOVERY_KEY;if(!R&&!n.skipDocker){let b=o.run(["logs",s.container_name]);R=Gr(`${b.stdout}
|
|
13
|
+
${b.stderr}`)||void 0}jr(u,s.port),R&&C.default.appendFileSync(a,`
|
|
14
|
+
# Cross-signing recovery key (preserved across re-provision).
|
|
15
|
+
MATRIX_RECOVERY_KEY=${R}
|
|
16
|
+
`);let f=!1;if(!n.skipDocker){o.run(["rm","-f",s.container_name]);let b=(u.llm?.baseUrl??"").includes("host.docker.internal"),g=Ur({containerName:s.container_name,dataDir:s.data_dir,port:s.port,image:s.image,hostNetwork:_,addHostGateway:b}),y=o.run(g);if(y.status!==0)throw new Error(`docker run failed re-provisioning ${e} (exit ${y.status}): ${y.stderr||y.stdout}`);f=!0}return s.started_at=new Date().toISOString(),r.hermes_bots[e]=s,Nr(r),{bot_id:e,rerendered:!0,restarted:f}}function an(e,t={}){let n=[];for(let o of Object.keys(me().hermes_bots))try{n.push(Bi(o,e,t))}catch(r){n.push({bot_id:o,rerendered:!0,restarted:!1,skipped:`restart failed: ${r.message}`})}return n}function to(e={}){let t=e.docker??vt,n=me(),o=[];for(let[r,s]of Object.entries(n.hermes_bots)){let a=t.run(["inspect","--format","{{.State.Status}}",s.container_name]),i,c=a.stdout.trim()||null;a.status!==0?(i="missing",c=(a.stderr||a.stdout).trim()||null):c==="running"?i="running":c==="exited"||c==="dead"||c==="created"?i="stopped":i="unknown",o.push({bot_id:r,container_name:s.container_name,status:i,raw:c})}return o}function Fr(e={}){return to(e).map(n=>({bot_id:n.bot_id,bot_username:"",status:n.status==="running"?"running":n.status==="missing"?"error":"stopped",activity_state:"idle",active_task:null,last_activity_at:null,messages_received:0,messages_sent:0,chunked_messages:0,total_chunks_sent:0,errors:n.status==="missing"?1:0,last_error:n.status==="missing"?n.raw||"container missing":null,last_error_at:null,uptime_seconds:0,rooms_active:0,room_details:[],claude_code_enabled:!1,claude_code_session_id:null,quarantined:!1}))}function Gr(e){let t=e.match(Ui);return t?t[1].trim():null}async function no(e,t,n,o={}){let r={recoveryKey:null,posted:!1,envUpdated:!1};if(!e.backendBotId)return r;let a=(o.docker??vt).run(["logs",t]),i=`${a.stdout}
|
|
17
|
+
${a.stderr}`,c=Gr(i);if(!c)return r;if(r.recoveryKey=c,o.api)try{let u=await o.api.post(`/api/v1/hermes/bots/${e.backendBotId}/runtime-bootstrap`,{recovery_key:c,image_ref:n});r.posted=u.status>=200&&u.status<300}catch(u){console.warn(`[hermes] failed to POST recovery key for ${e.botId}: ${u.message}. Daemon will retry next beat.`)}let l=o.envPath??F.default.join(nn(e.botId),".env");return C.default.existsSync(l)&&(C.default.readFileSync(l,"utf-8").includes("MATRIX_RECOVERY_KEY=")||(C.default.appendFileSync(l,`
|
|
13
18
|
# Cross-signing recovery key captured from first-boot logs.
|
|
14
19
|
# Hermes re-uses this on restart to re-sign the device after
|
|
15
20
|
# Synapse rotates server keys.
|
|
16
21
|
MATRIX_RECOVERY_KEY=${c}
|
|
17
|
-
`),r.envUpdated=!0)),r}async function
|
|
22
|
+
`),r.envUpdated=!0)),r}async function qr(e={}){let t=me(),n=[];for(let[o,r]of Object.entries(t.hermes_bots)){let s=F.default.join(r.data_dir,".env"),a=!1;try{C.default.existsSync(s)&&(a=C.default.readFileSync(s,"utf-8").includes("MATRIX_RECOVERY_KEY="))}catch{}if(a){n.push({bot_id:o,attempted:!1,recoveryKeyFound:!0,posted:!1,envUpdated:!1});continue}if(o.startsWith("@")){n.push({bot_id:o,attempted:!1,recoveryKeyFound:!1,posted:!1,envUpdated:!1});continue}try{let i=await no({botId:o,backendBotId:o},r.container_name,r.image,{docker:e.docker,api:e.api,envPath:s});n.push({bot_id:o,attempted:!0,recoveryKeyFound:i.recoveryKey!==null,posted:i.posted,envUpdated:i.envUpdated})}catch(i){console.warn(`[hermes] recovery-key retry failed for ${o}: ${i.message}`),n.push({bot_id:o,attempted:!0,recoveryKeyFound:!1,posted:!1,envUpdated:!1})}}return n}var Er,Pr,C,Or,Lr,F,le,xi,Xn,$i,Zn,Ii,vt,on,rn,Ui,Te=V(()=>{"use strict";Er=require("child_process"),Pr=require("crypto"),C=m(require("fs")),Or=m(require("net")),Lr=m(require("os")),F=m(require("path"));Le();le=F.default.join(Lr.default.homedir(),".badgerclaw"),xi=F.default.join(le,"hermes"),Xn=F.default.join(le,"state.json"),$i=8642,Zn="http://127.0.0.1:11434/v1",Ii="http://host.docker.internal:11434/v1";vt={run:e=>{let t=(0,Er.spawnSync)("docker",e,{encoding:"utf-8"});return{status:t.status,stdout:t.stdout||"",stderr:t.stderr||""}}};on=F.default.join(le,"local-llm.json"),rn=F.default.join(le,"cloud-llm.json");Ui=/SAVE THIS RECOVERY KEY[\s\S]*?key rotation:\s+((?:[A-Za-z0-9]{4}\s+){7,}[A-Za-z0-9]{4})/});function ln(e){return`'${e.replace(/'/g,"'\\''")}'`}function Hi(){if(Se!==null)return Se;let e=["/opt/homebrew/lib/node_modules/openclaw/openclaw.mjs","/usr/local/lib/node_modules/openclaw/openclaw.mjs","/usr/lib/node_modules/openclaw/openclaw.mjs"];for(let t of e)if(Rt.default.existsSync(t))return Se=`${ln(process.execPath)} ${ln(t)}`,Se;try{let{execSync:t}=require("child_process"),n=t("npm root -g",{encoding:"utf-8",timeout:5e3,stdio:["ignore","pipe","ignore"]}).toString().trim();if(n){let o=so.default.join(n,"openclaw","openclaw.mjs");if(Rt.default.existsSync(o))return Se=`${ln(process.execPath)} ${ln(o)}`,Se}}catch{}return Se="openclaw",Se}function oo(e,t,n){t?t.fail(X.default.red(n)):e&&console.error(`[autopair] ${n}`)}async function Fi(e){if(!e.owner_matrix_user_id)throw new Error("Hermes redeem response missing owner_matrix_user_id. Update the API or fall back to OpenClaw runtime for this bot.");let t=e.user_id.replace(/[^a-zA-Z0-9_.-]/g,"_"),n={botId:e.bot_id??t,backendBotId:e.bot_id??void 0,matrixHomeserver:e.homeserver,matrixAccessToken:e.access_token,matrixUserId:e.user_id,matrixDeviceId:e.device_id,ownerMatrixUserId:e.owner_matrix_user_id,botName:e.bot_name,llm:Ye()},o=Hr(n);if(n.backendBotId){let r=$();if(r){let s=bt(r.access_token);try{await no(n,eo(n.botId),o.image,{api:{post:async(a,i)=>{let c=await s.post(a,i);return{status:c.status,data:c.data}}}})}catch(a){console.warn(`[hermes] recovery key capture failed for ${n.botId}: ${a.message}. Daemon will retry next beat.`)}}}}async function Ct(e,t,n,o){let r=$();if(!r)return;let s=o?null:(0,ro.default)(`Pairing bot: ${t}...`).start();try{let a=await fetch(`${N}/api/v1/pairing/redeem`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:e,instance_id:ae()})});if(!a.ok){if(a.status===410){s?.info(X.default.dim(`${t} already paired (race-loss; another channel redeemed first).`));return}let u=await a.json().catch(()=>({errors:[a.statusText]}));oo(o,s,`Failed to redeem ${t}: ${u.errors?.[0]||a.status}`);return}let i=await a.json(),c=i.result??i;if((c.runtime??"openclaw")==="hermes"){try{await Fi(c),s?.succeed(X.default.green(`\u2705 ${c.bot_name} (${c.user_id}) running on Hermes \u2014 bot is live!`))}catch(u){oo(o,s,`Failed to start Hermes container for ${t}: ${u.message}`)}return}await _r(async()=>{let u=c.user_id.split(":")[0].replace("@","").replace(/_bot$/,""),p;try{p=JSON.parse(Rt.default.readFileSync(Vr,"utf-8"))}catch{p={}}p.channels||(p.channels={}),p.channels.badgerclaw||(p.channels.badgerclaw={}),p.channels.badgerclaw.accounts||(p.channels.badgerclaw.accounts={});let _={userId:c.user_id,accessToken:c.access_token,homeserver:c.homeserver,encryption:!0,groupPolicy:"open",allowlistOnly:!1,dm:{policy:"open"}};c.device_id&&(_.deviceId=c.device_id),p.channels.badgerclaw.accounts[u]=_,Rt.default.writeFileSync(Vr,JSON.stringify(p,null,2),{mode:384})}),await fetch(`${N}/api/v1/openclaw/pending-pairs/${e}/claim`,{method:"POST",headers:{Authorization:`Bearer ${r.access_token}`}}).catch(()=>{}),s?.succeed(X.default.green(`\u2705 ${c.bot_name} (${c.user_id}) paired \u2014 bot is live!`))}catch(a){oo(o,s,`Failed to pair ${t}: ${a.message}`)}}async function xt(e=!1){let t=$();if(!t)return 0;let n=bt(t.access_token);try{let{data:o}=await n.get("/api/v1/openclaw/pending-pairs");if(!o||o.length===0)return 0;let r=new Map;for(let a of o)r.set(a.bot_user_id,a);for(let a of o)r.get(a.bot_user_id)!==a&&await n.post(`/api/v1/openclaw/pending-pairs/${a.pair_code}/claim`).catch(()=>{});let s=0;for(let a of r.values()){let i=e?null:(0,ro.default)(`Pairing bot: ${a.bot_name} (${a.bot_user_id})`).start();try{await Ct(a.pair_code,a.bot_name,a.bot_user_id,e),await n.post(`/api/v1/openclaw/pending-pairs/${a.pair_code}/claim`).catch(()=>{}),s++}catch(c){i?.fail(X.default.red(`Error pairing ${a.bot_name}: ${c}`))}}if(s>0)try{let{execSync:a}=await import("child_process");a(`${Hi()} gateway restart`,{stdio:"ignore"}),e||console.log(X.default.green(`
|
|
18
23
|
\u26A1 ${s} bot(s) paired \u2014 gateway restarted, bots are live!`))}catch(a){e?console.error(`[autopair] gateway restart after pair failed: ${a?.message||a}`):console.log(X.default.yellow(`
|
|
19
|
-
\u26A1 ${s} bot(s) paired. Run: openclaw gateway restart`))}return s}catch{return 0}}var
|
|
24
|
+
\u26A1 ${s} bot(s) paired. Run: openclaw gateway restart`))}return s}catch{return 0}}var Kr,X,ro,Wr,so,Rt,Vr,Se,Yr,$t=V(()=>{"use strict";Kr=require("commander"),X=m(require("chalk")),ro=m(require("ora")),Wr=m(require("os")),so=m(require("path")),Rt=m(require("fs"));H();Ee();ie();Ae();wt();Te();Vr=so.default.join(Wr.default.homedir(),".openclaw","openclaw.json");Se=null;Yr=new Kr.Command("autopair").description("Check for pending bot pairs and connect them to OpenClaw automatically").action(async()=>{if(!$()){console.log(X.default.yellow("Not logged in. Run `badgerclaw login` first."));return}console.log(X.default.dim("Checking for pending bot pairs...")),await xt(!1)===0&&console.log(X.default.dim("No pending pairs found."))})});async function po(){try{return await mn.default.get(il,{timeout:2e3,validateStatus:()=>!0}),!0}catch{return!1}}async function Pt(){try{let t=(await mn.default.get(`${as}/health`,{timeout:5e3})).data;return t.pluginVersion&&t.pluginVersion!=="unknown"&&(mo=t.pluginVersion),t}catch{return{status:await po()?"running":"stopped",pid:null,lastRestart:null,pluginVersion:mo,bots:[]}}}function go(e){e&&e!=="unknown"&&(mo=e)}async function et(){try{return{success:!0,message:(await mn.default.post(`${as}/restart`,{},{timeout:15e3})).data?.message||"Gateway restart initiated"}}catch(e){try{let{execSync:t}=await import("child_process");return t("openclaw gateway restart",{stdio:"pipe",timeout:15e3}),{success:!0,message:"Gateway restarted via CLI fallback"}}catch{return{success:!1,message:e.message||"Failed to restart gateway"}}}}var mn,sl,as,al,il,mo,Re=V(()=>{"use strict";mn=m(require("axios")),sl=7331,as=`http://localhost:${sl}`,al=parseInt(process.env.OPENCLAW_GATEWAY_PORT||"",10)||18789,il=`http://127.0.0.1:${al}`,mo="unknown"});var xo={};Bn(xo,{getActiveSessions:()=>Co,launchClaudeCode:()=>ko,launchMCPServer:()=>vo,recordSession:()=>So,stopSession:()=>Ro});function yo(){try{if(G.default.existsSync(ho))return JSON.parse(G.default.readFileSync(ho,"utf-8"))}catch{}return[]}function bo(e){G.default.mkdirSync(Ne,{recursive:!0}),G.default.writeFileSync(ho,JSON.stringify(e,null,2))}function hn(e){try{return process.kill(e,0),!0}catch{return!1}}function dl(){if(process.env.TMUX)return"tmux";try{if((0,pe.execSync)(`osascript -e 'tell application "System Events" to (name of processes) contains "iTerm2"'`,{stdio:"pipe",timeout:3e3}),G.default.existsSync("/Applications/iTerm.app"))return"iterm2"}catch{}return process.platform==="darwin"?"terminal":"direct"}function wo(e,t){let n=J.default.join(Ne,`claude-launcher-${t}.exp`),o=J.default.join(Ne,`pending-input-${t}.txt`);G.default.writeFileSync(o,"","utf-8");let r=`#!/usr/bin/expect -f
|
|
20
25
|
set timeout -1
|
|
21
26
|
set msgfile "${o}"
|
|
22
27
|
|
|
@@ -53,21 +58,21 @@ send "\\r"
|
|
|
53
58
|
interact timeout 2 {
|
|
54
59
|
check_and_send_messages
|
|
55
60
|
}
|
|
56
|
-
`;return
|
|
61
|
+
`;return G.default.mkdirSync(Ne,{recursive:!0}),G.default.writeFileSync(n,r,{mode:493}),n}function _o(e){return Object.entries(e).map(([t,n])=>`export ${t}="${n}"`).join("; ")}function ml(e,t,n){let o=wo(e,n.BADGERCLAW_SESSION_ID||"default"),r=_o(n),s=`
|
|
57
62
|
tell application "iTerm"
|
|
58
63
|
activate
|
|
59
64
|
set newWindow to (create window with default profile)
|
|
60
65
|
tell current session of newWindow
|
|
61
|
-
write text "cd ${
|
|
66
|
+
write text "cd ${yn(t)} && ${r} && ${yn(o)}"
|
|
62
67
|
end tell
|
|
63
68
|
end tell
|
|
64
|
-
`;(0,
|
|
69
|
+
`;(0,pe.execSync)(`osascript -e '${s.replace(/'/g,`'"'"'`)}'`,{stdio:"pipe",timeout:1e4})}function pl(e,t,n){let o=wo(e,n.BADGERCLAW_SESSION_ID||"default"),r=_o(n),a=`
|
|
65
70
|
tell application "Terminal"
|
|
66
71
|
activate
|
|
67
|
-
do script "${`cd ${
|
|
72
|
+
do script "${`cd ${yn(t)} && ${r} && ${yn(o)}`.replace(/"/g,'\\"')}"
|
|
68
73
|
end tell
|
|
69
|
-
`;(0,
|
|
70
|
-
`)}catch{}try{(0,T.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:1e4})}catch{}return await
|
|
74
|
+
`;(0,pe.execSync)(`osascript -e '${a.replace(/'/g,`'"'"'`)}'`,{stdio:"pipe",timeout:1e4})}function gl(e,t,n){let o=wo(e,n.BADGERCLAW_SESSION_ID||"default"),r=_o(n),s=`badgerclaw-${n.BADGERCLAW_SESSION_ID||"default"}`,a=`cd ${t} && ${r} && ${o}`;(0,pe.execSync)(`tmux new-session -d -s ${s} "${a}"`,{stdio:"pipe",timeout:1e4})}function yn(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function vo(e){let t=J.default.join(__dirname,"..","claude-code","mcp-server.js"),n=G.default.existsSync(t)?t:J.default.join(__dirname,"mcp-server.js"),o=(0,pe.spawn)("node",[n],{env:{...process.env,BADGERCLAW_MCP_PORT:String(e.port),BADGERCLAW_SESSION_ID:e.sessionId},cwd:e.projectDir,stdio:"ignore",detached:!0});return o.unref(),o}function ko(e){let t=J.default.join(J.default.dirname(process.argv[1]||""),"..","lib","node_modules","badgerclaw","dist","claude-code","mcp-server.js"),n=J.default.join(__dirname,"..","claude-code","mcp-server.js"),o=J.default.join(__dirname,"mcp-server.js"),r=G.default.existsSync(t)?t:G.default.existsSync(n)?n:o,s={BADGERCLAW_MCP_PORT:String(e.port),BADGERCLAW_SESSION_ID:e.sessionId},a=JSON.stringify({mcpServers:{badgerclaw:{command:"node",args:[r],env:s}}}),i=J.default.join(Ne,`mcp-config-${e.sessionId}.json`);G.default.mkdirSync(Ne,{recursive:!0}),G.default.writeFileSync(i,a,"utf-8");let c=`claude --dangerously-skip-permissions --mcp-config ${i}`;switch(dl()){case"iterm2":ml(c,e.projectDir,s);break;case"terminal":pl(c,e.projectDir,s);break;case"tmux":gl(c,e.projectDir,s);break;case"direct":(0,pe.spawn)(c,[],{shell:!0,cwd:e.projectDir,env:{...process.env,...s},stdio:"ignore",detached:!0}).unref();break}}function So(e,t,n,o,r){let s=yo().filter(a=>a.sessionId!==e);s.push({sessionId:e,pid:t,mcpPid:n,port:o,projectDir:r,startedAt:new Date().toISOString()}),bo(s)}function Ro(e){let t=yo(),n=t.find(r=>r.sessionId===e);if(!n)return!1;if(n.mcpPid&&hn(n.mcpPid))try{process.kill(n.mcpPid,"SIGTERM")}catch{}if(n.pid&&hn(n.pid))try{process.kill(n.pid,"SIGTERM")}catch{}try{(0,pe.execSync)(`tmux kill-session -t badgerclaw-${e} 2>/dev/null`,{stdio:"pipe",timeout:3e3})}catch{}let o=t.filter(r=>r.sessionId!==e);return bo(o),!0}function Co(){let e=yo(),t=e.filter(n=>hn(n.mcpPid)||hn(n.pid));return t.length!==e.length&&bo(t),t}var pe,G,J,cs,Ne,ho,bn=V(()=>{"use strict";pe=require("child_process"),G=m(require("fs")),J=m(require("path")),cs=m(require("os")),Ne=J.default.join(cs.default.homedir(),".badgerclaw"),ho=J.default.join(Ne,"claude-sessions.json")});async function ge(e){return e.command_type==="update_cli"?hl(e.payload?.target_version):e.command_type==="update_plugin"?yl(e.payload?.target_version):e.command_type==="restart_gateway"?fl():e.command_type==="leave_room"?Ol(e.payload):e.command_type==="start_claude_code"?Sl(e.payload):e.command_type==="stop_claude_code"?Rl(e.payload):e.command_type==="start_claude_control"?Cl(e.payload):e.command_type==="stop_claude_control"?xl(e.payload):e.command_type==="update_claude_tools"?$l(e.payload):e.command_type==="bot_share_notify"?Il(e.payload):e.command_type==="bot_share_revoked"?Al(e.payload):e.command_type==="bot_share_expired"?El(e.payload):e.command_type==="bot_refresh"?Pl(e.payload):{success:!1,message:`Unknown command: ${e.command_type}`}}function fl(){try{return(0,T.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:15e3}),{success:!0,message:"Gateway restarted"}}catch{try{return{success:!0,message:`Gateway restarted via probe: ${(0,T.execSync)("curl -s -X POST http://localhost:7331/restart",{stdio:"pipe",timeout:1e4}).toString()}`}}catch(e){return{success:!1,message:`Gateway restart failed: ${e.message||"unknown error"}`}}}}function hl(e){let t=e?`badgerclaw@${e}`:"badgerclaw@latest";try{(0,T.execSync)(`npm install -g ${t}`,{stdio:"pipe",timeout:12e4});let n=(0,T.execSync)("npm list -g badgerclaw --json",{stdio:"pipe"}).toString(),r=JSON.parse(n)?.dependencies?.badgerclaw?.version||"unknown";return wl(),{success:!0,message:`Updated CLI to ${r}`,newVersion:r}}catch(n){let o=n.stderr?.toString()||n.message||"Unknown error";return o.includes("EACCES")||o.includes("permission")?{success:!1,message:"Permission denied. Run: sudo npm install -g badgerclaw"}:{success:!1,message:`CLI update failed: ${o.slice(0,500)}`}}}async function yl(e){let t=E.default.join(je.default.homedir(),".openclaw","extensions","badgerclaw"),n=E.default.join(je.default.homedir(),".openclaw","npm","node_modules","@badgerclaw","connect"),o=k.default.existsSync(n)?"npm":k.default.existsSync(t)?"legacy":null;if(o===null)return{success:!1,message:"Cannot find plugin install (checked ~/.openclaw/npm/node_modules/@badgerclaw/connect and ~/.openclaw/extensions/badgerclaw). Re-run: badgerclaw setup"};let r=e?`@badgerclaw/connect@${e}`:"@badgerclaw/connect@latest";try{if(o==="npm"){let u=E.default.dirname(E.default.dirname(E.default.dirname(n)));(0,T.execSync)(`npm install ${r}`,{cwd:u,stdio:"pipe",timeout:12e4});let p=E.default.join(n,"package.json"),_="unknown";k.default.existsSync(p)&&(_=JSON.parse(k.default.readFileSync(p,"utf-8")).version||"unknown"),go(_);try{(0,T.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:1e4})}catch{}return await us(_)}let s=t;(0,T.execSync)(`npm install ${r}`,{cwd:s,stdio:"pipe",timeout:12e4});let a=E.default.join(s,"node_modules","@badgerclaw","connect"),i=E.default.join(a,"package.json"),c="unknown";k.default.existsSync(i)&&(c=JSON.parse(k.default.readFileSync(i,"utf-8")).version||"unknown"),go(c),bl(a,s);let l=E.default.join(s,"package.json");if(k.default.existsSync(l)&&k.default.existsSync(i))try{let u=JSON.parse(k.default.readFileSync(l,"utf-8")),p=JSON.parse(k.default.readFileSync(i,"utf-8"));u.version=c,p.openclaw&&(u.openclaw={...u.openclaw,...p.openclaw}),k.default.writeFileSync(l,JSON.stringify(u,null,2)+`
|
|
75
|
+
`)}catch{}try{(0,T.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:1e4})}catch{}return await us(c)}catch(s){return{success:!1,message:`Plugin update failed: ${(s.stderr?.toString()||s.message||"").slice(0,500)}`}}}async function us(e){for(let t=0;t<15;t++){await new Promise(n=>setTimeout(n,1e3));try{let n=await q.default.get("http://localhost:7331/health",{timeout:3e3});if(n.data?.status==="running"&&n.data?.pluginVersion===e)break}catch{}}return{success:!0,message:`Updated plugin to ${e}, gateway restarted`,newVersion:e}}function bl(e,t){let n=E.default.join(e,"dist"),o=E.default.join(t,"dist");k.default.existsSync(n)&&(k.default.mkdirSync(o,{recursive:!0}),$o(n,o));let r=E.default.join(e,"openclaw.plugin.json");k.default.existsSync(r)&&k.default.copyFileSync(r,E.default.join(t,"openclaw.plugin.json"));let s=E.default.join(e,"scripts"),a=E.default.join(t,"scripts");k.default.existsSync(s)&&(k.default.mkdirSync(a,{recursive:!0}),$o(s,a));for(let i of["index.ts","src","STREAMING.md","SETUP.md","CHANGELOG.md"]){let c=E.default.join(t,i);k.default.existsSync(c)&&(k.default.statSync(c).isDirectory()?k.default.rmSync(c,{recursive:!0,force:!0}):k.default.unlinkSync(c))}}function $o(e,t){for(let n of k.default.readdirSync(e,{withFileTypes:!0})){let o=E.default.join(e,n.name),r=E.default.join(t,n.name);n.isDirectory()?(k.default.mkdirSync(r,{recursive:!0}),$o(o,r)):k.default.copyFileSync(o,r)}}function wl(){try{if(_l())return;try{(0,T.execSync)('pkill -f "badgerclaw heartbeat"',{stdio:"pipe",timeout:5e3})}catch{}try{(0,T.execSync)('pkill -f "badgerclaw watch"',{stdio:"pipe",timeout:5e3})}catch{}(0,T.execSync)("sleep 1",{stdio:"pipe"});let e=(0,T.execSync)("command -v badgerclaw",{stdio:"pipe",shell:"/bin/sh"}).toString().trim()||"badgerclaw";(0,T.execSync)(`nohup ${e} heartbeat > /dev/null 2>&1 &`,{stdio:"pipe",shell:"/bin/zsh"}),(0,T.execSync)(`nohup ${e} watch > /dev/null 2>&1 &`,{stdio:"pipe",shell:"/bin/zsh"})}catch{}}function _l(){let e="";try{e=(0,T.execSync)("supervisorctl status badgerclaw-heartbeat 2>&1",{stdio:"pipe",shell:"/bin/sh",timeout:8e3}).toString()}catch(t){e=(t?.stdout?.toString()||"")+(t?.stderr?.toString()||"")}if(!e.includes("badgerclaw-heartbeat")||/no such process|ERROR/i.test(e))return!1;try{return(0,T.execSync)("nohup supervisorctl restart badgerclaw-heartbeat >/dev/null 2>&1 &",{stdio:"pipe",shell:"/bin/sh",timeout:8e3}),!0}catch{return!1}}function vl(){let e=E.default.join(je.default.homedir(),".badgerclaw","workspace");k.default.mkdirSync(e,{recursive:!0});let t=E.default.join(e,".git");return k.default.existsSync(t)||(0,T.execSync)("git init",{cwd:e,stdio:"pipe",timeout:5e3}),k.default.writeFileSync(E.default.join(e,"CLAUDE.md"),`You are a chat assistant connected to Matrix rooms via BadgerClaw.
|
|
71
76
|
|
|
72
77
|
When you receive a message, it will be formatted like:
|
|
73
78
|
[Room: RoomName] Sender: message text
|
|
@@ -76,14 +81,14 @@ Reply using the reply tool with botId="..." and roomId="..."
|
|
|
76
81
|
ALWAYS use the \`reply\` MCP tool to send your response back to the chat room.
|
|
77
82
|
Use the exact botId and roomId provided in the message \u2014 do not modify them.
|
|
78
83
|
Keep responses concise and helpful.
|
|
79
|
-
`),e}function gl(){try{w.default.mkdirSync(A.default.dirname(fn),{recursive:!0});try{let e=w.default.statSync(fn);if(Date.now()-e.mtimeMs<6e4)return!1;w.default.unlinkSync(fn)}catch{}return w.default.writeFileSync(fn,String(process.pid),{flag:"wx"}),!0}catch{return!1}}async function fl(e){if(!e?.bot_id)return{success:!1,message:"Missing bot_id in payload"};if(!gl())return{success:!0,message:"Claude Code start already in progress, skipping duplicate"};try{let t=e.bot_user_id?.startsWith("@")?e.bot_user_id:await It(e.bot_id),{launchClaudeCode:n,recordSession:o,getActiveSessions:r,stopSession:s}=(gn(),er(Ro)),a=new Set;try{let S=(await G.default.get("http://localhost:7331/claude-code/status",{timeout:5e3})).data?.bots||[];for(let P of S)if(P.botId!==t&&P.enabled!==!1){let g=P.port;typeof g=="number"&&g>=7332&&a.add(g)}}catch{}for(let p=7332;p<=7340;p++)try{(0,T.execSync)(`lsof -ti :${p} >/dev/null 2>&1`,{stdio:"pipe",timeout:2e3}),a.add(p)}catch{}let i=7332;for(;a.has(i);)i++;let c=r();for(let p of c)p.port===i&&(console.log(`[command-executor] Stopping existing session ${p.sessionId} on port ${i}`),s(p.sessionId));try{(0,T.execSync)(`lsof -ti :${i} | xargs kill -9 2>/dev/null`,{stdio:"pipe",timeout:3e3})}catch{}let l=e.session_id||`session-${Date.now()}`;await G.default.post("http://localhost:7331/claude-code/toggle",{botId:t,enabled:!0,sessionId:l,port:i},{timeout:1e4});let u=pl();return n({sessionId:l,port:i,projectDir:u}),o(l,0,0,i,u),{success:!0,message:`Claude Code started globally for bot ${t} (session: ${l}, port: ${i})`}}catch(t){return{success:!1,message:`Failed to start Claude Code: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function It(e){if(e.startsWith("@"))return e;try{let n=(await G.default.get("http://localhost:7331/health",{timeout:1e4})).data?.bots||[],o=A.default.join(De.default.homedir(),".badgerclaw","bot-mapping.json");if(w.default.existsSync(o)){let r=JSON.parse(w.default.readFileSync(o,"utf-8"));if(r[e])return r[e]}return n.length===1?n[0].botId:(console.warn(`[command-executor] Could not resolve bot UUID ${e} to Matrix ID, trying as-is`),e)}catch{return e}}async function hl(e){if(!e?.bot_id)return{success:!1,message:"Missing bot_id in payload"};try{let t=e.bot_user_id?.startsWith("@")?e.bot_user_id:await It(e.bot_id),n=e.session_id;if(!n)try{let o=await G.default.get("http://localhost:7331/claude-code/status",{timeout:5e3}),s=(o.data?.bots||o.data?.rooms||[]).find(a=>a.botId===t);s&&(n=s.sessionId)}catch{}if(await G.default.post("http://localhost:7331/claude-code/toggle",{botId:t,enabled:!1},{timeout:1e4}),n){let{stopSession:o}=(gn(),er(Ro));o(n)}return{success:!0,message:"Claude Code stopped"}}catch(t){return{success:!1,message:`Failed to stop Claude Code: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function yl(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in payload"};let n=t;n.startsWith("@")||(n=await It(t));let o=e?.session_id||`cc-${Date.now()}`;return await G.default.post("http://localhost:7331/claude-code/toggle",{botId:n,enabled:!0,sessionId:o,mode:"primary",tools:e?.tools},{timeout:1e4}),console.log(`[claude-control] Primary mode enabled for ${n} (session: ${o})`),{success:!0,message:`Claude Control (primary) started for ${n}`}}catch(t){return{success:!1,message:`Failed to start Claude Control: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function bl(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in payload"};let n=t;return n.startsWith("@")||(n=await It(t)),await G.default.post("http://localhost:7331/claude-code/toggle",{botId:n,enabled:!1},{timeout:1e4}),console.log(`[claude-control] Primary mode disabled for ${n}`),{success:!0,message:`Claude Control stopped for ${n}`}}catch(t){return{success:!1,message:`Failed to stop Claude Control: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function wl(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t||!e?.tools)return{success:!1,message:"Missing bot_id or tools in payload"};let n=t;return n.startsWith("@")||(n=await It(t)),await G.default.post("http://localhost:7331/claude-code/tools",{botId:n,tools:e.tools},{timeout:1e4}),{success:!0,message:`Tools updated for ${n}`}}catch(t){return{success:!1,message:`Failed to update tools: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function _l(e){if(!e?.bot_user_id||!e?.shared_with_user_id)return{success:!1,message:"Missing bot_user_id or shared_with_user_id"};try{return await G.default.post("http://localhost:7331/bot-share/notify",{botId:e.bot_user_id,sharedWithUserId:e.shared_with_user_id,ownerDisplayName:e.owner_display_name||void 0,botDisplayName:e.bot_name||void 0,expiresAt:e.expires_at||void 0},{timeout:1e4}),console.log(`[bot-share] Notify: ${e.shared_with_user_id} can now access ${e.bot_user_id}`),{success:!0,message:`Share notification sent for ${e.shared_with_user_id}`}}catch(t){return{success:!1,message:`Failed to notify share: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Sl(e){if(!e?.bot_user_id||!e?.shared_with_user_id)return{success:!1,message:"Missing bot_user_id or shared_with_user_id"};try{return await G.default.post("http://localhost:7331/bot-share/revoked",{botId:e.bot_user_id,sharedWithUserId:e.shared_with_user_id,botDisplayName:e.bot_name||void 0},{timeout:1e4}),console.log(`[bot-share] Revoked: ${e.shared_with_user_id} lost access to ${e.bot_user_id}`),{success:!0,message:`Share revocation sent for ${e.shared_with_user_id}`}}catch(t){return{success:!1,message:`Failed to revoke share: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function vl(e){if(!e?.bot_user_id||!e?.shared_with_user_id)return{success:!1,message:"Missing bot_user_id or shared_with_user_id"};try{return await G.default.post("http://localhost:7331/bot-share/expired",{botId:e.bot_user_id,sharedWithUserId:e.shared_with_user_id,botDisplayName:e.bot_name||void 0},{timeout:1e4}),console.log(`[bot-share] Expired: ${e.shared_with_user_id} access to ${e.bot_user_id}`),{success:!0,message:`Share expiry sent for ${e.shared_with_user_id}`}}catch(t){return{success:!1,message:`Failed to expire share: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function kl(e){let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in bot_refresh payload"};let n=t;t.startsWith("@")&&(n=t.split(":")[0].replace("@","").replace(/_bot$/,""));let o=A.default.join(De.default.homedir(),".openclaw","badgerclaw","quarantine",`${n}.json`);try{w.default.rmSync(o,{force:!0})}catch{}try{return(0,T.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:15e3}),{success:!0,message:`Bot ${n} quarantine cleared, gateway restarted`}}catch{return{success:!1,message:`Quarantine cleared for ${n} but gateway restart failed`}}}async function Rl(e){if(!e?.bot_id||!e?.room_id)return{success:!1,message:"Missing bot_id or room_id in payload"};try{return{success:!0,message:(await G.default.post("http://localhost:7331/leave-room",{botId:e.bot_id,roomId:e.room_id},{timeout:15e3})).data?.message||"Left room"}}catch(t){return{success:!1,message:`Failed to leave room: ${t.response?.data?.message||t.message||"Unknown error"}`}}}var T,w,A,De,G,fn,xo=q(()=>{"use strict";T=require("child_process"),w=m(require("fs")),A=m(require("path")),De=m(require("os")),G=m(require("axios"));ke();fn=A.default.join(De.default.homedir(),".badgerclaw","claude-code.lock")});var Io={};Dn(Io,{heartbeatCommand:()=>$o,pushHeartbeat:()=>U});function yn(e,t){if(e!=="start_claude_code"&&e!=="stop_claude_code")return!1;let n=`${e}:${t||"unknown"}`,o=Date.now();for(let[r,s]of hn)o-s>Al&&hn.delete(r);return hn.has(n)?!0:(hn.set(n,o),!1)}function cs(e,t){let n=Gt(),o=e.bots.map(a=>({bot_id:a.botId,bot_username:a.botUsername,status:a.status,activity_state:a.activityState||"idle",active_task:a.activeTask||null,last_activity_at:a.lastActivity,messages_received:a.messagesReceived,messages_sent:a.messagesSent,chunked_messages:a.chunkedMessages,total_chunks_sent:a.totalChunksSent,errors:a.errors,last_error:a.lastError,last_error_at:a.lastErrorAt,uptime_seconds:a.uptimeSeconds,rooms_active:a.roomsActive,room_details:(a.roomDetails||[]).map(i=>({roomId:i.roomId,roomName:i.roomName,workspaceName:i.workspaceName||null,messagesInRoom:i.messagesInRoom,lastActivityInRoom:i.lastActivityInRoom})),claude_code_enabled:a.claudeCodeEnabled??!1,claude_code_session_id:a.claudeCodeSessionId??null,quarantined:a.quarantined??!1})),r=new Set(o.map(a=>a.bot_id)),s=Mr().filter(a=>!r.has(a.bot_id));return{instance_id:ae(),timestamp:new Date().toISOString(),cli_version:t,plugin_version:e.pluginVersion||"unknown",hostname:n.hostname,os:n.os,arch:n.arch,uptime_seconds:n.uptimeSeconds,mem_free_mb:n.memFreeMb,gateway_status:e.status,gateway_pid:e.pid,last_gateway_restart:e.lastRestart,capabilities:Se(),hermes_containers:Zn(),bots:[...o,...s]}}async function U(){let{version:e}=we(),t=await $t(),n=cs(t,e);await B().post(ls,n)}function El(e,t){if(!e||e.status!==t.status)return!0;let n=new Map(e.bots.map(o=>[o.botId,o]));for(let o of t.bots){let r=n.get(o.botId);if(!r||r.status!==o.status||r.errors!==o.errors)return!0}return e.bots.length!==t.bots.length}async function ss(){try{let n=(await B().get($l)).data?.shares||[],o={shares:n.map(r=>({botUserId:r.bot_user_id,sharedWithUserId:r.shared_with_user_id,expiresAt:r.expires_at||null,shareId:r.share_id}))};await is.default.post("http://localhost:7331/bot-share/sync",o,{timeout:1e4}),console.log(f.default.dim(` [${E()}] Share sync: ${n.length} active share(s) pushed to plugin`))}catch(e){console.log(f.default.dim(` [${E()}] Share sync failed (non-fatal): ${e.message}`))}}async function Pl(){if(!rs){rs=!0;try{await B().post(xl,{instance_id:ae()},{timeout:Il}),console.log(f.default.dim(` [${new Date().toISOString()}] Posted disconnect \u2014 dashboard will mark machine offline immediately.`))}catch(e){console.log(f.default.dim(` [${new Date().toISOString()}] Disconnect post failed (non-fatal): ${e?.message||e}`))}}}async function us(){let e=R();if(!e?.expires_at)return;let t=new Date(e.expires_at).getTime(),n=Date.now(),o=300*1e3;if(t-n<o)if(console.log(f.default.dim(` [${E()}] Token expires soon \u2014 refreshing proactively...`)),await Un())console.log(f.default.green(` [${E()}] Token refreshed successfully`));else{let s=cr()||"unknown reason";console.log(f.default.yellow(` [${E()}] Token refresh failed: ${s}`))}}function Ll(e,t){let n=3e3,o=3e4,r=null;async function s(){try{await us();let i=R()?.access_token||e,c=require("eventsource"),l=new c(`${N}/api/v1/openclaw/events`,{headers:{Authorization:`Bearer ${i}`}});l.onopen=()=>{n=3e3,console.log(f.default.dim(` [${E()}] SSE connected \u2014 listening for real-time events`)),r&&clearTimeout(r),r=setTimeout(()=>{console.log(f.default.dim(` [${E()}] SSE proactive reconnect \u2014 refreshing token before expiry`)),l.close(),s()},Ol)},l.onmessage=async u=>{try{let p=JSON.parse(u.data);await Tl(p)}catch{}},l.onerror=async()=>{l.close(),r&&(clearTimeout(r),r=null),console.log(f.default.dim(` [${E()}] SSE disconnected \u2014 reconnecting in ${n/1e3}s...`)),setTimeout(s,n),n=Math.min(n*1.5,o)}}catch(a){console.log(f.default.dim(` [${E()}] SSE connection failed: ${a.message}`)),setTimeout(s,n),n=Math.min(n*1.5,o)}}s()}function E(){return new Date().toISOString()}async function Tl(e){if(e.type==="pair"){console.log(f.default.cyan(` [${E()}] Pair event: ${e.bot_name}`)),await St(e.pair_code,e.bot_name,e.bot_user_id,!0);try{let{execSync:t}=await import("child_process");t("openclaw gateway restart",{stdio:"ignore"}),console.log(f.default.green(` [${E()}] Gateway restarted \u2014 ${e.bot_name} is live`))}catch{}try{await U()}catch{}}else if(e.type==="bot.delete"&&e.bot_user_id){let t=e.bot_user_id,n=t.split(":")[0].replace("@","").replace(/_bot$/,"");console.log(f.default.yellow(` [${E()}] Bot deleted: ${t}`));try{let o=await import("fs"),r=await import("path"),s=await import("os"),a=r.join(s.homedir(),".openclaw","openclaw.json");if(Pe(()=>{let c=JSON.parse(o.readFileSync(a,"utf-8")),l=!1;if(c.channels?.badgerclaw?.accounts?.[n]&&(delete c.channels.badgerclaw.accounts[n],l=!0),c.agents?.list){let u=c.agents.list.length;c.agents.list=c.agents.list.filter(p=>p.id!==n),c.agents.list.length!==u&&(l=!0)}return l&&o.writeFileSync(a,JSON.stringify(c,null,2)),l})){console.log(f.default.green(` [${E()}] Removed "${n}" from openclaw.json`));let{execSync:c}=await import("child_process");try{c("openclaw gateway restart",{stdio:"ignore"}),console.log(f.default.green(` [${E()}] Gateway restarted`))}catch{}}}catch(o){console.log(f.default.red(` [${E()}] Failed to clean up bot: ${o}`))}try{await U()}catch{}}else if(e.type==="gateway-restart"){console.log(f.default.cyan(` [${E()}] Remote gateway-restart received`));let t=await Qe();console.log(t.success?f.default.green(` [${E()}] Gateway restarted`):f.default.red(` [${E()}] Gateway restart failed: ${t.message}`));try{await U()}catch{}}else if(e.type==="claude_code.start"&&e.bot_id){if(console.log(f.default.cyan(` [${E()}] Claude Code START: bot=${e.bot_id}`)),yn("start_claude_code",e.bot_id)){console.log(f.default.dim(` [${E()}] Skipping duplicate start_claude_code (already processed)`));return}try{let t=await pe({id:e.command_id||`cc-start-${Date.now()}`,command_type:"start_claude_code",payload:{bot_id:e.bot_id,bot_user_id:e.bot_user_id,room_id:e.room_id,session_id:e.session_id}});e.command_id&&await B().post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:t.success?"success":"failed",result:t.message}).catch(()=>{})}catch{}try{await U()}catch{}}else if(e.type==="claude_code.stop"&&e.bot_id){if(console.log(f.default.cyan(` [${E()}] Claude Code STOP: bot=${e.bot_id}`)),yn("stop_claude_code",e.bot_id)){console.log(f.default.dim(` [${E()}] Skipping duplicate stop_claude_code (already processed)`));return}try{let t=await pe({id:e.command_id||`cc-stop-${Date.now()}`,command_type:"stop_claude_code",payload:{bot_id:e.bot_id,bot_user_id:e.bot_user_id,room_id:e.room_id,session_id:e.session_id}});e.command_id&&await B().post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:t.success?"success":"failed",result:t.message}).catch(()=>{})}catch{}try{await U()}catch{}}else if(e.type==="command.execute"&&e.command_id){if(console.log(f.default.cyan(` [${E()}] Command: ${e.command_type} (${e.command_id})`)),yn(e.command_type,e.payload?.bot_id)){console.log(f.default.dim(` [${E()}] Skipping duplicate ${e.command_type} (already processed)`));return}try{let t=B();await t.post(`/api/v1/dashboard/commands/${e.command_id}/ack`);let n=await pe({id:e.command_id,command_type:e.command_type,payload:e.payload});await t.post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:n.success?"success":"failed",result:n.message,new_version:n.newVersion||null}),e.command_type==="update_cli"&&n.success&&(console.log(f.default.cyan(` [${E()}] CLI updated \u2014 restarting in 2s...`)),setTimeout(()=>process.exit(0),2e3))}catch{}try{await U()}catch{}}}var as,f,is,os,Cl,ls,xl,$l,Il,rs,hn,Al,$o,Ol,Xe=q(()=>{"use strict";as=require("commander"),f=m(require("chalk")),is=m(require("axios"));F();Ae();Ie();Oe();Ve();ke();xo();ie();kt();ke();yt();Ge();os=3e4,Cl=15e4,ls="/api/v1/dashboard/heartbeat",xl="/api/v1/dashboard/disconnect",$l="/api/v1/me/active-shares",Il=3e3,rs=!1,hn=new Map,Al=3e4;$o=new as.Command("heartbeat").description("Run heartbeat daemon \u2014 reports telemetry to the BadgerClaw dashboard every 30s").action(async()=>{let e=R();e||(console.log(f.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let{version:t}=we();console.log(f.default.green(`Heartbeat daemon started (v${t})`)),console.log(f.default.dim(` Instance: ${ae()}`)),console.log(f.default.dim(` Interval: ${os/1e3}s`)),console.log(f.default.dim(` Press Ctrl+C to stop.
|
|
80
|
-
`));let n=null,o=10,r=0,s=async()=>{try{await
|
|
81
|
-
[${new Date().toISOString()}] Received ${c} \u2014 posting disconnect and exiting.`)),
|
|
84
|
+
`),e}function kl(){try{k.default.mkdirSync(E.default.dirname(wn),{recursive:!0});try{let e=k.default.statSync(wn);if(Date.now()-e.mtimeMs<6e4)return!1;k.default.unlinkSync(wn)}catch{}return k.default.writeFileSync(wn,String(process.pid),{flag:"wx"}),!0}catch{return!1}}async function Sl(e){if(!e?.bot_id)return{success:!1,message:"Missing bot_id in payload"};if(!kl())return{success:!0,message:"Claude Code start already in progress, skipping duplicate"};try{let t=e.bot_user_id?.startsWith("@")?e.bot_user_id:await Ot(e.bot_id),{launchClaudeCode:n,recordSession:o,getActiveSessions:r,stopSession:s}=(bn(),nr(xo)),a=new Set;try{let _=(await q.default.get("http://localhost:7331/claude-code/status",{timeout:5e3})).data?.bots||[];for(let R of _)if(R.botId!==t&&R.enabled!==!1){let f=R.port;typeof f=="number"&&f>=7332&&a.add(f)}}catch{}for(let p=7332;p<=7340;p++)try{(0,T.execSync)(`lsof -ti :${p} >/dev/null 2>&1`,{stdio:"pipe",timeout:2e3}),a.add(p)}catch{}let i=7332;for(;a.has(i);)i++;let c=r();for(let p of c)p.port===i&&(console.log(`[command-executor] Stopping existing session ${p.sessionId} on port ${i}`),s(p.sessionId));try{(0,T.execSync)(`lsof -ti :${i} | xargs kill -9 2>/dev/null`,{stdio:"pipe",timeout:3e3})}catch{}let l=e.session_id||`session-${Date.now()}`;await q.default.post("http://localhost:7331/claude-code/toggle",{botId:t,enabled:!0,sessionId:l,port:i},{timeout:1e4});let u=vl();return n({sessionId:l,port:i,projectDir:u}),o(l,0,0,i,u),{success:!0,message:`Claude Code started globally for bot ${t} (session: ${l}, port: ${i})`}}catch(t){return{success:!1,message:`Failed to start Claude Code: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Ot(e){if(e.startsWith("@"))return e;try{let n=(await q.default.get("http://localhost:7331/health",{timeout:1e4})).data?.bots||[],o=E.default.join(je.default.homedir(),".badgerclaw","bot-mapping.json");if(k.default.existsSync(o)){let r=JSON.parse(k.default.readFileSync(o,"utf-8"));if(r[e])return r[e]}return n.length===1?n[0].botId:(console.warn(`[command-executor] Could not resolve bot UUID ${e} to Matrix ID, trying as-is`),e)}catch{return e}}async function Rl(e){if(!e?.bot_id)return{success:!1,message:"Missing bot_id in payload"};try{let t=e.bot_user_id?.startsWith("@")?e.bot_user_id:await Ot(e.bot_id),n=e.session_id;if(!n)try{let o=await q.default.get("http://localhost:7331/claude-code/status",{timeout:5e3}),s=(o.data?.bots||o.data?.rooms||[]).find(a=>a.botId===t);s&&(n=s.sessionId)}catch{}if(await q.default.post("http://localhost:7331/claude-code/toggle",{botId:t,enabled:!1},{timeout:1e4}),n){let{stopSession:o}=(bn(),nr(xo));o(n)}return{success:!0,message:"Claude Code stopped"}}catch(t){return{success:!1,message:`Failed to stop Claude Code: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Cl(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in payload"};let n=t;n.startsWith("@")||(n=await Ot(t));let o=e?.session_id||`cc-${Date.now()}`;return await q.default.post("http://localhost:7331/claude-code/toggle",{botId:n,enabled:!0,sessionId:o,mode:"primary",tools:e?.tools},{timeout:1e4}),console.log(`[claude-control] Primary mode enabled for ${n} (session: ${o})`),{success:!0,message:`Claude Control (primary) started for ${n}`}}catch(t){return{success:!1,message:`Failed to start Claude Control: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function xl(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in payload"};let n=t;return n.startsWith("@")||(n=await Ot(t)),await q.default.post("http://localhost:7331/claude-code/toggle",{botId:n,enabled:!1},{timeout:1e4}),console.log(`[claude-control] Primary mode disabled for ${n}`),{success:!0,message:`Claude Control stopped for ${n}`}}catch(t){return{success:!1,message:`Failed to stop Claude Control: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function $l(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t||!e?.tools)return{success:!1,message:"Missing bot_id or tools in payload"};let n=t;return n.startsWith("@")||(n=await Ot(t)),await q.default.post("http://localhost:7331/claude-code/tools",{botId:n,tools:e.tools},{timeout:1e4}),{success:!0,message:`Tools updated for ${n}`}}catch(t){return{success:!1,message:`Failed to update tools: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Il(e){if(!e?.bot_user_id||!e?.shared_with_user_id)return{success:!1,message:"Missing bot_user_id or shared_with_user_id"};try{return await q.default.post("http://localhost:7331/bot-share/notify",{botId:e.bot_user_id,sharedWithUserId:e.shared_with_user_id,ownerDisplayName:e.owner_display_name||void 0,botDisplayName:e.bot_name||void 0,expiresAt:e.expires_at||void 0},{timeout:1e4}),console.log(`[bot-share] Notify: ${e.shared_with_user_id} can now access ${e.bot_user_id}`),{success:!0,message:`Share notification sent for ${e.shared_with_user_id}`}}catch(t){return{success:!1,message:`Failed to notify share: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Al(e){if(!e?.bot_user_id||!e?.shared_with_user_id)return{success:!1,message:"Missing bot_user_id or shared_with_user_id"};try{return await q.default.post("http://localhost:7331/bot-share/revoked",{botId:e.bot_user_id,sharedWithUserId:e.shared_with_user_id,botDisplayName:e.bot_name||void 0},{timeout:1e4}),console.log(`[bot-share] Revoked: ${e.shared_with_user_id} lost access to ${e.bot_user_id}`),{success:!0,message:`Share revocation sent for ${e.shared_with_user_id}`}}catch(t){return{success:!1,message:`Failed to revoke share: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function El(e){if(!e?.bot_user_id||!e?.shared_with_user_id)return{success:!1,message:"Missing bot_user_id or shared_with_user_id"};try{return await q.default.post("http://localhost:7331/bot-share/expired",{botId:e.bot_user_id,sharedWithUserId:e.shared_with_user_id,botDisplayName:e.bot_name||void 0},{timeout:1e4}),console.log(`[bot-share] Expired: ${e.shared_with_user_id} access to ${e.bot_user_id}`),{success:!0,message:`Share expiry sent for ${e.shared_with_user_id}`}}catch(t){return{success:!1,message:`Failed to expire share: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Pl(e){let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in bot_refresh payload"};let n=t;t.startsWith("@")&&(n=t.split(":")[0].replace("@","").replace(/_bot$/,""));let o=E.default.join(je.default.homedir(),".openclaw","badgerclaw","quarantine",`${n}.json`);try{k.default.rmSync(o,{force:!0})}catch{}try{return(0,T.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:15e3}),{success:!0,message:`Bot ${n} quarantine cleared, gateway restarted`}}catch{return{success:!1,message:`Quarantine cleared for ${n} but gateway restart failed`}}}async function Ol(e){if(!e?.bot_id||!e?.room_id)return{success:!1,message:"Missing bot_id or room_id in payload"};try{return{success:!0,message:(await q.default.post("http://localhost:7331/leave-room",{botId:e.bot_id,roomId:e.room_id},{timeout:15e3})).data?.message||"Left room"}}catch(t){return{success:!1,message:`Failed to leave room: ${t.response?.data?.message||t.message||"Unknown error"}`}}}var T,k,E,je,q,wn,Io=V(()=>{"use strict";T=require("child_process"),k=m(require("fs")),E=m(require("path")),je=m(require("os")),q=m(require("axios"));Re();wn=E.default.join(je.default.homedir(),".badgerclaw","claude-code.lock")});var Eo={};Bn(Eo,{heartbeatCommand:()=>Ao,pushHeartbeat:()=>U});function vn(e,t){if(e!=="start_claude_code"&&e!=="stop_claude_code")return!1;let n=`${e}:${t||"unknown"}`,o=Date.now();for(let[r,s]of _n)o-s>Nl&&_n.delete(r);return _n.has(n)?!0:(_n.set(n,o),!1)}function ys(e,t){let n=Yt(),o=e.bots.map(a=>({bot_id:a.botId,bot_username:a.botUsername,status:a.status,activity_state:a.activityState||"idle",active_task:a.activeTask||null,last_activity_at:a.lastActivity,messages_received:a.messagesReceived,messages_sent:a.messagesSent,chunked_messages:a.chunkedMessages,total_chunks_sent:a.totalChunksSent,errors:a.errors,last_error:a.lastError,last_error_at:a.lastErrorAt,uptime_seconds:a.uptimeSeconds,rooms_active:a.roomsActive,room_details:(a.roomDetails||[]).map(i=>({roomId:i.roomId,roomName:i.roomName,workspaceName:i.workspaceName||null,messagesInRoom:i.messagesInRoom,lastActivityInRoom:i.lastActivityInRoom})),claude_code_enabled:a.claudeCodeEnabled??!1,claude_code_session_id:a.claudeCodeSessionId??null,quarantined:a.quarantined??!1})),r=new Set(o.map(a=>a.bot_id)),s=Fr().filter(a=>!r.has(a.bot_id));return{instance_id:ae(),timestamp:new Date().toISOString(),cli_version:t,plugin_version:e.pluginVersion||"unknown",hostname:n.hostname,os:n.os,arch:n.arch,uptime_seconds:n.uptimeSeconds,mem_free_mb:n.memFreeMb,gateway_status:e.status,gateway_pid:e.pid,last_gateway_restart:e.lastRestart,capabilities:ke(),hermes_containers:to(),bots:[...o,...s]}}async function U(){let{version:e}=_e(),t=await Pt(),n=ys(t,e);await B().post(hs,n)}function jl(e,t){if(!e||e.status!==t.status)return!0;let n=new Map(e.bots.map(o=>[o.botId,o]));for(let o of t.bots){let r=n.get(o.botId);if(!r||r.status!==o.status||r.errors!==o.errors)return!0}return e.bots.length!==t.bots.length}async function ps(){try{let n=(await B().get(Ml)).data?.shares||[],o={shares:n.map(r=>({botUserId:r.bot_user_id,sharedWithUserId:r.shared_with_user_id,expiresAt:r.expires_at||null,shareId:r.share_id}))};await fs.default.post("http://localhost:7331/bot-share/sync",o,{timeout:1e4}),console.log(h.default.dim(` [${P()}] Share sync: ${n.length} active share(s) pushed to plugin`))}catch(e){console.log(h.default.dim(` [${P()}] Share sync failed (non-fatal): ${e.message}`))}}async function Bl(){if(!ms){ms=!0;try{await B().post(Tl,{instance_id:ae()},{timeout:Dl}),console.log(h.default.dim(` [${new Date().toISOString()}] Posted disconnect \u2014 dashboard will mark machine offline immediately.`))}catch(e){console.log(h.default.dim(` [${new Date().toISOString()}] Disconnect post failed (non-fatal): ${e?.message||e}`))}}}async function bs(){let e=$();if(!e?.expires_at)return;let t=new Date(e.expires_at).getTime(),n=Date.now(),o=300*1e3;if(t-n<o)if(console.log(h.default.dim(` [${P()}] Token expires soon \u2014 refreshing proactively...`)),await Gn())console.log(h.default.green(` [${P()}] Token refreshed successfully`));else{let s=dr()||"unknown reason";console.log(h.default.yellow(` [${P()}] Token refresh failed: ${s}`))}}function Hl(e,t){let n=3e3,o=3e4,r=null;async function s(){try{await bs();let i=$()?.access_token||e,c=require("eventsource"),l=new c(`${N}/api/v1/openclaw/events`,{headers:{Authorization:`Bearer ${i}`}});l.onopen=()=>{n=3e3,console.log(h.default.dim(` [${P()}] SSE connected \u2014 listening for real-time events`)),r&&clearTimeout(r),r=setTimeout(()=>{console.log(h.default.dim(` [${P()}] SSE proactive reconnect \u2014 refreshing token before expiry`)),l.close(),s()},Ul)},l.onmessage=async u=>{try{let p=JSON.parse(u.data);await Fl(p)}catch{}},l.onerror=async()=>{l.close(),r&&(clearTimeout(r),r=null),console.log(h.default.dim(` [${P()}] SSE disconnected \u2014 reconnecting in ${n/1e3}s...`)),setTimeout(s,n),n=Math.min(n*1.5,o)}}catch(a){console.log(h.default.dim(` [${P()}] SSE connection failed: ${a.message}`)),setTimeout(s,n),n=Math.min(n*1.5,o)}}s()}function P(){return new Date().toISOString()}async function Fl(e){if(e.type==="pair"){console.log(h.default.cyan(` [${P()}] Pair event: ${e.bot_name}`)),await Ct(e.pair_code,e.bot_name,e.bot_user_id,!0);try{let{execSync:t}=await import("child_process");t("openclaw gateway restart",{stdio:"ignore"}),console.log(h.default.green(` [${P()}] Gateway restarted \u2014 ${e.bot_name} is live`))}catch{}try{await U()}catch{}}else if(e.type==="bot.delete"&&e.bot_user_id){let t=e.bot_user_id,n=t.split(":")[0].replace("@","").replace(/_bot$/,"");console.log(h.default.yellow(` [${P()}] Bot deleted: ${t}`));try{let o=await import("fs"),r=await import("path"),s=await import("os"),a=r.join(s.homedir(),".openclaw","openclaw.json");if(Oe(()=>{let c=JSON.parse(o.readFileSync(a,"utf-8")),l=!1;if(c.channels?.badgerclaw?.accounts?.[n]&&(delete c.channels.badgerclaw.accounts[n],l=!0),c.agents?.list){let u=c.agents.list.length;c.agents.list=c.agents.list.filter(p=>p.id!==n),c.agents.list.length!==u&&(l=!0)}return l&&o.writeFileSync(a,JSON.stringify(c,null,2)),l})){console.log(h.default.green(` [${P()}] Removed "${n}" from openclaw.json`));let{execSync:c}=await import("child_process");try{c("openclaw gateway restart",{stdio:"ignore"}),console.log(h.default.green(` [${P()}] Gateway restarted`))}catch{}}}catch(o){console.log(h.default.red(` [${P()}] Failed to clean up bot: ${o}`))}try{await U()}catch{}}else if(e.type==="gateway-restart"){console.log(h.default.cyan(` [${P()}] Remote gateway-restart received`));let t=await et();console.log(t.success?h.default.green(` [${P()}] Gateway restarted`):h.default.red(` [${P()}] Gateway restart failed: ${t.message}`));try{await U()}catch{}}else if(e.type==="claude_code.start"&&e.bot_id){if(console.log(h.default.cyan(` [${P()}] Claude Code START: bot=${e.bot_id}`)),vn("start_claude_code",e.bot_id)){console.log(h.default.dim(` [${P()}] Skipping duplicate start_claude_code (already processed)`));return}try{let t=await ge({id:e.command_id||`cc-start-${Date.now()}`,command_type:"start_claude_code",payload:{bot_id:e.bot_id,bot_user_id:e.bot_user_id,room_id:e.room_id,session_id:e.session_id}});e.command_id&&await B().post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:t.success?"success":"failed",result:t.message}).catch(()=>{})}catch{}try{await U()}catch{}}else if(e.type==="claude_code.stop"&&e.bot_id){if(console.log(h.default.cyan(` [${P()}] Claude Code STOP: bot=${e.bot_id}`)),vn("stop_claude_code",e.bot_id)){console.log(h.default.dim(` [${P()}] Skipping duplicate stop_claude_code (already processed)`));return}try{let t=await ge({id:e.command_id||`cc-stop-${Date.now()}`,command_type:"stop_claude_code",payload:{bot_id:e.bot_id,bot_user_id:e.bot_user_id,room_id:e.room_id,session_id:e.session_id}});e.command_id&&await B().post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:t.success?"success":"failed",result:t.message}).catch(()=>{})}catch{}try{await U()}catch{}}else if(e.type==="command.execute"&&e.command_id){if(console.log(h.default.cyan(` [${P()}] Command: ${e.command_type} (${e.command_id})`)),vn(e.command_type,e.payload?.bot_id)){console.log(h.default.dim(` [${P()}] Skipping duplicate ${e.command_type} (already processed)`));return}try{let t=B();await t.post(`/api/v1/dashboard/commands/${e.command_id}/ack`);let n=await ge({id:e.command_id,command_type:e.command_type,payload:e.payload});await t.post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:n.success?"success":"failed",result:n.message,new_version:n.newVersion||null}),e.command_type==="update_cli"&&n.success&&(console.log(h.default.cyan(` [${P()}] CLI updated \u2014 restarting in 2s...`)),setTimeout(()=>process.exit(0),2e3))}catch{}try{await U()}catch{}}}var gs,h,fs,ds,Ll,hs,Tl,Ml,Dl,ms,_n,Nl,Ao,Ul,tt=V(()=>{"use strict";gs=require("commander"),h=m(require("chalk")),fs=m(require("axios"));H();Ee();Ae();Le();Te();Re();Io();ie();$t();Re();wt();Ke();ds=3e4,Ll=15e4,hs="/api/v1/dashboard/heartbeat",Tl="/api/v1/dashboard/disconnect",Ml="/api/v1/me/active-shares",Dl=3e3,ms=!1,_n=new Map,Nl=3e4;Ao=new gs.Command("heartbeat").description("Run heartbeat daemon \u2014 reports telemetry to the BadgerClaw dashboard every 30s").action(async()=>{let e=$();e||(console.log(h.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let{version:t}=_e();console.log(h.default.green(`Heartbeat daemon started (v${t})`)),console.log(h.default.dim(` Instance: ${ae()}`)),console.log(h.default.dim(` Interval: ${ds/1e3}s`)),console.log(h.default.dim(` Press Ctrl+C to stop.
|
|
85
|
+
`));let n=null,o=10,r=0,s=async()=>{try{await bs();let c=await Pt(),l=ys(c,t);jl(n,c)&&n!==null&&console.log(h.default.cyan(` [${new Date().toISOString()}] State change detected \u2014 pushing immediately`));let p=B(),_=await p.post(hs,l);r=0;let R=c.bots.length,f=c.bots.filter(y=>y.status==="running").length;console.log(h.default.dim(` [${new Date().toISOString()}] gateway=${c.status} bots=${f}/${R} running mem=${l.mem_free_mb}MB free`)),n=c;try{let v=await qr({api:{post:async(j,oe)=>{let re=await p.post(j,oe);return{status:re.status,data:re.data}}}});for(let j of v)j.attempted&&(j.posted||j.envUpdated)&&console.log(h.default.cyan(` [${new Date().toISOString()}] Hermes recovery key captured for ${j.bot_id} (posted=${j.posted}, env_updated=${j.envUpdated})`))}catch(y){console.log(h.default.dim(` [${new Date().toISOString()}] Hermes recovery-key retry skipped: ${y.message}`))}let b=_.data?.pending_commands||[],g=!1;for(let y of b)try{if(console.log(h.default.cyan(` [${new Date().toISOString()}] Received command: ${y.command_type} (${y.id})`)),await p.post(`/api/v1/dashboard/commands/${y.id}/ack`),vn(y.command_type,y.payload?.bot_id)){console.log(h.default.dim(` [${new Date().toISOString()}] Skipping duplicate ${y.command_type}`));continue}let v=await ge(y);console.log(v.success?h.default.green(` [${new Date().toISOString()}] ${v.message}`):h.default.red(` [${new Date().toISOString()}] ${v.message}`)),await p.post(`/api/v1/dashboard/commands/${y.id}/result`,{status:v.success?"success":"failed",result:v.message,new_version:v.newVersion||null}),y.command_type==="update_cli"&&v.success&&(g=!0)}catch(v){console.log(h.default.dim(` [${new Date().toISOString()}] Command ${y.id} error: ${v.message}`))}if(g){console.log(h.default.cyan(` [${new Date().toISOString()}] CLI updated \u2014 restarting in 2s...`));try{await U()}catch{}setTimeout(()=>process.exit(0),2e3);return}}catch(c){c instanceof Ve&&(console.log(h.default.yellow(` [${new Date().toISOString()}] Account deactivated \u2014 signed out. Exiting daemon.`)),process.exit(0)),r+=1,console.log(h.default.dim(` [${new Date().toISOString()}] Heartbeat failed (${r}/${o}): ${c.message}`)),r>=o&&(console.log(h.default.red(` [${new Date().toISOString()}] ${o} consecutive heartbeat failures \u2014 exiting so launchctl/systemd can restart the daemon.`)),process.exit(1))}};await s();let a=Y()!==null;a?await ps():console.log(h.default.dim(` [${P()}] Hermes-only host \u2014 share-sync disabled (no plugin to push to).`)),setInterval(s,ds),a&&setInterval(ps,Ll),Hl(e.access_token,t);let i=c=>{console.log(h.default.yellow(`
|
|
86
|
+
[${new Date().toISOString()}] Received ${c} \u2014 posting disconnect and exiting.`)),Bl().finally(()=>{process.exit(0)})};process.on("SIGTERM",i),process.on("SIGINT",i),await new Promise(()=>{})});Ul=2700*1e3});var oi=require("commander");var ws=require("commander"),z=m(require("chalk")),_s=m(require("ora")),vs=m(require("open")),Po=m(require("os"));var Un=m(require("crypto"));function or(){return Un.default.randomBytes(32).toString("base64url")}function rr(e){return Un.default.createHash("sha256").update(e).digest("base64url")}H();Ae();Ee();ie();$t();var un=m(require("os")),ee=m(require("fs")),dn=m(require("path")),De=require("child_process");var Je=m(require("fs")),Me=m(require("path")),Qe=m(require("os")),Xe=require("child_process"),Jr="ai.badgerclaw.watch",zr=Me.default.join(Qe.default.homedir(),"Library","LaunchAgents"),ze=Me.default.join(zr,`${Jr}.plist`);function Gi(){try{return(0,Xe.execSync)("which badgerclaw",{encoding:"utf-8"}).trim()}catch{return"/opt/homebrew/bin/badgerclaw"}}function qi(){try{return(0,Xe.execSync)("which node",{encoding:"utf-8"}).trim()}catch{return"/opt/homebrew/bin/node"}}function Vi(e){let t=Me.default.join(Qe.default.homedir(),".badgerclaw"),n=qi(),o=Me.default.dirname(n),r=Me.default.dirname(e),s=[o,r,"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin"],a=new Set,i=s.filter(c=>!c||a.has(c)?!1:(a.add(c),!0)).join(":");return`<?xml version="1.0" encoding="UTF-8"?>
|
|
82
87
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
83
88
|
<plist version="1.0">
|
|
84
89
|
<dict>
|
|
85
90
|
<key>Label</key>
|
|
86
|
-
<string>${
|
|
91
|
+
<string>${Jr}</string>
|
|
87
92
|
<key>ProgramArguments</key>
|
|
88
93
|
<array>
|
|
89
94
|
<string>${n}</string>
|
|
@@ -106,7 +111,7 @@ Keep responses concise and helpful.
|
|
|
106
111
|
<key>ThrottleInterval</key>
|
|
107
112
|
<integer>30</integer>
|
|
108
113
|
</dict>
|
|
109
|
-
</plist>`}function
|
|
114
|
+
</plist>`}function Qr(){if(Qe.default.platform()==="darwin")try{let e=Gi();Je.default.mkdirSync(zr,{recursive:!0}),Je.default.mkdirSync(Me.default.join(Qe.default.homedir(),".badgerclaw"),{recursive:!0}),Je.default.writeFileSync(ze,Vi(e));try{(0,Xe.execSync)(`launchctl unload "${ze}" 2>/dev/null`)}catch{}(0,Xe.execSync)(`launchctl load "${ze}"`),console.log("\x1B[2mPair-watcher registered as background service (auto-starts on login).\x1B[0m")}catch{console.log("\x1B[2mNote: could not register background service. Run `badgerclaw watch` manually.\x1B[0m")}}function Xr(){if(Qe.default.platform()==="darwin")try{(0,Xe.execSync)(`launchctl unload "${ze}" 2>/dev/null`),Je.default.existsSync(ze)&&Je.default.unlinkSync(ze)}catch{}}var Ze=m(require("fs")),At=m(require("path")),It=m(require("os")),Z=require("child_process"),cn="badgerclaw-watch.service",Zr=At.default.join(It.default.homedir(),".config","systemd","user"),ao=At.default.join(Zr,cn);function Ki(){try{return(0,Z.execSync)("which badgerclaw",{encoding:"utf-8"}).trim()}catch{return"/usr/local/bin/badgerclaw"}}function Wi(){try{return(0,Z.execSync)("which node",{encoding:"utf-8"}).trim()}catch{return"/usr/bin/node"}}function Yi(e){let t=Wi(),n=At.default.dirname(t);return`[Unit]
|
|
110
115
|
Description=BadgerClaw background watcher (heartbeat + pair events)
|
|
111
116
|
After=network-online.target
|
|
112
117
|
Wants=network-online.target
|
|
@@ -122,80 +127,82 @@ StandardError=append:%h/.badgerclaw/heartbeat.log
|
|
|
122
127
|
|
|
123
128
|
[Install]
|
|
124
129
|
WantedBy=default.target
|
|
125
|
-
`}function
|
|
126
|
-
\u26A0\uFE0F systemd lingering is OFF for root. The gateway will stop on SSH disconnect.`)),console.log(
|
|
127
|
-
`));for(let r of o){let s=
|
|
128
|
-
\u2705 Refresh triggered. Watch the gateway logs for token refresh progress:`)),console.log(
|
|
129
|
-
\
|
|
130
|
-
\
|
|
131
|
-
\u{
|
|
132
|
-
|
|
133
|
-
\u{
|
|
134
|
-
|
|
135
|
-
\
|
|
136
|
-
`)
|
|
137
|
-
`):null}function zl(){let e=[],t=(0,kn.spawnSync)("npm",["root","-g"],{encoding:"utf-8",shell:!0});t.status===0&&t.stdout?.trim()&&e.push(nt.default.join(t.stdout.trim(),"openclaw","pnpm-workspace.yaml"));let n=(0,kn.spawnSync)("npm",["prefix","-g"],{encoding:"utf-8",shell:!0});n.status===0&&n.stdout?.trim()&&e.push(nt.default.join(n.stdout.trim(),"lib","node_modules","openclaw","pnpm-workspace.yaml"));let o=(0,kn.spawnSync)("which",["openclaw"],{encoding:"utf-8"});if(o.status===0&&o.stdout?.trim()){let r=nt.default.dirname(nt.default.dirname(o.stdout.trim()));e.push(nt.default.join(r,"lib","node_modules","openclaw","pnpm-workspace.yaml"))}for(let r of e)try{if(Ot.default.existsSync(r))return r}catch{}return null}function Ls(){let e=zl();if(!e)return null;let t;try{t=Ot.default.readFileSync(e,"utf-8")}catch{return null}let n=Jl(t);if(n===null)return null;try{Ot.default.writeFileSync(e,n)}catch{return null}return{yamlPath:e,original:t}}function Ts(e){if(!e)return!1;try{return Ot.default.writeFileSync(e.yamlPath,e.original),!0}catch{return!1}}Oe();var rt=require("child_process"),Ds=m(require("fs")),D=m(require("chalk")),W={alreadyUp:!1,install:null,start:null,requiresRoot:!1,networkHostRequired:!1,guidance:null},Ms='On this distro the bare "docker" package is the podman shim (no dockerd). Install Docker Engine from the official docker-ce repository (https://docs.docker.com/engine/install/), then re-run `badgerclaw setup --hermes`.';function Ql(e){return e.daemonUp?{...W,alreadyUp:!0}:e.host==="darwin"?e.hasDockerApp?{...W,start:"docker-app"}:e.hasColima?{...W,start:"colima"}:e.binaryPresent?{...W,guidance:"A `docker` CLI is present but no engine we can start (Docker.app / colima) was found and the daemon is not answering. Start your Docker daemon (or `colima start`), then re-run `badgerclaw setup --hermes`."}:e.hasBrew?{...W,install:{tool:"colima",via:"brew"},start:"colima"}:{...W,guidance:"No container engine found and Homebrew is absent. Install Docker Desktop (https://www.docker.com/products/docker-desktop) or Homebrew (https://brew.sh) then colima, and re-run `badgerclaw setup --hermes`."}:e.host==="linux-systemd"?e.binaryPresent?{...W,start:"systemctl",requiresRoot:!0}:e.pkgManager==="apt"?{...W,install:{tool:"docker",via:"apt"},start:"systemctl",requiresRoot:!0}:{...W,guidance:e.pkgManager?Ms:"Docker is not installed and no supported package manager (apt) was found. Install Docker, then re-run."}:e.host==="linux-nosystemd"?e.binaryPresent?{...W,start:"dockerd-restricted",requiresRoot:!0,networkHostRequired:!0}:e.pkgManager==="apt"?{...W,install:{tool:"docker",via:"apt"},start:"dockerd-restricted",requiresRoot:!0,networkHostRequired:!0}:{...W,guidance:e.pkgManager?Ms:"Docker is not installed and no supported package manager (apt) was found. Install Docker (e.g. `apt-get install -y docker.io`), then re-run."}:{...W,guidance:`Automatic Docker setup isn't supported on this platform (${process.platform}). Install + start Docker manually, then re-run.`}}function Ns(e,t,n=8e3){return(0,rt.spawnSync)(e,t,{encoding:"utf-8",timeout:n})}function Lo(){let e=Ns("docker",["version","--format","{{.Server.Version}}"],4e3);return e.status===0&&!!(e.stdout||"").trim()}function Xl(){return Ns("docker",["--version"],4e3).status===0}function ot(e){return(0,rt.spawnSync)("sh",["-c",`command -v ${e}`],{encoding:"utf-8"}).status===0}function Zl(){return ot("apt-get")?"apt":ot("dnf")?"dnf":ot("yum")?"yum":null}function ec(){let e=(0,rt.spawnSync)("systemctl",["is-system-running"],{encoding:"utf-8",timeout:4e3});if(e.error)return!1;let t=`${e.stdout||""}${e.stderr||""}`.toLowerCase();return!/failed to connect to bus|not been booted with systemd|no such file/.test(t)}function tc(){return process.platform==="darwin"?"darwin":process.platform==="linux"?ec()?"linux-systemd":"linux-nosystemd":"unsupported"}function nc(){return typeof process.getuid=="function"?process.getuid()===0:!1}function Ce(e,t){return t(D.default.dim(` $ ${e}`)),(0,rt.spawnSync)("sh",["-c",e],{stdio:"inherit"}).status===0}function oc(e,t,n){let o=t?"sudo ":"";switch(e.via){case"brew":return n(D.default.dim(" Installing colima + docker CLI via Homebrew...")),Ce("brew install colima docker",n);case"apt":return n(D.default.dim(" Installing Docker via apt...")),Ce(`${o}apt-get update`,n),Ce(`${o}apt-get install -y docker.io`,n);default:return!1}}function rc(e,t,n){let o=t?"sudo ":"";switch(e){case"docker-app":return Ce("open -a Docker",n);case"colima":return Ce("colima start",n);case"systemctl":return Ce(`${o}systemctl enable --now docker`,n);case"dockerd-restricted":{if(!ot("dockerd"))return n(D.default.red(" \u274C `dockerd` is not installed (the docker CLI alone is not enough on a no-systemd host).")),!1;if(t&&!Ce("sudo -v",n))return n(D.default.red(" \u274C root is required to start dockerd here, but sudo is unavailable.")),!1;Ce(`${o}sh -c 'mount -t cgroup2 none /sys/fs/cgroup 2>/dev/null || true'`,n);let r="/tmp/badgerclaw-dockerd.log";n(D.default.dim(` Starting dockerd (restricted: --bridge=none --iptables=false) \u2192 ${r}`));let s=`nohup dockerd --storage-driver=overlay2 --iptables=false --bridge=none > ${r} 2>&1 &`,a=t?`sudo sh -c ${JSON.stringify(s)}`:`sh -c ${JSON.stringify(s)}`;return(0,rt.spawnSync)("sh",["-c",a],{stdio:"ignore"}).status===0}}}async function sc(e,t,n){let o=Date.now()+t;for(;;){if(e())return!0;if(Date.now()>=o)return!1;await new Promise(r=>setTimeout(r,n))}}async function js(e={}){let t=e.log??(a=>console.log(a));if(process.env.BADGERCLAW_NO_AUTO_DEPS==="1"){let a=Lo();return a||(t(D.default.yellow(" BADGERCLAW_NO_AUTO_DEPS=1 \u2014 skipping automatic Docker setup.")),t(D.default.dim(" Start the Docker daemon yourself, then re-run `badgerclaw setup --hermes`."))),{ok:a,daemonUp:a,networkHostRequired:!1}}let n={daemonUp:Lo(),binaryPresent:Xl(),host:tc(),hasBrew:ot("brew"),hasDockerApp:Ds.default.existsSync("/Applications/Docker.app"),hasColima:ot("colima"),pkgManager:Zl(),isRoot:nc()},o=Ql(n);if(o.alreadyUp)return{ok:!0,daemonUp:!0,networkHostRequired:!1};if(o.guidance)return t(D.default.red(` \u274C ${o.guidance}`)),{ok:!1,daemonUp:!1,networkHostRequired:o.networkHostRequired};let r=o.requiresRoot&&!n.isRoot;return r&&t(D.default.dim(" (some steps need root \u2014 using sudo; you may be prompted for your password)")),o.install&&!oc(o.install,r,t)?(t(D.default.red(" \u274C Docker install failed (see output above). Install it manually, then re-run.")),{ok:!1,daemonUp:!1,networkHostRequired:o.networkHostRequired}):rc(o.start,r,t)?(t(D.default.dim(" Waiting for the Docker daemon to answer...")),await sc(Lo,12e4,1500)?(t(D.default.green(" \u2705 Docker daemon is up.")),{ok:!0,daemonUp:!0,networkHostRequired:o.networkHostRequired}):(t(D.default.red(" \u274C Docker did not become ready in time.")),o.start==="dockerd-restricted"?t(D.default.dim(" Check /tmp/badgerclaw-dockerd.log for the daemon output.")):o.start==="docker-app"&&t(D.default.dim(" Docker Desktop can take a minute to boot \u2014 re-run once its menu-bar icon is steady.")),{ok:!1,daemonUp:!1,networkHostRequired:o.networkHostRequired})):(t(D.default.red(" \u274C Could not start Docker (see output above). Resolve it, then re-run.")),{ok:!1,daemonUp:!1,networkHostRequired:o.networkHostRequired})}function Bs(e){let t=e.minMajor??18;if(!Number.isFinite(e.nodeMajor)||e.nodeMajor<t){let n=Number.isFinite(e.nodeMajor)?`Node v${e.nodeMajor}`:"an unknown Node version";return`badgerclaw requires Node.js >= ${t} (this is ${n}). Upgrade Node \u2014 nodesource (https://github.com/nodesource/distributions) or nvm (\`nvm install ${t}\`) \u2014 then re-run \`badgerclaw setup --hermes\`.`}return e.npmPresent?null:`npm was not found on PATH. badgerclaw is installed and updated via npm \u2014 install Node.js ${t}+ (which bundles npm) and re-run \`badgerclaw setup --hermes\`.`}function Us(e=process.versions.node){return parseInt(String(e).split(".")[0],10)}var To=require("child_process"),Rn=m(require("fs")),Fs=m(require("os")),Cn=m(require("path"));Oe();Ve();function ac(){let e=Hs();for(let t of e)if(Rn.default.existsSync(t))return t;return null}function Hs(){return[Cn.default.resolve(__dirname,"..","assets","hermes-agent-bc.Dockerfile"),Cn.default.resolve(__dirname,"..","..","assets","hermes-agent-bc.Dockerfile")]}var ic="nousresearch/hermes-agent";function lc(e){let t=(process.env.HERMES_UPSTREAM||e?.upstream||"").trim();if(!t)return;if(t.includes("/"))return t;let n=t.includes(":")?t.split(":").pop():t;return`${ic}:${n}`}var cc={run:e=>{let t=(0,To.spawnSync)("docker",e,{encoding:"utf-8"});return{status:t.status,stdout:t.stdout||"",stderr:t.stderr||""}}};function uc(e){return["build",...e.hostNetwork?["--network","host"]:[],...e.upstreamRef?["--build-arg",`UPSTREAM=${e.upstreamRef}`]:[],"-t",e.image,"-f",e.dockerfile,e.ctxDir]}function Gs(e){let t=Jt();if(!t.installed)return{status:"no-docker"};let n=process.env.HERMES_IMAGE||qe;if(zt(t).installed)return{status:"already-present",image:n};let r=ac();if(!r)return{status:"no-dockerfile",expectedPaths:Hs()};let s=lc(e),a=Xn({bridgeUsable:()=>nn(cc)}),i=Rn.default.mkdtempSync(Cn.default.join(Fs.default.tmpdir(),"bc-hermes-build-"));try{let c=(0,To.spawnSync)("docker",uc({hostNetwork:a,upstreamRef:s,image:n,dockerfile:r,ctxDir:i}),{stdio:"inherit",timeout:6e5});return c.status===0?{status:"built",image:n}:{status:"build-failed",image:n,stderr:`docker build exited ${c.status??"<no-status>"}`}}finally{try{Rn.default.rmSync(i,{recursive:!0,force:!0})}catch{}}}F();var Do=m(require("os")),at=require("child_process"),Mo="https://ollama.com/download",dc={run:(e,t)=>{let n=(0,at.spawnSync)(e,t,{encoding:"utf-8",timeout:1e4});return{status:n.status,stdout:n.stdout||"",stderr:n.stderr||""}}};function st(e={}){let t=e.runner??dc,n=t.run("ollama",["--version"]);if(n.status!==0)return{installed:!1,serving:!1};let o=(n.stdout||"").match(/(\d+\.\d+\.\d+)/),r=o?o[1]:void 0;return{installed:!0,serving:t.run("ollama",["list"]).status===0,version:r}}function qs(e){return e.installed?e.serving?"Ollama is installed and serving.":"Ollama is installed but its server isn't running. Start it with `ollama serve` (or open the Ollama app), then re-run setup.":`Ollama is not installed. Install it from ${Mo}, then re-run setup.`}function xn(e,t={}){if(t.runner){let o=t.runner.run("ollama",["pull",e]);return o.status===0?{ok:!0}:{ok:!1,error:(o.stderr||o.stdout||`exit ${o.status}`).trim()}}let n=(0,at.spawnSync)("ollama",["pull",e],{stdio:t.inheritStdio===!1?"pipe":"inherit",encoding:"utf-8"});return n.status===0?{ok:!0}:{ok:!1,error:(n.stderr||`ollama pull ${e} failed (exit ${n.status})`).toString().trim()}}function mc(e,t){return t?t.run("sh",["-c",`command -v ${e}`]).status===0:(0,at.spawnSync)("sh",["-c",`command -v ${e}`],{encoding:"utf-8"}).status===0}function pc(e={}){let t=Do.default.platform();if(t!=="darwin"&&t!=="linux")return{ok:!1,error:`Automatic install isn't supported on ${t}. Install from ${Mo}.`};let n=e.log??(()=>{}),o=t==="darwin"&&mc("brew",e.runner),r=o?"brew install ollama":"curl -fsSL https://ollama.com/install.sh | sh";return n(`Installing Ollama via ${o?"Homebrew":"the official installer"}\u2026`),e.runner?e.runner.run("sh",["-c",r]):(0,at.spawnSync)("sh",["-c",r],{stdio:"inherit"}),st({runner:e.runner}).installed?{ok:!0}:{ok:!1,error:`Ollama still not detected after install \u2014 install it manually from ${Mo}.`}}function gc(e={}){((r,s)=>e.runner?e.runner.run(r,s):(0,at.spawnSync)(r,s,{stdio:"ignore",timeout:1e4}))("sh",["-c",Do.default.platform()==="darwin"?"(brew services start ollama 2>/dev/null) || (nohup ollama serve >/dev/null 2>&1 &)":"(systemctl start ollama 2>/dev/null) || (nohup ollama serve >/dev/null 2>&1 &)"])}var fc=e=>new Promise(t=>setTimeout(t,e));async function Vs(e){let t=e.log??(()=>{}),n=e.sleepImpl??fc,o=st({runner:e.runner});if(!o.installed){if(!(e.assumeYes||(e.confirmInstall?await e.confirmInstall(" Ollama is not installed. Install it now? [Y/n] "):!1)))return{ready:!1,installed:!1,reason:"install declined"};let s=pc({runner:e.runner,log:t});if(!s.ok)return{ready:!1,installed:!1,reason:s.error};o=st({runner:e.runner})}if(o.installed&&!o.serving){t(" Starting the Ollama server\u2026"),gc({runner:e.runner});let r=e.pollAttempts??20;for(let s=0;s<r&&!o.serving;s++)await n(1e3),o=st({runner:e.runner})}return{ready:o.installed&&o.serving,installed:o.installed,reason:o.installed&&o.serving?void 0:"ollama not serving"}}var ea=m(require("os")),ta=m(require("readline"));ie();Ve();var ge=m(require("os")),Ys=require("child_process");Ie();var Ws=[1,2,4,6,8,12,16];function Ks(e){if(e>16)return"16GB+";let t=Ws[0];for(let n of Ws)e>=n&&(t=n);return`${t}GB`}var Js={run:(e,t)=>{let n=(0,Ys.spawnSync)(e,t,{encoding:"utf-8",timeout:5e3});return{status:n.status,stdout:n.stdout||""}}};function $n(){return Math.round(ge.default.totalmem()/1024/1024/1024)}function zs(e){return e==="x64"?"x86_64":e}function Qs(e,t){if(e==="darwin")return t.run("sysctl",["-n","machdep.cpu.brand_string"]).stdout.trim()||null;if(e==="linux")return t.run("sh",["-c","grep -m1 'model name' /proc/cpuinfo | cut -d: -f2"]).stdout.trim()||null;let n=ge.default.cpus();return n.length?n[0].model.trim():null}function Xs(e,t,n,o){if(e==="darwin"&&t==="arm64")return{accelerator:n?`${n} GPU`:"Apple GPU",vram_gb:null,unified_memory:!0};let r=o.run("nvidia-smi",["--query-gpu=name,memory.total","--format=csv,noheader,nounits"]);if(r.status===0&&r.stdout.trim()){let s=r.stdout.trim().split(`
|
|
138
|
-
`)
|
|
139
|
-
Local models that fit ${e.ram_gb} GB (${e.ram_tier}):`),n.forEach((l,u)=>{r(` ${l===o?"*":" "} ${u+1}) ${yc(l)}`)});let a=n.indexOf(o)+1,i=(await s(` Pick a model [${a}]: `)).trim();if(!i)return{tag:o.ollama_tag,note:e.note,model:o};let c=Number.parseInt(i,10);if(Number.isInteger(c)&&c>=1&&c<=n.length){let l=n[c-1];return{tag:l.ollama_tag,note:e.note,model:l}}return{tag:o.ollama_tag,note:e.note,model:o}}async function An(e){let t=e.ramGb??No(),n=e.accelerator===void 0?In():e.accelerator,o=await Lt(t,e.runtime,{apiBase:e.apiBase,fetchImpl:e.fetchImpl,accelerator:n});if(!o)return{tag:e.preferredTag||wt("ollama"),note:"Could not reach the model-recommendation service \u2014 using a safe default model.",ramGb:t,fellBack:!0};let r=await bc(o,{interactive:e.interactive,preferredTag:e.preferredTag,prompt:e.prompt,log:e.log});return{tag:r.tag,note:r.note,ramGb:t,fellBack:!1}}Ve();var te=fe.default.join(Mt.default.homedir(),".openclaw","openclaw.json"),Tt=te+".badgerclaw-stash",ca=fe.default.join(Mt.default.homedir(),".openclaw","npm"),wc=fe.default.join(ca,"package.json");async function na(){try{let e=await fetch(`${N}/api/v1/dashboard/versions/latest`,{headers:{Accept:"application/json"}});if(!e.ok)return{cli:null,plugin:null,openclaw:null,hermes:null};let t=await e.json(),n=t.result&&typeof t.result=="object"?t.result:t,o=r=>typeof r!="string"||!r||r==="unknown"?null:r;return{cli:o(n.cli),plugin:o(n.plugin),openclaw:o(n.supported_openclaw),hermes:o(n.supported_hermes)}}catch{return{cli:null,plugin:null,openclaw:null,hermes:null}}}function _c(){if(!L.default.existsSync(te))return null;try{let e=JSON.parse(L.default.readFileSync(te,"utf-8")),t=e.channels?.badgerclaw;return t?(delete e.channels.badgerclaw,e.plugins?.entries?.badgerclaw&&delete e.plugins.entries.badgerclaw,L.default.writeFileSync(te,JSON.stringify(e,null,2)),L.default.writeFileSync(Tt,JSON.stringify(t,null,2)),t):null}catch{return null}}function Sc(){if(!L.default.existsSync(te))return null;try{let e=JSON.parse(L.default.readFileSync(te,"utf-8"));return!e.gateway||typeof e.gateway!="object"?null:JSON.parse(JSON.stringify(e.gateway))}catch{return null}}function vc(e){if(e&&L.default.existsSync(te))try{let t=JSON.parse(L.default.readFileSync(te,"utf-8"));t.gateway={...e,...t.gateway||{}},L.default.writeFileSync(te,JSON.stringify(t,null,2))}catch(t){console.log(d.default.yellow(` \u26A0\uFE0F Could not restore gateway block: ${t.message}`)),console.log(d.default.yellow(" Run `openclaw config set gateway.mode local` manually if the gateway fails to start."))}}function kc(){if(L.default.existsSync(Tt))try{let e=JSON.parse(L.default.readFileSync(Tt,"utf-8")),t=JSON.parse(L.default.readFileSync(te,"utf-8"));t.channels=t.channels||{},t.channels.badgerclaw=e,L.default.writeFileSync(te,JSON.stringify(t,null,2)),L.default.unlinkSync(Tt)}catch(e){console.log(d.default.yellow(` \u26A0\uFE0F Could not restore config: ${e.message}`)),console.log(d.default.yellow(` Your bot credentials are backed up at: ${Tt}`))}}async function oa(e){return new Promise(t=>{let n=(0,ue.spawn)("openclaw",["plugins","install",e,"--force","--dangerously-force-unsafe-install"],{stdio:["ignore","pipe","pipe"],shell:!0}),o="",r=!1,s=!1,a=/Installed plugin:|Restart the gateway to load plugins/,i=()=>{r||s||(r=!0,setTimeout(()=>{if(!s){try{n.kill("SIGTERM")}catch{}setTimeout(()=>{if(!s)try{n.kill("SIGKILL")}catch{}},1e3)}},3e3))},c=(l,u)=>{u.write(l),o+=l.toString("utf-8"),a.test(o)&&i()};n.stdout.on("data",l=>c(l,process.stdout)),n.stderr.on("data",l=>c(l,process.stderr)),n.on("exit",l=>{s=!0,t({status:r?0:l??1})}),n.on("error",l=>{s||(s=!0,console.error(d.default.red(` openclaw spawn error: ${l.message}`)),t({status:1}))})})}function ra(e){if(!L.default.existsSync(e))return!1;let t;try{t=JSON.parse(L.default.readFileSync(e,"utf-8"))}catch{return!1}let n=!1;return t.overrides&&typeof t.overrides=="object"&&"uuid"in t.overrides&&(delete t.overrides.uuid,n=!0),t.openclaw&&Array.isArray(t.openclaw.managedOverrides)&&t.openclaw.managedOverrides.includes("uuid")&&(t.openclaw.managedOverrides=t.openclaw.managedOverrides.filter(o=>o!=="uuid"),n=!0),n?(L.default.writeFileSync(e,JSON.stringify(t,null,2)+`
|
|
140
|
-
`)
|
|
130
|
+
`}function es(){try{return(0,Z.execSync)("systemctl --user --version",{stdio:"ignore"}),!0}catch{return!1}}function ts(){if(It.default.platform()!=="linux"||!es())return!1;try{let e=Ki();Ze.default.mkdirSync(Zr,{recursive:!0}),Ze.default.mkdirSync(At.default.join(It.default.homedir(),".badgerclaw"),{recursive:!0}),Ze.default.writeFileSync(ao,Yi(e)),(0,Z.execSync)("systemctl --user daemon-reload",{stdio:"ignore"});try{(0,Z.execSync)(`systemctl --user restart ${cn}`,{stdio:"ignore"})}catch{}(0,Z.execSync)(`systemctl --user enable --now ${cn}`,{stdio:"ignore"});try{let t=(0,Z.execSync)("whoami",{encoding:"utf-8"}).trim();(0,Z.execSync)(`loginctl enable-linger ${t}`,{stdio:"ignore"})}catch{}return console.log("\x1B[2mHeartbeat daemon registered as systemd user service (auto-starts on login).\x1B[0m"),!0}catch{return!1}}function ns(){if(It.default.platform()==="linux"&&es()){try{(0,Z.execSync)(`systemctl --user disable --now ${cn}`,{stdio:"ignore"})}catch{}try{Ze.default.existsSync(ao)&&Ze.default.unlinkSync(ao),(0,Z.execSync)("systemctl --user daemon-reload",{stdio:"ignore"})}catch{}}}var io=dn.default.join(un.default.homedir(),".badgerclaw"),Et=dn.default.join(io,"heartbeat.pid"),Ji=dn.default.join(io,"heartbeat.log");function zi(e){if(!e||e.length===0)return!1;let t=s=>/(^|[\\/])node(\.exe)?$/i.test(s||""),n=t(e[0])||t(e[1]),o=e.includes("heartbeat"),r=e.some(s=>/(^|[\\/])badgerclaw($|[\\/. ])/i.test(s)||/[\\/]dist[\\/]index\.js$/.test(s));return n&&o&&r}function Qi(e){try{if(process.platform==="linux")return ee.default.readFileSync(`/proc/${e}/cmdline`).toString("utf-8").split("\0").filter(Boolean);let t=(0,De.execSync)(`ps -o args= -p ${e}`,{encoding:"utf-8"}).trim();return t.length?t.split(/\s+/).filter(Boolean):null}catch{return null}}function lo(e){if(!Number.isFinite(e)||e<=0||e===process.pid||e===process.ppid)return!1;let t=Qi(e);return t?zi(t):!1}function co(){let e="";try{e=(0,De.execSync)("pgrep -f heartbeat",{encoding:"utf-8"})}catch{return[]}return e.split(/\s+/).map(t=>parseInt(t,10)).filter(t=>Number.isFinite(t)&&t>0).filter(t=>lo(t))}function Xi(e){try{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,Math.max(0,e))}catch{}}function Zi(e){try{if(process.platform==="linux"){let r=ee.default.readFileSync(`/proc/${e}/status`,"utf-8").match(/^PPid:\s*(\d+)/m),s=r?parseInt(r[1],10):NaN;if(!Number.isFinite(s))return null;let a="";try{a=ee.default.readFileSync(`/proc/${s}/comm`,"utf-8").trim()}catch{}return{ppid:s,comm:a}}let t=parseInt((0,De.execSync)(`ps -o ppid= -p ${e}`,{encoding:"utf-8"}).trim(),10);if(!Number.isFinite(t))return null;let n="";try{n=(0,De.execSync)(`ps -o comm= -p ${t}`,{encoding:"utf-8"}).trim()}catch{}return{ppid:t,comm:n}}catch{return null}}var el=/(systemd|supervisord|launchd|runsv|runit|s6-supervise|openrc)/i;function tl(){for(let e of co()){let t=Zi(e);if(t&&el.test(t.comm))return e}return null}function nl(){try{let e=parseInt(ee.default.readFileSync(Et,"utf-8").trim(),10);if(Number.isFinite(e)&&e>0&&lo(e))return!0}catch{}return rs(),co().length>0}function rs(){try{let e=parseInt(ee.default.readFileSync(Et,"utf-8").trim(),10);if(Number.isFinite(e)&&e>0&&lo(e))return}catch{}try{ee.default.rmSync(Et,{force:!0})}catch{}}function ol(e="SIGTERM"){let t=co(),n=0;for(let a of t)try{process.kill(a,e),n++}catch{}if(n===0)return 0;let o=a=>{try{return process.kill(a,0),!0}catch{return!1}},r=Date.now()+4e3,s=t.filter(o);for(;s.length>0&&Date.now()<r;)Xi(120),s=s.filter(o);for(let a of s)try{process.kill(a,"SIGKILL")}catch{}return n}function os(){if(nl())return console.log("\x1B[2mHeartbeat already running.\x1B[0m"),!0;try{ee.default.mkdirSync(io,{recursive:!0});let e=ee.default.openSync(Ji,"a"),t=(0,De.spawn)(process.execPath,[process.argv[1],"heartbeat"],{detached:!0,stdio:["ignore",e,e]});if(t.pid==null)return console.log("\x1B[2mNote: could not start the heartbeat. Run `badgerclaw heartbeat` manually.\x1B[0m"),!1;try{ee.default.writeFileSync(Et,String(t.pid))}catch{}return t.unref(),console.log("\x1B[2mHeartbeat started in the background (no service manager on this host; it will not auto-restart after a reboot \u2014 re-run `badgerclaw setup --hermes` or `badgerclaw heartbeat` if the host restarts).\x1B[0m"),!0}catch{return console.log("\x1B[2mNote: could not start the heartbeat. Run `badgerclaw heartbeat` manually.\x1B[0m"),!1}}function uo(){let e=un.default.platform();if(e==="darwin"){Qr();return}if(e==="linux"){if(ts())return;os();return}os()}function rl(){let e=un.default.platform();e==="darwin"?Xr():e==="linux"&&ns(),ol();try{ee.default.rmSync(Et,{force:!0})}catch{}}function ss(e){let t=e.log||(()=>{}),n=tl();if(n!==null){rs(),t(`heartbeat already supervised (pid ${n}) \u2014 left in place; cleared any stale pid file.`);return}rl(),t("removed stale heartbeat hooks from previous installs."),e.loggedIn?(uo(),t("reinstalled a fresh heartbeat daemon for this host.")):t("run `badgerclaw login` to start the heartbeat for this host.")}var Ce=m(require("fs")),fo=m(require("os")),pn=m(require("path")),ce=require("child_process"),K=m(require("chalk"));wt();Re();var is=pn.default.join(fo.default.homedir(),".openclaw","openclaw.json");function ll(){return process.platform!=="linux"?!1:!!(Ce.default.existsSync("/.dockerenv")||(0,ce.spawnSync)("which",["systemctl"],{encoding:"utf-8"}).status!==0)}function ls(e=!1){let t=null;try{Ce.default.existsSync(is)&&(t=JSON.parse(Ce.default.readFileSync(is,"utf-8"))?.gateway?.mode??null)}catch{}if(typeof t=="string"&&t.length>0)return!0;e||console.log(K.default.dim(" Setting gateway.mode=local..."));try{return Oe(()=>((0,ce.execSync)("openclaw config set gateway.mode local",{encoding:"utf-8",timeout:1e4,stdio:"pipe"}),!0))}catch(n){return e||(console.log(K.default.yellow(` \u26A0\uFE0F Could not set gateway.mode automatically: ${n.message}`)),console.log(K.default.yellow(" Run `openclaw config set gateway.mode local` manually."))),!1}}async function gn(e={}){let t=e.verbose!==!1,n=i=>{t&&console.log(i)};if(ll())return ul(t);n(K.default.dim(" Installing OpenClaw gateway service (if not present)...")),(0,ce.spawnSync)("openclaw",["gateway","install"],{stdio:t?"inherit":"pipe",shell:!0}).status!==0&&n(K.default.yellow(" \u26A0\uFE0F `openclaw gateway install` returned non-zero \u2014 continuing.")),ls(!t),n(K.default.dim(" Starting OpenClaw gateway..."));let r=(0,ce.spawnSync)("openclaw",["gateway","start"],{stdio:t?"inherit":"pipe",shell:!0}),s="started";if(r.status!==0&&(r=(0,ce.spawnSync)("openclaw",["gateway","restart"],{stdio:t?"inherit":"pipe",shell:!0}),s="restarted"),r.status!==0)return{ok:!1,error:"gateway start/restart returned non-zero exit"};n(K.default.dim(" Waiting for the gateway to start answering..."));let a=await cl(2e4);return a||n(K.default.yellow(" \u26A0\uFE0F Gateway start issued but it is not answering yet \u2014 it may still be booting.")),{ok:!0,state:s,ready:a}}async function cl(e,t=po,n=500){let o=Date.now()+e;for(;;){if(await t())return!0;if(Date.now()>=o)return!1;await new Promise(r=>setTimeout(r,n))}}async function ul(e){let t=l=>{e&&console.log(l)};t(K.default.dim(" Container mode (no systemd) \u2014 running gateway in foreground via nohup.")),ls(!e);let n=pn.default.join(fo.default.homedir(),".openclaw","logs");try{Ce.default.mkdirSync(n,{recursive:!0})}catch{}let o=pn.default.join(n,"gateway-foreground.log"),r,s;try{r=Ce.default.openSync(o,"a"),s=Ce.default.openSync(o,"a")}catch(l){return{ok:!1,error:`cannot open gateway log at ${o}: ${l.message}`}}let a=(0,ce.spawn)("openclaw",["gateway"],{detached:!0,stdio:["ignore",r,s]});if(a.pid==null)return{ok:!1,error:"failed to spawn `openclaw gateway`"};a.unref(),t(K.default.dim(` Gateway PID: ${a.pid}, log: ${o}`));let i=Date.now()+2e4,c=!1;for(;Date.now()<i;){try{let l=Ce.default.readFileSync(o,"utf-8");if(l.includes("[gateway] ready")){c=!0;break}if(/EADDRINUSE|FATAL|Error:|gateway start blocked/.test(l))return{ok:!1,error:`gateway aborted on startup \u2014 see ${o}`}}catch{}await new Promise(l=>setTimeout(l,200))}return c?{ok:!0,state:"started",ready:!0}:{ok:!1,error:`gateway did not report ready within 20s \u2014 see ${o}`}}function fn(){if(process.platform!=="linux"||process.getuid?.()!==0)return;let e=(0,ce.spawnSync)("loginctl",["show-user","root","--property=Linger"],{encoding:"utf-8"});e.status===0&&/Linger=no/.test(e.stdout||"")&&(console.log(K.default.yellow(`
|
|
131
|
+
\u26A0\uFE0F systemd lingering is OFF for root. The gateway will stop on SSH disconnect.`)),console.log(K.default.dim(" Enable persistence: loginctl enable-linger root")))}Ke();var Gl=2e3,ql=12e4,ks=new ws.Command("login").description("Log in to BadgerClaw via browser").action(async()=>{let e=or(),t=rr(e),n=`${cr}/cli-auth?code=${t}`;console.log(z.default.yellow("Opening browser for authentication...")),console.log(z.default.dim(`If the browser doesn't open, visit: ${n}`)),await(0,vs.default)(n);let o=(0,_s.default)("Waiting for authentication...").start(),r=gr(),s=Date.now();for(;Date.now()-s<ql;){try{let a=await r.post(`/api/v1/openclaw/cli/auth/poll/${t}`,{code_verifier:e,code_challenge:t});if(a.status===429){await new Promise(i=>setTimeout(i,5e3));continue}if(a.data?.access_token){let{access_token:i,user_id:c,expires_at:l,refresh_token:u,email:p}=a.data,_=ft(),R=`openclaw-${Po.default.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${_}`,f=R;try{let{version:g}=_e(),y=await r.post("/api/v1/openclaw/register",{instance_id:R,label:Po.default.hostname(),version:g,machine_fingerprint:_},{headers:{Authorization:`Bearer ${i}`}}),v=y.data?.result||y.data;v&&typeof v.instance_id=="string"&&v.instance_id.length>0&&(f=v.instance_id)}catch(g){let y=g?.response?.status;if(y===403||y===409){let v=g?.response?.data,j=Array.isArray(v?.errors)&&typeof v.errors[0]=="string"?v.errors[0]:null,oe=v?.detail,re=typeof oe=="string"?oe:oe?.error==="downgrade_selection_required"?"Your account has an unresolved plan-change request. Open the BadgerClaw app to finish the downgrade selection, then try again.":null,x=j||re||`Login refused (status ${y}). Open the BadgerClaw app to check your subscription state.`;o.fail(z.default.red(x)),process.exit(1)}}if(Vt({access_token:i,user_id:c,instance_id:f,expires_at:l,refresh_token:u,email:p}),o.succeed(z.default.green(`Logged in as ${Wt(c)}`)),Y()!==null){let g=await gn({verbose:!0});g.ok?g.ready===!1&&console.log(z.default.dim(" Gateway is still coming up \u2014 it should be online shortly.")):console.log(z.default.yellow("\n \u26A0\uFE0F Logged in, but gateway failed to start. Re-run `badgerclaw setup` to repair."))}else console.log(z.default.dim(" Hermes-only host \u2014 skipping OpenClaw gateway bring-up."));let b=await xt(!0);b>0&&console.log(z.default.green(`\u2705 ${b} bot(s) automatically paired to OpenClaw.`)),uo(),fn();try{let{pushHeartbeat:g}=await Promise.resolve().then(()=>(tt(),Eo));await g(),console.log(z.default.dim(" Capabilities pushed \u2014 the machine should show online shortly."))}catch(g){console.log(z.default.dim(` Capability push will retry via the heartbeat (${g?.message||g}).`))}return}}catch{}await new Promise(a=>setTimeout(a,Gl))}o.fail(z.default.red("Authentication timed out. Please try again.")),process.exit(1)});var Rs=require("commander"),Lt=m(require("chalk")),kn=m(require("fs")),nt=m(require("os")),Mt=m(require("path")),Tt=require("child_process");H();Ee();function Vl(){let e=process.platform;if(e==="darwin"){let t=Mt.default.join(nt.default.homedir(),"Library","LaunchAgents","ai.badgerclaw.watch.plist");(0,Tt.spawnSync)("launchctl",["unload",t],{stdio:"ignore"});try{kn.default.unlinkSync(t)}catch{}}else if(e==="linux"){(0,Tt.spawnSync)("systemctl",["--user","disable","--now","badgerclaw-watch.service"],{stdio:"ignore"});let t=Mt.default.join(nt.default.homedir(),".config","systemd","user","badgerclaw-watch.service");try{kn.default.unlinkSync(t)}catch{}(0,Tt.spawnSync)("systemctl",["--user","daemon-reload"],{stdio:"ignore"})}(0,Tt.spawnSync)("pkill",["-f","badgerclaw heartbeat"],{stdio:"ignore"})}function Ss(e){try{return kn.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{return!1}}var Cs=new Rs.Command("logout").description("Disconnect this machine, log out of BadgerClaw, and wipe local state").option("--keep-state","Only clear auth.json (legacy behaviour); preserve bot-mapping, claude-sessions, heartbeat logs, and plugin quarantine ledger").action(async e=>{let t=$();if(t)try{let a=bt(t.access_token),{version:i}=_e();await a.post("/api/v1/openclaw/register",{instance_id:t.instance_id,label:nt.default.hostname(),version:i,online:!1})}catch{}if(Vl(),e.keepState){gt(),console.log(Lt.default.green("Logged out \u2014 auth cleared. (state preserved with --keep-state)"));return}let n=Mt.default.join(nt.default.homedir(),".badgerclaw"),o=Ss(n),r=Mt.default.join(nt.default.homedir(),".openclaw","badgerclaw"),s=Ss(r);o||s?(console.log(Lt.default.green("Logged out \u2014 local state cleared.")),console.log(Lt.default.dim(` Removed: ${n}${s?`, ${r}`:""}`))):console.log(Lt.default.green("Logged out."))});var xs=require("commander"),Oo=m(require("chalk"));H();var $s=new xs.Command("status").description("Show connected instance info").action(async()=>{let e=$();(!e||!Kt())&&(console.log(Oo.default.red("Not logged in. Run `badgerclaw login` to authenticate.")),process.exit(1)),console.log(Oo.default.green("Authenticated")),console.log(` User: ${Wt(e.user_id)}`),console.log(` Instance: ${e.instance_id}`),console.log(` Expires: ${new Date(e.expires_at).toLocaleDateString()}`)});var Be=require("commander"),w=m(require("chalk")),Dt=m(require("ora")),Cn=m(require("fs")),Ls=m(require("os")),Ts=m(require("path")),Ms=require("child_process");H();Ee();var ot=m(require("fs")),Is=m(require("os")),As=m(require("path")),Es=require("child_process");wt();function Sn(e){let t=e.split(":")[0].replace(/^@/,"").replace(/_bot$/,""),n=As.join(Is.homedir(),".openclaw","openclaw.json");return ot.existsSync(n)?{changed:Oe(()=>{let r=JSON.parse(ot.readFileSync(n,"utf-8")),s=!1;if(r.channels?.badgerclaw?.accounts?.[t]&&(delete r.channels.badgerclaw.accounts[t],s=!0),r.agents?.list){let a=r.agents.list.length;r.agents.list=r.agents.list.filter(i=>i.id!==t),r.agents.list.length!==a&&(s=!0)}return s&&ot.writeFileSync(n,JSON.stringify(r,null,2)),s})}:{changed:!1}}function Rn(){try{return(0,Es.execSync)("openclaw gateway restart",{stdio:"ignore"}),!0}catch{return!1}}Le();function Ps(e){return e.hermes?"hermes":"openclaw"}function Os(e){return e.openclaw.installed?"openclaw":e.hermes.installed?"hermes":"openclaw"}Te();function To(){Kt()||(console.log(w.default.red("Not logged in. Run `badgerclaw login` to authenticate.")),process.exit(1))}function Kl(e){return/^[a-z0-9_]{4,20}$/.test(e)}function Ds(e){return e.replace(/_bot$/,"")}var Lo=["openclaw","hermes"],Wl=new Be.Command("create").description("Create a new bot").argument("<name>","Bot name (4-20 chars, lowercase alphanumeric + underscores)").option("-r, --runtime <runtime>",`Bot runtime: ${Lo.join(" | ")}. Default is selected from this host's capabilities (Hermes preferred when available, OpenClaw otherwise \u2014 see \`badgerclaw setup\` output). Hermes requires HERMES_RUNTIME_ENABLED on the backend and a host that advertises Hermes capability via badgerclaw setup. Operator-side LLM credentials (BADGERCLAW_LLM_API_KEY or GOOGLE_API_KEY etc.) must be set at pair time, not at create time.`).action(async(e,t)=>{To(),Kl(e)||(console.log(w.default.red("Invalid bot name. Must be 4-20 characters, lowercase alphanumeric and underscores only.")),process.exit(1));let n=t.runtime??Os(ke());Lo.includes(n)||(console.log(w.default.red(`Invalid --runtime "${t.runtime}". Must be one of: ${Lo.join(", ")}.`)),process.exit(1));let o=(0,Dt.default)(`Creating ${n} bot "${e}"...`).start();try{let r=B();n==="hermes"?await r.post("/api/v1/bots",{bot_name:e,bot_username:e,runtime:"hermes"}):await r.post("/api/v1/openclaw/bots",{username:e}),o.succeed(w.default.green(`Bot "${e}" (${n}) created successfully!`)),n==="hermes"&&console.log(w.default.dim(" Next: generate a pair code from Adminweb (or POST /api/v1/pairing/create),\n then run `badgerclaw autopair` on a Hermes-capable host."))}catch(r){let s=r.response?.data?.errors?.[0]||r.message;o.fail(w.default.red(`Failed to create bot: ${s}`)),process.exit(1)}}),Yl=new Be.Command("list").description("List your bots").action(async()=>{To();let e=(0,Dt.default)("Fetching bots...").start();try{let o=(await B().get("/api/v1/openclaw/bots")).data?.bots||[];if(e.stop(),o.length===0){console.log(w.default.yellow("No bots found. Create one with `badgerclaw bot create <name>`."));return}console.log(w.default.green(`Your bots (${o.length}):
|
|
132
|
+
`));for(let r of o){let s=Ds(r.username||r.name),a=r.active===!1?w.default.dim("deactivated"):r.openclaw_connected?w.default.green("connected"):w.default.gray("disconnected");console.log(` ${w.default.bold(s)} ${a}`)}}catch(t){let n=t.response?.data?.errors?.[0]||t.message;e.fail(w.default.red(`Failed to list bots: ${n}`)),process.exit(1)}}),Jl=new Be.Command("delete").description("Deactivate a bot").argument("<name>","Bot name to deactivate").action(async e=>{To();let t=(0,Dt.default)(`Deactivating bot "${e}"...`).start();try{await B().delete(`/api/v1/openclaw/bots/${e}`),t.succeed(w.default.green(`Bot "${e}" deactivated.`));let{changed:o}=Sn(e);o&&(console.log(w.default.dim(" Removed from openclaw.json")),Rn()&&console.log(w.default.dim(" Gateway restarted")))}catch(n){let o=n.response?.data?.errors?.[0]||n.message;t.fail(w.default.red(`Failed to deactivate bot: ${o}`)),process.exit(1)}});function zl(e){let t=e.trim();return t.startsWith("@")&&(t=t.slice(1)),t.includes(":")&&(t=t.split(":")[0]),Ds(t)}function Ql(e){let t=e.replace(/[^a-zA-Z0-9_.-]/g,"_")||"default";return Ts.default.join(Ls.default.homedir(),".openclaw","badgerclaw","quarantine",`${t}.json`)}var Xl=new Be.Command("refresh").description("Recover a bot stuck in quarantine \u2014 clears the quarantine ledger and restarts the gateway so the plugin's refresh chain runs from scratch").argument("<name>",'Bot name (e.g. "claude", "claude_bot", or full Matrix ID)').action(async e=>{let t=zl(e);t||(console.log(w.default.red(`Couldn't parse bot name "${e}".`)),process.exit(1));let n=Ql(t),o=!1;if(Cn.default.existsSync(n))try{let r=JSON.parse(Cn.default.readFileSync(n,"utf-8")),s=r.failureCount??"?",a=r.quarantinedAt??"?";console.log(w.default.dim(` Quarantine for "${t}" \u2014 ${s} failures, since ${a}`)),Cn.default.unlinkSync(n),o=!0}catch(r){console.log(w.default.red(`Failed to clear quarantine ledger at ${n}: ${r.message}`)),process.exit(1)}console.log(o?w.default.green(`\u2705 Cleared quarantine for "${t}"`):w.default.dim(` No quarantine file for "${t}" \u2014 already healthy or never quarantined.`)),console.log(w.default.dim(" Restarting OpenClaw gateway so the refresh chain runs..."));try{(0,Ms.execSync)("openclaw gateway restart",{stdio:"inherit"})}catch(r){console.log(w.default.yellow(` Gateway restart command failed: ${r.message}`)),console.log(w.default.yellow(" Run `openclaw gateway restart` manually.")),process.exit(1)}console.log(w.default.green(`
|
|
133
|
+
\u2705 Refresh triggered. Watch the gateway logs for token refresh progress:`)),console.log(w.default.dim(" tail -f ~/.openclaw/logs/*.log")),console.log(w.default.dim(" If the bot fails to recover, the plugin's 4 refresh strategies all hit")),console.log(w.default.dim(" dead ends. Verify SYNAPSE_REGISTRATION_SHARED_SECRET is set on this gateway,")),console.log(w.default.dim(" or re-pair the bot from the BadgerClaw iOS app."))}),Zl=new Be.Command("reprovision").description("Re-render every local Hermes bot's .env + config.yaml with this host's currently-configured LLM (e.g. after `badgerclaw setup --local-llm` picks a new model) and restart its container. No delete/re-create \u2014 Matrix credentials and the cross-signing recovery key are preserved.").action(async()=>{let e=Ye();e||(console.log(w.default.yellow("No LLM configured for this host. Pick one first with `badgerclaw setup --hermes --local-llm` (or set BADGERCLAW_LLM_* / a provider key like GOOGLE_API_KEY), then re-run this command.")),process.exit(1));let t=Object.keys(me().hermes_bots);if(t.length===0){console.log(w.default.dim("No Hermes bots on this host (state.json is empty) \u2014 nothing to re-provision."));return}let n=`${e.provider}${e.model?` (${e.model})`:""}`,o=(0,Dt.default)(`Re-provisioning ${t.length} Hermes bot(s) \u2192 ${n}...`).start(),r;try{r=an(e)}catch(a){o.fail(w.default.red(`Re-provision failed: ${a.message}`)),process.exit(1)}o.stop();let s=0;for(let a of r)a.skipped?console.log(` ${w.default.yellow("skipped")} ${a.bot_id} \u2014 ${a.skipped}`):a.restarted?(s++,console.log(` ${w.default.green("restarted")} ${a.bot_id}`)):console.log(` ${w.default.dim("rendered")} ${a.bot_id} (not restarted)`);console.log(s>0?w.default.green(`
|
|
134
|
+
\u2705 ${s} bot(s) now running on ${n}.`):w.default.yellow(`
|
|
135
|
+
No bots were restarted \u2014 see the per-bot notes above.`))}),Ns=new Be.Command("bot").description("Manage bots").addCommand(Wl).addCommand(Yl).addCommand(Jl).addCommand(Xl).addCommand(Zl);$t();var Bs=require("commander"),S=m(require("chalk"));H();ie();$t();Re();tt();Io();Ee();Ae();var js=3e3,ec=6e4,tc=2700*1e3,Us=new Bs.Command("watch").description("Watch for bot pair events in real-time (no polling)").action(async()=>{$()||(console.log(S.default.yellow("Not logged in.")),process.exit(1)),await xt(!1),console.log(S.default.green("\u{1F534} Listening for pair events... (Ctrl+C to stop)"));let t=require("eventsource"),n=js,o=null,r=null,s=()=>{let a=$();a||(console.log(S.default.red(" Auth missing \u2014 run `badgerclaw login` and re-start `badgerclaw watch`.")),process.exit(1));let i=new t(`${N}/api/v1/openclaw/events`,{headers:{Authorization:`Bearer ${a.access_token}`}});r=i,i.onopen=()=>{n=js,console.log(S.default.dim(" SSE connected \u2014 listening for events")),o&&clearTimeout(o),o=setTimeout(()=>{console.log(S.default.dim(" SSE proactive reconnect \u2014 refreshing token before expiry")),i.close(),s()},tc)},i.onerror=()=>{i.close(),o&&(clearTimeout(o),o=null),console.log(S.default.dim(` SSE disconnected \u2014 reconnecting in ${Math.floor(n/1e3)}s...`)),setTimeout(s,n),n=Math.min(n*1.5,ec)},i.onmessage=async c=>{try{let l=JSON.parse(c.data);if(l.type==="pair"){let u=l.target_instance_id;if(u&&u!==ae()){console.log(S.default.dim(`
|
|
136
|
+
\u{1F4F1} Pair event for ${l.bot_name} targets ${u} \u2014 not us, skipping`));return}console.log(S.default.cyan(`
|
|
137
|
+
\u{1F4F1} Pair event received: ${l.bot_name}`)),await Ct(l.pair_code,l.bot_name,l.bot_user_id,!1);try{let{execSync:p}=await import("child_process");p("openclaw gateway restart",{stdio:"ignore"}),console.log(S.default.green(" \u2705 Gateway restarted \u2014 bot is live!"))}catch{console.log(S.default.dim(" Gateway restart failed \u2014 run: openclaw gateway restart"))}}else if(l.type==="gateway-restart"){console.log(S.default.cyan(`
|
|
138
|
+
\u{1F504} Remote gateway-restart command received`));let u=await et();u.success?console.log(S.default.green(` \u2705 Gateway restarted: ${u.message}`)):console.log(S.default.red(` \u274C Gateway restart failed: ${u.message}`));try{await U(),console.log(S.default.dim(" Heartbeat pushed with updated status."))}catch{console.log(S.default.dim(" Could not push heartbeat."))}}else if((l.type==="bot.delete"||l.type==="bot.disconnect")&&l.bot_user_id){let u=l.bot_user_id,p=l.type==="bot.delete"?"deleted":"disconnected",_=l.type==="bot.delete"?"\u{1F5D1}\uFE0F ":"\u{1F50C}";console.log(S.default.yellow(`
|
|
139
|
+
${_} Bot ${p}: ${u} \u2014 removing from OpenClaw config...`));try{let{changed:R}=Sn(u);R?(console.log(S.default.green(" \u2705 Removed from openclaw.json")),Rn()?console.log(S.default.green(" \u2705 Gateway restarted")):console.log(S.default.dim(" Gateway restart failed \u2014 restart manually"))):console.log(S.default.dim(" Account not found in openclaw.json \u2014 nothing to remove"))}catch(R){console.log(S.default.red(` Failed to update openclaw.json: ${R}`))}}else if(l.type==="claude_code.start"&&l.bot_id&&l.room_id){console.log(S.default.cyan(`
|
|
140
|
+
\u{1F916} Claude Code START received: bot=${l.bot_id} room=${l.room_id}`));try{let u=await ge({id:l.command_id||`cc-start-${Date.now()}`,command_type:"start_claude_code",payload:{bot_id:l.bot_id,room_id:l.room_id,session_id:l.session_id}});if(console.log(u.success?S.default.green(` \u2705 ${u.message}`):S.default.red(` \u274C ${u.message}`)),l.command_id)try{await B().post(`/api/v1/dashboard/commands/${l.command_id}/result`,{status:u.success?"success":"failed",result:u.message})}catch{}try{await U()}catch{}}catch(u){console.log(S.default.red(` Claude Code start error: ${u.message}`))}}else if(l.type==="claude_code.stop"&&l.bot_id&&l.room_id){console.log(S.default.cyan(`
|
|
141
|
+
\u{1F916} Claude Code STOP received: bot=${l.bot_id} room=${l.room_id}`));try{let u=await ge({id:l.command_id||`cc-stop-${Date.now()}`,command_type:"stop_claude_code",payload:{bot_id:l.bot_id,room_id:l.room_id,session_id:l.session_id}});if(console.log(u.success?S.default.green(` \u2705 ${u.message}`):S.default.red(` \u274C ${u.message}`)),l.command_id)try{await B().post(`/api/v1/dashboard/commands/${l.command_id}/result`,{status:u.success?"success":"failed",result:u.message})}catch{}try{await U()}catch{}}catch(u){console.log(S.default.red(` Claude Code stop error: ${u.message}`))}}else if(l.type==="command.execute"&&l.command_id){console.log(S.default.cyan(`
|
|
142
|
+
\u26A1 SSE command received: ${l.command_type} (${l.command_id})`));try{let u=B();await u.post(`/api/v1/dashboard/commands/${l.command_id}/ack`);let p=await ge({id:l.command_id,command_type:l.command_type,payload:l.payload});console.log(p.success?S.default.green(` \u2705 ${p.message}`):S.default.red(` \u274C ${p.message}`)),await u.post(`/api/v1/dashboard/commands/${l.command_id}/result`,{status:p.success?"success":"failed",result:p.message,new_version:p.newVersion||null});try{await U()}catch{}l.command_type==="update_cli"&&p.success&&(console.log(S.default.cyan(" CLI updated \u2014 restarting in 2s...")),setTimeout(()=>process.exit(0),2e3))}catch(u){console.log(S.default.red(` Command ${l.command_id} error: ${u.message}`))}}}catch{}}};s(),await new Promise(()=>{})});var fa=require("commander"),d=m(require("chalk")),ue=require("child_process"),L=m(require("fs")),Ut=m(require("os")),he=m(require("path")),ha=m(require("readline"));ie();Ke();var Nt=m(require("fs")),rt=m(require("path")),xn=require("child_process"),nc=["request","request-promise"];function oc(e){let t=e.match(/^(\s*)(?:"([^"]+)"|'([^']+)'|([^:#\s][^:]*?))\s*:/);return t?{indent:t[1].length,key:(t[2]??t[3]??t[4]??"").trim()}:null}function rc(e){let t=e.split(`
|
|
143
|
+
`),n=[],o=!1,r=-1,s=!1,a=new Set(nc);for(let i of t){let c=oc(i);if(!o){c&&c.indent===0&&c.key==="overrides"&&(o=!0,r=c.indent),n.push(i);continue}if(c&&c.indent<=r){o=!1,n.push(i);continue}if(c&&c.indent>r&&a.has(c.key)){s=!0;continue}n.push(i)}return s?n.join(`
|
|
144
|
+
`):null}function sc(){let e=[],t=(0,xn.spawnSync)("npm",["root","-g"],{encoding:"utf-8",shell:!0});t.status===0&&t.stdout?.trim()&&e.push(rt.default.join(t.stdout.trim(),"openclaw","pnpm-workspace.yaml"));let n=(0,xn.spawnSync)("npm",["prefix","-g"],{encoding:"utf-8",shell:!0});n.status===0&&n.stdout?.trim()&&e.push(rt.default.join(n.stdout.trim(),"lib","node_modules","openclaw","pnpm-workspace.yaml"));let o=(0,xn.spawnSync)("which",["openclaw"],{encoding:"utf-8"});if(o.status===0&&o.stdout?.trim()){let r=rt.default.dirname(rt.default.dirname(o.stdout.trim()));e.push(rt.default.join(r,"lib","node_modules","openclaw","pnpm-workspace.yaml"))}for(let r of e)try{if(Nt.default.existsSync(r))return r}catch{}return null}function Hs(){let e=sc();if(!e)return null;let t;try{t=Nt.default.readFileSync(e,"utf-8")}catch{return null}let n=rc(t);if(n===null)return null;try{Nt.default.writeFileSync(e,n)}catch{return null}return{yamlPath:e,original:t}}function Fs(e){if(!e)return!1;try{return Nt.default.writeFileSync(e.yamlPath,e.original),!0}catch{return!1}}Le();var at=require("child_process"),qs=m(require("fs")),D=m(require("chalk")),W={alreadyUp:!1,install:null,start:null,requiresRoot:!1,networkHostRequired:!1,guidance:null},Gs='On this distro the bare "docker" package is the podman shim (no dockerd). Install Docker Engine from the official docker-ce repository (https://docs.docker.com/engine/install/), then re-run `badgerclaw setup --hermes`.';function ac(e){return e.daemonUp?{...W,alreadyUp:!0}:e.host==="darwin"?e.hasDockerApp?{...W,start:"docker-app"}:e.hasColima?{...W,start:"colima"}:e.binaryPresent?{...W,guidance:"A `docker` CLI is present but no engine we can start (Docker.app / colima) was found and the daemon is not answering. Start your Docker daemon (or `colima start`), then re-run `badgerclaw setup --hermes`."}:e.hasBrew?{...W,install:{tool:"colima",via:"brew"},start:"colima"}:{...W,guidance:"No container engine found and Homebrew is absent. Install Docker Desktop (https://www.docker.com/products/docker-desktop) or Homebrew (https://brew.sh) then colima, and re-run `badgerclaw setup --hermes`."}:e.host==="linux-systemd"?e.binaryPresent?{...W,start:"systemctl",requiresRoot:!0}:e.pkgManager==="apt"?{...W,install:{tool:"docker",via:"apt"},start:"systemctl",requiresRoot:!0}:{...W,guidance:e.pkgManager?Gs:"Docker is not installed and no supported package manager (apt) was found. Install Docker, then re-run."}:e.host==="linux-nosystemd"?e.binaryPresent?{...W,start:"dockerd-restricted",requiresRoot:!0,networkHostRequired:!0}:e.pkgManager==="apt"?{...W,install:{tool:"docker",via:"apt"},start:"dockerd-restricted",requiresRoot:!0,networkHostRequired:!0}:{...W,guidance:e.pkgManager?Gs:"Docker is not installed and no supported package manager (apt) was found. Install Docker (e.g. `apt-get install -y docker.io`), then re-run."}:{...W,guidance:`Automatic Docker setup isn't supported on this platform (${process.platform}). Install + start Docker manually, then re-run.`}}function Vs(e,t,n=8e3){return(0,at.spawnSync)(e,t,{encoding:"utf-8",timeout:n})}function Mo(){let e=Vs("docker",["version","--format","{{.Server.Version}}"],4e3);return e.status===0&&!!(e.stdout||"").trim()}function ic(){return Vs("docker",["--version"],4e3).status===0}function st(e){return(0,at.spawnSync)("sh",["-c",`command -v ${e}`],{encoding:"utf-8"}).status===0}function lc(){return st("apt-get")?"apt":st("dnf")?"dnf":st("yum")?"yum":null}function cc(){let e=(0,at.spawnSync)("systemctl",["is-system-running"],{encoding:"utf-8",timeout:4e3});if(e.error)return!1;let t=`${e.stdout||""}${e.stderr||""}`.toLowerCase();return!/failed to connect to bus|not been booted with systemd|no such file/.test(t)}function uc(){return process.platform==="darwin"?"darwin":process.platform==="linux"?cc()?"linux-systemd":"linux-nosystemd":"unsupported"}function dc(){return typeof process.getuid=="function"?process.getuid()===0:!1}function xe(e,t){return t(D.default.dim(` $ ${e}`)),(0,at.spawnSync)("sh",["-c",e],{stdio:"inherit"}).status===0}function mc(e,t,n){let o=t?"sudo ":"";switch(e.via){case"brew":return n(D.default.dim(" Installing colima + docker CLI via Homebrew...")),xe("brew install colima docker",n);case"apt":return n(D.default.dim(" Installing Docker via apt...")),xe(`${o}apt-get update`,n),xe(`${o}apt-get install -y docker.io`,n);default:return!1}}function pc(e,t,n){let o=t?"sudo ":"";switch(e){case"docker-app":return xe("open -a Docker",n);case"colima":return xe("colima start",n);case"systemctl":return xe(`${o}systemctl enable --now docker`,n);case"dockerd-restricted":{if(!st("dockerd"))return n(D.default.red(" \u274C `dockerd` is not installed (the docker CLI alone is not enough on a no-systemd host).")),!1;if(t&&!xe("sudo -v",n))return n(D.default.red(" \u274C root is required to start dockerd here, but sudo is unavailable.")),!1;xe(`${o}sh -c 'mount -t cgroup2 none /sys/fs/cgroup 2>/dev/null || true'`,n);let r="/tmp/badgerclaw-dockerd.log";n(D.default.dim(` Starting dockerd (restricted: --bridge=none --iptables=false) \u2192 ${r}`));let s=`nohup dockerd --storage-driver=overlay2 --iptables=false --bridge=none > ${r} 2>&1 &`,a=t?`sudo sh -c ${JSON.stringify(s)}`:`sh -c ${JSON.stringify(s)}`;return(0,at.spawnSync)("sh",["-c",a],{stdio:"ignore"}).status===0}}}async function gc(e,t,n){let o=Date.now()+t;for(;;){if(e())return!0;if(Date.now()>=o)return!1;await new Promise(r=>setTimeout(r,n))}}async function Ks(e={}){let t=e.log??(a=>console.log(a));if(process.env.BADGERCLAW_NO_AUTO_DEPS==="1"){let a=Mo();return a||(t(D.default.yellow(" BADGERCLAW_NO_AUTO_DEPS=1 \u2014 skipping automatic Docker setup.")),t(D.default.dim(" Start the Docker daemon yourself, then re-run `badgerclaw setup --hermes`."))),{ok:a,daemonUp:a,networkHostRequired:!1}}let n={daemonUp:Mo(),binaryPresent:ic(),host:uc(),hasBrew:st("brew"),hasDockerApp:qs.default.existsSync("/Applications/Docker.app"),hasColima:st("colima"),pkgManager:lc(),isRoot:dc()},o=ac(n);if(o.alreadyUp)return{ok:!0,daemonUp:!0,networkHostRequired:!1};if(o.guidance)return t(D.default.red(` \u274C ${o.guidance}`)),{ok:!1,daemonUp:!1,networkHostRequired:o.networkHostRequired};let r=o.requiresRoot&&!n.isRoot;return r&&t(D.default.dim(" (some steps need root \u2014 using sudo; you may be prompted for your password)")),o.install&&!mc(o.install,r,t)?(t(D.default.red(" \u274C Docker install failed (see output above). Install it manually, then re-run.")),{ok:!1,daemonUp:!1,networkHostRequired:o.networkHostRequired}):pc(o.start,r,t)?(t(D.default.dim(" Waiting for the Docker daemon to answer...")),await gc(Mo,12e4,1500)?(t(D.default.green(" \u2705 Docker daemon is up.")),{ok:!0,daemonUp:!0,networkHostRequired:o.networkHostRequired}):(t(D.default.red(" \u274C Docker did not become ready in time.")),o.start==="dockerd-restricted"?t(D.default.dim(" Check /tmp/badgerclaw-dockerd.log for the daemon output.")):o.start==="docker-app"&&t(D.default.dim(" Docker Desktop can take a minute to boot \u2014 re-run once its menu-bar icon is steady.")),{ok:!1,daemonUp:!1,networkHostRequired:o.networkHostRequired})):(t(D.default.red(" \u274C Could not start Docker (see output above). Resolve it, then re-run.")),{ok:!1,daemonUp:!1,networkHostRequired:o.networkHostRequired})}function Ws(e){let t=e.minMajor??18;if(!Number.isFinite(e.nodeMajor)||e.nodeMajor<t){let n=Number.isFinite(e.nodeMajor)?`Node v${e.nodeMajor}`:"an unknown Node version";return`badgerclaw requires Node.js >= ${t} (this is ${n}). Upgrade Node \u2014 nodesource (https://github.com/nodesource/distributions) or nvm (\`nvm install ${t}\`) \u2014 then re-run \`badgerclaw setup --hermes\`.`}return e.npmPresent?null:`npm was not found on PATH. badgerclaw is installed and updated via npm \u2014 install Node.js ${t}+ (which bundles npm) and re-run \`badgerclaw setup --hermes\`.`}function Ys(e=process.versions.node){return parseInt(String(e).split(".")[0],10)}var Do=require("child_process"),$n=m(require("fs")),Js=m(require("os")),In=m(require("path"));Le();Te();function fc(){let e=zs();for(let t of e)if($n.default.existsSync(t))return t;return null}function zs(){return[In.default.resolve(__dirname,"..","assets","hermes-agent-bc.Dockerfile"),In.default.resolve(__dirname,"..","..","assets","hermes-agent-bc.Dockerfile")]}var hc="nousresearch/hermes-agent";function yc(e){let t=(process.env.HERMES_UPSTREAM||e?.upstream||"").trim();if(!t)return;if(t.includes("/"))return t;let n=t.includes(":")?t.split(":").pop():t;return`${hc}:${n}`}var bc={run:e=>{let t=(0,Do.spawnSync)("docker",e,{encoding:"utf-8"});return{status:t.status,stdout:t.stdout||"",stderr:t.stderr||""}}};function wc(e){return["build",...e.hostNetwork?["--network","host"]:[],...e.upstreamRef?["--build-arg",`UPSTREAM=${e.upstreamRef}`]:[],"-t",e.image,"-f",e.dockerfile,e.ctxDir]}function Qs(e){let t=en();if(!t.installed)return{status:"no-docker"};let n=process.env.HERMES_IMAGE||We;if(tn(t).installed)return{status:"already-present",image:n};let r=fc();if(!r)return{status:"no-dockerfile",expectedPaths:zs()};let s=yc(e),a=sn({bridgeUsable:()=>St(bc)}),i=$n.default.mkdtempSync(In.default.join(Js.default.tmpdir(),"bc-hermes-build-"));try{let c=(0,Do.spawnSync)("docker",wc({hostNetwork:a,upstreamRef:s,image:n,dockerfile:r,ctxDir:i}),{stdio:"inherit",timeout:6e5});return c.status===0?{status:"built",image:n}:{status:"build-failed",image:n,stderr:`docker build exited ${c.status??"<no-status>"}`}}finally{try{$n.default.rmSync(i,{recursive:!0,force:!0})}catch{}}}H();var jo=m(require("os")),lt=require("child_process"),No="https://ollama.com/download",_c={run:(e,t)=>{let n=(0,lt.spawnSync)(e,t,{encoding:"utf-8",timeout:1e4});return{status:n.status,stdout:n.stdout||"",stderr:n.stderr||""}}};function it(e={}){let t=e.runner??_c,n=t.run("ollama",["--version"]);if(n.status!==0)return{installed:!1,serving:!1};let o=(n.stdout||"").match(/(\d+\.\d+\.\d+)/),r=o?o[1]:void 0;return{installed:!0,serving:t.run("ollama",["list"]).status===0,version:r}}function Xs(e){return e.installed?e.serving?"Ollama is installed and serving.":"Ollama is installed but its server isn't running. Start it with `ollama serve` (or open the Ollama app), then re-run setup.":`Ollama is not installed. Install it from ${No}, then re-run setup.`}function An(e,t={}){if(t.runner){let o=t.runner.run("ollama",["pull",e]);return o.status===0?{ok:!0}:{ok:!1,error:(o.stderr||o.stdout||`exit ${o.status}`).trim()}}let n=(0,lt.spawnSync)("ollama",["pull",e],{stdio:t.inheritStdio===!1?"pipe":"inherit",encoding:"utf-8"});return n.status===0?{ok:!0}:{ok:!1,error:(n.stderr||`ollama pull ${e} failed (exit ${n.status})`).toString().trim()}}function vc(e,t){return t?t.run("sh",["-c",`command -v ${e}`]).status===0:(0,lt.spawnSync)("sh",["-c",`command -v ${e}`],{encoding:"utf-8"}).status===0}function kc(e={}){let t=jo.default.platform();if(t!=="darwin"&&t!=="linux")return{ok:!1,error:`Automatic install isn't supported on ${t}. Install from ${No}.`};let n=e.log??(()=>{}),o=t==="darwin"&&vc("brew",e.runner),r=o?"brew install ollama":"curl -fsSL https://ollama.com/install.sh | sh";return n(`Installing Ollama via ${o?"Homebrew":"the official installer"}\u2026`),e.runner?e.runner.run("sh",["-c",r]):(0,lt.spawnSync)("sh",["-c",r],{stdio:"inherit"}),it({runner:e.runner}).installed?{ok:!0}:{ok:!1,error:`Ollama still not detected after install \u2014 install it manually from ${No}.`}}function Sc(e={}){((r,s)=>e.runner?e.runner.run(r,s):(0,lt.spawnSync)(r,s,{stdio:"ignore",timeout:1e4}))("sh",["-c",jo.default.platform()==="darwin"?"(brew services start ollama 2>/dev/null) || (nohup ollama serve >/dev/null 2>&1 &)":"(systemctl start ollama 2>/dev/null) || (nohup ollama serve >/dev/null 2>&1 &)"])}var Rc=e=>new Promise(t=>setTimeout(t,e));async function Zs(e){let t=e.log??(()=>{}),n=e.sleepImpl??Rc,o=it({runner:e.runner});if(!o.installed){if(!(e.assumeYes||(e.confirmInstall?await e.confirmInstall(" Ollama is not installed. Install it now? [Y/n] "):!1)))return{ready:!1,installed:!1,reason:"install declined"};let s=kc({runner:e.runner,log:t});if(!s.ok)return{ready:!1,installed:!1,reason:s.error};o=it({runner:e.runner})}if(o.installed&&!o.serving){t(" Starting the Ollama server\u2026"),Sc({runner:e.runner});let r=e.pollAttempts??20;for(let s=0;s<r&&!o.serving;s++)await n(1e3),o=it({runner:e.runner})}return{ready:o.installed&&o.serving,installed:o.installed,reason:o.installed&&o.serving?void 0:"ollama not serving"}}var la=m(require("os")),ca=m(require("readline"));ie();Te();var fe=m(require("os")),na=require("child_process");Ae();var ea=[1,2,4,6,8,12,16];function ta(e){if(e>16)return"16GB+";let t=ea[0];for(let n of ea)e>=n&&(t=n);return`${t}GB`}var oa={run:(e,t)=>{let n=(0,na.spawnSync)(e,t,{encoding:"utf-8",timeout:5e3});return{status:n.status,stdout:n.stdout||""}}};function En(){return Math.round(fe.default.totalmem()/1024/1024/1024)}function ra(e){return e==="x64"?"x86_64":e}function sa(e,t){if(e==="darwin")return t.run("sysctl",["-n","machdep.cpu.brand_string"]).stdout.trim()||null;if(e==="linux")return t.run("sh",["-c","grep -m1 'model name' /proc/cpuinfo | cut -d: -f2"]).stdout.trim()||null;let n=fe.default.cpus();return n.length?n[0].model.trim():null}function aa(e,t,n,o){if(e==="darwin"&&t==="arm64")return{accelerator:n?`${n} GPU`:"Apple GPU",vram_gb:null,unified_memory:!0};let r=o.run("nvidia-smi",["--query-gpu=name,memory.total","--format=csv,noheader,nounits"]);if(r.status===0&&r.stdout.trim()){let s=r.stdout.trim().split(`
|
|
145
|
+
`)[0],[a,i]=s.split(",").map(l=>l.trim()),c=Number.parseFloat(i);return{accelerator:a||"NVIDIA GPU",vram_gb:Number.isFinite(c)?Math.round(c/1024*10)/10:null,unified_memory:!1}}return{accelerator:null,vram_gb:null,unified_memory:!1}}function Pn(e={}){let t=e.runner??oa,n=fe.default.platform(),o=ra(fe.default.arch()),r=sa(n,t);return aa(n,o,r,t).accelerator}function ia(e={}){let t=e.runner??oa,n=fe.default.platform(),o=ra(fe.default.arch()),r=e.ramTotalGbOverride!=null&&Number.isFinite(e.ramTotalGbOverride)&&e.ramTotalGbOverride>0?e.ramTotalGbOverride:null,s=r??En(),a=sa(n,t),{accelerator:i,vram_gb:c,unified_memory:l}=aa(n,o,a,t);return{machine_fingerprint:ft(),ram_total_gb:r!=null?Math.round(r*10)/10:Math.round(fe.default.totalmem()/1024/1024/1024*10)/10,ram_tier:ta(s),cpu_model:a,core_count:fe.default.cpus().length||null,accelerator:i,vram_gb:c,unified_memory:l,os:n,arch:o}}function Bo(){return Math.round(la.default.totalmem()/1024/1024/1024)}async function jt(e,t,n={}){let o=n.apiBase??N,r=n.fetchImpl??fetch,s=n.accelerator&&n.accelerator.trim(),a=`${o}/api/v1/dashboard/recommended-models?ram_gb=${encodeURIComponent(e)}&runtime=${encodeURIComponent(t)}`+(s?`&accelerator=${encodeURIComponent(s)}`:"");try{let i=await r(a,{headers:{Accept:"application/json"}});if(!i.ok)return null;let c=await i.json(),l=c.result&&typeof c.result=="object"?c.result:c;return Array.isArray(l.recommended)?l:null}catch{return null}}async function Cc(e){let t=ca.default.createInterface({input:process.stdin,output:process.stdout});try{return await new Promise(n=>t.question(e,n))}finally{t.close()}}function xc(e){let t=e.verdict==="great"?"great":e.verdict==="usable"?"usable":e.verdict,n=e.decode_tps!=null?`${e.decode_tps} tok/s`:"speed not yet benchmarked",o=e.is_top_pick?" (recommended)":"";return`${e.model_name} [${e.ollama_tag}] \u2014 ${t}, ${n}. ${e.why}${o}`}async function $c(e,t){let n=e.recommended;if(!n||n.length===0)return{tag:null,note:e.note,model:null};let o=n.find(l=>l.is_top_pick)??n[0];if(t.preferredTag){let l=n.find(u=>u.ollama_tag===t.preferredTag);return l?{tag:l.ollama_tag,note:e.note,model:l}:{tag:t.preferredTag,note:e.note,model:null}}if(!t.interactive)return{tag:o.ollama_tag,note:e.note,model:o};let r=t.log??(l=>console.log(l)),s=t.prompt??Cc;r(`
|
|
146
|
+
Local models that fit ${e.ram_gb} GB (${e.ram_tier}):`),n.forEach((l,u)=>{r(` ${l===o?"*":" "} ${u+1}) ${xc(l)}`)});let a=n.indexOf(o)+1,i=(await s(` Pick a model [${a}]: `)).trim();if(!i)return{tag:o.ollama_tag,note:e.note,model:o};let c=Number.parseInt(i,10);if(Number.isInteger(c)&&c>=1&&c<=n.length){let l=n[c-1];return{tag:l.ollama_tag,note:e.note,model:l}}return{tag:o.ollama_tag,note:e.note,model:o}}async function On(e){let t=e.ramGb??Bo(),n=e.accelerator===void 0?Pn():e.accelerator,o=await jt(t,e.runtime,{apiBase:e.apiBase,fetchImpl:e.fetchImpl,accelerator:n});if(!o)return{tag:e.preferredTag||kt("ollama"),note:"Could not reach the model-recommendation service \u2014 using a safe default model.",ramGb:t,fellBack:!0};let r=await $c(o,{interactive:e.interactive,preferredTag:e.preferredTag,prompt:e.prompt,log:e.log});return{tag:r.tag,note:r.note,ramGb:t,fellBack:!1}}Te();var te=he.default.join(Ut.default.homedir(),".openclaw","openclaw.json"),Bt=te+".badgerclaw-stash",ya=he.default.join(Ut.default.homedir(),".openclaw","npm"),Ic=he.default.join(ya,"package.json");async function ua(){try{let e=await fetch(`${N}/api/v1/dashboard/versions/latest`,{headers:{Accept:"application/json"}});if(!e.ok)return{cli:null,plugin:null,openclaw:null,hermes:null};let t=await e.json(),n=t.result&&typeof t.result=="object"?t.result:t,o=r=>typeof r!="string"||!r||r==="unknown"?null:r;return{cli:o(n.cli),plugin:o(n.plugin),openclaw:o(n.supported_openclaw),hermes:o(n.supported_hermes)}}catch{return{cli:null,plugin:null,openclaw:null,hermes:null}}}function Ac(){if(!L.default.existsSync(te))return null;try{let e=JSON.parse(L.default.readFileSync(te,"utf-8")),t=e.channels?.badgerclaw;return t?(delete e.channels.badgerclaw,e.plugins?.entries?.badgerclaw&&delete e.plugins.entries.badgerclaw,L.default.writeFileSync(te,JSON.stringify(e,null,2)),L.default.writeFileSync(Bt,JSON.stringify(t,null,2)),t):null}catch{return null}}function Ec(){if(!L.default.existsSync(te))return null;try{let e=JSON.parse(L.default.readFileSync(te,"utf-8"));return!e.gateway||typeof e.gateway!="object"?null:JSON.parse(JSON.stringify(e.gateway))}catch{return null}}function Pc(e){if(e&&L.default.existsSync(te))try{let t=JSON.parse(L.default.readFileSync(te,"utf-8"));t.gateway={...e,...t.gateway||{}},L.default.writeFileSync(te,JSON.stringify(t,null,2))}catch(t){console.log(d.default.yellow(` \u26A0\uFE0F Could not restore gateway block: ${t.message}`)),console.log(d.default.yellow(" Run `openclaw config set gateway.mode local` manually if the gateway fails to start."))}}function Oc(){if(L.default.existsSync(Bt))try{let e=JSON.parse(L.default.readFileSync(Bt,"utf-8")),t=JSON.parse(L.default.readFileSync(te,"utf-8"));t.channels=t.channels||{},t.channels.badgerclaw=e,L.default.writeFileSync(te,JSON.stringify(t,null,2)),L.default.unlinkSync(Bt)}catch(e){console.log(d.default.yellow(` \u26A0\uFE0F Could not restore config: ${e.message}`)),console.log(d.default.yellow(` Your bot credentials are backed up at: ${Bt}`))}}async function da(e){return new Promise(t=>{let n=(0,ue.spawn)("openclaw",["plugins","install",e,"--force","--dangerously-force-unsafe-install"],{stdio:["ignore","pipe","pipe"],shell:!0}),o="",r=!1,s=!1,a=/Installed plugin:|Restart the gateway to load plugins/,i=()=>{r||s||(r=!0,setTimeout(()=>{if(!s){try{n.kill("SIGTERM")}catch{}setTimeout(()=>{if(!s)try{n.kill("SIGKILL")}catch{}},1e3)}},3e3))},c=(l,u)=>{u.write(l),o+=l.toString("utf-8"),a.test(o)&&i()};n.stdout.on("data",l=>c(l,process.stdout)),n.stderr.on("data",l=>c(l,process.stderr)),n.on("exit",l=>{s=!0,t({status:r?0:l??1})}),n.on("error",l=>{s||(s=!0,console.error(d.default.red(` openclaw spawn error: ${l.message}`)),t({status:1}))})})}function ma(e){if(!L.default.existsSync(e))return!1;let t;try{t=JSON.parse(L.default.readFileSync(e,"utf-8"))}catch{return!1}let n=!1;return t.overrides&&typeof t.overrides=="object"&&"uuid"in t.overrides&&(delete t.overrides.uuid,n=!0),t.openclaw&&Array.isArray(t.openclaw.managedOverrides)&&t.openclaw.managedOverrides.includes("uuid")&&(t.openclaw.managedOverrides=t.openclaw.managedOverrides.filter(o=>o!=="uuid"),n=!0),n?(L.default.writeFileSync(e,JSON.stringify(t,null,2)+`
|
|
147
|
+
`),!0):!1}function Lc(){let e=[];ma(Ic)&&e.push(ya);for(let t of Xt())ma(he.default.join(t,"package.json"))&&e.push(t);return e}async function Tc(){let{detectCapabilities:e}=await Promise.resolve().then(()=>(Le(),Ir)),{pushHeartbeat:t}=await Promise.resolve().then(()=>(tt(),Eo));console.log(d.default.green(`
|
|
141
148
|
\u{1F9A1} BadgerClaw \u2014 capability refresh
|
|
142
|
-
`));let n=e();console.log(d.default.dim(" Detected on this host:")),console.log(d.default.dim(` OpenClaw : ${
|
|
149
|
+
`));let n=e();console.log(d.default.dim(" Detected on this host:")),console.log(d.default.dim(` OpenClaw : ${Uo(n.openclaw)}`)),console.log(d.default.dim(` Hermes : ${Uo(n.hermes)}`)),console.log(d.default.dim(` Docker : ${Uo(n.docker)}`)),n.disk_free_gb!==null&&console.log(d.default.dim(` Disk free: ${n.disk_free_gb} GB`));try{await t(),console.log(d.default.green(`
|
|
143
150
|
\u2705 Capabilities pushed to the API.`)),console.log(d.default.dim(" The backend will route new bot assignments accordingly."))}catch(o){let r=o?.message||String(o);if(r.includes("no auth")||r.includes("401")){console.log(d.default.yellow(`
|
|
144
151
|
\u26A0\uFE0F Not logged in \u2014 capabilities re-scanned locally but not posted to the API.`)),console.log(d.default.dim(" Run `badgerclaw login` first, then re-run `badgerclaw setup --refresh`."));return}console.log(d.default.red(`
|
|
145
|
-
\u274C Failed to push capabilities: ${r}`)),process.exit(1)}}function
|
|
152
|
+
\u274C Failed to push capabilities: ${r}`)),process.exit(1)}}function Uo(e){return e.installed?e.version?`installed (${e.version})`:e.image?`installed (${e.image})`:"installed":"not installed"}function ba(e=t=>console.log(t)){let t;try{t=Object.keys(me().hermes_bots)}catch{return}if(t.length===0)return;let n=Ye();if(!n)return;e(d.default.dim(` Re-provisioning ${t.length} existing Hermes bot(s) to use the new model...`));let o;try{o=an(n)}catch(r){e(d.default.yellow(` \u26A0\uFE0F Re-provision of existing bots failed: ${r.message}`)),e(d.default.dim(" Run `badgerclaw bot reprovision` once Docker/Ollama are ready."));return}for(let r of o)r.skipped?e(d.default.dim(` \u2022 ${r.bot_id}: skipped (${r.skipped})`)):r.restarted&&e(d.default.green(` \u2022 ${r.bot_id}: restarted`))}async function pa(e){console.log(d.default.green(`
|
|
146
153
|
\u{1F9E0} Local LLM (Ollama/Qwen) \u2014 RAM-aware model picker
|
|
147
|
-
`));let t=process.stdout.isTTY===!0&&process.stdin.isTTY===!0&&!e.assumeYes,n=process.env.BADGERCLAW_LLM_MODEL||void 0,o=await
|
|
148
|
-
\u26A0\uFE0F ${o.note||"Your RAM is too low for a usable local model."}`)),console.log(d.default.dim(" Falling back to the normal cloud-LLM path. Set GOOGLE_API_KEY / ANTHROPIC_API_KEY / OPENROUTER_API_KEY (or BADGERCLAW_LLM_PROVIDER + BADGERCLAW_LLM_API_KEY) before pairing a bot."));return}o.fellBack&&o.note&&console.log(d.default.yellow(` \u26A0\uFE0F ${o.note}`)),console.log(d.default.dim(` Selected local model: ${d.default.cyan(o.tag)}`));let r=t?async i=>{let c=(await
|
|
149
|
-
\u26A0\uFE0F ${i}`)),console.log(d.default.dim(" Falling back to the cloud-LLM path. Install/start Ollama and re-run `badgerclaw setup --hermes --local-llm` to switch this host to a local model."));return}console.log(d.default.dim(" Ollama is installed and serving.")),console.log(d.default.dim(` Pulling ${o.tag} via Ollama (one-time, can be large)...`));let a=
|
|
150
|
-
\u274C Failed to pull ${o.tag}: ${a.error}`)),console.log(d.default.dim(` Pull it manually with \`ollama pull ${o.tag}\`, then re-run setup.`)),process.exit(1)),console.log(d.default.green(` \u2705 Model ${o.tag} ready.`)),
|
|
151
|
-
\u{1F9E0} Run a local LLM (Ollama) on this machine instead of a cloud provider? [y/N] `))).trim().toLowerCase();return n==="y"||n==="yes"}async function
|
|
152
|
-
\u{1F9E0} Configure this host's LLM now? [c]loud / [l]ocal / [s]kip (default: skip) `))).trim().toLowerCase();return n==="c"||n==="cloud"?"cloud":n==="l"||n==="local"?"local":"skip"}async function
|
|
154
|
+
`));let t=process.stdout.isTTY===!0&&process.stdin.isTTY===!0&&!e.assumeYes,n=process.env.BADGERCLAW_LLM_MODEL||void 0,o=await On({runtime:"hermes",interactive:t,preferredTag:n,log:i=>console.log(d.default.dim(i))});if(console.log(d.default.dim(` Detected RAM: ~${o.ramGb} GB`)),!o.tag){console.log(d.default.yellow(`
|
|
155
|
+
\u26A0\uFE0F ${o.note||"Your RAM is too low for a usable local model."}`)),console.log(d.default.dim(" Falling back to the normal cloud-LLM path. Set GOOGLE_API_KEY / ANTHROPIC_API_KEY / OPENROUTER_API_KEY (or BADGERCLAW_LLM_PROVIDER + BADGERCLAW_LLM_API_KEY) before pairing a bot."));return}o.fellBack&&o.note&&console.log(d.default.yellow(` \u26A0\uFE0F ${o.note}`)),console.log(d.default.dim(` Selected local model: ${d.default.cyan(o.tag)}`));let r=t?async i=>{let c=(await Ln(d.default.cyan(i))).trim().toLowerCase();return c===""||c==="y"||c==="yes"}:void 0,s=await Zs({assumeYes:e.assumeYes,confirmInstall:r,log:i=>console.log(d.default.dim(" "+i))});if(!s.ready){let i=s.reason==="install declined"?"Ollama install skipped.":`Ollama not ready (${s.reason||"unknown"}).`;console.log(d.default.yellow(`
|
|
156
|
+
\u26A0\uFE0F ${i}`)),console.log(d.default.dim(" Falling back to the cloud-LLM path. Install/start Ollama and re-run `badgerclaw setup --hermes --local-llm` to switch this host to a local model."));return}console.log(d.default.dim(" Ollama is installed and serving.")),console.log(d.default.dim(` Pulling ${o.tag} via Ollama (one-time, can be large)...`));let a=An(o.tag);a.ok||(console.log(d.default.red(`
|
|
157
|
+
\u274C Failed to pull ${o.tag}: ${a.error}`)),console.log(d.default.dim(` Pull it manually with \`ollama pull ${o.tag}\`, then re-run setup.`)),process.exit(1)),console.log(d.default.green(` \u2705 Model ${o.tag} ready.`)),Mr({provider:"ollama",model:o.tag}),console.log(d.default.green(" \u2705 This host will use the local Ollama model for new Hermes bots.")),ba()}function ga(e){return e===!0||(process.env.BADGERCLAW_LLM_PROVIDER||"").toLowerCase()==="ollama"}function Ln(e){let t=ha.default.createInterface({input:process.stdin,output:process.stdout});return new Promise(n=>t.question(e,o=>{t.close(),n(o)}))}function wa(e){let t=process.stdout.isTTY===!0&&process.stdin.isTTY===!0;return(e.isInteractive??t)&&!e.assumeYes}async function Mc(e){if(!wa(e))return!1;let n=(await(e.prompt??Ln)(d.default.cyan(`
|
|
158
|
+
\u{1F9E0} Run a local LLM (Ollama) on this machine instead of a cloud provider? [y/N] `))).trim().toLowerCase();return n==="y"||n==="yes"}async function Dc(e){if(!wa(e))return"skip";let n=(await(e.prompt??Ln)(d.default.cyan(`
|
|
159
|
+
\u{1F9E0} Configure this host's LLM now? [c]loud / [l]ocal / [s]kip (default: skip) `))).trim().toLowerCase();return n==="c"||n==="cloud"?"cloud":n==="l"||n==="local"?"local":"skip"}async function Nc(e={}){let t=e.log??(s=>console.log(s)),n=e.ramGb??Bo(),o=e.accelerator===void 0?Pn():e.accelerator;t(d.default.green(`
|
|
153
160
|
\u{1F9E0} Local LLM \u2014 RAM-aware recommendation
|
|
154
|
-
`)),t(d.default.dim(` Detected RAM: ~${n} GB \xB7 ${o||"CPU-only"}`));let r=null;try{r=await
|
|
161
|
+
`)),t(d.default.dim(` Detected RAM: ~${n} GB \xB7 ${o||"CPU-only"}`));let r=null;try{r=await jt(n,"hermes",{accelerator:o,fetchImpl:e.fetchImpl})}catch{r=null}if(!r||r.recommended.length===0)t(d.default.yellow(` \u26A0\uFE0F ${r?.note||"No local model is a good fit for this machine \u2014 use a cloud provider."}`));else{t(d.default.dim(" Local models that run well on this machine:"));for(let s of r.recommended){let a=s.decode_tps!=null?`${s.decode_tps} tok/s`:"speed not yet benchmarked";t(` \u2022 ${d.default.cyan(s.ollama_tag)} \u2014 ${s.verdict}, ${a}`+(s.is_top_pick?" (best fit)":""))}}t(d.default.dim(`
|
|
155
162
|
Note: OpenClaw bots use a cloud LLM provider, so this host won't run a
|
|
156
163
|
local model on the OpenClaw runtime. To actually use one of these models,
|
|
157
|
-
re-run \`badgerclaw setup --hermes --local-llm\` (the Hermes runtime).`))}var
|
|
164
|
+
re-run \`badgerclaw setup --hermes --local-llm\` (the Hermes runtime).`))}var Ho=[{key:"gemini",label:"Google Gemini",env:"GOOGLE_API_KEY"},{key:"anthropic",label:"Anthropic Claude",env:"ANTHROPIC_API_KEY"},{key:"openrouter",label:"OpenRouter",env:"OPENROUTER_API_KEY"}];async function jc(e){let t=e.log??(l=>console.log(l)),n=e.prompt??Ln,o=e.env??process.env;t(d.default.green(`
|
|
158
165
|
\u2601\uFE0F Cloud LLM provider
|
|
159
|
-
`)),
|
|
166
|
+
`)),Ho.forEach((l,u)=>t(` ${u+1}) ${l.label}`));let r=(await n(" Pick a provider [1]: ")).trim(),s=r===""?1:Number.parseInt(r,10),a=Ho[Number.isInteger(s)&&s>=1&&s<=Ho.length?s-1:0],i=(o[a.env]||"").trim();if(i?t(d.default.dim(` Using ${a.env} from your environment.`)):i=(await n(d.default.cyan(` Enter your ${a.label} API key: `))).trim(),!i){t(d.default.yellow(" No API key entered \u2014 skipping cloud-LLM setup. Set it via env before pairing."));return}let c=kt(a.key);Dr({provider:a.key,apiKey:i,model:c}),t(d.default.green(` \u2705 This host will use ${a.label} (${c}) for new Hermes bots.`)),t(d.default.dim(" Saved to ~/.badgerclaw/cloud-llm.json (mode 0600).")),ba(t)}var _a=new fa.Command("setup").description("Install or update the OpenClaw BadgerClaw plugin safely (handles config automatically)").option("--refresh","Skip install steps; re-scan host capabilities and push them to the API immediately. Useful right after installing a second runtime (Docker, Hermes image) so the backend doesn't have to wait for the next heartbeat tick to pick up the change.").option("--hermes","Opt this host into the Hermes runtime instead of OpenClaw. Skips the OpenClaw binary + plugin + gateway install entirely; instead builds the Hermes container image locally from the bundled Dockerfile (against the public nousresearch/hermes-agent base on Docker Hub \u2014 no AWS/ECR credentials needed). Default (no flag) installs OpenClaw \u2014 Hermes is opt-in while it's still being soaked on early-adopter hosts.").option("--local-llm","Opt into a local LLM (Ollama/Qwen) for this host. Setup also OFFERS this as an optional, skippable prompt in interactive runs; this flag (or BADGERCLAW_LLM_PROVIDER=ollama) skips the prompt and opts in directly. On HERMES it detects this host's RAM, asks the backend which Qwen models fit, lets you pick one (auto-picks the best fit non-interactively), pulls it via Ollama, and wires Hermes to 127.0.0.1:11434 (requires Ollama installed + serving; never auto-installed). On OPENCLAW (cloud-only runtime) it cannot run a local model, so it only SHOWS the RAM-aware recommendations and points you to `--hermes`. When RAM is too low for any usable model, setup says so.").option("--yes","Assume defaults for any interactive prompt (e.g. the local-LLM model picker auto-selects the recommended model). Useful for scripted / unattended runs.").action(async e=>{if(e.refresh){await Tc();return}if(console.log(d.default.green(`
|
|
160
167
|
\u{1F9A1} BadgerClaw Setup
|
|
161
|
-
`)),
|
|
162
|
-
\u274C ${
|
|
163
|
-
`)),!
|
|
164
|
-
\u274C Docker is required for the Hermes runtime and could not be started automatically.`)),console.log(d.default.dim(" Resolve the issue above (or set Docker up manually), then re-run `badgerclaw setup --hermes`.")),process.exit(1)),
|
|
168
|
+
`)),Ps({hermes:e.hermes})==="hermes"){let f=Ws({nodeMajor:Ys(),npmPresent:(0,ue.spawnSync)("sh",["-c","command -v npm"],{encoding:"utf-8"}).status===0});f&&(console.log(d.default.red(`
|
|
169
|
+
\u274C ${f}`)),process.exit(1));let b=ke();if(console.log(d.default.dim(` Detected: Docker ${b.docker.installed?"running":"not running"}, Hermes image ${b.hermes.installed?"present":"absent"}.`)),console.log(d.default.dim(` Selected runtime: ${d.default.cyan("hermes")} (via --hermes flag)
|
|
170
|
+
`)),!b.docker.installed){console.log(d.default.dim(" Docker is not ready \u2014 bootstrapping it for the Hermes runtime..."));let g=await Ks({log:y=>console.log(y)});g.ok||(console.log(d.default.red(`
|
|
171
|
+
\u274C Docker is required for the Hermes runtime and could not be started automatically.`)),console.log(d.default.dim(" Resolve the issue above (or set Docker up manually), then re-run `badgerclaw setup --hermes`.")),process.exit(1)),g.networkHostRequired&&console.log(d.default.dim(" \u24D8 No Docker bridge on this host \u2014 Hermes bots will use host networking automatically at pair time.")),b=ke()}if(b.disk_free_gb!==null&&b.disk_free_gb<6&&console.log(d.default.yellow(` \u26A0\uFE0F Low disk: ${b.disk_free_gb} GB free \u2014 the Hermes image build needs ~5 GB and may fail if it runs out.`)),b.hermes.installed)console.log(d.default.dim(` Hermes image already present: ${b.hermes.image}`));else{let g=(await ua()).hermes;g&&console.log(d.default.dim(` Pinned Hermes upstream: ${g}`)),console.log(d.default.dim(" Building Hermes image (one-time, ~2 min)..."));let y=Qs({upstream:g});y.status==="built"?console.log(d.default.green(` \u2705 Hermes image built: ${y.image}`)):y.status==="already-present"?console.log(d.default.dim(` Hermes image already present: ${y.image}`)):y.status==="no-dockerfile"?(console.log(d.default.red(`
|
|
165
172
|
\u274C Bundled Hermes Dockerfile missing from CLI install.`)),console.log(d.default.dim(" Reinstall: npm install -g badgerclaw@latest")),console.log(d.default.dim(` Looked at: ${y.expectedPaths.join(", ")}`)),process.exit(1)):y.status==="build-failed"?(console.log(d.default.red(`
|
|
166
173
|
\u274C Hermes image build failed: ${y.stderr}`)),console.log(d.default.dim(" See the docker output above for details.")),process.exit(1)):y.status==="no-docker"&&(console.log(d.default.red(`
|
|
167
|
-
\u274C Docker is not running.`)),process.exit(1))}if(
|
|
168
|
-
Cleaning up heartbeat hooks from previous installs...`)),
|
|
174
|
+
\u274C Docker is not running.`)),process.exit(1))}if(ga(e.localLlm))await pa({assumeYes:e.yes===!0});else{let g=await Dc({assumeYes:e.yes===!0});g==="local"?await pa({assumeYes:e.yes===!0}):g==="cloud"&&await jc({assumeYes:e.yes===!0})}console.log(d.default.dim(`
|
|
175
|
+
Cleaning up heartbeat hooks from previous installs...`)),ss({loggedIn:$()!==null,log:g=>console.log(d.default.dim(` ${g}`))}),console.log(d.default.dim(`
|
|
169
176
|
Hermes-only mode \u2014 skipping OpenClaw plugin + gateway install.
|
|
170
177
|
To enable OC bots later, drop the --hermes flag: \`badgerclaw setup\`.
|
|
171
|
-
`)),console.log(d.default.green("\u2705 Setup complete (Hermes-only host).")),console.log(d.default.dim("\nNext steps:\n 1. `badgerclaw login` \u2014 authenticate this CLI to the backend.\n 2. `badgerclaw setup --refresh` \u2014 push the Hermes capability so\n the backend (and the iOS app) knows this host can serve\n Hermes bots.\n 3. `badgerclaw bot create <name>` \u2014 defaults to Hermes on this host."));return}let n=await
|
|
172
|
-
\u274C OpenClaw install failed.`)),process.exit(1)));let o=
|
|
178
|
+
`)),console.log(d.default.green("\u2705 Setup complete (Hermes-only host).")),console.log(d.default.dim("\nNext steps:\n 1. `badgerclaw login` \u2014 authenticate this CLI to the backend.\n 2. `badgerclaw setup --refresh` \u2014 push the Hermes capability so\n the backend (and the iOS app) knows this host can serve\n Hermes bots.\n 3. `badgerclaw bot create <name>` \u2014 defaults to Hermes on this host."));return}let n=await ua();if(n.cli){let f=zn();f!==n.cli?(console.log(d.default.dim(` Updating CLI: ${f} \u2192 ${n.cli} (supported)...`)),(0,ue.spawnSync)("npm",["install","-g",`badgerclaw@${n.cli}`],{stdio:"inherit",shell:!0}).status!==0&&(console.log(d.default.red("\n\u274C CLI update failed. Fix the npm error above, then re-run `badgerclaw setup`.")),process.exit(1)),console.log(d.default.green(` \u2705 CLI updated to ${n.cli}`))):console.log(d.default.dim(` CLI already at approved version ${f}.`))}if(n.openclaw){let f=Y();f!==n.openclaw?(console.log(f===null?d.default.dim(` Installing OpenClaw ${n.openclaw} (supported)...`):d.default.dim(` Updating OpenClaw: ${f} \u2192 ${n.openclaw} (supported)...`)),(0,ue.spawnSync)("npm",["install","-g",`openclaw@${n.openclaw}`],{stdio:"inherit",shell:!0}).status!==0&&(console.log(d.default.red("\n\u274C OpenClaw install/update failed. Fix the npm error above, then re-run `badgerclaw setup`.")),process.exit(1)),console.log(d.default.green(` \u2705 OpenClaw at ${n.openclaw}`))):console.log(d.default.dim(` OpenClaw already at approved version ${f}.`))}else Y()===null&&(console.log(d.default.yellow(" \u26A0\uFE0F No approved OpenClaw version and none installed \u2014 installing latest from npm.")),(0,ue.spawnSync)("npm",["install","-g","openclaw"],{stdio:"inherit",shell:!0}).status!==0&&(console.log(d.default.red(`
|
|
179
|
+
\u274C OpenClaw install failed.`)),process.exit(1)));let o=Ac();o&&console.log(d.default.dim(" Existing bot config stashed temporarily..."));let r=Ec();(0,ue.spawnSync)("openclaw",["gateway","stop"],{stdio:"pipe",shell:!0,timeout:1e4});let s=[he.default.join(Ut.default.homedir(),".openclaw","extensions","badgerclaw"),he.default.join(Ut.default.homedir(),".openclaw","npm","node_modules","@badgerclaw","connect"),...Xt()],a=[],i=[],c=f=>{let b=`${f}.bc-setup-stash-${Date.now()}-${Math.floor(Math.random()*1e6)}`,g=(0,ue.spawnSync)("mv",["-f",f,b],{encoding:"utf-8"});return g.status===0?{ok:!0,stash:b}:{ok:!1,error:(g.stderr||"").trim()||`exit ${g.status}`}};for(let f of s)if(L.default.existsSync(f))try{L.default.rmSync(f,{recursive:!0,force:!0}),a.push(f)}catch(b){let g=c(f);g.ok?(i.push(`${f} \u2192 ${g.stash}`),console.log(d.default.yellow(` \u26A0\uFE0F Could not delete ${f} (${b.message}); renamed it aside instead.`))):(console.log(d.default.red(` \u274C Could not delete ${f} (${b.message})`)),console.log(d.default.red(` Could not rename ${f} aside either (${g.error}). Plugin install will likely fail.`)))}a.length>0&&console.log(d.default.dim(` Cleared existing plugin director${a.length>1?"ies":"y"}.`)),i.length>0&&console.log(d.default.dim(` Renamed busy install${i.length>1?"s":""} aside: ${i.join(", ")}`));let l=n.plugin?`@badgerclaw/connect@${n.plugin}`:"@badgerclaw/connect";n.plugin?console.log(d.default.dim(` Installing ${l} (supported)...`)):console.log(d.default.yellow(" \u26A0\uFE0F Allow-list unavailable \u2014 installing @badgerclaw/connect unpinned."));let u=null;kr("2026.6.5")&&(u=Hs(),u&&console.log(d.default.dim(" Adjusted OpenClaw 2026.6.5 npm overrides for a full channel-plugin install.")));let p=await da(l);if(p.status===0&&n.plugin&&n.plugin!=="unknown"){for(let b=1;b<=2&&!(Zt()===n.plugin||(console.log(d.default.yellow(` Plugin landed as a hook-pack-only install (not yet visible to \`badgerclaw login\`) \u2014 re-running plugin install (attempt ${b}/2)...`)),p=await da(l),p.status!==0));b++);p.status===0&&Zt()!==n.plugin&&console.log(d.default.yellow(" \u26A0\uFE0F Plugin still not detected at the npm install path after 2 attempt(s). If `badgerclaw login` reports it as not installed, re-run `badgerclaw setup`."))}o&&(Oc(),console.log(d.default.dim(" Bot config restored."))),r&&(Pc(r),console.log(d.default.dim(" Gateway config restored."))),u&&Fs(u),p.status!==0&&(console.log(d.default.red(`
|
|
173
180
|
\u274C Plugin install failed. Your bot config has been restored.`)),process.exit(1)),console.log(d.default.green(`
|
|
174
|
-
\u2705 BadgerClaw plugin installed successfully!`));let
|
|
175
|
-
\u274C npm install failed after removing uuid override. Bot will not reply correctly until this is resolved.`)),process.exit(1))}
|
|
176
|
-
\u274C Gateway failed to start. Diagnose with:`)),console.log(d.default.dim(" systemctl --user status openclaw-gateway.service # Linux")),console.log(d.default.dim(" launchctl list | grep openclaw # macOS")),console.log(d.default.dim(" openclaw config get gateway.mode")),console.log(d.default.dim(" Then re-run `badgerclaw setup` after fixing the underlying issue.")),process.exit(1)),
|
|
181
|
+
\u2705 BadgerClaw plugin installed successfully!`));let _=Lc();for(let f of _){console.log(d.default.dim(` Stripping OpenClaw uuid:14 override in ${f} (breaks request@2.88 in matrix-bot-sdk)...`));try{L.default.rmSync(he.default.join(f,"node_modules","uuid"),{recursive:!0,force:!0})}catch{}try{L.default.rmSync(he.default.join(f,"package-lock.json"),{force:!0})}catch{}(0,ue.spawnSync)("npm",["install"],{cwd:f,stdio:"inherit",shell:!0}).status!==0&&(console.log(d.default.red(`
|
|
182
|
+
\u274C npm install failed after removing uuid override. Bot will not reply correctly until this is resolved.`)),process.exit(1))}_.length>0&&console.log(d.default.green(" \u2705 uuid override stripped + npm tree re-resolved.")),console.log("");let R=await gn({verbose:!0});R.ok||(console.log(d.default.red(`
|
|
183
|
+
\u274C Gateway failed to start. Diagnose with:`)),console.log(d.default.dim(" systemctl --user status openclaw-gateway.service # Linux")),console.log(d.default.dim(" launchctl list | grep openclaw # macOS")),console.log(d.default.dim(" openclaw config get gateway.mode")),console.log(d.default.dim(" Then re-run `badgerclaw setup` after fixing the underlying issue.")),process.exit(1)),R.ready===!1?console.log(d.default.yellow(" \u23F3 Gateway started but not answering yet \u2014 it may still be coming up.")):console.log(d.default.green(" \u2705 Gateway is running.")),(ga(e.localLlm)||await Mc({assumeYes:e.yes===!0}))&&await Nc(),console.log(d.default.dim("\nNext: run `badgerclaw login` to authenticate.")),fn()});tt();var ka=require("commander"),M=m(require("chalk"));H();Ae();Re();var Sa=new ka.Command("dashboard").description("Show local diagnostic dashboard \u2014 machine health, gateway status, bot telemetry").action(async()=>{$()||(console.log(M.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let{version:t}=_e(),n=Yt(),o=await Pt();console.log(M.default.bold.green(`
|
|
177
184
|
BadgerClaw Dashboard
|
|
178
|
-
`)),console.log(M.default.bold(" Instance")),console.log(` ID: ${ae()}`),console.log(` CLI Version: ${t}`),console.log(` Plugin: ${o.pluginVersion}`),console.log(),console.log(M.default.bold(" Machine")),console.log(` Hostname: ${n.hostname}`),console.log(` OS: ${n.os} / ${n.arch}`),console.log(` Uptime: ${
|
|
179
|
-
Gateway Claude Code Rooms:`)),console.log(
|
|
180
|
-
Local Sessions:`)),console.log(
|
|
185
|
+
`)),console.log(M.default.bold(" Instance")),console.log(` ID: ${ae()}`),console.log(` CLI Version: ${t}`),console.log(` Plugin: ${o.pluginVersion}`),console.log(),console.log(M.default.bold(" Machine")),console.log(` Hostname: ${n.hostname}`),console.log(` OS: ${n.os} / ${n.arch}`),console.log(` Uptime: ${va(n.uptimeSeconds)}`),console.log(` Free Memory: ${n.memFreeMb} MB`),console.log();let r=o.status==="running"?M.default.green:o.status==="error"?M.default.red:M.default.yellow;if(console.log(M.default.bold(" Gateway")),console.log(` Status: ${r(o.status)}`),console.log(` PID: ${o.pid??"N/A"}`),console.log(` Last Restart: ${o.lastRestart??"N/A"}`),console.log(),o.bots.length===0)console.log(M.default.bold(" Bots")),console.log(M.default.dim(" No bots detected. Pair bots at https://badgerclaw.ai")),console.log();else{console.log(M.default.bold(` Bots (${o.bots.length})`)),console.log();for(let s of o.bots){let a=s.status==="running"?M.default.green:s.status==="error"?M.default.red:M.default.yellow;if(console.log(` ${M.default.bold(s.botUsername)} ${a(`[${s.status}]`)}`),console.log(` ID: ${s.botId}`),console.log(` Uptime: ${va(s.uptimeSeconds)}`),console.log(` Messages: ${s.messagesReceived} in / ${s.messagesSent} out`),s.chunkedMessages>0&&console.log(` Chunked: ${s.chunkedMessages} messages, ${s.totalChunksSent} chunks`),console.log(` Rooms: ${s.roomsActive} active`),console.log(` Errors: ${s.errors}`),s.lastError&&(console.log(` Last Error: ${M.default.red(s.lastError)}`),console.log(` Error At: ${s.lastErrorAt}`)),console.log(` Last Active: ${s.lastActivity??"N/A"}`),s.roomDetails&&s.roomDetails.length>0){console.log(M.default.dim(" Rooms:"));for(let i of s.roomDetails)console.log(M.default.dim(` ${i.roomName} \u2014 ${i.messagesInRoom} msgs, last: ${i.lastActivityInRoom??"N/A"}`))}console.log()}}console.log(M.default.dim(` Last checked: ${new Date().toISOString()}`)),console.log()});function va(e){if(e<60)return`${e}s`;if(e<3600)return`${Math.floor(e/60)}m ${e%60}s`;let t=Math.floor(e/3600),n=Math.floor(e%3600/60);return t<24?`${t}h ${n}m`:`${Math.floor(t/24)}d ${t%24}h ${n}m`}var Fo=require("commander"),ct=m(require("chalk")),Ra=m(require("ora"));H();Re();tt();var Bc=new Fo.Command("restart").description("Restart the OpenClaw gateway via the plugin probe endpoint").action(async()=>{$()||(console.log(ct.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let t=(0,Ra.default)("Restarting gateway...").start(),n=await et();if(n.success){t.succeed(ct.default.green(`Gateway restarted: ${n.message}`));try{await U(),console.log(ct.default.dim(" Heartbeat pushed with updated status."))}catch{console.log(ct.default.dim(" Could not push heartbeat \u2014 daemon may not be running."))}}else t.fail(ct.default.red(`Gateway restart failed: ${n.message}`)),process.exit(1)}),Ca=new Fo.Command("gateway").description("Manage the OpenClaw gateway").addCommand(Bc);var Ie=require("commander"),A=m(require("chalk")),He=m(require("ora")),$e=m(require("axios"));bn();H();var Ue="http://localhost:7331",Uc=new Ie.Command("start").description("Start a Claude Code session for a bot+room").requiredOption("--bot <botId>","Bot ID or username").requiredOption("--room <roomId>","Matrix room ID").option("--session-id <id>","Session ID (default: auto-generated)").option("--port <port>","MCP server port (default: 7332)","7332").option("--project <dir>","Project directory",process.cwd()).action(async e=>{$()||(console.log(A.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let n=e.sessionId||`session-${Date.now()}`,o=parseInt(e.port,10),r=e.project.replace(/^~/,process.env.HOME||""),s=(0,He.default)("Starting Claude Code session...").start();try{await $e.default.post(`${Ue}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!0,sessionId:n},{timeout:1e4}),s.text="Gateway toggled \u2014 launching MCP server...";let i=vo({sessionId:n,port:o,projectDir:r}).pid||0;await new Promise(c=>setTimeout(c,1500)),s.text="MCP server running \u2014 launching Claude Code...",ko({sessionId:n,port:o,projectDir:r}),So(n,0,i,o,r),s.succeed(A.default.green("Claude Code session started")+A.default.dim(` (session: ${n}, port: ${o})`)),console.log(A.default.dim(` Bot: ${e.bot}`)),console.log(A.default.dim(` Room: ${e.room}`)),console.log(A.default.dim(` Project: ${r}`)),console.log(A.default.dim(" Claude Code should open in a new terminal window."))}catch(a){let i=a.response?.data?.message||a.message||"Unknown error";s.fail(A.default.red(`Failed to start Claude Code: ${i}`)),process.exit(1)}}),Hc=new Ie.Command("stop").description("Stop a Claude Code session").option("--bot <botId>","Bot ID or username").option("--room <roomId>","Matrix room ID").option("--session-id <id>","Session ID to stop").action(async e=>{$()||(console.log(A.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let n=(0,He.default)("Stopping Claude Code session...").start();try{e.bot&&e.room&&await $e.default.post(`${Ue}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!1},{timeout:1e4});let o=e.sessionId;if(!o)try{let a=((await $e.default.get(`${Ue}/claude-code/status`,{timeout:5e3})).data?.rooms||[]).find(i=>i.botId===e.bot&&i.roomId===e.room);a&&(o=a.sessionId)}catch{}o?(Ro(o),n.succeed(A.default.green(`Claude Code session stopped (${o})`))):(n.succeed(A.default.green("Claude Code toggled off at gateway")),console.log(A.default.dim(" No local session found to kill \u2014 may need to close Claude Code manually.")))}catch(o){let r=o.response?.data?.message||o.message||"Unknown error";n.fail(A.default.red(`Failed to stop Claude Code: ${r}`)),process.exit(1)}}),Fc=new Ie.Command("status").description("Show active Claude Code sessions").action(async()=>{$()||(console.log(A.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let t=(0,He.default)("Fetching Claude Code status...").start();try{let n=[];try{n=(await $e.default.get(`${Ue}/claude-code/status`,{timeout:5e3})).data?.rooms||[]}catch{}let o=Co();if(t.stop(),n.length===0&&o.length===0){console.log(A.default.dim("No active Claude Code sessions."));return}if(n.length>0){console.log(A.default.bold(`
|
|
186
|
+
Gateway Claude Code Rooms:`)),console.log(A.default.dim(" Bot".padEnd(30)+"Room".padEnd(30)+"Session".padEnd(20)+"Enabled")),console.log(A.default.dim(" "+"-".repeat(88)));for(let r of n){let s=r.enabled?A.default.green("ON"):A.default.dim("OFF");console.log(` ${(r.botId||"").padEnd(30)}${(r.roomName||r.roomId||"").padEnd(30)}${(r.sessionId||"-").padEnd(20)}${s}`)}}if(o.length>0){console.log(A.default.bold(`
|
|
187
|
+
Local Sessions:`)),console.log(A.default.dim(" Session".padEnd(25)+"Port".padEnd(8)+"MCP PID".padEnd(10)+"Project".padEnd(40)+"Started")),console.log(A.default.dim(" "+"-".repeat(90)));for(let r of o)console.log(` ${r.sessionId.padEnd(25)}${String(r.port).padEnd(8)}${String(r.mcpPid).padEnd(10)}${r.projectDir.padEnd(40)}${r.startedAt}`)}console.log("")}catch(n){t.fail(A.default.red(`Failed to get status: ${n.message}`)),process.exit(1)}}),Gc=new Ie.Command("group").description("Group bots/rooms into a shared Claude Code session").requiredOption("--session <id>","Session ID to group under").requiredOption("--bot <botId>","Bot ID or username").requiredOption("--room <roomId>","Matrix room ID").action(async e=>{let t=(0,He.default)("Grouping bot+room into session...").start();try{await $e.default.post(`${Ue}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!0,sessionId:e.session},{timeout:1e4}),t.succeed(A.default.green(`Grouped ${e.bot} + ${e.room} into session "${e.session}"`))}catch(n){let o=n.response?.data?.message||n.message||"Unknown error";t.fail(A.default.red(`Failed to group: ${o}`)),process.exit(1)}}),qc=new Ie.Command("allow").description("Add a user to the Claude Code allowlist for a bot+room").requiredOption("--bot <botId>","Bot ID or username").requiredOption("--room <roomId>","Matrix room ID").requiredOption("--user <userId>","Matrix user ID to allow (e.g. @alice:server)").action(async e=>{let t=(0,He.default)("Adding user to allowlist...").start();try{await $e.default.post(`${Ue}/claude-code/allow`,{botId:e.bot,roomId:e.room,userId:e.user},{timeout:1e4}),t.succeed(A.default.green(`Added ${e.user} to allowlist for ${e.bot} in ${e.room}`))}catch(n){let o=n.response?.data?.message||n.message||"Unknown error";t.fail(A.default.red(`Failed to add user: ${o}`)),process.exit(1)}}),Vc=new Ie.Command("revoke").description("Remove a user from the Claude Code allowlist for a bot+room").requiredOption("--bot <botId>","Bot ID or username").requiredOption("--room <roomId>","Matrix room ID").requiredOption("--user <userId>","Matrix user ID to revoke").action(async e=>{let t=(0,He.default)("Removing user from allowlist...").start();try{await $e.default.post(`${Ue}/claude-code/revoke`,{botId:e.bot,roomId:e.room,userId:e.user},{timeout:1e4}),t.succeed(A.default.green(`Removed ${e.user} from allowlist for ${e.bot} in ${e.room}`))}catch(n){let o=n.response?.data?.message||n.message||"Unknown error";t.fail(A.default.red(`Failed to revoke user: ${o}`)),process.exit(1)}}),xa=new Ie.Command("claude-code").description("Manage Claude Code sessions (relay Matrix messages to local Claude Code)").addCommand(Uc).addCommand(Hc).addCommand(Fc).addCommand(Gc).addCommand(qc).addCommand(Vc);var ei=require("commander"),I=m(require("chalk")),ti=m(require("path"));ie();var $a=require("child_process"),Kc="http://127.0.0.1:11434";function Go(){let e=process.env.OLLAMA_HOST?.trim();return e?/^https?:\/\//.test(e)?e.replace(/\/$/,""):`http://${e}`.replace(/\/$/,""):Kc}function Fe(e,t){return{num_ctx:e,num_predict:t,temperature:0,seed:42,top_p:1,top_k:0}}async function Ia(e,t,n={}){let o=n.host??Go(),r=n.fetchImpl??fetch,s=new AbortController,a=setTimeout(()=>s.abort(),n.timeoutMs??3e5);try{let i=await r(`${o}${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t),signal:s.signal});if(!i.ok){let c=await i.text().catch(()=>"");throw new Error(`ollama ${e} \u2192 HTTP ${i.status} ${c.slice(0,200)}`)}return await i.json()}finally{clearTimeout(a)}}async function Ge(e,t={}){let n={model:e.model,messages:e.messages,stream:!1,options:e.options,keep_alive:e.keepAlive??"5m"};return e.tools&&e.tools.length&&(n.tools=e.tools),Ia("/api/chat",n,t)}async function Aa(e,t={}){return Ia("/api/show",{model:e},{...t,timeoutMs:15e3})}async function Ea(e={}){try{let t=e.host??Go(),o=await(e.fetchImpl??fetch)(`${t}/api/version`);return o.ok?(await o.json()).version??null:null}catch{return null}}async function Pa(e={}){try{let t=e.host??Go(),o=await(e.fetchImpl??fetch)(`${t}/api/tags`);return o.ok?((await o.json()).models??[]).map(s=>s.name):[]}catch{return[]}}function Oa(e){try{(0,$a.spawnSync)("ollama",["stop",e],{encoding:"utf-8",timeout:15e3})}catch{}}function La(e){if(!e)return null;let t=e.trim().match(/([\d.]+)\s*([BM])/i);if(!t)return null;let n=Number.parseFloat(t[1]);return Number.isFinite(n)?t[2].toUpperCase()==="M"?Math.round(n/1e3*100)/100:n:null}var Ma=require("child_process");function ne(e,t=2){let n=10**t;return Math.round(e*n)/n}function Tn(e){let t=e.filter(o=>Number.isFinite(o)).sort((o,r)=>o-r);if(t.length===0)return null;let n=Math.floor(t.length/2);return t.length%2?t[n]:(t[n-1]+t[n])/2}function qo(e,t){let n=e.filter(i=>Number.isFinite(i)).sort((i,c)=>i-c);if(n.length===0)return null;if(n.length===1)return n[0];let o=t*(n.length-1),r=Math.floor(o),s=Math.ceil(o);if(r===s)return n[r];let a=o-r;return n[r]+(n[s]-n[r])*a}var Wc="You are assisting an engineer who is integrating a local language model into a chat application. The application runs on consumer hardware and must stay responsive. Background: the system pairs a small assistant model with a set of tools for sending messages, looking up records, and scheduling tasks. The assistant should be concise, accurate, and avoid unnecessary preamble. It coordinates with a gateway process that forwards requests and collects telemetry every thirty seconds. Latency budgets are tight: a typical turn should begin streaming within a second and sustain a steady token rate. Reliability matters more than cleverness, and the model must follow instructions exactly even under load. Operators benchmark candidate models "+"across a range of memory tiers to decide which ones run well locally. ".repeat(2)+`
|
|
181
188
|
|
|
182
|
-
Task: In about one hundred words, explain why steady decode throughput and low time-to-first-token both matter for an interactive local assistant.`,
|
|
183
|
-
`)){if(!/ollama/i.test(r))continue;let s=r.trim().match(/^(\d+)/);s&&(n+=Number.parseInt(s[1],10),o=!0)}return o?n/1024:null}start(){let t=()=>{let n=this.sampleOnce();n!=null&&(this.sawAny=!0,n>this.peakMb&&(this.peakMb=n))};t(),this.timer=setInterval(t,250),typeof this.timer.unref=="function"&&this.timer.unref()}stop(){return this.timer&&clearInterval(this.timer),this.timer=null,this.sawAny?Math.round(this.peakMb):null}};function
|
|
189
|
+
Task: In about one hundred words, explain why steady decode throughput and low time-to-first-token both matter for an interactive local assistant.`,Vo=class{constructor(t){this.timer=null;this.peakMb=0;this.sawAny=!1;this.runner=t??((n,o)=>{let r=(0,Ma.spawnSync)(n,o,{encoding:"utf-8",timeout:3e3});return{status:r.status,stdout:r.stdout||""}})}sampleOnce(){let t=this.runner("ps",["-axo","rss=,comm="]);if(t.status!==0)return null;let n=0,o=!1;for(let r of t.stdout.split(`
|
|
190
|
+
`)){if(!/ollama/i.test(r))continue;let s=r.trim().match(/^(\d+)/);s&&(n+=Number.parseInt(s[1],10),o=!0)}return o?n/1024:null}start(){let t=()=>{let n=this.sampleOnce();n!=null&&(this.sawAny=!0,n>this.peakMb&&(this.peakMb=n))};t(),this.timer=setInterval(t,250),typeof this.timer.unref=="function"&&this.timer.unref()}stop(){return this.timer&&clearInterval(this.timer),this.timer=null,this.sawAny?Math.round(this.peakMb):null}};function Yc(e){return e.eval_count&&e.eval_duration?e.eval_count/(e.eval_duration/1e9):null}function Jc(e){return e.prompt_eval_count&&e.prompt_eval_duration?e.prompt_eval_count/(e.prompt_eval_duration/1e9):null}function zc(e){return e.prompt_eval_duration?e.prompt_eval_duration/1e6:null}var Ta=[{role:"user",content:Wc}];async function Da(e){let{model:t,numCtx:n,warmTrials:o}=e,r=e.warmupTrials??2,s=e.perfOpts??{},a=s.log??(()=>{}),i=e.sampler??new Vo;Oa(t),a(" cold load trial\u2026");let c=null;try{let v=await Ge({model:t,messages:[{role:"user",content:"Reply with: OK"}],options:Fe(n,8)},s);c=v.load_duration?v.load_duration/1e6:null}catch(v){a(` cold trial failed: ${v.message}`)}for(let v=0;v<r;v++)a(` warmup ${v+1}/${r}\u2026`),await Ge({model:t,messages:Ta,options:Fe(n,64)},s);let l=[],u=[],p=[],_=null;i.start();try{for(let v=0;v<o;v++){a(` trial ${v+1}/${o}\u2026`);let j=await Ge({model:t,messages:Ta,options:Fe(n,128)},s),oe=Yc(j),re=zc(j),x=Jc(j);oe!=null&&l.push(oe),re!=null&&u.push(re),x!=null&&p.push(x)}}finally{_=i.stop()}let R=Tn(l),f=qo(l,.95),b=Tn(u),g=qo(u,.95),y=Tn(p);return{decode_tps_median:R!=null?ne(R):null,decode_tps_p95:f!=null?ne(f):null,prefill_tps_median:y!=null?ne(y):null,ttft_ms_median:b!=null?ne(b):null,ttft_ms_p95:g!=null?ne(g):null,peak_ram_mb:_,load_ms:c!=null?ne(c):null,max_context:null,num_ctx:n}}async function Na(e){let{model:t}=e,n=e.probes??[8192,16384,32768],o=e.perfOpts??{},r=o.log??(()=>{}),s=null;for(let a of n)try{r(` max-context probe ${a}\u2026`);let i=await Ge({model:t,messages:[{role:"user",content:"Reply with: OK"}],options:Fe(a,8)},o);if(i.eval_count&&i.eval_count>0)s=a;else break}catch{break}return s}var ja=require("child_process");function Qc(e){let t=e.match(/```(?:javascript|js)?\s*([\s\S]*?)```/i);return t?t[1].trim():e.trim()}function Ko(e,t,n,o={}){let s=`${Qc(e)}
|
|
184
191
|
const __out = [${t.join(", ")}];
|
|
185
|
-
process.stdout.write(JSON.stringify(__out));`,a=o.exec??(u=>(0,
|
|
192
|
+
process.stdout.write(JSON.stringify(__out));`,a=o.exec??(u=>(0,ja.execFileSync)(process.execPath,["-e",u],{timeout:5e3,encoding:"utf-8",env:{PATH:process.env.PATH??""},stdio:["ignore","pipe","ignore"]}));if(n.length===0)return{score:0,passed:!1,ran:!1};let i;try{let u=a(s);if(i=JSON.parse(u),!Array.isArray(i))throw new Error("non-array output")}catch{return{score:0,passed:!1,ran:!1}}let c=0;for(let u=0;u<n.length;u++)Ba(i[u],n[u])&&c++;return{score:Math.round(c/n.length*100),passed:c===n.length,ran:!0}}function Ba(e,t){return e===t?!0:typeof e!=typeof t?!1:Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((n,o)=>Ba(n,t[o])):!1}var Yo={tool_call:.4,instruction:.2,coding:.15,summarization:.15,chat:.1},Mn={type:"function",function:{name:"get_weather",description:"Get the current weather for a location.",parameters:{type:"object",properties:{location:{type:"string",description:'City, e.g. "Paris, France"'}},required:["location"]}}},Xc={type:"function",function:{name:"send_email",description:"Send an email to a recipient.",parameters:{type:"object",properties:{to:{type:"string",description:"Recipient email address"},subject:{type:"string"},body:{type:"string"}},required:["to","subject","body"]}}},Zc={type:"function",function:{name:"search_web",description:"Search the web for a query.",parameters:{type:"object",properties:{query:{type:"string"}},required:["query"]}}};function Dn(e){let t=e.message?.tool_calls?.[0];if(!t)return null;let n=t.function.arguments;if(typeof n=="string")try{n=JSON.parse(n)}catch{n={}}return{name:t.function.name,args:n||{}}}function Q(e){return(e.message?.content??"").trim()}function Wo(e){return JSON.stringify(e).toLowerCase()}function eu(e){let t=e.match(/```(?:json)?\s*([\s\S]*?)```/i),o=(t?t[1]:e).match(/\{[\s\S]*\}/);if(!o)return null;try{return JSON.parse(o[0])}catch{return null}}function Ua(e){return e.split(/[.!?]+/).map(t=>t.trim()).filter(Boolean).length}var tu=/\b(can'?t|cannot|won'?t|unable|not able|sorry|i'?m sorry)\b/i,Ha="The Voyager 1 probe launched in 1977 and became the first human-made object to enter interstellar space in 2012. It carries a golden record with sounds and images selected to portray life on Earth. As of recent years it remains in contact with NASA, transmitting data from beyond the heliosphere.",Fa=["voyager","1977","interstellar","golden record","nasa"],Jo=[{id:"tool_call:single_arg",category:"tool_call",tools:[Mn],numPredict:256,messages:[{role:"user",content:"What is the current weather in Paris, France? Use the available tool."}],gradeProgrammatic:e=>{let t=Dn(e),n=!!t&&t.name==="get_weather"&&/paris/.test(Wo(t.args));return{score:n?100:t?.name==="get_weather"?50:0,passed:n}}},{id:"tool_call:select_among_many",category:"tool_call",tools:[Mn,Xc,Zc],numPredict:256,messages:[{role:"user",content:'Send an email to bob@example.com with subject "Hello" and body "Hi Bob". Use the available tools.'}],gradeProgrammatic:e=>{let t=Dn(e),n=!!t&&t.name==="send_email",o=n&&/bob@example\.com/.test(Wo(t.args));return{score:o?100:n?50:0,passed:o}}},{id:"tool_call:no_tool_needed",category:"tool_call",tools:[Mn],numPredict:64,messages:[{role:"user",content:"What is 2 + 2? Answer directly with just the number. Do not call any tool."}],gradeProgrammatic:e=>{if(Dn(e))return{score:0,passed:!1};let n=/\b4\b/.test(Q(e));return{score:n?100:50,passed:n}}},{id:"tool_call:arg_from_context",category:"tool_call",tools:[Mn],numPredict:256,messages:[{role:"user",content:"I am currently in Tokyo, Japan. What is the weather where I am right now? Use the tool."}],gradeProgrammatic:e=>{let t=Dn(e),n=!!t&&t.name==="get_weather"&&/tokyo/.test(Wo(t.args));return{score:n?100:t?.name==="get_weather"?50:0,passed:n}}},{id:"instruction:format",category:"instruction",numPredict:16,messages:[{role:"user",content:"Respond with exactly this and nothing else: PONG"}],gradeProgrammatic:e=>{let t=Q(e);return t==="PONG"?{score:100,passed:!0}:{score:/\bPONG\b/.test(t)?50:0,passed:!1}}},{id:"instruction:length_constraint",category:"instruction",numPredict:16,messages:[{role:"user",content:"In exactly one word, what color is a clear daytime sky? Reply with only that one word, no punctuation."}],gradeProgrammatic:e=>{let t=Q(e).replace(/[.!?,]/g,"").trim(),o=t.split(/\s+/).filter(Boolean).length===1,r=/blue/i.test(t);return{score:o&&r?100:r?60:o?40:0,passed:o&&r}}},{id:"instruction:refusal_shape",category:"instruction",numPredict:32,messages:[{role:"system",content:"You are a calculator that ONLY performs arithmetic. If the user asks for anything that is not an arithmetic calculation, reply with exactly the single word: REFUSED"},{role:"user",content:"What is the capital of France?"}],gradeProgrammatic:e=>{let t=Q(e);return t==="REFUSED"?{score:100,passed:!0}:{score:/REFUSED/i.test(t)?60:0,passed:!1}}},{id:"coding:implement",category:"coding",numPredict:512,messages:[{role:"user",content:"Write a JavaScript function named add that takes two numbers and returns their sum. Output ONLY the function definition as code, with no explanation and no markdown code fences."}],gradeProgrammatic:e=>{let t=Ko(Q(e),["add(2,3)","add(-4,9)","add(0,0)"],[5,5,0]);return{score:t.score,passed:t.passed}}},{id:"coding:bugfix",category:"coding",numPredict:512,messages:[{role:"user",content:`The following JavaScript function should return true when n is even, but it is buggy:
|
|
186
193
|
|
|
187
194
|
function isEven(n) { return n % 2 === 1; }
|
|
188
195
|
|
|
189
|
-
Return a corrected version named isEven. Output ONLY the function definition as code, no explanation, no markdown fences.`}],gradeProgrammatic:e=>{let t=
|
|
196
|
+
Return a corrected version named isEven. Output ONLY the function definition as code, no explanation, no markdown fences.`}],gradeProgrammatic:e=>{let t=Ko(Q(e),["isEven(2)","isEven(3)","isEven(0)","isEven(7)"],[!0,!1,!0,!1]);return{score:t.score,passed:t.passed}}},{id:"extraction:json",category:"summarization",numPredict:128,messages:[{role:"user",content:`Extract the person's name and email from this text and return ONLY a JSON object with keys "name" and "email":
|
|
190
197
|
|
|
191
|
-
"Please reach out to Jane Doe at jane.doe@example.com regarding the invoice."`}],gradeProgrammatic:e=>{let t=
|
|
198
|
+
"Please reach out to Jane Doe at jane.doe@example.com regarding the invoice."`}],gradeProgrammatic:e=>{let t=eu(Q(e));if(!t)return{score:0,passed:!1};let n=String(t.name??"").toLowerCase(),o=String(t.email??"").toLowerCase(),r=0;return n.includes("jane")&&n.includes("doe")&&(r+=50),o==="jane.doe@example.com"&&(r+=50),{score:r,passed:r===100}}},{id:"summarization:two_sentence",category:"summarization",numPredict:256,messages:[{role:"user",content:`Summarize the following text in exactly two sentences. Be faithful \u2014 do not add information.
|
|
192
199
|
|
|
193
|
-
`+
|
|
200
|
+
`+Ha}],gradeProgrammatic:e=>{let t=Q(e).toLowerCase(),o=Fa.filter(i=>t.includes(i)).length/Fa.length*100,r=Ua(Q(e)),s=r>=1&&r<=3;return{score:Math.round(s?o:o*.7),passed:o>=60&&s}},judge:{dimension:"faithfulness",source:Ha,instruction:"Score 0\u2013100 how faithful the SUMMARY is to the SOURCE: 100 = every claim is supported by the source and nothing is invented; 0 = contradicts or fabricates. Also judge whether it is a reasonable 2-sentence summary.",getOutput:e=>Q(e),combine:(e,t)=>Math.round(((e??0)+t)/2)}},{id:"chat:helpfulness",category:"chat",numPredict:128,messages:[{role:"user",content:'A user says: "My home WiFi disconnects every few minutes." In one sentence, give one specific first troubleshooting step.'}],gradeProgrammatic:e=>{let t=Q(e);return{score:null,passed:t.length>0&&!tu.test(t)&&Ua(t)<=3}},judge:{dimension:"helpfulness",instruction:"Score 0\u2013100 how helpful and specific this single-step WiFi troubleshooting reply is: 100 = a concrete, correct, actionable first step in ~one sentence; 0 = vague, wrong, or off-topic.",getOutput:e=>Q(e),combine:(e,t)=>t}}];var Ht=class extends Error{constructor(t){super(t),this.name="JudgeUnavailableError"}};function Qo(){return process.env.BENCHMARK_JUDGE_MODEL?.trim()||"claude-opus-4-8"}function Nn(){return!!(process.env.ANTHROPIC_API_KEY||process.env.ANTHROPIC_AUTH_TOKEN)}var zo=null;function nu(){if(!Nn())throw new Ht("LLM-judge needs ANTHROPIC_API_KEY (set it for official runs, or pass --no-judge).");if(!zo){let e=require("@anthropic-ai/sdk"),t=e.default??e;zo=new t}return zo}var ou={type:"object",properties:{score:{type:"integer",description:"0 (worst) to 100 (best)"},passed:{type:"boolean"},reason:{type:"string",description:"one short sentence"}},required:["score","passed","reason"],additionalProperties:!1};function ru(e){return Number.isFinite(e)?Math.max(0,Math.min(100,Math.round(e))):0}function su(e){let t=null;try{t=JSON.parse(e)}catch{let n=e.match(/\{[\s\S]*\}/);if(n)try{t=JSON.parse(n[0])}catch{t=null}}if(!t)throw new Error(`judge returned non-JSON: ${e.slice(0,120)}`);return{score:ru(Number(t.score)),passed:!!t.passed,reason:String(t.reason??"").slice(0,300)}}async function Ga(e){if(e.impl)return e.impl(e);let t=nu(),n=[`You are an impartial grader. Dimension: ${e.dimension}.`,e.instruction,e.source?`
|
|
194
201
|
SOURCE:
|
|
195
202
|
${e.source}`:"",`
|
|
196
203
|
OUTPUT TO GRADE:
|
|
197
204
|
${e.output}`,`
|
|
198
|
-
Return a JSON object with keys "score" (integer 0-100), "passed" (boolean), and "reason" (one short sentence). Output only the JSON.`].filter(Boolean),r=(await t.messages.create({model:
|
|
199
|
-
`)}],output_config:{effort:"low",format:{type:"json_schema",schema:
|
|
200
|
-
`),e}async function
|
|
201
|
-
`+
|
|
205
|
+
Return a JSON object with keys "score" (integer 0-100), "passed" (boolean), and "reason" (one short sentence). Output only the JSON.`].filter(Boolean),r=(await t.messages.create({model:Qo(),max_tokens:1024,messages:[{role:"user",content:n.join(`
|
|
206
|
+
`)}],output_config:{effort:"low",format:{type:"json_schema",schema:ou}}})).content.find(s=>s.type==="text");if(!r?.text)throw new Error("judge returned no text block");return su(r.text)}var Xo="badgerclaw.benchmark.v1";function au(){let e={tool_call:0,instruction:0,coding:0,summarization:0,chat:0};for(let t of Jo)e[t.category]++;return e}async function iu(e,t,n){let o=t.log??(()=>{}),r=t.chatImpl??(u=>Ge(u,{host:t.host,fetchImpl:t.fetchImpl})),s;try{o(` task ${e.id}\u2026`),s=await r({model:t.model,messages:e.messages,options:Fe(t.numCtx,e.numPredict),tools:e.tools})}catch(u){return o(` task ${e.id} chat failed: ${u.message}`),{id:e.id,category:e.category,score:0,weight:n,passed:!1}}let a=e.gradeProgrammatic(s),i=a.score,c=!1,l=a.passed;if(t.useJudge&&e.judge){let u=a.score===null&&a.passed===!1;try{let p=await Ga({dimension:e.judge.dimension,instruction:e.judge.instruction,source:e.judge.source,output:e.judge.getOutput(s),impl:t.judgeImpl});c=!0;let _=e.judge.combine??((R,f)=>f);i=u?0:_(a.score,p.score),l=i>=60}catch(p){p instanceof Ht?o(` judge unavailable for ${e.id} \u2014 using programmatic score only`):o(` judge error for ${e.id}: ${p.message}`),i=u?0:a.score}}return{id:e.id,category:e.category,score:i,weight:n,passed:l,judged:c}}async function qa(e){let t=au(),n=[];for(let l of Jo){let u=ne(Yo[l.category]/t[l.category],4);n.push(await iu(l,e,u))}let o={},r=new Map;for(let l of n){if(l.score==null)continue;let u=r.get(l.category)??[];u.push(l.score),r.set(l.category,u)}for(let[l,u]of r)o[l]=ne(u.reduce((p,_)=>p+_,0)/u.length,1);let s=0,a=0;for(let l of Object.keys(o)){let u=Yo[l];s+=o[l]*u,a+=u}let i=a>0?ne(s/a,1):null,c={categories:o,tasks:n,suite_version:"v1",judged:n.some(l=>l.judged)};return{overall_score:i,quality_json:c}}var jn=require("fs"),Wa=m(require("path")),Ya=m(require("axios"));function lu(e,t){let n=e??0,o=t??0;return n>=20&&o>=75?"great":n>=8&&o>=60?"usable":n>=4?"marginal":"wont_run"}function cu(e,t){if(t==null)return e;let n=["wont_run","marginal","usable","great"],o=r=>{let s=n.indexOf(e),a=n.indexOf(r);return s>a?r:e};return t>8e3?o("marginal"):t>3e3?o("usable"):e}function Va(e){return e.status==="oom"||e.status==="skipped"||e.maxContext!=null&&e.maxContext<8192||e.peakRamMb!=null&&e.ramTotalGb!=null&&e.peakRamMb>e.ramTotalGb*1024-1024?"wont_run":e.decodeTps==null?"untested":cu(lu(e.decodeTps,e.qualityScore),e.ttftMs)}function Ka(e,t){return!t||e.decode_tps_median==null?"error":e.decode_tps_median<4?"too_slow":"ok"}function er(e){let{specs:t,perf:n,quality:o,model:r}=e,s=n.decode_tps_median!=null,a=e.statusOverride??Ka(n,s);return{runtime:e.runtime,model_name:r.model_name,model_size_total:r.model_size_total,model_size_active:r.model_size_active,quant:r.quant,inference_engine:"ollama",engine_version:r.engine_version,num_ctx:n.num_ctx,num_gpu_layers:r.num_gpu_layers??null,source:e.official?"official":"community",machine_fingerprint:t.machine_fingerprint,submitted_by:e.official?e.submittedBy??null:null,ram_total_gb:t.ram_total_gb,ram_tier:t.ram_tier,cpu_model:t.cpu_model,core_count:t.core_count,accelerator:t.accelerator,vram_gb:t.vram_gb,unified_memory:t.unified_memory,os:t.os,arch:t.arch,decode_tps_median:n.decode_tps_median,decode_tps_p95:n.decode_tps_p95,prefill_tps_median:n.prefill_tps_median,ttft_ms_median:n.ttft_ms_median,ttft_ms_p95:n.ttft_ms_p95,peak_ram_mb:n.peak_ram_mb,load_ms:n.load_ms,max_context:n.max_context,status:a,overall_score:o?.overall_score??null,quality_json:o?.quality_json??null,quality_suite_version:o?"v1":null,thermal_throttled:null,on_battery:e.onBattery??null,concurrent_load:null,confidence:e.official?"high":"community",benchmark_schema_version:Xo,cli_version:e.cliVersion,harness_version:Xo,started_at:e.startedAt,finished_at:e.finishedAt}}function Ja(e,t){let n=Wa.default.dirname(e);return n&&(0,jn.mkdirSync)(n,{recursive:!0}),(0,jn.writeFileSync)(e,JSON.stringify(t,null,2)+`
|
|
207
|
+
`),e}async function za(e){let t={"Content-Type":"application/json","X-Benchmark-Webhook-Secret":e.secret};e.officialToken&&(t["X-Benchmark-Official-Token"]=e.officialToken);try{let n=await Ya.default.post(e.webhookUrl,e.report,{headers:t,timeout:15e3,validateStatus:()=>!0}),o=n.status>=200&&n.status<300;return{ok:o,status:n.status,body:o?n.data:void 0,error:o?void 0:typeof n.data=="object"?JSON.stringify(n.data):String(n.data)}}catch(n){return{ok:!1,error:n.message}}}function Qa(e){return`${e.replace(/\/$/,"")}/api/v1/admin/benchmark-runs`}var Xa=8;function uu(e,t){return t.push(e),t}function Za(){try{return _e().version}catch{return"0.0.0"}}function du(e){return{decode_tps_median:null,decode_tps_p95:null,prefill_tps_median:null,ttft_ms_median:null,ttft_ms_p95:null,peak_ram_mb:null,load_ms:null,max_context:null,num_ctx:e}}var ni=new ei.Command("benchmark").description("Benchmark local Ollama models (perf + quality) and optionally publish results").option("-m, --model <tag>","Ollama model tag to benchmark (repeatable)",uu,[]).option("--all","benchmark every model recommended for this machine (CPU-aware set)",!1).option("--force","with --model, benchmark a named tag even if too big for CPU-only hardware",!1).option("--num-ctx <n>","context window for the perf/quality trials","8192").option("--trials <n>","measured warm trials (>=7 for official)","7").option("--ram-total-gb <n>","record this RAM tier instead of the host's physical RAM (for memory-constrained tier sweeps); constrain memory externally for faithful numbers").option("--runtime <runtime>","runtime the local LLM targets: hermes | openclaw","hermes").option("--official","publish as an OFFICIAL run (needs BENCHMARK_OFFICIAL_TOKEN)",!1).option("--share-results","publish as a community run (opt-in telemetry)",!1).option("--no-post","never POST \u2014 only write report.json").option("--no-judge","skip the LLM-judge even on official runs").option("--max-context","run the slow max-context staircase probe",!1).option("--output <dir>","directory for report.json files","./benchmark-reports").option("--api-base <url>","API base URL (default: env BADGERCLAW_API_URL or prod)").option("--submitted-by <id>","team identity recorded on official runs (e.g. team:fahed)").action(async e=>{let t=e.apiBase||N,n=Number.parseInt(e.numCtx,10)||8192,o=Number.parseInt(e.trials,10)||7,r=e.runtime==="openclaw"?"openclaw":"hermes",s=!!e.official,a=x=>console.log(I.default.gray(x)),i=it();if(!i.installed||!i.serving){console.error(I.default.red(Xs(i))),process.exitCode=1;return}let c=e.ramTotalGb!=null?Number.parseFloat(e.ramTotalGb):null,l=c!=null&&Number.isFinite(c)&&c>0?c:null,u=ia(l!=null?{ramTotalGbOverride:l}:{});l!=null&&console.log(I.default.yellow(`RAM override: recording tier ${u.ram_tier} (${u.ram_total_gb} GB) \u2014 ensure memory is constrained externally for a faithful run.`)),console.log(I.default.bold("Machine: ")+`${u.cpu_model??"unknown CPU"} \xB7 ${u.ram_total_gb} GB (${u.ram_tier}) \xB7 ${u.accelerator??"no accelerator"} \xB7 ${u.os}/${u.arch}`),console.log(I.default.gray(`fingerprint: ${u.machine_fingerprint}`));let p=!u.accelerator;p&&console.log(I.default.gray("no accelerator detected \u2014 treating this as a CPU-only machine."));let _=e.model;if(_.length===0)if(e.all){let x=await jt(En(),r,{apiBase:t,accelerator:u.accelerator});if(_=x?.recommended.map(qe=>qe.ollama_tag)??[],_.length===0){console.error(I.default.red(`No recommended models for this machine (${x?.note??"endpoint unreachable"}). Pass --model <tag>.`)),process.exitCode=1;return}console.log(I.default.gray(`--all: benchmarking the ${_.length} recommended models: ${_.join(", ")}`))}else{let x=await On({runtime:r,interactive:!1,ramGb:En(),apiBase:t,accelerator:u.accelerator});if(x.tag)_=[x.tag],console.log(I.default.gray(`no --model given; benchmarking the recommended pick: ${x.tag}`));else{console.error(I.default.red(`No model to benchmark (recommendation: ${x.note??"none"}). Pass --model <tag>.`)),process.exitCode=1;return}}let R=process.env.BENCHMARK_WEBHOOK_SECRET?.trim(),f=process.env.BENCHMARK_OFFICIAL_TOKEN?.trim(),b=process.env.BENCHMARK_WEBHOOK_URL?.trim()||Qa(t),g=s&&!!f,y=e.post!==!1&&(s||e.shareResults);y&&!R&&(console.log(I.default.yellow("BENCHMARK_WEBHOOK_SECRET not set \u2014 writing report.json locally but NOT posting.")),y=!1),s&&!f&&console.log(I.default.yellow("BENCHMARK_OFFICIAL_TOKEN not set \u2014 the server will record these as community runs."));let v=g&&e.judge!==!1&&Nn();g&&e.judge!==!1&&!Nn()&&console.log(I.default.yellow("ANTHROPIC_API_KEY not set \u2014 skipping the LLM-judge; chat + summarization scored programmatically only.")),v&&console.log(I.default.gray(`LLM-judge: ${Qo()}`)),y&&console.log(I.default.gray("Publishing: hardware class, model tags, numeric perf + quality scores, and a pseudonymous machine fingerprint. No hostname, account, or prompt text is sent.")),console.log(I.default.gray("Tip: do not run a live bot on this Ollama while benchmarking \u2014 it skews results."));let j=await Ea(),oe=await Pa(),re=async x=>{let qe=x.model_name.replace(/[^a-z0-9.]+/gi,"_"),ye=x.started_at.replace(/[:.]/g,"-"),ut=ti.default.join(e.output,`${qe}-${ye}.json`);if(Ja(ut,x),console.log(I.default.gray(`report: ${ut}`)),y&&R){let be=await za({webhookUrl:b,secret:R,officialToken:g?f:null,report:x});be.ok?console.log(I.default.green(` posted \u2192 id=${be.body?.id} (${be.body?.created?"created":"updated"}), source=${g?"official":"community"}`)):console.error(I.default.red(` webhook failed: ${be.status??""} ${be.error??""}`.trim()))}else console.log(I.default.gray(" (not posted \u2014 pass --official or --share-results to publish)"))};for(let x of _){if(console.log(`
|
|
208
|
+
`+I.default.bold.cyan(`\u25B6 ${x}`)),!oe.includes(x)){console.log(I.default.gray(`pulling ${x}\u2026`));let O=An(x);if(!O.ok){console.error(I.default.red(`pull failed: ${O.error}`));continue}}let qe="Q4_K_M",ye=null;try{let O=await Aa(x);O.details?.quantization_level&&(qe=O.details.quantization_level),ye=La(O.details?.parameter_size)}catch(O){console.log(I.default.gray(`ollama show failed (${O.message}); using defaults`))}if(p&&ye!=null&&ye>Xa&&!e.force){console.log(I.default.yellow(` skip: ${x} is ${ye}B \u2014 too slow to be usable on CPU-only hardware (>${Xa}B). Pass --force to benchmark it anyway.`));continue}let ut=new Date().toISOString(),be={model_name:x,quant:qe,model_size_total:ye,model_size_active:ye,engine_version:j};try{console.log(I.default.gray("perf suite\u2026"));let O=await Da({model:x,numCtx:n,warmTrials:o,perfOpts:{log:a}});e.maxContext&&(console.log(I.default.gray("max-context staircase\u2026")),O.max_context=await Na({model:x,perfOpts:{log:a}})),console.log(I.default.gray("quality suite\u2026"));let dt=await qa({model:x,numCtx:n,useJudge:v,log:a}),Ft=new Date().toISOString(),mt=er({specs:u,perf:O,quality:dt,model:be,runtime:r,official:g,submittedBy:e.submittedBy,cliVersion:Za(),startedAt:ut,finishedAt:Ft}),Gt=Va({status:mt.status,decodeTps:O.decode_tps_median,qualityScore:dt.overall_score,ttftMs:O.ttft_ms_median,peakRamMb:O.peak_ram_mb,ramTotalGb:u.ram_total_gb,maxContext:O.max_context});console.log(I.default.bold(` ${x}: `)+`decode ${O.decode_tps_median??"\u2014"} tok/s (p95 ${O.decode_tps_p95??"\u2014"}), ttft ${O.ttft_ms_median??"\u2014"} ms, peak ${O.peak_ram_mb??"\u2014"} MB, quality ${dt.overall_score??"\u2014"}, `+I.default.bold(`verdict ${Gt}`)+` (status ${mt.status})`),await re(mt)}catch(O){let dt=new Date().toISOString(),Ft=O?.message??String(O),mt=/memory|oom|killed|terminated|runner|HTTP 5\d\d|ECONNREFUSED|connection|fetch failed|aborted/i.test(Ft),Gt=l!=null||mt?"oom":"error";console.error(I.default.red(` ${x}: run failed \u2192 status ${Gt} (${Ft.slice(0,160)})`));let ri=er({specs:u,perf:du(n),quality:null,model:be,runtime:r,official:g,submittedBy:e.submittedBy,cliVersion:Za(),startedAt:ut,finishedAt:dt,statusOverride:Gt});await re(ri)}}});Ke();async function mu(){await Sr(process.argv);let e=new oi.Command;e.name("badgerclaw").description("BadgerClaw CLI \u2014 one-click bot provisioning").version("0.3.6"),e.addCommand(ks),e.addCommand(Cs),e.addCommand($s),e.addCommand(Ns),e.addCommand(Yr),e.addCommand(Us),e.addCommand(_a),e.addCommand(Ao),e.addCommand(Sa),e.addCommand(Ca),e.addCommand(xa),e.addCommand(ni),e.parse(process.argv)}mu().catch(e=>{console.error(e),process.exit(1)});
|