@sleep2agi/agent-network 0.0.4 → 0.0.6

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 (3) hide show
  1. package/README.md +116 -70
  2. package/dist/bin/cli.js +32 -46
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,83 +2,146 @@
2
2
 
3
3
  AI Agent 通信网络 — 让 AI Agent 互相发消息、派任务、协作。
4
4
 
5
- Server + Client + CLI,一个包搞定。
6
-
7
5
  ## 安装
8
6
 
9
7
  ```bash
10
- # 全局安装(提供 anet 命令)
11
8
  npm install -g @sleep2agi/agent-network
9
+ ```
10
+
11
+ ## 30 秒上手
12
+
13
+ ```bash
14
+ # 1. 配 hub(全局,一次性)
15
+ anet init
16
+
17
+ # 2. 配项目(下载 channel 插件 + 配 MCP)
18
+ anet init project
12
19
 
13
- # 或作为项目依赖(使用 SDK)
14
- npm install @sleep2agi/agent-network
20
+ # 3. 创建 profile(保存启动参数)
21
+ anet init profile commander --alias 指挥室 --channel server:commhub
22
+
23
+ # 4. 启动
24
+ anet start commander
25
+
26
+ # 5. 查看状态
27
+ anet ls
15
28
  ```
16
29
 
17
- ## 快速开始
30
+ ## 为什么需要 Profile?
18
31
 
19
- ### 1. 启动 Server(中心节点,需要 Bun)
32
+ Claude Code 启动参数可以非常长:
20
33
 
21
34
  ```bash
22
- # 从源码启动(推荐)
23
- git clone https://github.com/sleep2agi/agent-comm-hub.git
24
- cd agent-comm-hub/server && bun install && bun run start
25
- # CommHub 运行在 http://localhost:9200
35
+ COMMHUB_ALIAS="指挥室" TELEGRAM_STATE_DIR=~/.claude/channels/telegram-vincent \
36
+ claude --dangerously-skip-permissions \
37
+ --channels plugin:telegram@claude-plugins-official \
38
+ --dangerously-load-development-channels server:commhub \
39
+ --teammate-mode in-process --resume 98039093-...
26
40
  ```
27
41
 
28
- ### 2. 配置 Agent 加入网络
42
+ Profile 把这些参数存到 JSON,以后只需:**`anet start commander`**
43
+
44
+ 同一目录可以有多个 profile(指挥室、通信龙、SDK马)。
45
+
46
+ ## CLI 命令
47
+
48
+ ### anet init
49
+
50
+ 三级初始化:
29
51
 
30
52
  ```bash
31
- cd /path/to/your/project
32
- anet setup --hub http://YOUR_COMMHUB_IP:9200 --alias 我的Agent
53
+ anet init # → ~/.anet/config.json(hub URL)
54
+ anet init project # 下载 channel 插件 + 配 MCP + .env
55
+ anet init profile <id> [options] # → .anet/profiles/<id>.json
33
56
  ```
34
57
 
35
- 自动完成:
36
- - 测试连接
37
- - 写入全局配置 `~/.anet/config.json`(hub URL)
38
- - 写入项目配置 `.anet/config.json`(alias
39
- - 输出对应启动命令
58
+ 配置文件位置:
59
+
60
+ ```
61
+ ~/.anet/config.json 全局(hub URL
62
+ {workpath}/.anet/profiles/cmd.json 项目 profile
63
+ ```
40
64
 
41
- ### 3. 运行 Agent
65
+ #### anet init
42
66
 
43
- ```bash
44
- # 自动从 .anet/config.json 读取配置
45
- anet run
67
+ 交互式输入 hub URL,测试连接,保存到 `~/.anet/config.json`。
46
68
 
47
- # 或显式指定
48
- anet run --alias 我的Agent --hub http://YOUR_COMMHUB_IP:9200
69
+ ```bash
70
+ anet init
71
+ # CommHub URL: http://YOUR_IP:9200
72
+ # ✅ CommHub v0.4.1 — 26 sessions, 18 SSE
49
73
  ```
50
74
 
51
- Agent 启动后:自动注册 SSE 长连接监听 → 收到任务自动回复 → 3 分钟心跳。
75
+ 或直接传参:`anet init --hub http://YOUR_IP:9200`
52
76
 
53
- ## CLI 命令
77
+ #### anet init project
78
+
79
+ 下载 Channel 插件 + 安装依赖 + 配 `~/.claude.json` + 写 .env。
54
80
 
81
+ ```bash
82
+ cd ~/my-project
83
+ anet init project
55
84
  ```
56
- anet setup 配置 Agent 加入网络
57
- anet run 运行独立 Agent(SSE 实时监听)
58
- anet server 启动 CommHub Server(需要 Bun + @sleep2agi/commhub-server)
59
- anet --help 帮助
85
+
86
+ #### anet init profile
87
+
88
+ ```bash
89
+ anet init profile <id> --alias <别名> [options]
90
+ ```
91
+
92
+ | 参数 | 说明 |
93
+ |------|------|
94
+ | `<id>` | Profile ID(英文,作为文件名) |
95
+ | `--alias` | CommHub session 别名 |
96
+ | `--name` | 显示名 |
97
+ | `--channel` | 添加 channel(可重复) |
98
+ | `--env` | 环境变量 K=V(可重复) |
99
+ | `--resume` | Session resume ID |
100
+ | `--teammate-mode` | 如 in-process |
101
+
102
+ 示例:
103
+ ```bash
104
+ # 带 Telegram 双 channel
105
+ anet init profile commander --alias 指挥室 \
106
+ --channel server:commhub \
107
+ --channel plugin:telegram@claude-plugins-official \
108
+ --env TELEGRAM_STATE_DIR=~/.claude/channels/telegram-vincent \
109
+ --teammate-mode in-process
110
+
111
+ # 简单 agent
112
+ anet init profile worker --alias 开发马 --channel server:commhub
60
113
  ```
61
114
 
62
- ### anet setup
115
+ ### anet start
63
116
 
64
117
  ```bash
65
- anet setup --hub <url> --alias <name> [--type claude-code|sdk]
118
+ anet start commander # 启动指定 profile
119
+ anet start # 列出所有 profile
120
+ anet commander # 快捷方式(等于 anet start commander)
66
121
  ```
67
122
 
68
- | 参数 | 说明 | 默认值 |
69
- |------|------|--------|
70
- | `--hub` | CommHub Server URL | 从 ~/.anet/config.json 读 |
71
- | `--alias` | Agent 别名 | 必填 |
72
- | `--type` | claude-code 或 sdk | claude-code |
123
+ ### anet ls
124
+
125
+ 显示当前目录的 sessions + 网络状态:
126
+
127
+ ```
128
+ Profiles:
129
+ commander (指挥室) → 指挥室 [server:commhub, plugin:telegram]
130
+
131
+ Sessions (/home/vansin/agent-orchestra/channel):
132
+ SESSION PID NETWORK
133
+ ──────────────────── ─────── ─────────────────────
134
+ fef0eb55-b39c-4abc 64269 通信龙 offline ●
135
+ ```
73
136
 
74
137
  ### anet run
75
138
 
139
+ 独立 SSE Agent(不需要 Claude Code):
140
+
76
141
  ```bash
77
- anet run [--alias <name>] [--hub <url>] [--handler <script>]
142
+ anet run --alias SDK马 --hub http://YOUR_IP:9200
78
143
  ```
79
144
 
80
- 参数自动从 `.anet/config.json` 读取,setup 过的项目直接 `anet run` 即可。
81
-
82
145
  ## SDK 代码引用
83
146
 
84
147
  ```typescript
@@ -90,7 +153,6 @@ const hub = new CommHub({
90
153
  });
91
154
 
92
155
  hub.on('task', async (msg) => {
93
- console.log(`来自 ${msg.from_session}: ${msg.content}`);
94
156
  await hub.send(msg.from_session, '任务完成!');
95
157
  });
96
158
  ```
@@ -98,8 +160,6 @@ hub.on('task', async (msg) => {
98
160
  ```javascript
99
161
  // CommonJS
100
162
  const { CommHub } = require('@sleep2agi/agent-network');
101
- const hub = new CommHub({ url: 'http://YOUR_COMMHUB_IP:9200', alias: '我的Agent' });
102
- hub.on('task', (msg) => console.log(msg));
103
163
  ```
104
164
 
105
165
  ### SDK API
@@ -107,49 +167,35 @@ hub.on('task', (msg) => console.log(msg));
107
167
  | 方法 | 说明 |
108
168
  |------|------|
109
169
  | `hub.send(alias, content, priority?)` | 发任务 |
110
- | `hub.message(alias, content)` | 发消息(无生命周期) |
170
+ | `hub.message(alias, content)` | 发消息 |
111
171
  | `hub.reply(taskId, text, status?)` | 回复任务 |
112
172
  | `hub.status(state, extra?)` | 更新状态 |
113
173
  | `hub.broadcast(content, filter?)` | 广播 |
114
174
  | `hub.disconnect()` | 断开 |
115
175
 
116
- ### 事件
117
-
118
176
  | 事件 | 说明 |
119
177
  |------|------|
120
178
  | `task` | 收到任务(已自动 ACK) |
121
179
  | `connected` | SSE 连接成功 |
122
180
  | `disconnected` | SSE 断开(自动重连) |
123
- | `error` | 错误 |
124
-
125
- ## 配置文件
126
-
127
- 优先级:环境变量 > 命令行参数 > 项目 `.anet/config.json` > 全局 `~/.anet/config.json`
128
-
129
- **全局** `~/.anet/config.json`:
130
- ```json
131
- { "hub": "http://YOUR_COMMHUB_IP:9200", "token": "your-token" }
132
- ```
133
-
134
- **项目** `.anet/config.json`:
135
- ```json
136
- { "alias": "我的Agent", "type": "claude-code" }
137
- ```
138
181
 
139
182
  ## 运行时要求
140
183
 
141
184
  | 组件 | 运行时 |
142
185
  |------|--------|
143
- | anet setup / run / SDK | Node.js 18+ 或 Bun |
144
- | anet server | Bun 1.2+(bun:sqlite) |
186
+ | anet CLI / SDK | Node.js 18+ 或 Bun |
187
+ | CommHub Server | Bun 1.2+(单独部署) |
145
188
 
146
- ## 相关包
189
+ ## 版本历史
147
190
 
148
- | | 说明 |
149
- |---|------|
150
- | @sleep2agi/agent-network | 合并包(推荐) |
151
- | @sleep2agi/commhub-sdk | 仅客户端 SDK |
152
- | @sleep2agi/commhub-server | 仅服务端 |
191
+ | 版本 | 变更 |
192
+ |------|------|
193
+ | 0.0.6 | 三级 init(全局/项目/profile),`anet ls` 简化为当前目录 |
194
+ | 0.0.5 | `anet ls` 显示本地 sessions + CommHub 网络状态 |
195
+ | 0.0.4 | CLI 瘦身 580KB→13KB,Node.js 兼容,profile 系统 |
196
+ | 0.0.3 | `anet setup` 一键配置 Channel 插件 |
197
+ | 0.0.2 | CLI shebang 改为 node |
198
+ | 0.0.1 | 首次发布 |
153
199
 
154
200
  ## License
155
201
 
package/dist/bin/cli.js CHANGED
@@ -1,51 +1,37 @@
1
1
  #!/usr/bin/env node
2
- var h=Object.defineProperty;var u=(z)=>z;function g(z,B){this[z]=u.bind(null,B)}var c=(z,B)=>{for(var L in B)h(z,L,{get:B[L],enumerable:!0,configurable:!0,set:g.bind(B,L)})};var d=(z,B)=>()=>(z&&(B=z(z=0)),B);var x={};c(x,{default:()=>m,CommHub:()=>H});import{EventEmitter as f}from"events";import{hostname as D}from"os";var H,m;var A=d(()=>{H=class H extends f{url;alias;token;agent;resumeId;heartbeatInterval;reconnectDelay;heartbeatTimer;sseAbort;running=!1;constructor(z){super();if(this.url=z.url.replace(/\/$/,""),this.alias=z.alias,this.token=z.token,this.agent=z.agent||"sdk",this.resumeId=`sdk-${z.alias}-${Date.now().toString(36)}`,this.heartbeatInterval=z.heartbeatInterval??180000,this.reconnectDelay=z.reconnectDelay??3000,z.autoConnect!==!1)this.connect()}log(z){console.log(`[${new Date().toTimeString().slice(0,8)}] [commhub:${this.alias}] ${z}`)}async call(z,B){let L={"Content-Type":"application/json"};if(this.token)L.Authorization=`Bearer ${this.token}`;let R=await(await fetch(`${this.url}/mcp`,{method:"POST",headers:L,body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:"tools/call",params:{name:z,arguments:B}})})).json(),W=R?.result?.content?.[0]?.text;return W?JSON.parse(W):R}async connect(){if(this.running)return;this.running=!0,await this.status("idle"),this.log("registered"),this.heartbeatTimer=setInterval(()=>{this.status("idle").catch((z)=>this.log(`heartbeat failed: ${z.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(z,B,L="normal"){return this.call("send_task",{alias:z,task:B,priority:L,from_session:this.alias})}async message(z,B){return this.call("send_message",{alias:z,message:B,from_session:this.alias})}async reply(z,B,L="completed"){return this.call("reply",{task_id:z,text:B,status:L})}async status(z,B){return this.call("report_status",{resume_id:this.resumeId,alias:this.alias,status:z,server:D(),hostname:D(),agent:this.agent,project_dir:process.cwd(),...B})}async getAllStatus(){return this.call("get_all_status",{})}async broadcast(z,B){return this.call("broadcast",{message:z,filter_server:B?.server,filter_status:B?.status})}async connectSSE(){let z=encodeURIComponent(this.alias),B=`${this.url}/events/${z}`,L=this.reconnectDelay;while(this.running){try{this.sseAbort=new AbortController;let Q={Accept:"text/event-stream"};if(this.token)Q.Authorization=`Bearer ${this.token}`;let R=await fetch(B,{headers:Q,signal:this.sseAbort.signal});if(!R.ok||!R.body){this.log(`SSE failed: ${R.status}`),await this.sleep(L),L=Math.min(L*1.5,60000);continue}L=this.reconnectDelay;let W=R.body.getReader(),V=new TextDecoder,X="";while(this.running){let{done:Z,value:$}=await W.read();if(Z)break;X+=V.decode($,{stream:!0});let q=X.split(`
3
- `);X=q.pop()||"";for(let J of q){if(!J.startsWith("data: "))continue;try{let O=JSON.parse(J.slice(6));if(O.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(O.type==="new_task"||O.type==="new_message"||O.type==="broadcast")await this.processInbox()}catch{}}}}catch(Q){if(Q.name==="AbortError")break;this.emit("error",Q),this.log(`SSE error: ${Q.message}`)}if(this.running)this.emit("disconnected"),this.log(`SSE reconnecting in ${L/1000}s...`),await this.sleep(L),L=Math.min(L*1.5,60000)}}async processInbox(){try{let B=(await this.call("get_inbox",{alias:this.alias,limit:10}))?.messages||[];for(let L of B)await this.call("ack_inbox",{alias:this.alias,message_id:L.id}),this.log(`← ${L.from_session}: ${L.content.slice(0,60)}`),this.emit("task",L),this.emit("message",L)}catch(z){this.log(`inbox error: ${z.message}`)}}sleep(z){return new Promise((B)=>setTimeout(B,z))}};m=H});import{readFileSync as N,writeFileSync as _,existsSync as I,mkdirSync as K,readdirSync as p}from"fs";import{join as G}from"path";import{execSync as n,spawn as t}from"child_process";var Y=process.argv.slice(2),w=Y[0],E=process.env.HOME||"~";function C(){let z={_channels:[],_envs:[]};for(let B=1;B<Y.length;B++)if(Y[B]==="--channel"&&B+1<Y.length)z._channels.push(Y[++B]);else if(Y[B]==="--env"&&B+1<Y.length)z._envs.push(Y[++B]);else if(Y[B].startsWith("--")&&B+1<Y.length&&!Y[B+1].startsWith("--"))z[Y[B].replace(/^--/,"")]=Y[++B];return z}function U(){return G(process.cwd(),".anet","profiles")}function S(){return G(E,".anet","config.json")}function k(){let z=S();if(I(z))try{return JSON.parse(N(z,"utf-8"))}catch{}return{}}function M(z){let B=G(U(),`${z}.json`);if(I(B))try{return JSON.parse(N(B,"utf-8"))}catch{}return null}function o(z,B){let L=U();K(L,{recursive:!0}),_(G(L,`${z}.json`),JSON.stringify(B,null,2)+`
4
- `)}function F(){let z=U();if(!I(z))return[];return p(z).filter((B)=>B.endsWith(".json")).map((B)=>B.replace(/\.json$/,""))}function P(){console.log(`
2
+ import{createRequire as d}from"node:module";var h=Object.defineProperty;var g=(Q)=>Q;function m(Q,z){this[Q]=g.bind(null,z)}var p=(Q,z)=>{for(var V in z)h(Q,V,{get:z[V],enumerable:!0,configurable:!0,set:m.bind(z,V)})};var c=(Q,z)=>()=>(Q&&(z=Q(Q=0)),z);var t=d(import.meta.url);var D={};p(D,{default:()=>n,CommHub:()=>H});import{EventEmitter as l}from"events";import{hostname as x}from"os";var H,n;var P=c(()=>{H=class H extends l{url;alias;token;agent;resumeId;heartbeatInterval;reconnectDelay;heartbeatTimer;sseAbort;running=!1;constructor(Q){super();if(this.url=Q.url.replace(/\/$/,""),this.alias=Q.alias,this.token=Q.token,this.agent=Q.agent||"sdk",this.resumeId=`sdk-${Q.alias}-${Date.now().toString(36)}`,this.heartbeatInterval=Q.heartbeatInterval??180000,this.reconnectDelay=Q.reconnectDelay??3000,Q.autoConnect!==!1)this.connect()}log(Q){console.log(`[${new Date().toTimeString().slice(0,8)}] [commhub:${this.alias}] ${Q}`)}async call(Q,z){let V={"Content-Type":"application/json"};if(this.token)V.Authorization=`Bearer ${this.token}`;let $=await(await fetch(`${this.url}/mcp`,{method:"POST",headers:V,body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:"tools/call",params:{name:Q,arguments:z}})})).json(),Y=$?.result?.content?.[0]?.text;return Y?JSON.parse(Y):$}async connect(){if(this.running)return;this.running=!0,await this.status("idle"),this.log("registered"),this.heartbeatTimer=setInterval(()=>{this.status("idle").catch((Q)=>this.log(`heartbeat failed: ${Q.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(Q,z,V="normal"){return this.call("send_task",{alias:Q,task:z,priority:V,from_session:this.alias})}async message(Q,z){return this.call("send_message",{alias:Q,message:z,from_session:this.alias})}async reply(Q,z,V="completed"){return this.call("reply",{task_id:Q,text:z,status:V})}async status(Q,z){return this.call("report_status",{resume_id:this.resumeId,alias:this.alias,status:Q,server:x(),hostname:x(),agent:this.agent,project_dir:process.cwd(),...z})}async getAllStatus(){return this.call("get_all_status",{})}async broadcast(Q,z){return this.call("broadcast",{message:Q,filter_server:z?.server,filter_status:z?.status})}async connectSSE(){let Q=encodeURIComponent(this.alias),z=`${this.url}/events/${Q}`,V=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 $=await fetch(z,{headers:X,signal:this.sseAbort.signal});if(!$.ok||!$.body){this.log(`SSE failed: ${$.status}`),await this.sleep(V),V=Math.min(V*1.5,60000);continue}V=this.reconnectDelay;let Y=$.body.getReader(),N=new TextDecoder,Z="";while(this.running){let{done:L,value:U}=await Y.read();if(L)break;Z+=N.decode(U,{stream:!0});let _=Z.split(`
3
+ `);Z=_.pop()||"";for(let B of _){if(!B.startsWith("data: "))continue;try{let O=JSON.parse(B.slice(6));if(O.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(O.type==="new_task"||O.type==="new_message"||O.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 ${V/1000}s...`),await this.sleep(V),V=Math.min(V*1.5,60000)}}async processInbox(){try{let z=(await this.call("get_inbox",{alias:this.alias,limit:10}))?.messages||[];for(let V of z)await this.call("ack_inbox",{alias:this.alias,message_id:V.id}),this.log(`← ${V.from_session}: ${V.content.slice(0,60)}`),this.emit("task",V),this.emit("message",V)}catch(Q){this.log(`inbox error: ${Q.message}`)}}sleep(Q){return new Promise((z)=>setTimeout(z,Q))}};n=H});import{readFileSync as I,writeFileSync as E,existsSync as q,mkdirSync as J,readdirSync as v}from"fs";import{join as R}from"path";import{spawn as o}from"child_process";var W=process.argv.slice(2),A=W[0],K=process.env.HOME||process.env.USERPROFILE||"~";function S(){return R(K,".anet","config.json")}function C(){return R(process.cwd(),".anet","profiles")}function M(){let Q=S();if(q(Q))try{return JSON.parse(I(Q,"utf-8"))}catch{}return{}}function r(Q){let z=R(K,".anet");J(z,{recursive:!0}),E(R(z,"config.json"),JSON.stringify(Q,null,2)+`
4
+ `)}function G(Q){let z=R(C(),`${Q}.json`);if(q(z))try{return JSON.parse(I(z,"utf-8"))}catch{}return null}function a(Q,z){let V=C();J(V,{recursive:!0}),E(R(V,`${Q}.json`),JSON.stringify(z,null,2)+`
5
+ `)}function y(){let Q=C();if(!q(Q))return[];return v(Q).filter((z)=>z.endsWith(".json")).map((z)=>z.replace(/\.json$/,""))}function F(){let Q={_channels:[],_envs:[]};for(let z=0;z<W.length;z++){if(W[z]==="--channel"&&W[z+1]){Q._channels.push(W[++z]);continue}if(W[z]==="--env"&&W[z+1]){Q._envs.push(W[++z]);continue}if(W[z].startsWith("--")&&W[z+1]&&!W[z+1].startsWith("--"))Q[W[z].slice(2)]=W[++z]}return Q}function j(){console.log(`
5
6
  anet — AI Agent Network CLI
6
7
 
7
- Commands:
8
- setup Create a profile for a new Agent
9
- start Start Claude Code with a saved profile
10
- run Run standalone SSE agent (no Claude Code)
11
- list List all profiles
12
- --help Show this help
8
+ anet init Configure hub URL (global, once)
9
+ anet init project Setup current project (channel plugin + config)
10
+ anet init profile <id> Create a launch profile
11
+ anet start <id> Start Claude Code with profile
12
+ anet ls Show sessions + network status
13
+ anet run Run standalone SSE agent
14
+ anet --help This help
13
15
 
14
- Setup:
15
- anet setup --profile <session-id> --alias <alias> --hub <url> [options]
16
- --name <name> Display name (e.g. 指挥室)
17
- --channel <ch> Add channel (repeatable)
18
- --env <K=V> Add env var (repeatable)
19
- --resume <id> Session resume ID
20
- --type <type> claude-code (default) or sdk
21
- --teammate-mode <mode> Teammate mode (e.g. in-process)
22
-
23
- Start:
24
- anet start <session-id> Start Claude with saved profile
25
- anet start List profiles to pick from
26
-
27
- Examples:
28
- anet setup --profile commander --name 指挥室 --alias 指挥室 \\
29
- --hub http://YOUR_IP:9200 \\
30
- --channel server:commhub \\
31
- --channel plugin:telegram@claude-plugins-official \\
32
- --env TELEGRAM_STATE_DIR=~/.claude/channels/telegram-vincent
33
-
34
- anet start commander
35
- anet list
36
- `)}async function l(){let z=C(),B=k(),L=z.profile,Q=z.name,R=z.alias,W=z.hub||B.hub,V=z.type||"claude-code",X=z.resume,Z=z["teammate-mode"];if(!L||!R)console.error("Error: --profile and --alias are required"),console.error("Usage: anet setup --profile hub-01 --name 指挥室 --alias 指挥室 --hub http://YOUR_IP:9200 --channel server:commhub"),process.exit(1);if(!W)console.error("Error: --hub is required (first time) or set in ~/.anet/config.json"),process.exit(1);let $={};for(let T of z._envs){let j=T.indexOf("=");if(j>0)$[T.slice(0,j)]=T.slice(j+1)}let q={...Q?{name:Q}:{},alias:R,hub:W,channels:z._channels.length>0?z._channels:["server:commhub"],env:$,flags:{dangerouslySkipPermissions:!0,...Z?{teammateMode:Z}:{}},...X?{resume:X}:{}},J=G(E,".anet");K(J,{recursive:!0});let O=k();if(O.hub=W,_(S(),JSON.stringify(O,null,2)+`
37
- `),o(L,q),console.log(`
38
- ✅ Profile "${L}" saved to .anet/profiles/${L}.json`),V==="claude-code")await i(W,R);let y=b(q);console.log(`
39
- 启动命令 (anet start ${L}):
40
- ${y}
41
- `)}async function i(z,B){let L=`${E}/.claude/channels/commhub`;K(L,{recursive:!0});let Q=`${L}/server.ts`;if(!I(Q)){console.log("Downloading Channel plugin...");try{let $=await fetch("https://raw.githubusercontent.com/sleep2agi/agent-comm-hub/main/channel/server.ts");if($.ok)_(Q,await $.text()),console.log(` ✅ ${Q}`)}catch{}try{let $=await fetch("https://raw.githubusercontent.com/sleep2agi/agent-comm-hub/main/channel/package.json");if($.ok){_(`${L}/package.json`,await $.text());try{n("bun install",{cwd:L,stdio:"pipe"})}catch{}}}catch{}}let R=`${L}/.env`;if(!I(R))_(R,`COMMHUB_URL=${z}
42
- `);let W=process.cwd().replace(/\//g,"-"),V=`${L}/${W}`;K(V,{recursive:!0}),_(`${V}/.env`,`COMMHUB_ALIAS=${B}
43
- `);let X=`${E}/.claude.json`,Z={};if(I(X))try{Z=JSON.parse(N(X,"utf-8"))}catch{}if(!Z.mcpServers?.commhub)Z.mcpServers=Z.mcpServers||{},Z.mcpServers.commhub={type:"stdio",command:"bun",args:["run",Q]},_(X,JSON.stringify(Z,null,2)+`
44
- `)}function b(z){let B=[];B.push(`COMMHUB_ALIAS="${z.alias}"`);for(let[L,Q]of Object.entries(z.env))B.push(`${L}=${Q}`);if(B.push("claude"),z.flags.dangerouslySkipPermissions)B.push("--dangerously-skip-permissions");for(let L of z.channels)if(L.startsWith("server:"))B.push(`--dangerously-load-development-channels ${L}`);else B.push(`--channels ${L}`);if(z.flags.teammateMode)B.push(`--teammate-mode ${z.flags.teammateMode}`);if(z.resume)B.push(`--resume ${z.resume}`);return B.join(" ")}function v(){let z=Y[1];if(!z){let V=F();if(V.length===0)console.log("No profiles found. Create one with: anet setup --profile <name> --alias <alias> --hub <url>"),process.exit(1);console.log(`
45
- Available profiles:
46
- `);for(let X of V){let Z=M(X);console.log(` ${X} → alias: ${Z?.alias}, channels: ${Z?.channels.join(", ")}`)}console.log(`
47
- Start with: anet start <profile>
48
- `);return}let B=M(z);if(!B)console.error(`Profile "${z}" not found in .anet/profiles/`),console.error(`Available: ${F().join(", ")||"(none)"}`),process.exit(1);let L=b(B);console.log(`[anet] Starting "${z}"...`),console.log(`[anet] ${L}
49
- `);let Q={...process.env,COMMHUB_ALIAS:B.alias};for(let[V,X]of Object.entries(B.env))Q[V]=X.replace(/^~/,E);let R=[];if(B.flags.dangerouslySkipPermissions)R.push("--dangerously-skip-permissions");for(let V of B.channels)if(V.startsWith("server:"))R.push("--dangerously-load-development-channels",V);else R.push("--channels",V);if(B.flags.teammateMode)R.push("--teammate-mode",B.flags.teammateMode);if(B.resume)R.push("--resume",B.resume);t("claude",R,{env:Q,stdio:"inherit",shell:!0}).on("exit",(V)=>process.exit(V||0))}function s(){let z=F();if(z.length===0){console.log("No profiles. Create one: anet setup --profile <name> --alias <alias> --hub <url>");return}console.log(`
16
+ Quick start:
17
+ anet init
18
+ anet init project
19
+ anet init profile cmd --alias 指挥室 --channel server:commhub
20
+ anet start cmd
21
+ `)}async function i(){let Q=F(),z=Q.hub;if(!z)process.stdout.write("CommHub URL (e.g. http://YOUR_IP:9200): "),z=await new Promise((X)=>{let $="";process.stdin.setEncoding("utf-8"),process.stdin.once("data",(Y)=>{X(Y.toString().trim())})});if(!z)console.error("Error: hub URL required"),process.exit(1);try{let $=await(await fetch(`${z}/health`)).json();console.log(`✅ CommHub v${$.version} — ${$.sessions} sessions, ${$.sse_connections} SSE`)}catch(X){console.error(`❌ Cannot reach ${z}: ${X.message}`),process.exit(1)}let V=M();if(V.hub=z,Q.token)V.token=Q.token;r(V),console.log(`
22
+ Saved to ${S()}`),console.log("Next: anet init project")}async function e(){let z=M().hub;if(!z)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let V=R(K,".claude","channels","commhub");J(V,{recursive:!0});let X=R(V,"server.ts");if(!q(X)){console.log("Downloading Channel plugin...");try{let L=await fetch("https://raw.githubusercontent.com/sleep2agi/agent-comm-hub/main/channel/server.ts");if(L.ok)E(X,await L.text()),console.log(` ✅ ${X}`)}catch(L){console.log(` ❌ Failed: ${L.message}`),console.log(` Manual: curl -sL https://raw.githubusercontent.com/sleep2agi/agent-comm-hub/main/channel/server.ts -o ${X}`)}}else console.log("Channel plugin: exists");let $=R(V,"package.json");if(!q($))try{let L=await fetch("https://raw.githubusercontent.com/sleep2agi/agent-comm-hub/main/channel/package.json");if(L.ok){E($,await L.text());try{let{execSync:U}=await import("child_process");U("bun install",{cwd:V,stdio:"pipe"}),console.log("Dependencies installed")}catch{console.log("⚠️ Run: cd ~/.claude/channels/commhub && bun install")}}}catch{}let Y=R(V,".env");if(!q(Y))E(Y,`COMMHUB_URL=${z}
23
+ `);console.log(`CommHub URL: ${z}`);let N=R(K,".claude.json"),Z={};if(q(N))try{Z=JSON.parse(I(N,"utf-8"))}catch{}if(!Z.mcpServers?.commhub)Z.mcpServers=Z.mcpServers||{},Z.mcpServers.commhub={type:"stdio",command:"bun",args:["run",X]},E(N,JSON.stringify(Z,null,2)+`
24
+ `),console.log(`MCP config: ${N}`);else console.log("MCP config: already set");console.log(`
25
+ ✅ Project ready. Next: anet init profile <id> --alias <名字> --channel server:commhub`)}function s(){let Q=W[2];if(!Q)console.error("Usage: anet init profile <id> --alias <名字> [--channel ...] [--env ...]"),process.exit(1);let z=M(),V=F(),X=V.alias||Q,$=V.hub||z.hub;if(!$)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let Y={};for(let _ of V._envs){let B=_.indexOf("=");if(B>0)Y[_.slice(0,B)]=_.slice(B+1)}let N={...V.name?{name:V.name}:{},alias:X,hub:$,channels:V._channels.length>0?V._channels:["server:commhub"],env:Y,flags:{dangerouslySkipPermissions:!0,...V["teammate-mode"]?{teammateMode:V["teammate-mode"]}:{}},...V.resume?{resume:V.resume}:{}},Z=R(K,".claude","channels","commhub"),L=process.cwd().replace(/\//g,"-"),U=R(Z,L);if(J(U,{recursive:!0}),E(R(U,".env"),`COMMHUB_ALIAS=${X}
26
+ `),a(Q,N),console.log(`
27
+ Profile "${Q}" saved`),console.log(` alias: ${X}`),console.log(` channels: ${N.channels.join(", ")}`),Object.keys(Y).length)console.log(` env: ${Object.keys(Y).join(", ")}`);console.log(`
28
+ Start: anet start ${Q}`)}function b(){let Q=W[1];if(!Q){let Y=y();if(Y.length===0){console.log("No profiles. Run: anet init profile <id> --alias <名字>");return}console.log(`
29
+ Profiles:
30
+ `);for(let N of Y){let Z=G(N);console.log(` ${N}${Z?.name?` (${Z.name})`:""} → ${Z?.alias} [${Z?.channels.join(", ")}]`)}console.log(`
31
+ anet start <id>
32
+ `);return}let z=G(Q);if(!z)console.error(`Profile "${Q}" not found. Run: anet ls`),process.exit(1);let V={...process.env,COMMHUB_ALIAS:z.alias};for(let[Y,N]of Object.entries(z.env))V[Y]=N.replace(/^~/,K);let X=[];if(z.flags.dangerouslySkipPermissions)X.push("--dangerously-skip-permissions");for(let Y of z.channels)if(Y.startsWith("server:"))X.push("--dangerously-load-development-channels",Y);else X.push("--channels",Y);if(z.flags.teammateMode)X.push("--teammate-mode",z.flags.teammateMode);if(z.resume)X.push("--resume",z.resume);X.push("-n",z.name||z.alias),console.log(`[anet] Starting "${Q}" (${z.alias})...
33
+ `),o("claude",X,{env:V,stdio:"inherit",shell:!0}).on("exit",(Y)=>process.exit(Y||0))}async function zz(){let Q=y();if(Q.length>0){console.log(`
50
34
  Profiles:
51
- `);for(let B of z){let L=M(B),Q=L?.channels.join(", ")||"",R=Object.keys(L?.env||{}).join(", ");if(console.log(` ${B}`),console.log(` alias: ${L?.alias} hub: ${L?.hub}`),console.log(` channels: ${Q}`),R)console.log(` env: ${R}`);if(L?.resume)console.log(` resume: ${L.resume}`);console.log()}}async function a(){let z=k(),B=C(),L=process.env.COMMHUB_URL||B.hub||z.hub||"http://127.0.0.1:9200",Q=process.env.COMMHUB_ALIAS||B.alias||z.alias;if(!Q)console.error("Error: --alias required"),process.exit(1);let{CommHub:R}=await Promise.resolve().then(() => (A(),x)),W=new R({url:L,alias:Q});W.on("task",async(V)=>{console.log(`[${Q}] ${V.from_session}: ${V.content.slice(0,100)}`),await W.send(V.from_session,`[${Q}] 收到: ${V.content.slice(0,200)}`)}),W.on("connected",()=>console.log(`[${Q}] Connected to ${L}`)),W.on("disconnected",()=>console.log(`[${Q}] Disconnected, reconnecting...`)),process.on("SIGINT",()=>W.disconnect().then(()=>process.exit(0))),process.on("SIGTERM",()=>W.disconnect().then(()=>process.exit(0))),console.log(`[${Q}] Listening on ${L} (Ctrl+C to quit)`)}switch(w){case"setup":l();break;case"start":v();break;case"list":s();break;case"run":a();break;case"--help":case"-h":case void 0:P();break;default:if(M(w))Y.splice(0,0,"start"),v();else console.error(`Unknown command: ${w}`),P(),process.exit(1)}
35
+ `);for(let Z of Q){let L=G(Z);console.log(` ${Z}${L?.name?` (${L.name})`:""} → ${L?.alias} [${L?.channels.join(", ")}]`)}console.log()}let z=process.cwd(),V=R(K,".claude","sessions"),X=[];if(q(V))for(let Z of v(V).filter((L)=>L.endsWith(".json")))try{let L=JSON.parse(I(R(V,Z),"utf-8"));if(L.cwd===z)X.push(L)}catch{}if(X.length===0&&Q.length===0){console.log("No sessions or profiles in this directory."),console.log(`Get started: anet init
36
+ `);return}let $=M(),Y=[],N={};if($.hub)try{let[Z,L]=await Promise.all([fetch(`${$.hub}/api/status`).then((U)=>U.json()),fetch(`${$.hub}/health`).then((U)=>U.json())]);Y=Z.sessions||[],N=L.sse_sessions||{}}catch{}if(X.length>0){console.log(`Sessions (${z}):
37
+ `),console.log(" SESSION PID NETWORK"),console.log(" ──────────────────── ─────── ─────────────────────");for(let Z of X){let L=Z.sessionId.slice(0,18),U=!1;try{process.kill(Z.pid,0),U=!0}catch{}let _="(not in network)",B=z.replace(/\//g,"-"),O=R(K,".claude","channels","commhub",B,".env");if(q(O)){let k=I(O,"utf-8").match(/COMMHUB_ALIAS=(.+)/);if(k){let T=k[1].trim(),w=Y.find((f)=>f.alias===T),u=N[T]?"●":"○";_=w?`${T} ${w.status} ${u}`:`${T} (not registered)`}}console.log(` ${L} ${(U?`${Z.pid}`:`${Z.pid}✕`).padEnd(7)} ${_}`)}console.log()}}async function Qz(){let Q=M(),z=F(),V=process.env.COMMHUB_URL||z.hub||Q.hub||"http://127.0.0.1:9200",X=process.env.COMMHUB_ALIAS||z.alias;if(!X)console.error("Error: --alias required"),process.exit(1);let{CommHub:$}=await Promise.resolve().then(() => (P(),D)),Y=new $({url:V,alias:X});Y.on("task",async(N)=>{console.log(`[${X}] ← ${N.from_session}: ${N.content.slice(0,100)}`),await Y.send(N.from_session,`[${X}] 收到: ${N.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 ${V}`)}switch(A){case"init":if(W[1]==="project")e();else if(W[1]==="profile")s();else i();break;case"start":b();break;case"ls":case"list":zz();break;case"run":Qz();break;case"--help":case"-h":case void 0:j();break;default:if(G(A))W.unshift("start"),b();else console.error(`Unknown: ${A}`),j(),process.exit(1)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/agent-network",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
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",