@sleep2agi/agent-network 1.0.2 → 1.0.3

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.
Files changed (2) hide show
  1. package/dist/bin/cli.js +37 -33
  2. package/package.json +1 -1
package/dist/bin/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- var LB=Object.defineProperty;var JB=(B)=>B;function MB(B,Q){this[B]=JB.bind(null,Q)}var GB=(B,Q)=>{for(var W in Q)LB(B,W,{get:Q[W],enumerable:!0,configurable:!0,set:MB.bind(Q,W)})};var zB=(B,Q)=>()=>(B&&(Q=B(B=0)),Q);var s={};GB(s,{default:()=>AB,CommHub:()=>y});import{EventEmitter as TB}from"events";import{hostname as n}from"os";var y,AB;var o=zB(()=>{y=class y extends TB{url;alias;token;agent;resumeId;heartbeatInterval;reconnectDelay;heartbeatTimer;sseAbort;running=!1;constructor(B){super();if(this.url=B.url.replace(/\/$/,""),this.alias=B.alias,this.token=B.token,this.agent=B.agent||"sdk",this.resumeId=`sdk-${B.alias}-${Date.now().toString(36)}`,this.heartbeatInterval=B.heartbeatInterval??180000,this.reconnectDelay=B.reconnectDelay??3000,B.autoConnect!==!1)this.connect()}log(B){console.log(`[${new Date().toTimeString().slice(0,8)}] [commhub:${this.alias}] ${B}`)}async call(B,Q){let W={"Content-Type":"application/json",Accept:"application/json, text/event-stream"};if(this.token)W.Authorization=`Bearer ${this.token}`;let Z=await(await fetch(`${this.url}/mcp`,{method:"POST",headers:W,body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:"tools/call",params:{name:B,arguments:Q}})})).text(),Y=Z.match(/data: (.+)/),U=Y?JSON.parse(Y[1]):JSON.parse(Z),K=U?.result?.content?.[0]?.text;return K?JSON.parse(K):U}async connect(){if(this.running)return;this.running=!0,await this.status("idle"),this.log("registered"),this.heartbeatTimer=setInterval(()=>{this.status("idle").catch((B)=>this.log(`heartbeat failed: ${B.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(B,Q,W="normal"){return this.call("send_task",{alias:B,task:Q,priority:W,from_session:this.alias})}async message(B,Q){return this.call("send_message",{alias:B,message:Q,from_session:this.alias})}async reply(B,Q,W="completed"){return this.call("reply",{task_id:B,text:Q,status:W})}async status(B,Q){return this.call("report_status",{resume_id:this.resumeId,alias:this.alias,status:B,server:n(),hostname:n(),agent:this.agent,project_dir:process.cwd(),...Q})}async getAllStatus(){return this.call("get_all_status",{})}async broadcast(B,Q){return this.call("broadcast",{message:B,filter_server:Q?.server,filter_status:Q?.status})}async connectSSE(){let B=encodeURIComponent(this.alias),Q=`${this.url}/events/${B}`,W=this.reconnectDelay;while(this.running){try{this.sseAbort=new AbortController;let X={Accept:"text/event-stream"};if(this.token)X.Authorization=`Bearer ${this.token}`;let Z=await fetch(Q,{headers:X,signal:this.sseAbort.signal});if(!Z.ok||!Z.body){this.log(`SSE failed: ${Z.status}`),await this.sleep(W),W=Math.min(W*1.5,60000);continue}W=this.reconnectDelay;let Y=Z.body.getReader(),U=new TextDecoder,K="";while(this.running){let{done:$,value:_}=await Y.read();if($)break;K+=U.decode(_,{stream:!0});let q=K.split(`
3
- `);K=q.pop()||"";for(let J of q){if(!J.startsWith("data: "))continue;try{let H=JSON.parse(J.slice(6));if(H.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(H.type==="new_task"||H.type==="new_message"||H.type==="broadcast")await this.processInbox()}catch{}}}}catch(X){if(X.name==="AbortError")break;this.emit("error",X),this.log(`SSE error: ${X.message}`)}if(this.running)this.emit("disconnected"),this.log(`SSE reconnecting in ${W/1000}s...`),await this.sleep(W),W=Math.min(W*1.5,60000)}}async processInbox(){try{let Q=(await this.call("get_inbox",{alias:this.alias,limit:10}))?.messages||[];for(let W of Q)await this.call("ack_inbox",{alias:this.alias,message_id:W.id}),this.log(`← ${W.from_session}: ${W.content.slice(0,60)}`),this.emit("task",W),this.emit("message",W)}catch(B){this.log(`inbox error: ${B.message}`)}}sleep(B){return new Promise((Q)=>setTimeout(Q,B))}};AB=y});import{chmodSync as NB,readFileSync as z,writeFileSync as M,existsSync as L,mkdirSync as N,readdirSync as j,statSync as C}from"fs";import{join as V}from"path";import{spawn as h,execSync as p}from"child_process";import{createInterface as wB}from"readline";var E=process.argv.slice(2),S=E[0],F=process.env.HOME||process.env.USERPROFILE||"~";function XB(){return V(F,".anet","config.json")}function g(){return V(F,".anet","server","config.json")}function I(){return V(process.cwd(),".anet","nodes")}function YB(){return A().token||process.env.COMMHUB_TOKEN||x().token||""}function f(B){let Q=B||YB();return Q?{Authorization:`Bearer ${Q}`}:{}}function x(){let B=XB();if(L(B))try{return JSON.parse(z(B,"utf-8"))}catch{}return{}}function ZB(B){let Q=V(F,".anet");N(Q,{recursive:!0}),M(V(Q,"config.json"),JSON.stringify(B,null,2)+`
4
- `)}function i(){let B=g();if(L(B))try{return JSON.parse(z(B,"utf-8"))}catch{}return{}}function e(B){let Q=V(F,".anet","server");N(Q,{recursive:!0}),M(V(Q,"config.json"),JSON.stringify(B,null,2)+`
5
- `)}function T(B){if(typeof B==="string"){if(B==="codex"||B==="codex-sdk")return"codex-sdk";if(B==="claude"||B==="claude-sdk"||B==="claude-agent-sdk")return"claude-agent-sdk";if(B==="agent-sdk")return"claude-agent-sdk";return"claude-code-cli"}let Q=B;if(!Q)return"claude-code-cli";if(Q.runtime==="agent-sdk")return Q.codexRuntime==="codex"?"codex-sdk":"claude-agent-sdk";return T(Q.runtime||"claude-code-cli")}function $B(B,Q){return Q?.name||Q?.alias||B}function w(B){return B.session||B.resume||""}function FB(B){return B.normalize("NFC")}function k(B){if(B!==FB(B))console.error(`Error: node-name must be Unicode NFC normalized: ${B}`),process.exit(1);if(!/^[^\s\/\\:*?"<>|.][^\s\/\\:*?"<>|.]*$/.test(B))console.error(`Error: invalid node-name "${B}"`),console.error(`Allowed: Chinese/letters/numbers/-/_ ; forbidden: whitespace, '.', / \\ : * ? " < > |`),process.exit(1)}function R(B){let Q=V(I(),B,"config.json");if(!L(Q))return null;try{let W=JSON.parse(z(Q,"utf-8")),X=x();return{...W,name:W.name||W.alias||B,alias:W.alias||W.name||B,session:W.session||W.resume||"",hub:W.hub||X.hub||"",token:W.token||X.token||"",channels:Array.isArray(W.channels)?W.channels:[],env:{...W.env},flags:{...W.flags}}}catch{return null}}function UB(B){let Q=V(I(),B,"config.json");if(!L(Q))return null;try{let W=JSON.parse(z(Q,"utf-8"));return{...W,channels:Array.isArray(W.channels)?W.channels:[],env:{...W.env},flags:{...W.flags}}}catch{return null}}function v(B,Q){let W=V(I(),B);N(W,{recursive:!0}),M(V(W,"config.json"),JSON.stringify(Q,null,2)+`
6
- `)}function c(){let B=I();if(!L(B))return[];return j(B).filter((Q)=>L(V(B,Q,"config.json")))}function A(){let B={_channels:[],_envs:[]};for(let Q=0;Q<E.length;Q++){if(E[Q]==="--channel"&&E[Q+1]){B._channels.push(E[++Q]);continue}if(E[Q]==="--env"&&E[Q+1]){B._envs.push(E[++Q]);continue}if(E[Q].startsWith("--")&&E[Q+1]&&!E[Q+1].startsWith("--"))B[E[Q].slice(2)]=E[++Q];else if(E[Q].startsWith("--"))B[E[Q].slice(2)]="true"}return B}function BB(B){try{return p(`command -v ${JSON.stringify(B)}`,{stdio:"ignore",shell:"/bin/bash"}),!0}catch{return!1}}function d(){console.log("[anet] claude-code-cli requires:"),console.log(" - Claude Pro / Team / Enterprise subscription"),console.log(' - Run "claude auth login" first'),console.log(" - Uses Anthropic Claude only"),console.log(" - For other models, use --runtime codex-sdk or claude-agent-sdk")}function l(B,Q){if(B==="claude-code-cli"){if(!BB("claude"))console.warn("[anet] Warning: claude CLI not found in PATH."),console.warn("[anet] Install: npm install -g @anthropic-ai/claude-code");if(Q==="start")d();return}if(!BB("npx"))console.warn("[anet] Warning: npx not found in PATH; cannot launch @sleep2agi/agent-node."),console.warn("[anet] Install Node.js/npm first, or install agent-node globally: npm install -g @sleep2agi/agent-node")}function QB(){console.log(`
2
+ var LB=Object.defineProperty;var JB=(B)=>B;function MB(B,Q){this[B]=JB.bind(null,Q)}var GB=(B,Q)=>{for(var W in Q)LB(B,W,{get:Q[W],enumerable:!0,configurable:!0,set:MB.bind(Q,W)})};var zB=(B,Q)=>()=>(B&&(Q=B(B=0)),Q);var s={};GB(s,{default:()=>AB,CommHub:()=>S});import{EventEmitter as TB}from"events";import{hostname as n}from"os";var S,AB;var o=zB(()=>{S=class S extends TB{url;alias;token;agent;resumeId;heartbeatInterval;reconnectDelay;heartbeatTimer;sseAbort;running=!1;constructor(B){super();if(this.url=B.url.replace(/\/$/,""),this.alias=B.alias,this.token=B.token,this.agent=B.agent||"sdk",this.resumeId=`sdk-${B.alias}-${Date.now().toString(36)}`,this.heartbeatInterval=B.heartbeatInterval??180000,this.reconnectDelay=B.reconnectDelay??3000,B.autoConnect!==!1)this.connect()}log(B){console.log(`[${new Date().toTimeString().slice(0,8)}] [commhub:${this.alias}] ${B}`)}async call(B,Q){let W={"Content-Type":"application/json",Accept:"application/json, text/event-stream"};if(this.token)W.Authorization=`Bearer ${this.token}`;let Z=await(await fetch(`${this.url}/mcp`,{method:"POST",headers:W,body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:"tools/call",params:{name:B,arguments:Q}})})).text(),Y=Z.match(/data: (.+)/),U=Y?JSON.parse(Y[1]):JSON.parse(Z),K=U?.result?.content?.[0]?.text;return K?JSON.parse(K):U}async connect(){if(this.running)return;this.running=!0,await this.status("idle"),this.log("registered"),this.heartbeatTimer=setInterval(()=>{this.status("idle").catch((B)=>this.log(`heartbeat failed: ${B.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(B,Q,W="normal"){return this.call("send_task",{alias:B,task:Q,priority:W,from_session:this.alias})}async message(B,Q){return this.call("send_message",{alias:B,message:Q,from_session:this.alias})}async reply(B,Q,W="completed"){return this.call("reply",{task_id:B,text:Q,status:W})}async status(B,Q){return this.call("report_status",{resume_id:this.resumeId,alias:this.alias,status:B,server:n(),hostname:n(),agent:this.agent,project_dir:process.cwd(),...Q})}async getAllStatus(){return this.call("get_all_status",{})}async broadcast(B,Q){return this.call("broadcast",{message:B,filter_server:Q?.server,filter_status:Q?.status})}async connectSSE(){let B=encodeURIComponent(this.alias),Q=`${this.url}/events/${B}`,W=this.reconnectDelay;while(this.running){try{this.sseAbort=new AbortController;let X={Accept:"text/event-stream"};if(this.token)X.Authorization=`Bearer ${this.token}`;let Z=await fetch(Q,{headers:X,signal:this.sseAbort.signal});if(!Z.ok||!Z.body){this.log(`SSE failed: ${Z.status}`),await this.sleep(W),W=Math.min(W*1.5,60000);continue}W=this.reconnectDelay;let Y=Z.body.getReader(),U=new TextDecoder,K="";while(this.running){let{done:$,value:_}=await Y.read();if($)break;K+=U.decode(_,{stream:!0});let q=K.split(`
3
+ `);K=q.pop()||"";for(let J of q){if(!J.startsWith("data: "))continue;try{let H=JSON.parse(J.slice(6));if(H.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(H.type==="new_task"||H.type==="new_message"||H.type==="broadcast")await this.processInbox()}catch{}}}}catch(X){if(X.name==="AbortError")break;this.emit("error",X),this.log(`SSE error: ${X.message}`)}if(this.running)this.emit("disconnected"),this.log(`SSE reconnecting in ${W/1000}s...`),await this.sleep(W),W=Math.min(W*1.5,60000)}}async processInbox(){try{let Q=(await this.call("get_inbox",{alias:this.alias,limit:10}))?.messages||[];for(let W of Q)await this.call("ack_inbox",{alias:this.alias,message_id:W.id}),this.log(`← ${W.from_session}: ${W.content.slice(0,60)}`),this.emit("task",W),this.emit("message",W)}catch(B){this.log(`inbox error: ${B.message}`)}}sleep(B){return new Promise((Q)=>setTimeout(Q,B))}};AB=S});import{chmodSync as NB,readFileSync as z,writeFileSync as M,existsSync as L,mkdirSync as N,readdirSync as k,statSync as v}from"fs";import{join as V}from"path";import{spawn as g,execSync as D}from"child_process";import{createInterface as wB}from"readline";var E=process.argv.slice(2),u=E[0],F=process.env.HOME||process.env.USERPROFILE||"~";function XB(){return V(F,".anet","config.json")}function f(){return V(F,".anet","server","config.json")}function I(){return V(process.cwd(),".anet","nodes")}function YB(){return A().token||process.env.COMMHUB_TOKEN||x().token||""}function m(B){let Q=B||YB();return Q?{Authorization:`Bearer ${Q}`}:{}}function x(){let B=XB();if(L(B))try{return JSON.parse(z(B,"utf-8"))}catch{}return{}}function ZB(B){let Q=V(F,".anet");N(Q,{recursive:!0}),M(V(Q,"config.json"),JSON.stringify(B,null,2)+`
4
+ `)}function i(){let B=f();if(L(B))try{return JSON.parse(z(B,"utf-8"))}catch{}return{}}function e(B){let Q=V(F,".anet","server");N(Q,{recursive:!0}),M(V(Q,"config.json"),JSON.stringify(B,null,2)+`
5
+ `)}function T(B){if(typeof B==="string"){if(B==="codex"||B==="codex-sdk")return"codex-sdk";if(B==="claude"||B==="claude-sdk"||B==="claude-agent-sdk")return"claude-agent-sdk";if(B==="agent-sdk")return"claude-agent-sdk";return"claude-code-cli"}let Q=B;if(!Q)return"claude-code-cli";if(Q.runtime==="agent-sdk")return Q.codexRuntime==="codex"?"codex-sdk":"claude-agent-sdk";return T(Q.runtime||"claude-code-cli")}function $B(B,Q){return Q?.name||Q?.alias||B}function b(B){return B.session||B.resume||""}function FB(B){return B.normalize("NFC")}function y(B){if(B!==FB(B))console.error(`Error: node-name must be Unicode NFC normalized: ${B}`),process.exit(1);if(!/^[^\s\/\\:*?"<>|.][^\s\/\\:*?"<>|.]*$/.test(B))console.error(`Error: invalid node-name "${B}"`),console.error(`Allowed: Chinese/letters/numbers/-/_ ; forbidden: whitespace, '.', / \\ : * ? " < > |`),process.exit(1)}function R(B){let Q=V(I(),B,"config.json");if(!L(Q))return null;try{let W=JSON.parse(z(Q,"utf-8")),X=x();return{...W,name:W.name||W.alias||B,alias:W.alias||W.name||B,session:W.session||W.resume||"",hub:W.hub||X.hub||"",token:W.token||X.token||"",channels:Array.isArray(W.channels)?W.channels:[],env:{...W.env},flags:{...W.flags}}}catch{return null}}function UB(B){let Q=V(I(),B,"config.json");if(!L(Q))return null;try{let W=JSON.parse(z(Q,"utf-8"));return{...W,channels:Array.isArray(W.channels)?W.channels:[],env:{...W.env},flags:{...W.flags}}}catch{return null}}function j(B,Q){let W=V(I(),B);N(W,{recursive:!0}),M(V(W,"config.json"),JSON.stringify(Q,null,2)+`
6
+ `)}function c(){let B=I();if(!L(B))return[];return k(B).filter((Q)=>L(V(B,Q,"config.json")))}function A(){let B={_channels:[],_envs:[]};for(let Q=0;Q<E.length;Q++){if(E[Q]==="--channel"&&E[Q+1]){B._channels.push(E[++Q]);continue}if(E[Q]==="--env"&&E[Q+1]){B._envs.push(E[++Q]);continue}if(E[Q].startsWith("--")&&E[Q+1]&&!E[Q+1].startsWith("--"))B[E[Q].slice(2)]=E[++Q];else if(E[Q].startsWith("--"))B[E[Q].slice(2)]="true"}return B}function BB(B){try{return D(`command -v ${JSON.stringify(B)}`,{stdio:"ignore",shell:"/bin/bash"}),!0}catch{return!1}}function d(){console.log("[anet] claude-code-cli requires:"),console.log(" - Claude Pro / Team / Enterprise subscription"),console.log(' - Run "claude auth login" first'),console.log(" - Uses Anthropic Claude only"),console.log(" - For other models, use --runtime codex-sdk or claude-agent-sdk")}function l(B,Q){if(B==="claude-code-cli"){if(!BB("claude"))console.warn("[anet] Warning: claude CLI not found in PATH."),console.warn("[anet] Install: npm install -g @anthropic-ai/claude-code");if(Q==="start")d();return}if(!BB("npx"))console.warn("[anet] Warning: npx not found in PATH; cannot launch @sleep2agi/agent-node."),console.warn("[anet] Install Node.js/npm first, or install agent-node globally: npm install -g @sleep2agi/agent-node")}function QB(){console.log(`
7
7
  anet — AI Agent Network CLI
8
8
 
9
9
  anet init Configure hub URL (global, once)
@@ -24,9 +24,9 @@ Quick start:
24
24
  anet init --hub http://IP:9200
25
25
  anet create 指挥室
26
26
  anet start 指挥室
27
- `)}async function OB(){let B=A(),Q=B.hub;if(!Q)Q=await G("CommHub URL (e.g. http://YOUR_IP:9200)");if(!Q)D(),console.error("Error: hub URL required"),process.exit(1);Q=Q.replace(/\/+$/,"");let W=B.token||"";if(!W)W=await G("Auth token (empty to skip)");D();try{let Y=await(await fetch(`${Q}/health`,{headers:W?{Authorization:`Bearer ${W}`}:{}})).json();console.log(`✅ CommHub v${Y.version} — ${Y.sessions} sessions, ${Y.sse_connections} SSE`)}catch(Z){console.error(`❌ Cannot reach ${Q}: ${Z.message}`),process.exit(1)}let X=x();if(X.hub=Q,W)X.token=W;else if(!X.token)delete X.token;ZB(X),console.log(`
27
+ `)}async function OB(){let B=A(),Q=B.hub;if(!Q)Q=await G("CommHub URL (e.g. http://YOUR_IP:9200)");if(!Q)P(),console.error("Error: hub URL required"),process.exit(1);Q=Q.replace(/\/+$/,"");let W=B.token||"";if(!W)W=await G("Auth token (empty to skip)");P();try{let Y=await(await fetch(`${Q}/health`,{headers:W?{Authorization:`Bearer ${W}`}:{}})).json();console.log(`✅ CommHub v${Y.version} — ${Y.sessions} sessions, ${Y.sse_connections} SSE`)}catch(Z){console.error(`❌ Cannot reach ${Q}: ${Z.message}`),process.exit(1)}let X=x();if(X.hub=Q,W)X.token=W;else if(!X.token)delete X.token;ZB(X),console.log(`
28
28
  Saved to ${XB()}`),console.log("Next: anet init project")}async function IB(){let B=x(),Q=B.hub;if(!Q)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let W=V(process.cwd(),".anet");N(W,{recursive:!0});let X=V(W,"node-server.ts");if(!L(X)){let J=[V(new URL(".",import.meta.url).pathname,"..","..","src","node-server.ts"),V(new URL(".",import.meta.url).pathname,"..","src","node-server.ts"),V(process.argv[1],"..","..","src","node-server.ts")],H=!1;for(let O of J)if(L(O)){M(X,z(O,"utf-8")),console.log(" ✅ .anet/node-server.ts"),H=!0;break}if(!H)console.log(" ❌ Cannot find node-server.ts"),console.log(" Fix: cp $(npm root -g)/@sleep2agi/agent-network/src/node-server.ts .anet/node-server.ts")}else console.log(" Channel plugin: exists");let Z=V(W,"package.json");if(!L(Z)){M(Z,JSON.stringify({private:!0,dependencies:{"@modelcontextprotocol/sdk":"^1.12.0"}},null,2)+`
29
- `);try{p("bun install",{cwd:W,stdio:"pipe"}),console.log(" ✅ Dependencies installed")}catch{console.log(" ⚠️ Run: cd .anet && bun install")}}let Y=V(W,".env"),U=B.token||"",K=`COMMHUB_URL=${Q}
29
+ `);try{D("bun install",{cwd:W,stdio:"pipe"}),console.log(" ✅ Dependencies installed")}catch{console.log(" ⚠️ Run: cd .anet && bun install")}}let Y=V(W,".env"),U=B.token||"",K=`COMMHUB_URL=${Q}
30
30
  `;if(U)K+=`COMMHUB_TOKEN=${U}
31
31
  `;M(Y,K),console.log(`CommHub URL: ${Q}${U?" (with token)":""}`);let $=V(process.cwd(),".mcp.json"),_={};if(L($))try{_=JSON.parse(z($,"utf-8"))}catch{}if(!_.mcpServers?.commhub)_.mcpServers=_.mcpServers||{},_.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},M($,JSON.stringify(_,null,2)+`
32
32
  `),console.log(".mcp.json: commhub → .anet/node-server.ts");else console.log(".mcp.json: commhub already set");let q=V(process.cwd(),"CLAUDE.md");if(!L(q))M(q,`# Agent Network (CommHub)
@@ -70,9 +70,9 @@ commhub_get_all_status()
70
70
  `),console.log("CLAUDE.md: created");else console.log("CLAUDE.md: already exists");console.log(`
71
71
  ✅ Project ready. Next: anet create <node-name>`)}async function RB(){console.warn("[deprecated] anet init profile is now anet create."),console.warn(` Run: anet create <node-name> [--runtime ...]
72
72
  `),await qB(E[2])}function a(B,Q){let W=x(),X=Q.hub||W.hub;if(!X)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let Z={};for(let $ of Q._envs){let _=$.indexOf("=");if(_>0)Z[$.slice(0,_)]=$.slice(_+1)}let Y=T(Q.runtime||"claude-code-cli"),U=Y==="codex-sdk"?"gpt-5.4":void 0;return{anet_version:"0.1.0",name:B,runtime:Y,...Q.hub?{hub:X}:{},...Q.model||U?{model:Q.model||U}:{},...Q.tools?{tools:Q.tools.split(",").map(($)=>$.trim())}:{},channels:Q._channels.length>0?Q._channels:["server:commhub"],env:Z,flags:{dangerouslySkipPermissions:!0,...Y==="claude-code-cli"?{teammateMode:Q["teammate-mode"]||"in-process"}:{},...Q["max-turns"]?{maxTurns:parseInt(Q["max-turns"])}:{}},...Q.session?{session:Q.session}:{}}}function KB(B,Q){let W=V(F,".claude","channels","commhub"),X=process.cwd().replace(/\//g,"-"),Z=V(W,X);N(Z,{recursive:!0}),M(V(Z,".env"),`COMMHUB_ALIAS=${B}
73
- `),v(B,Q)}function VB(B,Q){if(B.channels=B.channels||[],!B.channels.includes(Q))B.channels.push(Q)}function _B(B,Q,W){let X=V(I(),B,"channels","telegram");N(X,{recursive:!0}),N(V(X,"inbox"),{recursive:!0});let Z=V(X,".env");M(Z,`TELEGRAM_BOT_TOKEN=${Q}
73
+ `),j(B,Q)}function VB(B,Q){if(B.channels=B.channels||[],!B.channels.includes(Q))B.channels.push(Q)}function _B(B,Q,W){let X=V(I(),B,"channels","telegram");N(X,{recursive:!0}),N(V(X,"inbox"),{recursive:!0});let Z=V(X,".env");M(Z,`TELEGRAM_BOT_TOKEN=${Q}
74
74
  `);try{NB(Z,384)}catch{}return M(V(X,"access.json"),JSON.stringify({dmPolicy:"allowlist",allowFrom:[W],groups:{},pending:{}},null,2)+`
75
- `),X}async function u(B,Q){console.log(B),Q.forEach((W,X)=>{let Z=W.description?` ${W.description}`:"";console.log(` ${X+1}) ${W.label}${Z}`)});while(!0){let W=await G("Select","1"),X=Number.parseInt(W,10)-1;if(X>=0&&X<Q.length)return Q[X].value;console.log(`Please enter 1-${Q.length}.`)}}function xB(B){let Q={};for(let[W,X]of Object.entries(B)){let Z=/TOKEN|KEY|SECRET|PASSWORD/i.test(W);Q[W]=Z&&X?`${X.slice(0,4)}...`:X}return Q}function DB(B,Q){let W={name:$B(B,Q),runtime:T(Q),model:Q.model||"(runtime default)",session:w(Q)||"(new)",channels:Q.channels,env:xB(Q.env||{}),config:V(I(),B,"config.json")};console.log(`
75
+ `),X}async function h(B,Q){console.log(B),Q.forEach((W,X)=>{let Z=W.description?` ${W.description}`:"";console.log(` ${X+1}) ${W.label}${Z}`)});while(!0){let W=await G("Select","1"),X=Number.parseInt(W,10)-1;if(X>=0&&X<Q.length)return Q[X].value;console.log(`Please enter 1-${Q.length}.`)}}function xB(B){let Q={};for(let[W,X]of Object.entries(B)){let Z=/TOKEN|KEY|SECRET|PASSWORD/i.test(W);Q[W]=Z&&X?`${X.slice(0,4)}...`:X}return Q}function DB(B,Q){let W={name:$B(B,Q),runtime:T(Q),model:Q.model||"(runtime default)",session:b(Q)||"(new)",channels:Q.channels,env:xB(Q.env||{}),config:V(I(),B,"config.json")};console.log(`
76
76
  [anet] Config summary:`),console.log(JSON.stringify(W,null,2))}async function PB(){console.log(`
77
77
  [anet] Create a node
78
78
 
@@ -80,24 +80,24 @@ This wizard creates one agent node for this project:
80
80
  - node config: .anet/nodes/<node-name>/config.json
81
81
  - runtime: claude-code-cli / codex-sdk / claude-agent-sdk
82
82
  - optional Telegram channel: text + images from an allowlist user
83
- `);let B=await G("Node name");if(!B)D(),console.error("Error: node-name required"),process.exit(1);if(k(B),R(B))D(),console.error(`Node "${B}" already exists: .anet/nodes/${B}/config.json`),process.exit(1);console.log(`
83
+ `);let B=await G("Node name");if(!B)P(),console.error("Error: node-name required"),process.exit(1);if(y(B),R(B))P(),console.error(`Node "${B}" already exists: .anet/nodes/${B}/config.json`),process.exit(1);console.log(`
84
84
  Runtime guide:
85
85
  1) claude-code-cli Use your Claude Code app/CLI session. Best for existing Claude Code workflows.
86
86
  2) codex-sdk Run through agent-node with Codex. Best for GPT-5.4 / OpenAI models.
87
87
  3) claude-agent-sdk Run through agent-node with an Anthropic-compatible API.
88
88
  Use this for MiniMax, Intern-S1, or Anthropic API keys.
89
- `);let Q=await u("Select runtime:",[{label:"claude-code-cli",value:"claude-code-cli",description:"Claude Code CLI(需要 Pro 订阅)"},{label:"codex-sdk",value:"codex-sdk",description:"Codex SDK(GPT-5.4)"},{label:"claude-agent-sdk",value:"claude-agent-sdk",description:"Claude Agent SDK(MiniMax/书生等)"}]),W=A();if(W.runtime=Q,Q==="codex-sdk"){console.log(`
89
+ `);let Q=await h("Select runtime:",[{label:"claude-code-cli",value:"claude-code-cli",description:"Claude Code CLI(需要 Pro 订阅)"},{label:"codex-sdk",value:"codex-sdk",description:"Codex SDK(GPT-5.4)"},{label:"claude-agent-sdk",value:"claude-agent-sdk",description:"Claude Agent SDK(MiniMax/书生等)"}]),W=A();if(W.runtime=Q,Q==="codex-sdk"){console.log(`
90
90
  Model guide:
91
91
  - gpt-5.4 Default Codex model.
92
92
  - o3 Reasoning model; use it if your account/session supports it.
93
93
  - custom Type an exact model name.
94
- `);let U=await u("Select model:",[{label:"gpt-5.4",value:"gpt-5.4"},{label:"o3",value:"o3"},{label:"custom",value:"__custom__"}]);W.model=U==="__custom__"?await G("Model"):U}else if(Q==="claude-agent-sdk"){console.log(`
94
+ `);let U=await h("Select model:",[{label:"gpt-5.4",value:"gpt-5.4"},{label:"o3",value:"o3"},{label:"custom",value:"__custom__"}]);W.model=U==="__custom__"?await G("Model"):U}else if(Q==="claude-agent-sdk"){console.log(`
95
95
  Model guide:
96
96
  - MiniMax-M2.7 Low-cost Claude-compatible model. anet fills MiniMax base URL for you.
97
97
  - intern-s1-pro Intern/书生 Claude-compatible endpoint. anet fills its base URL for you.
98
98
  - claude-sonnet-4-6 Anthropic Claude via the default Anthropic API.
99
99
  - custom Any Anthropic-compatible model name.
100
- `);let U=await u("Select model:",[{label:"MiniMax-M2.7",value:"MiniMax-M2.7"},{label:"intern-s1-pro",value:"intern-s1-pro"},{label:"claude-sonnet-4-6",value:"claude-sonnet-4-6"},{label:"custom",value:"__custom__"}]);if(W.model=U==="__custom__"?await G("Model"):U,W.model==="MiniMax-M2.7")W._envs.push("ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic");else if(W.model==="intern-s1-pro")W._envs.push("ANTHROPIC_BASE_URL=https://chat.intern-ai.org.cn");console.log(`
100
+ `);let U=await h("Select model:",[{label:"MiniMax-M2.7",value:"MiniMax-M2.7"},{label:"intern-s1-pro",value:"intern-s1-pro"},{label:"claude-sonnet-4-6",value:"claude-sonnet-4-6"},{label:"custom",value:"__custom__"}]);if(W.model=U==="__custom__"?await G("Model"):U,W.model==="MiniMax-M2.7")W._envs.push("ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic");else if(W.model==="intern-s1-pro")W._envs.push("ANTHROPIC_BASE_URL=https://chat.intern-ai.org.cn");console.log(`
101
101
  API key:
102
102
  Paste the provider key for the selected model.
103
103
  - MiniMax: get a token from the MiniMax platform / API Keys page.
@@ -108,28 +108,28 @@ Telegram setup:
108
108
  1. Open Telegram and talk to @BotFather.
109
109
  2. Create a bot and copy the bot token.
110
110
  3. Talk to @userinfobot to get your numeric user ID.
111
- `);let U=await G("Telegram Bot Token"),K=await G("Allow User ID (numeric ID from @userinfobot)","7612221352");if(!U)D(),console.error("Error: Telegram Bot Token required"),process.exit(1);Y={botToken:U,allowId:K},VB(X,"telegram")}if(D(),KB(B,X),Y)_B(B,Y.botToken,Y.allowId);if(l(T(X),"create"),console.log(`
111
+ `);let U=await G("Telegram Bot Token"),K=await G("Allow User ID (numeric ID from @userinfobot)","7612221352");if(!U)P(),console.error("Error: Telegram Bot Token required"),process.exit(1);Y={botToken:U,allowId:K},VB(X,"telegram")}if(P(),KB(B,X),Y)_B(B,Y.botToken,Y.allowId);if(l(T(X),"create"),console.log(`
112
112
  [anet] Created node "${B}" (${T(X)})`),Y)console.log("[anet] ✅ Telegram channel added");if(T(X)==="claude-code-cli")d();console.log("[anet] ⚠ dangerouslySkipPermissions and teammateMode enabled by default."),console.log(`[anet] To disable: edit .anet/nodes/${B}/config.json → flags`),DB(B,R(B)||X),console.log(`
113
- Start: anet start ${B}`)}async function qB(B){let Q=B||E[1];if(!Q)return PB();if(Q.startsWith("--"))console.error("Usage: anet create <node-name> [--runtime claude-code-cli|codex-sdk|claude-agent-sdk] [--model ...] [--tools ...]"),console.error("Or run fully interactive: anet create"),process.exit(1);if(k(Q),R(Q))console.error(`Node "${Q}" already exists: .anet/nodes/${Q}/config.json`),process.exit(1);let W=A(),X=a(Q,W);if(KB(Q,X),l(T(X),"create"),console.log(`
113
+ Start: anet start ${B}`)}async function qB(B){let Q=B||E[1];if(!Q)return PB();if(Q.startsWith("--"))console.error("Usage: anet create <node-name> [--runtime claude-code-cli|codex-sdk|claude-agent-sdk] [--model ...] [--tools ...]"),console.error("Or run fully interactive: anet create"),process.exit(1);if(y(Q),R(Q))console.error(`Node "${Q}" already exists: .anet/nodes/${Q}/config.json`),process.exit(1);let W=A(),X=a(Q,W);if(KB(Q,X),l(T(X),"create"),console.log(`
114
114
  [anet] Created node "${Q}" (${T(X)})`),T(X)==="claude-code-cli")d();console.log("[anet] ⚠ dangerouslySkipPermissions and teammateMode enabled by default."),console.log(`[anet] To disable: edit .anet/nodes/${Q}/config.json → flags`),console.log(`
115
- Start: anet start ${Q}`)}var P=null;function bB(){if(!P)P=wB({input:process.stdin,output:process.stdout});return P}function D(){if(P)P.close(),P=null}function G(B,Q){let W=Q?` [${Q}]`:"";return new Promise((X)=>{bB().question(`${B}${W}: `,(Z)=>{X(Z.trim()||Q||"")})})}function CB(B){if(T(B)!=="claude-code-cli")return;if(!B.channels?.some((J)=>J.includes("commhub")))return;let Q=V(process.cwd(),".mcp.json"),W={};if(L(Q))try{W=JSON.parse(z(Q,"utf-8"))}catch{}let X=V(process.cwd(),".anet"),Z=V(X,"node-server.ts"),Y=[V(new URL(".",import.meta.url).pathname,"..","..","src","node-server.ts"),V(new URL(".",import.meta.url).pathname,"..","src","node-server.ts"),V(process.argv[1],"..","..","src","node-server.ts")];for(let J of Y)if(L(J)){N(X,{recursive:!0});let H=z(J,"utf-8"),O=L(Z)?z(Z,"utf-8"):"";if(H!==O)M(Z,H),console.log("[anet] Updated .anet/node-server.ts");break}let U=V(X,"package.json");if(!L(U)){N(X,{recursive:!0}),M(U,JSON.stringify({private:!0,dependencies:{"@modelcontextprotocol/sdk":"^1.12.0"}},null,2)+`
116
- `);try{p("bun install",{cwd:X,stdio:"pipe"})}catch{}}if(W.mcpServers=W.mcpServers||{},!Object.keys(W.mcpServers).some((J)=>J.includes("commhub")))W.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},M(Q,JSON.stringify(W,null,2)+`
115
+ Start: anet start ${Q}`)}var w=null;function bB(){if(!w)w=wB({input:process.stdin,output:process.stdout});return w}function P(){if(w)w.close(),w=null}function G(B,Q){let W=Q?` [${Q}]`:"";return new Promise((X)=>{bB().question(`${B}${W}: `,(Z)=>{X(Z.trim()||Q||"")})})}function CB(B){if(T(B)!=="claude-code-cli")return;if(!B.channels?.some((J)=>J.includes("commhub")))return;let Q=V(process.cwd(),".mcp.json"),W={};if(L(Q))try{W=JSON.parse(z(Q,"utf-8"))}catch{}let X=V(process.cwd(),".anet"),Z=V(X,"node-server.ts"),Y=[V(new URL(".",import.meta.url).pathname,"..","..","src","node-server.ts"),V(new URL(".",import.meta.url).pathname,"..","src","node-server.ts"),V(process.argv[1],"..","..","src","node-server.ts")];for(let J of Y)if(L(J)){N(X,{recursive:!0});let H=z(J,"utf-8"),O=L(Z)?z(Z,"utf-8"):"";if(H!==O)M(Z,H),console.log("[anet] Updated .anet/node-server.ts");break}let U=V(X,"package.json");if(!L(U)){N(X,{recursive:!0}),M(U,JSON.stringify({private:!0,dependencies:{"@modelcontextprotocol/sdk":"^1.12.0"}},null,2)+`
116
+ `);try{D("bun install",{cwd:X,stdio:"pipe"})}catch{}}if(W.mcpServers=W.mcpServers||{},!Object.keys(W.mcpServers).some((J)=>J.includes("commhub")))W.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},M(Q,JSON.stringify(W,null,2)+`
117
117
  `),console.log("[anet] .mcp.json: added commhub");let $=V(X,".env"),_=B.token||"",q=`COMMHUB_URL=${B.hub||"http://127.0.0.1:9200"}
118
118
  `;if(_)q+=`COMMHUB_TOKEN=${_}
119
119
  `;M($,q),W.mcpServers=W.mcpServers||{},W.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},M(Q,JSON.stringify(W,null,2)+`
120
- `),console.log("[anet] .mcp.json: added commhub channel server")}async function m(B,Q=!1){let W=R(B);if(!W)console.error(`Node "${B}" not found. Create it first: anet create ${B}`),process.exit(1);let X=T(W),Z=$B(B,W),Y=w(W),U=!!Y&&!Q,K=U?`Resuming session ${Y.slice(0,8)}...`:"Starting new session";console.log(`[anet] ${K} for "${B}" [${X}]...
121
- `),l(X,"start"),CB(W);let $=W.token||"";if(X==="codex-sdk"||X==="claude-agent-sdk"){let _=["@sleep2agi/agent-node","--config",V(I(),B,"config.json"),"--alias",Z];if(Q)_.push("--new-session","true");let q={...process.env,...$?{COMMHUB_TOKEN:$}:{}};for(let[H,O]of Object.entries(W.env))q[H]=O.replace(/^~/,F);h("npx",_,{env:q,stdio:"inherit",shell:!0}).on("exit",(H)=>process.exit(H||0))}else{let _={...process.env,COMMHUB_ALIAS:W.alias,...$?{COMMHUB_TOKEN:$}:{}};for(let[H,O]of Object.entries(W.env))_[H]=O.replace(/^~/,F);if(W.channels.includes("telegram"))_.TELEGRAM_STATE_DIR=V(I(),B,"channels","telegram");let q=[];if(W.flags.dangerouslySkipPermissions)q.push("--dangerously-skip-permissions");for(let H of W.channels)if(H.startsWith("server:"))q.push("--dangerously-load-development-channels",H);else if(H==="telegram")q.push("--channels","plugin:telegram@claude-plugins-official");else q.push("--channels",H);if(W.flags.teammateMode)q.push("--teammate-mode",W.flags.teammateMode);if(U)q.push("--resume",Y);q.push("-n",Z),h("claude",q,{env:_,stdio:"inherit",shell:!0}).on("exit",(H)=>{if(!U||Q){if(console.log(`
122
- [anet] Tip: bind this Claude Code session with:`),console.log("[anet] anet session ls"),console.log(`[anet] anet resume ${B} --session <session-id>`),Q&&Y)console.log(`[anet] Next "anet start ${B}" will still resume ${Y.slice(0,8)}... until you rebind.`)}process.exit(H||0)})}}async function WB(){let B=E[1];if(!B){jB("start");return}await m(B,!!A()["new-session"])}async function vB(){let B=E[1];if(!B){console.error("Usage: anet resume <node-name> --session <session-id>"),console.error("Daily start/resume: anet start <node-name>");return}let Q=R(B),W=A(),X=W.session;if(!X){console.warn("[deprecated] anet resume <node-name> without --session is now anet start <node-name>."),await m(B,!1);return}if(k(B),!Q){let Z={...W,session:X,runtime:W.runtime||"claude-code-cli"};Q=a(B,Z),v(B,Q),console.log(`[anet] Created node "${B}"`)}else{let Z=w(Q);if(Z&&Z!==X&&W.yes!=="true"){let U=await G(`[anet] ${B} already has session ${Z.slice(0,8)}..., overwrite? (y/n)`,"n");if(D(),!/^y(es)?$/i.test(U)){console.log("[anet] Session unchanged.");return}}let Y=UB(B)||Q;Y.session=X,delete Y.resume,delete Y.resumeAlias,v(B,Y)}console.log(`[anet] Saved session ${X.slice(0,8)}... to .anet/nodes/${B}/config.json
123
- `),await m(B,!1)}function jB(B){let Q=c();if(Q.length===0){console.log("No nodes. Run: anet create <node-name>");return}console.log(`
120
+ `),console.log("[anet] .mcp.json: added commhub channel server")}async function p(B,Q=!1){let W=R(B);if(!W)console.error(`Node "${B}" not found. Create it first: anet create ${B}`),process.exit(1);let X=T(W),Z=$B(B,W),Y=b(W),U=!!Y&&!Q,K=U?`Resuming session ${Y.slice(0,8)}...`:"Starting new session";console.log(`[anet] ${K} for "${B}" [${X}]...
121
+ `),l(X,"start"),CB(W);let $=W.token||"";if($)console.log(`[anet] Token: ${$.slice(0,8)}...`);else console.log("[anet] Warning: no token configured. Check ~/.anet/config.json");if(X==="codex-sdk"||X==="claude-agent-sdk"){let _=["@sleep2agi/agent-node","--config",V(I(),B,"config.json"),"--alias",Z];if(Q)_.push("--new-session","true");let q={...process.env,...$?{COMMHUB_TOKEN:$}:{}};for(let[H,O]of Object.entries(W.env))q[H]=O.replace(/^~/,F);g("npx",_,{env:q,stdio:"inherit",shell:!0}).on("exit",(H)=>process.exit(H||0))}else{let _={...process.env,COMMHUB_ALIAS:W.alias,...$?{COMMHUB_TOKEN:$}:{}};for(let[H,O]of Object.entries(W.env))_[H]=O.replace(/^~/,F);if(W.channels.includes("telegram"))_.TELEGRAM_STATE_DIR=V(I(),B,"channels","telegram");let q=[];if(W.flags.dangerouslySkipPermissions)q.push("--dangerously-skip-permissions");for(let H of W.channels)if(H.startsWith("server:"))q.push("--dangerously-load-development-channels",H);else if(H==="telegram")q.push("--channels","plugin:telegram@claude-plugins-official");else q.push("--channels",H);if(W.flags.teammateMode)q.push("--teammate-mode",W.flags.teammateMode);if(U)q.push("--resume",Y);q.push("-n",Z),g("claude",q,{env:_,stdio:"inherit",shell:!0}).on("exit",(H)=>{if(!U||Q){if(console.log(`
122
+ [anet] Tip: bind this Claude Code session with:`),console.log("[anet] anet session ls"),console.log(`[anet] anet resume ${B} --session <session-id>`),Q&&Y)console.log(`[anet] Next "anet start ${B}" will still resume ${Y.slice(0,8)}... until you rebind.`)}process.exit(H||0)})}}async function WB(){let B=E[1];if(!B){jB("start");return}await p(B,!!A()["new-session"])}async function vB(){let B=E[1];if(!B){console.error("Usage: anet resume <node-name> --session <session-id>"),console.error("Daily start/resume: anet start <node-name>");return}let Q=R(B),W=A(),X=W.session;if(!X){console.warn("[deprecated] anet resume <node-name> without --session is now anet start <node-name>."),await p(B,!1);return}if(y(B),!Q){let Z={...W,session:X,runtime:W.runtime||"claude-code-cli"};Q=a(B,Z),j(B,Q),console.log(`[anet] Created node "${B}"`)}else{let Z=b(Q);if(Z&&Z!==X&&W.yes!=="true"){let U=await G(`[anet] ${B} already has session ${Z.slice(0,8)}..., overwrite? (y/n)`,"n");if(P(),!/^y(es)?$/i.test(U)){console.log("[anet] Session unchanged.");return}}let Y=UB(B)||Q;Y.session=X,delete Y.resume,delete Y.resumeAlias,j(B,Y)}console.log(`[anet] Saved session ${X.slice(0,8)}... to .anet/nodes/${B}/config.json
123
+ `),await p(B,!1)}function jB(B){let Q=c();if(Q.length===0){console.log("No nodes. Run: anet create <node-name>");return}console.log(`
124
124
  Nodes:
125
- `);for(let W of Q){let X=R(W);console.log(` ${W} [${T(X||void 0)}] session=${X?w(X).slice(0,8)||"-":"-"} channels=[${X?.channels.join(", ")}]`)}console.log(`
125
+ `);for(let W of Q){let X=R(W);console.log(` ${W} [${T(X||void 0)}] session=${X?b(X).slice(0,8)||"-":"-"} channels=[${X?.channels.join(", ")}]`)}console.log(`
126
126
  anet ${B} <node-name>
127
127
  `)}async function kB(){let B=c();if(B.length>0){console.log(`
128
128
  Nodes:
129
- `);for(let K of B){let $=R(K),_=$?w($).slice(0,8)||"-":"-";console.log(` ${K} [${T($||void 0)}] session=${_} channels=[${$?.channels.join(", ")}]`)}console.log()}let Q=process.cwd(),W=V(F,".claude","sessions"),X=[];if(L(W))for(let K of j(W).filter(($)=>$.endsWith(".json")))try{let $=JSON.parse(z(V(W,K),"utf-8"));if($.cwd===Q)X.push($)}catch{}if(X.length===0&&B.length===0){console.log("No sessions or nodes in this directory."),console.log(`Get started: anet init
130
- `);return}let Z=x(),Y=[],U={};if(Z.hub)try{let[K,$]=await Promise.all([fetch(`${Z.hub}/api/status`,{headers:f()}).then((_)=>_.json()),fetch(`${Z.hub}/health`,{headers:f()}).then((_)=>_.json())]);Y=K.sessions||[],U=$.sse_sessions||{}}catch{}if(X.length>0){console.log(`Sessions (${Q}):
131
- `),console.log(" SESSION PID NETWORK"),console.log(" ──────────────────── ─────── ─────────────────────");for(let K of X){let $=K.sessionId.slice(0,18),_=!1;try{process.kill(K.pid,0),_=!0}catch{}let q="(not in network)",J=Q.replace(/\//g,"-"),H=V(F,".claude","channels","commhub",J,".env");if(L(H)){let t=z(H,"utf-8").match(/COMMHUB_ALIAS=(.+)/);if(t){let b=t[1].trim(),r=Y.find((HB)=>HB.alias===b),EB=U[b]?"●":"○";q=r?`${b} ${r.status} ${EB}`:`${b} (not registered)`}}console.log(` ${$} ${(_?`${K.pid}`:`${K.pid}✕`).padEnd(7)} ${q}`)}console.log()}}async function yB(){let B=x(),Q=A(),W=process.env.COMMHUB_URL||Q.hub||B.hub||"http://127.0.0.1:9200",X=process.env.COMMHUB_ALIAS||Q.alias;if(!X)console.error("Error: --alias required"),process.exit(1);let{CommHub:Z}=await Promise.resolve().then(() => (o(),s)),Y=new Z({url:W,alias:X});Y.on("task",async(U)=>{console.log(`[${X}] ← ${U.from_session}: ${U.content.slice(0,100)}`),await Y.send(U.from_session,`[${X}] 收到: ${U.content.slice(0,200)}`)}),Y.on("connected",()=>console.log(`[${X}] Connected`)),Y.on("disconnected",()=>console.log(`[${X}] Reconnecting...`)),process.on("SIGINT",()=>Y.disconnect().then(()=>process.exit(0))),console.log(`[${X}] Listening on ${W}`)}async function SB(){let B=E[1];if(B==="start"){let Q=A(),W=i(),X=Q.port||W.port||"9200",Z=Q.host||W.host||"0.0.0.0",Y=Q.token||W.token||YB();if(!Y)Y=crypto.randomUUID().replace(/-/g,""),console.log(`[anet] Generated auth token: ${Y}`),console.log(`[anet] Save this token — agents need it to connect.
132
- `);e({port:X,host:Z,token:Y});let U=x();if(!U.token)U.token=Y,ZB(U);console.log(`[anet] Starting CommHub Server on ${Z}:${X}${Y?" (auth enabled)":""}...`);let K={...process.env,PORT:X,HOST:Z};if(Y)K.COMMHUB_AUTH_TOKEN=Y;h("bunx",["@sleep2agi/commhub-server"],{env:K,stdio:"inherit",shell:!0}).on("exit",(_)=>process.exit(_||0))}else if(B==="config"){let Q=A(),W=i();if(Q.port)W.port=Q.port;if(Q.host)W.host=Q.host;if(Q.token)W.token=Q.token;if(Q.port||Q.host||Q.token)e(W),console.log(`Server config saved: ${g()}`);console.log(JSON.stringify(W,null,2))}else console.log(`
129
+ `);for(let K of B){let $=R(K),_=$?b($).slice(0,8)||"-":"-";console.log(` ${K} [${T($||void 0)}] session=${_} channels=[${$?.channels.join(", ")}]`)}console.log()}let Q=process.cwd(),W=V(F,".claude","sessions"),X=[];if(L(W))for(let K of k(W).filter(($)=>$.endsWith(".json")))try{let $=JSON.parse(z(V(W,K),"utf-8"));if($.cwd===Q)X.push($)}catch{}if(X.length===0&&B.length===0){console.log("No sessions or nodes in this directory."),console.log(`Get started: anet init
130
+ `);return}let Z=x(),Y=[],U={};if(Z.hub)try{let[K,$]=await Promise.all([fetch(`${Z.hub}/api/status`,{headers:m()}).then((_)=>_.json()),fetch(`${Z.hub}/health`,{headers:m()}).then((_)=>_.json())]);Y=K.sessions||[],U=$.sse_sessions||{}}catch{}if(X.length>0){console.log(`Sessions (${Q}):
131
+ `),console.log(" SESSION PID NETWORK"),console.log(" ──────────────────── ─────── ─────────────────────");for(let K of X){let $=K.sessionId.slice(0,18),_=!1;try{process.kill(K.pid,0),_=!0}catch{}let q="(not in network)",J=Q.replace(/\//g,"-"),H=V(F,".claude","channels","commhub",J,".env");if(L(H)){let t=z(H,"utf-8").match(/COMMHUB_ALIAS=(.+)/);if(t){let C=t[1].trim(),r=Y.find((HB)=>HB.alias===C),EB=U[C]?"●":"○";q=r?`${C} ${r.status} ${EB}`:`${C} (not registered)`}}console.log(` ${$} ${(_?`${K.pid}`:`${K.pid}✕`).padEnd(7)} ${q}`)}console.log()}}async function yB(){let B=x(),Q=A(),W=process.env.COMMHUB_URL||Q.hub||B.hub||"http://127.0.0.1:9200",X=process.env.COMMHUB_ALIAS||Q.alias;if(!X)console.error("Error: --alias required"),process.exit(1);let{CommHub:Z}=await Promise.resolve().then(() => (o(),s)),Y=new Z({url:W,alias:X});Y.on("task",async(U)=>{console.log(`[${X}] ← ${U.from_session}: ${U.content.slice(0,100)}`),await Y.send(U.from_session,`[${X}] 收到: ${U.content.slice(0,200)}`)}),Y.on("connected",()=>console.log(`[${X}] Connected`)),Y.on("disconnected",()=>console.log(`[${X}] Reconnecting...`)),process.on("SIGINT",()=>Y.disconnect().then(()=>process.exit(0))),console.log(`[${X}] Listening on ${W}`)}async function SB(){let B=E[1];if(B==="start"){let Q=A(),W=i(),X=Q.port||W.port||"9200",Z=Q.host||W.host||"0.0.0.0",Y=Q.token||W.token||YB();if(!Y)Y=crypto.randomUUID().replace(/-/g,""),console.log(`[anet] Generated auth token: ${Y}`),console.log(`[anet] Save this token — agents need it to connect.
132
+ `);e({port:X,host:Z,token:Y});let U=x();if(!U.token)U.token=Y,ZB(U);console.log(`[anet] Starting CommHub Server on ${Z}:${X}${Y?" (auth enabled)":""}...`);let K={...process.env,PORT:X,HOST:Z};if(Y)K.COMMHUB_AUTH_TOKEN=Y;g("bunx",["@sleep2agi/commhub-server"],{env:K,stdio:"inherit",shell:!0}).on("exit",(_)=>process.exit(_||0))}else if(B==="config"){let Q=A(),W=i();if(Q.port)W.port=Q.port;if(Q.host)W.host=Q.host;if(Q.token)W.token=Q.token;if(Q.port||Q.host||Q.token)e(W),console.log(`Server config saved: ${f()}`);console.log(JSON.stringify(W,null,2))}else console.log(`
133
133
  anet server <command>
134
134
 
135
135
  start [options] Start CommHub Server
@@ -140,18 +140,18 @@ Options:
140
140
  --host <host> Bind address (default: 0.0.0.0)
141
141
  --token <token> Auth token
142
142
 
143
- Config: ${g()}
143
+ Config: ${f()}
144
144
  First 'anet server start' saves config, after that just 'anet server start'.
145
145
 
146
146
  Example:
147
147
  anet server start --port 9200 --token my-secret # 首次,保存配置
148
148
  anet server start # 之后直接启动
149
149
  anet server config # 查看配置
150
- `)}async function uB(){let B=x(),W=A().hub||B.hub;if(!W)console.error("Run 'anet init' first"),process.exit(1);let X=[];try{X=(await(await fetch(`${W}/api/status`,{headers:f()})).json()).sessions||[]}catch($){console.error(`Cannot reach ${W}: ${$.message}`),process.exit(1)}if(X.length===0){console.log("No sessions in CommHub.");return}let Z=X.filter(($)=>$.agent==="claude-code"&&$.project_dir);if(Z.length===0){console.log("No claude-code sessions found.");return}let Y=E[1],U=Y?Z.filter(($)=>$.alias===Y):Z;if(U.length===0){console.log(`No session found for "${Y}".`);return}let K=0;for(let $ of U){let _=$.project_dir,q=V(_,".anet","nodes",$.alias),J=V(q,"config.json");if(L(J)){console.log(` ⏭ ${$.alias} — already exists (${_})`);continue}if(!L(_)){console.log(` ⚠ ${$.alias} — project_dir not found: ${_}`);continue}let H={anet_version:"0.1.0",name:$.alias,runtime:"claude-code-cli",channels:["server:commhub"],env:{},flags:{dangerouslySkipPermissions:!0,teammateMode:"in-process"},session:$.resume_id};N(q,{recursive:!0}),M(J,JSON.stringify(H,null,2)+`
150
+ `)}async function uB(){let B=x(),W=A().hub||B.hub;if(!W)console.error("Run 'anet init' first"),process.exit(1);let X=[];try{X=(await(await fetch(`${W}/api/status`,{headers:m()})).json()).sessions||[]}catch($){console.error(`Cannot reach ${W}: ${$.message}`),process.exit(1)}if(X.length===0){console.log("No sessions in CommHub.");return}let Z=X.filter(($)=>$.agent==="claude-code"&&$.project_dir);if(Z.length===0){console.log("No claude-code sessions found.");return}let Y=E[1],U=Y?Z.filter(($)=>$.alias===Y):Z;if(U.length===0){console.log(`No session found for "${Y}".`);return}let K=0;for(let $ of U){let _=$.project_dir,q=V(_,".anet","nodes",$.alias),J=V(q,"config.json");if(L(J)){console.log(` ⏭ ${$.alias} — already exists (${_})`);continue}if(!L(_)){console.log(` ⚠ ${$.alias} — project_dir not found: ${_}`);continue}let H={anet_version:"0.1.0",name:$.alias,runtime:"claude-code-cli",channels:["server:commhub"],env:{},flags:{dangerouslySkipPermissions:!0,teammateMode:"in-process"},session:$.resume_id};N(q,{recursive:!0}),M(J,JSON.stringify(H,null,2)+`
151
151
  `),console.log(` ✅ ${$.alias} → ${_}/.anet/nodes/${$.alias}/config.json`),K++}console.log(`
152
- Imported ${K} session(s). Use: cd <project> && anet resume <alias>`)}function hB(){let B=E[1];if(B==="ls"||B==="list"||!B){let Q=process.cwd(),W=Q.replace(/\//g,"-"),X=V(F,".claude","projects",W);if(!L(X)){console.log(`No sessions for ${Q}`);return}let Z=j(X).filter((Y)=>Y.endsWith(".jsonl")).sort((Y,U)=>{let K=C(V(X,Y));return C(V(X,U)).mtimeMs-K.mtimeMs});if(Z.length===0){console.log("No sessions.");return}console.log(`
152
+ Imported ${K} session(s). Use: cd <project> && anet resume <alias>`)}function hB(){let B=E[1];if(B==="ls"||B==="list"||!B){let Q=process.cwd(),W=Q.replace(/\//g,"-"),X=V(F,".claude","projects",W);if(!L(X)){console.log(`No sessions for ${Q}`);return}let Z=k(X).filter((Y)=>Y.endsWith(".jsonl")).sort((Y,U)=>{let K=v(V(X,Y));return v(V(X,U)).mtimeMs-K.mtimeMs});if(Z.length===0){console.log("No sessions.");return}console.log(`
153
153
  Sessions in ${Q} (${Z.length} total):
154
- `),console.log(" SESSION ID SIZE MODIFIED"),console.log(" ────────────────────────────────────── ──────── ────────────────");for(let Y of Z){let U=Y.replace(".jsonl",""),K=C(V(X,Y)),$=K.size<1024?`${K.size}B`:K.size<1048576?`${(K.size/1024).toFixed(0)}KB`:`${(K.size/1024/1024).toFixed(1)}MB`,_=K.mtime.toISOString().replace("T"," ").slice(0,16);console.log(` ${U} ${$.padStart(8)} ${_}`)}console.log()}else console.log(`
154
+ `),console.log(" SESSION ID SIZE MODIFIED"),console.log(" ────────────────────────────────────── ──────── ────────────────");for(let Y of Z){let U=Y.replace(".jsonl",""),K=v(V(X,Y)),$=K.size<1024?`${K.size}B`:K.size<1048576?`${(K.size/1024).toFixed(0)}KB`:`${(K.size/1024/1024).toFixed(1)}MB`,_=K.mtime.toISOString().replace("T"," ").slice(0,16);console.log(` ${U} ${$.padStart(8)} ${_}`)}console.log()}else console.log(`
155
155
  anet session <command>
156
156
 
157
157
  ls List Claude Code sessions in current project
@@ -167,8 +167,8 @@ Options:
167
167
  Example:
168
168
  anet channel add telegram 指挥室 --bot-token 123:ABC --allow 7612221352
169
169
  anet channel add telegram 指挥室 # 交互式
170
- `);return}if(W!=="telegram")console.error(`P0 only supports telegram channels. Unsupported type: ${W}`),process.exit(1);k(X);let Z=R(X),Y=UB(X);if(!Z)console.error(`Node "${X}" not found. Create it first: anet create ${X} --runtime codex-sdk`),process.exit(1);let U=Q["bot-token"],K=Q.allow;if(!U)U=await G(`${W} Bot Token`);if(!K)K=await G("Allow User ID (发 @userinfobot 获取数字ID)","7612221352");if(D(),!U||!K)console.error("Error: bot-token and allow required"),process.exit(1);let $=_B(X,U,K);if(!Y)console.error(`Node "${X}" not found. Create it first: anet create ${X} --runtime codex-sdk`),process.exit(1);VB(Y,"telegram"),v(X,Y),console.log(`
171
- ✅ ${W} channel added to "${X}"`),console.log(` ${$}/`),console.log(" config.json updated")}else if(B==="ls"){let W=E[2],X=W?[W]:c(),Z=!1;for(let Y of X){let U=V(I(),Y,"channels");if(!L(U))continue;let K=j(U).filter(($)=>{try{return C(V(U,$)).isDirectory()}catch{return!1}});if(K.length===0)continue;if(!Z)console.log(`
170
+ `);return}if(W!=="telegram")console.error(`P0 only supports telegram channels. Unsupported type: ${W}`),process.exit(1);y(X);let Z=R(X),Y=UB(X);if(!Z)console.error(`Node "${X}" not found. Create it first: anet create ${X} --runtime codex-sdk`),process.exit(1);let U=Q["bot-token"],K=Q.allow;if(!U)U=await G(`${W} Bot Token`);if(!K)K=await G("Allow User ID (发 @userinfobot 获取数字ID)","7612221352");if(P(),!U||!K)console.error("Error: bot-token and allow required"),process.exit(1);let $=_B(X,U,K);if(!Y)console.error(`Node "${X}" not found. Create it first: anet create ${X} --runtime codex-sdk`),process.exit(1);VB(Y,"telegram"),j(X,Y),console.log(`
171
+ ✅ ${W} channel added to "${X}"`),console.log(` ${$}/`),console.log(" config.json updated")}else if(B==="ls"){let W=E[2],X=W?[W]:c(),Z=!1;for(let Y of X){let U=V(I(),Y,"channels");if(!L(U))continue;let K=k(U).filter(($)=>{try{return v(V(U,$)).isDirectory()}catch{return!1}});if(K.length===0)continue;if(!Z)console.log(`
172
172
  Node Channels:
173
173
  `),Z=!0;for(let $ of K){let _=V(U,$,"access.json"),q="";if(L(_))try{q=JSON.parse(z(_,"utf-8")).allowFrom?.join(", ")||""}catch{}console.log(` ${Y.padEnd(20)} ${$.padEnd(12)} allow: ${q||"(none)"}`)}}if(!Z)console.log("No channels. Add one: anet channel add telegram <node-id>");console.log()}else console.log(`
174
174
  anet channel <command>
@@ -177,4 +177,8 @@ anet channel <command>
177
177
  ls [node-id] List channels
178
178
 
179
179
  Data: .anet/nodes/<node-id>/channels/<type>/
180
- `)}switch(S){case"init":if(E[1]==="project")IB();else if(E[1]==="profile")await RB();else await OB();break;case"create":await qB();break;case"server":SB();break;case"start":WB();break;case"resume":vB();break;case"import":uB();break;case"channel":gB();break;case"session":hB();break;case"ls":case"list":kB();break;case"run":yB();break;case"-v":case"--version":case"version":{let B=JSON.parse(z(V(new URL(".",import.meta.url).pathname,"..","..","package.json"),"utf-8"));console.log(`anet v${B.version}`);break}case"--help":case"-h":case void 0:QB();break;default:if(R(S))E.unshift("start"),WB();else console.error(`Unknown: ${S}`),QB(),process.exit(1)}
180
+ `)}function fB(){console.log(`[anet] Upgrading all packages...
181
+ `);try{console.log("1/3 Updating @sleep2agi/agent-network..."),D("npm install -g @sleep2agi/agent-network@latest",{stdio:"inherit"})}catch{console.log(" ⚠ Failed to update agent-network")}try{console.log(`
182
+ 2/3 Updating @sleep2agi/agent-node...`),D("npm install -g @sleep2agi/agent-node@latest",{stdio:"inherit"})}catch{console.log(" ⚠ Failed to update agent-node")}try{console.log(`
183
+ 3/3 Clearing npx cache...`),D("rm -rf ~/.npm/_npx",{stdio:"inherit"}),console.log(" ✅ npx cache cleared")}catch{}console.log(`
184
+ ✅ Done. Check versions:`);try{D("anet -v",{stdio:"inherit"})}catch{}try{let Q=D("npm ls -g @sleep2agi/agent-node --depth=0 2>/dev/null",{encoding:"utf-8"}).match(/@sleep2agi\/agent-node@([\d.]+)/);if(Q)console.log(`agent-node v${Q[1]}`)}catch{}}switch(u){case"init":if(E[1]==="project")IB();else if(E[1]==="profile")await RB();else await OB();break;case"create":await qB();break;case"server":SB();break;case"start":WB();break;case"resume":vB();break;case"import":uB();break;case"channel":gB();break;case"upgrade":fB();break;case"session":hB();break;case"ls":case"list":kB();break;case"run":yB();break;case"-v":case"--version":case"version":{let B=JSON.parse(z(V(new URL(".",import.meta.url).pathname,"..","..","package.json"),"utf-8"));console.log(`anet v${B.version}`);break}case"--help":case"-h":case void 0:QB();break;default:if(R(u))E.unshift("start"),WB();else console.error(`Unknown: ${u}`),QB(),process.exit(1)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
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",