@sleep2agi/agent-network 0.0.5 → 0.0.7

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 +118 -70
  2. package/dist/bin/cli.js +33 -52
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,83 +2,147 @@
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}/.mcp.json MCP server 配置(commhub stdio)
63
+ {workpath}/.anet/profiles/cmd.json 启动 profile
64
+ ```
40
65
 
41
- ### 3. 运行 Agent
66
+ #### anet init
42
67
 
43
- ```bash
44
- # 自动从 .anet/config.json 读取配置
45
- anet run
68
+ 交互式输入 hub URL,测试连接,保存到 `~/.anet/config.json`。
46
69
 
47
- # 或显式指定
48
- anet run --alias 我的Agent --hub http://YOUR_COMMHUB_IP:9200
70
+ ```bash
71
+ anet init
72
+ # CommHub URL: http://YOUR_IP:9200
73
+ # ✅ CommHub v0.4.1 — 26 sessions, 18 SSE
49
74
  ```
50
75
 
51
- Agent 启动后:自动注册 SSE 长连接监听 → 收到任务自动回复 → 3 分钟心跳。
76
+ 或直接传参:`anet init --hub http://YOUR_IP:9200`
52
77
 
53
- ## CLI 命令
78
+ #### anet init project
79
+
80
+ 下载 Channel 插件 + 安装依赖 + 写 `.mcp.json`(commhub stdio)+ 写 .env。
54
81
 
82
+ ```bash
83
+ cd ~/my-project
84
+ anet init project
55
85
  ```
56
- anet setup 配置 Agent 加入网络
57
- anet run 运行独立 Agent(SSE 实时监听)
58
- anet server 启动 CommHub Server(需要 Bun + @sleep2agi/commhub-server)
59
- anet --help 帮助
86
+
87
+ #### anet init profile
88
+
89
+ ```bash
90
+ anet init profile <id> --alias <别名> [options]
91
+ ```
92
+
93
+ | 参数 | 说明 |
94
+ |------|------|
95
+ | `<id>` | Profile ID(英文,作为文件名) |
96
+ | `--alias` | CommHub session 别名 |
97
+ | `--name` | 显示名 |
98
+ | `--channel` | 添加 channel(可重复) |
99
+ | `--env` | 环境变量 K=V(可重复) |
100
+ | `--resume` | Session resume ID |
101
+ | `--teammate-mode` | 如 in-process |
102
+
103
+ 示例:
104
+ ```bash
105
+ # 带 Telegram 双 channel
106
+ anet init profile commander --alias 指挥室 \
107
+ --channel server:commhub \
108
+ --channel plugin:telegram@claude-plugins-official \
109
+ --env TELEGRAM_STATE_DIR=~/.claude/channels/telegram-vincent \
110
+ --teammate-mode in-process
111
+
112
+ # 简单 agent
113
+ anet init profile worker --alias 开发马 --channel server:commhub
60
114
  ```
61
115
 
62
- ### anet setup
116
+ ### anet start
63
117
 
64
118
  ```bash
65
- anet setup --hub <url> --alias <name> [--type claude-code|sdk]
119
+ anet start commander # 启动指定 profile
120
+ anet start # 列出所有 profile
121
+ anet commander # 快捷方式(等于 anet start commander)
66
122
  ```
67
123
 
68
- | 参数 | 说明 | 默认值 |
69
- |------|------|--------|
70
- | `--hub` | CommHub Server URL | 从 ~/.anet/config.json 读 |
71
- | `--alias` | Agent 别名 | 必填 |
72
- | `--type` | claude-code 或 sdk | claude-code |
124
+ ### anet ls
125
+
126
+ 显示当前目录的 sessions + 网络状态:
127
+
128
+ ```
129
+ Profiles:
130
+ commander (指挥室) → 指挥室 [server:commhub, plugin:telegram]
131
+
132
+ Sessions (/home/vansin/agent-orchestra/channel):
133
+ SESSION PID NETWORK
134
+ ──────────────────── ─────── ─────────────────────
135
+ fef0eb55-b39c-4abc 64269 通信龙 offline ●
136
+ ```
73
137
 
74
138
  ### anet run
75
139
 
140
+ 独立 SSE Agent(不需要 Claude Code):
141
+
76
142
  ```bash
77
- anet run [--alias <name>] [--hub <url>] [--handler <script>]
143
+ anet run --alias SDK马 --hub http://YOUR_IP:9200
78
144
  ```
79
145
 
80
- 参数自动从 `.anet/config.json` 读取,setup 过的项目直接 `anet run` 即可。
81
-
82
146
  ## SDK 代码引用
83
147
 
84
148
  ```typescript
@@ -90,7 +154,6 @@ const hub = new CommHub({
90
154
  });
91
155
 
92
156
  hub.on('task', async (msg) => {
93
- console.log(`来自 ${msg.from_session}: ${msg.content}`);
94
157
  await hub.send(msg.from_session, '任务完成!');
95
158
  });
96
159
  ```
@@ -98,8 +161,6 @@ hub.on('task', async (msg) => {
98
161
  ```javascript
99
162
  // CommonJS
100
163
  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
164
  ```
104
165
 
105
166
  ### SDK API
@@ -107,49 +168,36 @@ hub.on('task', (msg) => console.log(msg));
107
168
  | 方法 | 说明 |
108
169
  |------|------|
109
170
  | `hub.send(alias, content, priority?)` | 发任务 |
110
- | `hub.message(alias, content)` | 发消息(无生命周期) |
171
+ | `hub.message(alias, content)` | 发消息 |
111
172
  | `hub.reply(taskId, text, status?)` | 回复任务 |
112
173
  | `hub.status(state, extra?)` | 更新状态 |
113
174
  | `hub.broadcast(content, filter?)` | 广播 |
114
175
  | `hub.disconnect()` | 断开 |
115
176
 
116
- ### 事件
117
-
118
177
  | 事件 | 说明 |
119
178
  |------|------|
120
179
  | `task` | 收到任务(已自动 ACK) |
121
180
  | `connected` | SSE 连接成功 |
122
181
  | `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
182
 
139
183
  ## 运行时要求
140
184
 
141
185
  | 组件 | 运行时 |
142
186
  |------|--------|
143
- | anet setup / run / SDK | Node.js 18+ 或 Bun |
144
- | anet server | Bun 1.2+(bun:sqlite) |
187
+ | anet CLI / SDK | Node.js 18+ 或 Bun |
188
+ | CommHub Server | Bun 1.2+(单独部署) |
145
189
 
146
- ## 相关包
190
+ ## 版本历史
147
191
 
148
- | | 说明 |
149
- |---|------|
150
- | @sleep2agi/agent-network | 合并包(推荐) |
151
- | @sleep2agi/commhub-sdk | 仅客户端 SDK |
152
- | @sleep2agi/commhub-server | 仅服务端 |
192
+ | 版本 | 变更 |
193
+ |------|------|
194
+ | 0.0.7 | init project 改写 .mcp.json(不写 ~/.claude.json),避免全局污染 |
195
+ | 0.0.6 | 三级 init(全局/项目/profile),`anet ls` 简化为当前目录 |
196
+ | 0.0.5 | `anet ls` 显示本地 sessions + CommHub 网络状态 |
197
+ | 0.0.4 | CLI 瘦身 580KB→13KB,Node.js 兼容,profile 系统 |
198
+ | 0.0.3 | `anet setup` 一键配置 Channel 插件 |
199
+ | 0.0.2 | CLI shebang 改为 node |
200
+ | 0.0.1 | 首次发布 |
153
201
 
154
202
  ## License
155
203
 
package/dist/bin/cli.js CHANGED
@@ -1,56 +1,37 @@
1
1
  #!/usr/bin/env node
2
- var f=Object.defineProperty;var p=(z)=>z;function n(z,B){this[z]=p.bind(null,B)}var t=(z,B)=>{for(var L in B)f(z,L,{get:B[L],enumerable:!0,configurable:!0,set:n.bind(B,L)})};var l=(z,B)=>()=>(z&&(B=z(z=0)),B);var y={};t(y,{default:()=>i,CommHub:()=>U});import{EventEmitter as o}from"events";import{hostname as b}from"os";var U,i;var S=l(()=>{U=class U extends o{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 Y=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(),X=Y?.result?.content?.[0]?.text;return X?JSON.parse(X):Y}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:b(),hostname:b(),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 Y=await fetch(B,{headers:Q,signal:this.sseAbort.signal});if(!Y.ok||!Y.body){this.log(`SSE failed: ${Y.status}`),await this.sleep(L),L=Math.min(L*1.5,60000);continue}L=this.reconnectDelay;let X=Y.body.getReader(),V=new TextDecoder,Z="";while(this.running){let{done:R,value:W}=await X.read();if(R)break;Z+=V.decode(W,{stream:!0});let $=Z.split(`
3
- `);Z=$.pop()||"";for(let q of $){if(!q.startsWith("data: "))continue;try{let K=JSON.parse(q.slice(6));if(K.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(K.type==="new_task"||K.type==="new_message"||K.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))}};i=U});import{readFileSync as I,writeFileSync as M,existsSync as J,mkdirSync as F,readdirSync as g}from"fs";import{join as E}from"path";import{execSync as r,spawn as a}from"child_process";var _=process.argv.slice(2),k=_[0],T=process.env.HOME||"~";function m(){let z={_channels:[],_envs:[]};for(let B=1;B<_.length;B++)if(_[B]==="--channel"&&B+1<_.length)z._channels.push(_[++B]);else if(_[B]==="--env"&&B+1<_.length)z._envs.push(_[++B]);else if(_[B].startsWith("--")&&B+1<_.length&&!_[B+1].startsWith("--"))z[_[B].replace(/^--/,"")]=_[++B];return z}function w(){return E(process.cwd(),".anet","profiles")}function c(){return E(T,".anet","config.json")}function A(){let z=c();if(J(z))try{return JSON.parse(I(z,"utf-8"))}catch{}return{}}function x(z){let B=E(w(),`${z}.json`);if(J(B))try{return JSON.parse(I(B,"utf-8"))}catch{}return null}function s(z,B){let L=w();F(L,{recursive:!0}),M(E(L,`${z}.json`),JSON.stringify(B,null,2)+`
4
- `)}function P(){let z=w();if(!J(z))return[];return g(z).filter((B)=>B.endsWith(".json")).map((B)=>B.replace(/\.json$/,""))}function h(){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 c=(Q,z)=>{for(var V in z)h(Q,V,{get:z[V],enumerable:!0,configurable:!0,set:m.bind(z,V)})};var p=(Q,z)=>()=>(Q&&(z=Q(Q=0)),z);var l=d(import.meta.url);var P={};c(P,{default:()=>t,CommHub:()=>H});import{EventEmitter as n}from"events";import{hostname as x}from"os";var H,t;var C=p(()=>{H=class H extends n{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 K=JSON.parse(B.slice(6));if(K.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(K.type==="new_task"||K.type==="new_message"||K.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))}};t=H});import{readFileSync as I,writeFileSync as O,existsSync as q,mkdirSync as G,readdirSync as v}from"fs";import{join as R}from"path";import{spawn as r}from"child_process";var W=process.argv.slice(2),A=W[0],E=process.env.HOME||process.env.USERPROFILE||"~";function S(){return R(E,".anet","config.json")}function F(){return R(process.cwd(),".anet","profiles")}function T(){let Q=S();if(q(Q))try{return JSON.parse(I(Q,"utf-8"))}catch{}return{}}function o(Q){let z=R(E,".anet");G(z,{recursive:!0}),O(R(z,"config.json"),JSON.stringify(Q,null,2)+`
4
+ `)}function M(Q){let z=R(F(),`${Q}.json`);if(q(z))try{return JSON.parse(I(z,"utf-8"))}catch{}return null}function a(Q,z){let V=F();G(V,{recursive:!0}),O(R(V,`${Q}.json`),JSON.stringify(z,null,2)+`
5
+ `)}function y(){let Q=F();if(!q(Q))return[];return v(Q).filter((z)=>z.endsWith(".json")).map((z)=>z.replace(/\.json$/,""))}function k(){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 e(){let z=m(),B=A(),L=z.profile,Q=z.name,Y=z.alias,X=z.hub||B.hub,V=z.type||"claude-code",Z=z.resume,R=z["teammate-mode"];if(!L||!Y)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(!X)console.error("Error: --hub is required (first time) or set in ~/.anet/config.json"),process.exit(1);let W={};for(let O of z._envs){let G=O.indexOf("=");if(G>0)W[O.slice(0,G)]=O.slice(G+1)}let $={...Q?{name:Q}:{},alias:Y,hub:X,channels:z._channels.length>0?z._channels:["server:commhub"],env:W,flags:{dangerouslySkipPermissions:!0,...R?{teammateMode:R}:{}},...Z?{resume:Z}:{}},q=E(T,".anet");F(q,{recursive:!0});let K=A();if(K.hub=X,M(c(),JSON.stringify(K,null,2)+`
37
- `),s(L,$),console.log(`
38
- ✅ Profile "${L}" saved to .anet/profiles/${L}.json`),V==="claude-code")await zz(X,Y);let H=d($);console.log(`
39
- 启动命令 (anet start ${L}):
40
- ${H}
41
- `)}async function zz(z,B){let L=`${T}/.claude/channels/commhub`;F(L,{recursive:!0});let Q=`${L}/server.ts`;if(!J(Q)){console.log("Downloading Channel plugin...");try{let W=await fetch("https://raw.githubusercontent.com/sleep2agi/agent-comm-hub/main/channel/server.ts");if(W.ok)M(Q,await W.text()),console.log(` ✅ ${Q}`)}catch{}try{let W=await fetch("https://raw.githubusercontent.com/sleep2agi/agent-comm-hub/main/channel/package.json");if(W.ok){M(`${L}/package.json`,await W.text());try{r("bun install",{cwd:L,stdio:"pipe"})}catch{}}}catch{}}let Y=`${L}/.env`;if(!J(Y))M(Y,`COMMHUB_URL=${z}
42
- `);let X=process.cwd().replace(/\//g,"-"),V=`${L}/${X}`;F(V,{recursive:!0}),M(`${V}/.env`,`COMMHUB_ALIAS=${B}
43
- `);let Z=`${T}/.claude.json`,R={};if(J(Z))try{R=JSON.parse(I(Z,"utf-8"))}catch{}if(!R.mcpServers?.commhub)R.mcpServers=R.mcpServers||{},R.mcpServers.commhub={type:"stdio",command:"bun",args:["run",Q]},M(Z,JSON.stringify(R,null,2)+`
44
- `)}function d(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 u(){let z=_[1];if(!z){let V=P();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 Z of V){let R=x(Z);console.log(` ${Z} → alias: ${R?.alias}, channels: ${R?.channels.join(", ")}`)}console.log(`
47
- Start with: anet start <profile>
48
- `);return}let B=x(z);if(!B)console.error(`Profile "${z}" not found in .anet/profiles/`),console.error(`Available: ${P().join(", ")||"(none)"}`),process.exit(1);let L=d(B);console.log(`[anet] Starting "${z}"...`),console.log(`[anet] ${L}
49
- `);let Q={...process.env,COMMHUB_ALIAS:B.alias};for(let[V,Z]of Object.entries(B.env))Q[V]=Z.replace(/^~/,T);let Y=[];if(B.flags.dangerouslySkipPermissions)Y.push("--dangerously-skip-permissions");for(let V of B.channels)if(V.startsWith("server:"))Y.push("--dangerously-load-development-channels",V);else Y.push("--channels",V);if(B.flags.teammateMode)Y.push("--teammate-mode",B.flags.teammateMode);if(B.resume)Y.push("--resume",B.resume);a("claude",Y,{env:Q,stdio:"inherit",shell:!0}).on("exit",(V)=>process.exit(V||0))}async function Bz(){let z=process.cwd(),B=P();if(B.length>0){console.log(`
50
- Profiles (.anet/profiles/):
51
- `);for(let R of B){let W=x(R);console.log(` ${R}${W?.name?` (${W.name})`:""}`),console.log(` alias: ${W?.alias} channels: ${W?.channels.join(", ")}`),console.log()}}let L=E(T,".claude","sessions"),Q=[];if(J(L))for(let R of g(L).filter((W)=>W.endsWith(".json")))try{let W=JSON.parse(I(E(L,R),"utf-8"));if(W.cwd===z)Q.push(W)}catch{}let X=A().hub,V={};if(X)try{let W=await(await fetch(`${X}/api/status`)).json();if(W.sessions)for(let $ of W.sessions)V[$.resume_id]=$}catch{}let Z={};if(X)try{Z=(await(await fetch(`${X}/health`)).json()).sse_sessions||{}}catch{}if(Q.length>0){console.log(`Sessions in ${z}:
52
- `),console.log(" SESSION ID PID NETWORK STATUS"),console.log(" ─────────────────── ─────── ──────────────────────────");for(let R of Q){let W=R.sessionId.slice(0,18),$="",q=!1;for(let[,O]of Object.entries(V))if(O.resume_id?.startsWith(R.sessionId.slice(0,8))){let G=O.alias||"?",v=O.status||"?",N=Z[G]?"SSE":"";$=`${G} ${v} ${N}`,q=!0;break}if(!q){let O=z.replace(/\//g,"-"),G=E(T,".claude","channels","commhub",O,".env");if(J(G)){let N=I(G,"utf-8").match(/COMMHUB_ALIAS=(.+)/);if(N){let j=N[1].trim(),C=Object.values(V).find((D)=>D.alias===j);if(C){let D=Z[j]?"SSE":"";$=`${j} ${C.status} ${D}`,q=!0}else $=`${j} (not registered)`}}}if(!q)$="(not in network)";let K=!1;try{process.kill(R.pid,0),K=!0}catch{}let H=K?`${R.pid}`:`${R.pid} ✕`;console.log(` ${W} ${H.padEnd(7)} ${$}`)}console.log()}else console.log(`
53
- No Claude Code sessions found in ${z}
54
- `);if(X){let R=Object.keys(Z).length,W=Object.keys(V).length;if(console.log(`Network: ${X} (${R} online / ${W} total)`),R>0){console.log(`
55
- ALIAS STATUS SSE`),console.log(" ────────────────── ────────── ───");for(let[$,q]of Object.entries(Z)){let H=Object.values(V).find((O)=>O.alias===$)?.status||"?";console.log(` ${$.padEnd(18)} ${H.padEnd(10)} ${q>0?"●":"○"}`)}}console.log()}if(B.length===0&&Q.length===0)console.log(`Get started: anet setup --profile <id> --alias <alias> --hub <url>
56
- `)}async function Lz(){let z=A(),B=m(),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:Y}=await Promise.resolve().then(() => (S(),y)),X=new Y({url:L,alias:Q});X.on("task",async(V)=>{console.log(`[${Q}] ← ${V.from_session}: ${V.content.slice(0,100)}`),await X.send(V.from_session,`[${Q}] 收到: ${V.content.slice(0,200)}`)}),X.on("connected",()=>console.log(`[${Q}] Connected to ${L}`)),X.on("disconnected",()=>console.log(`[${Q}] Disconnected, reconnecting...`)),process.on("SIGINT",()=>X.disconnect().then(()=>process.exit(0))),process.on("SIGTERM",()=>X.disconnect().then(()=>process.exit(0))),console.log(`[${Q}] Listening on ${L} (Ctrl+C to quit)`)}switch(k){case"setup":e();break;case"start":u();break;case"list":case"ls":Bz();break;case"run":Lz();break;case"--help":case"-h":case void 0:h();break;default:if(x(k))_.splice(0,0,"start"),u();else console.error(`Unknown command: ${k}`),h(),process.exit(1)}
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=k(),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=T();if(V.hub=z,Q.token)V.token=Q.token;o(V),console.log(`
22
+ Saved to ${S()}`),console.log("Next: anet init project")}async function e(){let z=T().hub;if(!z)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let V=R(E,".claude","channels","commhub");G(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)O(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){O($,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))O(Y,`COMMHUB_URL=${z}
23
+ `);console.log(`CommHub URL: ${z}`);let N=R(process.cwd(),".mcp.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:[X]},O(N,JSON.stringify(Z,null,2)+`
24
+ `),console.log(".mcp.json: commhub added");else console.log(".mcp.json: commhub 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=T(),V=k(),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(E,".claude","channels","commhub"),L=process.cwd().replace(/\//g,"-"),U=R(Z,L);if(G(U,{recursive:!0}),O(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=M(N);console.log(` ${N}${Z?.name?` (${Z.name})`:""} → ${Z?.alias} [${Z?.channels.join(", ")}]`)}console.log(`
31
+ anet start <id>
32
+ `);return}let z=M(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(/^~/,E);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
+ `),r("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(`
34
+ Profiles:
35
+ `);for(let Z of Q){let L=M(Z);console.log(` ${Z}${L?.name?` (${L.name})`:""} → ${L?.alias} [${L?.channels.join(", ")}]`)}console.log()}let z=process.cwd(),V=R(E,".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 $=T(),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,"-"),K=R(E,".claude","channels","commhub",B,".env");if(q(K)){let w=I(K,"utf-8").match(/COMMHUB_ALIAS=(.+)/);if(w){let J=w[1].trim(),D=Y.find((f)=>f.alias===J),u=N[J]?"●":"○";_=D?`${J} ${D.status} ${u}`:`${J} (not registered)`}}console.log(` ${L} ${(U?`${Z.pid}`:`${Z.pid}✕`).padEnd(7)} ${_}`)}console.log()}}async function Qz(){let Q=T(),z=k(),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(() => (C(),P)),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(M(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.5",
3
+ "version": "0.0.7",
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",