@sleep2agi/agent-network 1.0.2 → 1.1.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 +55 -52
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`);K=
|
|
4
|
-
`)}function
|
|
5
|
-
`)}function
|
|
6
|
-
`)}function
|
|
2
|
+
var AB=Object.defineProperty;var FB=(B)=>B;function OB(B,Q){this[B]=FB.bind(null,Q)}var IB=(B,Q)=>{for(var W in Q)AB(B,W,{get:Q[W],enumerable:!0,configurable:!0,set:OB.bind(Q,W)})};var RB=(B,Q)=>()=>(B&&(Q=B(B=0)),Q);var BB={};IB(BB,{default:()=>xB,CommHub:()=>g});import{EventEmitter as DB}from"events";import{hostname as e}from"os";var g,xB;var QB=RB(()=>{g=class g extends DB{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 Y=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(),Z=Y.match(/data: (.+)/),$=Z?JSON.parse(Z[1]):JSON.parse(Y),K=$?.result?.content?.[0]?.text;return K?JSON.parse(K):$}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:e(),hostname:e(),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 Y=await fetch(Q,{headers:X,signal:this.sseAbort.signal});if(!Y.ok||!Y.body){this.log(`SSE failed: ${Y.status}`),await this.sleep(W),W=Math.min(W*1.5,60000);continue}W=this.reconnectDelay;let Z=Y.body.getReader(),$=new TextDecoder,K="";while(this.running){let{done:U,value:q}=await Z.read();if(U)break;K+=$.decode(q,{stream:!0});let E=K.split(`
|
|
3
|
+
`);K=E.pop()||"";for(let J of E){if(!J.startsWith("data: "))continue;try{let L=JSON.parse(J.slice(6));if(L.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(L.type==="new_task"||L.type==="new_message"||L.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))}};xB=g});import{chmodSync as PB,readFileSync as T,writeFileSync as z,existsSync as M,mkdirSync as F,readdirSync as h,statSync as y}from"fs";import{join as _}from"path";import{spawn as p,execSync as w}from"child_process";import{createInterface as uB}from"readline";var H=process.argv.slice(2),f=H[0],O=process.env.HOME||process.env.USERPROFILE||"~";function KB(){return _(O,".anet","config.json")}function c(){return _(O,".anet","server","config.json")}function R(){return _(process.cwd(),".anet","nodes")}function _B(){return A().token||process.env.COMMHUB_TOKEN||x().token||""}function l(B){let Q=B||_B();return Q?{Authorization:`Bearer ${Q}`}:{}}function x(){let B=KB();if(M(B))try{return JSON.parse(T(B,"utf-8"))}catch{}return{}}function qB(B){let Q=_(O,".anet");F(Q,{recursive:!0}),z(_(Q,"config.json"),JSON.stringify(B,null,2)+`
|
|
4
|
+
`)}function WB(){let B=c();if(M(B))try{return JSON.parse(T(B,"utf-8"))}catch{}return{}}function XB(B){let Q=_(O,".anet","server");F(Q,{recursive:!0}),z(_(Q,"config.json"),JSON.stringify(B,null,2)+`
|
|
5
|
+
`)}function V(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 V(Q.runtime||"claude-code-cli")}function EB(B,Q){return Q?.name||Q?.alias||B}function b(B){return B.session||B.resume||""}function wB(B){return B.normalize("NFC")}function u(B){if(B!==wB(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 D(B){let Q=_(R(),B,"config.json");if(!M(Q))return null;try{let W=JSON.parse(T(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 HB(B){let Q=_(R(),B,"config.json");if(!M(Q))return null;try{let W=JSON.parse(T(Q,"utf-8"));return{...W,channels:Array.isArray(W.channels)?W.channels:[],env:{...W.env},flags:{...W.flags}}}catch{return null}}function k(B,Q){let W=_(R(),B);F(W,{recursive:!0}),z(_(W,"config.json"),JSON.stringify(Q,null,2)+`
|
|
6
|
+
`)}function r(){let B=R();if(!M(B))return[];return h(B).filter((Q)=>M(_(B,Q,"config.json")))}function A(){let B={_channels:[],_envs:[]};for(let Q=0;Q<H.length;Q++){if(H[Q]==="--channel"&&H[Q+1]){B._channels.push(H[++Q]);continue}if(H[Q]==="--env"&&H[Q+1]){B._envs.push(H[++Q]);continue}if(H[Q].startsWith("--")&&H[Q+1]&&!H[Q+1].startsWith("--"))B[H[Q].slice(2)]=H[++Q];else if(H[Q].startsWith("--"))B[H[Q].slice(2)]="true"}return B}function d(B){try{return w(`command -v ${JSON.stringify(B)}`,{stdio:"ignore",shell:"/bin/bash"}),!0}catch{return!1}}function NB(){return _(new URL(".",import.meta.url).pathname,"..","..","package.json")}function N(B){let Q=B.match(/(?:^|[^0-9])v?(\d+)\.(\d+)\.(\d+)(?:[^0-9]|$)/);if(!Q)return null;return{major:Number.parseInt(Q[1],10),minor:Number.parseInt(Q[2],10),patch:Number.parseInt(Q[3],10)}}function YB(B,Q){if(B.major!==Q.major)return B.major>Q.major?1:-1;if(B.minor!==Q.minor)return B.minor>Q.minor?1:-1;if(B.patch!==Q.patch)return B.patch>Q.patch?1:-1;return 0}function v(B,Q,W){if(!d(B))return{name:B,displayName:Q,version:null,state:"not-installed",source:W};try{let X=w(`${JSON.stringify(B)} --version`,{encoding:"utf-8",stdio:["ignore","pipe","pipe"],shell:"/bin/bash",timeout:5000}).trim(),Y=N(X);if(!Y)return{name:B,displayName:Q,version:null,state:"unknown",source:W};return{name:B,displayName:Q,version:`${Y.major}.${Y.minor}.${Y.patch}`,state:"ok",source:W}}catch{return{name:B,displayName:Q,version:null,state:"unknown",source:W}}}function ZB(B,Q,W="global"){try{let X=w(`npm ls -g ${JSON.stringify(B)} --depth=0 --json`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"],shell:"/bin/bash"}),Z=JSON.parse(X)?.dependencies?.[B]?.version;if(!Z)return{name:B,displayName:Q,version:null,state:"unknown",source:W};let $=N(Z);if(!$)return{name:B,displayName:Q,version:null,state:"unknown",source:W};return{name:B,displayName:Q,version:`${$.major}.${$.minor}.${$.patch}`,state:"ok",source:W}}catch{return{name:B,displayName:Q,version:null,state:"not-installed",source:W}}}function LB(){let Q={anet:{name:"anet",displayName:"anet",version:JSON.parse(T(NB(),"utf-8")).version,state:"ok"},agentNode:v("agent-node","agent-node","global"),commhubServer:v("commhub-server","commhub-server","global"),claude:v("claude","claude CLI"),codex:v("codex","codex CLI")};if(Q.agentNode.state!=="ok")Q.agentNode=ZB("@sleep2agi/agent-node","agent-node","global");if(Q.commhubServer.state!=="ok")Q.commhubServer=ZB("@sleep2agi/commhub-server","commhub-server","global");return Q}function S(B){let Q=B.source?` (${B.source})`:"";if(B.state==="ok"&&B.version)return`${B.displayName} v${B.version}${Q}`;if(B.state==="unknown")return`${B.displayName} installed (version unknown)${Q}`;return`${B.displayName} not installed`}function CB(){let B=LB();console.log(`anet v${B.anet.version}`),console.log(S(B.agentNode)),console.log(S(B.commhubServer)),console.log(S(B.claude)),console.log(S(B.codex))}function bB(B){if(B!=="codex-sdk"&&B!=="claude-agent-sdk")return;let Q=LB(),W=N("1.0.0"),X=N("0.4.0");if(Q.agentNode.state!=="ok"||!Q.agentNode.version)console.error("[anet] agent-node is not installed or cannot report a version."),console.error("[anet] Run: anet upgrade"),process.exit(1);let Y=N(Q.agentNode.version);if(!Y||YB(Y,W)<0)console.error("[anet] Incompatible package versions."),console.error(`[anet] anet v${Q.anet.version} requires agent-node >= 1.0.0, but found agent-node v${Q.agentNode.version}.`),console.error("[anet] Run: anet upgrade"),process.exit(1);if(Q.commhubServer.state==="ok"&&Q.commhubServer.version){let Z=N(Q.commhubServer.version);if(Z&&YB(Z,X)<0)console.warn(`[anet] Warning: local commhub-server v${Q.commhubServer.version} is older than recommended >= 0.4.0.`),console.warn("[anet] If this machine hosts CommHub, run: anet upgrade")}}function a(){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 n(B,Q){if(B==="claude-code-cli"){if(!d("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")a();return}if(!d("agent-node"))console.warn("[anet] Warning: agent-node not found in PATH."),console.warn("[anet] Run: anet upgrade")}function $B(){console.log(`
|
|
7
7
|
anet — AI Agent Network CLI
|
|
8
8
|
|
|
9
9
|
anet init Configure hub URL (global, once)
|
|
@@ -24,12 +24,12 @@ Quick start:
|
|
|
24
24
|
anet init --hub http://IP:9200
|
|
25
25
|
anet create 指挥室
|
|
26
26
|
anet start 指挥室
|
|
27
|
-
`)}async function
|
|
28
|
-
Saved to ${
|
|
29
|
-
`);try{
|
|
30
|
-
`;if(
|
|
31
|
-
`;
|
|
32
|
-
`),console.log(".mcp.json: commhub → .anet/node-server.ts");else console.log(".mcp.json: commhub already set");let
|
|
27
|
+
`)}async function jB(){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 Z=await(await fetch(`${Q}/health`,{headers:W?{Authorization:`Bearer ${W}`}:{}})).json();console.log(`✅ CommHub v${Z.version} — ${Z.sessions} sessions, ${Z.sse_connections} SSE`)}catch(Y){console.error(`❌ Cannot reach ${Q}: ${Y.message}`),process.exit(1)}let X=x();if(X.hub=Q,W)X.token=W;else if(!X.token)delete X.token;qB(X),console.log(`
|
|
28
|
+
Saved to ${KB()}`),console.log("Next: anet init project")}async function vB(){let B=x(),Q=B.hub;if(!Q)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let W=_(process.cwd(),".anet");F(W,{recursive:!0});let X=_(W,"node-server.ts");if(!M(X)){let J=[_(new URL(".",import.meta.url).pathname,"..","..","src","node-server.ts"),_(new URL(".",import.meta.url).pathname,"..","src","node-server.ts"),_(process.argv[1],"..","..","src","node-server.ts")],L=!1;for(let I of J)if(M(I)){z(X,T(I,"utf-8")),console.log(" ✅ .anet/node-server.ts"),L=!0;break}if(!L)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 Y=_(W,"package.json");if(!M(Y)){z(Y,JSON.stringify({private:!0,dependencies:{"@modelcontextprotocol/sdk":"^1.12.0"}},null,2)+`
|
|
29
|
+
`);try{w("bun install",{cwd:W,stdio:"pipe"}),console.log(" ✅ Dependencies installed")}catch{console.log(" ⚠️ Run: cd .anet && bun install")}}let Z=_(W,".env"),$=B.token||"",K=`COMMHUB_URL=${Q}
|
|
30
|
+
`;if($)K+=`COMMHUB_TOKEN=${$}
|
|
31
|
+
`;z(Z,K),console.log(`CommHub URL: ${Q}${$?" (with token)":""}`);let U=_(process.cwd(),".mcp.json"),q={};if(M(U))try{q=JSON.parse(T(U,"utf-8"))}catch{}if(!q.mcpServers?.commhub)q.mcpServers=q.mcpServers||{},q.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},z(U,JSON.stringify(q,null,2)+`
|
|
32
|
+
`),console.log(".mcp.json: commhub → .anet/node-server.ts");else console.log(".mcp.json: commhub already set");let E=_(process.cwd(),"CLAUDE.md");if(!M(E))z(E,`# Agent Network (CommHub)
|
|
33
33
|
|
|
34
34
|
## 通信方式
|
|
35
35
|
|
|
@@ -68,68 +68,68 @@ commhub_get_all_status()
|
|
|
68
68
|
- 回复指挥室用 commhub_send_task(不是 commhub_reply,reply 不推送)
|
|
69
69
|
- 不要猜 alias,用 get_all_status 查
|
|
70
70
|
`),console.log("CLAUDE.md: created");else console.log("CLAUDE.md: already exists");console.log(`
|
|
71
|
-
✅ Project ready. Next: anet create <node-name>`)}async function
|
|
72
|
-
`),await
|
|
73
|
-
`),
|
|
74
|
-
`);try{
|
|
75
|
-
`),X}async function
|
|
76
|
-
[anet] Config summary:`),console.log(JSON.stringify(W,null,2))}async function
|
|
71
|
+
✅ Project ready. Next: anet create <node-name>`)}async function SB(){console.warn("[deprecated] anet init profile is now anet create."),console.warn(` Run: anet create <node-name> [--runtime ...]
|
|
72
|
+
`),await GB(H[2])}function s(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 Y={};for(let U of Q._envs){let q=U.indexOf("=");if(q>0)Y[U.slice(0,q)]=U.slice(q+1)}let Z=V(Q.runtime||"claude-code-cli"),$=Z==="codex-sdk"?"gpt-5.4":void 0;return{anet_version:"0.1.0",name:B,runtime:Z,...Q.hub?{hub:X}:{},...Q.model||$?{model:Q.model||$}:{},...Q.tools?{tools:Q.tools.split(",").map((U)=>U.trim())}:{},channels:Q._channels.length>0?Q._channels:["server:commhub"],env:Y,flags:{dangerouslySkipPermissions:!0,...Z==="claude-code-cli"?{teammateMode:Q["teammate-mode"]||"in-process"}:{},...Q["max-turns"]?{maxTurns:parseInt(Q["max-turns"])}:{}},...Q.session?{session:Q.session}:{}}}function MB(B,Q){let W=_(O,".claude","channels","commhub"),X=process.cwd().replace(/\//g,"-"),Y=_(W,X);F(Y,{recursive:!0}),z(_(Y,".env"),`COMMHUB_ALIAS=${B}
|
|
73
|
+
`),k(B,Q)}function JB(B,Q){if(B.channels=B.channels||[],!B.channels.includes(Q))B.channels.push(Q)}function zB(B,Q,W){let X=_(R(),B,"channels","telegram");F(X,{recursive:!0}),F(_(X,"inbox"),{recursive:!0});let Y=_(X,".env");z(Y,`TELEGRAM_BOT_TOKEN=${Q}
|
|
74
|
+
`);try{PB(Y,384)}catch{}return z(_(X,"access.json"),JSON.stringify({dmPolicy:"allowlist",allowFrom:[W],groups:{},pending:{}},null,2)+`
|
|
75
|
+
`),X}async function m(B,Q){console.log(B),Q.forEach((W,X)=>{let Y=W.description?` ${W.description}`:"";console.log(` ${X+1}) ${W.label}${Y}`)});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 yB(B){let Q={};for(let[W,X]of Object.entries(B)){let Y=/TOKEN|KEY|SECRET|PASSWORD/i.test(W);Q[W]=Y&&X?`${X.slice(0,4)}...`:X}return Q}function kB(B,Q){let W={name:EB(B,Q),runtime:V(Q),model:Q.model||"(runtime default)",session:b(Q)||"(new)",channels:Q.channels,env:yB(Q.env||{}),config:_(R(),B,"config.json")};console.log(`
|
|
76
|
+
[anet] Config summary:`),console.log(JSON.stringify(W,null,2))}async function hB(){console.log(`
|
|
77
77
|
[anet] Create a node
|
|
78
78
|
|
|
79
79
|
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)
|
|
83
|
+
`);let B=await G("Node name");if(!B)P(),console.error("Error: node-name required"),process.exit(1);if(u(B),D(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
|
|
89
|
+
`);let Q=await m("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
|
|
94
|
+
`);let $=await m("Select model:",[{label:"gpt-5.4",value:"gpt-5.4"},{label:"o3",value:"o3"},{label:"custom",value:"__custom__"}]);W.model=$==="__custom__"?await G("Model"):$}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
|
|
100
|
+
`);let $=await m("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=$==="__custom__"?await G("Model"):$,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.
|
|
104
104
|
- Intern-S1: use the key/token for chat.intern-ai.org.cn.
|
|
105
105
|
- Anthropic Claude: use an Anthropic Console API key.
|
|
106
|
-
`);let K=await G("ANTHROPIC_AUTH_TOKEN");if(K)W._envs.push(`ANTHROPIC_AUTH_TOKEN=${K}`)}let X=
|
|
106
|
+
`);let K=await G("ANTHROPIC_AUTH_TOKEN");if(K)W._envs.push(`ANTHROPIC_AUTH_TOKEN=${K}`)}let X=s(B,W),Y=await G("Add Telegram channel? (y/n)","n"),Z=null;if(/^y(es)?$/i.test(Y)){console.log(`
|
|
107
107
|
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
|
|
112
|
-
[anet] Created node "${B}" (${
|
|
113
|
-
Start: anet start ${B}`)}async function
|
|
114
|
-
[anet] Created node "${Q}" (${
|
|
115
|
-
Start: anet start ${Q}`)}var
|
|
116
|
-
`);try{
|
|
117
|
-
`),console.log("[anet] .mcp.json: added commhub");let
|
|
118
|
-
`;if(
|
|
119
|
-
`;
|
|
120
|
-
`),console.log("[anet] .mcp.json: added commhub channel server")}async function
|
|
121
|
-
`),
|
|
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&&
|
|
123
|
-
`),await
|
|
111
|
+
`);let $=await G("Telegram Bot Token"),K=await G("Allow User ID (numeric ID from @userinfobot)","7612221352");if(!$)P(),console.error("Error: Telegram Bot Token required"),process.exit(1);Z={botToken:$,allowId:K},JB(X,"telegram")}if(P(),MB(B,X),Z)zB(B,Z.botToken,Z.allowId);if(n(V(X),"create"),console.log(`
|
|
112
|
+
[anet] Created node "${B}" (${V(X)})`),Z)console.log("[anet] ✅ Telegram channel added");if(V(X)==="claude-code-cli")a();console.log("[anet] ⚠ dangerouslySkipPermissions and teammateMode enabled by default."),console.log(`[anet] To disable: edit .anet/nodes/${B}/config.json → flags`),kB(B,D(B)||X),console.log(`
|
|
113
|
+
Start: anet start ${B}`)}async function GB(B){let Q=B||H[1];if(!Q)return hB();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(u(Q),D(Q))console.error(`Node "${Q}" already exists: .anet/nodes/${Q}/config.json`),process.exit(1);let W=A(),X=s(Q,W);if(MB(Q,X),n(V(X),"create"),console.log(`
|
|
114
|
+
[anet] Created node "${Q}" (${V(X)})`),V(X)==="claude-code-cli")a();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 C=null;function gB(){if(!C)C=uB({input:process.stdin,output:process.stdout});return C}function P(){if(C)C.close(),C=null}function G(B,Q){let W=Q?` [${Q}]`:"";return new Promise((X)=>{gB().question(`${B}${W}: `,(Y)=>{X(Y.trim()||Q||"")})})}function fB(B){if(V(B)!=="claude-code-cli")return;if(!B.channels?.some((J)=>J.includes("commhub")))return;let Q=_(process.cwd(),".mcp.json"),W={};if(M(Q))try{W=JSON.parse(T(Q,"utf-8"))}catch{}let X=_(process.cwd(),".anet"),Y=_(X,"node-server.ts"),Z=[_(new URL(".",import.meta.url).pathname,"..","..","src","node-server.ts"),_(new URL(".",import.meta.url).pathname,"..","src","node-server.ts"),_(process.argv[1],"..","..","src","node-server.ts")];for(let J of Z)if(M(J)){F(X,{recursive:!0});let L=T(J,"utf-8"),I=M(Y)?T(Y,"utf-8"):"";if(L!==I)z(Y,L),console.log("[anet] Updated .anet/node-server.ts");break}let $=_(X,"package.json");if(!M($)){F(X,{recursive:!0}),z($,JSON.stringify({private:!0,dependencies:{"@modelcontextprotocol/sdk":"^1.12.0"}},null,2)+`
|
|
116
|
+
`);try{w("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"]},z(Q,JSON.stringify(W,null,2)+`
|
|
117
|
+
`),console.log("[anet] .mcp.json: added commhub");let U=_(X,".env"),q=B.token||"",E=`COMMHUB_URL=${B.hub||"http://127.0.0.1:9200"}
|
|
118
|
+
`;if(q)E+=`COMMHUB_TOKEN=${q}
|
|
119
|
+
`;z(U,E),W.mcpServers=W.mcpServers||{},W.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},z(Q,JSON.stringify(W,null,2)+`
|
|
120
|
+
`),console.log("[anet] .mcp.json: added commhub channel server")}async function t(B,Q=!1){let W=D(B);if(!W)console.error(`Node "${B}" not found. Create it first: anet create ${B}`),process.exit(1);let X=V(W),Y=EB(B,W),Z=b(W),$=!!Z&&!Q,K=$?`Resuming session ${Z.slice(0,8)}...`:"Starting new session";console.log(`[anet] ${K} for "${B}" [${X}]...
|
|
121
|
+
`),n(X,"start"),bB(X),fB(W);let U=W.token||"";if(U)console.log(`[anet] Token: ${U.slice(0,8)}...`);else console.log("[anet] Warning: no token configured. Check ~/.anet/config.json");if(X==="codex-sdk"||X==="claude-agent-sdk"){let q=["--config",_(R(),B,"config.json"),"--alias",Y];if(Q)q.push("--new-session","true");let E={...process.env,...U?{COMMHUB_TOKEN:U}:{}};for(let[L,I]of Object.entries(W.env))E[L]=I.replace(/^~/,O);p("agent-node",q,{env:E,stdio:"inherit",shell:!0}).on("exit",(L)=>process.exit(L||0))}else{let q={...process.env,COMMHUB_ALIAS:W.alias,...U?{COMMHUB_TOKEN:U}:{}};for(let[L,I]of Object.entries(W.env))q[L]=I.replace(/^~/,O);if(W.channels.includes("telegram"))q.TELEGRAM_STATE_DIR=_(R(),B,"channels","telegram");let E=[];if(W.flags.dangerouslySkipPermissions)E.push("--dangerously-skip-permissions");for(let L of W.channels)if(L.startsWith("server:"))E.push("--dangerously-load-development-channels",L);else if(L==="telegram")E.push("--channels","plugin:telegram@claude-plugins-official");else E.push("--channels",L);if(W.flags.teammateMode)E.push("--teammate-mode",W.flags.teammateMode);if($)E.push("--resume",Z);E.push("-n",Y),p("claude",E,{env:q,stdio:"inherit",shell:!0}).on("exit",(L)=>{if(!$||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&&Z)console.log(`[anet] Next "anet start ${B}" will still resume ${Z.slice(0,8)}... until you rebind.`)}process.exit(L||0)})}}async function UB(){let B=H[1];if(!B){pB("start");return}await t(B,!!A()["new-session"])}async function mB(){let B=H[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=D(B),W=A(),X=W.session;if(!X){console.warn("[deprecated] anet resume <node-name> without --session is now anet start <node-name>."),await t(B,!1);return}if(u(B),!Q){let Y={...W,session:X,runtime:W.runtime||"claude-code-cli"};Q=s(B,Y),k(B,Q),console.log(`[anet] Created node "${B}"`)}else{let Y=b(Q);if(Y&&Y!==X&&W.yes!=="true"){let $=await G(`[anet] ${B} already has session ${Y.slice(0,8)}..., overwrite? (y/n)`,"n");if(P(),!/^y(es)?$/i.test($)){console.log("[anet] Session unchanged.");return}}let Z=HB(B)||Q;Z.session=X,delete Z.resume,delete Z.resumeAlias,k(B,Z)}console.log(`[anet] Saved session ${X.slice(0,8)}... to .anet/nodes/${B}/config.json
|
|
123
|
+
`),await t(B,!1)}function pB(B){let Q=r();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=
|
|
125
|
+
`);for(let W of Q){let X=D(W);console.log(` ${W} [${V(X||void 0)}] session=${X?b(X).slice(0,8)||"-":"-"} channels=[${X?.channels.join(", ")}]`)}console.log(`
|
|
126
126
|
anet ${B} <node-name>
|
|
127
|
-
`)}async function
|
|
127
|
+
`)}async function cB(){let B=r();if(B.length>0){console.log(`
|
|
128
128
|
Nodes:
|
|
129
|
-
`);for(let K of B){let
|
|
130
|
-
`);return}let
|
|
131
|
-
`),console.log(" SESSION PID NETWORK"),console.log(" ──────────────────── ─────── ─────────────────────");for(let K of X){let
|
|
132
|
-
`);
|
|
129
|
+
`);for(let K of B){let U=D(K),q=U?b(U).slice(0,8)||"-":"-";console.log(` ${K} [${V(U||void 0)}] session=${q} channels=[${U?.channels.join(", ")}]`)}console.log()}let Q=process.cwd(),W=_(O,".claude","sessions"),X=[];if(M(W))for(let K of h(W).filter((U)=>U.endsWith(".json")))try{let U=JSON.parse(T(_(W,K),"utf-8"));if(U.cwd===Q)X.push(U)}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 Y=x(),Z=[],$={};if(Y.hub)try{let[K,U]=await Promise.all([fetch(`${Y.hub}/api/status`,{headers:l()}).then((q)=>q.json()),fetch(`${Y.hub}/health`,{headers:l()}).then((q)=>q.json())]);Z=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 U=K.sessionId.slice(0,18),q=!1;try{process.kill(K.pid,0),q=!0}catch{}let E="(not in network)",J=Q.replace(/\//g,"-"),L=_(O,".claude","channels","commhub",J,".env");if(M(L)){let i=T(L,"utf-8").match(/COMMHUB_ALIAS=(.+)/);if(i){let j=i[1].trim(),o=Z.find((VB)=>VB.alias===j),TB=$[j]?"●":"○";E=o?`${j} ${o.status} ${TB}`:`${j} (not registered)`}}console.log(` ${U} ${(q?`${K.pid}`:`${K.pid}✕`).padEnd(7)} ${E}`)}console.log()}}async function lB(){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:Y}=await Promise.resolve().then(() => (QB(),BB)),Z=new Y({url:W,alias:X});Z.on("task",async($)=>{console.log(`[${X}] ← ${$.from_session}: ${$.content.slice(0,100)}`),await Z.send($.from_session,`[${X}] 收到: ${$.content.slice(0,200)}`)}),Z.on("connected",()=>console.log(`[${X}] Connected`)),Z.on("disconnected",()=>console.log(`[${X}] Reconnecting...`)),process.on("SIGINT",()=>Z.disconnect().then(()=>process.exit(0))),console.log(`[${X}] Listening on ${W}`)}async function dB(){let B=H[1];if(B==="start"){let Q=A(),W=WB(),X=Q.port||W.port||"9200",Y=Q.host||W.host||"0.0.0.0",Z=Q.token||W.token||_B();if(!Z)Z=crypto.randomUUID().replace(/-/g,""),console.log(`[anet] Generated auth token: ${Z}`),console.log(`[anet] Save this token — agents need it to connect.
|
|
132
|
+
`);XB({port:X,host:Y,token:Z});let $=x();if(!$.token)$.token=Z,qB($);console.log(`[anet] Starting CommHub Server on ${Y}:${X}${Z?" (auth enabled)":""}...`);let K={...process.env,PORT:X,HOST:Y};if(Z)K.COMMHUB_AUTH_TOKEN=Z;p("bunx",["@sleep2agi/commhub-server"],{env:K,stdio:"inherit",shell:!0}).on("exit",(q)=>process.exit(q||0))}else if(B==="config"){let Q=A(),W=WB();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)XB(W),console.log(`Server config saved: ${c()}`);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,22 +140,22 @@ Options:
|
|
|
140
140
|
--host <host> Bind address (default: 0.0.0.0)
|
|
141
141
|
--token <token> Auth token
|
|
142
142
|
|
|
143
|
-
Config: ${
|
|
143
|
+
Config: ${c()}
|
|
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
|
|
151
|
-
`),console.log(` ✅ ${
|
|
152
|
-
Imported ${K} session(s). Use: cd <project> && anet resume <alias>`)}function
|
|
153
|
-
Sessions in ${Q} (${
|
|
154
|
-
`),console.log(" SESSION ID SIZE MODIFIED"),console.log(" ────────────────────────────────────── ──────── ────────────────");for(let
|
|
150
|
+
`)}async function tB(){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:l()})).json()).sessions||[]}catch(U){console.error(`Cannot reach ${W}: ${U.message}`),process.exit(1)}if(X.length===0){console.log("No sessions in CommHub.");return}let Y=X.filter((U)=>U.agent==="claude-code"&&U.project_dir);if(Y.length===0){console.log("No claude-code sessions found.");return}let Z=H[1],$=Z?Y.filter((U)=>U.alias===Z):Y;if($.length===0){console.log(`No session found for "${Z}".`);return}let K=0;for(let U of $){let q=U.project_dir,E=_(q,".anet","nodes",U.alias),J=_(E,"config.json");if(M(J)){console.log(` ⏭ ${U.alias} — already exists (${q})`);continue}if(!M(q)){console.log(` ⚠ ${U.alias} — project_dir not found: ${q}`);continue}let L={anet_version:"0.1.0",name:U.alias,runtime:"claude-code-cli",channels:["server:commhub"],env:{},flags:{dangerouslySkipPermissions:!0,teammateMode:"in-process"},session:U.resume_id};F(E,{recursive:!0}),z(J,JSON.stringify(L,null,2)+`
|
|
151
|
+
`),console.log(` ✅ ${U.alias} → ${q}/.anet/nodes/${U.alias}/config.json`),K++}console.log(`
|
|
152
|
+
Imported ${K} session(s). Use: cd <project> && anet resume <alias>`)}function rB(){let B=H[1];if(B==="ls"||B==="list"||!B){let Q=process.cwd(),W=Q.replace(/\//g,"-"),X=_(O,".claude","projects",W);if(!M(X)){console.log(`No sessions for ${Q}`);return}let Y=h(X).filter((Z)=>Z.endsWith(".jsonl")).sort((Z,$)=>{let K=y(_(X,Z));return y(_(X,$)).mtimeMs-K.mtimeMs});if(Y.length===0){console.log("No sessions.");return}console.log(`
|
|
153
|
+
Sessions in ${Q} (${Y.length} total):
|
|
154
|
+
`),console.log(" SESSION ID SIZE MODIFIED"),console.log(" ────────────────────────────────────── ──────── ────────────────");for(let Z of Y){let $=Z.replace(".jsonl",""),K=y(_(X,Z)),U=K.size<1024?`${K.size}B`:K.size<1048576?`${(K.size/1024).toFixed(0)}KB`:`${(K.size/1024/1024).toFixed(1)}MB`,q=K.mtime.toISOString().replace("T"," ").slice(0,16);console.log(` ${$} ${U.padStart(8)} ${q}`)}console.log()}else console.log(`
|
|
155
155
|
anet session <command>
|
|
156
156
|
|
|
157
157
|
ls List Claude Code sessions in current project
|
|
158
|
-
`)}async function
|
|
158
|
+
`)}async function aB(){let B=H[1],Q=A();if(B==="add"){let W=H[2],X=H[3];if(!W||!X){console.log(`
|
|
159
159
|
anet channel add <type> <node-id> [options]
|
|
160
160
|
|
|
161
161
|
Types: telegram
|
|
@@ -167,14 +167,17 @@ 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);
|
|
171
|
-
✅ ${W} channel added to "${X}"`),console.log(` ${
|
|
170
|
+
`);return}if(W!=="telegram")console.error(`P0 only supports telegram channels. Unsupported type: ${W}`),process.exit(1);u(X);let Y=D(X),Z=HB(X);if(!Y)console.error(`Node "${X}" not found. Create it first: anet create ${X} --runtime codex-sdk`),process.exit(1);let $=Q["bot-token"],K=Q.allow;if(!$)$=await G(`${W} Bot Token`);if(!K)K=await G("Allow User ID (发 @userinfobot 获取数字ID)","7612221352");if(P(),!$||!K)console.error("Error: bot-token and allow required"),process.exit(1);let U=zB(X,$,K);if(!Z)console.error(`Node "${X}" not found. Create it first: anet create ${X} --runtime codex-sdk`),process.exit(1);JB(Z,"telegram"),k(X,Z),console.log(`
|
|
171
|
+
✅ ${W} channel added to "${X}"`),console.log(` ${U}/`),console.log(" config.json updated")}else if(B==="ls"){let W=H[2],X=W?[W]:r(),Y=!1;for(let Z of X){let $=_(R(),Z,"channels");if(!M($))continue;let K=h($).filter((U)=>{try{return y(_($,U)).isDirectory()}catch{return!1}});if(K.length===0)continue;if(!Y)console.log(`
|
|
172
172
|
Node Channels:
|
|
173
|
-
`),
|
|
173
|
+
`),Y=!0;for(let U of K){let q=_($,U,"access.json"),E="";if(M(q))try{E=JSON.parse(T(q,"utf-8")).allowFrom?.join(", ")||""}catch{}console.log(` ${Z.padEnd(20)} ${U.padEnd(12)} allow: ${E||"(none)"}`)}}if(!Y)console.log("No channels. Add one: anet channel add telegram <node-id>");console.log()}else console.log(`
|
|
174
174
|
anet channel <command>
|
|
175
175
|
|
|
176
176
|
add <type> <node-id> Add channel to a node
|
|
177
177
|
ls [node-id] List channels
|
|
178
178
|
|
|
179
179
|
Data: .anet/nodes/<node-id>/channels/<type>/
|
|
180
|
-
`)}
|
|
180
|
+
`)}function nB(){console.log(`[anet] Upgrading all packages...
|
|
181
|
+
`);try{console.log("1/2 Updating @sleep2agi/agent-network..."),w("npm install -g @sleep2agi/agent-network@latest",{stdio:"inherit"})}catch{console.log(" ⚠ Failed to update agent-network")}try{console.log(`
|
|
182
|
+
2/2 Updating @sleep2agi/agent-node...`),w("npm install -g @sleep2agi/agent-node@latest",{stdio:"inherit"})}catch{console.log(" ⚠ Failed to update agent-node")}console.log(`
|
|
183
|
+
✅ Done. Check versions:`);try{w("anet -v",{stdio:"inherit"})}catch{}}switch(f){case"init":if(H[1]==="project")vB();else if(H[1]==="profile")await SB();else await jB();break;case"create":await GB();break;case"server":dB();break;case"start":UB();break;case"resume":mB();break;case"import":tB();break;case"channel":aB();break;case"upgrade":nB();break;case"session":rB();break;case"ls":case"list":cB();break;case"run":lB();break;case"-v":case"--version":case"version":{CB();break}case"--help":case"-h":case void 0:$B();break;default:if(D(f))H.unshift("start"),UB();else console.error(`Unknown: ${f}`),$B(),process.exit(1)}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/agent-network",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.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",
|