@sleep2agi/agent-network 1.1.0 → 1.3.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.
- package/dist/bin/cli.js +1 -182
- package/dist/src/client.js +1 -2
- package/dist/src/node-server.js +2 -0
- package/package.json +8 -5
- package/src/node-server.ts +0 -472
- package/src/server.ts +0 -28
package/dist/src/client.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import{EventEmitter as
|
|
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_0x3e3619=a0_0xcd91;function a0_0xcd91(_0x18c051,_0x287de3){_0x18c051=_0x18c051-0x160;const _0x31029a=a0_0x3102();let _0xcd9108=_0x31029a[_0x18c051];return _0xcd9108;}(function(_0x599574,_0x3a8807){const _0x502874=a0_0xcd91,_0x2b6f43=_0x599574();while(!![]){try{const _0x5c21b3=-parseInt(_0x502874(0x174))/0x1*(parseInt(_0x502874(0x18f))/0x2)+-parseInt(_0x502874(0x19e))/0x3+-parseInt(_0x502874(0x19a))/0x4+parseInt(_0x502874(0x16f))/0x5*(parseInt(_0x502874(0x1b7))/0x6)+parseInt(_0x502874(0x166))/0x7+-parseInt(_0x502874(0x1b0))/0x8*(-parseInt(_0x502874(0x172))/0x9)+-parseInt(_0x502874(0x194))/0xa*(parseInt(_0x502874(0x18d))/0xb);if(_0x5c21b3===_0x3a8807)break;else _0x2b6f43['push'](_0x2b6f43['shift']());}catch(_0x3871c0){_0x2b6f43['push'](_0x2b6f43['shift']());}}}(a0_0x3102,0x56713));function a0_0x3102(){const _0x4e732c=['/events/','s...','data:\x20','Authorization','text','alias','catch','connectSSE','startsWith','1925671XhZlno','getAllStatus','2110pOtYfp','agent','emit','parse','heartbeatInterval','30UNAEur','resumeId','autoConnect','log','content','task','1380716qUTKrl','sleep','now','slice','1802439PTjoHw','send_message','message','type','body','status','sseAbort','connected','token','reply','ack_inbox','reconnectDelay','registered','match','application/json','replace','report_status','tools/call','8VTSLJI','running','disconnected','text/event-stream','signal','Bearer\x20','from_session','281418oJqaxT','error','send','min','SSE\x20failed:\x20','2.0','server','sdk','getReader','disconnect','4615387jBGrjh','POST','send_task','application/json,\x20text/event-stream','heartbeat\x20failed:\x20','url','get_inbox','heartbeatTimer','toTimeString','75uIkILt','inbox\x20error:\x20','connect','5481009wVvvNM','AbortError','139MCmIEe','/mcp','broadcast','new_message','pop','cwd',']\x20[commhub:','result','name','call','messages','idle','processInbox','SSE\x20connected','get_all_status','decode'];a0_0x3102=function(){return _0x4e732c;};return a0_0x3102();}import{EventEmitter as a0_0x15dfbf}from'events';import{hostname as a0_0x53b27f}from'os';class T extends a0_0x15dfbf{[a0_0x3e3619(0x16b)];[a0_0x3e3619(0x189)];['token'];['agent'];[a0_0x3e3619(0x195)];[a0_0x3e3619(0x193)];[a0_0x3e3619(0x1a9)];[a0_0x3e3619(0x16d)];[a0_0x3e3619(0x1a4)];[a0_0x3e3619(0x1b1)]=!0x1;constructor(_0x43f22a){const _0x5a5a1d=a0_0x3e3619;super();if(this['url']=_0x43f22a['url'][_0x5a5a1d(0x1ad)](/\/$/,''),this['alias']=_0x43f22a[_0x5a5a1d(0x189)],this[_0x5a5a1d(0x1a6)]=_0x43f22a[_0x5a5a1d(0x1a6)],this[_0x5a5a1d(0x190)]=_0x43f22a[_0x5a5a1d(0x190)]||_0x5a5a1d(0x163),this[_0x5a5a1d(0x195)]='sdk-'+_0x43f22a[_0x5a5a1d(0x189)]+'-'+Date['now']()['toString'](0x24),this[_0x5a5a1d(0x193)]=_0x43f22a['heartbeatInterval']??0x2bf20,this['reconnectDelay']=_0x43f22a[_0x5a5a1d(0x1a9)]??0xbb8,_0x43f22a[_0x5a5a1d(0x196)]!==!0x1)this[_0x5a5a1d(0x171)]();}[a0_0x3e3619(0x197)](_0x4107ee){const _0x2955a6=a0_0x3e3619;console[_0x2955a6(0x197)]('['+new Date()[_0x2955a6(0x16e)]()[_0x2955a6(0x19d)](0x0,0x8)+_0x2955a6(0x17a)+this[_0x2955a6(0x189)]+']\x20'+_0x4107ee);}async[a0_0x3e3619(0x17d)](_0x3ef70e,_0x3b59ac){const _0x452dc2=a0_0x3e3619;let _0xffece9={'Content-Type':_0x452dc2(0x1ac),'Accept':_0x452dc2(0x169)};if(this[_0x452dc2(0x1a6)])_0xffece9[_0x452dc2(0x187)]=_0x452dc2(0x1b5)+this[_0x452dc2(0x1a6)];let _0x14aa40=await(await fetch(this[_0x452dc2(0x16b)]+_0x452dc2(0x175),{'method':_0x452dc2(0x167),'headers':_0xffece9,'body':JSON['stringify']({'jsonrpc':_0x452dc2(0x161),'id':Date[_0x452dc2(0x19c)](),'method':_0x452dc2(0x1af),'params':{'name':_0x3ef70e,'arguments':_0x3b59ac}})}))[_0x452dc2(0x188)](),_0x1c2367=_0x14aa40[_0x452dc2(0x1ab)](/data: (.+)/),_0x295c90=_0x1c2367?JSON[_0x452dc2(0x192)](_0x1c2367[0x1]):JSON[_0x452dc2(0x192)](_0x14aa40),_0x1cbc39=_0x295c90?.[_0x452dc2(0x17b)]?.[_0x452dc2(0x198)]?.[0x0]?.['text'];return _0x1cbc39?JSON[_0x452dc2(0x192)](_0x1cbc39):_0x295c90;}async[a0_0x3e3619(0x171)](){const _0x59910e=a0_0x3e3619;if(this[_0x59910e(0x1b1)])return;this['running']=!0x0,await this[_0x59910e(0x1a3)](_0x59910e(0x17f)),this[_0x59910e(0x197)](_0x59910e(0x1aa)),this[_0x59910e(0x16d)]=setInterval(()=>{const _0x5d6b49=_0x59910e;this[_0x5d6b49(0x1a3)](_0x5d6b49(0x17f))['catch'](_0x22b466=>this[_0x5d6b49(0x197)](_0x5d6b49(0x16a)+_0x22b466['message']));},this['heartbeatInterval']),this[_0x59910e(0x18b)]();}async[a0_0x3e3619(0x165)](){const _0x2e0e49=a0_0x3e3619;if(this[_0x2e0e49(0x1b1)]=!0x1,this[_0x2e0e49(0x1a4)]?.['abort'](),this[_0x2e0e49(0x16d)])clearInterval(this[_0x2e0e49(0x16d)]);await this[_0x2e0e49(0x1a3)]('offline')[_0x2e0e49(0x18a)](()=>{}),this['log'](_0x2e0e49(0x1b2));}async[a0_0x3e3619(0x1b9)](_0x5c98c8,_0x35b1f6,_0xf182a0='normal'){const _0x525421=a0_0x3e3619;return this[_0x525421(0x17d)](_0x525421(0x168),{'alias':_0x5c98c8,'task':_0x35b1f6,'priority':_0xf182a0,'from_session':this[_0x525421(0x189)]});}async[a0_0x3e3619(0x1a0)](_0x4cd191,_0x5c49db){const _0xb7e75e=a0_0x3e3619;return this[_0xb7e75e(0x17d)](_0xb7e75e(0x19f),{'alias':_0x4cd191,'message':_0x5c49db,'from_session':this[_0xb7e75e(0x189)]});}async[a0_0x3e3619(0x1a7)](_0x52ab92,_0x59926e,_0x1c1f72='completed'){const _0x15f08d=a0_0x3e3619;return this[_0x15f08d(0x17d)](_0x15f08d(0x1a7),{'task_id':_0x52ab92,'text':_0x59926e,'status':_0x1c1f72});}async[a0_0x3e3619(0x1a3)](_0x5960fc,_0x2c92a5){const _0x4a6171=a0_0x3e3619;return this['call'](_0x4a6171(0x1ae),{'resume_id':this[_0x4a6171(0x195)],'alias':this[_0x4a6171(0x189)],'status':_0x5960fc,'server':a0_0x53b27f(),'hostname':a0_0x53b27f(),'agent':this[_0x4a6171(0x190)],'project_dir':process[_0x4a6171(0x179)](),..._0x2c92a5});}async[a0_0x3e3619(0x18e)](){const _0x4ee9cc=a0_0x3e3619;return this[_0x4ee9cc(0x17d)](_0x4ee9cc(0x182),{});}async[a0_0x3e3619(0x176)](_0x5f25b5,_0x58936d){const _0x82e731=a0_0x3e3619;return this[_0x82e731(0x17d)](_0x82e731(0x176),{'message':_0x5f25b5,'filter_server':_0x58936d?.[_0x82e731(0x162)],'filter_status':_0x58936d?.[_0x82e731(0x1a3)]});}async[a0_0x3e3619(0x18b)](){const _0x598d48=a0_0x3e3619;let _0x4878c4=encodeURIComponent(this[_0x598d48(0x189)]),_0xf8d3c5=this[_0x598d48(0x16b)]+_0x598d48(0x184)+_0x4878c4,_0x58ea6f=this[_0x598d48(0x1a9)];while(this[_0x598d48(0x1b1)]){try{this[_0x598d48(0x1a4)]=new AbortController();let _0x4cfec3={'Accept':_0x598d48(0x1b3)};if(this[_0x598d48(0x1a6)])_0x4cfec3[_0x598d48(0x187)]='Bearer\x20'+this[_0x598d48(0x1a6)];let _0x338e61=await fetch(_0xf8d3c5,{'headers':_0x4cfec3,'signal':this['sseAbort'][_0x598d48(0x1b4)]});if(!_0x338e61['ok']||!_0x338e61[_0x598d48(0x1a2)]){this[_0x598d48(0x197)](_0x598d48(0x160)+_0x338e61[_0x598d48(0x1a3)]),await this[_0x598d48(0x19b)](_0x58ea6f),_0x58ea6f=Math[_0x598d48(0x1ba)](_0x58ea6f*1.5,0xea60);continue;}_0x58ea6f=this[_0x598d48(0x1a9)];let _0x37a202=_0x338e61[_0x598d48(0x1a2)][_0x598d48(0x164)](),_0x34040d=new TextDecoder(),_0x1fe02f='';while(this['running']){let {done:_0x232c25,value:_0x13dce2}=await _0x37a202['read']();if(_0x232c25)break;_0x1fe02f+=_0x34040d[_0x598d48(0x183)](_0x13dce2,{'stream':!0x0});let _0x3acea2=_0x1fe02f['split']('\x0a');_0x1fe02f=_0x3acea2[_0x598d48(0x178)]()||'';for(let _0x2b2dcb of _0x3acea2){if(!_0x2b2dcb[_0x598d48(0x18c)](_0x598d48(0x186)))continue;try{let _0x1539ea=JSON[_0x598d48(0x192)](_0x2b2dcb[_0x598d48(0x19d)](0x6));if(_0x1539ea[_0x598d48(0x1a1)]==='connected'){this['log'](_0x598d48(0x181)),this[_0x598d48(0x191)](_0x598d48(0x1a5));continue;}if(_0x1539ea['type']==='new_task'||_0x1539ea[_0x598d48(0x1a1)]===_0x598d48(0x177)||_0x1539ea[_0x598d48(0x1a1)]===_0x598d48(0x176))await this[_0x598d48(0x180)]();}catch{}}}}catch(_0x1799df){if(_0x1799df[_0x598d48(0x17c)]===_0x598d48(0x173))break;this[_0x598d48(0x191)](_0x598d48(0x1b8),_0x1799df),this[_0x598d48(0x197)]('SSE\x20error:\x20'+_0x1799df['message']);}if(this['running'])this[_0x598d48(0x191)](_0x598d48(0x1b2)),this[_0x598d48(0x197)]('SSE\x20reconnecting\x20in\x20'+_0x58ea6f/0x3e8+_0x598d48(0x185)),await this[_0x598d48(0x19b)](_0x58ea6f),_0x58ea6f=Math[_0x598d48(0x1ba)](_0x58ea6f*1.5,0xea60);}}async[a0_0x3e3619(0x180)](){const _0x49e587=a0_0x3e3619;try{let _0x2b860e=(await this[_0x49e587(0x17d)](_0x49e587(0x16c),{'alias':this[_0x49e587(0x189)],'limit':0xa}))?.[_0x49e587(0x17e)]||[];for(let _0xa98257 of _0x2b860e)await this[_0x49e587(0x17d)](_0x49e587(0x1a8),{'alias':this[_0x49e587(0x189)],'message_id':_0xa98257['id']}),this['log']('←\x20'+_0xa98257[_0x49e587(0x1b6)]+':\x20'+_0xa98257[_0x49e587(0x198)][_0x49e587(0x19d)](0x0,0x3c)),this[_0x49e587(0x191)](_0x49e587(0x199),_0xa98257),this[_0x49e587(0x191)](_0x49e587(0x1a0),_0xa98257);}catch(_0x5534b9){this['log'](_0x49e587(0x170)+_0x5534b9['message']);}}[a0_0x3e3619(0x19b)](_0x1f7277){return new Promise(_0x44e534=>setTimeout(_0x44e534,_0x1f7277));}}var $=T;export{$ as default,T as CommHub};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
const a0_0x3e9770=a0_0x3144;(function(_0x16ad91,_0x2e6e64){const _0x583782=a0_0x3144,_0x1059b2=_0x16ad91();while(!![]){try{const _0x1364bd=-parseInt(_0x583782(0x1aa))/0x1*(-parseInt(_0x583782(0x155))/0x2)+parseInt(_0x583782(0x169))/0x3+parseInt(_0x583782(0x190))/0x4+parseInt(_0x583782(0x1a5))/0x5+-parseInt(_0x583782(0x16a))/0x6+parseInt(_0x583782(0x17f))/0x7+-parseInt(_0x583782(0x142))/0x8;if(_0x1364bd===_0x2e6e64)break;else _0x1059b2['push'](_0x1059b2['shift']());}catch(_0x48c2cd){_0x1059b2['push'](_0x1059b2['shift']());}}}(a0_0x4956,0x3ace8));import{readFileSync as a0_0x33c0b7,existsSync as a0_0x4b25ea}from'fs';import{join as a0_0x280624}from'path';import{hostname as a0_0x2b58f0}from'os';import{execSync as a0_0x1157b3}from'child_process';import{Server as a0_0xd3011b}from'@modelcontextprotocol/sdk/server/index.js';import{StdioServerTransport as a0_0x803ad4}from'@modelcontextprotocol/sdk/server/stdio.js';function a0_0x4956(){const _0x59acb2=['config.json','commhub_report_status','inbox_count','pop','Current\x20task\x20description','text','get_all_status','2WIsUZF','\x20PROJECT_ENV=','SSE\x20stream\x20ended,\x20reconnecting...','registered\x20as\x20\x22','working','send_message','stderr','join','shutting\x20down,\x20reporting\x20offline...','ready\x20—\x20waiting\x20for\x20events','initialize','new_message','find','from',':\x20inbox_count=','decode','statusText','Task\x20content','cwd','error','549036UCGJUa','828186FUyYDO','These\x20are\x20tasks\x20dispatched\x20by\x20the\x20hub\x20or\x20other\x20sessions\x20via\x20the\x20CommHub\x20Server.','string','ack_inbox','slice','message_id','application/json','...\x20TMUX=','connect','Update\x20this\x20session\x27s\x20status\x20in\x20CommHub\x20(working/idle/blocked/error).\x20Returns\x20inbox_count.','Session\x20alias:\x20','messages','\x20RESUME_ID=','notifications/claude/channel','from_session','body','\x22\x20(','parse\x20error:\x20','content','result','commhub_reply','1873585QvhJny','idle','application/json,\x20text/event-stream','task','broadcast','claude-code','low','2.0','startsWith','normal','data:\x20','warning:\x20could\x20not\x20register:\x20','end','message','type','Get\x20status\x20of\x20all\x20sessions\x20from\x20CommHub.','in_progress','1923080OjGlbA','tmux\x20display-message\x20-p\x20\x27#S\x27','randomUUID','then','stdin','exit','hub','priority',']\x20[commhub]\x20','replace','Bearer\x20','blocked','commhub-channel','report_status','write','session\x20disconnected','commhub_send_message','toTimeString','none','fatal:\x20','unknown\x20tool','1025360QJDbuF','object','parse','\x20from\x20','Reply\x20using\x20the\x20commhub_reply\x20tool\x20to\x20report\x20status\x20or\x20results\x20back.','373798rsVPCg','/mcp','commhub_send_task','read','.env','high','Priority\x20(default:\x20normal)','indexOf','completed','notification','connected','Message\x20content','no\x20response','.claude/channels/commhub','send_task','→\x20injected\x20task\x20','getReader','params','number','http://127.0.0.1:9200','get_inbox','SSE\x20error:\x20','catch','status','alias','stringify','heartbeat\x20failed:\x20','Send\x20a\x20message\x20to\x20another\x20session\x20(no\x20task\x20lifecycle,\x20just\x20chat).\x20Use\x20for\x20replies\x20and\x20status\x20updates.','CommHub\x20init\x20failed:\x20','commhub_get_all_status','You\x20can\x20also\x20use\x20commhub_report_status\x20to\x20update\x20your\x20session\x20status.','SIGTERM','9051232fJvMka','utf-8','Target\x20session\x20alias','trim','commhub','report_completion','SSE\x20connected\x20as\x20\x22','setRequestHandler','connecting\x20to\x20','\x20ALIAS=','0.3.0','split'];a0_0x4956=function(){return _0x59acb2;};return a0_0x4956();}function a0_0x3144(_0x1f376e,_0x5a7de3){_0x1f376e=_0x1f376e-0x13e;const _0x495696=a0_0x4956();let _0x314480=_0x495696[_0x1f376e];return _0x314480;}import{ListToolsRequestSchema as a0_0x93d1e0,CallToolRequestSchema as a0_0x29ae72}from'@modelcontextprotocol/sdk/types.js';function h(_0x4e3062){const _0x12ab83=a0_0x3144;if(!a0_0x4b25ea(_0x4e3062))return;for(let _0x4c516c of a0_0x33c0b7(_0x4e3062,'utf-8')[_0x12ab83(0x14d)]('\x0a')){let _0xe1bbfc=_0x4c516c[_0x12ab83(0x145)]();if(!_0xe1bbfc||_0xe1bbfc[_0x12ab83(0x187)]('#'))continue;let _0x4db1be=_0xe1bbfc[_0x12ab83(0x1b1)]('=');if(_0x4db1be<0x0)continue;let _0x347e0a=_0xe1bbfc[_0x12ab83(0x16e)](0x0,_0x4db1be)[_0x12ab83(0x145)](),_0x154d0e=_0xe1bbfc[_0x12ab83(0x16e)](_0x4db1be+0x1)[_0x12ab83(0x145)]()['replace'](/^["']|["']$/g,'');if(!process.env[_0x347e0a])process.env[_0x347e0a]=_0x154d0e;}}var R=process.env.HOME||'~',p=a0_0x280624(R,a0_0x3e9770(0x1b7));h(a0_0x280624(p,a0_0x3e9770(0x1ae)));var C=process[a0_0x3e9770(0x167)]()[a0_0x3e9770(0x199)](/\//g,'-');h(a0_0x280624(p,C,a0_0x3e9770(0x1ae)));function _(){const _0xb970d=a0_0x3e9770;try{return a0_0x1157b3(_0xb970d(0x191),{'encoding':_0xb970d(0x143),'timeout':0x7d0})[_0xb970d(0x145)]();}catch{return'';}}function E(){const _0x5c2ffb=a0_0x3e9770;try{let _0x1a1e65=a0_0x280624(R,'.anet',_0x5c2ffb(0x14e));if(a0_0x4b25ea(_0x1a1e65))return JSON[_0x5c2ffb(0x1a7)](a0_0x33c0b7(_0x1a1e65,_0x5c2ffb(0x143)));}catch{}return{};}var T=E(),k=process.env.COMMHUB_URL||T['hub']||a0_0x3e9770(0x1bd),q=process.env.COMMHUB_TMUX||_(),Z=process.env.COMMHUB_ALIAS||q||a0_0x2b58f0(),w=process.env.COMMHUB_RESUME_ID||process.env.CLAUDE_RESUME_ID||crypto[a0_0x3e9770(0x192)](),X=process.env.COMMHUB_TOKEN||T['token']||'';function V(_0x393606){const _0xac0258=a0_0x3e9770;let _0x840234=new Date()[_0xac0258(0x1a1)]()[_0xac0258(0x16e)](0x0,0x8);process[_0xac0258(0x15b)][_0xac0258(0x19e)]('['+_0x840234+_0xac0258(0x198)+_0x393606+'\x0a');}function O(_0x50c802){return new Promise(_0x1e97fb=>setTimeout(_0x1e97fb,_0x50c802));}V('ENV:\x20URL='+k+a0_0x3e9770(0x14b)+Z+a0_0x3e9770(0x176)+w[a0_0x3e9770(0x16e)](0x0,0x8)+a0_0x3e9770(0x171)+(q||a0_0x3e9770(0x1a2))+'\x20CWD='+process[a0_0x3e9770(0x167)]()+a0_0x3e9770(0x156)+C);var K=new a0_0xd3011b({'name':a0_0x3e9770(0x19c),'version':a0_0x3e9770(0x14c)},{'capabilities':{'experimental':{'claude/channel':{}},'tools':{}},'instructions':['Messages\x20from\x20CommHub\x20arrive\x20as\x20<channel\x20source=\x22commhub\x22\x20task_id=\x22...\x22\x20priority=\x22...\x22\x20from=\x22...\x22>',a0_0x3e9770(0x16b),a0_0x3e9770(0x1a9),a0_0x3e9770(0x140),a0_0x3e9770(0x174)+Z][a0_0x3e9770(0x15c)]('\x0a')});K[a0_0x3e9770(0x149)](a0_0x93d1e0,async()=>({'tools':[{'name':a0_0x3e9770(0x17e),'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_0x3e9770(0x16c),'description':'The\x20task_id\x20from\x20the\x20channel\x20message\x20(or\x20\x27hub\x27\x20for\x20general)'},'text':{'type':a0_0x3e9770(0x16c),'description':'Reply\x20text\x20/\x20result\x20summary'},'status':{'type':'string','enum':['completed',a0_0x3e9770(0x19b),a0_0x3e9770(0x168),a0_0x3e9770(0x18f)],'description':'Task\x20status'}},'required':[a0_0x3e9770(0x153)]}},{'name':a0_0x3e9770(0x14f),'description':a0_0x3e9770(0x173),'inputSchema':{'type':a0_0x3e9770(0x1a6),'properties':{'status':{'type':a0_0x3e9770(0x16c),'enum':[a0_0x3e9770(0x159),a0_0x3e9770(0x180),'blocked',a0_0x3e9770(0x168)]},'task':{'type':a0_0x3e9770(0x16c),'description':a0_0x3e9770(0x152)},'progress':{'type':a0_0x3e9770(0x1bc),'description':'Progress\x200-100'}},'required':[a0_0x3e9770(0x1c1)]}},{'name':a0_0x3e9770(0x1ac),'description':'Send\x20a\x20task\x20to\x20another\x20session\x20via\x20CommHub.','inputSchema':{'type':'object','properties':{'alias':{'type':'string','description':a0_0x3e9770(0x144)},'task':{'type':a0_0x3e9770(0x16c),'description':a0_0x3e9770(0x166)},'priority':{'type':a0_0x3e9770(0x16c),'enum':[a0_0x3e9770(0x1af),a0_0x3e9770(0x188),a0_0x3e9770(0x185)],'description':a0_0x3e9770(0x1b0)}},'required':[a0_0x3e9770(0x1c2),a0_0x3e9770(0x182)]}},{'name':a0_0x3e9770(0x1a0),'description':a0_0x3e9770(0x1c5),'inputSchema':{'type':a0_0x3e9770(0x1a6),'properties':{'alias':{'type':'string','description':a0_0x3e9770(0x144)},'message':{'type':a0_0x3e9770(0x16c),'description':a0_0x3e9770(0x1b5)}},'required':['alias',a0_0x3e9770(0x18c)]}},{'name':a0_0x3e9770(0x13f),'description':a0_0x3e9770(0x18e),'inputSchema':{'type':'object','properties':{}}}]}));async function P(_0x5d804c,_0x4990bc){const _0x2db4c9=a0_0x3e9770;let _0x3a1c2e=await fetch(k+_0x2db4c9(0x1ab),{'method':'POST','headers':{'Content-Type':_0x2db4c9(0x170),'Accept':_0x2db4c9(0x181),...X?{'Authorization':_0x2db4c9(0x19a)+X}:{}},'body':JSON[_0x2db4c9(0x1c3)]({'jsonrpc':_0x2db4c9(0x186),'id':0x1,'method':_0x2db4c9(0x15f),'params':{'protocolVersion':'2025-03-26','capabilities':{},'clientInfo':{'name':_0x2db4c9(0x19c),'version':_0x2db4c9(0x14c)}}})});if(!_0x3a1c2e['ok']){let _0x1df69e=await _0x3a1c2e[_0x2db4c9(0x153)]();return V(_0x2db4c9(0x13e)+_0x3a1c2e[_0x2db4c9(0x1c1)]+'\x20'+_0x1df69e['slice'](0x0,0x64)),{'ok':!0x1,'error':'init\x20failed:\x20'+_0x3a1c2e['status']};}await _0x3a1c2e['text']();let _0x4045a4=(await(await fetch(k+_0x2db4c9(0x1ab),{'method':'POST','headers':{'Content-Type':_0x2db4c9(0x170),'Accept':'application/json,\x20text/event-stream',...X?{'Authorization':_0x2db4c9(0x19a)+X}:{}},'body':JSON[_0x2db4c9(0x1c3)]({'jsonrpc':_0x2db4c9(0x186),'id':0x2,'method':'tools/call','params':{'name':_0x5d804c,'arguments':_0x4990bc}})}))[_0x2db4c9(0x153)]())[_0x2db4c9(0x14d)]('\x0a')['find'](_0x4caeb3=>_0x4caeb3[_0x2db4c9(0x187)](_0x2db4c9(0x189)));if(_0x4045a4){let _0x1565c5=JSON[_0x2db4c9(0x1a7)](_0x4045a4[_0x2db4c9(0x16e)](0x6));return _0x1565c5?.[_0x2db4c9(0x17d)]?.['content']?.[0x0]?.[_0x2db4c9(0x153)]?JSON[_0x2db4c9(0x1a7)](_0x1565c5[_0x2db4c9(0x17d)][_0x2db4c9(0x17c)][0x0][_0x2db4c9(0x153)]):_0x1565c5;}return{'ok':!0x1,'error':_0x2db4c9(0x1b6)};}K['setRequestHandler'](a0_0x29ae72,async _0x1136f8=>{const _0x2a5205=a0_0x3e9770;let {name:_0x48f35a,arguments:_0x412849}=_0x1136f8[_0x2a5205(0x1bb)];if(_0x48f35a===_0x2a5205(0x17e)){let {task_id:_0x399e38,text:_0x20fdfa,status:_0x2bfed2}=_0x412849;if(_0x2bfed2===_0x2a5205(0x1b2)){let _0x3fe566=await P(_0x2a5205(0x147),{'alias':Z,'task':_0x399e38||_0x2a5205(0x182),'result':_0x20fdfa});return{'content':[{'type':_0x2a5205(0x153),'text':JSON['stringify'](_0x3fe566)}]};}let _0x2dc476=await P(_0x2a5205(0x19d),{'resume_id':w,'alias':Z,'status':_0x2bfed2===_0x2a5205(0x19b)?'blocked':_0x2bfed2===_0x2a5205(0x168)?_0x2a5205(0x168):_0x2a5205(0x159),'task':_0x20fdfa[_0x2a5205(0x16e)](0x0,0xc8),'output':_0x20fdfa});return{'content':[{'type':'text','text':JSON[_0x2a5205(0x1c3)](_0x2dc476)}]};}if(_0x48f35a==='commhub_report_status'){let {status:_0x507e40,task:_0x6eff92,progress:_0x45d810}=_0x412849,_0x4a3c24=await P('report_status',{'resume_id':w,'alias':Z,'status':_0x507e40,'task':_0x6eff92,'progress':_0x45d810});return{'content':[{'type':_0x2a5205(0x153),'text':JSON[_0x2a5205(0x1c3)](_0x4a3c24)}]};}if(_0x48f35a===_0x2a5205(0x1ac)){let {alias:_0x3ca1ab,task:_0x46cdfa,priority:_0x3bc7e6}=_0x412849,_0x2e2a05=await P(_0x2a5205(0x1b8),{'alias':_0x3ca1ab,'task':_0x46cdfa,'priority':_0x3bc7e6||_0x2a5205(0x188),'from_session':Z});return{'content':[{'type':_0x2a5205(0x153),'text':JSON[_0x2a5205(0x1c3)](_0x2e2a05)}]};}if(_0x48f35a===_0x2a5205(0x1a0)){let {alias:_0x2d307d,message:_0x1e4bf7}=_0x412849,_0x310613=await P(_0x2a5205(0x15a),{'alias':_0x2d307d,'message':_0x1e4bf7,'from_session':Z});return{'content':[{'type':_0x2a5205(0x153),'text':JSON[_0x2a5205(0x1c3)](_0x310613)}]};}if(_0x48f35a===_0x2a5205(0x13f)){let _0x4eacc9=await P(_0x2a5205(0x154),{});return{'content':[{'type':_0x2a5205(0x153),'text':JSON[_0x2a5205(0x1c3)](_0x4eacc9)}]};}return{'content':[{'type':_0x2a5205(0x153),'text':JSON['stringify']({'error':_0x2a5205(0x1a4)})}]};});async function c(){const _0x321d23=a0_0x3e9770;let _0x42e5eb=k+'/events/'+encodeURIComponent(Z),_0x5f0b36={};if(X)_0x5f0b36['Authorization']=_0x321d23(0x19a)+X;V(_0x321d23(0x14a)+_0x42e5eb);while(!0x0){try{let _0x49dcbf=await fetch(_0x42e5eb,{'headers':_0x5f0b36});if(!_0x49dcbf['ok']){V(_0x321d23(0x1bf)+_0x49dcbf[_0x321d23(0x1c1)]+'\x20'+_0x49dcbf[_0x321d23(0x165)]),await O(0x1388);continue;}let _0x2eccb1=_0x49dcbf[_0x321d23(0x179)][_0x321d23(0x1ba)](),_0x32f888=new TextDecoder(),_0x5664e6='';while(!0x0){let {done:_0x46d671,value:_0x59f4c5}=await _0x2eccb1[_0x321d23(0x1ad)]();if(_0x46d671)break;_0x5664e6+=_0x32f888[_0x321d23(0x164)](_0x59f4c5,{'stream':!0x0});let _0xb03861=_0x5664e6[_0x321d23(0x14d)]('\x0a\x0a');_0x5664e6=_0xb03861[_0x321d23(0x151)]()||'';for(let _0x6e16d2 of _0xb03861){let _0x4b8423=_0x6e16d2['split']('\x0a')[_0x321d23(0x161)](_0x3cabb1=>_0x3cabb1[_0x321d23(0x187)](_0x321d23(0x189)));if(!_0x4b8423)continue;try{let _0x377601=JSON[_0x321d23(0x1a7)](_0x4b8423[_0x321d23(0x16e)](0x6));await v(_0x377601);}catch(_0x20861b){V(_0x321d23(0x17b)+_0x20861b);}}}V(_0x321d23(0x157));}catch(_0x34d1b0){V('SSE\x20connection\x20error:\x20'+_0x34d1b0);}await O(0xbb8);}}async function v(_0x415d7a){const _0x1ae7a5=a0_0x3e9770;if(_0x415d7a[_0x1ae7a5(0x18d)]===_0x1ae7a5(0x1b4)){V(_0x1ae7a5(0x148)+Z+'\x22');return;}if(_0x415d7a[_0x1ae7a5(0x18d)]===_0x1ae7a5(0x160)){if(V('←\x20message\x20from\x20'+_0x415d7a[_0x1ae7a5(0x162)]+':\x20'+_0x415d7a[_0x1ae7a5(0x18c)][_0x1ae7a5(0x16e)](0x0,0x3c)),await K[_0x1ae7a5(0x1b3)]({'method':_0x1ae7a5(0x177),'params':{'content':_0x415d7a[_0x1ae7a5(0x18c)],'meta':{'sender':_0x415d7a['from']||_0x1ae7a5(0x196),'sender_id':_0x1ae7a5(0x146),'user':_0x415d7a[_0x1ae7a5(0x162)]||'hub','priority':_0x1ae7a5(0x188)}}}),_0x415d7a['message_id'])await P(_0x1ae7a5(0x16d),{'alias':Z,'message_id':_0x415d7a[_0x1ae7a5(0x16f)]});return;}if(_0x415d7a['type']==='new_task'||_0x415d7a[_0x1ae7a5(0x18d)]===_0x1ae7a5(0x183)){V('←\x20'+_0x415d7a[_0x1ae7a5(0x18d)]+_0x1ae7a5(0x163)+_0x415d7a[_0x1ae7a5(0x150)]+'\x20priority='+(_0x415d7a[_0x1ae7a5(0x197)]||_0x1ae7a5(0x188)));let _0x41a9e9=await P(_0x1ae7a5(0x1be),{'alias':Z,'limit':0x5});if(_0x41a9e9?.['ok']&&_0x41a9e9[_0x1ae7a5(0x175)]?.['length']>0x0)for(let _0x47cd22 of _0x41a9e9[_0x1ae7a5(0x175)]){let _0x4e21bb={'sender':_0x47cd22[_0x1ae7a5(0x178)]||_0x1ae7a5(0x196),'sender_id':_0x1ae7a5(0x146),'user':_0x47cd22[_0x1ae7a5(0x178)]||_0x1ae7a5(0x196),'task_id':_0x47cd22['id'],'priority':_0x47cd22[_0x1ae7a5(0x197)]||_0x1ae7a5(0x188)};await K['notification']({'method':_0x1ae7a5(0x177),'params':{'content':_0x47cd22[_0x1ae7a5(0x17c)],'meta':_0x4e21bb}}),V(_0x1ae7a5(0x1b9)+_0x47cd22['id'][_0x1ae7a5(0x16e)](0x0,0x8)+_0x1ae7a5(0x1a8)+_0x47cd22[_0x1ae7a5(0x178)]+':\x20'+_0x47cd22[_0x1ae7a5(0x17c)]['slice'](0x0,0x3c)),await P(_0x1ae7a5(0x16d),{'alias':Z,'message_id':_0x47cd22['id']});}}}async function u(){const _0x4d63d7=a0_0x3e9770;let _0x423a88=new a0_0x803ad4();await K[_0x4d63d7(0x172)](_0x423a88),V('MCP\x20stdio\x20connected'),V('starting\x20SSE\x20listener...'),c()[_0x4d63d7(0x1c0)](_0x1a024a=>V('SSE\x20fatal:\x20'+_0x1a024a)),P(_0x4d63d7(0x19d),{'resume_id':w,'alias':Z,'status':'idle','server':a0_0x2b58f0(),'hostname':a0_0x2b58f0(),'agent':_0x4d63d7(0x184),'project_dir':process[_0x4d63d7(0x167)](),'tmux_name':q||void 0x0})[_0x4d63d7(0x193)](()=>V(_0x4d63d7(0x158)+Z+_0x4d63d7(0x17a)+w[_0x4d63d7(0x16e)](0x0,0x8)+')'))[_0x4d63d7(0x1c0)](_0x1ee56f=>V(_0x4d63d7(0x18a)+_0x1ee56f)),setInterval(()=>{const _0x474d7c=_0x4d63d7;P('report_status',{'resume_id':w,'alias':Z,'status':'idle','server':a0_0x2b58f0(),'hostname':a0_0x2b58f0(),'agent':'claude-code','project_dir':process[_0x474d7c(0x167)](),'tmux_name':q||void 0x0})['catch'](_0x281c5f=>V(_0x474d7c(0x1c4)+_0x281c5f));},0x2bf20),V(_0x4d63d7(0x15e));}u()[a0_0x3e9770(0x1c0)](_0x356965=>{const _0x142308=a0_0x3e9770;V(_0x142308(0x1a3)+_0x356965),process[_0x142308(0x195)](0x1);});async function f(){const _0x42065c=a0_0x3e9770;V(_0x42065c(0x15d)),await P(_0x42065c(0x19d),{'resume_id':w,'alias':Z,'status':'error','task':_0x42065c(0x19f)})[_0x42065c(0x1c0)](()=>{}),process['exit'](0x0);}process[a0_0x3e9770(0x194)]['on'](a0_0x3e9770(0x18b),()=>f()),process['on'](a0_0x3e9770(0x141),()=>f()),process['on']('SIGINT',()=>f());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/agent-network",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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,13 @@
|
|
|
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",
|
|
57
|
+
"node-pty": "^1.1.0",
|
|
55
58
|
"typescript": "^5.0.0"
|
|
56
59
|
}
|
|
57
60
|
}
|
package/src/node-server.ts
DELETED
|
@@ -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
|
-
}
|