@sleep2agi/agent-network 0.0.24 → 0.0.26
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 +129 -154
- package/dist/bin/cli.js +16 -16
- package/dist/src/client.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,168 +2,175 @@
|
|
|
2
2
|
|
|
3
3
|
AI Agent 通信网络 — 让 AI Agent 互相发消息、派任务、协作。
|
|
4
4
|
|
|
5
|
+
支持两种 Agent 运行时:
|
|
6
|
+
- **claude-code** — Claude Code CLI(交互式开发)
|
|
7
|
+
- **agent-sdk** — Claude Agent SDK + 任意模型(MiniMax/Claude,自动化)
|
|
8
|
+
|
|
5
9
|
## 安装
|
|
6
10
|
|
|
7
11
|
```bash
|
|
8
|
-
#
|
|
12
|
+
# 必装:anet CLI + CommHub SDK
|
|
9
13
|
npm install -g @sleep2agi/agent-network
|
|
10
14
|
|
|
11
|
-
#
|
|
12
|
-
|
|
15
|
+
# 按需装:
|
|
16
|
+
npm install -g @anthropic-ai/claude-code # claude-code runtime
|
|
17
|
+
npm install -g @sleep2agi/agent-node # agent-sdk runtime
|
|
13
18
|
```
|
|
14
19
|
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
# 1. 配 hub(全局,一次性)
|
|
19
|
-
npx @sleep2agi/agent-network init --hub http://YOUR_COMMHUB_IP:9200
|
|
20
|
-
|
|
21
|
-
# 2. 配项目(下载 channel 插件 + 配 MCP + 生成 CLAUDE.md)
|
|
22
|
-
cd ~/your-project
|
|
23
|
-
npx @sleep2agi/agent-network init project
|
|
24
|
-
|
|
25
|
-
# 3. 创建 profile(保存启动参数)
|
|
26
|
-
npx anet init profile commander --alias 指挥室 --channel server:commhub
|
|
27
|
-
|
|
28
|
-
# 4. 启动(新建 session)
|
|
29
|
-
npx anet start commander
|
|
30
|
-
|
|
31
|
-
# 5. 恢复上次 session
|
|
32
|
-
npx anet resume commander
|
|
33
|
-
|
|
34
|
-
# 6. 查看状态
|
|
35
|
-
npx anet ls
|
|
36
|
-
```
|
|
20
|
+
## 快速开始
|
|
37
21
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Claude Code 启动参数可以非常长:
|
|
22
|
+
### Claude Code Agent
|
|
41
23
|
|
|
42
24
|
```bash
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
--teammate-mode in-process --resume 98039093-...
|
|
25
|
+
anet init --hub http://YOUR_IP:9200
|
|
26
|
+
anet init project
|
|
27
|
+
anet init profile 指挥室 --alias 指挥室 --channel server:commhub
|
|
28
|
+
anet start 指挥室
|
|
48
29
|
```
|
|
49
30
|
|
|
50
|
-
|
|
31
|
+
### MiniMax Agent(低成本)
|
|
51
32
|
|
|
52
33
|
```bash
|
|
53
|
-
anet
|
|
54
|
-
anet
|
|
34
|
+
anet init --hub http://YOUR_IP:9200
|
|
35
|
+
anet init profile 小明1号 \
|
|
36
|
+
--runtime agent-sdk \
|
|
37
|
+
--alias 小明1号 \
|
|
38
|
+
--model MiniMax-M2.7 \
|
|
39
|
+
--tools "Read,Bash,Grep" \
|
|
40
|
+
--env "ANTHROPIC_BASE_URL=https://api.minimax.chat/anthropic" \
|
|
41
|
+
--env "ANTHROPIC_AUTH_TOKEN=your-minimax-key"
|
|
42
|
+
anet start 小明1号
|
|
55
43
|
```
|
|
56
44
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
## CLI 命令
|
|
60
|
-
|
|
61
|
-
### anet init
|
|
45
|
+
## 工作原理
|
|
62
46
|
|
|
63
|
-
|
|
47
|
+
`anet start` 读 profile,根据 `runtime` 自动选择启动方式:
|
|
64
48
|
|
|
65
|
-
```bash
|
|
66
|
-
anet init # → ~/.anet/config.json(hub URL)
|
|
67
|
-
anet init project # → 下载 channel 插件 + 配 MCP + .env
|
|
68
|
-
anet init profile <id> [options] # → .anet/profiles/<id>.json
|
|
69
49
|
```
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
50
|
+
anet start 指挥室 → runtime: claude-code → spawn claude CLI
|
|
51
|
+
anet start 小明1号 → runtime: agent-sdk → spawn agent-node (MiniMax)
|
|
73
52
|
```
|
|
74
|
-
~/.anet/config.json 全局(hub URL,一次性)
|
|
75
|
-
{workpath}/.anet/node-server.ts Channel 插件
|
|
76
|
-
{workpath}/.anet/package.json 依赖声明
|
|
77
|
-
{workpath}/.anet/.env COMMHUB_URL
|
|
78
|
-
{workpath}/.anet/profiles/cmd.json 启动 profile
|
|
79
|
-
{workpath}/.mcp.json MCP 配置(commhub → .anet/node-server.ts)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
全部在项目目录内,不碰全局 `~/.claude/`。
|
|
83
53
|
|
|
84
|
-
|
|
54
|
+
同一目录可以有多个 profile,不同 runtime 共存。
|
|
85
55
|
|
|
86
|
-
|
|
56
|
+
## CLI 命令
|
|
87
57
|
|
|
88
|
-
```bash
|
|
89
|
-
anet init
|
|
90
|
-
# CommHub URL: http://YOUR_IP:9200
|
|
91
|
-
# ✅ CommHub v0.4.1 — 26 sessions, 18 SSE
|
|
92
58
|
```
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
cd ~/my-project
|
|
102
|
-
anet init project
|
|
103
|
-
# ✅ .anet/node-server.ts
|
|
104
|
-
# ✅ Dependencies installed
|
|
105
|
-
# CommHub URL: http://YOUR_IP:9200
|
|
106
|
-
# .mcp.json: commhub → .anet/node-server.ts
|
|
59
|
+
anet init 配 hub URL(全局,一次性)
|
|
60
|
+
anet init project 配项目(claude-code 用:channel 插件 + .mcp.json + CLAUDE.md)
|
|
61
|
+
anet init profile <id> 创建启动配置
|
|
62
|
+
anet start <id> 新建 session
|
|
63
|
+
anet resume <id> 恢复上次 session
|
|
64
|
+
anet ls 查看 profiles + sessions + 网络状态
|
|
107
65
|
```
|
|
108
66
|
|
|
109
|
-
|
|
67
|
+
### anet init profile
|
|
110
68
|
|
|
111
69
|
```bash
|
|
112
|
-
anet init profile <id>
|
|
70
|
+
anet init profile <id> [options]
|
|
113
71
|
```
|
|
114
72
|
|
|
73
|
+
**共用参数:**
|
|
74
|
+
|
|
115
75
|
| 参数 | 说明 |
|
|
116
76
|
|------|------|
|
|
117
|
-
| `<id>` | Profile ID(英文,作为文件名) |
|
|
118
77
|
| `--alias` | CommHub session 别名 |
|
|
119
|
-
| `--
|
|
120
|
-
| `--channel` | 添加 channel(可重复) |
|
|
78
|
+
| `--runtime` | `claude-code`(默认)或 `agent-sdk` |
|
|
121
79
|
| `--env` | 环境变量 K=V(可重复) |
|
|
122
|
-
| `--resume` | Session resume ID |
|
|
123
|
-
| `--teammate-mode` | 如 in-process |
|
|
124
80
|
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
# 带 Telegram 双 channel
|
|
128
|
-
anet init profile commander --alias 指挥室 \
|
|
129
|
-
--channel server:commhub \
|
|
130
|
-
--channel plugin:telegram@claude-plugins-official \
|
|
131
|
-
--env TELEGRAM_STATE_DIR=~/.claude/channels/telegram-vincent \
|
|
132
|
-
--teammate-mode in-process
|
|
81
|
+
**claude-code 参数:**
|
|
133
82
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
83
|
+
| 参数 | 说明 |
|
|
84
|
+
|------|------|
|
|
85
|
+
| `--channel` | Channel(可重复,默认 server:commhub) |
|
|
86
|
+
| `--teammate-mode` | 默认 in-process |
|
|
87
|
+
| `--resume-alias` | 恢复搜索名 |
|
|
88
|
+
|
|
89
|
+
**agent-sdk 参数:**
|
|
90
|
+
|
|
91
|
+
| 参数 | 说明 |
|
|
92
|
+
|------|------|
|
|
93
|
+
| `--model` | 模型名(如 MiniMax-M2.7) |
|
|
94
|
+
| `--tools` | 工具列表,逗号分隔 |
|
|
95
|
+
| `--max-turns` | 每任务最大轮次 |
|
|
137
96
|
|
|
138
97
|
### anet start / resume
|
|
139
98
|
|
|
140
99
|
```bash
|
|
141
100
|
anet start 指挥室 # 新建 session
|
|
142
|
-
anet resume 指挥室 # 恢复上次 session
|
|
101
|
+
anet resume 指挥室 # 恢复上次 session
|
|
143
102
|
anet start # 列出所有 profile
|
|
144
|
-
anet 指挥室 #
|
|
103
|
+
anet 指挥室 # 快捷方式
|
|
145
104
|
```
|
|
146
105
|
|
|
147
|
-
|
|
106
|
+
交互式创建:profile 不存在时自动引导创建(选 runtime、填 alias、model 等)。
|
|
107
|
+
|
|
108
|
+
### anet init project
|
|
109
|
+
|
|
110
|
+
仅 claude-code runtime 需要:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
anet init project
|
|
114
|
+
# ✅ .anet/node-server.ts(Channel 插件)
|
|
115
|
+
# ✅ Dependencies installed
|
|
116
|
+
# ✅ .mcp.json
|
|
117
|
+
# ✅ CLAUDE.md
|
|
118
|
+
```
|
|
148
119
|
|
|
149
|
-
|
|
120
|
+
agent-sdk runtime 不需要 init project。
|
|
121
|
+
|
|
122
|
+
### anet ls
|
|
150
123
|
|
|
151
124
|
```
|
|
152
125
|
Profiles:
|
|
153
|
-
|
|
126
|
+
指挥室 → 指挥室 [server:commhub, plugin:telegram]
|
|
127
|
+
小明1号 → 小明1号 []
|
|
154
128
|
|
|
155
|
-
Sessions (/home/vansin/
|
|
129
|
+
Sessions (/home/vansin/project):
|
|
156
130
|
SESSION PID NETWORK
|
|
157
131
|
──────────────────── ─────── ─────────────────────
|
|
158
132
|
fef0eb55-b39c-4abc 64269 通信龙 offline ●
|
|
159
133
|
```
|
|
160
134
|
|
|
161
|
-
|
|
135
|
+
## Profile 格式
|
|
162
136
|
|
|
163
|
-
|
|
137
|
+
路径:`.anet/profiles/<id>.json`
|
|
164
138
|
|
|
165
|
-
|
|
166
|
-
|
|
139
|
+
anet 和 agent-node 共用同一套配置。
|
|
140
|
+
|
|
141
|
+
### claude-code 示例
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"runtime": "claude-code",
|
|
146
|
+
"alias": "指挥室",
|
|
147
|
+
"hub": "http://YOUR_IP:9200",
|
|
148
|
+
"channels": ["server:commhub", "plugin:telegram@claude-plugins-official"],
|
|
149
|
+
"env": { "TELEGRAM_STATE_DIR": "~/.claude/channels/telegram-vincent" },
|
|
150
|
+
"flags": { "dangerouslySkipPermissions": true, "teammateMode": "in-process" }
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### agent-sdk 示例(MiniMax)
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"runtime": "agent-sdk",
|
|
159
|
+
"alias": "小明1号",
|
|
160
|
+
"hub": "http://YOUR_IP:9200",
|
|
161
|
+
"model": "MiniMax-M2.7",
|
|
162
|
+
"tools": ["Read", "Bash", "Grep"],
|
|
163
|
+
"env": {
|
|
164
|
+
"ANTHROPIC_BASE_URL": "https://api.minimax.chat/anthropic",
|
|
165
|
+
"ANTHROPIC_AUTH_TOKEN": "your-key"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 配置优先级
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
CLI 参数 > profile env > 系统环境变量 > ~/.anet/config.json > 默认值
|
|
167
174
|
```
|
|
168
175
|
|
|
169
176
|
## SDK 代码引用
|
|
@@ -171,67 +178,35 @@ anet run --alias SDK马 --hub http://YOUR_IP:9200
|
|
|
171
178
|
```typescript
|
|
172
179
|
import { CommHub } from '@sleep2agi/agent-network';
|
|
173
180
|
|
|
174
|
-
const hub = new CommHub({
|
|
175
|
-
url: 'http://YOUR_COMMHUB_IP:9200',
|
|
176
|
-
alias: '我的Agent',
|
|
177
|
-
});
|
|
178
|
-
|
|
181
|
+
const hub = new CommHub({ url: 'http://YOUR_IP:9200', alias: '我的Agent' });
|
|
179
182
|
hub.on('task', async (msg) => {
|
|
180
|
-
await hub.send(msg.from_session, '
|
|
183
|
+
await hub.send(msg.from_session, '完成!');
|
|
181
184
|
});
|
|
182
185
|
```
|
|
183
186
|
|
|
184
|
-
```javascript
|
|
185
|
-
// CommonJS
|
|
186
|
-
const { CommHub } = require('@sleep2agi/agent-network');
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### SDK API
|
|
190
|
-
|
|
191
187
|
| 方法 | 说明 |
|
|
192
188
|
|------|------|
|
|
193
|
-
| `hub.send(alias, content
|
|
189
|
+
| `hub.send(alias, content)` | 发任务 |
|
|
194
190
|
| `hub.message(alias, content)` | 发消息 |
|
|
195
|
-
| `hub.reply(taskId, text, status?)` | 回复任务 |
|
|
196
191
|
| `hub.status(state, extra?)` | 更新状态 |
|
|
197
|
-
| `hub.broadcast(content, filter?)` | 广播 |
|
|
198
192
|
| `hub.disconnect()` | 断开 |
|
|
199
193
|
|
|
200
|
-
|
|
201
|
-
|------|------|
|
|
202
|
-
| `task` | 收到任务(已自动 ACK) |
|
|
203
|
-
| `connected` | SSE 连接成功 |
|
|
204
|
-
| `disconnected` | SSE 断开(自动重连) |
|
|
205
|
-
|
|
206
|
-
## 运行时要求
|
|
194
|
+
## 依赖
|
|
207
195
|
|
|
208
|
-
|
|
|
209
|
-
|
|
210
|
-
|
|
|
211
|
-
|
|
|
196
|
+
| 包 | 什么时候装 |
|
|
197
|
+
|---|---------|
|
|
198
|
+
| @sleep2agi/agent-network | 必装(anet CLI) |
|
|
199
|
+
| @anthropic-ai/claude-code | runtime: claude-code |
|
|
200
|
+
| @sleep2agi/agent-node | runtime: agent-sdk |
|
|
201
|
+
| Bun 1.2+ | 部署 CommHub Server |
|
|
212
202
|
|
|
213
|
-
##
|
|
203
|
+
## 相关包
|
|
214
204
|
|
|
215
|
-
|
|
|
216
|
-
|
|
217
|
-
|
|
|
218
|
-
|
|
|
219
|
-
|
|
|
220
|
-
| 0.0.15 | 自动去掉 hub URL 结尾斜杠 |
|
|
221
|
-
| 0.0.14 | init project 自动生成 CLAUDE.md |
|
|
222
|
-
| 0.0.13 | init 交互输入后不再卡住 |
|
|
223
|
-
| 0.0.12 | README 同步 |
|
|
224
|
-
| 0.0.11 | node config 加 anet_version 字段 |
|
|
225
|
-
| 0.0.10 | resumeAlias 字段,resume 按名字搜索 |
|
|
226
|
-
| 0.0.9 | start/resume 分离 |
|
|
227
|
-
| 0.0.8 | init project 所有文件放 .anet/(不碰全局 ~/.claude/) |
|
|
228
|
-
| 0.0.7 | init project 改写 .mcp.json(不写 ~/.claude.json) |
|
|
229
|
-
| 0.0.6 | 三级 init(全局/项目/profile),`anet ls` 简化为当前目录 |
|
|
230
|
-
| 0.0.5 | `anet ls` 显示本地 sessions + CommHub 网络状态 |
|
|
231
|
-
| 0.0.4 | CLI 瘦身 580KB→13KB,Node.js 兼容,profile 系统 |
|
|
232
|
-
| 0.0.3 | `anet setup` 一键配置 Channel 插件 |
|
|
233
|
-
| 0.0.2 | CLI shebang 改为 node |
|
|
234
|
-
| 0.0.1 | 首次发布 |
|
|
205
|
+
| 包 | 说明 |
|
|
206
|
+
|---|------|
|
|
207
|
+
| [@sleep2agi/agent-network](https://www.npmjs.com/package/@sleep2agi/agent-network) | anet CLI + CommHub SDK |
|
|
208
|
+
| [@sleep2agi/agent-node](https://www.npmjs.com/package/@sleep2agi/agent-node) | Agent 运行时 |
|
|
209
|
+
| [@sleep2agi/commhub-server](https://www.npmjs.com/package/@sleep2agi/commhub-server) | CommHub Server |
|
|
235
210
|
|
|
236
211
|
## License
|
|
237
212
|
|
package/dist/bin/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{createRequire as s}from"node:module";var t=Object.defineProperty;var r=(B)=>B;function o(B,Q){this[B]=r.bind(null,Q)}var a=(B,Q)=>{for(var z in Q)t(B,z,{get:Q[z],enumerable:!0,configurable:!0,set:o.bind(Q,z)})};var i=(B,Q)=>()=>(B&&(Q=B(B=0)),Q);var e=s(import.meta.url);var v={};a(v,{default:()=>Bz,CommHub:()=>x});import{EventEmitter as zz}from"events";import{hostname as P}from"os";var x,Bz;var b=i(()=>{x=class x extends zz{url;alias;token;agent;resumeId;heartbeatInterval;reconnectDelay;heartbeatTimer;sseAbort;running=!1;constructor(B){super();if(this.url=B.url.replace(/\/$/,""),this.alias=B.alias,this.token=B.token,this.agent=B.agent||"sdk",this.resumeId=`sdk-${B.alias}-${Date.now().toString(36)}`,this.heartbeatInterval=B.heartbeatInterval??180000,this.reconnectDelay=B.reconnectDelay??3000,B.autoConnect!==!1)this.connect()}log(B){console.log(`[${new Date().toTimeString().slice(0,8)}] [commhub:${this.alias}] ${B}`)}async call(B,Q){let z={"Content-Type":"application/json"};if(this.token)z.Authorization=`Bearer ${this.token}`;let N=await(await fetch(`${this.url}/mcp`,{method:"POST",headers:z,body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:"tools/call",params:{name:B,arguments:Q}})})).
|
|
3
|
-
`)
|
|
4
|
-
`)}function M(B){let Q=K(
|
|
5
|
-
`)}function m(){let B=
|
|
2
|
+
import{createRequire as s}from"node:module";var t=Object.defineProperty;var r=(B)=>B;function o(B,Q){this[B]=r.bind(null,Q)}var a=(B,Q)=>{for(var z in Q)t(B,z,{get:Q[z],enumerable:!0,configurable:!0,set:o.bind(Q,z)})};var i=(B,Q)=>()=>(B&&(Q=B(B=0)),Q);var e=s(import.meta.url);var v={};a(v,{default:()=>Bz,CommHub:()=>x});import{EventEmitter as zz}from"events";import{hostname as P}from"os";var x,Bz;var b=i(()=>{x=class x extends zz{url;alias;token;agent;resumeId;heartbeatInterval;reconnectDelay;heartbeatTimer;sseAbort;running=!1;constructor(B){super();if(this.url=B.url.replace(/\/$/,""),this.alias=B.alias,this.token=B.token,this.agent=B.agent||"sdk",this.resumeId=`sdk-${B.alias}-${Date.now().toString(36)}`,this.heartbeatInterval=B.heartbeatInterval??180000,this.reconnectDelay=B.reconnectDelay??3000,B.autoConnect!==!1)this.connect()}log(B){console.log(`[${new Date().toTimeString().slice(0,8)}] [commhub:${this.alias}] ${B}`)}async call(B,Q){let z={"Content-Type":"application/json",Accept:"application/json, text/event-stream"};if(this.token)z.Authorization=`Bearer ${this.token}`;let N=await(await fetch(`${this.url}/mcp`,{method:"POST",headers:z,body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:"tools/call",params:{name:B,arguments:Q}})})).text(),$=N.match(/data: (.+)/),Y=$?JSON.parse($[1]):JSON.parse(N),Z=Y?.result?.content?.[0]?.text;return Z?JSON.parse(Z):Y}async connect(){if(this.running)return;this.running=!0,await this.status("idle"),this.log("registered"),this.heartbeatTimer=setInterval(()=>{this.status("idle").catch((B)=>this.log(`heartbeat failed: ${B.message}`))},this.heartbeatInterval),this.connectSSE()}async disconnect(){if(this.running=!1,this.sseAbort?.abort(),this.heartbeatTimer)clearInterval(this.heartbeatTimer);await this.status("offline").catch(()=>{}),this.log("disconnected")}async send(B,Q,z="normal"){return this.call("send_task",{alias:B,task:Q,priority:z,from_session:this.alias})}async message(B,Q){return this.call("send_message",{alias:B,message:Q,from_session:this.alias})}async reply(B,Q,z="completed"){return this.call("reply",{task_id:B,text:Q,status:z})}async status(B,Q){return this.call("report_status",{resume_id:this.resumeId,alias:this.alias,status:B,server:P(),hostname:P(),agent:this.agent,project_dir:process.cwd(),...Q})}async getAllStatus(){return this.call("get_all_status",{})}async broadcast(B,Q){return this.call("broadcast",{message:B,filter_server:Q?.server,filter_status:Q?.status})}async connectSSE(){let B=encodeURIComponent(this.alias),Q=`${this.url}/events/${B}`,z=this.reconnectDelay;while(this.running){try{this.sseAbort=new AbortController;let W={Accept:"text/event-stream"};if(this.token)W.Authorization=`Bearer ${this.token}`;let N=await fetch(Q,{headers:W,signal:this.sseAbort.signal});if(!N.ok||!N.body){this.log(`SSE failed: ${N.status}`),await this.sleep(z),z=Math.min(z*1.5,60000);continue}z=this.reconnectDelay;let $=N.body.getReader(),Y=new TextDecoder,Z="";while(this.running){let{done:X,value:U}=await $.read();if(X)break;Z+=Y.decode(U,{stream:!0});let E=Z.split(`
|
|
3
|
+
`);Z=E.pop()||"";for(let O of E){if(!O.startsWith("data: "))continue;try{let L=JSON.parse(O.slice(6));if(L.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(L.type==="new_task"||L.type==="new_message"||L.type==="broadcast")await this.processInbox()}catch{}}}}catch(W){if(W.name==="AbortError")break;this.emit("error",W),this.log(`SSE error: ${W.message}`)}if(this.running)this.emit("disconnected"),this.log(`SSE reconnecting in ${z/1000}s...`),await this.sleep(z),z=Math.min(z*1.5,60000)}}async processInbox(){try{let Q=(await this.call("get_inbox",{alias:this.alias,limit:10}))?.messages||[];for(let z of Q)await this.call("ack_inbox",{alias:this.alias,message_id:z.id}),this.log(`← ${z.from_session}: ${z.content.slice(0,60)}`),this.emit("task",z),this.emit("message",z)}catch(B){this.log(`inbox error: ${B.message}`)}}sleep(B){return new Promise((Q)=>setTimeout(Q,B))}};Bz=x});import{readFileSync as _,writeFileSync as V,existsSync as R,mkdirSync as F,readdirSync as u}from"fs";import{join as K}from"path";import{spawn as S}from"child_process";import{createInterface as Zz}from"readline";var T=process.argv.slice(2),C=T[0],q=process.env.HOME||process.env.USERPROFILE||"~";function g(){return K(q,".anet","config.json")}function A(){return K(process.cwd(),".anet","profiles")}function H(){let B=g();if(R(B))try{return JSON.parse(_(B,"utf-8"))}catch{}return{}}function Qz(B){let Q=K(q,".anet");F(Q,{recursive:!0}),V(K(Q,"config.json"),JSON.stringify(B,null,2)+`
|
|
4
|
+
`)}function M(B){let Q=K(A(),`${B}.json`);if(R(Q))try{return JSON.parse(_(Q,"utf-8"))}catch{}return null}function f(B,Q){let z=A();F(z,{recursive:!0}),V(K(z,`${B}.json`),JSON.stringify(Q,null,2)+`
|
|
5
|
+
`)}function m(){let B=A();if(!R(B))return[];return u(B).filter((Q)=>Q.endsWith(".json")).map((Q)=>Q.replace(/\.json$/,""))}function k(){let B={_channels:[],_envs:[]};for(let Q=0;Q<T.length;Q++){if(T[Q]==="--channel"&&T[Q+1]){B._channels.push(T[++Q]);continue}if(T[Q]==="--env"&&T[Q+1]){B._envs.push(T[++Q]);continue}if(T[Q].startsWith("--")&&T[Q+1]&&!T[Q+1].startsWith("--"))B[T[Q].slice(2)]=T[++Q]}return B}function y(){console.log(`
|
|
6
6
|
anet — AI Agent Network CLI
|
|
7
7
|
|
|
8
8
|
anet init Configure hub URL (global, once)
|
|
@@ -20,10 +20,10 @@ Quick start:
|
|
|
20
20
|
anet init profile 指挥室 --alias 指挥室 --channel server:commhub
|
|
21
21
|
anet start 指挥室 # 新建
|
|
22
22
|
anet resume 指挥室 # 下次恢复
|
|
23
|
-
`)}async function Wz(){let B=
|
|
23
|
+
`)}async function Wz(){let B=k(),Q=B.hub;if(!Q)Q=await J("CommHub URL (e.g. http://YOUR_IP:9200)"),c();if(!Q)console.error("Error: hub URL required"),process.exit(1);Q=Q.replace(/\/+$/,"");try{let N=await(await fetch(`${Q}/health`)).json();console.log(`✅ CommHub v${N.version} — ${N.sessions} sessions, ${N.sse_connections} SSE`)}catch(W){console.error(`❌ Cannot reach ${Q}: ${W.message}`),process.exit(1)}let z=H();if(z.hub=Q,B.token)z.token=B.token;Qz(z),console.log(`
|
|
24
24
|
Saved to ${g()}`),console.log("Next: anet init project")}async function Xz(){let Q=H().hub;if(!Q)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let z=K(process.cwd(),".anet");F(z,{recursive:!0});let W=K(z,"node-server.ts");if(!R(W)){let U=[K(new URL(".",import.meta.url).pathname,"..","..","src","node-server.ts"),K(new URL(".",import.meta.url).pathname,"..","src","node-server.ts"),K(process.argv[1],"..","..","src","node-server.ts")],E=!1;for(let O of U)if(R(O)){V(W,_(O,"utf-8")),console.log(" ✅ .anet/node-server.ts"),E=!0;break}if(!E)console.log(" ❌ Cannot find node-server.ts"),console.log(" Fix: cp $(npm root -g)/@sleep2agi/agent-network/src/node-server.ts .anet/node-server.ts")}else console.log(" Channel plugin: exists");let N=K(z,"package.json");if(!R(N)){V(N,JSON.stringify({private:!0,dependencies:{"@modelcontextprotocol/sdk":"^1.12.0"}},null,2)+`
|
|
25
|
-
`);try{let{execSync:U}=await import("child_process");U("bun install",{cwd:z,stdio:"pipe"}),console.log(" ✅ Dependencies installed")}catch{console.log(" ⚠️ Run: cd .anet && bun install")}}let
|
|
26
|
-
`),console.log(`CommHub URL: ${Q}`);let
|
|
25
|
+
`);try{let{execSync:U}=await import("child_process");U("bun install",{cwd:z,stdio:"pipe"}),console.log(" ✅ Dependencies installed")}catch{console.log(" ⚠️ Run: cd .anet && bun install")}}let $=K(z,".env");V($,`COMMHUB_URL=${Q}
|
|
26
|
+
`),console.log(`CommHub URL: ${Q}`);let Y=K(process.cwd(),".mcp.json"),Z={};if(R(Y))try{Z=JSON.parse(_(Y,"utf-8"))}catch{}if(!Z.mcpServers?.commhub)Z.mcpServers=Z.mcpServers||{},Z.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},V(Y,JSON.stringify(Z,null,2)+`
|
|
27
27
|
`),console.log(".mcp.json: commhub → .anet/node-server.ts");else console.log(".mcp.json: commhub already set");let X=K(process.cwd(),"CLAUDE.md");if(!R(X))V(X,`# Agent Network (CommHub)
|
|
28
28
|
|
|
29
29
|
## 通信方式
|
|
@@ -63,16 +63,16 @@ commhub_get_all_status()
|
|
|
63
63
|
- 回复指挥室用 commhub_send_task(不是 commhub_reply,reply 不推送)
|
|
64
64
|
- 不要猜 alias,用 get_all_status 查
|
|
65
65
|
`),console.log("CLAUDE.md: created");else console.log("CLAUDE.md: already exists");console.log(`
|
|
66
|
-
✅ Project ready. Next: anet init profile <id> --alias <名字> --channel server:commhub`)}function Yz(){let B=T[2];if(!B)console.error("Usage: anet init profile <id> --alias <名字> [--channel ...] [--env ...]"),process.exit(1);let Q=H(),z=
|
|
67
|
-
`),f(B
|
|
68
|
-
✅ Profile "${B}" saved`),console.log(` alias: ${W}`),console.log(` channels: ${
|
|
66
|
+
✅ Project ready. Next: anet init profile <id> --alias <名字> --channel server:commhub`)}function Yz(){let B=T[2];if(!B)console.error("Usage: anet init profile <id> --alias <名字> [--channel ...] [--env ...]"),process.exit(1);let Q=H(),z=k(),W=z.alias||B,N=z.hub||Q.hub;if(!N)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let $={};for(let O of z._envs){let L=O.indexOf("=");if(L>0)$[O.slice(0,L)]=O.slice(L+1)}let Y=z.runtime||"claude-code",Z={anet_version:"0.0.24",...z.name?{name:z.name}:{},runtime:Y,alias:W,hub:N,...z.model?{model:z.model}:{},...z.tools?{tools:z.tools.split(",").map((O)=>O.trim())}:{},channels:z._channels.length>0?z._channels:Y==="claude-code"?["server:commhub"]:[],env:$,flags:{dangerouslySkipPermissions:!0,...Y==="claude-code"?{teammateMode:z["teammate-mode"]||"in-process"}:{},...z["max-turns"]?{maxTurns:parseInt(z["max-turns"])}:{}},...z.resume?{resume:z.resume}:{},...z["resume-alias"]?{resumeAlias:z["resume-alias"]}:{}},X=K(q,".claude","channels","commhub"),U=process.cwd().replace(/\//g,"-"),E=K(X,U);if(F(E,{recursive:!0}),V(K(E,".env"),`COMMHUB_ALIAS=${W}
|
|
67
|
+
`),f(B,Z),console.log(`
|
|
68
|
+
✅ Profile "${B}" saved`),console.log(` alias: ${W}`),console.log(` channels: ${Z.channels.join(", ")}`),Object.keys($).length)console.log(` env: ${Object.keys($).join(", ")}`);console.log(`
|
|
69
69
|
Start: anet start ${B}`)}var G=null;function $z(){if(!G)G=Zz({input:process.stdin,output:process.stdout});return G}function c(){if(G)G.close(),G=null}function J(B,Q){let z=Q?` [${Q}]`:"";return new Promise((W)=>{$z().question(`${B}${z}: `,(N)=>{W(N.trim()||Q||"")})})}async function Nz(B){let Q=H();console.log(`
|
|
70
70
|
Profile "${B}" not found. Let's create it:
|
|
71
|
-
`);let z=await J("Runtime (claude-code / agent-sdk)","claude-code"),W=await J("Alias",B),N,Y=[],Z=
|
|
72
|
-
Run 'anet init' first to configure hub URL`),process.exit(1);let O={anet_version:"0.0.23",alias:W,hub:E,runtime:z,...N?{model:N}:{}
|
|
71
|
+
`);let z=await J("Runtime (claude-code / agent-sdk)","claude-code"),W=await J("Alias",B),N,$=[],Y=[],Z="";if(z==="agent-sdk")N=await J("Model","MiniMax-M2.7"),$=(await J("Tools (comma-separated)","Read,Bash,Grep")).split(",").map((I)=>I.trim()).filter(Boolean);else Y=(await J("Channels (comma-separated)","server:commhub")).split(",").map((I)=>I.trim()).filter(Boolean),Z=await J("Teammate mode","in-process");let X=await J("Extra env (K=V, comma-separated, empty to skip)"),U={};if(X)for(let L of X.split(",")){let I=L.trim().indexOf("=");if(I>0)U[L.trim().slice(0,I)]=L.trim().slice(I+1)}let E=Q.hub;if(!E)console.error(`
|
|
72
|
+
Run 'anet init' first to configure hub URL`),process.exit(1);let O={anet_version:"0.0.23",alias:W,hub:E,runtime:z,...N?{model:N}:{},...$.length?{tools:$}:{},channels:Y,env:U,flags:{dangerouslySkipPermissions:!0,...Z?{teammateMode:Z}:{}}};return f(B,O),c(),console.log(`
|
|
73
73
|
✅ Profile "${B}" saved
|
|
74
74
|
`),O}async function p(B,Q){let z=M(B);if(!z)z=await Nz(B);let W=z.runtime||"claude-code";if(console.log(`[anet] ${Q==="start"?"Starting new":"Resuming"} "${B}" (${z.alias}) [${W}]...
|
|
75
|
-
`),W==="agent-sdk"){let
|
|
75
|
+
`),W==="agent-sdk"){let $=["@sleep2agi/agent-node","--alias",z.alias,"--hub",z.hub];if(z.model)$.push("--model",z.model);if(z.tools?.length)$.push("--tools",z.tools.join(","));if(z.flags?.maxTurns)$.push("--max-turns",String(z.flags.maxTurns));let Y={...process.env};for(let[X,U]of Object.entries(z.env))Y[X]=U.replace(/^~/,q);S("npx",$,{env:Y,stdio:"inherit",shell:!0}).on("exit",(X)=>process.exit(X||0))}else{let $={...process.env,COMMHUB_ALIAS:z.alias};for(let[X,U]of Object.entries(z.env))$[X]=U.replace(/^~/,q);let Y=[];if(z.flags.dangerouslySkipPermissions)Y.push("--dangerously-skip-permissions");for(let X of z.channels)if(X.startsWith("server:"))Y.push("--dangerously-load-development-channels",X);else Y.push("--channels",X);if(z.flags.teammateMode)Y.push("--teammate-mode",z.flags.teammateMode);if(Q==="resume"){let X=z.resumeAlias||z.name||z.alias;Y.push("--resume",X)}Y.push("-n",z.name||z.alias),S("claude",Y,{env:$,stdio:"inherit",shell:!0}).on("exit",(X)=>process.exit(X||0))}}async function h(){let B=T[1];if(!B){d("start");return}await p(B,"start")}async function Uz(){let B=T[1];if(!B){d("resume");return}if(!M(B))console.log(`Profile "${B}" not found. Create one first:
|
|
76
76
|
|
|
77
77
|
anet start ${B}
|
|
78
78
|
`),process.exit(1);await p(B,"resume")}function d(B){let Q=m();if(Q.length===0){console.log("No profiles. Run: anet init profile <id> --alias <名字>");return}console.log(`
|
|
@@ -81,6 +81,6 @@ Profiles:
|
|
|
81
81
|
anet ${B} <id>
|
|
82
82
|
`)}async function Kz(){let B=m();if(B.length>0){console.log(`
|
|
83
83
|
Profiles:
|
|
84
|
-
`);for(let
|
|
85
|
-
`);return}let N=H()
|
|
86
|
-
`),console.log(" SESSION PID NETWORK"),console.log(" ──────────────────── ─────── ─────────────────────");for(let
|
|
84
|
+
`);for(let Z of B){let X=M(Z);console.log(` ${Z}${X?.name?` (${X.name})`:""} → ${X?.alias} [${X?.channels.join(", ")}]`)}console.log()}let Q=process.cwd(),z=K(q,".claude","sessions"),W=[];if(R(z))for(let Z of u(z).filter((X)=>X.endsWith(".json")))try{let X=JSON.parse(_(K(z,Z),"utf-8"));if(X.cwd===Q)W.push(X)}catch{}if(W.length===0&&B.length===0){console.log("No sessions or profiles in this directory."),console.log(`Get started: anet init
|
|
85
|
+
`);return}let N=H(),$=[],Y={};if(N.hub)try{let[Z,X]=await Promise.all([fetch(`${N.hub}/api/status`).then((U)=>U.json()),fetch(`${N.hub}/health`).then((U)=>U.json())]);$=Z.sessions||[],Y=X.sse_sessions||{}}catch{}if(W.length>0){console.log(`Sessions (${Q}):
|
|
86
|
+
`),console.log(" SESSION PID NETWORK"),console.log(" ──────────────────── ─────── ─────────────────────");for(let Z of W){let X=Z.sessionId.slice(0,18),U=!1;try{process.kill(Z.pid,0),U=!0}catch{}let E="(not in network)",O=Q.replace(/\//g,"-"),L=K(q,".claude","channels","commhub",O,".env");if(R(L)){let w=_(L,"utf-8").match(/COMMHUB_ALIAS=(.+)/);if(w){let D=w[1].trim(),j=$.find((l)=>l.alias===D),n=Y[D]?"●":"○";E=j?`${D} ${j.status} ${n}`:`${D} (not registered)`}}console.log(` ${X} ${(U?`${Z.pid}`:`${Z.pid}✕`).padEnd(7)} ${E}`)}console.log()}}async function Tz(){let B=H(),Q=k(),z=process.env.COMMHUB_URL||Q.hub||B.hub||"http://127.0.0.1:9200",W=process.env.COMMHUB_ALIAS||Q.alias;if(!W)console.error("Error: --alias required"),process.exit(1);let{CommHub:N}=await Promise.resolve().then(() => (b(),v)),$=new N({url:z,alias:W});$.on("task",async(Y)=>{console.log(`[${W}] ← ${Y.from_session}: ${Y.content.slice(0,100)}`),await $.send(Y.from_session,`[${W}] 收到: ${Y.content.slice(0,200)}`)}),$.on("connected",()=>console.log(`[${W}] Connected`)),$.on("disconnected",()=>console.log(`[${W}] Reconnecting...`)),process.on("SIGINT",()=>$.disconnect().then(()=>process.exit(0))),console.log(`[${W}] Listening on ${z}`)}switch(C){case"init":if(T[1]==="project")Xz();else if(T[1]==="profile")Yz();else Wz();break;case"start":h();break;case"resume":Uz();break;case"ls":case"list":Kz();break;case"run":Tz();break;case"--help":case"-h":case void 0:y();break;default:if(M(C))T.unshift("start"),h();else console.error(`Unknown: ${C}`),y(),process.exit(1)}
|
package/dist/src/client.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{EventEmitter as
|
|
2
|
-
`);
|
|
1
|
+
import{EventEmitter as X}from"events";import{hostname as R}from"os";class T extends X{url;alias;token;agent;resumeId;heartbeatInterval;reconnectDelay;heartbeatTimer;sseAbort;running=!1;constructor(j){super();if(this.url=j.url.replace(/\/$/,""),this.alias=j.alias,this.token=j.token,this.agent=j.agent||"sdk",this.resumeId=`sdk-${j.alias}-${Date.now().toString(36)}`,this.heartbeatInterval=j.heartbeatInterval??180000,this.reconnectDelay=j.reconnectDelay??3000,j.autoConnect!==!1)this.connect()}log(j){console.log(`[${new Date().toTimeString().slice(0,8)}] [commhub:${this.alias}] ${j}`)}async call(j,z){let q={"Content-Type":"application/json",Accept:"application/json, text/event-stream"};if(this.token)q.Authorization=`Bearer ${this.token}`;let D=await(await fetch(`${this.url}/mcp`,{method:"POST",headers:q,body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:"tools/call",params:{name:j,arguments:z}})})).text(),G=D.match(/data: (.+)/),L=G?JSON.parse(G[1]):JSON.parse(D),F=L?.result?.content?.[0]?.text;return F?JSON.parse(F):L}async connect(){if(this.running)return;this.running=!0,await this.status("idle"),this.log("registered"),this.heartbeatTimer=setInterval(()=>{this.status("idle").catch((j)=>this.log(`heartbeat failed: ${j.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(j,z,q="normal"){return this.call("send_task",{alias:j,task:z,priority:q,from_session:this.alias})}async message(j,z){return this.call("send_message",{alias:j,message:z,from_session:this.alias})}async reply(j,z,q="completed"){return this.call("reply",{task_id:j,text:z,status:q})}async status(j,z){return this.call("report_status",{resume_id:this.resumeId,alias:this.alias,status:j,server:R(),hostname:R(),agent:this.agent,project_dir:process.cwd(),...z})}async getAllStatus(){return this.call("get_all_status",{})}async broadcast(j,z){return this.call("broadcast",{message:j,filter_server:z?.server,filter_status:z?.status})}async connectSSE(){let j=encodeURIComponent(this.alias),z=`${this.url}/events/${j}`,q=this.reconnectDelay;while(this.running){try{this.sseAbort=new AbortController;let B={Accept:"text/event-stream"};if(this.token)B.Authorization=`Bearer ${this.token}`;let D=await fetch(z,{headers:B,signal:this.sseAbort.signal});if(!D.ok||!D.body){this.log(`SSE failed: ${D.status}`),await this.sleep(q),q=Math.min(q*1.5,60000);continue}q=this.reconnectDelay;let G=D.body.getReader(),L=new TextDecoder,F="";while(this.running){let{done:V,value:W}=await G.read();if(V)break;F+=L.decode(W,{stream:!0});let P=F.split(`
|
|
2
|
+
`);F=P.pop()||"";for(let Q of P){if(!Q.startsWith("data: "))continue;try{let M=JSON.parse(Q.slice(6));if(M.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(M.type==="new_task"||M.type==="new_message"||M.type==="broadcast")await this.processInbox()}catch{}}}}catch(B){if(B.name==="AbortError")break;this.emit("error",B),this.log(`SSE error: ${B.message}`)}if(this.running)this.emit("disconnected"),this.log(`SSE reconnecting in ${q/1000}s...`),await this.sleep(q),q=Math.min(q*1.5,60000)}}async processInbox(){try{let z=(await this.call("get_inbox",{alias:this.alias,limit:10}))?.messages||[];for(let q of z)await this.call("ack_inbox",{alias:this.alias,message_id:q.id}),this.log(`← ${q.from_session}: ${q.content.slice(0,60)}`),this.emit("task",q),this.emit("message",q)}catch(j){this.log(`inbox error: ${j.message}`)}}sleep(j){return new Promise((z)=>setTimeout(z,j))}}var $=T;export{$ as default,T as CommHub};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/agent-network",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
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",
|