badgerclaw 0.3.6 → 0.3.8
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 +78 -74
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
`)}async function
|
|
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 fi=Object.create;var qt=Object.defineProperty;var hi=Object.getOwnPropertyDescriptor;var yi=Object.getOwnPropertyNames;var bi=Object.getPrototypeOf,wi=Object.prototype.hasOwnProperty;var K=(e,t)=>()=>(e&&(t=e(e=0)),t);var _i=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),qn=(e,t)=>{for(var n in t)qt(e,n,{get:t[n],enumerable:!0})},cr=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of yi(t))!wi.call(e,r)&&r!==n&&qt(e,r,{get:()=>t[r],enumerable:!(o=hi(t,r))||o.enumerable});return e};var m=(e,t,n)=>(n=e!=null?fi(bi(e)):{},cr(t||!e||!e.__esModule?qt(n,"default",{value:e,enumerable:!0}):n,e)),ur=e=>cr(qt({},"__esModule",{value:!0}),e);function I(){try{let e=ft.default.readFileSync(Wn,"utf-8");return JSON.parse(e)}catch{return null}}function Vt(e){ft.default.mkdirSync(gr,{recursive:!0}),ft.default.writeFileSync(Wn,JSON.stringify(e,null,2),{mode:384})}function ht(){try{ft.default.unlinkSync(Wn)}catch{}}function Kt(){let e=I();return e?new Date(e.expires_at)>new Date:!1}function Wt(e){let t=e.match(/^@?([^:]+)/);return t?t[1]:e}var ft,Kn,pr,gr,Wn,H=K(()=>{"use strict";ft=m(require("fs")),Kn=m(require("path")),pr=m(require("os")),gr=Kn.default.join(pr.default.homedir(),".badgerclaw"),Wn=Kn.default.join(gr,"auth.json")});function yt(){return fr.default.createHash("sha256").update(`${se.default.hostname()}-${se.default.platform()}-${se.default.arch()}`).digest("hex").slice(0,16)}function ae(){let e=I();if(e?.instance_id)return e.instance_id;let t=yt();return`openclaw-${se.default.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${t}`}function Jt(){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,fr,Oe=K(()=>{"use strict";se=m(require("os")),fr=m(require("crypto"));H()});var hr,N,yr,ie=K(()=>{"use strict";hr=process.env.BADGERCLAW_ENV==="local",N=process.env.BADGERCLAW_API_URL??(hr?"http://localhost:8000":"https://api.badger.signout.io"),yr=process.env.BADGERCLAW_AUTH_URL??(hr?"http://localhost:5500":"https://badgerclaw.ai")});function wr(){return bt}function vi(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 Jn(){let e=I();if(!e?.refresh_token||!e?.email)return bt=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=vi(e.access_token);try{let n=await wt.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 bt="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}),bt=null,r}catch(n){let o=n?.response?.status,r=n?.response?.data?.errors?.[0]||n?.response?.data?.detail||n?.message||"unknown error";bt=o?`HTTP ${o}: ${r}`:`network error: ${r}`;let s=n?.response?.data?.errors?.[0]||n?.response?.data?.detail||n?.message||"";if(o===403&&br.test(s))throw ht(),new Je(s);return null}}function Yn(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 _r(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(br.test(o))return ht(),Promise.reject(new Je(o))}return Promise.reject(t)}),e}function vr(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,Yt||(Yt=Jn().finally(()=>{Yt=null}));let o=await Yt;return o?(n.headers.Authorization=`Bearer ${o}`,e.request(n)):Promise.reject(t)}),e}function B(){let e=I(),t={"Content-Type":"application/json"};e&&(t.Authorization=`Bearer ${e.access_token}`);let n=wt.default.create({baseURL:zt,headers:t});return Yn(n),vr(n),_r(n),n}function Sr(){return Yn(wt.default.create({baseURL:zt,headers:{"Content-Type":"application/json"}}))}function _t(e){let t=wt.default.create({baseURL:zt,headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`}});return Yn(t),vr(t),_r(t),t}var wt,zt,Je,br,Yt,bt,Pe=K(()=>{"use strict";wt=m(require("axios"));H();ie();zt=N,Je=class extends Error{constructor(n){super(n);this.code="DEACTIVATED";this.name="DeactivatedError"}},br=/account\s+has\s+been\s+(deleted|deactivated)/i,Yt=null,bt=null});function xr(){try{let e=ve.default.statSync(Le);return Date.now()-e.mtimeMs>Rr}catch{return!0}}function zn(){try{return ve.default.mkdirSync(Qn.default.dirname(Le),{recursive:!0}),ve.default.writeFileSync(Le,String(process.pid),{flag:"wx"}),!0}catch{if(xr())try{return ve.default.unlinkSync(Le),ve.default.writeFileSync(Le,String(process.pid),{flag:"wx"}),!0}catch{return!1}return!1}}function $r(){try{ve.default.unlinkSync(Le)}catch{}}async function Ir(e){let t=!1;for(let n=0;n<Si;n++){if(zn()){t=!0;break}await new Promise(o=>setTimeout(o,Cr))}t||console.warn("[config-lock] Could not acquire lock after timeout \u2014 proceeding without lock");try{return await e()}finally{t&&$r()}}function Te(e){let t=zn();if(!t&&xr()){try{ve.default.unlinkSync(Le)}catch{}t=zn()}try{return e()}finally{t&&$r()}}var ve,kr,Qn,Le,Rr,Cr,Si,vt=K(()=>{"use strict";ve=m(require("fs")),kr=m(require("os")),Qn=m(require("path")),Le=Qn.default.join(kr.default.homedir(),".openclaw","openclaw.json.lock"),Rr=1e4,Cr=50,Si=Math.ceil(Rr/Cr)});var Se=_i((Fu,ki)=>{ki.exports={name:"badgerclaw",version:"0.3.8",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=Ii){try{return eo.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 Ai(){return[...Xt().map(t=>de.default.join(t,"node_modules/@badgerclaw/connect/package.json")),de.default.join($i,"package.json"),de.default.join(Ar,"node_modules/@badgerclaw/connect/package.json"),de.default.join(Ar,"package.json")]}function to(){return"0.3.8"}function Zt(){for(let e of Ai())try{let t=eo.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 Zn(e){let t=e.trim(),n=t.match(/\d+(?:\.\d+){1,3}/);return n?n[0]:t}function Y(){if((0,Xn.spawnSync)("which",["openclaw"],{encoding:"utf-8"}).status!==0)return null;let t=(0,Xn.spawnSync)("openclaw",["--version"],{encoding:"utf-8",timeout:3e3});return t.status!==0||!t.stdout?null:Zn(t.stdout)||null}function Ei(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 Er(e,t=Y()){return t?Ei(Zn(t),Zn(e))>=0:!1}async function Oi(){let e=new AbortController,t=setTimeout(()=>e.abort(),Ci);try{let n=await fetch(Ri,{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 Pi(e){let t=[];if(e.cli&&e.cli!=="unknown"){let o=to();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 Li(e){return e[2]}function Ti(e){return!e||e.startsWith("-")?!0:xi.has(e)}function Mi(e){let t=[];t.push(""),t.push(ke.default.red.bold("\u2717 Unsupported versions detected")),t.push("");for(let n of e){let o=n.component.padEnd(8);t.push(` ${o} ${ke.default.yellow(n.current)} \u2192 ${ke.default.green(n.approved)} ${ke.default.dim("(supported)")}`)}return t.push(""),t.push("This command is blocked until your installation matches the supported versions."),t.push(""),t.push(ke.default.green(" To fix, run:")),t.push(ke.default.green.bold(" badgerclaw setup")),t.push(""),t.push(ke.default.dim(" (bypass temporarily: BADGERCLAW_NO_VERSION_CHECK=1 badgerclaw <cmd>)")),t.push(""),t.join(`
|
|
3
|
+
`)}async function Or(e){if(process.env.BADGERCLAW_NO_VERSION_CHECK)return;let t=Li(e);if(Ti(t))return;let n=await Oi();if(!n)return;let o=Pi(n);o.length!==0&&(process.stderr.write(Mi(o)),process.exit(1))}var eo,Qt,de,Xn,ke,Ri,Ci,xi,Ar,$i,Ii,Ye=K(()=>{"use strict";eo=m(require("fs")),Qt=m(require("os")),de=m(require("path")),Xn=require("child_process"),ke=m(require("chalk"));ie();Ri=`${N}/api/v1/dashboard/versions/latest`,Ci=2500,xi=new Set(["setup","logout","help","--version","-V","--help","-h","heartbeat","watch","autopair","benchmark"]),Ar=de.default.join(Qt.default.homedir(),".openclaw/extensions/badgerclaw"),$i=de.default.join(Qt.default.homedir(),".openclaw/npm/node_modules/@badgerclaw/connect"),Ii=de.default.join(Qt.default.homedir(),".openclaw/npm/projects")});var Dr={};qn(Dr,{HERMES_IMAGE_DEFAULT:()=>ze,detectCapabilities:()=>Re,detectDiskFreeGb:()=>Mr,detectDocker:()=>en,detectHermes:()=>tn,detectOpenClaw:()=>Tr});function en(){if((0,St.spawnSync)("which",["docker"],{encoding:"utf-8"}).status!==0)return{installed:!1};let t=(0,St.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 Tr(){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||ze;return(0,St.spawnSync)("docker",["image","inspect","--format","{{.RepoDigests}}",t],{encoding:"utf-8",timeout:3e3}).status!==0?{installed:!1}:{installed:!0,image:t}}function Mr(){let e=Lr.default.join(no.default.homedir(),".badgerclaw"),t=Pr.default.existsSync(e)?e:no.default.homedir();if(process.platform==="win32")return null;let n=(0,St.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 Re(){let e=en();return{openclaw:Tr(),hermes:tn(e),docker:e,disk_free_gb:Mr(),ports_in_use:[]}}var St,Pr,no,Lr,ze,Me=K(()=>{"use strict";St=require("child_process"),Pr=m(require("fs")),no=m(require("os")),Lr=m(require("path"));Ye();ze="hermes-agent-bc:bundled"});function Fr(e){return e?ro:ji}function nn(e){return F.default.join(Di,`bot-${e}`)}function Gr(e){x.default.existsSync(le)||x.default.mkdirSync(le,{recursive:!0,mode:448}),x.default.writeFileSync(on,JSON.stringify(e,null,2)+`
|
|
5
|
+
`,{mode:384});try{x.default.rmSync(rn,{force:!0})}catch{}}function Nr(){if(!x.default.existsSync(on))return null;try{let e=JSON.parse(x.default.readFileSync(on,"utf-8"));return e&&e.provider==="ollama"?e:null}catch{return null}}function Bi(e){return e==="gemini"||e==="anthropic"||e==="openrouter"}function qr(e){x.default.existsSync(le)||x.default.mkdirSync(le,{recursive:!0,mode:448}),x.default.writeFileSync(rn,JSON.stringify(e,null,2)+`
|
|
6
|
+
`,{mode:384});try{x.default.rmSync(on,{force:!0})}catch{}}function Ui(){if(!x.default.existsSync(rn))return null;try{let e=JSON.parse(x.default.readFileSync(rn,"utf-8"));return e&&Bi(e.provider)&&typeof e.apiKey=="string"&&e.apiKey?{provider:e.provider,apiKey:e.apiKey,model:e.model||void 0}:null}catch{return null}}function so(e){return`badgerclaw-hermes-bot-${e}`}function me(){if(!x.default.existsSync(oo))return{hermes_bots:{}};try{let e=x.default.readFileSync(oo,"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 Vr(e){x.default.existsSync(le)||x.default.mkdirSync(le,{recursive:!0,mode:448}),x.default.writeFileSync(oo,JSON.stringify(e,null,2)+`
|
|
7
|
+
`,{mode:384})}function Hi(e,t=Ni){let n=new Set(Object.values(e.hermes_bots).map(r=>r.port)),o=t;for(;n.has(o)||!Fi(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 Fi(e){let t=Ur.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 Qe(e=process.env){let t=e.BADGERCLAW_LLM_PROVIDER?.toLowerCase(),n=e.BADGERCLAW_LLM_API_KEY;if(t==="ollama"){let a=Nr();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=Nr();if(o)return{provider:"ollama",apiKey:"",baseUrl:o.base_url||void 0,model:o.model||void 0};let r=Ui();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 Rt(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 Gi(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 qi(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||ro}`),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(`${Gi(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,Br.randomBytes)(24).toString("hex")}`),n.join(`
|
|
8
8
|
`)+`
|
|
9
|
-
`}function
|
|
10
|
-
`)}function
|
|
11
|
-
`)}function
|
|
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
|
|
13
|
-
${b.stderr}`)||void 0}
|
|
9
|
+
`}function Vi(e){let t=[`# Hermes agent config for BadgerClaw bot ${e.botId}.`,"# Generated by badgerclaw CLI; secrets live in the sibling .env file.",""],n=e.llm?.provider==="ollama";if(e.llm){let o=e.llm.model||Rt(e.llm.provider);t.push("model:",` default: ${o}`,` provider: ${e.llm.provider}`),n&&t.push(` base_url: ${e.llm.baseUrl||ro}`," ollama_num_ctx: 65536"),t.push("")}return t.push("agent:",` name: ${e.botName||"BadgerClaw bot"}`),n&&t.push(" reasoning_effort: none"),t.push("terminal:"," backend: local","subagents:"," max_spawn_depth: 1","cron:"," enabled: false",""),t.join(`
|
|
10
|
+
`)}function Ki(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 Kr(e,t){let n=nn(e.botId);x.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 x.default.writeFileSync(o,qi(e,t),{mode:384}),x.default.writeFileSync(r,Vi(e),{mode:384}),x.default.existsSync(s)||x.default.writeFileSync(s,Ki(e),{mode:384}),{dataDir:n,envPath:o,configPath:r,soulPath:s}}function Ct(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??(()=>Ct(kt)))()}function Wr(e){return!!e&&e.provider==="ollama"&&!e.baseUrl}function Jr(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 Yr(e,t={}){let n=t.docker??kt,o=e.image||process.env.HERMES_IMAGE||ze;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??Hi(r,t.portBase),i=s?.container_name??so(e.botId),c=Wr(e.llm),l=t.skipDocker?!1:sn({bridgeUsable:()=>Ct(n),localOllama:c});if(e.llm&&e.llm.provider==="ollama"&&!e.llm.baseUrl&&(e={...e,llm:{...e.llm,baseUrl:Fr(l)}}),Kr(e,a),!t.skipDocker){n.run(["rm","-f",i]);let p=(e.llm?.baseUrl??"").includes("host.docker.internal"),_=Jr({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,Vr(r),u}function Wi(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 Yi(e,t,n={}){let o=n.docker??kt,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(!x.default.existsSync(a))return{bot_id:e,rerendered:!1,restarted:!1,skipped:"no .env on disk"};let i=Wi(x.default.readFileSync(a,"utf-8")),c=F.default.join(s.data_dir,"config.yaml"),l=x.default.existsSync(c)?x.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=Wr(u.llm),_=n.skipDocker?!1:sn({bridgeUsable:()=>Ct(o),localOllama:p});u.llm&&u.llm.provider==="ollama"&&!u.llm.baseUrl&&(u={...u,llm:{...u.llm,baseUrl:Fr(_)}});let R=i.MATRIX_RECOVERY_KEY;if(!R&&!n.skipDocker){let b=o.run(["logs",s.container_name]);R=Qr(`${b.stdout}
|
|
13
|
+
${b.stderr}`)||void 0}Kr(u,s.port),R&&x.default.appendFileSync(a,`
|
|
14
14
|
# Cross-signing recovery key (preserved across re-provision).
|
|
15
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=
|
|
17
|
-
${a.stderr}`,c=
|
|
16
|
+
`);let f=!1;if(!n.skipDocker){o.run(["rm","-f",s.container_name]);let b=(u.llm?.baseUrl??"").includes("host.docker.internal"),g=Jr({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,Vr(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(Yi(o,e,t))}catch(r){n.push({bot_id:o,rerendered:!0,restarted:!1,skipped:`restart failed: ${r.message}`})}return n}function ao(e={}){let t=e.docker??kt,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 zr(e={}){return ao(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 Qr(e){let t=e.match(zi);return t?t[1].trim():null}async function io(e,t,n,o={}){let r={recoveryKey:null,posted:!1,envUpdated:!1};if(!e.backendBotId)return r;let a=(o.docker??kt).run(["logs",t]),i=`${a.stdout}
|
|
17
|
+
${a.stderr}`,c=Qr(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 x.default.existsSync(l)&&(x.default.readFileSync(l,"utf-8").includes("MATRIX_RECOVERY_KEY=")||(x.default.appendFileSync(l,`
|
|
18
18
|
# Cross-signing recovery key captured from first-boot logs.
|
|
19
19
|
# Hermes re-uses this on restart to re-sign the device after
|
|
20
20
|
# Synapse rotates server keys.
|
|
21
21
|
MATRIX_RECOVERY_KEY=${c}
|
|
22
|
-
`),r.envUpdated=!0)),r}async function
|
|
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(
|
|
24
|
-
\u26A1 ${s} bot(s) paired. Run: openclaw gateway restart`))}return s}catch{return 0}}var
|
|
22
|
+
`),r.envUpdated=!0)),r}async function Xr(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{x.default.existsSync(s)&&(a=x.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 io({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 jr,Br,x,Ur,Hr,F,le,Di,oo,Ni,ro,ji,kt,on,rn,zi,De=K(()=>{"use strict";jr=require("child_process"),Br=require("crypto"),x=m(require("fs")),Ur=m(require("net")),Hr=m(require("os")),F=m(require("path"));Me();le=F.default.join(Hr.default.homedir(),".badgerclaw"),Di=F.default.join(le,"hermes"),oo=F.default.join(le,"state.json"),Ni=8642,ro="http://127.0.0.1:11434/v1",ji="http://host.docker.internal:11434/v1";kt={run:e=>{let t=(0,jr.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");zi=/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 Qi(){if(Ce!==null)return Ce;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(xt.default.existsSync(t))return Ce=`${ln(process.execPath)} ${ln(t)}`,Ce;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=uo.default.join(n,"openclaw","openclaw.mjs");if(xt.default.existsSync(o))return Ce=`${ln(process.execPath)} ${ln(o)}`,Ce}}catch{}return Ce="openclaw",Ce}function lo(e,t,n){t?t.fail(Z.default.red(n)):e&&console.error(`[autopair] ${n}`)}async function Xi(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:Qe()},o=Yr(n);if(n.backendBotId){let r=I();if(r){let s=_t(r.access_token);try{await io(n,so(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 $t(e,t,n,o){let r=I();if(!r)return;let s=o?null:(0,co.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(Z.default.dim(`${t} already paired (race-loss; another channel redeemed first).`));return}let u=await a.json().catch(()=>({errors:[a.statusText]}));lo(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 Xi(c),s?.succeed(Z.default.green(`\u2705 ${c.bot_name} (${c.user_id}) running on Hermes \u2014 bot is live!`))}catch(u){lo(o,s,`Failed to start Hermes container for ${t}: ${u.message}`)}return}await Ir(async()=>{let u=c.user_id.split(":")[0].replace("@","").replace(/_bot$/,""),p;try{p=JSON.parse(xt.default.readFileSync(Zr,"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]=_,xt.default.writeFileSync(Zr,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(Z.default.green(`\u2705 ${c.bot_name} (${c.user_id}) paired \u2014 bot is live!`))}catch(a){lo(o,s,`Failed to pair ${t}: ${a.message}`)}}async function It(e=!1){let t=I();if(!t)return 0;let n=_t(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,co.default)(`Pairing bot: ${a.bot_name} (${a.bot_user_id})`).start();try{await $t(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(Z.default.red(`Error pairing ${a.bot_name}: ${c}`))}}if(s>0)try{let{execSync:a}=await import("child_process");a(`${Qi()} gateway restart`,{stdio:"ignore"}),e||console.log(Z.default.green(`
|
|
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(Z.default.yellow(`
|
|
24
|
+
\u26A1 ${s} bot(s) paired. Run: openclaw gateway restart`))}return s}catch{return 0}}var es,Z,co,ts,uo,xt,Zr,Ce,ns,At=K(()=>{"use strict";es=require("commander"),Z=m(require("chalk")),co=m(require("ora")),ts=m(require("os")),uo=m(require("path")),xt=m(require("fs"));H();Pe();ie();Oe();vt();De();Zr=uo.default.join(ts.default.homedir(),".openclaw","openclaw.json");Ce=null;ns=new es.Command("autopair").description("Check for pending bot pairs and connect them to OpenClaw automatically").action(async()=>{if(!I()){console.log(Z.default.yellow("Not logged in. Run `badgerclaw login` first."));return}console.log(Z.default.dim("Checking for pending bot pairs...")),await It(!1)===0&&console.log(Z.default.dim("No pending pairs found."))})});async function bo(){try{return await mn.default.get(yl,{timeout:2e3,validateStatus:()=>!0}),!0}catch{return!1}}async function Lt(){try{let t=(await mn.default.get(`${gs}/health`,{timeout:5e3})).data;return t.pluginVersion&&t.pluginVersion!=="unknown"&&(yo=t.pluginVersion),t}catch{return{status:await bo()?"running":"stopped",pid:null,lastRestart:null,pluginVersion:yo,bots:[]}}}function wo(e){e&&e!=="unknown"&&(yo=e)}async function ot(){try{return{success:!0,message:(await mn.default.post(`${gs}/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,fl,gs,hl,yl,yo,xe=K(()=>{"use strict";mn=m(require("axios")),fl=7331,gs=`http://localhost:${fl}`,hl=parseInt(process.env.OPENCLAW_GATEWAY_PORT||"",10)||18789,yl=`http://127.0.0.1:${hl}`,yo="unknown"});var Oo={};qn(Oo,{getActiveSessions:()=>Eo,launchClaudeCode:()=>$o,launchMCPServer:()=>xo,recordSession:()=>Io,stopSession:()=>Ao});function So(){try{if(G.default.existsSync(vo))return JSON.parse(G.default.readFileSync(vo,"utf-8"))}catch{}return[]}function ko(e){G.default.mkdirSync(Be,{recursive:!0}),G.default.writeFileSync(vo,JSON.stringify(e,null,2))}function hn(e){try{return process.kill(e,0),!0}catch{return!1}}function vl(){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 Ro(e,t){let n=z.default.join(Be,`claude-launcher-${t}.exp`),o=z.default.join(Be,`pending-input-${t}.txt`);G.default.writeFileSync(o,"","utf-8");let r=`#!/usr/bin/expect -f
|
|
25
25
|
set timeout -1
|
|
26
26
|
set msgfile "${o}"
|
|
27
27
|
|
|
@@ -58,7 +58,7 @@ send "\\r"
|
|
|
58
58
|
interact timeout 2 {
|
|
59
59
|
check_and_send_messages
|
|
60
60
|
}
|
|
61
|
-
`;return G.default.mkdirSync(
|
|
61
|
+
`;return G.default.mkdirSync(Be,{recursive:!0}),G.default.writeFileSync(n,r,{mode:493}),n}function Co(e){return Object.entries(e).map(([t,n])=>`export ${t}="${n}"`).join("; ")}function Sl(e,t,n){let o=Ro(e,n.BADGERCLAW_SESSION_ID||"default"),r=Co(n),s=`
|
|
62
62
|
tell application "iTerm"
|
|
63
63
|
activate
|
|
64
64
|
set newWindow to (create window with default profile)
|
|
@@ -66,13 +66,13 @@ interact timeout 2 {
|
|
|
66
66
|
write text "cd ${yn(t)} && ${r} && ${yn(o)}"
|
|
67
67
|
end tell
|
|
68
68
|
end tell
|
|
69
|
-
`;(0,pe.execSync)(`osascript -e '${s.replace(/'/g,`'"'"'`)}'`,{stdio:"pipe",timeout:1e4})}function
|
|
69
|
+
`;(0,pe.execSync)(`osascript -e '${s.replace(/'/g,`'"'"'`)}'`,{stdio:"pipe",timeout:1e4})}function kl(e,t,n){let o=Ro(e,n.BADGERCLAW_SESSION_ID||"default"),r=Co(n),a=`
|
|
70
70
|
tell application "Terminal"
|
|
71
71
|
activate
|
|
72
72
|
do script "${`cd ${yn(t)} && ${r} && ${yn(o)}`.replace(/"/g,'\\"')}"
|
|
73
73
|
end tell
|
|
74
|
-
`;(0,pe.execSync)(`osascript -e '${a.replace(/'/g,`'"'"'`)}'`,{stdio:"pipe",timeout:1e4})}function
|
|
75
|
-
`)}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 Rl(e,t,n){let o=Ro(e,n.BADGERCLAW_SESSION_ID||"default"),r=Co(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 xo(e){let t=z.default.join(__dirname,"..","claude-code","mcp-server.js"),n=G.default.existsSync(t)?t:z.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 $o(e){let t=z.default.join(z.default.dirname(process.argv[1]||""),"..","lib","node_modules","badgerclaw","dist","claude-code","mcp-server.js"),n=z.default.join(__dirname,"..","claude-code","mcp-server.js"),o=z.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=z.default.join(Be,`mcp-config-${e.sessionId}.json`);G.default.mkdirSync(Be,{recursive:!0}),G.default.writeFileSync(i,a,"utf-8");let c=`claude --dangerously-skip-permissions --mcp-config ${i}`;switch(vl()){case"iterm2":Sl(c,e.projectDir,s);break;case"terminal":kl(c,e.projectDir,s);break;case"tmux":Rl(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 Io(e,t,n,o,r){let s=So().filter(a=>a.sessionId!==e);s.push({sessionId:e,pid:t,mcpPid:n,port:o,projectDir:r,startedAt:new Date().toISOString()}),ko(s)}function Ao(e){let t=So(),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 ko(o),!0}function Eo(){let e=So(),t=e.filter(n=>hn(n.mcpPid)||hn(n.pid));return t.length!==e.length&&ko(t),t}var pe,G,z,ys,Be,vo,bn=K(()=>{"use strict";pe=require("child_process"),G=m(require("fs")),z=m(require("path")),ys=m(require("os")),Be=z.default.join(ys.default.homedir(),".badgerclaw"),vo=z.default.join(Be,"claude-sessions.json")});async function ge(e){return e.command_type==="update_cli"?xl(e.payload?.target_version):e.command_type==="update_plugin"?$l(e.payload?.target_version):e.command_type==="restart_gateway"?Cl():e.command_type==="leave_room"?Fl(e.payload):e.command_type==="start_claude_code"?Ll(e.payload):e.command_type==="stop_claude_code"?Tl(e.payload):e.command_type==="start_claude_control"?Ml(e.payload):e.command_type==="stop_claude_control"?Dl(e.payload):e.command_type==="update_claude_tools"?Nl(e.payload):e.command_type==="bot_share_notify"?jl(e.payload):e.command_type==="bot_share_revoked"?Bl(e.payload):e.command_type==="bot_share_expired"?Ul(e.payload):e.command_type==="bot_refresh"?Hl(e.payload):{success:!1,message:`Unknown command: ${e.command_type}`}}function Cl(){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 xl(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 Al(),{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 $l(e){let t=E.default.join(Ue.default.homedir(),".openclaw","extensions","badgerclaw"),n=E.default.join(Ue.default.homedir(),".openclaw","npm","node_modules","@badgerclaw","connect"),o=S.default.existsSync(n)?"npm":S.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";S.default.existsSync(p)&&(_=JSON.parse(S.default.readFileSync(p,"utf-8")).version||"unknown"),wo(_);try{(0,T.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:1e4})}catch{}return await bs(_)}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";S.default.existsSync(i)&&(c=JSON.parse(S.default.readFileSync(i,"utf-8")).version||"unknown"),wo(c),Il(a,s);let l=E.default.join(s,"package.json");if(S.default.existsSync(l)&&S.default.existsSync(i))try{let u=JSON.parse(S.default.readFileSync(l,"utf-8")),p=JSON.parse(S.default.readFileSync(i,"utf-8"));u.version=c,p.openclaw&&(u.openclaw={...u.openclaw,...p.openclaw}),S.default.writeFileSync(l,JSON.stringify(u,null,2)+`
|
|
75
|
+
`)}catch{}try{(0,T.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:1e4})}catch{}return await bs(c)}catch(s){return{success:!1,message:`Plugin update failed: ${(s.stderr?.toString()||s.message||"").slice(0,500)}`}}}async function bs(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 Il(e,t){let n=E.default.join(e,"dist"),o=E.default.join(t,"dist");S.default.existsSync(n)&&(S.default.mkdirSync(o,{recursive:!0}),Po(n,o));let r=E.default.join(e,"openclaw.plugin.json");S.default.existsSync(r)&&S.default.copyFileSync(r,E.default.join(t,"openclaw.plugin.json"));let s=E.default.join(e,"scripts"),a=E.default.join(t,"scripts");S.default.existsSync(s)&&(S.default.mkdirSync(a,{recursive:!0}),Po(s,a));for(let i of["index.ts","src","STREAMING.md","SETUP.md","CHANGELOG.md"]){let c=E.default.join(t,i);S.default.existsSync(c)&&(S.default.statSync(c).isDirectory()?S.default.rmSync(c,{recursive:!0,force:!0}):S.default.unlinkSync(c))}}function Po(e,t){for(let n of S.default.readdirSync(e,{withFileTypes:!0})){let o=E.default.join(e,n.name),r=E.default.join(t,n.name);n.isDirectory()?(S.default.mkdirSync(r,{recursive:!0}),Po(o,r)):S.default.copyFileSync(o,r)}}function Al(){try{if(El())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 El(){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 Ol(){let e=E.default.join(Ue.default.homedir(),".badgerclaw","workspace");S.default.mkdirSync(e,{recursive:!0});let t=E.default.join(e,".git");return S.default.existsSync(t)||(0,T.execSync)("git init",{cwd:e,stdio:"pipe",timeout:5e3}),S.default.writeFileSync(E.default.join(e,"CLAUDE.md"),`You are a chat assistant connected to Matrix rooms via BadgerClaw.
|
|
76
76
|
|
|
77
77
|
When you receive a message, it will be formatted like:
|
|
78
78
|
[Room: RoomName] Sender: message text
|
|
@@ -81,14 +81,14 @@ Reply using the reply tool with botId="..." and roomId="..."
|
|
|
81
81
|
ALWAYS use the \`reply\` MCP tool to send your response back to the chat room.
|
|
82
82
|
Use the exact botId and roomId provided in the message \u2014 do not modify them.
|
|
83
83
|
Keep responses concise and helpful.
|
|
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
|
|
86
|
-
[${new Date().toISOString()}] Received ${c} \u2014 posting disconnect and exiting.`)),
|
|
84
|
+
`),e}function Pl(){try{S.default.mkdirSync(E.default.dirname(wn),{recursive:!0});try{let e=S.default.statSync(wn);if(Date.now()-e.mtimeMs<6e4)return!1;S.default.unlinkSync(wn)}catch{}return S.default.writeFileSync(wn,String(process.pid),{flag:"wx"}),!0}catch{return!1}}async function Ll(e){if(!e?.bot_id)return{success:!1,message:"Missing bot_id in payload"};if(!Pl())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 Tt(e.bot_id),{launchClaudeCode:n,recordSession:o,getActiveSessions:r,stopSession:s}=(bn(),ur(Oo)),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=Ol();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 Tt(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(Ue.default.homedir(),".badgerclaw","bot-mapping.json");if(S.default.existsSync(o)){let r=JSON.parse(S.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 Tl(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 Tt(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(),ur(Oo));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 Ml(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 Tt(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 Dl(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 Tt(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 Nl(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 Tt(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 jl(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 Bl(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 Ul(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 Hl(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(Ue.default.homedir(),".openclaw","badgerclaw","quarantine",`${n}.json`);try{S.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 Fl(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,S,E,Ue,q,wn,Lo=K(()=>{"use strict";T=require("child_process"),S=m(require("fs")),E=m(require("path")),Ue=m(require("os")),q=m(require("axios"));xe();wn=E.default.join(Ue.default.homedir(),".badgerclaw","claude-code.lock")});var Mo={};qn(Mo,{heartbeatCommand:()=>To,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>Wl&&_n.delete(r);return _n.has(n)?!0:(_n.set(n,o),!1)}function Cs(e,t){let n=Jt(),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=zr().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:Re(),hermes_containers:ao(),bots:[...o,...s]}}async function U(){let{version:e}=Se(),t=await Lt(),n=Cs(t,e);await B().post(Rs,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 vs(){try{let n=(await B().get(Vl)).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 ks.default.post("http://localhost:7331/bot-share/sync",o,{timeout:1e4}),console.log(h.default.dim(` [${O()}] Share sync: ${n.length} active share(s) pushed to plugin`))}catch(e){console.log(h.default.dim(` [${O()}] Share sync failed (non-fatal): ${e.message}`))}}async function Yl(){if(!_s){_s=!0;try{await B().post(ql,{instance_id:ae()},{timeout:Kl}),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 xs(){let e=I();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(` [${O()}] Token expires soon \u2014 refreshing proactively...`)),await Jn())console.log(h.default.green(` [${O()}] Token refreshed successfully`));else{let s=wr()||"unknown reason";console.log(h.default.yellow(` [${O()}] Token refresh failed: ${s}`))}}function Ql(e,t){let n=3e3,o=3e4,r=null;async function s(){try{await xs();let i=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(` [${O()}] SSE connected \u2014 listening for real-time events`)),r&&clearTimeout(r),r=setTimeout(()=>{console.log(h.default.dim(` [${O()}] SSE proactive reconnect \u2014 refreshing token before expiry`)),l.close(),s()},zl)},l.onmessage=async u=>{try{let p=JSON.parse(u.data);await Xl(p)}catch{}},l.onerror=async()=>{l.close(),r&&(clearTimeout(r),r=null),console.log(h.default.dim(` [${O()}] 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(` [${O()}] SSE connection failed: ${a.message}`)),setTimeout(s,n),n=Math.min(n*1.5,o)}}s()}function O(){return new Date().toISOString()}async function Xl(e){if(e.type==="pair"){console.log(h.default.cyan(` [${O()}] Pair event: ${e.bot_name}`)),await $t(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(` [${O()}] 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(` [${O()}] 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(Te(()=>{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(` [${O()}] Removed "${n}" from openclaw.json`));let{execSync:c}=await import("child_process");try{c("openclaw gateway restart",{stdio:"ignore"}),console.log(h.default.green(` [${O()}] Gateway restarted`))}catch{}}}catch(o){console.log(h.default.red(` [${O()}] Failed to clean up bot: ${o}`))}try{await U()}catch{}}else if(e.type==="gateway-restart"){console.log(h.default.cyan(` [${O()}] Remote gateway-restart received`));let t=await ot();console.log(t.success?h.default.green(` [${O()}] Gateway restarted`):h.default.red(` [${O()}] 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(` [${O()}] Claude Code START: bot=${e.bot_id}`)),vn("start_claude_code",e.bot_id)){console.log(h.default.dim(` [${O()}] 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(` [${O()}] Claude Code STOP: bot=${e.bot_id}`)),vn("stop_claude_code",e.bot_id)){console.log(h.default.dim(` [${O()}] 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(` [${O()}] Command: ${e.command_type} (${e.command_id})`)),vn(e.command_type,e.payload?.bot_id)){console.log(h.default.dim(` [${O()}] 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(` [${O()}] CLI updated \u2014 restarting in 2s...`)),setTimeout(()=>process.exit(0),2e3))}catch{}try{await U()}catch{}}}var Ss,h,ks,ws,Gl,Rs,ql,Vl,Kl,_s,_n,Wl,To,zl,rt=K(()=>{"use strict";Ss=require("commander"),h=m(require("chalk")),ks=m(require("axios"));H();Pe();Oe();Me();De();xe();Lo();ie();At();xe();vt();Ye();ws=3e4,Gl=15e4,Rs="/api/v1/dashboard/heartbeat",ql="/api/v1/dashboard/disconnect",Vl="/api/v1/me/active-shares",Kl=3e3,_s=!1,_n=new Map,Wl=3e4;To=new Ss.Command("heartbeat").description("Run heartbeat daemon \u2014 reports telemetry to the BadgerClaw dashboard every 30s").action(async()=>{let e=I();e||(console.log(h.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let{version:t}=Se();console.log(h.default.green(`Heartbeat daemon started (v${t})`)),console.log(h.default.dim(` Instance: ${ae()}`)),console.log(h.default.dim(` Interval: ${ws/1e3}s`)),console.log(h.default.dim(` Press Ctrl+C to stop.
|
|
85
|
+
`));let n=null,o=10,r=0,s=async()=>{try{await xs();let c=await Lt(),l=Cs(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(Rs,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 Xr({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 Je&&(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 vs():console.log(h.default.dim(` [${O()}] Hermes-only host \u2014 share-sync disabled (no plugin to push to).`)),setInterval(s,ws),a&&setInterval(vs,Gl),Ql(e.access_token,t);let i=c=>{console.log(h.default.yellow(`
|
|
86
|
+
[${new Date().toISOString()}] Received ${c} \u2014 posting disconnect and exiting.`)),Yl().finally(()=>{process.exit(0)})};process.on("SIGTERM",i),process.on("SIGINT",i),await new Promise(()=>{})});zl=2700*1e3});var gi=require("commander");var $s=require("commander"),Q=m(require("chalk")),Is=m(require("ora")),As=m(require("open")),Do=m(require("os"));var Vn=m(require("crypto"));function dr(){return Vn.default.randomBytes(32).toString("base64url")}function mr(e){return Vn.default.createHash("sha256").update(e).digest("base64url")}H();Oe();Pe();ie();At();var un=m(require("os")),te=m(require("fs")),dn=m(require("path")),je=require("child_process");var Xe=m(require("fs")),Ne=m(require("path")),et=m(require("os")),tt=require("child_process"),os="ai.badgerclaw.watch",rs=Ne.default.join(et.default.homedir(),"Library","LaunchAgents"),Ze=Ne.default.join(rs,`${os}.plist`);function Zi(){try{return(0,tt.execSync)("which badgerclaw",{encoding:"utf-8"}).trim()}catch{return"/opt/homebrew/bin/badgerclaw"}}function el(){try{return(0,tt.execSync)("which node",{encoding:"utf-8"}).trim()}catch{return"/opt/homebrew/bin/node"}}function tl(e){let t=Ne.default.join(et.default.homedir(),".badgerclaw"),n=el(),o=Ne.default.dirname(n),r=Ne.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"?>
|
|
87
87
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
88
88
|
<plist version="1.0">
|
|
89
89
|
<dict>
|
|
90
90
|
<key>Label</key>
|
|
91
|
-
<string>${
|
|
91
|
+
<string>${os}</string>
|
|
92
92
|
<key>ProgramArguments</key>
|
|
93
93
|
<array>
|
|
94
94
|
<string>${n}</string>
|
|
@@ -111,7 +111,7 @@ Keep responses concise and helpful.
|
|
|
111
111
|
<key>ThrottleInterval</key>
|
|
112
112
|
<integer>30</integer>
|
|
113
113
|
</dict>
|
|
114
|
-
</plist>`}function
|
|
114
|
+
</plist>`}function ss(){if(et.default.platform()==="darwin")try{let e=Zi();Xe.default.mkdirSync(rs,{recursive:!0}),Xe.default.mkdirSync(Ne.default.join(et.default.homedir(),".badgerclaw"),{recursive:!0}),Xe.default.writeFileSync(Ze,tl(e));try{(0,tt.execSync)(`launchctl unload "${Ze}" 2>/dev/null`)}catch{}(0,tt.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 as(){if(et.default.platform()==="darwin")try{(0,tt.execSync)(`launchctl unload "${Ze}" 2>/dev/null`),Xe.default.existsSync(Ze)&&Xe.default.unlinkSync(Ze)}catch{}}var nt=m(require("fs")),Ot=m(require("path")),Et=m(require("os")),ee=require("child_process"),cn="badgerclaw-watch.service",is=Ot.default.join(Et.default.homedir(),".config","systemd","user"),mo=Ot.default.join(is,cn);function nl(){try{return(0,ee.execSync)("which badgerclaw",{encoding:"utf-8"}).trim()}catch{return"/usr/local/bin/badgerclaw"}}function ol(){try{return(0,ee.execSync)("which node",{encoding:"utf-8"}).trim()}catch{return"/usr/bin/node"}}function rl(e){let t=ol(),n=Ot.default.dirname(t);return`[Unit]
|
|
115
115
|
Description=BadgerClaw background watcher (heartbeat + pair events)
|
|
116
116
|
After=network-online.target
|
|
117
117
|
Wants=network-online.target
|
|
@@ -127,82 +127,86 @@ StandardError=append:%h/.badgerclaw/heartbeat.log
|
|
|
127
127
|
|
|
128
128
|
[Install]
|
|
129
129
|
WantedBy=default.target
|
|
130
|
-
`}function
|
|
131
|
-
\u26A0\uFE0F systemd lingering is OFF for root. The gateway will stop on SSH disconnect.`)),console.log(
|
|
132
|
-
`));for(let r of o){let s=
|
|
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."))}),
|
|
130
|
+
`}function ls(){try{return(0,ee.execSync)("systemctl --user --version",{stdio:"ignore"}),!0}catch{return!1}}function cs(){if(Et.default.platform()!=="linux"||!ls())return!1;try{let e=nl();nt.default.mkdirSync(is,{recursive:!0}),nt.default.mkdirSync(Ot.default.join(Et.default.homedir(),".badgerclaw"),{recursive:!0}),nt.default.writeFileSync(mo,rl(e)),(0,ee.execSync)("systemctl --user daemon-reload",{stdio:"ignore"});try{(0,ee.execSync)(`systemctl --user restart ${cn}`,{stdio:"ignore"})}catch{}(0,ee.execSync)(`systemctl --user enable --now ${cn}`,{stdio:"ignore"});try{let t=(0,ee.execSync)("whoami",{encoding:"utf-8"}).trim();(0,ee.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 us(){if(Et.default.platform()==="linux"&&ls()){try{(0,ee.execSync)(`systemctl --user disable --now ${cn}`,{stdio:"ignore"})}catch{}try{nt.default.existsSync(mo)&&nt.default.unlinkSync(mo),(0,ee.execSync)("systemctl --user daemon-reload",{stdio:"ignore"})}catch{}}}var po=dn.default.join(un.default.homedir(),".badgerclaw"),Pt=dn.default.join(po,"heartbeat.pid"),sl=dn.default.join(po,"heartbeat.log");function al(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 il(e){try{if(process.platform==="linux")return te.default.readFileSync(`/proc/${e}/cmdline`).toString("utf-8").split("\0").filter(Boolean);let t=(0,je.execSync)(`ps -o args= -p ${e}`,{encoding:"utf-8"}).trim();return t.length?t.split(/\s+/).filter(Boolean):null}catch{return null}}function go(e){if(!Number.isFinite(e)||e<=0||e===process.pid||e===process.ppid)return!1;let t=il(e);return t?al(t):!1}function fo(){let e="";try{e=(0,je.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=>go(t))}function ll(e){try{Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,Math.max(0,e))}catch{}}function cl(e){try{if(process.platform==="linux"){let r=te.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=te.default.readFileSync(`/proc/${s}/comm`,"utf-8").trim()}catch{}return{ppid:s,comm:a}}let t=parseInt((0,je.execSync)(`ps -o ppid= -p ${e}`,{encoding:"utf-8"}).trim(),10);if(!Number.isFinite(t))return null;let n="";try{n=(0,je.execSync)(`ps -o comm= -p ${t}`,{encoding:"utf-8"}).trim()}catch{}return{ppid:t,comm:n}}catch{return null}}var ul=/(systemd|supervisord|launchd|runsv|runit|s6-supervise|openrc)/i;function dl(){for(let e of fo()){let t=cl(e);if(t&&ul.test(t.comm))return e}return null}function ml(){try{let e=parseInt(te.default.readFileSync(Pt,"utf-8").trim(),10);if(Number.isFinite(e)&&e>0&&go(e))return!0}catch{}return ms(),fo().length>0}function ms(){try{let e=parseInt(te.default.readFileSync(Pt,"utf-8").trim(),10);if(Number.isFinite(e)&&e>0&&go(e))return}catch{}try{te.default.rmSync(Pt,{force:!0})}catch{}}function pl(e="SIGTERM"){let t=fo(),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;)ll(120),s=s.filter(o);for(let a of s)try{process.kill(a,"SIGKILL")}catch{}return n}function ds(){if(ml())return console.log("\x1B[2mHeartbeat already running.\x1B[0m"),!0;try{te.default.mkdirSync(po,{recursive:!0});let e=te.default.openSync(sl,"a"),t=(0,je.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{te.default.writeFileSync(Pt,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 ho(){let e=un.default.platform();if(e==="darwin"){ss();return}if(e==="linux"){if(cs())return;ds();return}ds()}function gl(){let e=un.default.platform();e==="darwin"?as():e==="linux"&&us(),pl();try{te.default.rmSync(Pt,{force:!0})}catch{}}function ps(e){let t=e.log||(()=>{}),n=dl();if(n!==null){ms(),t(`heartbeat already supervised (pid ${n}) \u2014 left in place; cleared any stale pid file.`);return}gl(),t("removed stale heartbeat hooks from previous installs."),e.loggedIn?(ho(),t("reinstalled a fresh heartbeat daemon for this host.")):t("run `badgerclaw login` to start the heartbeat for this host.")}var $e=m(require("fs")),_o=m(require("os")),pn=m(require("path")),ce=require("child_process"),W=m(require("chalk"));vt();xe();var fs=pn.default.join(_o.default.homedir(),".openclaw","openclaw.json");function bl(){return process.platform!=="linux"?!1:!!($e.default.existsSync("/.dockerenv")||(0,ce.spawnSync)("which",["systemctl"],{encoding:"utf-8"}).status!==0)}function hs(e=!1){let t=null;try{$e.default.existsSync(fs)&&(t=JSON.parse($e.default.readFileSync(fs,"utf-8"))?.gateway?.mode??null)}catch{}if(typeof t=="string"&&t.length>0)return!0;e||console.log(W.default.dim(" Setting gateway.mode=local..."));try{return Te(()=>((0,ce.execSync)("openclaw config set gateway.mode local",{encoding:"utf-8",timeout:1e4,stdio:"pipe"}),!0))}catch(n){return e||(console.log(W.default.yellow(` \u26A0\uFE0F Could not set gateway.mode automatically: ${n.message}`)),console.log(W.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(bl())return _l(t);n(W.default.dim(" Installing OpenClaw gateway service (if not present)...")),(0,ce.spawnSync)("openclaw",["gateway","install"],{stdio:t?"inherit":"pipe",shell:!0}).status!==0&&n(W.default.yellow(" \u26A0\uFE0F `openclaw gateway install` returned non-zero \u2014 continuing.")),hs(!t),n(W.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(W.default.dim(" Waiting for the gateway to start answering..."));let a=await wl(2e4);return a||n(W.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 wl(e,t=bo,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 _l(e){let t=l=>{e&&console.log(l)};t(W.default.dim(" Container mode (no systemd) \u2014 running gateway in foreground via nohup.")),hs(!e);let n=pn.default.join(_o.default.homedir(),".openclaw","logs");try{$e.default.mkdirSync(n,{recursive:!0})}catch{}let o=pn.default.join(n,"gateway-foreground.log"),r,s;try{r=$e.default.openSync(o,"a"),s=$e.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(W.default.dim(` Gateway PID: ${a.pid}, log: ${o}`));let i=Date.now()+2e4,c=!1;for(;Date.now()<i;){try{let l=$e.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(W.default.yellow(`
|
|
131
|
+
\u26A0\uFE0F systemd lingering is OFF for root. The gateway will stop on SSH disconnect.`)),console.log(W.default.dim(" Enable persistence: loginctl enable-linger root")))}Ye();var Zl=2e3,ec=12e4,Es=new $s.Command("login").description("Log in to BadgerClaw via browser").action(async()=>{let e=dr(),t=mr(e),n=`${yr}/cli-auth?code=${t}`;console.log(Q.default.yellow("Opening browser for authentication...")),console.log(Q.default.dim(`If the browser doesn't open, visit: ${n}`)),await(0,As.default)(n);let o=(0,Is.default)("Waiting for authentication...").start(),r=Sr(),s=Date.now();for(;Date.now()-s<ec;){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,_=yt(),R=`openclaw-${Do.default.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${_}`,f=R;try{let{version:g}=Se(),y=await r.post("/api/v1/openclaw/register",{instance_id:R,label:Do.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,C=j||re||`Login refused (status ${y}). Open the BadgerClaw app to check your subscription state.`;o.fail(Q.default.red(C)),process.exit(1)}}if(Vt({access_token:i,user_id:c,instance_id:f,expires_at:l,refresh_token:u,email:p}),o.succeed(Q.default.green(`Logged in as ${Wt(c)}`)),Y()!==null){let g=await gn({verbose:!0});g.ok?g.ready===!1&&console.log(Q.default.dim(" Gateway is still coming up \u2014 it should be online shortly.")):console.log(Q.default.yellow("\n \u26A0\uFE0F Logged in, but gateway failed to start. Re-run `badgerclaw setup` to repair."))}else console.log(Q.default.dim(" Hermes-only host \u2014 skipping OpenClaw gateway bring-up."));let b=await It(!0);b>0&&console.log(Q.default.green(`\u2705 ${b} bot(s) automatically paired to OpenClaw.`)),ho(),fn();try{let{pushHeartbeat:g}=await Promise.resolve().then(()=>(rt(),Mo));await g(),console.log(Q.default.dim(" Capabilities pushed \u2014 the machine should show online shortly."))}catch(g){console.log(Q.default.dim(` Capability push will retry via the heartbeat (${g?.message||g}).`))}return}}catch{}await new Promise(a=>setTimeout(a,Zl))}o.fail(Q.default.red("Authentication timed out. Please try again.")),process.exit(1)});var Ps=require("commander"),Mt=m(require("chalk")),Sn=m(require("fs")),st=m(require("os")),Nt=m(require("path")),Dt=require("child_process");H();Pe();function tc(){let e=process.platform;if(e==="darwin"){let t=Nt.default.join(st.default.homedir(),"Library","LaunchAgents","ai.badgerclaw.watch.plist");(0,Dt.spawnSync)("launchctl",["unload",t],{stdio:"ignore"});try{Sn.default.unlinkSync(t)}catch{}}else if(e==="linux"){(0,Dt.spawnSync)("systemctl",["--user","disable","--now","badgerclaw-watch.service"],{stdio:"ignore"});let t=Nt.default.join(st.default.homedir(),".config","systemd","user","badgerclaw-watch.service");try{Sn.default.unlinkSync(t)}catch{}(0,Dt.spawnSync)("systemctl",["--user","daemon-reload"],{stdio:"ignore"})}(0,Dt.spawnSync)("pkill",["-f","badgerclaw heartbeat"],{stdio:"ignore"})}function Os(e){try{return Sn.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{return!1}}var Ls=new Ps.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=I();if(t)try{let a=_t(t.access_token),{version:i}=Se();await a.post("/api/v1/openclaw/register",{instance_id:t.instance_id,label:st.default.hostname(),version:i,online:!1})}catch{}if(tc(),e.keepState){ht(),console.log(Mt.default.green("Logged out \u2014 auth cleared. (state preserved with --keep-state)"));return}let n=Nt.default.join(st.default.homedir(),".badgerclaw"),o=Os(n),r=Nt.default.join(st.default.homedir(),".openclaw","badgerclaw"),s=Os(r);o||s?(console.log(Mt.default.green("Logged out \u2014 local state cleared.")),console.log(Mt.default.dim(` Removed: ${n}${s?`, ${r}`:""}`))):console.log(Mt.default.green("Logged out."))});var Ts=require("commander"),No=m(require("chalk"));H();var Ms=new Ts.Command("status").description("Show connected instance info").action(async()=>{let e=I();(!e||!Kt())&&(console.log(No.default.red("Not logged in. Run `badgerclaw login` to authenticate.")),process.exit(1)),console.log(No.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 He=require("commander"),w=m(require("chalk")),jt=m(require("ora")),Cn=m(require("fs")),Hs=m(require("os")),Fs=m(require("path")),Gs=require("child_process");H();Pe();var at=m(require("fs")),Ds=m(require("os")),Ns=m(require("path")),js=require("child_process");vt();function kn(e){let t=e.split(":")[0].replace(/^@/,"").replace(/_bot$/,""),n=Ns.join(Ds.homedir(),".openclaw","openclaw.json");return at.existsSync(n)?{changed:Te(()=>{let r=JSON.parse(at.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&&at.writeFileSync(n,JSON.stringify(r,null,2)),s})}:{changed:!1}}function Rn(){try{return(0,js.execSync)("openclaw gateway restart",{stdio:"ignore"}),!0}catch{return!1}}Me();function Bs(e){return e.hermes?"hermes":"openclaw"}function Us(e){return e.openclaw.installed?"openclaw":e.hermes.installed?"hermes":"openclaw"}De();function Bo(){Kt()||(console.log(w.default.red("Not logged in. Run `badgerclaw login` to authenticate.")),process.exit(1))}function nc(e){return/^[a-z0-9_]{4,20}$/.test(e)}function qs(e){return e.replace(/_bot$/,"")}var jo=["openclaw","hermes"],oc=new He.Command("create").description("Create a new bot").argument("<name>","Bot name (4-20 chars, lowercase alphanumeric + underscores)").option("-r, --runtime <runtime>",`Bot runtime: ${jo.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)=>{Bo(),nc(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??Us(Re());jo.includes(n)||(console.log(w.default.red(`Invalid --runtime "${t.runtime}". Must be one of: ${jo.join(", ")}.`)),process.exit(1));let o=(0,jt.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)}}),rc=new He.Command("list").description("List your bots").action(async()=>{Bo();let e=(0,jt.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=qs(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)}}),sc=new He.Command("delete").description("Deactivate a bot").argument("<name>","Bot name to deactivate").action(async e=>{Bo();let t=(0,jt.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}=kn(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 ac(e){let t=e.trim();return t.startsWith("@")&&(t=t.slice(1)),t.includes(":")&&(t=t.split(":")[0]),qs(t)}function ic(e){let t=e.replace(/[^a-zA-Z0-9_.-]/g,"_")||"default";return Fs.default.join(Hs.default.homedir(),".openclaw","badgerclaw","quarantine",`${t}.json`)}var lc=new He.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=ac(e);t||(console.log(w.default.red(`Couldn't parse bot name "${e}".`)),process.exit(1));let n=ic(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,Gs.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."))}),cc=new He.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=Qe();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,jt.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
134
|
\u2705 ${s} bot(s) now running on ${n}.`):w.default.yellow(`
|
|
135
|
-
No bots were restarted \u2014 see the per-bot notes above.`))}),
|
|
136
|
-
\u{1F4F1} Pair event for ${l.bot_name} targets ${u} \u2014 not us, skipping`));return}console.log(
|
|
137
|
-
\u{1F4F1} Pair event received: ${l.bot_name}`)),await
|
|
138
|
-
\u{1F504} Remote gateway-restart command received`));let u=await
|
|
139
|
-
${_} Bot ${p}: ${u} \u2014 removing from OpenClaw config...`));try{let{changed:R}=
|
|
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?
|
|
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?
|
|
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?
|
|
143
|
-
`),n=[],o=!1,r=-1,s=!1,a=new Set(
|
|
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
|
|
146
|
-
Local models that fit ${e.ram_gb} GB (${e.ram_tier}):`),n.forEach((l,u)=>{r(` ${l===o?"*":" "} ${u+1}) ${
|
|
147
|
-
`),!0):!1}function
|
|
135
|
+
No bots were restarted \u2014 see the per-bot notes above.`))}),Vs=new He.Command("bot").description("Manage bots").addCommand(oc).addCommand(rc).addCommand(sc).addCommand(lc).addCommand(cc);At();var Ws=require("commander"),k=m(require("chalk"));H();ie();At();xe();rt();Lo();Pe();Oe();var Ks=3e3,uc=6e4,dc=2700*1e3,Js=new Ws.Command("watch").description("Watch for bot pair events in real-time (no polling)").action(async()=>{I()||(console.log(k.default.yellow("Not logged in.")),process.exit(1)),await It(!1),console.log(k.default.green("\u{1F534} Listening for pair events... (Ctrl+C to stop)"));let t=require("eventsource"),n=Ks,o=null,r=null,s=()=>{let a=I();a||(console.log(k.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=Ks,console.log(k.default.dim(" SSE connected \u2014 listening for events")),o&&clearTimeout(o),o=setTimeout(()=>{console.log(k.default.dim(" SSE proactive reconnect \u2014 refreshing token before expiry")),i.close(),s()},dc)},i.onerror=()=>{i.close(),o&&(clearTimeout(o),o=null),console.log(k.default.dim(` SSE disconnected \u2014 reconnecting in ${Math.floor(n/1e3)}s...`)),setTimeout(s,n),n=Math.min(n*1.5,uc)},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(k.default.dim(`
|
|
136
|
+
\u{1F4F1} Pair event for ${l.bot_name} targets ${u} \u2014 not us, skipping`));return}console.log(k.default.cyan(`
|
|
137
|
+
\u{1F4F1} Pair event received: ${l.bot_name}`)),await $t(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(k.default.green(" \u2705 Gateway restarted \u2014 bot is live!"))}catch{console.log(k.default.dim(" Gateway restart failed \u2014 run: openclaw gateway restart"))}}else if(l.type==="gateway-restart"){console.log(k.default.cyan(`
|
|
138
|
+
\u{1F504} Remote gateway-restart command received`));let u=await ot();u.success?console.log(k.default.green(` \u2705 Gateway restarted: ${u.message}`)):console.log(k.default.red(` \u274C Gateway restart failed: ${u.message}`));try{await U(),console.log(k.default.dim(" Heartbeat pushed with updated status."))}catch{console.log(k.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(k.default.yellow(`
|
|
139
|
+
${_} Bot ${p}: ${u} \u2014 removing from OpenClaw config...`));try{let{changed:R}=kn(u);R?(console.log(k.default.green(" \u2705 Removed from openclaw.json")),Rn()?console.log(k.default.green(" \u2705 Gateway restarted")):console.log(k.default.dim(" Gateway restart failed \u2014 restart manually"))):console.log(k.default.dim(" Account not found in openclaw.json \u2014 nothing to remove"))}catch(R){console.log(k.default.red(` Failed to update openclaw.json: ${R}`))}}else if(l.type==="claude_code.start"&&l.bot_id&&l.room_id){console.log(k.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?k.default.green(` \u2705 ${u.message}`):k.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(k.default.red(` Claude Code start error: ${u.message}`))}}else if(l.type==="claude_code.stop"&&l.bot_id&&l.room_id){console.log(k.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?k.default.green(` \u2705 ${u.message}`):k.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(k.default.red(` Claude Code stop error: ${u.message}`))}}else if(l.type==="command.execute"&&l.command_id){console.log(k.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?k.default.green(` \u2705 ${p.message}`):k.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(k.default.cyan(" CLI updated \u2014 restarting in 2s...")),setTimeout(()=>process.exit(0),2e3))}catch(u){console.log(k.default.red(` Command ${l.command_id} error: ${u.message}`))}}}catch{}}};s(),await new Promise(()=>{})});var ka=require("commander"),d=m(require("chalk")),ue=require("child_process"),L=m(require("fs")),Ft=m(require("os")),he=m(require("path")),Ra=m(require("readline"));ie();Ye();var Bt=m(require("fs")),it=m(require("path")),xn=require("child_process"),mc=["request","request-promise"];function pc(e){let t=e.match(/^(\s*)(?:"([^"]+)"|'([^']+)'|([^:#\s][^:]*?))\s*:/);return t?{indent:t[1].length,key:(t[2]??t[3]??t[4]??"").trim()}:null}function gc(e){let t=e.split(`
|
|
143
|
+
`),n=[],o=!1,r=-1,s=!1,a=new Set(mc);for(let i of t){let c=pc(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 fc(){let e=[],t=(0,xn.spawnSync)("npm",["root","-g"],{encoding:"utf-8",shell:!0});t.status===0&&t.stdout?.trim()&&e.push(it.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(it.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=it.default.dirname(it.default.dirname(o.stdout.trim()));e.push(it.default.join(r,"lib","node_modules","openclaw","pnpm-workspace.yaml"))}for(let r of e)try{if(Bt.default.existsSync(r))return r}catch{}return null}function Ys(){let e=fc();if(!e)return null;let t;try{t=Bt.default.readFileSync(e,"utf-8")}catch{return null}let n=gc(t);if(n===null)return null;try{Bt.default.writeFileSync(e,n)}catch{return null}return{yamlPath:e,original:t}}function zs(e){if(!e)return!1;try{return Bt.default.writeFileSync(e.yamlPath,e.original),!0}catch{return!1}}Me();var ct=require("child_process"),Xs=m(require("fs")),D=m(require("chalk")),J={alreadyUp:!1,install:null,start:null,requiresRoot:!1,networkHostRequired:!1,guidance:null},Qs='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 hc(e){return e.daemonUp?{...J,alreadyUp:!0}:e.host==="darwin"?e.hasDockerApp?{...J,start:"docker-app"}:e.hasColima?{...J,start:"colima"}:e.binaryPresent?{...J,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?{...J,install:{tool:"colima",via:"brew"},start:"colima"}:{...J,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?{...J,start:"systemctl",requiresRoot:!0}:e.pkgManager==="apt"?{...J,install:{tool:"docker",via:"apt"},start:"systemctl",requiresRoot:!0}:{...J,guidance:e.pkgManager?Qs:"Docker is not installed and no supported package manager (apt) was found. Install Docker, then re-run."}:e.host==="linux-nosystemd"?e.binaryPresent?{...J,start:"dockerd-restricted",requiresRoot:!0,networkHostRequired:!0}:e.pkgManager==="apt"?{...J,install:{tool:"docker",via:"apt"},start:"dockerd-restricted",requiresRoot:!0,networkHostRequired:!0}:{...J,guidance:e.pkgManager?Qs:"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."}:{...J,guidance:`Automatic Docker setup isn't supported on this platform (${process.platform}). Install + start Docker manually, then re-run.`}}function Zs(e,t,n=8e3){return(0,ct.spawnSync)(e,t,{encoding:"utf-8",timeout:n})}function Uo(){let e=Zs("docker",["version","--format","{{.Server.Version}}"],4e3);return e.status===0&&!!(e.stdout||"").trim()}function yc(){return Zs("docker",["--version"],4e3).status===0}function lt(e){return(0,ct.spawnSync)("sh",["-c",`command -v ${e}`],{encoding:"utf-8"}).status===0}function bc(){return lt("apt-get")?"apt":lt("dnf")?"dnf":lt("yum")?"yum":null}function wc(){let e=(0,ct.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 _c(){return process.platform==="darwin"?"darwin":process.platform==="linux"?wc()?"linux-systemd":"linux-nosystemd":"unsupported"}function vc(){return typeof process.getuid=="function"?process.getuid()===0:!1}function Ie(e,t){return t(D.default.dim(` $ ${e}`)),(0,ct.spawnSync)("sh",["-c",e],{stdio:"inherit"}).status===0}function Sc(e,t,n){let o=t?"sudo ":"";switch(e.via){case"brew":return n(D.default.dim(" Installing colima + docker CLI via Homebrew...")),Ie("brew install colima docker",n);case"apt":return n(D.default.dim(" Installing Docker via apt...")),Ie(`${o}apt-get update`,n),Ie(`${o}apt-get install -y docker.io`,n);default:return!1}}function kc(e,t,n){let o=t?"sudo ":"";switch(e){case"docker-app":return Ie("open -a Docker",n);case"colima":return Ie("colima start",n);case"systemctl":return Ie(`${o}systemctl enable --now docker`,n);case"dockerd-restricted":{if(!lt("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&&!Ie("sudo -v",n))return n(D.default.red(" \u274C root is required to start dockerd here, but sudo is unavailable.")),!1;Ie(`${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,ct.spawnSync)("sh",["-c",a],{stdio:"ignore"}).status===0}}}async function Rc(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 ea(e={}){let t=e.log??(a=>console.log(a));if(process.env.BADGERCLAW_NO_AUTO_DEPS==="1"){let a=Uo();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:Uo(),binaryPresent:yc(),host:_c(),hasBrew:lt("brew"),hasDockerApp:Xs.default.existsSync("/Applications/Docker.app"),hasColima:lt("colima"),pkgManager:bc(),isRoot:vc()},o=hc(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&&!Sc(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}):kc(o.start,r,t)?(t(D.default.dim(" Waiting for the Docker daemon to answer...")),await Rc(Uo,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 ta(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 na(e=process.versions.node){return parseInt(String(e).split(".")[0],10)}var Ho=require("child_process"),$n=m(require("fs")),oa=m(require("os")),In=m(require("path"));Me();De();function Cc(){let e=ra();for(let t of e)if($n.default.existsSync(t))return t;return null}function ra(){return[In.default.resolve(__dirname,"..","assets","hermes-agent-bc.Dockerfile"),In.default.resolve(__dirname,"..","..","assets","hermes-agent-bc.Dockerfile")]}var xc="nousresearch/hermes-agent";function $c(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`${xc}:${n}`}var Ic={run:e=>{let t=(0,Ho.spawnSync)("docker",e,{encoding:"utf-8"});return{status:t.status,stdout:t.stdout||"",stderr:t.stderr||""}}};function Ac(e){return["build",...e.hostNetwork?["--network","host"]:[],...e.upstreamRef?["--build-arg",`UPSTREAM=${e.upstreamRef}`]:[],"-t",e.image,"-f",e.dockerfile,e.ctxDir]}function sa(e){let t=en();if(!t.installed)return{status:"no-docker"};let n=process.env.HERMES_IMAGE||ze;if(tn(t).installed)return{status:"already-present",image:n};let r=Cc();if(!r)return{status:"no-dockerfile",expectedPaths:ra()};let s=$c(e),a=sn({bridgeUsable:()=>Ct(Ic)}),i=$n.default.mkdtempSync(In.default.join(oa.default.tmpdir(),"bc-hermes-build-"));try{let c=(0,Ho.spawnSync)("docker",Ac({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 Go=m(require("os")),dt=require("child_process"),Fo="https://ollama.com/download",Ec={run:(e,t)=>{let n=(0,dt.spawnSync)(e,t,{encoding:"utf-8",timeout:1e4});return{status:n.status,stdout:n.stdout||"",stderr:n.stderr||""}}};function ut(e={}){let t=e.runner??Ec,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 aa(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 ${Fo}, 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,dt.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 Oc(e,t){return t?t.run("sh",["-c",`command -v ${e}`]).status===0:(0,dt.spawnSync)("sh",["-c",`command -v ${e}`],{encoding:"utf-8"}).status===0}function Pc(e={}){let t=Go.default.platform();if(t!=="darwin"&&t!=="linux")return{ok:!1,error:`Automatic install isn't supported on ${t}. Install from ${Fo}.`};let n=e.log??(()=>{}),o=t==="darwin"&&Oc("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,dt.spawnSync)("sh",["-c",r],{stdio:"inherit"}),ut({runner:e.runner}).installed?{ok:!0}:{ok:!1,error:`Ollama still not detected after install \u2014 install it manually from ${Fo}.`}}function Lc(e={}){((r,s)=>e.runner?e.runner.run(r,s):(0,dt.spawnSync)(r,s,{stdio:"ignore",timeout:1e4}))("sh",["-c",Go.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 Tc=e=>new Promise(t=>setTimeout(t,e));async function ia(e){let t=e.log??(()=>{}),n=e.sleepImpl??Tc,o=ut({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=ut({runner:e.runner})}if(o.installed&&!o.serving){t(" Starting the Ollama server\u2026"),Lc({runner:e.runner});let r=e.pollAttempts??20;for(let s=0;s<r&&!o.serving;s++)await n(1e3),o=ut({runner:e.runner})}return{ready:o.installed&&o.serving,installed:o.installed,reason:o.installed&&o.serving?void 0:"ollama not serving"}}var ha=m(require("os")),ya=m(require("readline"));ie();De();var fe=m(require("os")),ua=require("child_process");Oe();var la=[1,2,4,6,8,12,16];function ca(e){if(e>16)return"16GB+";let t=la[0];for(let n of la)e>=n&&(t=n);return`${t}GB`}var da={run:(e,t)=>{let n=(0,ua.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 ma(e){return e==="x64"?"x86_64":e}function pa(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 ga(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 On(e={}){let t=e.runner??da,n=fe.default.platform(),o=ma(fe.default.arch()),r=pa(n,t);return ga(n,o,r,t).accelerator}function fa(e={}){let t=e.runner??da,n=fe.default.platform(),o=ma(fe.default.arch()),r=e.ramTotalGbOverride!=null&&Number.isFinite(e.ramTotalGbOverride)&&e.ramTotalGbOverride>0?e.ramTotalGbOverride:null,s=r??En(),a=pa(n,t),{accelerator:i,vram_gb:c,unified_memory:l}=ga(n,o,a,t);return{machine_fingerprint:yt(),ram_total_gb:r!=null?Math.round(r*10)/10:Math.round(fe.default.totalmem()/1024/1024/1024*10)/10,ram_tier:ca(s),cpu_model:a,core_count:fe.default.cpus().length||null,accelerator:i,vram_gb:c,unified_memory:l,os:n,arch:o}}function qo(){return Math.round(ha.default.totalmem()/1024/1024/1024)}async function Ut(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 Mc(e){let t=ya.default.createInterface({input:process.stdin,output:process.stdout});try{return await new Promise(n=>t.question(e,n))}finally{t.close()}}function Dc(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 Nc(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??Mc;r(`
|
|
146
|
+
Local models that fit ${e.ram_gb} GB (${e.ram_tier}):`),n.forEach((l,u)=>{r(` ${l===o?"*":" "} ${u+1}) ${Dc(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 Pn(e){let t=e.ramGb??qo(),n=e.accelerator===void 0?On():e.accelerator,o=await Ut(t,e.runtime,{apiBase:e.apiBase,fetchImpl:e.fetchImpl,accelerator:n});if(!o)return{tag:e.preferredTag||Rt("ollama"),note:"Could not reach the model-recommendation service \u2014 using a safe default model.",ramGb:t,fellBack:!0};let r=await Nc(o,{interactive:e.interactive,preferredTag:e.preferredTag,prompt:e.prompt,log:e.log});return{tag:r.tag,note:r.note,ramGb:t,fellBack:!1}}De();var ne=he.default.join(Ft.default.homedir(),".openclaw","openclaw.json"),Ht=ne+".badgerclaw-stash",Ca=he.default.join(Ft.default.homedir(),".openclaw","npm"),jc=he.default.join(Ca,"package.json");async function ba(){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 Bc(){if(!L.default.existsSync(ne))return null;try{let e=JSON.parse(L.default.readFileSync(ne,"utf-8")),t=e.channels?.badgerclaw;return t?(delete e.channels.badgerclaw,e.plugins?.entries?.badgerclaw&&delete e.plugins.entries.badgerclaw,L.default.writeFileSync(ne,JSON.stringify(e,null,2)),L.default.writeFileSync(Ht,JSON.stringify(t,null,2)),t):null}catch{return null}}function Uc(){if(!L.default.existsSync(ne))return null;try{let e=JSON.parse(L.default.readFileSync(ne,"utf-8"));return!e.gateway||typeof e.gateway!="object"?null:JSON.parse(JSON.stringify(e.gateway))}catch{return null}}function Hc(e){if(e&&L.default.existsSync(ne))try{let t=JSON.parse(L.default.readFileSync(ne,"utf-8"));t.gateway={...e,...t.gateway||{}},L.default.writeFileSync(ne,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 Fc(){if(L.default.existsSync(Ht))try{let e=JSON.parse(L.default.readFileSync(Ht,"utf-8")),t=JSON.parse(L.default.readFileSync(ne,"utf-8"));t.channels=t.channels||{},t.channels.badgerclaw=e,L.default.writeFileSync(ne,JSON.stringify(t,null,2)),L.default.unlinkSync(Ht)}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: ${Ht}`))}}async function wa(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 _a(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 Gc(){let e=[];_a(jc)&&e.push(Ca);for(let t of Xt())_a(he.default.join(t,"package.json"))&&e.push(t);return e}async function qc(){let{detectCapabilities:e}=await Promise.resolve().then(()=>(Me(),Dr)),{pushHeartbeat:t}=await Promise.resolve().then(()=>(rt(),Mo));console.log(d.default.green(`
|
|
148
148
|
\u{1F9A1} BadgerClaw \u2014 capability refresh
|
|
149
|
-
`));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 : ${Vo(n.openclaw)}`)),console.log(d.default.dim(` Hermes : ${Vo(n.hermes)}`)),console.log(d.default.dim(` Docker : ${Vo(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(`
|
|
150
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(`
|
|
151
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(`
|
|
152
|
-
\u274C Failed to push capabilities: ${r}`)),process.exit(1)}}function
|
|
152
|
+
\u274C Failed to push capabilities: ${r}`)),process.exit(1)}}function Vo(e){return e.installed?e.version?`installed (${e.version})`:e.image?`installed (${e.image})`:"installed":"not installed"}function xa(e=t=>console.log(t)){let t;try{t=Object.keys(me().hermes_bots)}catch{return}if(t.length===0)return;let n=Qe();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 va(e){console.log(d.default.green(`
|
|
153
153
|
\u{1F9E0} Local LLM (Ollama/Qwen) \u2014 RAM-aware model picker
|
|
154
|
-
`));let t=process.stdout.isTTY===!0&&process.stdin.isTTY===!0&&!e.assumeYes,n=process.env.BADGERCLAW_LLM_MODEL||void 0,o=await
|
|
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
|
|
154
|
+
`));let t=process.stdout.isTTY===!0&&process.stdin.isTTY===!0&&!e.assumeYes,n=process.env.BADGERCLAW_LLM_MODEL||void 0,o=await Pn({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 ia({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
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.`)),
|
|
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
|
|
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
|
|
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.`)),Gr({provider:"ollama",model:o.tag}),console.log(d.default.green(" \u2705 This host will use the local Ollama model for new Hermes bots.")),xa()}function Sa(e){return e===!0||(process.env.BADGERCLAW_LLM_PROVIDER||"").toLowerCase()==="ollama"}function Ln(e){let t=Ra.default.createInterface({input:process.stdin,output:process.stdout});return new Promise(n=>t.question(e,o=>{t.close(),n(o)}))}function $a(e){let t=process.stdout.isTTY===!0&&process.stdin.isTTY===!0;return(e.isInteractive??t)&&!e.assumeYes}async function Vc(e){if(!$a(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 Kc(e){if(!$a(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 Wc(e={}){let t=e.log??(s=>console.log(s)),n=e.ramGb??qo(),o=e.accelerator===void 0?On():e.accelerator;t(d.default.green(`
|
|
160
160
|
\u{1F9E0} Local LLM \u2014 RAM-aware recommendation
|
|
161
|
-
`)),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 Ut(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(`
|
|
162
162
|
Note: OpenClaw bots use a cloud LLM provider, so this host won't run a
|
|
163
163
|
local model on the OpenClaw runtime. To actually use one of these models,
|
|
164
|
-
re-run \`badgerclaw setup --hermes --local-llm\` (the Hermes runtime).`))}var
|
|
164
|
+
re-run \`badgerclaw setup --hermes --local-llm\` (the Hermes runtime).`))}var Ko=[{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(`
|
|
165
165
|
\u2601\uFE0F Cloud LLM provider
|
|
166
|
-
`)),
|
|
166
|
+
`)),Ko.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=Ko[Number.isInteger(s)&&s>=1&&s<=Ko.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=Rt(a.key);qr({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).")),xa(t)}var Ia=new ka.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 qc();return}if(console.log(d.default.green(`
|
|
167
167
|
\u{1F9A1} BadgerClaw Setup
|
|
168
|
-
`)),
|
|
169
|
-
\u274C ${f}`)),process.exit(1));let b=
|
|
170
|
-
`)),!b.docker.installed){console.log(d.default.dim(" Docker is not ready \u2014 bootstrapping it for the Hermes runtime..."));let g=await
|
|
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=
|
|
168
|
+
`)),Bs({hermes:e.hermes})==="hermes"){let f=ta({nodeMajor:na(),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=Re();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 ea({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=Re()}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 ba()).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=sa({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(`
|
|
172
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(`
|
|
173
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(`
|
|
174
|
-
\u274C Docker is not running.`)),process.exit(1))}if(
|
|
175
|
-
Cleaning up heartbeat hooks from previous installs...`)),
|
|
174
|
+
\u274C Docker is not running.`)),process.exit(1))}if(Sa(e.localLlm))await va({assumeYes:e.yes===!0});else{let g=await Kc({assumeYes:e.yes===!0});g==="local"?await va({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...`)),ps({loggedIn:I()!==null,log:g=>console.log(d.default.dim(` ${g}`))}),console.log(d.default.dim(`
|
|
176
176
|
Hermes-only mode \u2014 skipping OpenClaw plugin + gateway install.
|
|
177
177
|
To enable OC bots later, drop the --hermes flag: \`badgerclaw setup\`.
|
|
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
|
|
179
|
-
\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 ba();if(n.cli){let f=to();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=Bc();o&&console.log(d.default.dim(" Existing bot config stashed temporarily..."));let r=Uc();(0,ue.spawnSync)("openclaw",["gateway","stop"],{stdio:"pipe",shell:!0,timeout:1e4});let s=[he.default.join(Ft.default.homedir(),".openclaw","extensions","badgerclaw"),he.default.join(Ft.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;Er("2026.6.5")&&(u=Ys(),u&&console.log(d.default.dim(" Adjusted OpenClaw 2026.6.5 npm overrides for a full channel-plugin install.")));let p=await wa(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 wa(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&&(Fc(),console.log(d.default.dim(" Bot config restored."))),r&&(Hc(r),console.log(d.default.dim(" Gateway config restored."))),u&&zs(u),p.status!==0&&(console.log(d.default.red(`
|
|
180
180
|
\u274C Plugin install failed. Your bot config has been restored.`)),process.exit(1)),console.log(d.default.green(`
|
|
181
|
-
\u2705 BadgerClaw plugin installed successfully!`));let _=
|
|
181
|
+
\u2705 BadgerClaw plugin installed successfully!`));let _=Gc();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
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.")),(
|
|
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.")),(Sa(e.localLlm)||await Vc({assumeYes:e.yes===!0}))&&await Wc(),console.log(d.default.dim("\nNext: run `badgerclaw login` to authenticate.")),fn()});rt();var Ea=require("commander"),M=m(require("chalk"));H();Oe();xe();var Oa=new Ea.Command("dashboard").description("Show local diagnostic dashboard \u2014 machine health, gateway status, bot telemetry").action(async()=>{I()||(console.log(M.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let{version:t}=Se(),n=Jt(),o=await Lt();console.log(M.default.bold.green(`
|
|
184
184
|
BadgerClaw Dashboard
|
|
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: ${
|
|
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: ${Aa(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: ${Aa(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 Aa(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 Wo=require("commander"),mt=m(require("chalk")),Pa=m(require("ora"));H();xe();rt();var Yc=new Wo.Command("restart").description("Restart the OpenClaw gateway via the plugin probe endpoint").action(async()=>{I()||(console.log(mt.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let t=(0,Pa.default)("Restarting gateway...").start(),n=await ot();if(n.success){t.succeed(mt.default.green(`Gateway restarted: ${n.message}`));try{await U(),console.log(mt.default.dim(" Heartbeat pushed with updated status."))}catch{console.log(mt.default.dim(" Could not push heartbeat \u2014 daemon may not be running."))}}else t.fail(mt.default.red(`Gateway restart failed: ${n.message}`)),process.exit(1)}),La=new Wo.Command("gateway").description("Manage the OpenClaw gateway").addCommand(Yc);var Ee=require("commander"),A=m(require("chalk")),Ge=m(require("ora")),Ae=m(require("axios"));bn();H();var Fe="http://localhost:7331",zc=new Ee.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=>{I()||(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,Ge.default)("Starting Claude Code session...").start();try{await Ae.default.post(`${Fe}/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=xo({sessionId:n,port:o,projectDir:r}).pid||0;await new Promise(c=>setTimeout(c,1500)),s.text="MCP server running \u2014 launching Claude Code...",$o({sessionId:n,port:o,projectDir:r}),Io(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)}}),Qc=new Ee.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=>{I()||(console.log(A.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let n=(0,Ge.default)("Stopping Claude Code session...").start();try{e.bot&&e.room&&await Ae.default.post(`${Fe}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!1},{timeout:1e4});let o=e.sessionId;if(!o)try{let a=((await Ae.default.get(`${Fe}/claude-code/status`,{timeout:5e3})).data?.rooms||[]).find(i=>i.botId===e.bot&&i.roomId===e.room);a&&(o=a.sessionId)}catch{}o?(Ao(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)}}),Xc=new Ee.Command("status").description("Show active Claude Code sessions").action(async()=>{I()||(console.log(A.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let t=(0,Ge.default)("Fetching Claude Code status...").start();try{let n=[];try{n=(await Ae.default.get(`${Fe}/claude-code/status`,{timeout:5e3})).data?.rooms||[]}catch{}let o=Eo();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
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)}}),
|
|
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)}}),Zc=new Ee.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,Ge.default)("Grouping bot+room into session...").start();try{await Ae.default.post(`${Fe}/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)}}),eu=new Ee.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,Ge.default)("Adding user to allowlist...").start();try{await Ae.default.post(`${Fe}/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)}}),tu=new Ee.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,Ge.default)("Removing user from allowlist...").start();try{await Ae.default.post(`${Fe}/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)}}),Ta=new Ee.Command("claude-code").description("Manage Claude Code sessions (relay Matrix messages to local Claude Code)").addCommand(zc).addCommand(Qc).addCommand(Xc).addCommand(Zc).addCommand(eu).addCommand(tu);var di=require("commander"),$=m(require("chalk")),mi=m(require("path"));ie();var Ma=require("child_process"),nu="http://127.0.0.1:11434";function Jo(){let e=process.env.OLLAMA_HOST?.trim();return e?/^https?:\/\//.test(e)?e.replace(/\/$/,""):`http://${e}`.replace(/\/$/,""):nu}function ye(e,t){return{num_ctx:e,num_predict:t,temperature:0,seed:42,top_p:1,top_k:0}}async function Da(e,t,n={}){let o=n.host??Jo(),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 be(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),Da("/api/chat",n,t)}async function Na(e,t={}){return Da("/api/show",{model:e},{...t,timeoutMs:15e3})}async function ja(e={}){try{let t=e.host??Jo(),o=await(e.fetchImpl??fetch)(`${t}/api/version`);return o.ok?(await o.json()).version??null:null}catch{return null}}async function Ba(e={}){try{let t=e.host??Jo(),o=await(e.fetchImpl??fetch)(`${t}/api/tags`);return o.ok?((await o.json()).models??[]).map(s=>s.name):[]}catch{return[]}}function Ua(e){try{(0,Ma.spawnSync)("ollama",["stop",e],{encoding:"utf-8",timeout:15e3})}catch{}}function Ha(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 Ga=require("child_process");function V(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 Yo(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 ou="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)+`
|
|
188
188
|
|
|
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.`,
|
|
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
|
|
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.`,zo=class{constructor(t){this.timer=null;this.peakMb=0;this.sawAny=!1;this.runner=t??((n,o)=>{let r=(0,Ga.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 ru(e){return e.eval_count&&e.eval_duration?e.eval_count/(e.eval_duration/1e9):null}function su(e){return e.prompt_eval_count&&e.prompt_eval_duration?e.prompt_eval_count/(e.prompt_eval_duration/1e9):null}function au(e){return e.prompt_eval_duration?e.prompt_eval_duration/1e6:null}var Fa=[{role:"user",content:ou}];async function qa(e){let{model:t,numCtx:n,warmTrials:o}=e,r=e.warmupTrials??2,s=e.perfOpts??{},a=s.log??(()=>{}),i=e.sampler??new zo;Ua(t),a(" cold load trial\u2026");let c=null;try{let v=await be({model:t,messages:[{role:"user",content:"Reply with: OK"}],options:ye(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 be({model:t,messages:Fa,options:ye(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 be({model:t,messages:Fa,options:ye(n,128)},s),oe=ru(j),re=au(j),C=su(j);oe!=null&&l.push(oe),re!=null&&u.push(re),C!=null&&p.push(C)}}finally{_=i.stop()}let R=Tn(l),f=Yo(l,.95),b=Tn(u),g=Yo(u,.95),y=Tn(p);return{decode_tps_median:R!=null?V(R):null,decode_tps_p95:f!=null?V(f):null,prefill_tps_median:y!=null?V(y):null,ttft_ms_median:b!=null?V(b):null,ttft_ms_p95:g!=null?V(g):null,peak_ram_mb:_,load_ms:c!=null?V(c):null,max_context:null,num_ctx:n}}async function Va(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 be({model:t,messages:[{role:"user",content:"Reply with: OK"}],options:ye(a,8)},o);if(i.eval_count&&i.eval_count>0)s=a;else break}catch{break}return s}var Ka=require("child_process");function iu(e){let t=e.match(/```(?:javascript|js)?\s*([\s\S]*?)```/i);return t?t[1].trim():e.trim()}function Qo(e,t,n,o={}){let s=`${iu(e)}
|
|
191
191
|
const __out = [${t.join(", ")}];
|
|
192
|
-
process.stdout.write(JSON.stringify(__out));`,a=o.exec??(u=>(0,
|
|
192
|
+
process.stdout.write(JSON.stringify(__out));`,a=o.exec??(u=>(0,Ka.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++)Wa(i[u],n[u])&&c++;return{score:Math.round(c/n.length*100),passed:c===n.length,ran:!0}}function Wa(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)=>Wa(n,t[o])):!1}var Zo={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"]}}},lu={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"]}}},cu={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 X(e){return(e.message?.content??"").trim()}function Xo(e){return JSON.stringify(e).toLowerCase()}function uu(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 Ja(e){return e.split(/[.!?]+/).map(t=>t.trim()).filter(Boolean).length}var du=/\b(can'?t|cannot|won'?t|unable|not able|sorry|i'?m sorry)\b/i,Ya="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.",za=["voyager","1977","interstellar","golden record","nasa"],er=[{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(Xo(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,lu,cu],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(Xo(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(X(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(Xo(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=X(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=X(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=X(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=Qo(X(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:
|
|
193
193
|
|
|
194
194
|
function isEven(n) { return n % 2 === 1; }
|
|
195
195
|
|
|
196
|
-
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=Qo(X(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":
|
|
197
197
|
|
|
198
|
-
"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=uu(X(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.
|
|
199
199
|
|
|
200
|
-
`+
|
|
200
|
+
`+Ya}],gradeProgrammatic:e=>{let t=X(e).toLowerCase(),o=za.filter(i=>t.includes(i)).length/za.length*100,r=Ja(X(e)),s=r>=1&&r<=3;return{score:Math.round(s?o:o*.7),passed:o>=60&&s}},judge:{dimension:"faithfulness",source:Ya,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=>X(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=X(e);return{score:null,passed:t.length>0&&!du.test(t)&&Ja(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=>X(e),combine:(e,t)=>t}}];var qe=class extends Error{constructor(t){super(t),this.name="JudgeUnavailableError"}};function nr(){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 tr=null;function mu(){if(!Nn())throw new qe("LLM-judge needs ANTHROPIC_API_KEY (set it for official runs, or pass --no-judge).");if(!tr){let e=require("@anthropic-ai/sdk"),t=e.default??e;tr=new t}return tr}var pu={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 gu(e){return Number.isFinite(e)?Math.max(0,Math.min(100,Math.round(e))):0}function fu(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:gu(Number(t.score)),passed:!!t.passed,reason:String(t.reason??"").slice(0,300)}}async function jn(e){if(e.impl)return e.impl(e);let t=mu(),n=[`You are an impartial grader. Dimension: ${e.dimension}.`,e.instruction,e.source?`
|
|
201
201
|
SOURCE:
|
|
202
202
|
${e.source}`:"",`
|
|
203
203
|
OUTPUT TO GRADE:
|
|
204
204
|
${e.output}`,`
|
|
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:
|
|
206
|
-
`)}],output_config:{effort:"low",format:{type:"json_schema",schema:
|
|
207
|
-
|
|
208
|
-
|
|
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:nr(),max_tokens:1024,messages:[{role:"user",content:n.join(`
|
|
206
|
+
`)}],output_config:{effort:"low",format:{type:"json_schema",schema:pu}}})).content.find(s=>s.type==="text");if(!r?.text)throw new Error("judge returned no text block");return fu(r.text)}var or="badgerclaw.benchmark.v1";function hu(){let e={tool_call:0,instruction:0,coding:0,summarization:0,chat:0};for(let t of er)e[t.category]++;return e}async function yu(e,t,n){let o=t.log??(()=>{}),r=t.chatImpl??(u=>be(u,{host:t.host,fetchImpl:t.fetchImpl})),s;try{o(` task ${e.id}\u2026`),s=await r({model:t.model,messages:e.messages,options:ye(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 jn({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 qe?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=hu(),n=[];for(let l of er){let u=V(Zo[l.category]/t[l.category],4);n.push(await yu(l,e,u))}let o={},r=new Map;for(let l of n){if(l.score==null||l.category==="tool_reliability")continue;let u=r.get(l.category)??[];u.push(l.score),r.set(l.category,u)}for(let[l,u]of r)o[l]=V(u.reduce((p,_)=>p+_,0)/u.length,1);let s=0,a=0;for(let l of Object.keys(o)){let u=Zo[l];s+=o[l]*u,a+=u}let i=a>0?V(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 bu={type:"function",function:{name:"fetch_url",description:"Fetch the contents of a web page by URL.",parameters:{type:"object",properties:{url:{type:"string",description:"The URL to fetch"}},required:["url"]}}},rr={type:"function",function:{name:"write_file",description:"Write text content to a file on disk.",parameters:{type:"object",properties:{path:{type:"string",description:"Destination file path"},content:{type:"string",description:"The full text to write"}},required:["path","content"]}}},Xa={type:"function",function:{name:"run_terminal",description:"Run a shell command and return its output.",parameters:{type:"object",properties:{command:{type:"string",description:"The shell command to run"}},required:["command"]}}},Za={type:"function",function:{name:"get_weather",description:"Get the current weather for a location.",parameters:{type:"object",properties:{location:{type:"string"}},required:["location"]}}};function Gt(e){return(e.message?.tool_calls??[]).map(n=>{let o=n.function.arguments;if(typeof o=="string")try{o=JSON.parse(o)}catch{o={}}return{name:n.function.name,args:o||{}}})}function ar(e){return(e.message?.content??"").trim()}function sr(e,t){let n=e[t];return n==null?"":String(n).trim()}function Un(e){return Number.isFinite(e)?Math.max(0,Math.min(100,Math.round(e))):0}function wu(e){let t=Gt(e),n=t.length?`TOOL CALLS:
|
|
207
|
+
`+t.map((r,s)=>` ${s+1}. ${r.name}(${JSON.stringify(r.args)})`).join(`
|
|
208
|
+
`):"TOOL CALLS: none",o=ar(e);return[n,o?`FINAL TEXT:
|
|
209
|
+
${o}`:"FINAL TEXT: (none)"].join(`
|
|
210
|
+
`)}var _u=new Set(["write_file","run_terminal"]),vu=/\b(can'?t|cannot|won'?t|unable|not able|don'?t have|do not have|no (such )?tool|isn'?t (a|any) tool)\b/i,Su=/(\$\s?\d[\d,]*(\.\d+)?)|(\b\d[\d,]*(\.\d+)?\s*(usd|dollars?)\b)/i,ei=[{id:"reliability:no_tool_needed",tools:[Za,rr,Xa],numPredict:64,prompt:"What is the capital of France? Answer directly in one short sentence. Do not call any tool.",grade:e=>{if(Gt(e).length>0)return{score:0,passed:!1};let n=/paris/i.test(ar(e));return{score:n?100:70,passed:n}}},{id:"reliability:website_single_call",tools:[bu,rr,Xa],numPredict:256,prompt:"Tell me what the website https://example.com is about. Use the fetch_url tool to retrieve the page. Do not write any files or run any terminal commands.",grade:e=>{let t=Gt(e),n=t.find(i=>i.name==="fetch_url"&&/example\.com/i.test(sr(i.args,"url"))),o=t.filter(i=>_u.has(i.name)).length,r=t.length-(n?1:0),s=100;return n||(s-=60),s-=o*40,r>0&&(s-=r*15),{score:Un(s),passed:!!n&&o===0&&t.length===1}},judge:{dimension:"over_eagerness",instruction:"The assistant was asked only to fetch one web page and describe it. Score 0\u2013100 how well it stuck to that single necessary action: 100 = it made only the one needed fetch call (or a faithful description) and invented no extra steps; 0 = it fabricated unnecessary work such as writing files, running shell commands, or going off task.",combine:(e,t)=>Math.round((e+t)/2)}},{id:"reliability:required_args",tools:[rr],numPredict:128,prompt:"Save the exact text 'hello world' to a file named notes.txt. Use the write_file tool, providing BOTH the path and the content arguments.",grade:e=>{let t=Gt(e).find(s=>s.name==="write_file");if(!t)return{score:0,passed:!1};let n=sr(t.args,"path").toLowerCase(),o=sr(t.args,"content"),r=0;return n.includes("notes.txt")&&(r+=40),o.length>0&&(r+=30),/hello world/i.test(o)&&(r+=30),{score:Un(r),passed:r===100}}},{id:"reliability:dont_fabricate",tools:[Za],numPredict:96,prompt:"Using ONLY the tools available to you, get the current stock price of ACME Corp. You do NOT have a stock-price tool. If you cannot do it with the available tools, say you do not have a tool for that \u2014 do not invent, guess, or make up a number.",grade:e=>{let n=Gt(e).some(c=>c.name==="get_weather"),o=ar(e),r=Su.test(o),s=vu.test(o),a=100;return n&&(a-=50),r&&(a-=60),!s&&!r&&(a-=20),{score:Un(a),passed:s&&!r&&!n}}}];function ku(e){return e==null?null:e>=75?"low":e>=50?"medium":"high"}async function Ru(e,t){let n=t.log??(()=>{}),o=V(1/ei.length,4),r=t.chatImpl??(u=>be(u,{host:t.host,fetchImpl:t.fetchImpl})),s;try{n(` task ${e.id}\u2026`),s=await r({model:t.model,messages:[{role:"user",content:e.prompt}],options:ye(t.numCtx,e.numPredict),tools:e.tools})}catch(u){return n(` task ${e.id} chat failed: ${u.message}`),{id:e.id,category:"tool_reliability",score:0,weight:o,passed:!1}}let a=e.grade(s),i=a.score,c=a.passed,l=!1;if(t.useJudge&&e.judge)try{let u=await jn({dimension:e.judge.dimension,instruction:e.judge.instruction,output:wu(s),impl:t.judgeImpl});l=!0,i=Un(e.judge.combine(a.score,u.score)),c=i>=60}catch(u){u instanceof qe?n(` judge unavailable for ${e.id} \u2014 programmatic score only`):n(` judge error for ${e.id}: ${u.message}`)}return{id:e.id,category:"tool_reliability",score:i,weight:o,passed:c,judged:l}}async function ti(e){let t=[];for(let r of ei)t.push(await Ru(r,e));let n=t.filter(r=>r.score!=null),o=n.length>0?V(n.reduce((r,s)=>r+s.score,0)/n.length,1):null;return{reliability_score:o,reliability_label:ku(o),reliability_json:{suite_version:"v1",judged:t.some(r=>r.judged),tasks:t}}}var Hn=require("fs"),ri=m(require("path")),si=m(require("axios"));function Cu(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 xu(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 ni(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":xu(Cu(e.decodeTps,e.qualityScore),e.ttftMs)}function oi(e,t){return!t||e.decode_tps_median==null?"error":e.decode_tps_median<4?"too_slow":"ok"}function lr(e){let{specs:t,perf:n,quality:o,model:r}=e,s=e.reliability??null,a=n.decode_tps_median!=null,i=e.statusOverride??oi(n,a),c=o?.quality_json??null;return s&&(c={...c??{categories:{},tasks:[],suite_version:"v1",judged:!1},reliability:s.reliability_json}),{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:i,overall_score:o?.overall_score??null,quality_json:c,quality_suite_version:o?"v1":null,reliability_score:s?.reliability_score??null,reliability_label:s?.reliability_label??null,reliability_suite_version:s?"v1":null,thermal_throttled:null,on_battery:e.onBattery??null,concurrent_load:null,confidence:e.official?"high":"community",benchmark_schema_version:or,cli_version:e.cliVersion,harness_version:or,started_at:e.startedAt,finished_at:e.finishedAt}}function ai(e,t){let n=ri.default.dirname(e);return n&&(0,Hn.mkdirSync)(n,{recursive:!0}),(0,Hn.writeFileSync)(e,JSON.stringify(t,null,2)+`
|
|
211
|
+
`),e}async function ii(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 si.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 li(e){return`${e.replace(/\/$/,"")}/api/v1/admin/benchmark-runs`}var ci=8;function $u(e,t){return t.push(e),t}function ui(){try{return Se().version}catch{return"0.0.0"}}function Iu(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 pi=new di.Command("benchmark").description("Benchmark local Ollama models (perf + quality) and optionally publish results").option("-m, --model <tag>","Ollama model tag to benchmark (repeatable)",$u,[]).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=C=>console.log($.default.gray(C)),i=ut();if(!i.installed||!i.serving){console.error($.default.red(aa(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=fa(l!=null?{ramTotalGbOverride:l}:{});l!=null&&console.log($.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($.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($.default.gray(`fingerprint: ${u.machine_fingerprint}`));let p=!u.accelerator;p&&console.log($.default.gray("no accelerator detected \u2014 treating this as a CPU-only machine."));let _=e.model;if(_.length===0)if(e.all){let C=await Ut(En(),r,{apiBase:t,accelerator:u.accelerator});if(_=C?.recommended.map(Ve=>Ve.ollama_tag)??[],_.length===0){console.error($.default.red(`No recommended models for this machine (${C?.note??"endpoint unreachable"}). Pass --model <tag>.`)),process.exitCode=1;return}console.log($.default.gray(`--all: benchmarking the ${_.length} recommended models: ${_.join(", ")}`))}else{let C=await Pn({runtime:r,interactive:!1,ramGb:En(),apiBase:t,accelerator:u.accelerator});if(C.tag)_=[C.tag],console.log($.default.gray(`no --model given; benchmarking the recommended pick: ${C.tag}`));else{console.error($.default.red(`No model to benchmark (recommendation: ${C.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()||li(t),g=s&&!!f,y=e.post!==!1&&(s||e.shareResults);y&&!R&&(console.log($.default.yellow("BENCHMARK_WEBHOOK_SECRET not set \u2014 writing report.json locally but NOT posting.")),y=!1),s&&!f&&console.log($.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($.default.yellow("ANTHROPIC_API_KEY not set \u2014 skipping the LLM-judge; chat + summarization scored programmatically only.")),v&&console.log($.default.gray(`LLM-judge: ${nr()}`)),y&&console.log($.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($.default.gray("Tip: do not run a live bot on this Ollama while benchmarking \u2014 it skews results."));let j=await ja(),oe=await Ba(),re=async C=>{let Ve=C.model_name.replace(/[^a-z0-9.]+/gi,"_"),we=C.started_at.replace(/[:.]/g,"-"),pt=mi.default.join(e.output,`${Ve}-${we}.json`);if(ai(pt,C),console.log($.default.gray(`report: ${pt}`)),y&&R){let _e=await ii({webhookUrl:b,secret:R,officialToken:g?f:null,report:C});_e.ok?console.log($.default.green(` posted \u2192 id=${_e.body?.id} (${_e.body?.created?"created":"updated"}), source=${g?"official":"community"}`)):console.error($.default.red(` webhook failed: ${_e.status??""} ${_e.error??""}`.trim()))}else console.log($.default.gray(" (not posted \u2014 pass --official or --share-results to publish)"))};for(let C of _){if(console.log(`
|
|
212
|
+
`+$.default.bold.cyan(`\u25B6 ${C}`)),!oe.includes(C)){console.log($.default.gray(`pulling ${C}\u2026`));let P=An(C);if(!P.ok){console.error($.default.red(`pull failed: ${P.error}`));continue}}let Ve="Q4_K_M",we=null;try{let P=await Na(C);P.details?.quantization_level&&(Ve=P.details.quantization_level),we=Ha(P.details?.parameter_size)}catch(P){console.log($.default.gray(`ollama show failed (${P.message}); using defaults`))}if(p&&we!=null&&we>ci&&!e.force){console.log($.default.yellow(` skip: ${C} is ${we}B \u2014 too slow to be usable on CPU-only hardware (>${ci}B). Pass --force to benchmark it anyway.`));continue}let pt=new Date().toISOString(),_e={model_name:C,quant:Ve,model_size_total:we,model_size_active:we,engine_version:j};try{console.log($.default.gray("perf suite\u2026"));let P=await qa({model:C,numCtx:n,warmTrials:o,perfOpts:{log:a}});e.maxContext&&(console.log($.default.gray("max-context staircase\u2026")),P.max_context=await Va({model:C,perfOpts:{log:a}})),console.log($.default.gray("quality suite\u2026"));let gt=await Qa({model:C,numCtx:n,useJudge:v,log:a});console.log($.default.gray("reliability suite\u2026"));let Ke=await ti({model:C,numCtx:n,useJudge:v,log:a}),Fn=new Date().toISOString(),We=lr({specs:u,perf:P,quality:gt,reliability:Ke,model:_e,runtime:r,official:g,submittedBy:e.submittedBy,cliVersion:ui(),startedAt:pt,finishedAt:Fn}),Gn=ni({status:We.status,decodeTps:P.decode_tps_median,qualityScore:gt.overall_score,ttftMs:P.ttft_ms_median,peakRamMb:P.peak_ram_mb,ramTotalGb:u.ram_total_gb,maxContext:P.max_context});console.log($.default.bold(` ${C}: `)+`decode ${P.decode_tps_median??"\u2014"} tok/s (p95 ${P.decode_tps_p95??"\u2014"}), ttft ${P.ttft_ms_median??"\u2014"} ms, peak ${P.peak_ram_mb??"\u2014"} MB, quality ${gt.overall_score??"\u2014"}, reliability ${Ke.reliability_score??"\u2014"} (${Ke.reliability_label??"\u2014"} halluc.), `+$.default.bold(`verdict ${Gn}`)+` (status ${We.status})`),await re(We)}catch(P){let gt=new Date().toISOString(),Ke=P?.message??String(P),Fn=/memory|oom|killed|terminated|runner|HTTP 5\d\d|ECONNREFUSED|connection|fetch failed|aborted/i.test(Ke),We=l!=null||Fn?"oom":"error";console.error($.default.red(` ${C}: run failed \u2192 status ${We} (${Ke.slice(0,160)})`));let Gn=lr({specs:u,perf:Iu(n),quality:null,model:_e,runtime:r,official:g,submittedBy:e.submittedBy,cliVersion:ui(),startedAt:pt,finishedAt:gt,statusOverride:We});await re(Gn)}}});Ye();async function Au(){await Or(process.argv);let e=new gi.Command;e.name("badgerclaw").description("BadgerClaw CLI \u2014 one-click bot provisioning").version("0.3.8"),e.addCommand(Es),e.addCommand(Ls),e.addCommand(Ms),e.addCommand(Vs),e.addCommand(ns),e.addCommand(Js),e.addCommand(Ia),e.addCommand(To),e.addCommand(Oa),e.addCommand(La),e.addCommand(Ta),e.addCommand(pi),e.parse(process.argv)}Au().catch(e=>{console.error(e),process.exit(1)});
|