@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.
- package/README.md +118 -70
- package/dist/bin/cli.js +33 -52
- 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
|
-
#
|
|
14
|
-
|
|
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
|
-
|
|
32
|
+
Claude Code 启动参数可以非常长:
|
|
20
33
|
|
|
21
34
|
```bash
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
anet
|
|
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
|
-
|
|
38
|
-
|
|
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
|
-
|
|
66
|
+
#### anet init
|
|
42
67
|
|
|
43
|
-
|
|
44
|
-
# 自动从 .anet/config.json 读取配置
|
|
45
|
-
anet run
|
|
68
|
+
交互式输入 hub URL,测试连接,保存到 `~/.anet/config.json`。
|
|
46
69
|
|
|
47
|
-
|
|
48
|
-
anet
|
|
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
|
-
|
|
76
|
+
或直接传参:`anet init --hub http://YOUR_IP:9200`
|
|
52
77
|
|
|
53
|
-
|
|
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
|
-
|
|
57
|
-
anet
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
116
|
+
### anet start
|
|
63
117
|
|
|
64
118
|
```bash
|
|
65
|
-
anet
|
|
119
|
+
anet start commander # 启动指定 profile
|
|
120
|
+
anet start # 列出所有 profile
|
|
121
|
+
anet commander # 快捷方式(等于 anet start commander)
|
|
66
122
|
```
|
|
67
123
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
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
|
|
144
|
-
|
|
|
187
|
+
| anet CLI / SDK | Node.js 18+ 或 Bun |
|
|
188
|
+
| CommHub Server | Bun 1.2+(单独部署) |
|
|
145
189
|
|
|
146
|
-
##
|
|
190
|
+
## 版本历史
|
|
147
191
|
|
|
148
|
-
|
|
|
149
|
-
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
|
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
|
|
3
|
-
`);Z
|
|
4
|
-
`)}function
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
anet
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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.
|
|
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",
|