@sleep2agi/agent-network 1.0.3 → 1.2.0

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.
@@ -1,2 +1 @@
1
- import{EventEmitter as X}from"events";import{hostname as R}from"os";class T extends X{url;alias;token;agent;resumeId;heartbeatInterval;reconnectDelay;heartbeatTimer;sseAbort;running=!1;constructor(j){super();if(this.url=j.url.replace(/\/$/,""),this.alias=j.alias,this.token=j.token,this.agent=j.agent||"sdk",this.resumeId=`sdk-${j.alias}-${Date.now().toString(36)}`,this.heartbeatInterval=j.heartbeatInterval??180000,this.reconnectDelay=j.reconnectDelay??3000,j.autoConnect!==!1)this.connect()}log(j){console.log(`[${new Date().toTimeString().slice(0,8)}] [commhub:${this.alias}] ${j}`)}async call(j,z){let q={"Content-Type":"application/json",Accept:"application/json, text/event-stream"};if(this.token)q.Authorization=`Bearer ${this.token}`;let D=await(await fetch(`${this.url}/mcp`,{method:"POST",headers:q,body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:"tools/call",params:{name:j,arguments:z}})})).text(),G=D.match(/data: (.+)/),L=G?JSON.parse(G[1]):JSON.parse(D),F=L?.result?.content?.[0]?.text;return F?JSON.parse(F):L}async connect(){if(this.running)return;this.running=!0,await this.status("idle"),this.log("registered"),this.heartbeatTimer=setInterval(()=>{this.status("idle").catch((j)=>this.log(`heartbeat failed: ${j.message}`))},this.heartbeatInterval),this.connectSSE()}async disconnect(){if(this.running=!1,this.sseAbort?.abort(),this.heartbeatTimer)clearInterval(this.heartbeatTimer);await this.status("offline").catch(()=>{}),this.log("disconnected")}async send(j,z,q="normal"){return this.call("send_task",{alias:j,task:z,priority:q,from_session:this.alias})}async message(j,z){return this.call("send_message",{alias:j,message:z,from_session:this.alias})}async reply(j,z,q="completed"){return this.call("reply",{task_id:j,text:z,status:q})}async status(j,z){return this.call("report_status",{resume_id:this.resumeId,alias:this.alias,status:j,server:R(),hostname:R(),agent:this.agent,project_dir:process.cwd(),...z})}async getAllStatus(){return this.call("get_all_status",{})}async broadcast(j,z){return this.call("broadcast",{message:j,filter_server:z?.server,filter_status:z?.status})}async connectSSE(){let j=encodeURIComponent(this.alias),z=`${this.url}/events/${j}`,q=this.reconnectDelay;while(this.running){try{this.sseAbort=new AbortController;let B={Accept:"text/event-stream"};if(this.token)B.Authorization=`Bearer ${this.token}`;let D=await fetch(z,{headers:B,signal:this.sseAbort.signal});if(!D.ok||!D.body){this.log(`SSE failed: ${D.status}`),await this.sleep(q),q=Math.min(q*1.5,60000);continue}q=this.reconnectDelay;let G=D.body.getReader(),L=new TextDecoder,F="";while(this.running){let{done:V,value:W}=await G.read();if(V)break;F+=L.decode(W,{stream:!0});let P=F.split(`
2
- `);F=P.pop()||"";for(let Q of P){if(!Q.startsWith("data: "))continue;try{let M=JSON.parse(Q.slice(6));if(M.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(M.type==="new_task"||M.type==="new_message"||M.type==="broadcast")await this.processInbox()}catch{}}}}catch(B){if(B.name==="AbortError")break;this.emit("error",B),this.log(`SSE error: ${B.message}`)}if(this.running)this.emit("disconnected"),this.log(`SSE reconnecting in ${q/1000}s...`),await this.sleep(q),q=Math.min(q*1.5,60000)}}async processInbox(){try{let z=(await this.call("get_inbox",{alias:this.alias,limit:10}))?.messages||[];for(let q of z)await this.call("ack_inbox",{alias:this.alias,message_id:q.id}),this.log(`← ${q.from_session}: ${q.content.slice(0,60)}`),this.emit("task",q),this.emit("message",q)}catch(j){this.log(`inbox error: ${j.message}`)}}sleep(j){return new Promise((z)=>setTimeout(z,j))}}var $=T;export{$ as default,T as CommHub};
1
+ const a0_0xa29dcf=a0_0x536f;(function(_0x56b1c2,_0x1961d0){const _0x4e2d3d=a0_0x536f,_0x1f35d2=_0x56b1c2();while(!![]){try{const _0x528383=parseInt(_0x4e2d3d(0x1bb))/0x1*(-parseInt(_0x4e2d3d(0x1b4))/0x2)+-parseInt(_0x4e2d3d(0x1ce))/0x3*(-parseInt(_0x4e2d3d(0x1c8))/0x4)+-parseInt(_0x4e2d3d(0x1ac))/0x5+parseInt(_0x4e2d3d(0x1d8))/0x6+parseInt(_0x4e2d3d(0x1a0))/0x7+-parseInt(_0x4e2d3d(0x1b5))/0x8+-parseInt(_0x4e2d3d(0x1a1))/0x9*(-parseInt(_0x4e2d3d(0x1b0))/0xa);if(_0x528383===_0x1961d0)break;else _0x1f35d2['push'](_0x1f35d2['shift']());}catch(_0x20671f){_0x1f35d2['push'](_0x1f35d2['shift']());}}}(a0_0x2f70,0x3ed4b));import{EventEmitter as a0_0x35453a}from'events';import{hostname as a0_0x258229}from'os';function a0_0x2f70(){const _0x3ce9f0=['sdk','connectSSE','2012350FlASLv','POST','status','from_session','215190DYpAdJ','startsWith','2.0','sseAbort','2990haVEdt','2055672fnPACh','inbox\x20error:\x20','read','send_message','disconnected','send','159tNFExS','application/json,\x20text/event-stream','resumeId','content','offline','type','SSE\x20reconnecting\x20in\x20','agent','heartbeatTimer',']\x20[commhub:','completed','running','decode','990232KBbxjt','alias','/mcp','replace','parse','text','3udagBR','heartbeatInterval','connected','signal','min','now','call','emit','task','normal','418638hOrEQb','processInbox','split','get_inbox','error','catch','heartbeat\x20failed:\x20','new_task','body','idle','name','log','getAllStatus','token','Bearer\x20','text/event-stream','SSE\x20connected','stringify','messages','sleep','SSE\x20error:\x20','match','slice','reconnectDelay','Authorization','get_all_status','pop','url','2696813dHKjIz','189vQdcKD','abort','toString','reply','broadcast','message','server','result','SSE\x20failed:\x20'];a0_0x2f70=function(){return _0x3ce9f0;};return a0_0x2f70();}class T extends a0_0x35453a{[a0_0xa29dcf(0x19f)];[a0_0xa29dcf(0x1c9)];['token'];['agent'];['resumeId'];[a0_0xa29dcf(0x1cf)];[a0_0xa29dcf(0x19b)];['heartbeatTimer'];[a0_0xa29dcf(0x1b3)];['running']=!0x1;constructor(_0x2e13e3){const _0x29f191=a0_0xa29dcf;super();if(this['url']=_0x2e13e3[_0x29f191(0x19f)][_0x29f191(0x1cb)](/\/$/,''),this[_0x29f191(0x1c9)]=_0x2e13e3['alias'],this[_0x29f191(0x1e5)]=_0x2e13e3[_0x29f191(0x1e5)],this[_0x29f191(0x1c2)]=_0x2e13e3['agent']||_0x29f191(0x1aa),this[_0x29f191(0x1bd)]='sdk-'+_0x2e13e3['alias']+'-'+Date['now']()[_0x29f191(0x1a3)](0x24),this[_0x29f191(0x1cf)]=_0x2e13e3[_0x29f191(0x1cf)]??0x2bf20,this[_0x29f191(0x19b)]=_0x2e13e3[_0x29f191(0x19b)]??0xbb8,_0x2e13e3['autoConnect']!==!0x1)this['connect']();}[a0_0xa29dcf(0x1e3)](_0x1ece92){const _0x1510d3=a0_0xa29dcf;console[_0x1510d3(0x1e3)]('['+new Date()['toTimeString']()[_0x1510d3(0x19a)](0x0,0x8)+_0x1510d3(0x1c4)+this[_0x1510d3(0x1c9)]+']\x20'+_0x1ece92);}async['call'](_0x510898,_0x2724e5){const _0x56fde4=a0_0xa29dcf;let _0x1a666e={'Content-Type':'application/json','Accept':_0x56fde4(0x1bc)};if(this['token'])_0x1a666e[_0x56fde4(0x19c)]=_0x56fde4(0x1e6)+this['token'];let _0x4e6733=await(await fetch(this[_0x56fde4(0x19f)]+_0x56fde4(0x1ca),{'method':_0x56fde4(0x1ad),'headers':_0x1a666e,'body':JSON[_0x56fde4(0x1e9)]({'jsonrpc':_0x56fde4(0x1b2),'id':Date[_0x56fde4(0x1d3)](),'method':'tools/call','params':{'name':_0x510898,'arguments':_0x2724e5}})}))[_0x56fde4(0x1cd)](),_0x25fd40=_0x4e6733[_0x56fde4(0x199)](/data: (.+)/),_0x989aac=_0x25fd40?JSON[_0x56fde4(0x1cc)](_0x25fd40[0x1]):JSON[_0x56fde4(0x1cc)](_0x4e6733),_0x47164d=_0x989aac?.[_0x56fde4(0x1a8)]?.[_0x56fde4(0x1be)]?.[0x0]?.['text'];return _0x47164d?JSON['parse'](_0x47164d):_0x989aac;}async['connect'](){const _0x491a03=a0_0xa29dcf;if(this[_0x491a03(0x1c6)])return;this[_0x491a03(0x1c6)]=!0x0,await this[_0x491a03(0x1ae)](_0x491a03(0x1e1)),this[_0x491a03(0x1e3)]('registered'),this[_0x491a03(0x1c3)]=setInterval(()=>{const _0x33a10f=_0x491a03;this['status']('idle')[_0x33a10f(0x1dd)](_0x293b07=>this[_0x33a10f(0x1e3)](_0x33a10f(0x1de)+_0x293b07['message']));},this['heartbeatInterval']),this[_0x491a03(0x1ab)]();}async['disconnect'](){const _0x2f3b76=a0_0xa29dcf;if(this['running']=!0x1,this[_0x2f3b76(0x1b3)]?.[_0x2f3b76(0x1a2)](),this[_0x2f3b76(0x1c3)])clearInterval(this[_0x2f3b76(0x1c3)]);await this[_0x2f3b76(0x1ae)](_0x2f3b76(0x1bf))['catch'](()=>{}),this[_0x2f3b76(0x1e3)](_0x2f3b76(0x1b9));}async[a0_0xa29dcf(0x1ba)](_0x498bb6,_0x2e0e23,_0x65436b=a0_0xa29dcf(0x1d7)){const _0x2ffea8=a0_0xa29dcf;return this['call']('send_task',{'alias':_0x498bb6,'task':_0x2e0e23,'priority':_0x65436b,'from_session':this[_0x2ffea8(0x1c9)]});}async[a0_0xa29dcf(0x1a6)](_0x5b5434,_0x3dbb23){const _0x55a52c=a0_0xa29dcf;return this[_0x55a52c(0x1d4)](_0x55a52c(0x1b8),{'alias':_0x5b5434,'message':_0x3dbb23,'from_session':this['alias']});}async[a0_0xa29dcf(0x1a4)](_0x1b592c,_0x348ad5,_0xfd4e83=a0_0xa29dcf(0x1c5)){const _0x7994a5=a0_0xa29dcf;return this[_0x7994a5(0x1d4)](_0x7994a5(0x1a4),{'task_id':_0x1b592c,'text':_0x348ad5,'status':_0xfd4e83});}async[a0_0xa29dcf(0x1ae)](_0x4167b0,_0x494ef6){const _0x3df93a=a0_0xa29dcf;return this['call']('report_status',{'resume_id':this[_0x3df93a(0x1bd)],'alias':this[_0x3df93a(0x1c9)],'status':_0x4167b0,'server':a0_0x258229(),'hostname':a0_0x258229(),'agent':this[_0x3df93a(0x1c2)],'project_dir':process['cwd'](),..._0x494ef6});}async[a0_0xa29dcf(0x1e4)](){const _0x522510=a0_0xa29dcf;return this['call'](_0x522510(0x19d),{});}async[a0_0xa29dcf(0x1a5)](_0x4444fa,_0x288b98){const _0x246907=a0_0xa29dcf;return this[_0x246907(0x1d4)](_0x246907(0x1a5),{'message':_0x4444fa,'filter_server':_0x288b98?.[_0x246907(0x1a7)],'filter_status':_0x288b98?.['status']});}async[a0_0xa29dcf(0x1ab)](){const _0x2e1656=a0_0xa29dcf;let _0x2cfd33=encodeURIComponent(this[_0x2e1656(0x1c9)]),_0x389002=this[_0x2e1656(0x19f)]+'/events/'+_0x2cfd33,_0x293aff=this[_0x2e1656(0x19b)];while(this['running']){try{this[_0x2e1656(0x1b3)]=new AbortController();let _0x54eaae={'Accept':_0x2e1656(0x1e7)};if(this[_0x2e1656(0x1e5)])_0x54eaae[_0x2e1656(0x19c)]=_0x2e1656(0x1e6)+this['token'];let _0x4e605c=await fetch(_0x389002,{'headers':_0x54eaae,'signal':this['sseAbort'][_0x2e1656(0x1d1)]});if(!_0x4e605c['ok']||!_0x4e605c[_0x2e1656(0x1e0)]){this[_0x2e1656(0x1e3)](_0x2e1656(0x1a9)+_0x4e605c[_0x2e1656(0x1ae)]),await this['sleep'](_0x293aff),_0x293aff=Math[_0x2e1656(0x1d2)](_0x293aff*1.5,0xea60);continue;}_0x293aff=this[_0x2e1656(0x19b)];let _0x29ffd2=_0x4e605c[_0x2e1656(0x1e0)]['getReader'](),_0x487cad=new TextDecoder(),_0x1e6c97='';while(this[_0x2e1656(0x1c6)]){let {done:_0x30ed84,value:_0x1ac4b1}=await _0x29ffd2[_0x2e1656(0x1b7)]();if(_0x30ed84)break;_0x1e6c97+=_0x487cad[_0x2e1656(0x1c7)](_0x1ac4b1,{'stream':!0x0});let _0x12abd7=_0x1e6c97[_0x2e1656(0x1da)]('\x0a');_0x1e6c97=_0x12abd7[_0x2e1656(0x19e)]()||'';for(let _0x25cd58 of _0x12abd7){if(!_0x25cd58[_0x2e1656(0x1b1)]('data:\x20'))continue;try{let _0x1fc0b0=JSON[_0x2e1656(0x1cc)](_0x25cd58['slice'](0x6));if(_0x1fc0b0[_0x2e1656(0x1c0)]===_0x2e1656(0x1d0)){this[_0x2e1656(0x1e3)](_0x2e1656(0x1e8)),this[_0x2e1656(0x1d5)]('connected');continue;}if(_0x1fc0b0[_0x2e1656(0x1c0)]===_0x2e1656(0x1df)||_0x1fc0b0[_0x2e1656(0x1c0)]==='new_message'||_0x1fc0b0[_0x2e1656(0x1c0)]===_0x2e1656(0x1a5))await this[_0x2e1656(0x1d9)]();}catch{}}}}catch(_0x306887){if(_0x306887[_0x2e1656(0x1e2)]==='AbortError')break;this[_0x2e1656(0x1d5)](_0x2e1656(0x1dc),_0x306887),this['log'](_0x2e1656(0x198)+_0x306887[_0x2e1656(0x1a6)]);}if(this['running'])this[_0x2e1656(0x1d5)](_0x2e1656(0x1b9)),this[_0x2e1656(0x1e3)](_0x2e1656(0x1c1)+_0x293aff/0x3e8+'s...'),await this[_0x2e1656(0x1eb)](_0x293aff),_0x293aff=Math['min'](_0x293aff*1.5,0xea60);}}async[a0_0xa29dcf(0x1d9)](){const _0x1a625c=a0_0xa29dcf;try{let _0x1ddc52=(await this[_0x1a625c(0x1d4)](_0x1a625c(0x1db),{'alias':this[_0x1a625c(0x1c9)],'limit':0xa}))?.[_0x1a625c(0x1ea)]||[];for(let _0x2a0455 of _0x1ddc52)await this[_0x1a625c(0x1d4)]('ack_inbox',{'alias':this[_0x1a625c(0x1c9)],'message_id':_0x2a0455['id']}),this[_0x1a625c(0x1e3)]('←\x20'+_0x2a0455[_0x1a625c(0x1af)]+':\x20'+_0x2a0455[_0x1a625c(0x1be)][_0x1a625c(0x19a)](0x0,0x3c)),this[_0x1a625c(0x1d5)](_0x1a625c(0x1d6),_0x2a0455),this[_0x1a625c(0x1d5)](_0x1a625c(0x1a6),_0x2a0455);}catch(_0x460b5e){this[_0x1a625c(0x1e3)](_0x1a625c(0x1b6)+_0x460b5e['message']);}}['sleep'](_0x190918){return new Promise(_0x3b9f2=>setTimeout(_0x3b9f2,_0x190918));}}function a0_0x536f(_0x2fc11c,_0x36ca04){_0x2fc11c=_0x2fc11c-0x198;const _0x2f7084=a0_0x2f70();let _0x536f9c=_0x2f7084[_0x2fc11c];return _0x536f9c;}var $=T;export{$ as default,T as CommHub};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ const a0_0x2b9b3d=a0_0x1bf4;(function(_0x1a57c0,_0x1eceb2){const _0x58f1cd=a0_0x1bf4,_0x12b7f1=_0x1a57c0();while(!![]){try{const _0x566dd4=-parseInt(_0x58f1cd(0x10a))/0x1*(parseInt(_0x58f1cd(0x144))/0x2)+parseInt(_0x58f1cd(0x12c))/0x3*(-parseInt(_0x58f1cd(0x142))/0x4)+parseInt(_0x58f1cd(0x154))/0x5+parseInt(_0x58f1cd(0x120))/0x6+-parseInt(_0x58f1cd(0x15d))/0x7*(parseInt(_0x58f1cd(0x126))/0x8)+parseInt(_0x58f1cd(0x108))/0x9*(-parseInt(_0x58f1cd(0x13f))/0xa)+parseInt(_0x58f1cd(0x15b))/0xb;if(_0x566dd4===_0x1eceb2)break;else _0x12b7f1['push'](_0x12b7f1['shift']());}catch(_0x36e2df){_0x12b7f1['push'](_0x12b7f1['shift']());}}}(a0_0x3646,0xccce1));import{readFileSync as a0_0x12a839,existsSync as a0_0x5c3398}from'fs';import{join as a0_0x52fce1}from'path';import{hostname as a0_0x1fcddb}from'os';import{execSync as a0_0x2ff0d7}from'child_process';import{Server as a0_0x5c0769}from'@modelcontextprotocol/sdk/server/index.js';import{StdioServerTransport as a0_0x590708}from'@modelcontextprotocol/sdk/server/stdio.js';import{ListToolsRequestSchema as a0_0x45ba50,CallToolRequestSchema as a0_0x1369c1}from'@modelcontextprotocol/sdk/types.js';function h(_0x393fe3){const _0x5168d8=a0_0x1bf4;if(!a0_0x5c3398(_0x393fe3))return;for(let _0x1015d5 of a0_0x12a839(_0x393fe3,'utf-8')[_0x5168d8(0x16e)]('\x0a')){let _0x4b41dc=_0x1015d5[_0x5168d8(0x10f)]();if(!_0x4b41dc||_0x4b41dc[_0x5168d8(0x156)]('#'))continue;let _0x525b41=_0x4b41dc[_0x5168d8(0x15c)]('=');if(_0x525b41<0x0)continue;let _0x312cdb=_0x4b41dc[_0x5168d8(0x162)](0x0,_0x525b41)['trim'](),_0x2f055e=_0x4b41dc[_0x5168d8(0x162)](_0x525b41+0x1)[_0x5168d8(0x10f)]()[_0x5168d8(0x11a)](/^["']|["']$/g,'');if(!process.env[_0x312cdb])process.env[_0x312cdb]=_0x2f055e;}}var R=process.env.HOME||'~',p=a0_0x52fce1(R,a0_0x2b9b3d(0x152));h(a0_0x52fce1(p,a0_0x2b9b3d(0x10b)));var C=process['cwd']()[a0_0x2b9b3d(0x11a)](/\//g,'-');h(a0_0x52fce1(p,C,a0_0x2b9b3d(0x10b)));function _(){const _0xc54e42=a0_0x2b9b3d;try{return a0_0x2ff0d7('tmux\x20display-message\x20-p\x20\x27#S\x27',{'encoding':_0xc54e42(0x168),'timeout':0x7d0})['trim']();}catch{return'';}}function E(){const _0x4a52f1=a0_0x2b9b3d;try{let _0x2ec6ee=a0_0x52fce1(R,_0x4a52f1(0xe6),_0x4a52f1(0x122));if(a0_0x5c3398(_0x2ec6ee))return JSON[_0x4a52f1(0x14f)](a0_0x12a839(_0x2ec6ee,_0x4a52f1(0x168)));}catch{}return{};}var T=E(),k=process.env.COMMHUB_URL||T['hub']||a0_0x2b9b3d(0x153),q=process.env.COMMHUB_TMUX||_(),Z=process.env.COMMHUB_ALIAS||q||a0_0x1fcddb(),w=process.env.COMMHUB_RESUME_ID||process.env.CLAUDE_RESUME_ID||crypto['randomUUID'](),X=process.env.COMMHUB_TOKEN||T[a0_0x2b9b3d(0x14d)]||'';function V(_0x422215){const _0x10f246=a0_0x2b9b3d;let _0x50f45c=new Date()[_0x10f246(0xe7)]()['slice'](0x0,0x8);process['stderr'][_0x10f246(0x124)]('['+_0x50f45c+_0x10f246(0x13b)+_0x422215+'\x0a');}function O(_0x4d8701){return new Promise(_0x345750=>setTimeout(_0x345750,_0x4d8701));}V('ENV:\x20URL='+k+a0_0x2b9b3d(0x135)+Z+'\x20RESUME_ID='+w[a0_0x2b9b3d(0x162)](0x0,0x8)+a0_0x2b9b3d(0x139)+(q||a0_0x2b9b3d(0x150))+'\x20CWD='+process[a0_0x2b9b3d(0x159)]()+'\x20PROJECT_ENV='+C);var K=new a0_0x5c0769({'name':'commhub-channel','version':'0.3.0'},{'capabilities':{'experimental':{'claude/channel':{}},'tools':{}},'instructions':[a0_0x2b9b3d(0x136),a0_0x2b9b3d(0x157),'Reply\x20using\x20the\x20commhub_reply\x20tool\x20to\x20report\x20status\x20or\x20results\x20back.','You\x20can\x20also\x20use\x20commhub_report_status\x20to\x20update\x20your\x20session\x20status.','Session\x20alias:\x20'+Z][a0_0x2b9b3d(0xec)]('\x0a')});function a0_0x3646(){const _0x369f80=['CommHub\x20init\x20failed:\x20','config.json','from','write','parse\x20error:\x20','4105000fSNFwD','Current\x20task\x20description','SSE\x20connection\x20error:\x20','Task\x20status','notifications/claude/channel','completed','9468ZbOFSX','object','task','0.3.0','shutting\x20down,\x20reporting\x20offline...','read','body','commhub','commhub-channel','\x20ALIAS=','Messages\x20from\x20CommHub\x20arrive\x20as\x20<channel\x20source=\x22commhub\x22\x20task_id=\x22...\x22\x20priority=\x22...\x22\x20from=\x22...\x22>','\x22\x20(','broadcast','...\x20TMUX=','Reply\x20text\x20/\x20result\x20summary',']\x20[commhub]\x20','params','init\x20failed:\x20','in_progress','13361770IBaTVL','send_message','←\x20message\x20from\x20','740FgagRF','message','8EouULk','get_all_status','error','text','from_session','blocked','message_id','Send\x20a\x20message\x20to\x20another\x20session\x20(no\x20task\x20lifecycle,\x20just\x20chat).\x20Use\x20for\x20replies\x20and\x20status\x20updates.','/mcp','token','starting\x20SSE\x20listener...','parse','none','result','.claude/channels/commhub','http://127.0.0.1:9200','7688170QivzVY','Get\x20status\x20of\x20all\x20sessions\x20from\x20CommHub.','startsWith','These\x20are\x20tasks\x20dispatched\x20by\x20the\x20hub\x20or\x20other\x20sessions\x20via\x20the\x20CommHub\x20Server.','decode','cwd','The\x20task_id\x20from\x20the\x20channel\x20message\x20(or\x20\x27hub\x27\x20for\x20general)','21862236JAEbvq','indexOf','14puWDyF','application/json','tools/call','setRequestHandler','get_inbox','slice','Update\x20this\x20session\x27s\x20status\x20in\x20CommHub\x20(working/idle/blocked/error).\x20Returns\x20inbox_count.','stringify','commhub_reply','statusText','registered\x20as\x20\x22','utf-8','status','then','Target\x20session\x20alias','stdin','Authorization','split','Task\x20content','connecting\x20to\x20','ack_inbox','.anet','toTimeString','send_task','warning:\x20could\x20not\x20register:\x20','idle','data:\x20','join','find','2.0','inbox_count','high','heartbeat\x20failed:\x20','getReader','POST','alias','commhub_get_all_status','type','Message\x20content','unknown\x20tool','SSE\x20error:\x20','Progress\x200-100','catch','new_task','\x20priority=','commhub_report_status','priority','commhub_send_task','working','\x20from\x20','report_status','/events/','application/json,\x20text/event-stream','session\x20disconnected','MCP\x20stdio\x20connected','9OGNMUc','exit','172673VhwvTq','.env','SSE\x20connected\x20as\x20\x22','initialize','Bearer\x20','trim','string','content','ready\x20—\x20waiting\x20for\x20events','commhub_send_message','connect','low','2025-03-26','no\x20response','end','messages','replace','notification','new_message','hub','normal','claude-code','5704500UtzoJO'];a0_0x3646=function(){return _0x369f80;};return a0_0x3646();}K[a0_0x2b9b3d(0x160)](a0_0x45ba50,async()=>({'tools':[{'name':a0_0x2b9b3d(0x165),'description':'Reply\x20to\x20a\x20CommHub\x20task\x20—\x20report\x20completion\x20or\x20send\x20a\x20message\x20back\x20to\x20the\x20hub.','inputSchema':{'type':'object','properties':{'task_id':{'type':a0_0x2b9b3d(0x110),'description':a0_0x2b9b3d(0x15a)},'text':{'type':a0_0x2b9b3d(0x110),'description':a0_0x2b9b3d(0x13a)},'status':{'type':a0_0x2b9b3d(0x110),'enum':[a0_0x2b9b3d(0x12b),'blocked','error',a0_0x2b9b3d(0x13e)],'description':a0_0x2b9b3d(0x129)}},'required':['text']}},{'name':a0_0x2b9b3d(0xfe),'description':a0_0x2b9b3d(0x163),'inputSchema':{'type':a0_0x2b9b3d(0x12d),'properties':{'status':{'type':'string','enum':[a0_0x2b9b3d(0x101),a0_0x2b9b3d(0xea),a0_0x2b9b3d(0x149),'error']},'task':{'type':a0_0x2b9b3d(0x110),'description':a0_0x2b9b3d(0x127)},'progress':{'type':'number','description':a0_0x2b9b3d(0xfa)}},'required':[a0_0x2b9b3d(0x169)]}},{'name':a0_0x2b9b3d(0x100),'description':'Send\x20a\x20task\x20to\x20another\x20session\x20via\x20CommHub.','inputSchema':{'type':a0_0x2b9b3d(0x12d),'properties':{'alias':{'type':'string','description':a0_0x2b9b3d(0x16b)},'task':{'type':a0_0x2b9b3d(0x110),'description':a0_0x2b9b3d(0xe3)},'priority':{'type':a0_0x2b9b3d(0x110),'enum':[a0_0x2b9b3d(0xf0),'normal',a0_0x2b9b3d(0x115)],'description':'Priority\x20(default:\x20normal)'}},'required':['alias',a0_0x2b9b3d(0x12e)]}},{'name':a0_0x2b9b3d(0x113),'description':a0_0x2b9b3d(0x14b),'inputSchema':{'type':a0_0x2b9b3d(0x12d),'properties':{'alias':{'type':a0_0x2b9b3d(0x110),'description':'Target\x20session\x20alias'},'message':{'type':'string','description':a0_0x2b9b3d(0xf7)}},'required':[a0_0x2b9b3d(0xf4),a0_0x2b9b3d(0x143)]}},{'name':a0_0x2b9b3d(0xf5),'description':a0_0x2b9b3d(0x155),'inputSchema':{'type':a0_0x2b9b3d(0x12d),'properties':{}}}]}));function a0_0x1bf4(_0x1139c0,_0x2760b1){_0x1139c0=_0x1139c0-0xe3;const _0x36467d=a0_0x3646();let _0x1bf4e7=_0x36467d[_0x1139c0];return _0x1bf4e7;}async function P(_0x19cd25,_0x76bea9){const _0xcd5b22=a0_0x2b9b3d;let _0x5f57cd=await fetch(k+_0xcd5b22(0x14c),{'method':_0xcd5b22(0xf3),'headers':{'Content-Type':_0xcd5b22(0x15e),'Accept':'application/json,\x20text/event-stream',...X?{'Authorization':_0xcd5b22(0x10e)+X}:{}},'body':JSON[_0xcd5b22(0x164)]({'jsonrpc':_0xcd5b22(0xee),'id':0x1,'method':_0xcd5b22(0x10d),'params':{'protocolVersion':_0xcd5b22(0x116),'capabilities':{},'clientInfo':{'name':_0xcd5b22(0x134),'version':_0xcd5b22(0x12f)}}})});if(!_0x5f57cd['ok']){let _0x5bf6df=await _0x5f57cd[_0xcd5b22(0x147)]();return V(_0xcd5b22(0x121)+_0x5f57cd[_0xcd5b22(0x169)]+'\x20'+_0x5bf6df[_0xcd5b22(0x162)](0x0,0x64)),{'ok':!0x1,'error':_0xcd5b22(0x13d)+_0x5f57cd[_0xcd5b22(0x169)]};}await _0x5f57cd[_0xcd5b22(0x147)]();let _0x3e79bd=(await(await fetch(k+'/mcp',{'method':_0xcd5b22(0xf3),'headers':{'Content-Type':_0xcd5b22(0x15e),'Accept':_0xcd5b22(0x105),...X?{'Authorization':'Bearer\x20'+X}:{}},'body':JSON[_0xcd5b22(0x164)]({'jsonrpc':_0xcd5b22(0xee),'id':0x2,'method':_0xcd5b22(0x15f),'params':{'name':_0x19cd25,'arguments':_0x76bea9}})}))['text']())[_0xcd5b22(0x16e)]('\x0a')[_0xcd5b22(0xed)](_0x57b831=>_0x57b831['startsWith'](_0xcd5b22(0xeb)));if(_0x3e79bd){let _0x2c3ded=JSON['parse'](_0x3e79bd['slice'](0x6));return _0x2c3ded?.['result']?.[_0xcd5b22(0x111)]?.[0x0]?.[_0xcd5b22(0x147)]?JSON[_0xcd5b22(0x14f)](_0x2c3ded[_0xcd5b22(0x151)]['content'][0x0]['text']):_0x2c3ded;}return{'ok':!0x1,'error':_0xcd5b22(0x117)};}K[a0_0x2b9b3d(0x160)](a0_0x1369c1,async _0x464e9b=>{const _0x5ca19b=a0_0x2b9b3d;let {name:_0xa72498,arguments:_0x49a128}=_0x464e9b[_0x5ca19b(0x13c)];if(_0xa72498==='commhub_reply'){let {task_id:_0x93eb0a,text:_0x974bda,status:_0xc3415}=_0x49a128;if(_0xc3415==='completed'){let _0x1425bb=await P('report_completion',{'alias':Z,'task':_0x93eb0a||'task','result':_0x974bda});return{'content':[{'type':'text','text':JSON['stringify'](_0x1425bb)}]};}let _0x10ea67=await P('report_status',{'resume_id':w,'alias':Z,'status':_0xc3415==='blocked'?_0x5ca19b(0x149):_0xc3415===_0x5ca19b(0x146)?_0x5ca19b(0x146):'working','task':_0x974bda[_0x5ca19b(0x162)](0x0,0xc8),'output':_0x974bda});return{'content':[{'type':_0x5ca19b(0x147),'text':JSON['stringify'](_0x10ea67)}]};}if(_0xa72498==='commhub_report_status'){let {status:_0x4b38ec,task:_0x3258ca,progress:_0x6d499e}=_0x49a128,_0x295f09=await P(_0x5ca19b(0x103),{'resume_id':w,'alias':Z,'status':_0x4b38ec,'task':_0x3258ca,'progress':_0x6d499e});return{'content':[{'type':_0x5ca19b(0x147),'text':JSON['stringify'](_0x295f09)}]};}if(_0xa72498===_0x5ca19b(0x100)){let {alias:_0x4fd2c6,task:_0xe8315c,priority:_0x2a0b1e}=_0x49a128,_0x2f1be1=await P(_0x5ca19b(0xe8),{'alias':_0x4fd2c6,'task':_0xe8315c,'priority':_0x2a0b1e||_0x5ca19b(0x11e),'from_session':Z});return{'content':[{'type':_0x5ca19b(0x147),'text':JSON[_0x5ca19b(0x164)](_0x2f1be1)}]};}if(_0xa72498===_0x5ca19b(0x113)){let {alias:_0x153b9e,message:_0x21b73e}=_0x49a128,_0x4673ca=await P(_0x5ca19b(0x140),{'alias':_0x153b9e,'message':_0x21b73e,'from_session':Z});return{'content':[{'type':'text','text':JSON[_0x5ca19b(0x164)](_0x4673ca)}]};}if(_0xa72498===_0x5ca19b(0xf5)){let _0x5e942e=await P(_0x5ca19b(0x145),{});return{'content':[{'type':_0x5ca19b(0x147),'text':JSON[_0x5ca19b(0x164)](_0x5e942e)}]};}return{'content':[{'type':'text','text':JSON[_0x5ca19b(0x164)]({'error':_0x5ca19b(0xf8)})}]};});async function c(){const _0x193af9=a0_0x2b9b3d;let _0x545cd7=k+_0x193af9(0x104)+encodeURIComponent(Z),_0x5f0a9d={};if(X)_0x5f0a9d[_0x193af9(0x16d)]=_0x193af9(0x10e)+X;V(_0x193af9(0xe4)+_0x545cd7);while(!0x0){try{let _0x310a06=await fetch(_0x545cd7,{'headers':_0x5f0a9d});if(!_0x310a06['ok']){V(_0x193af9(0xf9)+_0x310a06[_0x193af9(0x169)]+'\x20'+_0x310a06[_0x193af9(0x166)]),await O(0x1388);continue;}let _0x46d95e=_0x310a06[_0x193af9(0x132)][_0x193af9(0xf2)](),_0x5871ea=new TextDecoder(),_0x380a1f='';while(!0x0){let {done:_0x543f87,value:_0x22413f}=await _0x46d95e[_0x193af9(0x131)]();if(_0x543f87)break;_0x380a1f+=_0x5871ea[_0x193af9(0x158)](_0x22413f,{'stream':!0x0});let _0x482845=_0x380a1f[_0x193af9(0x16e)]('\x0a\x0a');_0x380a1f=_0x482845['pop']()||'';for(let _0x331dd2 of _0x482845){let _0x20a3b=_0x331dd2[_0x193af9(0x16e)]('\x0a')[_0x193af9(0xed)](_0x2b2f24=>_0x2b2f24[_0x193af9(0x156)]('data:\x20'));if(!_0x20a3b)continue;try{let _0x1a6837=JSON[_0x193af9(0x14f)](_0x20a3b[_0x193af9(0x162)](0x6));await v(_0x1a6837);}catch(_0x5dea5d){V(_0x193af9(0x125)+_0x5dea5d);}}}V('SSE\x20stream\x20ended,\x20reconnecting...');}catch(_0x488ab1){V(_0x193af9(0x128)+_0x488ab1);}await O(0xbb8);}}async function v(_0x1497e7){const _0x2cb4fb=a0_0x2b9b3d;if(_0x1497e7[_0x2cb4fb(0xf6)]==='connected'){V(_0x2cb4fb(0x10c)+Z+'\x22');return;}if(_0x1497e7[_0x2cb4fb(0xf6)]===_0x2cb4fb(0x11c)){if(V(_0x2cb4fb(0x141)+_0x1497e7[_0x2cb4fb(0x123)]+':\x20'+_0x1497e7[_0x2cb4fb(0x143)][_0x2cb4fb(0x162)](0x0,0x3c)),await K[_0x2cb4fb(0x11b)]({'method':_0x2cb4fb(0x12a),'params':{'content':_0x1497e7['message'],'meta':{'sender':_0x1497e7[_0x2cb4fb(0x123)]||_0x2cb4fb(0x11d),'sender_id':'commhub','user':_0x1497e7[_0x2cb4fb(0x123)]||_0x2cb4fb(0x11d),'priority':_0x2cb4fb(0x11e)}}}),_0x1497e7[_0x2cb4fb(0x14a)])await P(_0x2cb4fb(0xe5),{'alias':Z,'message_id':_0x1497e7[_0x2cb4fb(0x14a)]});return;}if(_0x1497e7[_0x2cb4fb(0xf6)]===_0x2cb4fb(0xfc)||_0x1497e7[_0x2cb4fb(0xf6)]===_0x2cb4fb(0x138)){V('←\x20'+_0x1497e7[_0x2cb4fb(0xf6)]+':\x20inbox_count='+_0x1497e7[_0x2cb4fb(0xef)]+_0x2cb4fb(0xfd)+(_0x1497e7[_0x2cb4fb(0xff)]||_0x2cb4fb(0x11e)));let _0x15927a=await P(_0x2cb4fb(0x161),{'alias':Z,'limit':0x5});if(_0x15927a?.['ok']&&_0x15927a[_0x2cb4fb(0x119)]?.['length']>0x0)for(let _0x117405 of _0x15927a[_0x2cb4fb(0x119)]){let _0x4a8693={'sender':_0x117405[_0x2cb4fb(0x148)]||_0x2cb4fb(0x11d),'sender_id':_0x2cb4fb(0x133),'user':_0x117405['from_session']||_0x2cb4fb(0x11d),'task_id':_0x117405['id'],'priority':_0x117405[_0x2cb4fb(0xff)]||_0x2cb4fb(0x11e)};await K[_0x2cb4fb(0x11b)]({'method':'notifications/claude/channel','params':{'content':_0x117405[_0x2cb4fb(0x111)],'meta':_0x4a8693}}),V('→\x20injected\x20task\x20'+_0x117405['id'][_0x2cb4fb(0x162)](0x0,0x8)+_0x2cb4fb(0x102)+_0x117405[_0x2cb4fb(0x148)]+':\x20'+_0x117405[_0x2cb4fb(0x111)][_0x2cb4fb(0x162)](0x0,0x3c)),await P(_0x2cb4fb(0xe5),{'alias':Z,'message_id':_0x117405['id']});}}}async function u(){const _0xf8936f=a0_0x2b9b3d;let _0x523e3d=new a0_0x590708();await K[_0xf8936f(0x114)](_0x523e3d),V(_0xf8936f(0x107)),V(_0xf8936f(0x14e)),c()[_0xf8936f(0xfb)](_0x5eefea=>V('SSE\x20fatal:\x20'+_0x5eefea)),P(_0xf8936f(0x103),{'resume_id':w,'alias':Z,'status':_0xf8936f(0xea),'server':a0_0x1fcddb(),'hostname':a0_0x1fcddb(),'agent':_0xf8936f(0x11f),'project_dir':process[_0xf8936f(0x159)](),'tmux_name':q||void 0x0})[_0xf8936f(0x16a)](()=>V(_0xf8936f(0x167)+Z+_0xf8936f(0x137)+w[_0xf8936f(0x162)](0x0,0x8)+')'))[_0xf8936f(0xfb)](_0x2d1e74=>V(_0xf8936f(0xe9)+_0x2d1e74)),setInterval(()=>{const _0x194879=_0xf8936f;P(_0x194879(0x103),{'resume_id':w,'alias':Z,'status':_0x194879(0xea),'server':a0_0x1fcddb(),'hostname':a0_0x1fcddb(),'agent':_0x194879(0x11f),'project_dir':process[_0x194879(0x159)](),'tmux_name':q||void 0x0})[_0x194879(0xfb)](_0x242ab6=>V(_0x194879(0xf1)+_0x242ab6));},0x2bf20),V(_0xf8936f(0x112));}u()[a0_0x2b9b3d(0xfb)](_0x369bfe=>{const _0x3ef76d=a0_0x2b9b3d;V('fatal:\x20'+_0x369bfe),process[_0x3ef76d(0x109)](0x1);});async function f(){const _0x18fdf1=a0_0x2b9b3d;V(_0x18fdf1(0x130)),await P(_0x18fdf1(0x103),{'resume_id':w,'alias':Z,'status':_0x18fdf1(0x146),'task':_0x18fdf1(0x106)})[_0x18fdf1(0xfb)](()=>{}),process[_0x18fdf1(0x109)](0x0);}process[a0_0x2b9b3d(0x16c)]['on'](a0_0x2b9b3d(0x118),()=>f()),process['on']('SIGTERM',()=>f()),process['on']('SIGINT',()=>f());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "description": "AI Agent Network — Server + Client + Setup in one package. SSE real-time communication for multi-agent orchestration.",
5
5
  "type": "module",
6
6
  "main": "dist/src/client.js",
@@ -18,12 +18,10 @@
18
18
  "anet": "dist/bin/cli.js"
19
19
  },
20
20
  "files": [
21
- "dist",
22
- "src/server.ts",
23
- "src/node-server.ts"
21
+ "dist"
24
22
  ],
25
23
  "scripts": {
26
- "build": "bun build src/client.ts --outdir dist/src --target node --minify && bun build bin/cli.ts --outdir dist/bin --target node --minify --external @sleep2agi/commhub-server --external bun:sqlite --external '../../server/*' && tsc --emitDeclarationOnly --declaration --outDir dist",
24
+ "build": "bun build src/client.ts --outdir dist/src --target node --minify && bun build bin/cli.ts --outdir dist/bin --target node --minify --external @sleep2agi/commhub-server --external bun:sqlite --external '../../server/*' && bun build src/node-server.ts --outdir dist/src --target node --minify --external @modelcontextprotocol/sdk && tsc --emitDeclarationOnly --declaration --outDir dist && npx javascript-obfuscator dist/bin/cli.js --output dist/bin/cli.js --compact true --control-flow-flattening true --string-array true --string-array-encoding base64 && npx javascript-obfuscator dist/src/client.js --output dist/src/client.js --compact true --string-array true && npx javascript-obfuscator dist/src/node-server.js --output dist/src/node-server.js --compact true --string-array true",
27
25
  "prepublishOnly": "npm run build"
28
26
  },
29
27
  "keywords": [
@@ -50,8 +48,12 @@
50
48
  "engines": {
51
49
  "bun": ">=1.2.0"
52
50
  },
51
+ "dependencies": {
52
+ "@inquirer/prompts": "^7.10.1"
53
+ },
53
54
  "devDependencies": {
54
55
  "@types/node": "^25.0.0",
56
+ "javascript-obfuscator": "^5.4.1",
55
57
  "typescript": "^5.0.0"
56
58
  }
57
59
  }
@@ -1,472 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * CommHub Channel Plugin for Claude Code
4
- *
5
- * Alias resolution (priority order):
6
- * 1. COMMHUB_ALIAS env var
7
- * 2. Project .env: ~/.claude/channels/commhub/{project-path}/.env
8
- * 3. tmux session name
9
- * 4. hostname
10
- *
11
- * Shared config from: ~/.claude/channels/commhub/.env
12
- * COMMHUB_URL, COMMHUB_TOKEN
13
- */
14
-
15
- import { readFileSync, existsSync } from "fs";
16
- import { join } from "path";
17
- import { hostname } from "os";
18
- import { execSync } from "child_process";
19
-
20
- // ── .env loader helper ────────────────────────────────
21
- function loadEnvFile(path: string): void {
22
- if (!existsSync(path)) return;
23
- for (const line of readFileSync(path, "utf-8").split("\n")) {
24
- const trimmed = line.trim();
25
- if (!trimmed || trimmed.startsWith("#")) continue;
26
- const eq = trimmed.indexOf("=");
27
- if (eq < 0) continue;
28
- const key = trimmed.slice(0, eq).trim();
29
- const val = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
30
- if (!process.env[key]) process.env[key] = val;
31
- }
32
- }
33
-
34
- // ── Load shared config ────────────────────────────────
35
- const HOME = process.env.HOME || "~";
36
- const COMMHUB_DIR = join(HOME, ".claude/channels/commhub");
37
- loadEnvFile(join(COMMHUB_DIR, ".env"));
38
-
39
- // ── Load project-specific config ──────────────────────
40
- // /home/vansin/vincent → -home-vansin-vincent
41
- const projectPath = process.cwd().replace(/\//g, "-");
42
- loadEnvFile(join(COMMHUB_DIR, projectPath, ".env"));
43
-
44
- // ── Get tmux session name ─────────────────────────────
45
- function getTmuxSessionName(): string {
46
- try {
47
- return execSync("tmux display-message -p '#S'", { encoding: "utf-8", timeout: 2000 }).trim();
48
- } catch {
49
- return "";
50
- }
51
- }
52
-
53
- // ── Resolve config ────────────────────────────────────
54
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
55
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
56
- import {
57
- ListToolsRequestSchema,
58
- CallToolRequestSchema,
59
- } from "@modelcontextprotocol/sdk/types.js";
60
-
61
- // ── Load ~/.anet/config.json for token fallback ──────
62
- function loadAnetConfig(): Record<string, string> {
63
- try {
64
- const p = join(HOME, ".anet", "config.json");
65
- if (existsSync(p)) return JSON.parse(readFileSync(p, "utf-8"));
66
- } catch {}
67
- return {};
68
- }
69
- const ANET_CONFIG = loadAnetConfig();
70
-
71
- const COMMHUB_URL = process.env.COMMHUB_URL || ANET_CONFIG.hub || "http://127.0.0.1:9200";
72
- const TMUX_NAME = process.env.COMMHUB_TMUX || getTmuxSessionName();
73
- const ALIAS = process.env.COMMHUB_ALIAS || TMUX_NAME || hostname();
74
- const RESUME_ID = process.env.COMMHUB_RESUME_ID || process.env.CLAUDE_RESUME_ID || crypto.randomUUID();
75
- const AUTH_TOKEN = process.env.COMMHUB_TOKEN || ANET_CONFIG.token || "";
76
-
77
- function log(msg: string) {
78
- const ts = new Date().toTimeString().slice(0, 8);
79
- process.stderr.write(`[${ts}] [commhub] ${msg}\n`);
80
- }
81
-
82
- function sleep(ms: number): Promise<void> {
83
- return new Promise((r) => setTimeout(r, ms));
84
- }
85
-
86
- log(`ENV: URL=${COMMHUB_URL} ALIAS=${ALIAS} RESUME_ID=${RESUME_ID.slice(0, 8)}... TMUX=${TMUX_NAME || "none"} CWD=${process.cwd()} PROJECT_ENV=${projectPath}`);
87
-
88
- // ── MCP Server with Channel capability ──────────────
89
- // name 不要拼 alias!Claude Code 用 meta.user 自动加 "· xxx" 后缀
90
- // 参考: telegram 插件 name 也只是 "telegram",不是 "telegram · vansinhu"
91
- const mcp = new Server(
92
- {
93
- name: "commhub-channel",
94
- version: "0.3.0",
95
- },
96
- {
97
- capabilities: {
98
- experimental: { "claude/channel": {} },
99
- tools: {},
100
- },
101
- instructions: [
102
- `Messages from CommHub arrive as <channel source="commhub" task_id="..." priority="..." from="...">`,
103
- `These are tasks dispatched by the hub or other sessions via the CommHub Server.`,
104
- `Reply using the commhub_reply tool to report status or results back.`,
105
- `You can also use commhub_report_status to update your session status.`,
106
- `Session alias: ${ALIAS}`,
107
- ].join("\n"),
108
- }
109
- );
110
-
111
- // ── Tools ───────────────────────────────────────────
112
- mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
113
- tools: [
114
- {
115
- name: "commhub_reply",
116
- description: "Reply to a CommHub task — report completion or send a message back to the hub.",
117
- inputSchema: {
118
- type: "object" as const,
119
- properties: {
120
- task_id: { type: "string", description: "The task_id from the channel message (or 'hub' for general)" },
121
- text: { type: "string", description: "Reply text / result summary" },
122
- status: {
123
- type: "string",
124
- enum: ["completed", "blocked", "error", "in_progress"],
125
- description: "Task status",
126
- },
127
- },
128
- required: ["text"],
129
- },
130
- },
131
- {
132
- name: "commhub_report_status",
133
- description: "Update this session's status in CommHub (working/idle/blocked/error). Returns inbox_count.",
134
- inputSchema: {
135
- type: "object" as const,
136
- properties: {
137
- status: {
138
- type: "string",
139
- enum: ["working", "idle", "blocked", "error"],
140
- },
141
- task: { type: "string", description: "Current task description" },
142
- progress: { type: "number", description: "Progress 0-100" },
143
- },
144
- required: ["status"],
145
- },
146
- },
147
- {
148
- name: "commhub_send_task",
149
- description: "Send a task to another session via CommHub.",
150
- inputSchema: {
151
- type: "object" as const,
152
- properties: {
153
- alias: { type: "string", description: "Target session alias" },
154
- task: { type: "string", description: "Task content" },
155
- priority: { type: "string", enum: ["high", "normal", "low"], description: "Priority (default: normal)" },
156
- },
157
- required: ["alias", "task"],
158
- },
159
- },
160
- {
161
- name: "commhub_send_message",
162
- description: "Send a message to another session (no task lifecycle, just chat). Use for replies and status updates.",
163
- inputSchema: {
164
- type: "object" as const,
165
- properties: {
166
- alias: { type: "string", description: "Target session alias" },
167
- message: { type: "string", description: "Message content" },
168
- },
169
- required: ["alias", "message"],
170
- },
171
- },
172
- {
173
- name: "commhub_get_all_status",
174
- description: "Get status of all sessions from CommHub.",
175
- inputSchema: {
176
- type: "object" as const,
177
- properties: {},
178
- },
179
- },
180
- ],
181
- }));
182
-
183
- // Helper: call CommHub MCP endpoint
184
- async function callCommHub(toolName: string, args: Record<string, unknown>): Promise<any> {
185
- const initRes = await fetch(`${COMMHUB_URL}/mcp`, {
186
- method: "POST",
187
- headers: {
188
- "Content-Type": "application/json",
189
- Accept: "application/json, text/event-stream",
190
- ...(AUTH_TOKEN ? { Authorization: `Bearer ${AUTH_TOKEN}` } : {}),
191
- },
192
- body: JSON.stringify({
193
- jsonrpc: "2.0",
194
- id: 1,
195
- method: "initialize",
196
- params: {
197
- protocolVersion: "2025-03-26",
198
- capabilities: {},
199
- clientInfo: { name: "commhub-channel", version: "0.3.0" },
200
- },
201
- }),
202
- });
203
- if (!initRes.ok) {
204
- const errText = await initRes.text();
205
- log(`CommHub init failed: ${initRes.status} ${errText.slice(0, 100)}`);
206
- return { ok: false, error: `init failed: ${initRes.status}` };
207
- }
208
- await initRes.text();
209
-
210
- const res = await fetch(`${COMMHUB_URL}/mcp`, {
211
- method: "POST",
212
- headers: {
213
- "Content-Type": "application/json",
214
- Accept: "application/json, text/event-stream",
215
- ...(AUTH_TOKEN ? { Authorization: `Bearer ${AUTH_TOKEN}` } : {}),
216
- },
217
- body: JSON.stringify({
218
- jsonrpc: "2.0",
219
- id: 2,
220
- method: "tools/call",
221
- params: { name: toolName, arguments: args },
222
- }),
223
- });
224
-
225
- const text = await res.text();
226
- const dataLine = text.split("\n").find((l) => l.startsWith("data: "));
227
- if (dataLine) {
228
- const json = JSON.parse(dataLine.slice(6));
229
- return json?.result?.content?.[0]?.text ? JSON.parse(json.result.content[0].text) : json;
230
- }
231
- return { ok: false, error: "no response" };
232
- }
233
-
234
- mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
235
- const { name, arguments: args } = req.params;
236
-
237
- if (name === "commhub_reply") {
238
- const { task_id, text, status } = args as any;
239
- if (status === "completed") {
240
- const result = await callCommHub("report_completion", {
241
- alias: ALIAS,
242
- task: task_id || "task",
243
- result: text,
244
- });
245
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
246
- }
247
- const result = await callCommHub("report_status", {
248
- resume_id: RESUME_ID,
249
- alias: ALIAS,
250
- status: status === "blocked" ? "blocked" : status === "error" ? "error" : "working",
251
- task: text.slice(0, 200),
252
- output: text,
253
- });
254
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
255
- }
256
-
257
- if (name === "commhub_report_status") {
258
- const { status, task, progress } = args as any;
259
- const result = await callCommHub("report_status", {
260
- resume_id: RESUME_ID,
261
- alias: ALIAS,
262
- status,
263
- task,
264
- progress,
265
- });
266
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
267
- }
268
-
269
- if (name === "commhub_send_task") {
270
- const { alias, task, priority } = args as any;
271
- const result = await callCommHub("send_task", {
272
- alias,
273
- task,
274
- priority: priority || "normal",
275
- from_session: ALIAS,
276
- });
277
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
278
- }
279
-
280
- if (name === "commhub_send_message") {
281
- const { alias, message } = args as any;
282
- const result = await callCommHub("send_message", {
283
- alias,
284
- message,
285
- from_session: ALIAS,
286
- });
287
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
288
- }
289
-
290
- if (name === "commhub_get_all_status") {
291
- const result = await callCommHub("get_all_status", {});
292
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
293
- }
294
-
295
- return { content: [{ type: "text", text: JSON.stringify({ error: "unknown tool" }) }] };
296
- });
297
-
298
- // ── SSE Listener: subscribe to /events/:alias ─────
299
- async function connectSSE() {
300
- const url = `${COMMHUB_URL}/events/${encodeURIComponent(ALIAS)}`;
301
- const headers: Record<string, string> = {};
302
- if (AUTH_TOKEN) headers.Authorization = `Bearer ${AUTH_TOKEN}`;
303
-
304
- log(`connecting to ${url}`);
305
-
306
- while (true) {
307
- try {
308
- const res = await fetch(url, { headers });
309
- if (!res.ok) {
310
- log(`SSE error: ${res.status} ${res.statusText}`);
311
- await sleep(5000);
312
- continue;
313
- }
314
-
315
- const reader = res.body!.getReader();
316
- const decoder = new TextDecoder();
317
- let buffer = "";
318
-
319
- while (true) {
320
- const { done, value } = await reader.read();
321
- if (done) break;
322
-
323
- buffer += decoder.decode(value, { stream: true });
324
- const lines = buffer.split("\n\n");
325
- buffer = lines.pop() || "";
326
-
327
- for (const block of lines) {
328
- const dataLine = block.split("\n").find((l) => l.startsWith("data: "));
329
- if (!dataLine) continue;
330
-
331
- try {
332
- const event = JSON.parse(dataLine.slice(6));
333
- await handleSSEEvent(event);
334
- } catch (e) {
335
- log(`parse error: ${e}`);
336
- }
337
- }
338
- }
339
-
340
- log("SSE stream ended, reconnecting...");
341
- } catch (err) {
342
- log(`SSE connection error: ${err}`);
343
- }
344
-
345
- await sleep(3000);
346
- }
347
- }
348
-
349
- async function handleSSEEvent(event: any) {
350
- if (event.type === "connected") {
351
- log(`SSE connected as "${ALIAS}"`);
352
- return;
353
- }
354
-
355
- if (event.type === "new_message") {
356
- log(`← message from ${event.from}: ${(event.message as string).slice(0, 60)}`);
357
-
358
- await mcp.notification({
359
- method: "notifications/claude/channel",
360
- params: {
361
- content: event.message,
362
- meta: {
363
- sender: event.from || "hub",
364
- sender_id: "commhub",
365
- user: event.from || "hub", // Claude Code 用 meta.user 显示 "commhub · {user}"
366
- priority: "normal",
367
- },
368
- },
369
- });
370
-
371
- // Auto-ack the message in inbox
372
- if (event.message_id) {
373
- await callCommHub("ack_inbox", { alias: ALIAS, message_id: event.message_id });
374
- }
375
- return;
376
- }
377
-
378
- if (event.type === "new_task" || event.type === "broadcast") {
379
- log(`← ${event.type}: inbox_count=${event.inbox_count} priority=${event.priority || "normal"}`);
380
-
381
- const inbox = await callCommHub("get_inbox", {
382
- alias: ALIAS,
383
- limit: 5,
384
- });
385
-
386
- if (inbox?.ok && inbox.messages?.length > 0) {
387
- for (const msg of inbox.messages) {
388
- const meta: Record<string, string> = {
389
- sender: msg.from_session || "hub",
390
- sender_id: "commhub",
391
- user: msg.from_session || "hub", // Claude Code 用 meta.user 显示 "commhub · {user}"
392
- task_id: msg.id,
393
- priority: msg.priority || "normal",
394
- };
395
-
396
- await mcp.notification({
397
- method: "notifications/claude/channel",
398
- params: {
399
- content: msg.content,
400
- meta,
401
- },
402
- });
403
-
404
- log(`→ injected task ${msg.id.slice(0, 8)} from ${msg.from_session}: ${(msg.content as string).slice(0, 60)}`);
405
-
406
- await callCommHub("ack_inbox", {
407
- alias: ALIAS,
408
- message_id: msg.id,
409
- });
410
- }
411
- }
412
- }
413
- }
414
-
415
- // ── Main ────────────────────────────────────────────
416
- async function main() {
417
- const transport = new StdioServerTransport();
418
- await mcp.connect(transport);
419
- log("MCP stdio connected");
420
-
421
- log("starting SSE listener...");
422
- connectSSE().catch((err) => log(`SSE fatal: ${err}`));
423
-
424
- callCommHub("report_status", {
425
- resume_id: RESUME_ID,
426
- alias: ALIAS,
427
- status: "idle",
428
- server: hostname(),
429
- hostname: hostname(),
430
- agent: "claude-code",
431
- project_dir: process.cwd(),
432
- tmux_name: TMUX_NAME || undefined,
433
- })
434
- .then(() => log(`registered as "${ALIAS}" (${RESUME_ID.slice(0, 8)})`))
435
- .catch((e) => log(`warning: could not register: ${e}`));
436
-
437
- // Heartbeat: report_status every 3 minutes to prevent offline timeout
438
- setInterval(() => {
439
- callCommHub("report_status", {
440
- resume_id: RESUME_ID,
441
- alias: ALIAS,
442
- status: "idle",
443
- server: hostname(),
444
- hostname: hostname(),
445
- agent: "claude-code",
446
- project_dir: process.cwd(),
447
- tmux_name: TMUX_NAME || undefined,
448
- }).catch((e) => log(`heartbeat failed: ${e}`));
449
- }, 3 * 60 * 1000);
450
-
451
- log("ready — waiting for events");
452
- }
453
-
454
- main().catch((err) => {
455
- log(`fatal: ${err}`);
456
- process.exit(1);
457
- });
458
-
459
- async function gracefulShutdown() {
460
- log("shutting down, reporting offline...");
461
- await callCommHub("report_status", {
462
- resume_id: RESUME_ID,
463
- alias: ALIAS,
464
- status: "error",
465
- task: "session disconnected",
466
- }).catch(() => {});
467
- process.exit(0);
468
- }
469
-
470
- process.stdin.on("end", () => gracefulShutdown());
471
- process.on("SIGTERM", () => gracefulShutdown());
472
- process.on("SIGINT", () => gracefulShutdown());
package/src/server.ts DELETED
@@ -1,28 +0,0 @@
1
- /**
2
- * CommHub Server — AI Agent communication hub
3
- *
4
- * Reexports the server startup. The actual server code lives in
5
- * the commhub-server package (server/ directory). This module
6
- * provides a programmatic entry point.
7
- */
8
-
9
- export interface ServerOptions {
10
- port?: number;
11
- token?: string;
12
- db?: string;
13
- corsOrigins?: string[];
14
- }
15
-
16
- /**
17
- * Start CommHub server programmatically.
18
- * Sets environment variables and imports the server module.
19
- */
20
- export async function startServer(opts: ServerOptions = {}) {
21
- if (opts.port) process.env.PORT = String(opts.port);
22
- if (opts.token) process.env.COMMHUB_AUTH_TOKEN = opts.token;
23
- if (opts.db) process.env.COMMHUB_DB = opts.db;
24
- if (opts.corsOrigins) process.env.COMMHUB_CORS_ORIGINS = opts.corsOrigins.join(",");
25
-
26
- // Dynamic import to avoid loading server code when only using client
27
- await import("../../server/src/index.js");
28
- }