badgerclaw 0.2.10 → 0.2.11

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.
@@ -491,6 +491,20 @@ Reply using the reply tool with botId="${msg.botId}" and roomId="${msg.roomId}"`
491
491
  }
492
492
  });
493
493
  });
494
+ this.httpServer.on("error", (err) => {
495
+ if (err.code === "EADDRINUSE") {
496
+ console.warn(`[mcp-server] Port ${this.port} in use \u2014 killing old process and retrying`);
497
+ this.httpServer = null;
498
+ try {
499
+ const { execSync } = require("child_process");
500
+ execSync(`lsof -ti :${this.port} | xargs kill -9 2>/dev/null`, { stdio: "pipe", timeout: 3e3 });
501
+ } catch {
502
+ }
503
+ setTimeout(() => this.startHttpServer(), 1e3);
504
+ } else {
505
+ console.error(`[mcp-server] HTTP server error: ${err.message}`);
506
+ }
507
+ });
494
508
  this.httpServer.listen(this.port, "127.0.0.1", () => {
495
509
  console.log(`[mcp-server] Listening on http://127.0.0.1:${this.port}`);
496
510
  });
@@ -544,13 +558,13 @@ ${formatted}`);
544
558
  }
545
559
  }
546
560
  async start() {
547
- this.startHttpServer();
548
- this.injectSessionContext().catch(() => {
549
- });
550
561
  const transport = new import_stdio.StdioServerTransport();
551
562
  await this.server.connect(transport);
552
563
  this.installTransportSpy();
553
564
  console.log(`[mcp-server] MCP server connected (session: ${this.sessionId})`);
565
+ this.startHttpServer();
566
+ this.injectSessionContext().catch(() => {
567
+ });
554
568
  }
555
569
  async stop() {
556
570
  if (this.httpServer) {
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var ho=Object.create;var ge=Object.defineProperty;var yo=Object.getOwnPropertyDescriptor;var wo=Object.getOwnPropertyNames;var bo=Object.getPrototypeOf,_o=Object.prototype.hasOwnProperty;var $o=(e,t)=>()=>(e&&(t=e(e=0)),t);var So=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Co=(e,t)=>{for(var o in t)ge(e,o,{get:t[o],enumerable:!0})},lt=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of wo(t))!_o.call(e,n)&&n!==o&&ge(e,n,{get:()=>t[n],enumerable:!(s=yo(t,n))||s.enumerable});return e};var c=(e,t,o)=>(o=e!=null?ho(bo(e)):{},lt(t||!e||!e.__esModule?ge(o,"default",{value:e,enumerable:!0}):o,e)),dt=e=>lt(ge({},"__esModule",{value:!0}),e);var U=So((Vn,Bo)=>{Bo.exports={name:"badgerclaw",version:"0.2.10",description:"BadgerClaw CLI \u2014 one-click bot provisioning",main:"dist/index.js",bin:{badgerclaw:"./dist/index.js"},files:["dist/","README.md"],scripts:{build:"node build.mjs",test:'echo "Error: no test specified" && exit 1'},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",typescript:"^5.3.0"}}});var st={};Co(st,{getActiveSessions:()=>nt,launchClaudeCode:()=>et,launchMCPServer:()=>Ze,recordSession:()=>tt,stopSession:()=>ot});function Ke(){try{if(k.default.existsSync(ze))return JSON.parse(k.default.readFileSync(ze,"utf-8"))}catch{}return[]}function Ye(e){k.default.mkdirSync(H,{recursive:!0}),k.default.writeFileSync(ze,JSON.stringify(e,null,2))}function xe(e){try{return process.kill(e,0),!0}catch{return!1}}function qo(){if(process.env.TMUX)return"tmux";try{if((0,L.execSync)(`osascript -e 'tell application "System Events" to (name of processes) contains "iTerm2"'`,{stdio:"pipe",timeout:3e3}),k.default.existsSync("/Applications/iTerm.app"))return"iterm2"}catch{}return process.platform==="darwin"?"terminal":"direct"}function Xe(e){let t=R.default.join(H,"claude-launcher.exp"),o=R.default.join(H,"pending-input.txt");k.default.writeFileSync(o,"","utf-8");let s=`#!/usr/bin/expect -f
2
+ "use strict";var ho=Object.create;var ge=Object.defineProperty;var yo=Object.getOwnPropertyDescriptor;var wo=Object.getOwnPropertyNames;var bo=Object.getPrototypeOf,_o=Object.prototype.hasOwnProperty;var $o=(e,t)=>()=>(e&&(t=e(e=0)),t);var So=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Co=(e,t)=>{for(var o in t)ge(e,o,{get:t[o],enumerable:!0})},lt=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of wo(t))!_o.call(e,n)&&n!==o&&ge(e,n,{get:()=>t[n],enumerable:!(s=yo(t,n))||s.enumerable});return e};var c=(e,t,o)=>(o=e!=null?ho(bo(e)):{},lt(t||!e||!e.__esModule?ge(o,"default",{value:e,enumerable:!0}):o,e)),dt=e=>lt(ge({},"__esModule",{value:!0}),e);var U=So((Vn,Bo)=>{Bo.exports={name:"badgerclaw",version:"0.2.11",description:"BadgerClaw CLI \u2014 one-click bot provisioning",main:"dist/index.js",bin:{badgerclaw:"./dist/index.js"},files:["dist/","README.md"],scripts:{build:"node build.mjs",test:'echo "Error: no test specified" && exit 1'},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",typescript:"^5.3.0"}}});var st={};Co(st,{getActiveSessions:()=>nt,launchClaudeCode:()=>et,launchMCPServer:()=>Ze,recordSession:()=>tt,stopSession:()=>ot});function Ke(){try{if(k.default.existsSync(ze))return JSON.parse(k.default.readFileSync(ze,"utf-8"))}catch{}return[]}function Ye(e){k.default.mkdirSync(H,{recursive:!0}),k.default.writeFileSync(ze,JSON.stringify(e,null,2))}function xe(e){try{return process.kill(e,0),!0}catch{return!1}}function qo(){if(process.env.TMUX)return"tmux";try{if((0,L.execSync)(`osascript -e 'tell application "System Events" to (name of processes) contains "iTerm2"'`,{stdio:"pipe",timeout:3e3}),k.default.existsSync("/Applications/iTerm.app"))return"iterm2"}catch{}return process.platform==="darwin"?"terminal":"direct"}function Xe(e){let t=R.default.join(H,"claude-launcher.exp"),o=R.default.join(H,"pending-input.txt");k.default.writeFileSync(o,"","utf-8");let s=`#!/usr/bin/expect -f
3
3
  set timeout -1
4
4
  set msgfile "${o}"
5
5
 
@@ -128,4 +128,4 @@ Keep responses concise and helpful.
128
128
  BadgerClaw Dashboard
129
129
  `)),console.log(_.default.bold(" Instance")),console.log(` ID: ${de()}`),console.log(` CLI Version: ${t}`),console.log(` Plugin: ${s.pluginVersion}`),console.log(),console.log(_.default.bold(" Machine")),console.log(` Hostname: ${o.hostname}`),console.log(` OS: ${o.os} / ${o.arch}`),console.log(` Uptime: ${io(o.uptimeSeconds)}`),console.log(` Free Memory: ${o.memFreeMb} MB`),console.log();let n=s.status==="running"?_.default.green:s.status==="error"?_.default.red:_.default.yellow;if(console.log(_.default.bold(" Gateway")),console.log(` Status: ${n(s.status)}`),console.log(` PID: ${s.pid??"N/A"}`),console.log(` Last Restart: ${s.lastRestart??"N/A"}`),console.log(),s.bots.length===0)console.log(_.default.bold(" Bots")),console.log(_.default.dim(" No bots detected. Pair bots at https://badgerclaw.ai")),console.log();else{console.log(_.default.bold(` Bots (${s.bots.length})`)),console.log();for(let r of s.bots){let a=r.status==="running"?_.default.green:r.status==="error"?_.default.red:_.default.yellow;if(console.log(` ${_.default.bold(r.botUsername)} ${a(`[${r.status}]`)}`),console.log(` ID: ${r.botId}`),console.log(` Uptime: ${io(r.uptimeSeconds)}`),console.log(` Messages: ${r.messagesReceived} in / ${r.messagesSent} out`),r.chunkedMessages>0&&console.log(` Chunked: ${r.chunkedMessages} messages, ${r.totalChunksSent} chunks`),console.log(` Rooms: ${r.roomsActive} active`),console.log(` Errors: ${r.errors}`),r.lastError&&(console.log(` Last Error: ${_.default.red(r.lastError)}`),console.log(` Error At: ${r.lastErrorAt}`)),console.log(` Last Active: ${r.lastActivity??"N/A"}`),r.roomDetails&&r.roomDetails.length>0){console.log(_.default.dim(" Rooms:"));for(let i of r.roomDetails)console.log(_.default.dim(` ${i.roomName} \u2014 ${i.messagesInRoom} msgs, last: ${i.lastActivityInRoom??"N/A"}`))}console.log()}}console.log(_.default.dim(` Last checked: ${new Date().toISOString()}`)),console.log()});function io(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 it=require("commander"),Q=c(require("chalk")),mo=c(require("ora"));var pn=new it.Command("restart").description("Restart the OpenClaw gateway via the plugin probe endpoint").action(async()=>{f()||(console.log(Q.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let t=(0,mo.default)("Restarting gateway...").start(),o=await Y();if(o.success){t.succeed(Q.default.green(`Gateway restarted: ${o.message}`));try{await I(),console.log(Q.default.dim(" Heartbeat pushed with updated status."))}catch{console.log(Q.default.dim(" Could not push heartbeat \u2014 daemon may not be running."))}}else t.fail(Q.default.red(`Gateway restart failed: ${o.message}`)),process.exit(1)}),uo=new it.Command("gateway").description("Manage the OpenClaw gateway").addCommand(pn);var G=require("commander"),p=c(require("chalk")),J=c(require("ora")),F=c(require("axios"));Pe();var q="http://localhost:7331",fn=new G.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=>{f()||(console.log(p.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let o=e.sessionId||`session-${Date.now()}`,s=parseInt(e.port,10),n=e.project.replace(/^~/,process.env.HOME||""),r=(0,J.default)("Starting Claude Code session...").start();try{await F.default.post(`${q}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!0,sessionId:o},{timeout:1e4}),r.text="Gateway toggled \u2014 launching MCP server...";let i=Ze({sessionId:o,port:s,projectDir:n}).pid||0;await new Promise(l=>setTimeout(l,1500)),r.text="MCP server running \u2014 launching Claude Code...",et({sessionId:o,port:s,projectDir:n}),tt(o,0,i,s,n),r.succeed(p.default.green("Claude Code session started")+p.default.dim(` (session: ${o}, port: ${s})`)),console.log(p.default.dim(` Bot: ${e.bot}`)),console.log(p.default.dim(` Room: ${e.room}`)),console.log(p.default.dim(` Project: ${n}`)),console.log(p.default.dim(" Claude Code should open in a new terminal window."))}catch(a){let i=a.response?.data?.message||a.message||"Unknown error";r.fail(p.default.red(`Failed to start Claude Code: ${i}`)),process.exit(1)}}),hn=new G.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=>{f()||(console.log(p.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let o=(0,J.default)("Stopping Claude Code session...").start();try{e.bot&&e.room&&await F.default.post(`${q}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!1},{timeout:1e4});let s=e.sessionId;if(!s)try{let a=((await F.default.get(`${q}/claude-code/status`,{timeout:5e3})).data?.rooms||[]).find(i=>i.botId===e.bot&&i.roomId===e.room);a&&(s=a.sessionId)}catch{}s?(ot(s),o.succeed(p.default.green(`Claude Code session stopped (${s})`))):(o.succeed(p.default.green("Claude Code toggled off at gateway")),console.log(p.default.dim(" No local session found to kill \u2014 may need to close Claude Code manually.")))}catch(s){let n=s.response?.data?.message||s.message||"Unknown error";o.fail(p.default.red(`Failed to stop Claude Code: ${n}`)),process.exit(1)}}),yn=new G.Command("status").description("Show active Claude Code sessions").action(async()=>{f()||(console.log(p.default.yellow("Not logged in. Run `badgerclaw login` first.")),process.exit(1));let t=(0,J.default)("Fetching Claude Code status...").start();try{let o=[];try{o=(await F.default.get(`${q}/claude-code/status`,{timeout:5e3})).data?.rooms||[]}catch{}let s=nt();if(t.stop(),o.length===0&&s.length===0){console.log(p.default.dim("No active Claude Code sessions."));return}if(o.length>0){console.log(p.default.bold(`
130
130
  Gateway Claude Code Rooms:`)),console.log(p.default.dim(" Bot".padEnd(30)+"Room".padEnd(30)+"Session".padEnd(20)+"Enabled")),console.log(p.default.dim(" "+"-".repeat(88)));for(let n of o){let r=n.enabled?p.default.green("ON"):p.default.dim("OFF");console.log(` ${(n.botId||"").padEnd(30)}${(n.roomName||n.roomId||"").padEnd(30)}${(n.sessionId||"-").padEnd(20)}${r}`)}}if(s.length>0){console.log(p.default.bold(`
131
- Local Sessions:`)),console.log(p.default.dim(" Session".padEnd(25)+"Port".padEnd(8)+"MCP PID".padEnd(10)+"Project".padEnd(40)+"Started")),console.log(p.default.dim(" "+"-".repeat(90)));for(let n of s)console.log(` ${n.sessionId.padEnd(25)}${String(n.port).padEnd(8)}${String(n.mcpPid).padEnd(10)}${n.projectDir.padEnd(40)}${n.startedAt}`)}console.log("")}catch(o){t.fail(p.default.red(`Failed to get status: ${o.message}`)),process.exit(1)}}),wn=new G.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,J.default)("Grouping bot+room into session...").start();try{await F.default.post(`${q}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!0,sessionId:e.session},{timeout:1e4}),t.succeed(p.default.green(`Grouped ${e.bot} + ${e.room} into session "${e.session}"`))}catch(o){let s=o.response?.data?.message||o.message||"Unknown error";t.fail(p.default.red(`Failed to group: ${s}`)),process.exit(1)}}),bn=new G.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,J.default)("Adding user to allowlist...").start();try{await F.default.post(`${q}/claude-code/allow`,{botId:e.bot,roomId:e.room,userId:e.user},{timeout:1e4}),t.succeed(p.default.green(`Added ${e.user} to allowlist for ${e.bot} in ${e.room}`))}catch(o){let s=o.response?.data?.message||o.message||"Unknown error";t.fail(p.default.red(`Failed to add user: ${s}`)),process.exit(1)}}),_n=new G.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,J.default)("Removing user from allowlist...").start();try{await F.default.post(`${q}/claude-code/revoke`,{botId:e.bot,roomId:e.room,userId:e.user},{timeout:1e4}),t.succeed(p.default.green(`Removed ${e.user} from allowlist for ${e.bot} in ${e.room}`))}catch(o){let s=o.response?.data?.message||o.message||"Unknown error";t.fail(p.default.red(`Failed to revoke user: ${s}`)),process.exit(1)}}),go=new G.Command("claude-code").description("Manage Claude Code sessions (relay Matrix messages to local Claude Code)").addCommand(fn).addCommand(hn).addCommand(yn).addCommand(wn).addCommand(bn).addCommand(_n);var P=new po.Command;P.name("badgerclaw").description("BadgerClaw CLI \u2014 one-click bot provisioning").version("0.2.10");P.addCommand(Lt);P.addCommand(Ut);P.addCommand(Gt);P.addCommand(Vt);P.addCommand(Ct);P.addCommand(oo);P.addCommand(ao);P.addCommand(Zt);P.addCommand(lo);P.addCommand(uo);P.addCommand(go);P.parse(process.argv);
131
+ Local Sessions:`)),console.log(p.default.dim(" Session".padEnd(25)+"Port".padEnd(8)+"MCP PID".padEnd(10)+"Project".padEnd(40)+"Started")),console.log(p.default.dim(" "+"-".repeat(90)));for(let n of s)console.log(` ${n.sessionId.padEnd(25)}${String(n.port).padEnd(8)}${String(n.mcpPid).padEnd(10)}${n.projectDir.padEnd(40)}${n.startedAt}`)}console.log("")}catch(o){t.fail(p.default.red(`Failed to get status: ${o.message}`)),process.exit(1)}}),wn=new G.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,J.default)("Grouping bot+room into session...").start();try{await F.default.post(`${q}/claude-code/toggle`,{botId:e.bot,roomId:e.room,enabled:!0,sessionId:e.session},{timeout:1e4}),t.succeed(p.default.green(`Grouped ${e.bot} + ${e.room} into session "${e.session}"`))}catch(o){let s=o.response?.data?.message||o.message||"Unknown error";t.fail(p.default.red(`Failed to group: ${s}`)),process.exit(1)}}),bn=new G.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,J.default)("Adding user to allowlist...").start();try{await F.default.post(`${q}/claude-code/allow`,{botId:e.bot,roomId:e.room,userId:e.user},{timeout:1e4}),t.succeed(p.default.green(`Added ${e.user} to allowlist for ${e.bot} in ${e.room}`))}catch(o){let s=o.response?.data?.message||o.message||"Unknown error";t.fail(p.default.red(`Failed to add user: ${s}`)),process.exit(1)}}),_n=new G.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,J.default)("Removing user from allowlist...").start();try{await F.default.post(`${q}/claude-code/revoke`,{botId:e.bot,roomId:e.room,userId:e.user},{timeout:1e4}),t.succeed(p.default.green(`Removed ${e.user} from allowlist for ${e.bot} in ${e.room}`))}catch(o){let s=o.response?.data?.message||o.message||"Unknown error";t.fail(p.default.red(`Failed to revoke user: ${s}`)),process.exit(1)}}),go=new G.Command("claude-code").description("Manage Claude Code sessions (relay Matrix messages to local Claude Code)").addCommand(fn).addCommand(hn).addCommand(yn).addCommand(wn).addCommand(bn).addCommand(_n);var P=new po.Command;P.name("badgerclaw").description("BadgerClaw CLI \u2014 one-click bot provisioning").version("0.2.11");P.addCommand(Lt);P.addCommand(Ut);P.addCommand(Gt);P.addCommand(Vt);P.addCommand(Ct);P.addCommand(oo);P.addCommand(ao);P.addCommand(Zt);P.addCommand(lo);P.addCommand(uo);P.addCommand(go);P.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "badgerclaw",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "BadgerClaw CLI — one-click bot provisioning",
5
5
  "main": "dist/index.js",
6
6
  "bin": {