badgerclaw 0.2.53 → 0.2.54
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/assets/hermes-agent-bc.Dockerfile +52 -0
- package/dist/index.js +55 -48
- package/package.json +2 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# BadgerClaw-flavored Hermes agent image.
|
|
2
|
+
#
|
|
3
|
+
# Why this exists: nousresearch/hermes-agent's gateway/platforms/matrix.py
|
|
4
|
+
# imports mautrix types at module load time. mautrix isn't in the base
|
|
5
|
+
# image — it's lazy-installed via tools/lazy_deps.py on first Matrix
|
|
6
|
+
# connect. That ordering is broken: by the time lazy_deps runs, matrix.py
|
|
7
|
+
# has already fallen back to a local _EventTypeStub class, and
|
|
8
|
+
# add_event_handler raises "Invalid event type" against the real mautrix
|
|
9
|
+
# Syncer at connect time. PgCryptoStore also imports asyncpg
|
|
10
|
+
# unconditionally even on SQLite. Pre-baking the deps here fixes both.
|
|
11
|
+
#
|
|
12
|
+
# Phase-0 spike receipts: docs/hermes-phase0.md, docs/hermes-phase0-evidence/.
|
|
13
|
+
# Memory ref: feedback_hermes_pre_install_deps.
|
|
14
|
+
#
|
|
15
|
+
# Mirrored to: ${ECR_REGISTRY}/hermes-agent-bc:<tag>
|
|
16
|
+
# Built and pushed by: .github/workflows/mirror-hermes-image.yml
|
|
17
|
+
#
|
|
18
|
+
# Pin upstream by digest, not tag — keeps reproducibility tight and prevents
|
|
19
|
+
# a silent upstream :latest move from changing what we ship. The mirror
|
|
20
|
+
# workflow updates this base on its daily cron and commits the new digest.
|
|
21
|
+
|
|
22
|
+
ARG UPSTREAM=nousresearch/hermes-agent:latest
|
|
23
|
+
|
|
24
|
+
FROM ${UPSTREAM}
|
|
25
|
+
|
|
26
|
+
# The upstream image runs as root briefly (for gosu UID remap) before
|
|
27
|
+
# dropping to `hermes`. The venv lives under /opt/hermes and is owned by
|
|
28
|
+
# the hermes user — `uv pip install` needs to write there, so do this
|
|
29
|
+
# as root before the entrypoint takes over.
|
|
30
|
+
USER root
|
|
31
|
+
|
|
32
|
+
# Install the dependencies matrix.py needs at module-import time, plus
|
|
33
|
+
# the asyncpg dep that PgCryptoStore imports unconditionally even on
|
|
34
|
+
# SQLite-backed crypto stores. Versions pin to the same set Hermes' own
|
|
35
|
+
# lazy_deps.py uses (tools/lazy_deps.py:126 in the upstream image as of
|
|
36
|
+
# 2026-05-15), so behaviour matches what a healthy lazy-install would
|
|
37
|
+
# produce — just without the startup-order race.
|
|
38
|
+
# `uv` is at /usr/local/bin/uv in the upstream image, not inside the
|
|
39
|
+
# venv. Targeting the venv via --python keeps the install scoped to the
|
|
40
|
+
# same interpreter that hermes runs under.
|
|
41
|
+
RUN /usr/local/bin/uv pip install \
|
|
42
|
+
--python /opt/hermes/.venv/bin/python \
|
|
43
|
+
--no-cache \
|
|
44
|
+
"mautrix[encryption]==0.21.0" \
|
|
45
|
+
"asyncpg>=0.29,<1" \
|
|
46
|
+
"aiosqlite>=0.19,<1" \
|
|
47
|
+
&& /opt/hermes/.venv/bin/python -c \
|
|
48
|
+
"from mautrix.types import EventType; from mautrix.crypto.store.asyncpg import PgCryptoStore; print('mautrix+asyncpg import OK')"
|
|
49
|
+
|
|
50
|
+
# Don't override ENTRYPOINT/CMD — the upstream values (`tini` calling
|
|
51
|
+
# /opt/hermes/docker/entrypoint.sh) handle UID remap, volume chown, and
|
|
52
|
+
# the `hermes` subcommand dispatch. We only changed the venv contents.
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
|
|
2
|
+
"use strict";var yr=Object.create;var Xe=Object.defineProperty;var wr=Object.getOwnPropertyDescriptor;var br=Object.getOwnPropertyNames;var _r=Object.getPrototypeOf,Sr=Object.prototype.hasOwnProperty;var N=(e,t)=>()=>(e&&(t=e(e=0)),t);var $r=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Ot=(e,t)=>{for(var o in t)Xe(e,o,{get:t[o],enumerable:!0})},xo=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of br(t))!Sr.call(e,r)&&r!==o&&Xe(e,r,{get:()=>t[r],enumerable:!(n=wr(t,r))||n.enumerable});return e};var c=(e,t,o)=>(o=e!=null?yr(_r(e)):{},xo(t||!e||!e.__esModule?Xe(o,"default",{value:e,enumerable:!0}):o,e)),Ro=e=>xo(Xe({},"__esModule",{value:!0}),e);function _(){try{let e=Re.default.readFileSync(Lt,"utf-8");return JSON.parse(e)}catch{return null}}function Qe(e){Re.default.mkdirSync(Oo,{recursive:!0}),Re.default.writeFileSync(Lt,JSON.stringify(e,null,2),{mode:384})}function Ae(){try{Re.default.unlinkSync(Lt)}catch{}}function Ze(){let e=_();return e?new Date(e.expires_at)>new Date:!1}function et(e){let t=e.match(/^@?([^:]+)/);return t?t[1]:e}var Re,Tt,Po,Oo,Lt,j=N(()=>{"use strict";Re=c(require("fs")),Tt=c(require("path")),Po=c(require("os")),Oo=Tt.default.join(Po.default.homedir(),".badgerclaw"),Lt=Tt.default.join(Oo,"auth.json")});function Nt(){return Do.default.createHash("sha256").update(`${G.default.hostname()}-${G.default.platform()}-${G.default.arch()}`).digest("hex").slice(0,16)}function V(){let e=_();if(e?.instance_id)return e.instance_id;let t=Nt();return`openclaw-${G.default.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${t}`}function tt(){return{hostname:G.default.hostname(),os:G.default.platform(),arch:G.default.arch(),uptimeSeconds:Math.floor(G.default.uptime()),memFreeMb:Math.floor(G.default.freemem()/1024/1024)}}var G,Do,we=N(()=>{"use strict";G=c(require("os")),Do=c(require("crypto"));j()});var To,B,Lo,X=N(()=>{"use strict";To=process.env.BADGERCLAW_ENV==="local",B=process.env.BADGERCLAW_API_URL??(To?"http://localhost:8000":"https://api.badger.signout.io"),Lo=process.env.BADGERCLAW_AUTH_URL??(To?"http://localhost:5500":"https://badgerclaw.ai")});function jo(){return Ee}function vr(e){if(!e)return null;let t=e.split(".");if(t.length!==3)return null;try{let o=t[1].replace(/-/g,"+").replace(/_/g,"/"),n=o.length%4===0?o:o+"=".repeat(4-o.length%4),r=JSON.parse(Buffer.from(n,"base64").toString("utf8"));return(typeof r?.sub=="string"?r.sub:null)||null}catch{return null}}async function jt(){let e=_();if(!e?.refresh_token||!e?.email)return Ee=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=vr(e.access_token);try{let o=await Pe.default.post(`${nt}/api/v1/user/token/refresh`,{refresh_token:e.refresh_token,email:e.email,...t?{cognito_username:t}:{}}),n=o.data?.result??o.data,r=n?.access_token;if(!r)return Ee="server returned no access_token",null;let s=n.expires_in||3600,a=new Date(Date.now()+s*1e3).toISOString();return Qe({...e,access_token:r,expires_at:a}),Ee=null,r}catch(o){let n=o?.response?.status,r=o?.response?.data?.errors?.[0]||o?.response?.data?.detail||o?.message||"unknown error";Ee=n?`HTTP ${n}: ${r}`:`network error: ${r}`;let s=o?.response?.data?.errors?.[0]||o?.response?.data?.detail||o?.message||"";if(n===403&&No.test(s))throw Ae(),new be(s);return null}}function Bt(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 o=t.response?.data?.errors;return o?.length&&(t.message=o[0]),Promise.reject(t)}),e}function Bo(e){return e.interceptors.response.use(void 0,t=>{if(t.response?.status===403){let n=t.message||t.response?.data?.errors?.[0]||t.response?.data?.detail||"";if(No.test(n))return Ae(),Promise.reject(new be(n))}return Promise.reject(t)}),e}function Mo(e){return e.interceptors.response.use(void 0,async t=>{let o=t.config;if(t.response?.status!==401||o._retried)return Promise.reject(t);o._retried=!0,ot||(ot=jt().finally(()=>{ot=null}));let n=await ot;return n?(o.headers.Authorization=`Bearer ${n}`,e.request(o)):Promise.reject(t)}),e}function E(){let e=_(),t={"Content-Type":"application/json"};e&&(t.Authorization=`Bearer ${e.access_token}`);let o=Pe.default.create({baseURL:nt,headers:t});return Bt(o),Mo(o),Bo(o),o}function Uo(){return Bt(Pe.default.create({baseURL:nt,headers:{"Content-Type":"application/json"}}))}function Oe(e){let t=Pe.default.create({baseURL:nt,headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`}});return Bt(t),Mo(t),Bo(t),t}var Pe,nt,be,No,ot,Ee,se=N(()=>{"use strict";Pe=c(require("axios"));j();X();nt=B,be=class extends Error{constructor(o){super(o);this.code="DEACTIVATED";this.name="DeactivatedError"}},No=/account\s+has\s+been\s+(deleted|deactivated)/i,ot=null,Ee=null});function Vo(){try{let e=Q.default.statSync(ae);return Date.now()-e.mtimeMs>Ho}catch{return!0}}function Mt(){try{return Q.default.mkdirSync(Ut.default.dirname(ae),{recursive:!0}),Q.default.writeFileSync(ae,String(process.pid),{flag:"wx"}),!0}catch{if(Vo())try{return Q.default.unlinkSync(ae),Q.default.writeFileSync(ae,String(process.pid),{flag:"wx"}),!0}catch{return!1}return!1}}function Wo(){try{Q.default.unlinkSync(ae)}catch{}}async function Ko(e){let t=!1;for(let o=0;o<Cr;o++){if(Mt()){t=!0;break}await new Promise(n=>setTimeout(n,Go))}t||console.warn("[config-lock] Could not acquire lock after timeout \u2014 proceeding without lock");try{return await e()}finally{t&&Wo()}}function ie(e){let t=Mt();if(!t&&Vo()){try{Q.default.unlinkSync(ae)}catch{}t=Mt()}try{return e()}finally{t&&Wo()}}var Q,Fo,Ut,ae,Ho,Go,Cr,De=N(()=>{"use strict";Q=c(require("fs")),Fo=c(require("os")),Ut=c(require("path")),ae=Ut.default.join(Fo.default.homedir(),".openclaw","openclaw.json.lock"),Ho=1e4,Go=50,Cr=Math.ceil(Ho/Go)});var ce=$r((Aa,Ir)=>{Ir.exports={name:"badgerclaw",version:"0.2.54",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:{"@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 Gt(){return"0.2.54"}function Pr(){for(let e of Er)try{let t=Jo.default.readFileSync(e,"utf-8"),o=JSON.parse(t);if(o.version&&(!o.name||o.name==="@badgerclaw/connect"||o.name==="badgerclaw")||o.version)return o.version}catch{}return null}function Or(e){let t=e.trim(),o=t.match(/\d+(?:\.\d+){1,3}/);return o?o[0]:t}function Se(){if((0,Ft.spawnSync)("which",["openclaw"],{encoding:"utf-8"}).status!==0)return null;let t=(0,Ft.spawnSync)("openclaw",["--version"],{encoding:"utf-8",timeout:3e3});return t.status!==0||!t.stdout?null:Or(t.stdout)||null}async function Dr(){let e=new AbortController,t=setTimeout(()=>e.abort(),xr);try{let o=await fetch(kr,{signal:e.signal});if(!o.ok)return null;let n=await o.json(),r=n.result&&typeof n.result=="object"?n.result:n;return{cli:r.cli??"unknown",plugin:r.plugin??"unknown",supported_openclaw:r.supported_openclaw??"unknown"}}catch{return null}finally{clearTimeout(t)}}function Tr(e){let t=[];if(e.cli&&e.cli!=="unknown"){let o=Gt();o!==e.cli&&t.push({component:"cli",current:o,approved:e.cli})}if(e.plugin&&e.plugin!=="unknown"){let o=Pr();(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=Se();(o===null||o!==e.supported_openclaw)&&t.push({component:"openclaw",current:o??"not installed",approved:e.supported_openclaw})}return t}function Lr(e){return e[2]}function Nr(e){return!e||e.startsWith("-")?!0:Rr.has(e)}function jr(e){let t=[];t.push(""),t.push(Z.default.red.bold("\u2717 Unsupported versions detected")),t.push("");for(let o of e){let n=o.component.padEnd(8);t.push(` ${n} ${Z.default.yellow(o.current)} \u2192 ${Z.default.green(o.approved)} ${Z.default.dim("(supported)")}`)}return t.push(""),t.push("This command is blocked until your installation matches the supported versions."),t.push(""),t.push(Z.default.green(" To fix, run:")),t.push(Z.default.green.bold(" badgerclaw setup")),t.push(""),t.push(Z.default.dim(" (bypass temporarily: BADGERCLAW_NO_VERSION_CHECK=1 badgerclaw <cmd>)")),t.push(""),t.join(`
|
|
3
|
+
`)}async function Yo(e){if(process.env.BADGERCLAW_NO_VERSION_CHECK)return;let t=Lr(e);if(Nr(t))return;let o=await Dr();if(!o)return;let n=Tr(o);n.length!==0&&(process.stderr.write(jr(n)),process.exit(1))}var Jo,Ht,_e,Ft,Z,kr,xr,Rr,qo,Ar,Er,rt=N(()=>{"use strict";Jo=c(require("fs")),Ht=c(require("os")),_e=c(require("path")),Ft=require("child_process"),Z=c(require("chalk"));X();kr=`${B}/api/v1/dashboard/versions/latest`,xr=2500,Rr=new Set(["setup","logout","help","--version","-V","--help","-h","heartbeat","watch","autopair"]),qo=_e.default.join(Ht.default.homedir(),".openclaw/extensions/badgerclaw"),Ar=_e.default.join(Ht.default.homedir(),".openclaw/npm/node_modules/@badgerclaw/connect"),Er=[_e.default.join(Ar,"package.json"),_e.default.join(qo,"node_modules/@badgerclaw/connect/package.json"),_e.default.join(qo,"package.json")]});var en={};Ot(en,{HERMES_IMAGE_DEFAULT:()=>$e,detectCapabilities:()=>le,detectDiskFreeGb:()=>Zo,detectDocker:()=>st,detectHermes:()=>at,detectOpenClaw:()=>Qo});function st(){if((0,Te.spawnSync)("which",["docker"],{encoding:"utf-8"}).status!==0)return{installed:!1};let t=(0,Te.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 Qo(){let e=Se();return e?{installed:!0,version:e}:{installed:!1}}function at(e){if(!e.installed)return{installed:!1};let t=process.env.HERMES_IMAGE||$e;return(0,Te.spawnSync)("docker",["image","inspect","--format","{{.RepoDigests}}",t],{encoding:"utf-8",timeout:3e3}).status!==0?{installed:!1}:{installed:!0,image:t}}function Zo(){let e=Xo.default.join(Vt.default.homedir(),".badgerclaw"),t=zo.default.existsSync(e)?e:Vt.default.homedir();if(process.platform==="win32")return null;let o=(0,Te.spawnSync)("df",["-Pk",t],{encoding:"utf-8",timeout:3e3});if(o.status!==0||!o.stdout)return null;let n=o.stdout.trim().split(`
|
|
4
|
+
`);if(n.length<2)return null;let r=n[1].split(/\s+/),s=parseInt(r[3]||"",10);return Number.isFinite(s)?Math.floor(s/1024/1024):null}function le(){let e=st();return{openclaw:Qo(),hermes:at(e),docker:e,disk_free_gb:Zo(),ports_in_use:[]}}var Te,zo,Vt,Xo,$e,de=N(()=>{"use strict";Te=require("child_process"),zo=c(require("fs")),Vt=c(require("os")),Xo=c(require("path"));rt();$e="hermes-agent-bc:bundled"});function ct(e){return W.default.join(Br,`bot-${e}`)}function qt(e){return`badgerclaw-hermes-bot-${e}`}function Jt(){if(!P.default.existsSync(Wt))return{hermes_bots:{}};try{let e=P.default.readFileSync(Wt,"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 Ur(e){P.default.existsSync(it)||P.default.mkdirSync(it,{recursive:!0,mode:448}),P.default.writeFileSync(Wt,JSON.stringify(e,null,2)+`
|
|
5
|
+
`,{mode:384})}function Fr(e,t=Mr){let o=new Set(Object.values(e.hermes_bots).map(r=>r.port)),n=t;for(;o.has(n)||!Hr(n);)if(n+=1,n-t>1e3)throw new Error(`Could not find a free port near ${t} after 1000 tries \u2014 host is unusually busy`);return n}function Hr(e){let t=on.default.createServer(),o=!1;try{t.listen(e,"127.0.0.1"),o=!0}catch{o=!1}finally{try{t.close()}catch{}}return o}function rn(e=process.env){let t=e.BADGERCLAW_LLM_PROVIDER?.toLowerCase(),o=e.BADGERCLAW_LLM_API_KEY;if(t&&o){if(t!=="gemini"&&t!=="anthropic"&&t!=="openrouter")throw new Error(`BADGERCLAW_LLM_PROVIDER must be one of: gemini, anthropic, openrouter. Got '${t}'.`);return{provider:t,apiKey:o}}let n=e.GOOGLE_API_KEY||e.GEMINI_API_KEY;if(n)return{provider:"gemini",apiKey:n};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 Gr(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"}}function Vr(e){switch(e){case"gemini":return"GOOGLE_API_KEY";case"anthropic":return"ANTHROPIC_API_KEY";case"openrouter":return"OPENROUTER_API_KEY"}}function Wr(e,t){let o=[`# 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&&o.push(`MATRIX_HOME_ROOM=${e.homeRoomId}`),e.llm&&(o.push(""),o.push("# LLM provider \u2014 set by the operator's environment at pair time."),o.push("# Hermes reads this directly; model selection lives in config.yaml."),o.push(`${Vr(e.llm.provider)}=${e.llm.apiKey}`)),o.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}`),o.join(`
|
|
4
6
|
`)+`
|
|
5
|
-
`}function
|
|
6
|
-
`)}function
|
|
7
|
-
`)}function
|
|
8
|
-
${a.stderr}`,
|
|
7
|
+
`}function Kr(e){let t=[`# Hermes agent config for BadgerClaw bot ${e.botId}.`,"# Generated by badgerclaw CLI; secrets live in the sibling .env file.",""];return e.llm&&t.push("model:",` default: ${Gr(e.llm.provider)}`,` provider: ${e.llm.provider}`,""),t.push("agent:",` name: ${e.botName||"BadgerClaw bot"}`,"terminal:"," backend: local","subagents:"," max_spawn_depth: 1","cron:"," enabled: false",""),t.join(`
|
|
8
|
+
`)}function qr(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(`
|
|
9
|
+
`)}function Jr(e,t){let o=ct(e.botId);P.default.mkdirSync(o,{recursive:!0,mode:448});let n=W.default.join(o,".env"),r=W.default.join(o,"config.yaml"),s=W.default.join(o,"SOUL.md");return P.default.writeFileSync(n,Wr(e,t),{mode:384}),P.default.writeFileSync(r,Kr(e),{mode:384}),P.default.existsSync(s)||P.default.writeFileSync(s,qr(e),{mode:384}),{dataDir:o,envPath:n,configPath:r,soulPath:s}}function Yr(e){let t=process.getuid?.()??0,o=process.getgid?.()??0,n=["run","-d","--name",e.containerName,"--restart","unless-stopped","-v",`${e.dataDir}:/opt/data`,"-p",`127.0.0.1:${e.port}:${e.port}`];return t>0&&n.push("-e",`HERMES_UID=${t}`),o>0&&n.push("-e",`HERMES_GID=${o}`),n.push("--env-file",`${e.dataDir}/.env`,e.image,"gateway","run"),n}function sn(e,t={}){let o=t.docker??Kt,n=e.image||process.env.HERMES_IMAGE||$e;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=Jt(),s=r.hermes_bots[e.botId],a=s?.port??Fr(r,t.portBase),i=s?.container_name??qt(e.botId);if(Jr(e,a),!t.skipDocker){o.run(["rm","-f",i]);let m=Yr({containerName:i,dataDir:ct(e.botId),port:a,image:n}),p=o.run(m);if(p.status!==0)throw new Error(`docker run failed (exit ${p.status}): ${p.stderr||p.stdout}`)}let l={bot_id:e.botId,runtime:"hermes",container_name:i,port:a,data_dir:ct(e.botId),image:n,started_at:new Date().toISOString()};return r.hermes_bots[e.botId]=l,Ur(r),l}function an(e={}){let t=e.docker??Kt,o=Jt(),n=[];for(let[r,s]of Object.entries(o.hermes_bots)){let a=t.run(["inspect","--format","{{.State.Status}}",s.container_name]),i,l=a.stdout.trim()||null;a.status!==0?(i="missing",l=(a.stderr||a.stdout).trim()||null):l==="running"?i="running":l==="exited"||l==="dead"||l==="created"?i="stopped":i="unknown",n.push({bot_id:r,container_name:s.container_name,status:i,raw:l})}return n}function Xr(e){let t=e.match(zr);return t?t[1].trim():null}async function Yt(e,t,o,n={}){let r={recoveryKey:null,posted:!1,envUpdated:!1};if(!e.backendBotId)return r;let a=(n.docker??Kt).run(["logs",t]),i=`${a.stdout}
|
|
10
|
+
${a.stderr}`,l=Xr(i);if(!l)return r;if(r.recoveryKey=l,n.api)try{let p=await n.api.post(`/api/v1/hermes/bots/${e.backendBotId}/runtime-bootstrap`,{recovery_key:l,image_ref:o});r.posted=p.status>=200&&p.status<300}catch(p){console.warn(`[hermes] failed to POST recovery key for ${e.botId}: ${p.message}. Daemon will retry next beat.`)}let m=n.envPath??W.default.join(ct(e.botId),".env");return P.default.existsSync(m)&&(P.default.readFileSync(m,"utf-8").includes("MATRIX_RECOVERY_KEY=")||(P.default.appendFileSync(m,`
|
|
9
11
|
# Cross-signing recovery key captured from first-boot logs.
|
|
10
12
|
# Hermes re-uses this on restart to re-sign the device after
|
|
11
13
|
# Synapse rotates server keys.
|
|
12
|
-
MATRIX_RECOVERY_KEY=${
|
|
13
|
-
`),r.envUpdated=!0)),r}async function
|
|
14
|
-
\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(
|
|
15
|
-
\u26A1 ${s} bot(s) paired. Run: openclaw gateway restart`))}return s}catch{return 0}}var
|
|
16
|
-
`)}async function En(e){if(process.env.BADGERCLAW_NO_VERSION_CHECK)return;let t=ms(e);if(ps(t))return;let o=await ds();if(!o)return;let n=us(o);n.length!==0&&(process.stderr.write(gs(n)),process.exit(1))}var An,Zt,$e,Qt,te,ns,rs,ss,Rn,as,is,ft=L(()=>{"use strict";An=l(require("fs")),Zt=l(require("os")),$e=l(require("path")),Qt=require("child_process"),te=l(require("chalk"));X();ns=`${j}/api/v1/dashboard/versions/latest`,rs=2500,ss=new Set(["setup","logout","help","--version","-V","--help","-h","heartbeat","watch","autopair"]),Rn=$e.default.join(Zt.default.homedir(),".openclaw/extensions/badgerclaw"),as=$e.default.join(Zt.default.homedir(),".openclaw/npm/node_modules/@badgerclaw/connect"),is=[$e.default.join(as,"package.json"),$e.default.join(Rn,"node_modules/@badgerclaw/connect/package.json"),$e.default.join(Rn,"package.json")]});var jn={};It(jn,{detectCapabilities:()=>oo,detectDiskFreeGb:()=>Nn,detectDocker:()=>Dn,detectHermes:()=>Ln,detectOpenClaw:()=>Tn});function Dn(){if((0,Ge.spawnSync)("which",["docker"],{encoding:"utf-8"}).status!==0)return{installed:!1};let t=(0,Ge.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 Tn(){let e=ve();return e?{installed:!0,version:e}:{installed:!1}}function Ln(e){if(!e.installed)return{installed:!1};let t=process.env.HERMES_IMAGE||`${fs}:latest`;return(0,Ge.spawnSync)("docker",["image","inspect","--format","{{.RepoDigests}}",t],{encoding:"utf-8",timeout:3e3}).status!==0?{installed:!1}:{installed:!0,image:t}}function Nn(){let e=On.default.join(to.default.homedir(),".badgerclaw"),t=Pn.default.existsSync(e)?e:to.default.homedir();if(process.platform==="win32")return null;let o=(0,Ge.spawnSync)("df",["-Pk",t],{encoding:"utf-8",timeout:3e3});if(o.status!==0||!o.stdout)return null;let n=o.stdout.trim().split(`
|
|
17
|
-
`);if(n.length<2)return null;let r=n[1].split(/\s+/),s=parseInt(r[3]||"",10);return Number.isFinite(s)?Math.floor(s/1024/1024):null}function oo(){let e=Dn();return{openclaw:Tn(),hermes:Ln(e),docker:e,disk_free_gb:Nn(),ports_in_use:[]}}var Ge,Pn,to,On,fs,no=L(()=>{"use strict";Ge=require("child_process"),Pn=l(require("fs")),to=l(require("os")),On=l(require("path"));ft();fs="registry.getsignout.com/hermes-agent-bc"});var fo={};It(fo,{getActiveSessions:()=>go,launchClaudeCode:()=>uo,launchMCPServer:()=>lo,recordSession:()=>mo,stopSession:()=>po});function so(){try{if(D.default.existsSync(ro))return JSON.parse(D.default.readFileSync(ro,"utf-8"))}catch{}return[]}function ao(e){D.default.mkdirSync(me,{recursive:!0}),D.default.writeFileSync(ro,JSON.stringify(e,null,2))}function ht(e){try{return process.kill(e,0),!0}catch{return!1}}function hs(){if(process.env.TMUX)return"tmux";try{if((0,q.execSync)(`osascript -e 'tell application "System Events" to (name of processes) contains "iTerm2"'`,{stdio:"pipe",timeout:3e3}),D.default.existsSync("/Applications/iTerm.app"))return"iterm2"}catch{}return process.platform==="darwin"?"terminal":"direct"}function io(e,t){let o=B.default.join(me,`claude-launcher-${t}.exp`),n=B.default.join(me,`pending-input-${t}.txt`);D.default.writeFileSync(n,"","utf-8");let r=`#!/usr/bin/expect -f
|
|
14
|
+
MATRIX_RECOVERY_KEY=${l}
|
|
15
|
+
`),r.envUpdated=!0)),r}async function cn(e={}){let t=Jt(),o=[];for(let[n,r]of Object.entries(t.hermes_bots)){let s=W.default.join(r.data_dir,".env"),a=!1;try{P.default.existsSync(s)&&(a=P.default.readFileSync(s,"utf-8").includes("MATRIX_RECOVERY_KEY="))}catch{}if(a){o.push({bot_id:n,attempted:!1,recoveryKeyFound:!0,posted:!1,envUpdated:!1});continue}if(n.startsWith("@")){o.push({bot_id:n,attempted:!1,recoveryKeyFound:!1,posted:!1,envUpdated:!1});continue}try{let i=await Yt({botId:n,backendBotId:n},r.container_name,r.image,{docker:e.docker,api:e.api,envPath:s});o.push({bot_id:n,attempted:!0,recoveryKeyFound:i.recoveryKey!==null,posted:i.posted,envUpdated:i.envUpdated})}catch(i){console.warn(`[hermes] recovery-key retry failed for ${n}: ${i.message}`),o.push({bot_id:n,attempted:!0,recoveryKeyFound:!1,posted:!1,envUpdated:!1})}}return o}var tn,P,on,nn,W,it,Br,Wt,Mr,Kt,zr,zt=N(()=>{"use strict";tn=require("child_process"),P=c(require("fs")),on=c(require("net")),nn=c(require("os")),W=c(require("path"));de();it=W.default.join(nn.default.homedir(),".badgerclaw"),Br=W.default.join(it,"hermes"),Wt=W.default.join(it,"state.json"),Mr=8642,Kt={run:e=>{let t=(0,tn.spawnSync)("docker",e,{encoding:"utf-8"});return{status:t.status,stdout:t.stdout||"",stderr:t.stderr||""}}};zr=/SAVE THIS RECOVERY KEY[\s\S]*?key rotation:\s+((?:[A-Za-z0-9]{4}\s+){7,}[A-Za-z0-9]{4})/});function lt(e){return`'${e.replace(/'/g,"'\\''")}'`}function Qr(){if(ee!==null)return ee;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(Le.default.existsSync(t))return ee=`${lt(process.execPath)} ${lt(t)}`,ee;try{let{execSync:t}=require("child_process"),o=t("npm root -g",{encoding:"utf-8",timeout:5e3,stdio:["ignore","pipe","ignore"]}).toString().trim();if(o){let n=Zt.default.join(o,"openclaw","openclaw.mjs");if(Le.default.existsSync(n))return ee=`${lt(process.execPath)} ${lt(n)}`,ee}}catch{}return ee="openclaw",ee}function Xt(e,t,o){t?t.fail(F.default.red(o)):e&&console.error(`[autopair] ${o}`)}async function Zr(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,"_"),o={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:rn()},n=sn(o);if(o.backendBotId){let r=_();if(r){let s=Oe(r.access_token);try{await Yt(o,qt(o.botId),n.image,{api:{post:async(a,i)=>{let l=await s.post(a,i);return{status:l.status,data:l.data}}}})}catch(a){console.warn(`[hermes] recovery key capture failed for ${o.botId}: ${a.message}. Daemon will retry next beat.`)}}}}async function Ne(e,t,o,n){let r=_();if(!r)return;let s=n?null:(0,Qt.default)(`Pairing bot: ${t}...`).start();try{let a=await fetch(`${B}/api/v1/pairing/redeem`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:e,instance_id:V()})});if(!a.ok){if(a.status===410){s?.info(F.default.dim(`${t} already paired (race-loss; another channel redeemed first).`));return}let p=await a.json().catch(()=>({errors:[a.statusText]}));Xt(n,s,`Failed to redeem ${t}: ${p.errors?.[0]||a.status}`);return}let i=await a.json(),l=i.result??i;if((l.runtime??"openclaw")==="hermes"){try{await Zr(l),s?.succeed(F.default.green(`\u2705 ${l.bot_name} (${l.user_id}) running on Hermes \u2014 bot is live!`))}catch(p){Xt(n,s,`Failed to start Hermes container for ${t}: ${p.message}`)}return}await Ko(async()=>{let p=l.user_id.split(":")[0].replace("@","").replace(/_bot$/,""),f;try{f=JSON.parse(Le.default.readFileSync(ln,"utf-8"))}catch{f={}}f.channels||(f.channels={}),f.channels.badgerclaw||(f.channels.badgerclaw={}),f.channels.badgerclaw.accounts||(f.channels.badgerclaw.accounts={});let g={userId:l.user_id,accessToken:l.access_token,homeserver:l.homeserver,encryption:!0,groupPolicy:"open",allowlistOnly:!1,dm:{policy:"open"}};l.device_id&&(g.deviceId=l.device_id),f.channels.badgerclaw.accounts[p]=g,Le.default.writeFileSync(ln,JSON.stringify(f,null,2),{mode:384})}),await fetch(`${B}/api/v1/openclaw/pending-pairs/${e}/claim`,{method:"POST",headers:{Authorization:`Bearer ${r.access_token}`}}).catch(()=>{}),s?.succeed(F.default.green(`\u2705 ${l.bot_name} (${l.user_id}) paired \u2014 bot is live!`))}catch(a){Xt(n,s,`Failed to pair ${t}: ${a.message}`)}}async function je(e=!1){let t=_();if(!t)return 0;let o=Oe(t.access_token);try{let{data:n}=await o.get("/api/v1/openclaw/pending-pairs");if(!n||n.length===0)return 0;let r=new Map;for(let a of n)r.set(a.bot_user_id,a);for(let a of n)r.get(a.bot_user_id)!==a&&await o.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,Qt.default)(`Pairing bot: ${a.bot_name} (${a.bot_user_id})`).start();try{await Ne(a.pair_code,a.bot_name,a.bot_user_id,e),await o.post(`/api/v1/openclaw/pending-pairs/${a.pair_code}/claim`).catch(()=>{}),s++}catch(l){i?.fail(F.default.red(`Error pairing ${a.bot_name}: ${l}`))}}if(s>0)try{let{execSync:a}=await import("child_process");a(`${Qr()} gateway restart`,{stdio:"ignore"}),e||console.log(F.default.green(`
|
|
16
|
+
\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(F.default.yellow(`
|
|
17
|
+
\u26A1 ${s} bot(s) paired. Run: openclaw gateway restart`))}return s}catch{return 0}}var dn,F,Qt,un,Zt,Le,ln,ee,mn,Be=N(()=>{"use strict";dn=require("commander"),F=c(require("chalk")),Qt=c(require("ora")),un=c(require("os")),Zt=c(require("path")),Le=c(require("fs"));j();se();X();we();De();zt();ln=Zt.default.join(un.default.homedir(),".openclaw","openclaw.json");ee=null;mn=new dn.Command("autopair").description("Check for pending bot pairs and connect them to OpenClaw automatically").action(async()=>{if(!_()){console.log(F.default.yellow("Not logged in. Run `badgerclaw login` first."));return}console.log(F.default.dim("Checking for pending bot pairs...")),await je(!1)===0&&console.log(F.default.dim("No pending pairs found."))})});async function $s(){try{return await _t.default.get(Ss,{timeout:2e3,validateStatus:()=>!0}),!0}catch{return!1}}async function We(){try{let t=(await _t.default.get(`${Hn}/health`,{timeout:5e3})).data;return t.pluginVersion&&t.pluginVersion!=="unknown"&&(io=t.pluginVersion),t}catch{return{status:await $s()?"running":"stopped",pid:null,lastRestart:null,pluginVersion:io,bots:[]}}}function co(e){e&&e!=="unknown"&&(io=e)}async function ke(){try{return{success:!0,message:(await _t.default.post(`${Hn}/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 _t,bs,Hn,_s,Ss,io,pe=N(()=>{"use strict";_t=c(require("axios")),bs=7331,Hn=`http://localhost:${bs}`,_s=parseInt(process.env.OPENCLAW_GATEWAY_PORT||"",10)||18789,Ss=`http://127.0.0.1:${_s}`,io="unknown"});var _o={};Ot(_o,{getActiveSessions:()=>bo,launchClaudeCode:()=>ho,launchMCPServer:()=>fo,recordSession:()=>yo,stopSession:()=>wo});function uo(){try{if(T.default.existsSync(lo))return JSON.parse(T.default.readFileSync(lo,"utf-8"))}catch{}return[]}function mo(e){T.default.mkdirSync(ge,{recursive:!0}),T.default.writeFileSync(lo,JSON.stringify(e,null,2))}function St(e){try{return process.kill(e,0),!0}catch{return!1}}function vs(){if(process.env.TMUX)return"tmux";try{if((0,J.execSync)(`osascript -e 'tell application "System Events" to (name of processes) contains "iTerm2"'`,{stdio:"pipe",timeout:3e3}),T.default.existsSync("/Applications/iTerm.app"))return"iterm2"}catch{}return process.platform==="darwin"?"terminal":"direct"}function po(e,t){let o=M.default.join(ge,`claude-launcher-${t}.exp`),n=M.default.join(ge,`pending-input-${t}.txt`);T.default.writeFileSync(n,"","utf-8");let r=`#!/usr/bin/expect -f
|
|
18
18
|
set timeout -1
|
|
19
19
|
set msgfile "${n}"
|
|
20
20
|
|
|
@@ -51,21 +51,21 @@ send "\\r"
|
|
|
51
51
|
interact timeout 2 {
|
|
52
52
|
check_and_send_messages
|
|
53
53
|
}
|
|
54
|
-
`;return
|
|
54
|
+
`;return T.default.mkdirSync(ge,{recursive:!0}),T.default.writeFileSync(o,r,{mode:493}),o}function go(e){return Object.entries(e).map(([t,o])=>`export ${t}="${o}"`).join("; ")}function Cs(e,t,o){let n=po(e,o.BADGERCLAW_SESSION_ID||"default"),r=go(o),s=`
|
|
55
55
|
tell application "iTerm"
|
|
56
56
|
activate
|
|
57
57
|
set newWindow to (create window with default profile)
|
|
58
58
|
tell current session of newWindow
|
|
59
|
-
write text "cd ${
|
|
59
|
+
write text "cd ${$t(t)} && ${r} && ${$t(n)}"
|
|
60
60
|
end tell
|
|
61
61
|
end tell
|
|
62
|
-
`;(0,
|
|
62
|
+
`;(0,J.execSync)(`osascript -e '${s.replace(/'/g,`'"'"'`)}'`,{stdio:"pipe",timeout:1e4})}function Is(e,t,o){let n=po(e,o.BADGERCLAW_SESSION_ID||"default"),r=go(o),a=`
|
|
63
63
|
tell application "Terminal"
|
|
64
64
|
activate
|
|
65
|
-
do script "${`cd ${
|
|
65
|
+
do script "${`cd ${$t(t)} && ${r} && ${$t(n)}`.replace(/"/g,'\\"')}"
|
|
66
66
|
end tell
|
|
67
|
-
`;(0,
|
|
68
|
-
`)}catch{}try{(0,
|
|
67
|
+
`;(0,J.execSync)(`osascript -e '${a.replace(/'/g,`'"'"'`)}'`,{stdio:"pipe",timeout:1e4})}function ks(e,t,o){let n=po(e,o.BADGERCLAW_SESSION_ID||"default"),r=go(o),s=`badgerclaw-${o.BADGERCLAW_SESSION_ID||"default"}`,a=`cd ${t} && ${r} && ${n}`;(0,J.execSync)(`tmux new-session -d -s ${s} "${a}"`,{stdio:"pipe",timeout:1e4})}function $t(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function fo(e){let t=M.default.join(__dirname,"..","claude-code","mcp-server.js"),o=T.default.existsSync(t)?t:M.default.join(__dirname,"mcp-server.js"),n=(0,J.spawn)("node",[o],{env:{...process.env,BADGERCLAW_MCP_PORT:String(e.port),BADGERCLAW_SESSION_ID:e.sessionId},cwd:e.projectDir,stdio:"ignore",detached:!0});return n.unref(),n}function ho(e){let t=M.default.join(M.default.dirname(process.argv[1]||""),"..","lib","node_modules","badgerclaw","dist","claude-code","mcp-server.js"),o=M.default.join(__dirname,"..","claude-code","mcp-server.js"),n=M.default.join(__dirname,"mcp-server.js"),r=T.default.existsSync(t)?t:T.default.existsSync(o)?o:n,s={BADGERCLAW_MCP_PORT:String(e.port),BADGERCLAW_SESSION_ID:e.sessionId},a=JSON.stringify({mcpServers:{badgerclaw:{command:"node",args:[r],env:s}}}),i=M.default.join(ge,`mcp-config-${e.sessionId}.json`);T.default.mkdirSync(ge,{recursive:!0}),T.default.writeFileSync(i,a,"utf-8");let l=`claude --dangerously-skip-permissions --mcp-config ${i}`;switch(vs()){case"iterm2":Cs(l,e.projectDir,s);break;case"terminal":Is(l,e.projectDir,s);break;case"tmux":ks(l,e.projectDir,s);break;case"direct":(0,J.spawn)(l,[],{shell:!0,cwd:e.projectDir,env:{...process.env,...s},stdio:"ignore",detached:!0}).unref();break}}function yo(e,t,o,n,r){let s=uo().filter(a=>a.sessionId!==e);s.push({sessionId:e,pid:t,mcpPid:o,port:n,projectDir:r,startedAt:new Date().toISOString()}),mo(s)}function wo(e){let t=uo(),o=t.find(r=>r.sessionId===e);if(!o)return!1;if(o.mcpPid&&St(o.mcpPid))try{process.kill(o.mcpPid,"SIGTERM")}catch{}if(o.pid&&St(o.pid))try{process.kill(o.pid,"SIGTERM")}catch{}try{(0,J.execSync)(`tmux kill-session -t badgerclaw-${e} 2>/dev/null`,{stdio:"pipe",timeout:3e3})}catch{}let n=t.filter(r=>r.sessionId!==e);return mo(n),!0}function bo(){let e=uo(),t=e.filter(o=>St(o.mcpPid)||St(o.pid));return t.length!==e.length&&mo(t),t}var J,T,M,Gn,ge,lo,vt=N(()=>{"use strict";J=require("child_process"),T=c(require("fs")),M=c(require("path")),Gn=c(require("os")),ge=M.default.join(Gn.default.homedir(),".badgerclaw"),lo=M.default.join(ge,"claude-sessions.json")});async function Y(e){return e.command_type==="update_cli"?Rs(e.payload?.target_version):e.command_type==="update_plugin"?As(e.payload?.target_version):e.command_type==="restart_gateway"?xs():e.command_type==="leave_room"?Gs(e.payload):e.command_type==="start_claude_code"?Ts(e.payload):e.command_type==="stop_claude_code"?Ls(e.payload):e.command_type==="start_claude_control"?Ns(e.payload):e.command_type==="stop_claude_control"?js(e.payload):e.command_type==="update_claude_tools"?Bs(e.payload):e.command_type==="bot_share_notify"?Ms(e.payload):e.command_type==="bot_share_revoked"?Us(e.payload):e.command_type==="bot_share_expired"?Fs(e.payload):e.command_type==="bot_refresh"?Hs(e.payload):{success:!1,message:`Unknown command: ${e.command_type}`}}function xs(){try{return(0,k.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:15e3}),{success:!0,message:"Gateway restarted"}}catch{try{return{success:!0,message:`Gateway restarted via probe: ${(0,k.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 Rs(e){let t=e?`badgerclaw@${e}`:"badgerclaw@latest";try{(0,k.execSync)(`npm install -g ${t}`,{stdio:"pipe",timeout:12e4});let o=(0,k.execSync)("npm list -g badgerclaw --json",{stdio:"pipe"}).toString(),r=JSON.parse(o)?.dependencies?.badgerclaw?.version||"unknown";return Ps(),{success:!0,message:`Updated CLI to ${r}`,newVersion:r}}catch(o){let n=o.stderr?.toString()||o.message||"Unknown error";return n.includes("EACCES")||n.includes("permission")?{success:!1,message:"Permission denied. Run: sudo npm install -g badgerclaw"}:{success:!1,message:`CLI update failed: ${n.slice(0,500)}`}}}async function As(e){let t=$.default.join(fe.default.homedir(),".openclaw","extensions","badgerclaw"),o=$.default.join(fe.default.homedir(),".openclaw","npm","node_modules","@badgerclaw","connect"),n=h.default.existsSync(o)?"npm":h.default.existsSync(t)?"legacy":null;if(n===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(n==="npm"){let p=$.default.dirname($.default.dirname($.default.dirname(o)));(0,k.execSync)(`npm install ${r}`,{cwd:p,stdio:"pipe",timeout:12e4});let f=$.default.join(o,"package.json"),g="unknown";h.default.existsSync(f)&&(g=JSON.parse(h.default.readFileSync(f,"utf-8")).version||"unknown"),co(g);try{(0,k.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:1e4})}catch{}return await Vn(g)}let s=t;(0,k.execSync)(`npm install ${r}`,{cwd:s,stdio:"pipe",timeout:12e4});let a=$.default.join(s,"node_modules","@badgerclaw","connect"),i=$.default.join(a,"package.json"),l="unknown";h.default.existsSync(i)&&(l=JSON.parse(h.default.readFileSync(i,"utf-8")).version||"unknown"),co(l),Es(a,s);let m=$.default.join(s,"package.json");if(h.default.existsSync(m)&&h.default.existsSync(i))try{let p=JSON.parse(h.default.readFileSync(m,"utf-8")),f=JSON.parse(h.default.readFileSync(i,"utf-8"));p.version=l,f.openclaw&&(p.openclaw={...p.openclaw,...f.openclaw}),h.default.writeFileSync(m,JSON.stringify(p,null,2)+`
|
|
68
|
+
`)}catch{}try{(0,k.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:1e4})}catch{}return await Vn(l)}catch(s){return{success:!1,message:`Plugin update failed: ${(s.stderr?.toString()||s.message||"").slice(0,500)}`}}}async function Vn(e){for(let t=0;t<15;t++){await new Promise(o=>setTimeout(o,1e3));try{let o=await L.default.get("http://localhost:7331/health",{timeout:3e3});if(o.data?.status==="running"&&o.data?.pluginVersion===e)break}catch{}}return{success:!0,message:`Updated plugin to ${e}, gateway restarted`,newVersion:e}}function Es(e,t){let o=$.default.join(e,"dist"),n=$.default.join(t,"dist");h.default.existsSync(o)&&(h.default.mkdirSync(n,{recursive:!0}),So(o,n));let r=$.default.join(e,"openclaw.plugin.json");h.default.existsSync(r)&&h.default.copyFileSync(r,$.default.join(t,"openclaw.plugin.json"));let s=$.default.join(e,"scripts"),a=$.default.join(t,"scripts");h.default.existsSync(s)&&(h.default.mkdirSync(a,{recursive:!0}),So(s,a));for(let i of["index.ts","src","STREAMING.md","SETUP.md","CHANGELOG.md"]){let l=$.default.join(t,i);h.default.existsSync(l)&&(h.default.statSync(l).isDirectory()?h.default.rmSync(l,{recursive:!0,force:!0}):h.default.unlinkSync(l))}}function So(e,t){for(let o of h.default.readdirSync(e,{withFileTypes:!0})){let n=$.default.join(e,o.name),r=$.default.join(t,o.name);o.isDirectory()?(h.default.mkdirSync(r,{recursive:!0}),So(n,r)):h.default.copyFileSync(n,r)}}function Ps(){try{try{(0,k.execSync)('pkill -f "badgerclaw heartbeat"',{stdio:"pipe",timeout:5e3})}catch{}try{(0,k.execSync)('pkill -f "badgerclaw watch"',{stdio:"pipe",timeout:5e3})}catch{}(0,k.execSync)("sleep 1",{stdio:"pipe"});let e=(0,k.execSync)("which badgerclaw",{stdio:"pipe"}).toString().trim();(0,k.execSync)(`nohup ${e} heartbeat > /dev/null 2>&1 &`,{stdio:"pipe",shell:"/bin/zsh"}),(0,k.execSync)(`nohup ${e} watch > /dev/null 2>&1 &`,{stdio:"pipe",shell:"/bin/zsh"})}catch{}}function Os(){let e=$.default.join(fe.default.homedir(),".badgerclaw","workspace");h.default.mkdirSync(e,{recursive:!0});let t=$.default.join(e,".git");return h.default.existsSync(t)||(0,k.execSync)("git init",{cwd:e,stdio:"pipe",timeout:5e3}),h.default.writeFileSync($.default.join(e,"CLAUDE.md"),`You are a chat assistant connected to Matrix rooms via BadgerClaw.
|
|
69
69
|
|
|
70
70
|
When you receive a message, it will be formatted like:
|
|
71
71
|
[Room: RoomName] Sender: message text
|
|
@@ -74,14 +74,14 @@ Reply using the reply tool with botId="..." and roomId="..."
|
|
|
74
74
|
ALWAYS use the \`reply\` MCP tool to send your response back to the chat room.
|
|
75
75
|
Use the exact botId and roomId provided in the message \u2014 do not modify them.
|
|
76
76
|
Keep responses concise and helpful.
|
|
77
|
-
`),e}function ks(){try{f.default.mkdirSync(b.default.dirname(_t),{recursive:!0});try{let e=f.default.statSync(_t);if(Date.now()-e.mtimeMs<6e4)return!1;f.default.unlinkSync(_t)}catch{}return f.default.writeFileSync(_t,String(process.pid),{flag:"wx"}),!0}catch{return!1}}async function xs(e){if(!e?.bot_id)return{success:!1,message:"Missing bot_id in payload"};if(!ks())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 He(e.bot_id),{launchClaudeCode:o,recordSession:n,getActiveSessions:r,stopSession:s}=(wt(),vo(fo)),a=new Set;try{let v=(await T.default.get("http://localhost:7331/claude-code/status",{timeout:5e3})).data?.bots||[];for(let x of v)if(x.botId!==t&&x.enabled!==!1){let z=x.port;typeof z=="number"&&z>=7332&&a.add(z)}}catch{}for(let d=7332;d<=7340;d++)try{(0,I.execSync)(`lsof -ti :${d} >/dev/null 2>&1`,{stdio:"pipe",timeout:2e3}),a.add(d)}catch{}let i=7332;for(;a.has(i);)i++;let c=r();for(let d of c)d.port===i&&(console.log(`[command-executor] Stopping existing session ${d.sessionId} on port ${i}`),s(d.sessionId));try{(0,I.execSync)(`lsof -ti :${i} | xargs kill -9 2>/dev/null`,{stdio:"pipe",timeout:3e3})}catch{}let p=e.session_id||`session-${Date.now()}`;await T.default.post("http://localhost:7331/claude-code/toggle",{botId:t,enabled:!0,sessionId:p,port:i},{timeout:1e4});let g=Is();return o({sessionId:p,port:i,projectDir:g}),n(p,0,0,i,g),{success:!0,message:`Claude Code started globally for bot ${t} (session: ${p}, port: ${i})`}}catch(t){return{success:!1,message:`Failed to start Claude Code: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function He(e){if(e.startsWith("@"))return e;try{let o=(await T.default.get("http://localhost:7331/health",{timeout:1e4})).data?.bots||[],n=b.default.join(pe.default.homedir(),".badgerclaw","bot-mapping.json");if(f.default.existsSync(n)){let r=JSON.parse(f.default.readFileSync(n,"utf-8"));if(r[e])return r[e]}return o.length===1?o[0].botId:(console.warn(`[command-executor] Could not resolve bot UUID ${e} to Matrix ID, trying as-is`),e)}catch{return e}}async function Rs(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 He(e.bot_id),o=e.session_id;if(!o)try{let n=await T.default.get("http://localhost:7331/claude-code/status",{timeout:5e3}),s=(n.data?.bots||n.data?.rooms||[]).find(a=>a.botId===t);s&&(o=s.sessionId)}catch{}if(await T.default.post("http://localhost:7331/claude-code/toggle",{botId:t,enabled:!1},{timeout:1e4}),o){let{stopSession:n}=(wt(),vo(fo));n(o)}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 As(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in payload"};let o=t;o.startsWith("@")||(o=await He(t));let n=e?.session_id||`cc-${Date.now()}`;return await T.default.post("http://localhost:7331/claude-code/toggle",{botId:o,enabled:!0,sessionId:n,mode:"primary",tools:e?.tools},{timeout:1e4}),console.log(`[claude-control] Primary mode enabled for ${o} (session: ${n})`),{success:!0,message:`Claude Control (primary) started for ${o}`}}catch(t){return{success:!1,message:`Failed to start Claude Control: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Es(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in payload"};let o=t;return o.startsWith("@")||(o=await He(t)),await T.default.post("http://localhost:7331/claude-code/toggle",{botId:o,enabled:!1},{timeout:1e4}),console.log(`[claude-control] Primary mode disabled for ${o}`),{success:!0,message:`Claude Control stopped for ${o}`}}catch(t){return{success:!1,message:`Failed to stop Claude Control: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Ps(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 o=t;return o.startsWith("@")||(o=await He(t)),await T.default.post("http://localhost:7331/claude-code/tools",{botId:o,tools:e.tools},{timeout:1e4}),{success:!0,message:`Tools updated for ${o}`}}catch(t){return{success:!1,message:`Failed to update tools: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Os(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 T.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 Ds(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 T.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 Ts(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 T.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 Ls(e){let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in bot_refresh payload"};let o=t;t.startsWith("@")&&(o=t.split(":")[0].replace("@","").replace(/_bot$/,""));let n=b.default.join(pe.default.homedir(),".openclaw","badgerclaw","quarantine",`${o}.json`);try{f.default.rmSync(n,{force:!0})}catch{}try{return(0,I.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:15e3}),{success:!0,message:`Bot ${o} quarantine cleared, gateway restarted`}}catch{return{success:!1,message:`Quarantine cleared for ${o} but gateway restart failed`}}}async function Ns(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 T.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 I,f,b,pe,T,_t,yo=L(()=>{"use strict";I=require("child_process"),f=l(require("fs")),b=l(require("path")),pe=l(require("os")),T=l(require("axios"));ue();_t=b.default.join(pe.default.homedir(),".badgerclaw","claude-code.lock")});var Jn={};It(Jn,{heartbeatCommand:()=>wo,pushHeartbeat:()=>O});function St(e,t){if(e!=="start_claude_code"&&e!=="stop_claude_code")return!1;let o=`${e}:${t||"unknown"}`,n=Date.now();for(let[r,s]of bt)n-s>Fs&&bt.delete(r);return bt.has(o)?!0:(bt.set(o,n),!1)}function Kn(e,t){let o=Qe();return{instance_id:H(),timestamp:new Date().toISOString(),cli_version:t,plugin_version:e.pluginVersion||"unknown",hostname:o.hostname,os:o.os,arch:o.arch,uptime_seconds:o.uptimeSeconds,mem_free_mb:o.memFreeMb,gateway_status:e.status,gateway_pid:e.pid,last_gateway_restart:e.lastRestart,capabilities:oo(),hermes_containers:qo(),bots:e.bots.map(n=>({bot_id:n.botId,bot_username:n.botUsername,status:n.status,activity_state:n.activityState||"idle",active_task:n.activeTask||null,last_activity_at:n.lastActivity,messages_received:n.messagesReceived,messages_sent:n.messagesSent,chunked_messages:n.chunkedMessages,total_chunks_sent:n.totalChunksSent,errors:n.errors,last_error:n.lastError,last_error_at:n.lastErrorAt,uptime_seconds:n.uptimeSeconds,rooms_active:n.roomsActive,room_details:(n.roomDetails||[]).map(r=>({roomId:r.roomId,roomName:r.roomName,workspaceName:r.workspaceName||null,messagesInRoom:r.messagesInRoom,lastActivityInRoom:r.lastActivityInRoom})),claude_code_enabled:n.claudeCodeEnabled??!1,claude_code_session_id:n.claudeCodeSessionId??null,quarantined:n.quarantined??!1}))}}async function O(){let{version:e}=de(),t=await Fe(),o=Kn(t,e);await A().post(Wn,o)}function Gs(e,t){if(!e||e.status!==t.status)return!0;let o=new Map(e.bots.map(n=>[n.botId,n]));for(let n of t.bots){let r=o.get(n.botId);if(!r||r.status!==n.status||r.errors!==n.errors)return!0}return e.bots.length!==t.bots.length}async function Gn(){try{let o=(await A().get(Ms)).data?.shares||[],n={shares:o.map(r=>({botUserId:r.bot_user_id,sharedWithUserId:r.shared_with_user_id,expiresAt:r.expires_at||null,shareId:r.share_id}))};await Vn.default.post("http://localhost:7331/bot-share/sync",n,{timeout:1e4}),console.log(m.default.dim(` [${S()}] Share sync: ${o.length} active share(s) pushed to plugin`))}catch(e){console.log(m.default.dim(` [${S()}] Share sync failed (non-fatal): ${e.message}`))}}async function Hs(){if(!Fn){Fn=!0;try{await A().post(Bs,{instance_id:H()},{timeout:Us}),console.log(m.default.dim(` [${new Date().toISOString()}] Posted disconnect \u2014 dashboard will mark machine offline immediately.`))}catch(e){console.log(m.default.dim(` [${new Date().toISOString()}] Disconnect post failed (non-fatal): ${e?.message||e}`))}}}async function qn(){let e=_();if(!e?.expires_at)return;let t=new Date(e.expires_at).getTime(),o=Date.now(),n=300*1e3;if(t-o<n)if(console.log(m.default.dim(` [${S()}] Token expires soon \u2014 refreshing proactively...`)),await Et())console.log(m.default.green(` [${S()}] Token refreshed successfully`));else{let s=Oo()||"unknown reason";console.log(m.default.yellow(` [${S()}] Token refresh failed: ${s}`))}}function Ws(e,t){let o=3e3,n=3e4,r=null;async function s(){try{await qn();let i=_()?.access_token||e,c=require("eventsource"),p=new c(`${j}/api/v1/openclaw/events`,{headers:{Authorization:`Bearer ${i}`}});p.onopen=()=>{o=3e3,console.log(m.default.dim(` [${S()}] SSE connected \u2014 listening for real-time events`)),r&&clearTimeout(r),r=setTimeout(()=>{console.log(m.default.dim(` [${S()}] SSE proactive reconnect \u2014 refreshing token before expiry`)),p.close(),s()},Vs)},p.onmessage=async g=>{try{let d=JSON.parse(g.data);await Ks(d)}catch{}},p.onerror=async()=>{p.close(),r&&(clearTimeout(r),r=null),console.log(m.default.dim(` [${S()}] SSE disconnected \u2014 reconnecting in ${o/1e3}s...`)),setTimeout(s,o),o=Math.min(o*1.5,n)}}catch(a){console.log(m.default.dim(` [${S()}] SSE connection failed: ${a.message}`)),setTimeout(s,o),o=Math.min(o*1.5,n)}}s()}function S(){return new Date().toISOString()}async function Ks(e){if(e.type==="pair"){console.log(m.default.cyan(` [${S()}] Pair event: ${e.bot_name}`)),await Oe(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(m.default.green(` [${S()}] Gateway restarted \u2014 ${e.bot_name} is live`))}catch{}try{await O()}catch{}}else if(e.type==="bot.delete"&&e.bot_user_id){let t=e.bot_user_id,o=t.split(":")[0].replace("@","").replace(/_bot$/,"");console.log(m.default.yellow(` [${S()}] Bot deleted: ${t}`));try{let n=await import("fs"),r=await import("path"),s=await import("os"),a=r.join(s.homedir(),".openclaw","openclaw.json");if(ie(()=>{let c=JSON.parse(n.readFileSync(a,"utf-8")),p=!1;if(c.channels?.badgerclaw?.accounts?.[o]&&(delete c.channels.badgerclaw.accounts[o],p=!0),c.agents?.list){let g=c.agents.list.length;c.agents.list=c.agents.list.filter(d=>d.id!==o),c.agents.list.length!==g&&(p=!0)}return p&&n.writeFileSync(a,JSON.stringify(c,null,2)),p})){console.log(m.default.green(` [${S()}] Removed "${o}" from openclaw.json`));let{execSync:c}=await import("child_process");try{c("openclaw gateway restart",{stdio:"ignore"}),console.log(m.default.green(` [${S()}] Gateway restarted`))}catch{}}}catch(n){console.log(m.default.red(` [${S()}] Failed to clean up bot: ${n}`))}try{await O()}catch{}}else if(e.type==="gateway-restart"){console.log(m.default.cyan(` [${S()}] Remote gateway-restart received`));let t=await Se();console.log(t.success?m.default.green(` [${S()}] Gateway restarted`):m.default.red(` [${S()}] Gateway restart failed: ${t.message}`));try{await O()}catch{}}else if(e.type==="claude_code.start"&&e.bot_id){if(console.log(m.default.cyan(` [${S()}] Claude Code START: bot=${e.bot_id}`)),St("start_claude_code",e.bot_id)){console.log(m.default.dim(` [${S()}] Skipping duplicate start_claude_code (already processed)`));return}try{let t=await J({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 A().post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:t.success?"success":"failed",result:t.message}).catch(()=>{})}catch{}try{await O()}catch{}}else if(e.type==="claude_code.stop"&&e.bot_id){if(console.log(m.default.cyan(` [${S()}] Claude Code STOP: bot=${e.bot_id}`)),St("stop_claude_code",e.bot_id)){console.log(m.default.dim(` [${S()}] Skipping duplicate stop_claude_code (already processed)`));return}try{let t=await J({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 A().post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:t.success?"success":"failed",result:t.message}).catch(()=>{})}catch{}try{await O()}catch{}}else if(e.type==="command.execute"&&e.command_id){if(console.log(m.default.cyan(` [${S()}] Command: ${e.command_type} (${e.command_id})`)),St(e.command_type,e.payload?.bot_id)){console.log(m.default.dim(` [${S()}] Skipping duplicate ${e.command_type} (already processed)`));return}try{let t=A();await t.post(`/api/v1/dashboard/commands/${e.command_id}/ack`);let o=await J({id:e.command_id,command_type:e.command_type,payload:e.payload});await t.post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:o.success?"success":"failed",result:o.message,new_version:o.newVersion||null}),e.command_type==="update_cli"&&o.success&&(console.log(m.default.cyan(` [${S()}] CLI updated \u2014 restarting in 2s...`)),setTimeout(()=>process.exit(0),2e3))}catch{}try{await O()}catch{}}}var Hn,m,Vn,Un,js,Wn,Bs,Ms,Us,Fn,bt,Fs,wo,Vs,Ve=L(()=>{"use strict";Hn=require("commander"),m=l(require("chalk")),Vn=l(require("axios"));N();se();he();no();Mt();ue();yo();X();Te();ue();Ee();Un=3e4,js=15e4,Wn="/api/v1/dashboard/heartbeat",Bs="/api/v1/dashboard/disconnect",Ms="/api/v1/me/active-shares",Us=3e3,Fn=!1,bt=new Map,Fs=3e4;wo=new Hn.Command("heartbeat").description("Run heartbeat daemon \u2014 reports telemetry to the BadgerClaw dashboard every 30s").action(async()=>{let e=_();e||(console.log(m.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let{version:t}=de();console.log(m.default.green(`Heartbeat daemon started (v${t})`)),console.log(m.default.dim(` Instance: ${H()}`)),console.log(m.default.dim(` Interval: ${Un/1e3}s`)),console.log(m.default.dim(` Press Ctrl+C to stop.
|
|
78
|
-
`));let o=null,n=10,r=0,s=async()=>{try{await
|
|
79
|
-
[${new Date().toISOString()}] Received ${i} \u2014 posting disconnect and exiting.`)),
|
|
77
|
+
`),e}function Ds(){try{h.default.mkdirSync($.default.dirname(Ct),{recursive:!0});try{let e=h.default.statSync(Ct);if(Date.now()-e.mtimeMs<6e4)return!1;h.default.unlinkSync(Ct)}catch{}return h.default.writeFileSync(Ct,String(process.pid),{flag:"wx"}),!0}catch{return!1}}async function Ts(e){if(!e?.bot_id)return{success:!1,message:"Missing bot_id in payload"};if(!Ds())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 Ke(e.bot_id),{launchClaudeCode:o,recordSession:n,getActiveSessions:r,stopSession:s}=(vt(),Ro(_o)),a=new Set;try{let g=(await L.default.get("http://localhost:7331/claude-code/status",{timeout:5e3})).data?.bots||[];for(let S of g)if(S.botId!==t&&S.enabled!==!1){let R=S.port;typeof R=="number"&&R>=7332&&a.add(R)}}catch{}for(let f=7332;f<=7340;f++)try{(0,k.execSync)(`lsof -ti :${f} >/dev/null 2>&1`,{stdio:"pipe",timeout:2e3}),a.add(f)}catch{}let i=7332;for(;a.has(i);)i++;let l=r();for(let f of l)f.port===i&&(console.log(`[command-executor] Stopping existing session ${f.sessionId} on port ${i}`),s(f.sessionId));try{(0,k.execSync)(`lsof -ti :${i} | xargs kill -9 2>/dev/null`,{stdio:"pipe",timeout:3e3})}catch{}let m=e.session_id||`session-${Date.now()}`;await L.default.post("http://localhost:7331/claude-code/toggle",{botId:t,enabled:!0,sessionId:m,port:i},{timeout:1e4});let p=Os();return o({sessionId:m,port:i,projectDir:p}),n(m,0,0,i,p),{success:!0,message:`Claude Code started globally for bot ${t} (session: ${m}, port: ${i})`}}catch(t){return{success:!1,message:`Failed to start Claude Code: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Ke(e){if(e.startsWith("@"))return e;try{let o=(await L.default.get("http://localhost:7331/health",{timeout:1e4})).data?.bots||[],n=$.default.join(fe.default.homedir(),".badgerclaw","bot-mapping.json");if(h.default.existsSync(n)){let r=JSON.parse(h.default.readFileSync(n,"utf-8"));if(r[e])return r[e]}return o.length===1?o[0].botId:(console.warn(`[command-executor] Could not resolve bot UUID ${e} to Matrix ID, trying as-is`),e)}catch{return e}}async function Ls(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 Ke(e.bot_id),o=e.session_id;if(!o)try{let n=await L.default.get("http://localhost:7331/claude-code/status",{timeout:5e3}),s=(n.data?.bots||n.data?.rooms||[]).find(a=>a.botId===t);s&&(o=s.sessionId)}catch{}if(await L.default.post("http://localhost:7331/claude-code/toggle",{botId:t,enabled:!1},{timeout:1e4}),o){let{stopSession:n}=(vt(),Ro(_o));n(o)}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 Ns(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in payload"};let o=t;o.startsWith("@")||(o=await Ke(t));let n=e?.session_id||`cc-${Date.now()}`;return await L.default.post("http://localhost:7331/claude-code/toggle",{botId:o,enabled:!0,sessionId:n,mode:"primary",tools:e?.tools},{timeout:1e4}),console.log(`[claude-control] Primary mode enabled for ${o} (session: ${n})`),{success:!0,message:`Claude Control (primary) started for ${o}`}}catch(t){return{success:!1,message:`Failed to start Claude Control: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function js(e){try{let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in payload"};let o=t;return o.startsWith("@")||(o=await Ke(t)),await L.default.post("http://localhost:7331/claude-code/toggle",{botId:o,enabled:!1},{timeout:1e4}),console.log(`[claude-control] Primary mode disabled for ${o}`),{success:!0,message:`Claude Control stopped for ${o}`}}catch(t){return{success:!1,message:`Failed to stop Claude Control: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Bs(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 o=t;return o.startsWith("@")||(o=await Ke(t)),await L.default.post("http://localhost:7331/claude-code/tools",{botId:o,tools:e.tools},{timeout:1e4}),{success:!0,message:`Tools updated for ${o}`}}catch(t){return{success:!1,message:`Failed to update tools: ${t.response?.data?.message||t.message||"Unknown error"}`}}}async function Ms(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 L.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 Us(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 L.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 Fs(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 L.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 Hs(e){let t=e?.bot_user_id||e?.bot_id;if(!t)return{success:!1,message:"Missing bot_id in bot_refresh payload"};let o=t;t.startsWith("@")&&(o=t.split(":")[0].replace("@","").replace(/_bot$/,""));let n=$.default.join(fe.default.homedir(),".openclaw","badgerclaw","quarantine",`${o}.json`);try{h.default.rmSync(n,{force:!0})}catch{}try{return(0,k.execSync)("openclaw gateway restart",{stdio:"pipe",timeout:15e3}),{success:!0,message:`Bot ${o} quarantine cleared, gateway restarted`}}catch{return{success:!1,message:`Quarantine cleared for ${o} but gateway restart failed`}}}async function Gs(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 L.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 k,h,$,fe,L,Ct,$o=N(()=>{"use strict";k=require("child_process"),h=c(require("fs")),$=c(require("path")),fe=c(require("os")),L=c(require("axios"));pe();Ct=$.default.join(fe.default.homedir(),".badgerclaw","claude-code.lock")});var Zn={};Ot(Zn,{heartbeatCommand:()=>vo,pushHeartbeat:()=>D});function kt(e,t){if(e!=="start_claude_code"&&e!=="stop_claude_code")return!1;let o=`${e}:${t||"unknown"}`,n=Date.now();for(let[r,s]of It)n-s>Js&&It.delete(r);return It.has(o)?!0:(It.set(o,n),!1)}function Xn(e,t){let o=tt();return{instance_id:V(),timestamp:new Date().toISOString(),cli_version:t,plugin_version:e.pluginVersion||"unknown",hostname:o.hostname,os:o.os,arch:o.arch,uptime_seconds:o.uptimeSeconds,mem_free_mb:o.memFreeMb,gateway_status:e.status,gateway_pid:e.pid,last_gateway_restart:e.lastRestart,capabilities:le(),hermes_containers:an(),bots:e.bots.map(n=>({bot_id:n.botId,bot_username:n.botUsername,status:n.status,activity_state:n.activityState||"idle",active_task:n.activeTask||null,last_activity_at:n.lastActivity,messages_received:n.messagesReceived,messages_sent:n.messagesSent,chunked_messages:n.chunkedMessages,total_chunks_sent:n.totalChunksSent,errors:n.errors,last_error:n.lastError,last_error_at:n.lastErrorAt,uptime_seconds:n.uptimeSeconds,rooms_active:n.roomsActive,room_details:(n.roomDetails||[]).map(r=>({roomId:r.roomId,roomName:r.roomName,workspaceName:r.workspaceName||null,messagesInRoom:r.messagesInRoom,lastActivityInRoom:r.lastActivityInRoom})),claude_code_enabled:n.claudeCodeEnabled??!1,claude_code_session_id:n.claudeCodeSessionId??null,quarantined:n.quarantined??!1}))}}async function D(){let{version:e}=ce(),t=await We(),o=Xn(t,e);await E().post(zn,o)}function Ys(e,t){if(!e||e.status!==t.status)return!0;let o=new Map(e.bots.map(n=>[n.botId,n]));for(let n of t.bots){let r=o.get(n.botId);if(!r||r.status!==n.status||r.errors!==n.errors)return!0}return e.bots.length!==t.bots.length}async function qn(){try{let o=(await E().get(Ks)).data?.shares||[],n={shares:o.map(r=>({botUserId:r.bot_user_id,sharedWithUserId:r.shared_with_user_id,expiresAt:r.expires_at||null,shareId:r.share_id}))};await Yn.default.post("http://localhost:7331/bot-share/sync",n,{timeout:1e4}),console.log(u.default.dim(` [${v()}] Share sync: ${o.length} active share(s) pushed to plugin`))}catch(e){console.log(u.default.dim(` [${v()}] Share sync failed (non-fatal): ${e.message}`))}}async function zs(){if(!Kn){Kn=!0;try{await E().post(Ws,{instance_id:V()},{timeout:qs}),console.log(u.default.dim(` [${new Date().toISOString()}] Posted disconnect \u2014 dashboard will mark machine offline immediately.`))}catch(e){console.log(u.default.dim(` [${new Date().toISOString()}] Disconnect post failed (non-fatal): ${e?.message||e}`))}}}async function Qn(){let e=_();if(!e?.expires_at)return;let t=new Date(e.expires_at).getTime(),o=Date.now(),n=300*1e3;if(t-o<n)if(console.log(u.default.dim(` [${v()}] Token expires soon \u2014 refreshing proactively...`)),await jt())console.log(u.default.green(` [${v()}] Token refreshed successfully`));else{let s=jo()||"unknown reason";console.log(u.default.yellow(` [${v()}] Token refresh failed: ${s}`))}}function Qs(e,t){let o=3e3,n=3e4,r=null;async function s(){try{await Qn();let i=_()?.access_token||e,l=require("eventsource"),m=new l(`${B}/api/v1/openclaw/events`,{headers:{Authorization:`Bearer ${i}`}});m.onopen=()=>{o=3e3,console.log(u.default.dim(` [${v()}] SSE connected \u2014 listening for real-time events`)),r&&clearTimeout(r),r=setTimeout(()=>{console.log(u.default.dim(` [${v()}] SSE proactive reconnect \u2014 refreshing token before expiry`)),m.close(),s()},Xs)},m.onmessage=async p=>{try{let f=JSON.parse(p.data);await Zs(f)}catch{}},m.onerror=async()=>{m.close(),r&&(clearTimeout(r),r=null),console.log(u.default.dim(` [${v()}] SSE disconnected \u2014 reconnecting in ${o/1e3}s...`)),setTimeout(s,o),o=Math.min(o*1.5,n)}}catch(a){console.log(u.default.dim(` [${v()}] SSE connection failed: ${a.message}`)),setTimeout(s,o),o=Math.min(o*1.5,n)}}s()}function v(){return new Date().toISOString()}async function Zs(e){if(e.type==="pair"){console.log(u.default.cyan(` [${v()}] Pair event: ${e.bot_name}`)),await Ne(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(u.default.green(` [${v()}] Gateway restarted \u2014 ${e.bot_name} is live`))}catch{}try{await D()}catch{}}else if(e.type==="bot.delete"&&e.bot_user_id){let t=e.bot_user_id,o=t.split(":")[0].replace("@","").replace(/_bot$/,"");console.log(u.default.yellow(` [${v()}] Bot deleted: ${t}`));try{let n=await import("fs"),r=await import("path"),s=await import("os"),a=r.join(s.homedir(),".openclaw","openclaw.json");if(ie(()=>{let l=JSON.parse(n.readFileSync(a,"utf-8")),m=!1;if(l.channels?.badgerclaw?.accounts?.[o]&&(delete l.channels.badgerclaw.accounts[o],m=!0),l.agents?.list){let p=l.agents.list.length;l.agents.list=l.agents.list.filter(f=>f.id!==o),l.agents.list.length!==p&&(m=!0)}return m&&n.writeFileSync(a,JSON.stringify(l,null,2)),m})){console.log(u.default.green(` [${v()}] Removed "${o}" from openclaw.json`));let{execSync:l}=await import("child_process");try{l("openclaw gateway restart",{stdio:"ignore"}),console.log(u.default.green(` [${v()}] Gateway restarted`))}catch{}}}catch(n){console.log(u.default.red(` [${v()}] Failed to clean up bot: ${n}`))}try{await D()}catch{}}else if(e.type==="gateway-restart"){console.log(u.default.cyan(` [${v()}] Remote gateway-restart received`));let t=await ke();console.log(t.success?u.default.green(` [${v()}] Gateway restarted`):u.default.red(` [${v()}] Gateway restart failed: ${t.message}`));try{await D()}catch{}}else if(e.type==="claude_code.start"&&e.bot_id){if(console.log(u.default.cyan(` [${v()}] Claude Code START: bot=${e.bot_id}`)),kt("start_claude_code",e.bot_id)){console.log(u.default.dim(` [${v()}] Skipping duplicate start_claude_code (already processed)`));return}try{let t=await Y({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 E().post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:t.success?"success":"failed",result:t.message}).catch(()=>{})}catch{}try{await D()}catch{}}else if(e.type==="claude_code.stop"&&e.bot_id){if(console.log(u.default.cyan(` [${v()}] Claude Code STOP: bot=${e.bot_id}`)),kt("stop_claude_code",e.bot_id)){console.log(u.default.dim(` [${v()}] Skipping duplicate stop_claude_code (already processed)`));return}try{let t=await Y({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 E().post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:t.success?"success":"failed",result:t.message}).catch(()=>{})}catch{}try{await D()}catch{}}else if(e.type==="command.execute"&&e.command_id){if(console.log(u.default.cyan(` [${v()}] Command: ${e.command_type} (${e.command_id})`)),kt(e.command_type,e.payload?.bot_id)){console.log(u.default.dim(` [${v()}] Skipping duplicate ${e.command_type} (already processed)`));return}try{let t=E();await t.post(`/api/v1/dashboard/commands/${e.command_id}/ack`);let o=await Y({id:e.command_id,command_type:e.command_type,payload:e.payload});await t.post(`/api/v1/dashboard/commands/${e.command_id}/result`,{status:o.success?"success":"failed",result:o.message,new_version:o.newVersion||null}),e.command_type==="update_cli"&&o.success&&(console.log(u.default.cyan(` [${v()}] CLI updated \u2014 restarting in 2s...`)),setTimeout(()=>process.exit(0),2e3))}catch{}try{await D()}catch{}}}var Jn,u,Yn,Wn,Vs,zn,Ws,Ks,qs,Kn,It,Js,vo,Xs,qe=N(()=>{"use strict";Jn=require("commander"),u=c(require("chalk")),Yn=c(require("axios"));j();se();we();de();zt();pe();$o();X();Be();pe();De();Wn=3e4,Vs=15e4,zn="/api/v1/dashboard/heartbeat",Ws="/api/v1/dashboard/disconnect",Ks="/api/v1/me/active-shares",qs=3e3,Kn=!1,It=new Map,Js=3e4;vo=new Jn.Command("heartbeat").description("Run heartbeat daemon \u2014 reports telemetry to the BadgerClaw dashboard every 30s").action(async()=>{let e=_();e||(console.log(u.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let{version:t}=ce();console.log(u.default.green(`Heartbeat daemon started (v${t})`)),console.log(u.default.dim(` Instance: ${V()}`)),console.log(u.default.dim(` Interval: ${Wn/1e3}s`)),console.log(u.default.dim(` Press Ctrl+C to stop.
|
|
78
|
+
`));let o=null,n=10,r=0,s=async()=>{try{await Qn();let i=await We(),l=Xn(i,t);Ys(o,i)&&o!==null&&console.log(u.default.cyan(` [${new Date().toISOString()}] State change detected \u2014 pushing immediately`));let p=E(),f=await p.post(zn,l);r=0;let g=i.bots.length,S=i.bots.filter(x=>x.status==="running").length;console.log(u.default.dim(` [${new Date().toISOString()}] gateway=${i.status} bots=${S}/${g} running mem=${l.mem_free_mb}MB free`)),o=i;try{let A=await cn({api:{post:async(O,U)=>{let ze=await p.post(O,U);return{status:ze.status,data:ze.data}}}});for(let O of A)O.attempted&&(O.posted||O.envUpdated)&&console.log(u.default.cyan(` [${new Date().toISOString()}] Hermes recovery key captured for ${O.bot_id} (posted=${O.posted}, env_updated=${O.envUpdated})`))}catch(x){console.log(u.default.dim(` [${new Date().toISOString()}] Hermes recovery-key retry skipped: ${x.message}`))}let R=f.data?.pending_commands||[],Et=!1;for(let x of R)try{if(console.log(u.default.cyan(` [${new Date().toISOString()}] Received command: ${x.command_type} (${x.id})`)),await p.post(`/api/v1/dashboard/commands/${x.id}/ack`),kt(x.command_type,x.payload?.bot_id)){console.log(u.default.dim(` [${new Date().toISOString()}] Skipping duplicate ${x.command_type}`));continue}let A=await Y(x);console.log(A.success?u.default.green(` [${new Date().toISOString()}] ${A.message}`):u.default.red(` [${new Date().toISOString()}] ${A.message}`)),await p.post(`/api/v1/dashboard/commands/${x.id}/result`,{status:A.success?"success":"failed",result:A.message,new_version:A.newVersion||null}),x.command_type==="update_cli"&&A.success&&(Et=!0)}catch(A){console.log(u.default.dim(` [${new Date().toISOString()}] Command ${x.id} error: ${A.message}`))}if(Et){console.log(u.default.cyan(` [${new Date().toISOString()}] CLI updated \u2014 restarting in 2s...`));try{await D()}catch{}setTimeout(()=>process.exit(0),2e3);return}}catch(i){i instanceof be&&(console.log(u.default.yellow(` [${new Date().toISOString()}] Account deactivated \u2014 signed out. Exiting daemon.`)),process.exit(0)),r+=1,console.log(u.default.dim(` [${new Date().toISOString()}] Heartbeat failed (${r}/${n}): ${i.message}`)),r>=n&&(console.log(u.default.red(` [${new Date().toISOString()}] ${n} consecutive heartbeat failures \u2014 exiting so launchctl/systemd can restart the daemon.`)),process.exit(1))}};await s(),await qn(),setInterval(s,Wn),setInterval(qn,Vs),Qs(e.access_token,t);let a=i=>{console.log(u.default.yellow(`
|
|
79
|
+
[${new Date().toISOString()}] Received ${i} \u2014 posting disconnect and exiting.`)),zs().finally(()=>{process.exit(0)})};process.on("SIGTERM",a),process.on("SIGINT",a),await new Promise(()=>{})});Xs=2700*1e3});var gr=require("commander");var vn=require("commander"),te=c(require("chalk")),Cn=c(require("ora")),In=c(require("open")),no=c(require("os"));var Dt=c(require("crypto"));function Ao(){return Dt.default.randomBytes(32).toString("base64url")}function Eo(e){return Dt.default.createHash("sha256").update(e).digest("base64url")}j();we();se();X();Be();var wn=c(require("os"));var dt=c(require("fs")),ue=c(require("path")),Me=c(require("os")),Ue=require("child_process"),pn="ai.badgerclaw.watch",gn=ue.default.join(Me.default.homedir(),"Library","LaunchAgents"),eo=ue.default.join(gn,`${pn}.plist`);function es(){try{return(0,Ue.execSync)("which badgerclaw",{encoding:"utf-8"}).trim()}catch{return"/opt/homebrew/bin/badgerclaw"}}function ts(){try{return(0,Ue.execSync)("which node",{encoding:"utf-8"}).trim()}catch{return"/opt/homebrew/bin/node"}}function os(e){let t=ue.default.join(Me.default.homedir(),".badgerclaw"),o=ts(),n=ue.default.dirname(o),r=ue.default.dirname(e),s=[n,r,"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin"],a=new Set,i=s.filter(l=>!l||a.has(l)?!1:(a.add(l),!0)).join(":");return`<?xml version="1.0" encoding="UTF-8"?>
|
|
80
80
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
81
81
|
<plist version="1.0">
|
|
82
82
|
<dict>
|
|
83
83
|
<key>Label</key>
|
|
84
|
-
<string>${
|
|
84
|
+
<string>${pn}</string>
|
|
85
85
|
<key>ProgramArguments</key>
|
|
86
86
|
<array>
|
|
87
87
|
<string>${o}</string>
|
|
@@ -104,7 +104,7 @@ Keep responses concise and helpful.
|
|
|
104
104
|
<key>ThrottleInterval</key>
|
|
105
105
|
<integer>30</integer>
|
|
106
106
|
</dict>
|
|
107
|
-
</plist>`}function
|
|
107
|
+
</plist>`}function fn(){if(Me.default.platform()==="darwin")try{let e=es();dt.default.mkdirSync(gn,{recursive:!0}),dt.default.mkdirSync(ue.default.join(Me.default.homedir(),".badgerclaw"),{recursive:!0}),dt.default.writeFileSync(eo,os(e));try{(0,Ue.execSync)(`launchctl unload "${eo}" 2>/dev/null`)}catch{}(0,Ue.execSync)(`launchctl load "${eo}"`),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")}}var ut=c(require("fs")),Fe=c(require("path")),mt=c(require("os")),K=require("child_process"),to="badgerclaw-watch.service",hn=Fe.default.join(mt.default.homedir(),".config","systemd","user"),ns=Fe.default.join(hn,to);function rs(){try{return(0,K.execSync)("which badgerclaw",{encoding:"utf-8"}).trim()}catch{return"/usr/local/bin/badgerclaw"}}function ss(){try{return(0,K.execSync)("which node",{encoding:"utf-8"}).trim()}catch{return"/usr/bin/node"}}function as(e){let t=ss(),o=Fe.default.dirname(t);return`[Unit]
|
|
108
108
|
Description=BadgerClaw background watcher (heartbeat + pair events)
|
|
109
109
|
After=network-online.target
|
|
110
110
|
Wants=network-online.target
|
|
@@ -120,31 +120,38 @@ StandardError=append:%h/.badgerclaw/heartbeat.log
|
|
|
120
120
|
|
|
121
121
|
[Install]
|
|
122
122
|
WantedBy=default.target
|
|
123
|
-
`}function
|
|
124
|
-
\u26A0\uFE0F systemd lingering is OFF for root. The gateway will stop on SSH disconnect.`)),console.log(
|
|
125
|
-
`));for(let r of n){let s=
|
|
126
|
-
\u2705 Refresh triggered. Watch the gateway logs for token refresh progress:`)),console.log(
|
|
127
|
-
\u{1F4F1} Pair event for ${r.bot_name} targets ${s} \u2014 not us, skipping`));return}console.log(
|
|
128
|
-
\u{1F4F1} Pair event received: ${r.bot_name}`)),await
|
|
129
|
-
\u{1F504} Remote gateway-restart command received`));let s=await
|
|
130
|
-
${i} Bot ${a}: ${s} \u2014 removing from OpenClaw config...`));try{let{changed:
|
|
131
|
-
\u{1F916} Claude Code START received: bot=${r.bot_id} room=${r.room_id}`));try{let s=await
|
|
132
|
-
\u{1F916} Claude Code STOP received: bot=${r.bot_id} room=${r.room_id}`));try{let s=await
|
|
133
|
-
\u26A1 SSE command received: ${r.command_type} (${r.command_id})`));try{let s=
|
|
134
|
-
`),!0):!1}async function
|
|
123
|
+
`}function is(){try{return(0,K.execSync)("systemctl --user --version",{stdio:"ignore"}),!0}catch{return!1}}function yn(){if(mt.default.platform()==="linux"){if(!is()){console.log("\x1B[2mNote: systemctl --user not available. Run `badgerclaw heartbeat` manually or start it under a process manager.\x1B[0m");return}try{let e=rs();ut.default.mkdirSync(hn,{recursive:!0}),ut.default.mkdirSync(Fe.default.join(mt.default.homedir(),".badgerclaw"),{recursive:!0}),ut.default.writeFileSync(ns,as(e)),(0,K.execSync)("systemctl --user daemon-reload",{stdio:"ignore"});try{(0,K.execSync)(`systemctl --user restart ${to}`,{stdio:"ignore"})}catch{}(0,K.execSync)(`systemctl --user enable --now ${to}`,{stdio:"ignore"});try{let t=(0,K.execSync)("whoami",{encoding:"utf-8"}).trim();(0,K.execSync)(`loginctl enable-linger ${t}`,{stdio:"ignore"})}catch{}console.log("\x1B[2mHeartbeat daemon registered as systemd user service (auto-starts on login).\x1B[0m")}catch{console.log("\x1B[2mNote: could not register systemd user service. Run `badgerclaw heartbeat` manually.\x1B[0m")}}}function bn(){let e=wn.default.platform();if(e==="darwin")return fn();if(e==="linux")return yn();console.log("\x1B[2mNote: background daemon install not supported on "+e+". Run `badgerclaw heartbeat` manually.\x1B[0m")}var oo=c(require("fs")),Sn=c(require("os")),$n=c(require("path")),me=require("child_process"),q=c(require("chalk"));De();var _n=$n.default.join(Sn.default.homedir(),".openclaw","openclaw.json");function cs(e=!1){let t=null;try{oo.default.existsSync(_n)&&(t=JSON.parse(oo.default.readFileSync(_n,"utf-8"))?.gateway?.mode??null)}catch{}if(typeof t=="string"&&t.length>0)return!0;e||console.log(q.default.dim(" Setting gateway.mode=local..."));try{return ie(()=>((0,me.execSync)("openclaw config set gateway.mode local",{encoding:"utf-8",timeout:1e4,stdio:"pipe"}),!0))}catch(o){return e||(console.log(q.default.yellow(` \u26A0\uFE0F Could not set gateway.mode automatically: ${o.message}`)),console.log(q.default.yellow(" Run `openclaw config set gateway.mode local` manually."))),!1}}function pt(e={}){let t=e.verbose!==!1,o=a=>{t&&console.log(a)};o(q.default.dim(" Installing OpenClaw gateway service (if not present)...")),(0,me.spawnSync)("openclaw",["gateway","install"],{stdio:t?"inherit":"pipe",shell:!0}).status!==0&&o(q.default.yellow(" \u26A0\uFE0F `openclaw gateway install` returned non-zero \u2014 continuing.")),cs(!t),o(q.default.dim(" Starting OpenClaw gateway..."));let r=(0,me.spawnSync)("openclaw",["gateway","start"],{stdio:t?"inherit":"pipe",shell:!0}),s="started";return r.status!==0&&(r=(0,me.spawnSync)("openclaw",["gateway","restart"],{stdio:t?"inherit":"pipe",shell:!0}),s="restarted"),r.status!==0?{ok:!1,error:"gateway start/restart returned non-zero exit"}:{ok:!0,state:s}}function gt(){if(process.platform!=="linux"||process.getuid?.()!==0)return;let e=(0,me.spawnSync)("loginctl",["show-user","root","--property=Linger"],{encoding:"utf-8"});e.status===0&&/Linger=no/.test(e.stdout||"")&&(console.log(q.default.yellow(`
|
|
124
|
+
\u26A0\uFE0F systemd lingering is OFF for root. The gateway will stop on SSH disconnect.`)),console.log(q.default.dim(" Enable persistence: loginctl enable-linger root")))}var ls=2e3,ds=12e4,kn=new vn.Command("login").description("Log in to BadgerClaw via browser").action(async()=>{let e=Ao(),t=Eo(e),o=`${Lo}/cli-auth?code=${t}`;console.log(te.default.yellow("Opening browser for authentication...")),console.log(te.default.dim(`If the browser doesn't open, visit: ${o}`)),await(0,In.default)(o);let n=(0,Cn.default)("Waiting for authentication...").start(),r=Uo(),s=Date.now();for(;Date.now()-s<ds;){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:l,expires_at:m,refresh_token:p,email:f}=a.data,g=Nt(),S=`openclaw-${no.default.hostname().toLowerCase().replace(/[^a-z0-9]/g,"-")}-${g}`,R=S;try{let{version:A}=ce(),O=await r.post("/api/v1/openclaw/register",{instance_id:S,label:no.default.hostname(),version:A,machine_fingerprint:g},{headers:{Authorization:`Bearer ${i}`}}),U=O.data?.result||O.data;U&&typeof U.instance_id=="string"&&U.instance_id.length>0&&(R=U.instance_id)}catch(A){let O=A?.response?.status;if(O===403||O===409){let U=A?.response?.data,ze=Array.isArray(U?.errors)&&typeof U.errors[0]=="string"?U.errors[0]:null,Pt=U?.detail,fr=typeof Pt=="string"?Pt:Pt?.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,hr=ze||fr||`Login refused (status ${O}). Open the BadgerClaw app to check your subscription state.`;n.fail(te.default.red(hr)),process.exit(1)}}Qe({access_token:i,user_id:l,instance_id:R,expires_at:m,refresh_token:p,email:f}),n.succeed(te.default.green(`Logged in as ${et(l)}`)),pt({verbose:!0}).ok||console.log(te.default.yellow("\n \u26A0\uFE0F Logged in, but gateway failed to start. Re-run `badgerclaw setup` to repair."));let x=await je(!0);x>0&&console.log(te.default.green(`\u2705 ${x} bot(s) automatically paired to OpenClaw.`)),bn(),gt();return}}catch{}await new Promise(a=>setTimeout(a,ls))}n.fail(te.default.red("Authentication timed out. Please try again.")),process.exit(1)});var Rn=require("commander"),He=c(require("chalk")),ft=c(require("fs")),ve=c(require("os")),Ve=c(require("path")),Ge=require("child_process");j();se();function us(){let e=process.platform;if(e==="darwin"){let t=Ve.default.join(ve.default.homedir(),"Library","LaunchAgents","ai.badgerclaw.watch.plist");(0,Ge.spawnSync)("launchctl",["unload",t],{stdio:"ignore"});try{ft.default.unlinkSync(t)}catch{}}else if(e==="linux"){(0,Ge.spawnSync)("systemctl",["--user","disable","--now","badgerclaw-watch.service"],{stdio:"ignore"});let t=Ve.default.join(ve.default.homedir(),".config","systemd","user","badgerclaw-watch.service");try{ft.default.unlinkSync(t)}catch{}(0,Ge.spawnSync)("systemctl",["--user","daemon-reload"],{stdio:"ignore"})}(0,Ge.spawnSync)("pkill",["-f","badgerclaw heartbeat"],{stdio:"ignore"})}function xn(e){try{return ft.default.rmSync(e,{recursive:!0,force:!0}),!0}catch{return!1}}var An=new Rn.Command("logout").description("Disconnect this machine, log out of BadgerClaw, and wipe local state").option("--keep-state","Only clear auth.json (legacy behaviour); preserve bot-mapping, claude-sessions, heartbeat logs, and plugin quarantine ledger").action(async e=>{let t=_();if(t)try{let a=Oe(t.access_token),{version:i}=ce();await a.post("/api/v1/openclaw/register",{instance_id:t.instance_id,label:ve.default.hostname(),version:i,online:!1})}catch{}if(us(),e.keepState){Ae(),console.log(He.default.green("Logged out \u2014 auth cleared. (state preserved with --keep-state)"));return}let o=Ve.default.join(ve.default.homedir(),".badgerclaw"),n=xn(o),r=Ve.default.join(ve.default.homedir(),".openclaw","badgerclaw"),s=xn(r);n||s?(console.log(He.default.green("Logged out \u2014 local state cleared.")),console.log(He.default.dim(` Removed: ${o}${s?`, ${r}`:""}`))):console.log(He.default.green("Logged out."))});var En=require("commander"),ro=c(require("chalk"));j();var Pn=new En.Command("status").description("Show connected instance info").action(async()=>{let e=_();(!e||!Ze())&&(console.log(ro.default.red("Not logged in. Run `badgerclaw login` to authenticate.")),process.exit(1)),console.log(ro.default.green("Authenticated")),console.log(` User: ${et(e.user_id)}`),console.log(` Instance: ${e.instance_id}`),console.log(` Expires: ${new Date(e.expires_at).toLocaleDateString()}`)});var Ie=require("commander"),w=c(require("chalk")),bt=c(require("ora")),wt=c(require("fs")),jn=c(require("os")),Bn=c(require("path")),Mn=require("child_process");j();se();var Ce=c(require("fs")),On=c(require("os")),Dn=c(require("path")),Tn=require("child_process");De();function ht(e){let t=e.split(":")[0].replace(/^@/,"").replace(/_bot$/,""),o=Dn.join(On.homedir(),".openclaw","openclaw.json");return Ce.existsSync(o)?{changed:ie(()=>{let r=JSON.parse(Ce.readFileSync(o,"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&&Ce.writeFileSync(o,JSON.stringify(r,null,2)),s})}:{changed:!1}}function yt(){try{return(0,Tn.execSync)("openclaw gateway restart",{stdio:"ignore"}),!0}catch{return!1}}de();function Ln(e){return e.hermes?"hermes":"openclaw"}function Nn(e){return e.openclaw.installed?"openclaw":e.hermes.installed?"hermes":"openclaw"}function ao(){Ze()||(console.log(w.default.red("Not logged in. Run `badgerclaw login` to authenticate.")),process.exit(1))}function ms(e){return/^[a-z0-9_]{4,20}$/.test(e)}function Un(e){return e.replace(/_bot$/,"")}var so=["openclaw","hermes"],ps=new Ie.Command("create").description("Create a new bot").argument("<name>","Bot name (4-20 chars, lowercase alphanumeric + underscores)").option("-r, --runtime <runtime>",`Bot runtime: ${so.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)=>{ao(),ms(e)||(console.log(w.default.red("Invalid bot name. Must be 4-20 characters, lowercase alphanumeric and underscores only.")),process.exit(1));let o=t.runtime??Nn(le());so.includes(o)||(console.log(w.default.red(`Invalid --runtime "${t.runtime}". Must be one of: ${so.join(", ")}.`)),process.exit(1));let n=(0,bt.default)(`Creating ${o} bot "${e}"...`).start();try{let r=E();o==="hermes"?await r.post("/api/v1/bots",{bot_name:e,bot_username:e,runtime:"hermes"}):await r.post("/api/v1/openclaw/bots",{username:e}),n.succeed(w.default.green(`Bot "${e}" (${o}) created successfully!`)),o==="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;n.fail(w.default.red(`Failed to create bot: ${s}`)),process.exit(1)}}),gs=new Ie.Command("list").description("List your bots").action(async()=>{ao();let e=(0,bt.default)("Fetching bots...").start();try{let n=(await E().get("/api/v1/openclaw/bots")).data?.bots||[];if(e.stop(),n.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 (${n.length}):
|
|
125
|
+
`));for(let r of n){let s=Un(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 o=t.response?.data?.errors?.[0]||t.message;e.fail(w.default.red(`Failed to list bots: ${o}`)),process.exit(1)}}),fs=new Ie.Command("delete").description("Deactivate a bot").argument("<name>","Bot name to deactivate").action(async e=>{ao();let t=(0,bt.default)(`Deactivating bot "${e}"...`).start();try{await E().delete(`/api/v1/openclaw/bots/${e}`),t.succeed(w.default.green(`Bot "${e}" deactivated.`));let{changed:n}=ht(e);n&&(console.log(w.default.dim(" Removed from openclaw.json")),yt()&&console.log(w.default.dim(" Gateway restarted")))}catch(o){let n=o.response?.data?.errors?.[0]||o.message;t.fail(w.default.red(`Failed to deactivate bot: ${n}`)),process.exit(1)}});function hs(e){let t=e.trim();return t.startsWith("@")&&(t=t.slice(1)),t.includes(":")&&(t=t.split(":")[0]),Un(t)}function ys(e){let t=e.replace(/[^a-zA-Z0-9_.-]/g,"_")||"default";return Bn.default.join(jn.default.homedir(),".openclaw","badgerclaw","quarantine",`${t}.json`)}var ws=new Ie.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=hs(e);t||(console.log(w.default.red(`Couldn't parse bot name "${e}".`)),process.exit(1));let o=ys(t),n=!1;if(wt.default.existsSync(o))try{let r=JSON.parse(wt.default.readFileSync(o,"utf-8")),s=r.failureCount??"?",a=r.quarantinedAt??"?";console.log(w.default.dim(` Quarantine for "${t}" \u2014 ${s} failures, since ${a}`)),wt.default.unlinkSync(o),n=!0}catch(r){console.log(w.default.red(`Failed to clear quarantine ledger at ${o}: ${r.message}`)),process.exit(1)}console.log(n?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,Mn.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(`
|
|
126
|
+
\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."))}),Fn=new Ie.Command("bot").description("Manage bots").addCommand(ps).addCommand(gs).addCommand(fs).addCommand(ws);Be();var er=require("commander"),y=c(require("chalk"));j();X();Be();pe();qe();$o();se();we();var tr=new er.Command("watch").description("Watch for bot pair events in real-time (no polling)").action(async()=>{let e=_();e||(console.log(y.default.yellow("Not logged in.")),process.exit(1)),await je(!1),console.log(y.default.green("\u{1F534} Listening for pair events... (Ctrl+C to stop)"));let t=require("eventsource"),o=new t(`${B}/api/v1/openclaw/events`,{headers:{Authorization:`Bearer ${e.access_token}`}});o.onmessage=async n=>{try{let r=JSON.parse(n.data);if(r.type==="pair"){let s=r.target_instance_id;if(s&&s!==V()){console.log(y.default.dim(`
|
|
127
|
+
\u{1F4F1} Pair event for ${r.bot_name} targets ${s} \u2014 not us, skipping`));return}console.log(y.default.cyan(`
|
|
128
|
+
\u{1F4F1} Pair event received: ${r.bot_name}`)),await Ne(r.pair_code,r.bot_name,r.bot_user_id,!1);try{let{execSync:a}=await import("child_process");a("openclaw gateway restart",{stdio:"ignore"}),console.log(y.default.green(" \u2705 Gateway restarted \u2014 bot is live!"))}catch{console.log(y.default.dim(" Gateway restart failed \u2014 run: openclaw gateway restart"))}}else if(r.type==="gateway-restart"){console.log(y.default.cyan(`
|
|
129
|
+
\u{1F504} Remote gateway-restart command received`));let s=await ke();s.success?console.log(y.default.green(` \u2705 Gateway restarted: ${s.message}`)):console.log(y.default.red(` \u274C Gateway restart failed: ${s.message}`));try{await D(),console.log(y.default.dim(" Heartbeat pushed with updated status."))}catch{console.log(y.default.dim(" Could not push heartbeat."))}}else if((r.type==="bot.delete"||r.type==="bot.disconnect")&&r.bot_user_id){let s=r.bot_user_id,a=r.type==="bot.delete"?"deleted":"disconnected",i=r.type==="bot.delete"?"\u{1F5D1}\uFE0F ":"\u{1F50C}";console.log(y.default.yellow(`
|
|
130
|
+
${i} Bot ${a}: ${s} \u2014 removing from OpenClaw config...`));try{let{changed:l}=ht(s);l?(console.log(y.default.green(" \u2705 Removed from openclaw.json")),yt()?console.log(y.default.green(" \u2705 Gateway restarted")):console.log(y.default.dim(" Gateway restart failed \u2014 restart manually"))):console.log(y.default.dim(" Account not found in openclaw.json \u2014 nothing to remove"))}catch(l){console.log(y.default.red(` Failed to update openclaw.json: ${l}`))}}else if(r.type==="claude_code.start"&&r.bot_id&&r.room_id){console.log(y.default.cyan(`
|
|
131
|
+
\u{1F916} Claude Code START received: bot=${r.bot_id} room=${r.room_id}`));try{let s=await Y({id:r.command_id||`cc-start-${Date.now()}`,command_type:"start_claude_code",payload:{bot_id:r.bot_id,room_id:r.room_id,session_id:r.session_id}});if(console.log(s.success?y.default.green(` \u2705 ${s.message}`):y.default.red(` \u274C ${s.message}`)),r.command_id)try{await E().post(`/api/v1/dashboard/commands/${r.command_id}/result`,{status:s.success?"success":"failed",result:s.message})}catch{}try{await D()}catch{}}catch(s){console.log(y.default.red(` Claude Code start error: ${s.message}`))}}else if(r.type==="claude_code.stop"&&r.bot_id&&r.room_id){console.log(y.default.cyan(`
|
|
132
|
+
\u{1F916} Claude Code STOP received: bot=${r.bot_id} room=${r.room_id}`));try{let s=await Y({id:r.command_id||`cc-stop-${Date.now()}`,command_type:"stop_claude_code",payload:{bot_id:r.bot_id,room_id:r.room_id,session_id:r.session_id}});if(console.log(s.success?y.default.green(` \u2705 ${s.message}`):y.default.red(` \u274C ${s.message}`)),r.command_id)try{await E().post(`/api/v1/dashboard/commands/${r.command_id}/result`,{status:s.success?"success":"failed",result:s.message})}catch{}try{await D()}catch{}}catch(s){console.log(y.default.red(` Claude Code stop error: ${s.message}`))}}else if(r.type==="command.execute"&&r.command_id){console.log(y.default.cyan(`
|
|
133
|
+
\u26A1 SSE command received: ${r.command_type} (${r.command_id})`));try{let s=E();await s.post(`/api/v1/dashboard/commands/${r.command_id}/ack`);let a=await Y({id:r.command_id,command_type:r.command_type,payload:r.payload});console.log(a.success?y.default.green(` \u2705 ${a.message}`):y.default.red(` \u274C ${a.message}`)),await s.post(`/api/v1/dashboard/commands/${r.command_id}/result`,{status:a.success?"success":"failed",result:a.message,new_version:a.newVersion||null});try{await D()}catch{}r.command_type==="update_cli"&&a.success&&(console.log(y.default.cyan(" CLI updated \u2014 restarting in 2s...")),setTimeout(()=>process.exit(0),2e3))}catch(s){console.log(y.default.red(` Command ${r.command_id} error: ${s.message}`))}}}catch{}},o.onerror=()=>{console.log(y.default.dim(" Reconnecting..."))},await new Promise(()=>{})});var ar=require("commander"),d=c(require("chalk")),z=require("child_process"),C=c(require("fs")),Ye=c(require("os")),oe=c(require("path"));X();rt();de();var or=require("child_process"),xt=c(require("fs")),nr=c(require("os")),Rt=c(require("path"));de();function ea(){let e=rr();for(let t of e)if(xt.default.existsSync(t))return t;return null}function rr(){return[Rt.default.resolve(__dirname,"..","assets","hermes-agent-bc.Dockerfile"),Rt.default.resolve(__dirname,"..","..","assets","hermes-agent-bc.Dockerfile")]}function sr(){let e=st();if(!e.installed)return{status:"no-docker"};let t=process.env.HERMES_IMAGE||$e;if(at(e).installed)return{status:"already-present",image:t};let n=ea();if(!n)return{status:"no-dockerfile",expectedPaths:rr()};let r=xt.default.mkdtempSync(Rt.default.join(nr.default.tmpdir(),"bc-hermes-build-"));try{let s=(0,or.spawnSync)("docker",["build","-t",t,"-f",n,r],{stdio:"inherit",timeout:6e5});return s.status===0?{status:"built",image:t}:{status:"build-failed",image:t,stderr:`docker build exited ${s.status??"<no-status>"}`}}finally{try{xt.default.rmSync(r,{recursive:!0,force:!0})}catch{}}}var H=oe.default.join(Ye.default.homedir(),".openclaw","openclaw.json"),Je=H+".badgerclaw-stash",At=oe.default.join(Ye.default.homedir(),".openclaw","npm"),Co=oe.default.join(At,"package.json");async function ta(){try{let e=await fetch(`${B}/api/v1/dashboard/versions/latest`,{headers:{Accept:"application/json"}});if(!e.ok)return{cli:null,plugin:null,openclaw:null};let t=await e.json(),o=t.result&&typeof t.result=="object"?t.result:t,n=r=>typeof r!="string"||!r||r==="unknown"?null:r;return{cli:n(o.cli),plugin:n(o.plugin),openclaw:n(o.supported_openclaw)}}catch{return{cli:null,plugin:null,openclaw:null}}}function oa(){if(!C.default.existsSync(H))return null;try{let e=JSON.parse(C.default.readFileSync(H,"utf-8")),t=e.channels?.badgerclaw;return t?(delete e.channels.badgerclaw,e.plugins?.entries?.badgerclaw&&delete e.plugins.entries.badgerclaw,C.default.writeFileSync(H,JSON.stringify(e,null,2)),C.default.writeFileSync(Je,JSON.stringify(t,null,2)),t):null}catch{return null}}function na(){if(!C.default.existsSync(H))return null;try{let e=JSON.parse(C.default.readFileSync(H,"utf-8"));return!e.gateway||typeof e.gateway!="object"?null:JSON.parse(JSON.stringify(e.gateway))}catch{return null}}function ra(e){if(e&&C.default.existsSync(H))try{let t=JSON.parse(C.default.readFileSync(H,"utf-8"));t.gateway={...e,...t.gateway||{}},C.default.writeFileSync(H,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 sa(){if(C.default.existsSync(Je))try{let e=JSON.parse(C.default.readFileSync(Je,"utf-8")),t=JSON.parse(C.default.readFileSync(H,"utf-8"));t.channels=t.channels||{},t.channels.badgerclaw=e,C.default.writeFileSync(H,JSON.stringify(t,null,2)),C.default.unlinkSync(Je)}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: ${Je}`))}}async function aa(e){return new Promise(t=>{let o=(0,z.spawn)("openclaw",["plugins","install",e,"--force","--dangerously-force-unsafe-install"],{stdio:["ignore","pipe","pipe"],shell:!0}),n="",r=!1,s=!1,a=/Installed plugin:|Restart the gateway to load plugins/,i=()=>{r||s||(r=!0,setTimeout(()=>{if(!s){try{o.kill("SIGTERM")}catch{}setTimeout(()=>{if(!s)try{o.kill("SIGKILL")}catch{}},1e3)}},3e3))},l=(m,p)=>{p.write(m),n+=m.toString("utf-8"),a.test(n)&&i()};o.stdout.on("data",m=>l(m,process.stdout)),o.stderr.on("data",m=>l(m,process.stderr)),o.on("exit",m=>{s=!0,t({status:r?0:m??1})}),o.on("error",m=>{s||(s=!0,console.error(d.default.red(` openclaw spawn error: ${m.message}`)),t({status:1}))})})}function ia(){if(!C.default.existsSync(Co))return!1;let e;try{e=JSON.parse(C.default.readFileSync(Co,"utf-8"))}catch{return!1}let t=!1;return e.overrides&&typeof e.overrides=="object"&&"uuid"in e.overrides&&(delete e.overrides.uuid,t=!0),e.openclaw&&Array.isArray(e.openclaw.managedOverrides)&&e.openclaw.managedOverrides.includes("uuid")&&(e.openclaw.managedOverrides=e.openclaw.managedOverrides.filter(o=>o!=="uuid"),t=!0),t?(C.default.writeFileSync(Co,JSON.stringify(e,null,2)+`
|
|
134
|
+
`),!0):!1}async function ca(){let{detectCapabilities:e}=await Promise.resolve().then(()=>(de(),en)),{pushHeartbeat:t}=await Promise.resolve().then(()=>(qe(),Zn));console.log(d.default.green(`
|
|
135
135
|
\u{1F9A1} BadgerClaw \u2014 capability refresh
|
|
136
|
-
`));let o=e();console.log(
|
|
137
|
-
\u2705 Capabilities pushed to the API.`)),console.log(
|
|
138
|
-
\u26A0\uFE0F Not logged in \u2014 capabilities re-scanned locally but not posted to the API.`)),console.log(
|
|
139
|
-
\u274C Failed to push capabilities: ${r}`)),process.exit(1)}}function
|
|
136
|
+
`));let o=e();console.log(d.default.dim(" Detected on this host:")),console.log(d.default.dim(` OpenClaw : ${Io(o.openclaw)}`)),console.log(d.default.dim(` Hermes : ${Io(o.hermes)}`)),console.log(d.default.dim(` Docker : ${Io(o.docker)}`)),o.disk_free_gb!==null&&console.log(d.default.dim(` Disk free: ${o.disk_free_gb} GB`));try{await t(),console.log(d.default.green(`
|
|
137
|
+
\u2705 Capabilities pushed to the API.`)),console.log(d.default.dim(" The backend will route new bot assignments accordingly."))}catch(n){let r=n?.message||String(n);if(r.includes("no auth")||r.includes("401")){console.log(d.default.yellow(`
|
|
138
|
+
\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(`
|
|
139
|
+
\u274C Failed to push capabilities: ${r}`)),process.exit(1)}}function Io(e){return e.installed?e.version?`installed (${e.version})`:e.image?`installed (${e.image})`:"installed":"not installed"}var ir=new ar.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.").action(async e=>{if(e.refresh){await ca();return}if(console.log(d.default.green(`
|
|
140
140
|
\u{1F9A1} BadgerClaw Setup
|
|
141
|
-
`))
|
|
142
|
-
|
|
143
|
-
\u274C
|
|
144
|
-
\
|
|
145
|
-
\u274C
|
|
146
|
-
|
|
141
|
+
`)),Ln({hermes:e.hermes})==="hermes"){let g=le();if(console.log(d.default.dim(` Detected: Docker ${g.docker.installed?"running":"not running"}, Hermes image ${g.hermes.installed?"present":"absent"}.`)),console.log(d.default.dim(` Selected runtime: ${d.default.cyan("hermes")} (via --hermes flag)
|
|
142
|
+
`)),g.docker.installed||(console.log(d.default.red("\u274C Docker is not running \u2014 required for the Hermes runtime.")),console.log(d.default.dim(" Install Docker Desktop (or colima on macOS / docker-ce on Linux) and start it,")),console.log(d.default.dim(" then re-run `badgerclaw setup --hermes`.")),process.exit(1)),g.hermes.installed)console.log(d.default.dim(` Hermes image already present: ${g.hermes.image}`));else{console.log(d.default.dim(" Building Hermes image (one-time, ~2 min)..."));let S=sr();S.status==="built"?console.log(d.default.green(` \u2705 Hermes image built: ${S.image}`)):S.status==="already-present"?console.log(d.default.dim(` Hermes image already present: ${S.image}`)):S.status==="no-dockerfile"?(console.log(d.default.red(`
|
|
143
|
+
\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: ${S.expectedPaths.join(", ")}`)),process.exit(1)):S.status==="build-failed"?(console.log(d.default.red(`
|
|
144
|
+
\u274C Hermes image build failed: ${S.stderr}`)),console.log(d.default.dim(" See the docker output above for details.")),process.exit(1)):S.status==="no-docker"&&(console.log(d.default.red(`
|
|
145
|
+
\u274C Docker is not running.`)),process.exit(1))}console.log(d.default.dim(`
|
|
146
|
+
Hermes-only mode \u2014 skipping OpenClaw plugin + gateway install.
|
|
147
|
+
To enable OC bots later, drop the --hermes flag: \`badgerclaw setup\`.
|
|
148
|
+
`)),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 o=await ta();if(o.cli){let g=Gt();g!==o.cli?(console.log(d.default.dim(` Updating CLI: ${g} \u2192 ${o.cli} (supported)...`)),(0,z.spawnSync)("npm",["install","-g",`badgerclaw@${o.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 ${o.cli}`))):console.log(d.default.dim(` CLI already at approved version ${g}.`))}if(o.openclaw){let g=Se();g!==o.openclaw?(console.log(g===null?d.default.dim(` Installing OpenClaw ${o.openclaw} (supported)...`):d.default.dim(` Updating OpenClaw: ${g} \u2192 ${o.openclaw} (supported)...`)),(0,z.spawnSync)("npm",["install","-g",`openclaw@${o.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 ${o.openclaw}`))):console.log(d.default.dim(` OpenClaw already at approved version ${g}.`))}else Se()===null&&(console.log(d.default.yellow(" \u26A0\uFE0F No approved OpenClaw version and none installed \u2014 installing latest from npm.")),(0,z.spawnSync)("npm",["install","-g","openclaw"],{stdio:"inherit",shell:!0}).status!==0&&(console.log(d.default.red(`
|
|
149
|
+
\u274C OpenClaw install failed.`)),process.exit(1)));let n=oa();n&&console.log(d.default.dim(" Existing bot config stashed temporarily..."));let r=na();(0,z.spawnSync)("openclaw",["gateway","stop"],{stdio:"pipe",shell:!0,timeout:1e4});let s=[oe.default.join(Ye.default.homedir(),".openclaw","extensions","badgerclaw"),oe.default.join(Ye.default.homedir(),".openclaw","npm","node_modules","@badgerclaw","connect")],a=[],i=[],l=g=>{let S=`${g}.bc-setup-stash-${Date.now()}-${Math.floor(Math.random()*1e6)}`,R=(0,z.spawnSync)("mv",["-f",g,S],{encoding:"utf-8"});return R.status===0?{ok:!0,stash:S}:{ok:!1,error:(R.stderr||"").trim()||`exit ${R.status}`}};for(let g of s)if(C.default.existsSync(g))try{C.default.rmSync(g,{recursive:!0,force:!0}),a.push(g)}catch(S){let R=l(g);R.ok?(i.push(`${g} \u2192 ${R.stash}`),console.log(d.default.yellow(` \u26A0\uFE0F Could not delete ${g} (${S.message}); renamed it aside instead.`))):(console.log(d.default.red(` \u274C Could not delete ${g} (${S.message})`)),console.log(d.default.red(` Could not rename ${g} aside either (${R.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 m=o.plugin?`@badgerclaw/connect@${o.plugin}`:"@badgerclaw/connect";o.plugin?console.log(d.default.dim(` Installing ${m} (supported)...`)):console.log(d.default.yellow(" \u26A0\uFE0F Allow-list unavailable \u2014 installing @badgerclaw/connect unpinned."));let p=await aa(m);if(n&&(sa(),console.log(d.default.dim(" Bot config restored."))),r&&(ra(r),console.log(d.default.dim(" Gateway config restored."))),p.status!==0&&(console.log(d.default.red(`
|
|
150
|
+
\u274C Plugin install failed. Your bot config has been restored.`)),process.exit(1)),console.log(d.default.green(`
|
|
151
|
+
\u2705 BadgerClaw plugin installed successfully!`)),ia()){console.log(d.default.dim(" Stripping OpenClaw uuid:14 override (breaks request@2.88 in matrix-bot-sdk)..."));try{C.default.rmSync(oe.default.join(At,"node_modules","uuid"),{recursive:!0,force:!0})}catch{}try{C.default.rmSync(oe.default.join(At,"package-lock.json"),{force:!0})}catch{}(0,z.spawnSync)("npm",["install"],{cwd:At,stdio:"inherit",shell:!0}).status!==0&&(console.log(d.default.red(`
|
|
152
|
+
\u274C npm install failed after removing uuid override. Bot will not reply correctly until this is resolved.`)),process.exit(1)),console.log(d.default.green(" \u2705 uuid override stripped + npm tree re-resolved."))}console.log(""),pt({verbose:!0}).ok||(console.log(d.default.red(`
|
|
153
|
+
\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)),console.log(d.default.green(" \u2705 Gateway is running.")),console.log(d.default.dim("\nNext: run `badgerclaw login` to authenticate.")),gt()});qe();var lr=require("commander"),I=c(require("chalk"));j();we();pe();var dr=new lr.Command("dashboard").description("Show local diagnostic dashboard \u2014 machine health, gateway status, bot telemetry").action(async()=>{_()||(console.log(I.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let{version:t}=ce(),o=tt(),n=await We();console.log(I.default.bold.green(`
|
|
147
154
|
BadgerClaw Dashboard
|
|
148
|
-
`)),console.log(
|
|
149
|
-
Gateway Claude Code Rooms:`)),console.log(
|
|
150
|
-
Local Sessions:`)),console.log(
|
|
155
|
+
`)),console.log(I.default.bold(" Instance")),console.log(` ID: ${V()}`),console.log(` CLI Version: ${t}`),console.log(` Plugin: ${n.pluginVersion}`),console.log(),console.log(I.default.bold(" Machine")),console.log(` Hostname: ${o.hostname}`),console.log(` OS: ${o.os} / ${o.arch}`),console.log(` Uptime: ${cr(o.uptimeSeconds)}`),console.log(` Free Memory: ${o.memFreeMb} MB`),console.log();let r=n.status==="running"?I.default.green:n.status==="error"?I.default.red:I.default.yellow;if(console.log(I.default.bold(" Gateway")),console.log(` Status: ${r(n.status)}`),console.log(` PID: ${n.pid??"N/A"}`),console.log(` Last Restart: ${n.lastRestart??"N/A"}`),console.log(),n.bots.length===0)console.log(I.default.bold(" Bots")),console.log(I.default.dim(" No bots detected. Pair bots at https://badgerclaw.ai")),console.log();else{console.log(I.default.bold(` Bots (${n.bots.length})`)),console.log();for(let s of n.bots){let a=s.status==="running"?I.default.green:s.status==="error"?I.default.red:I.default.yellow;if(console.log(` ${I.default.bold(s.botUsername)} ${a(`[${s.status}]`)}`),console.log(` ID: ${s.botId}`),console.log(` Uptime: ${cr(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: ${I.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(I.default.dim(" Rooms:"));for(let i of s.roomDetails)console.log(I.default.dim(` ${i.roomName} \u2014 ${i.messagesInRoom} msgs, last: ${i.lastActivityInRoom??"N/A"}`))}console.log()}}console.log(I.default.dim(` Last checked: ${new Date().toISOString()}`)),console.log()});function cr(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),o=Math.floor(e%3600/60);return t<24?`${t}h ${o}m`:`${Math.floor(t/24)}d ${t%24}h ${o}m`}var ko=require("commander"),xe=c(require("chalk")),ur=c(require("ora"));j();pe();qe();var la=new ko.Command("restart").description("Restart the OpenClaw gateway via the plugin probe endpoint").action(async()=>{_()||(console.log(xe.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let t=(0,ur.default)("Restarting gateway...").start(),o=await ke();if(o.success){t.succeed(xe.default.green(`Gateway restarted: ${o.message}`));try{await D(),console.log(xe.default.dim(" Heartbeat pushed with updated status."))}catch{console.log(xe.default.dim(" Could not push heartbeat \u2014 daemon may not be running."))}}else t.fail(xe.default.red(`Gateway restart failed: ${o.message}`)),process.exit(1)}),mr=new ko.Command("gateway").description("Manage the OpenClaw gateway").addCommand(la);var re=require("commander"),b=c(require("chalk")),ye=c(require("ora")),ne=c(require("axios"));vt();j();var he="http://localhost:7331",da=new re.Command("start").description("Start a Claude Code session for a bot+room").requiredOption("--bot <botId>","Bot ID or username").requiredOption("--room <roomId>","Matrix room ID").option("--session-id <id>","Session ID (default: auto-generated)").option("--port <port>","MCP server port (default: 7332)","7332").option("--project <dir>","Project directory",process.cwd()).action(async e=>{_()||(console.log(b.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let o=e.sessionId||`session-${Date.now()}`,n=parseInt(e.port,10),r=e.project.replace(/^~/,process.env.HOME||""),s=(0,ye.default)("Starting Claude Code session...").start();try{await ne.default.post(`${he}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!0,sessionId:o},{timeout:1e4}),s.text="Gateway toggled \u2014 launching MCP server...";let i=fo({sessionId:o,port:n,projectDir:r}).pid||0;await new Promise(l=>setTimeout(l,1500)),s.text="MCP server running \u2014 launching Claude Code...",ho({sessionId:o,port:n,projectDir:r}),yo(o,0,i,n,r),s.succeed(b.default.green("Claude Code session started")+b.default.dim(` (session: ${o}, port: ${n})`)),console.log(b.default.dim(` Bot: ${e.bot}`)),console.log(b.default.dim(` Room: ${e.room}`)),console.log(b.default.dim(` Project: ${r}`)),console.log(b.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(b.default.red(`Failed to start Claude Code: ${i}`)),process.exit(1)}}),ua=new re.Command("stop").description("Stop a Claude Code session").option("--bot <botId>","Bot ID or username").option("--room <roomId>","Matrix room ID").option("--session-id <id>","Session ID to stop").action(async e=>{_()||(console.log(b.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let o=(0,ye.default)("Stopping Claude Code session...").start();try{e.bot&&e.room&&await ne.default.post(`${he}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!1},{timeout:1e4});let n=e.sessionId;if(!n)try{let a=((await ne.default.get(`${he}/claude-code/status`,{timeout:5e3})).data?.rooms||[]).find(i=>i.botId===e.bot&&i.roomId===e.room);a&&(n=a.sessionId)}catch{}n?(wo(n),o.succeed(b.default.green(`Claude Code session stopped (${n})`))):(o.succeed(b.default.green("Claude Code toggled off at gateway")),console.log(b.default.dim(" No local session found to kill \u2014 may need to close Claude Code manually.")))}catch(n){let r=n.response?.data?.message||n.message||"Unknown error";o.fail(b.default.red(`Failed to stop Claude Code: ${r}`)),process.exit(1)}}),ma=new re.Command("status").description("Show active Claude Code sessions").action(async()=>{_()||(console.log(b.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let t=(0,ye.default)("Fetching Claude Code status...").start();try{let o=[];try{o=(await ne.default.get(`${he}/claude-code/status`,{timeout:5e3})).data?.rooms||[]}catch{}let n=bo();if(t.stop(),o.length===0&&n.length===0){console.log(b.default.dim("No active Claude Code sessions."));return}if(o.length>0){console.log(b.default.bold(`
|
|
156
|
+
Gateway Claude Code Rooms:`)),console.log(b.default.dim(" Bot".padEnd(30)+"Room".padEnd(30)+"Session".padEnd(20)+"Enabled")),console.log(b.default.dim(" "+"-".repeat(88)));for(let r of o){let s=r.enabled?b.default.green("ON"):b.default.dim("OFF");console.log(` ${(r.botId||"").padEnd(30)}${(r.roomName||r.roomId||"").padEnd(30)}${(r.sessionId||"-").padEnd(20)}${s}`)}}if(n.length>0){console.log(b.default.bold(`
|
|
157
|
+
Local Sessions:`)),console.log(b.default.dim(" Session".padEnd(25)+"Port".padEnd(8)+"MCP PID".padEnd(10)+"Project".padEnd(40)+"Started")),console.log(b.default.dim(" "+"-".repeat(90)));for(let r of n)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(o){t.fail(b.default.red(`Failed to get status: ${o.message}`)),process.exit(1)}}),pa=new re.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,ye.default)("Grouping bot+room into session...").start();try{await ne.default.post(`${he}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!0,sessionId:e.session},{timeout:1e4}),t.succeed(b.default.green(`Grouped ${e.bot} + ${e.room} into session "${e.session}"`))}catch(o){let n=o.response?.data?.message||o.message||"Unknown error";t.fail(b.default.red(`Failed to group: ${n}`)),process.exit(1)}}),ga=new re.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,ye.default)("Adding user to allowlist...").start();try{await ne.default.post(`${he}/claude-code/allow`,{botId:e.bot,roomId:e.room,userId:e.user},{timeout:1e4}),t.succeed(b.default.green(`Added ${e.user} to allowlist for ${e.bot} in ${e.room}`))}catch(o){let n=o.response?.data?.message||o.message||"Unknown error";t.fail(b.default.red(`Failed to add user: ${n}`)),process.exit(1)}}),fa=new re.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,ye.default)("Removing user from allowlist...").start();try{await ne.default.post(`${he}/claude-code/revoke`,{botId:e.bot,roomId:e.room,userId:e.user},{timeout:1e4}),t.succeed(b.default.green(`Removed ${e.user} from allowlist for ${e.bot} in ${e.room}`))}catch(o){let n=o.response?.data?.message||o.message||"Unknown error";t.fail(b.default.red(`Failed to revoke user: ${n}`)),process.exit(1)}}),pr=new re.Command("claude-code").description("Manage Claude Code sessions (relay Matrix messages to local Claude Code)").addCommand(da).addCommand(ua).addCommand(ma).addCommand(pa).addCommand(ga).addCommand(fa);rt();async function ha(){await Yo(process.argv);let e=new gr.Command;e.name("badgerclaw").description("BadgerClaw CLI \u2014 one-click bot provisioning").version("0.2.54"),e.addCommand(kn),e.addCommand(An),e.addCommand(Pn),e.addCommand(Fn),e.addCommand(mn),e.addCommand(tr),e.addCommand(ir),e.addCommand(vo),e.addCommand(dr),e.addCommand(mr),e.addCommand(pr),e.parse(process.argv)}ha().catch(e=>{console.error(e),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "badgerclaw",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.54",
|
|
4
4
|
"description": "BadgerClaw CLI — one-click bot provisioning",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"dist/",
|
|
11
11
|
"scripts/",
|
|
12
|
+
"assets/",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
14
15
|
"scripts": {
|