@sleep2agi/agent-network 0.0.48 → 0.0.49
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 +114 -134
- package/dist/bin/cli.js +60 -36
- package/package.json +1 -1
- package/src/node-server.ts +4 -2
package/README.md
CHANGED
|
@@ -1,145 +1,112 @@
|
|
|
1
1
|
# @sleep2agi/agent-network
|
|
2
2
|
|
|
3
|
-
AI Agent 通信网络 —
|
|
3
|
+
AI Agent 通信网络 — CLI + SDK + Channel 插件,一个包搞定。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## 安装
|
|
8
|
-
|
|
9
|
-
```bash
|
|
5
|
+
```
|
|
10
6
|
npm install -g @sleep2agi/agent-network
|
|
11
7
|
```
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
当前版本:v0.0.48 | [agent-node](https://www.npmjs.com/package/@sleep2agi/agent-node) v0.7.0 | [commhub-server](https://www.npmjs.com/package/@sleep2agi/commhub-server) v0.4.3
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
## 快速开始
|
|
16
12
|
|
|
17
13
|
```bash
|
|
14
|
+
# 1. 启动通信服务器(首次自动生成 auth token)
|
|
18
15
|
anet server start --port 9200
|
|
19
|
-
```
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
# 2. 配置(交互式,填 hub URL 和 token)
|
|
18
|
+
anet init
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
anet init --hub http://YOUR_IP:9200
|
|
20
|
+
# 3. 启动 Claude Code Agent
|
|
21
|
+
cd ~/your-project
|
|
26
22
|
anet init project
|
|
27
23
|
anet start 指挥室
|
|
28
|
-
```
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
anet init --hub http://YOUR_IP:9200
|
|
33
|
-
anet init profile 小明 --runtime agent-sdk --alias 小明 --model MiniMax-M2.7 --tools all \
|
|
34
|
-
--env "ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic" \
|
|
35
|
-
--env "ANTHROPIC_AUTH_TOKEN=your-key"
|
|
36
|
-
anet start 小明
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
**书生 Intern-S1-Pro:**
|
|
40
|
-
```bash
|
|
41
|
-
anet init profile 书生 --runtime agent-sdk --alias 书生 --model intern-s1-pro --tools all \
|
|
42
|
-
--env "ANTHROPIC_BASE_URL=https://chat.intern-ai.org.cn" \
|
|
43
|
-
--env "ANTHROPIC_AUTH_TOKEN=your-key"
|
|
44
|
-
anet start 书生
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### 3. 查看状态
|
|
25
|
+
# 4. 快速接入已有 session(无需 init profile)
|
|
26
|
+
anet resume 你的Agent --session <session-id>
|
|
48
27
|
|
|
49
|
-
|
|
28
|
+
# 5. 查看状态
|
|
50
29
|
anet ls
|
|
30
|
+
anet session ls # 列出当前项目的 Claude Code session
|
|
31
|
+
anet -v # 查看版本
|
|
51
32
|
```
|
|
52
33
|
|
|
53
34
|
## CLI 命令
|
|
54
35
|
|
|
55
|
-
|
|
36
|
+
| 命令 | 说明 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| `anet init` | 配 hub URL + token(全局,交互式) |
|
|
39
|
+
| `anet init project` | 配当前项目(channel 插件 + .mcp.json + CLAUDE.md) |
|
|
40
|
+
| `anet init profile <id>` | 创建 node 启动配置 |
|
|
41
|
+
| `anet start <id>` | 新建 session |
|
|
42
|
+
| `anet resume <id>` | 恢复 session |
|
|
43
|
+
| `anet resume <id> --session <sid>` | 快速接入已有 session(自动创建配置) |
|
|
44
|
+
| `anet session ls` | 列出当前项目的 session(ID / 大小 / 时间) |
|
|
45
|
+
| `anet ls` | nodes + sessions + 网络状态 |
|
|
46
|
+
| `anet server start` | 启动 CommHub Server |
|
|
47
|
+
| `anet server config` | 查看/设置 server 配置 |
|
|
48
|
+
| `anet import` | 从 CommHub 导入在线 session |
|
|
49
|
+
| `anet -v` | 查看版本 |
|
|
50
|
+
|
|
51
|
+
## 配置体系
|
|
52
|
+
|
|
53
|
+
### 全局配置
|
|
56
54
|
|
|
57
|
-
```bash
|
|
58
|
-
anet server start # 启动 CommHub Server(自动拉 @sleep2agi/commhub-server)
|
|
59
|
-
anet server start --port 9200 # 指定端口
|
|
60
|
-
anet server start --token my-secret # 加认证
|
|
61
55
|
```
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
### Agent 初始化
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
anet init # 配 hub URL(全局,一次性)
|
|
69
|
-
anet init project # 配项目(claude-code 用:channel 插件 + .mcp.json + CLAUDE.md)
|
|
70
|
-
anet init profile <id> [options] # 创建启动 profile
|
|
56
|
+
~/.anet/config.json # hub URL + token(anet init 写入)
|
|
57
|
+
~/.anet/server/config.json # server 配置(port/host/token)
|
|
71
58
|
```
|
|
72
59
|
|
|
73
|
-
###
|
|
60
|
+
### 项目配置
|
|
74
61
|
|
|
75
|
-
```bash
|
|
76
|
-
anet start <id> # 新建 session
|
|
77
|
-
anet resume <id> # 恢复上次 session
|
|
78
|
-
anet start # 列出所有 node 配置
|
|
79
|
-
anet <id> # 快捷启动
|
|
80
62
|
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
已有 Claude Code session 想接入 anet?一条命令:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
cd ~/your-project
|
|
94
|
-
anet resume 你的Agent --session <session-id>
|
|
95
|
-
# 自动创建 .anet/nodes/你的Agent/config.json + 配置 .mcp.json + resume
|
|
63
|
+
{project}/
|
|
64
|
+
├── .mcp.json # commhub MCP server
|
|
65
|
+
└── .anet/
|
|
66
|
+
├── node-server.ts # channel 插件(自动从 npm 包同步)
|
|
67
|
+
├── package.json
|
|
68
|
+
└── nodes/
|
|
69
|
+
└── 指挥室/
|
|
70
|
+
└── config.json # 启动配置
|
|
96
71
|
```
|
|
97
72
|
|
|
98
|
-
|
|
73
|
+
### 配置继承规则
|
|
99
74
|
|
|
100
|
-
|
|
75
|
+
两个 config.json 都会读,**字段级合并**:
|
|
101
76
|
|
|
102
|
-
```
|
|
103
|
-
anet
|
|
77
|
+
```
|
|
78
|
+
项目 .anet/nodes/<id>/config.json 有值的字段优先
|
|
79
|
+
↓ fallback
|
|
80
|
+
全局 ~/.anet/config.json 缺失字段兜底
|
|
104
81
|
```
|
|
105
82
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
**共用:**
|
|
109
|
-
|
|
110
|
-
| 参数 | 说明 |
|
|
111
|
-
|------|------|
|
|
112
|
-
| `--alias` | Agent 名称 |
|
|
113
|
-
| `--runtime` | `claude-code`(默认)或 `agent-sdk` |
|
|
114
|
-
| `--env` | 环境变量 K=V(可重复) |
|
|
115
|
-
|
|
116
|
-
**claude-code:**
|
|
117
|
-
|
|
118
|
-
| 参数 | 说明 |
|
|
119
|
-
|------|------|
|
|
120
|
-
| `--channel` | Channel(可重复,默认 server:commhub) |
|
|
121
|
-
| `--teammate-mode` | 默认 in-process |
|
|
122
|
-
|
|
123
|
-
**agent-sdk:**
|
|
83
|
+
项目 config 不需要写 token/hub,全局配一份所有项目共用。
|
|
124
84
|
|
|
125
|
-
|
|
126
|
-
|------|------|
|
|
127
|
-
| `--model` | 模型名 |
|
|
128
|
-
| `--tools` | `all` 或逗号分隔(Read,Write,Edit,Bash,Glob,Grep,WebSearch,WebFetch) |
|
|
129
|
-
| `--max-turns` | 每任务最大轮次 |
|
|
130
|
-
| `--max-budget` | 每任务预算(美元) |
|
|
85
|
+
### Node 配置示例
|
|
131
86
|
|
|
132
|
-
|
|
87
|
+
路径:`.anet/nodes/<id>/config.json`
|
|
133
88
|
|
|
134
|
-
|
|
89
|
+
**Claude Code:**
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"runtime": "claude-code",
|
|
93
|
+
"alias": "指挥室",
|
|
94
|
+
"hub": "http://YOUR_IP:9200",
|
|
95
|
+
"channels": ["server:commhub"],
|
|
96
|
+
"env": {},
|
|
97
|
+
"flags": { "dangerouslySkipPermissions": true, "teammateMode": "in-process" },
|
|
98
|
+
"resume": "98039093-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
99
|
+
}
|
|
100
|
+
```
|
|
135
101
|
|
|
102
|
+
**MiniMax(agent-sdk):**
|
|
136
103
|
```json
|
|
137
104
|
{
|
|
138
105
|
"runtime": "agent-sdk",
|
|
139
106
|
"alias": "小明",
|
|
140
107
|
"hub": "http://YOUR_IP:9200",
|
|
141
108
|
"model": "MiniMax-M2.7",
|
|
142
|
-
"tools": ["Read", "
|
|
109
|
+
"tools": ["Read", "Bash", "Grep"],
|
|
143
110
|
"env": {
|
|
144
111
|
"ANTHROPIC_BASE_URL": "https://api.minimaxi.com/anthropic",
|
|
145
112
|
"ANTHROPIC_AUTH_TOKEN": "your-key"
|
|
@@ -147,21 +114,50 @@ anet ls # profiles + sessions + 网络状态
|
|
|
147
114
|
}
|
|
148
115
|
```
|
|
149
116
|
|
|
150
|
-
|
|
117
|
+
## Token 认证
|
|
151
118
|
|
|
152
|
-
|
|
119
|
+
```bash
|
|
120
|
+
# 方式 1:anet init 交互式填写
|
|
121
|
+
anet init
|
|
153
122
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
| MiniMax M2.7(国际) | `https://api.minimaxi.com/anthropic` | ✅ 对话 + tool_use |
|
|
157
|
-
| MiniMax M2.7(国内) | `https://api.minimaxi.com/anthropic` | ✅ |
|
|
158
|
-
| 书生 Intern-S1-Pro | `https://chat.intern-ai.org.cn` | ✅ |
|
|
159
|
-
| Claude(默认) | 不设 | ✅ |
|
|
160
|
-
| 任意 Anthropic 兼容 | 对应端点 | — |
|
|
123
|
+
# 方式 2:命令行参数
|
|
124
|
+
anet init --hub http://YOUR_IP:9200 --token your-secret
|
|
161
125
|
|
|
162
|
-
|
|
126
|
+
# 方式 3:server 首次启动自动生成
|
|
127
|
+
anet server start
|
|
128
|
+
# → Generated auth token: xxxx(自动存到全局 + server config)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Token 流转:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
~/.anet/config.json (token)
|
|
135
|
+
↓ 自动传递
|
|
136
|
+
├→ anet start/resume → COMMHUB_TOKEN env → channel 插件
|
|
137
|
+
├→ anet ls/import → Authorization header → CommHub API
|
|
138
|
+
└→ channel 插件启动时也会直接读 ~/.anet/config.json
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
node config 可以单独配 token(覆盖全局),适用于连不同 CommHub 的场景。
|
|
142
|
+
|
|
143
|
+
## 自动配置行为
|
|
144
|
+
|
|
145
|
+
`anet start`/`anet resume` 对 `runtime: "claude-code"` 自动确保:
|
|
163
146
|
|
|
164
|
-
|
|
147
|
+
1. `.anet/node-server.ts` 从 npm 包同步(对比内容,不同才更新)
|
|
148
|
+
2. `.anet/package.json` + 依赖安装
|
|
149
|
+
3. `.mcp.json` 包含 commhub(**已有配置不覆盖**)
|
|
150
|
+
|
|
151
|
+
## 支持的模型
|
|
152
|
+
|
|
153
|
+
| 模型 | ANTHROPIC_BASE_URL | runtime |
|
|
154
|
+
|------|-------------------|---------|
|
|
155
|
+
| Claude | 不设 | claude-code |
|
|
156
|
+
| MiniMax M2.7 | `https://api.minimaxi.com/anthropic` | agent-sdk |
|
|
157
|
+
| 书生 Intern-S1-Pro | `https://chat.intern-ai.org.cn` | agent-sdk |
|
|
158
|
+
| 任意 Anthropic 兼容 | 对应端点 | agent-sdk |
|
|
159
|
+
|
|
160
|
+
## SDK
|
|
165
161
|
|
|
166
162
|
```typescript
|
|
167
163
|
import { CommHub } from '@sleep2agi/agent-network';
|
|
@@ -180,37 +176,21 @@ hub.on('task', async (msg) => {
|
|
|
180
176
|
| `hub.status(state, extra?)` | 更新状态 |
|
|
181
177
|
| `hub.broadcast(content)` | 广播 |
|
|
182
178
|
| `hub.getAllStatus()` | 查看所有 session |
|
|
183
|
-
| `hub.disconnect()` | 断开 |
|
|
184
|
-
|
|
185
|
-
| 事件 | 说明 |
|
|
186
|
-
|------|------|
|
|
187
|
-
| `task` | 收到任务(已自动 ACK) |
|
|
188
|
-
| `connected` | SSE 连接成功 |
|
|
189
|
-
| `disconnected` | SSE 断开(自动重连) |
|
|
190
|
-
|
|
191
|
-
## 依赖
|
|
192
|
-
|
|
193
|
-
| 组件 | 什么时候需要 | 安装 |
|
|
194
|
-
|------|------------|------|
|
|
195
|
-
| @sleep2agi/agent-network | 所有人 | `npm i -g @sleep2agi/agent-network` |
|
|
196
|
-
| @anthropic-ai/claude-code | Claude Code Agent | `npm i -g @anthropic-ai/claude-code` |
|
|
197
|
-
| @sleep2agi/agent-node | agent-sdk Agent | `npm i -g @sleep2agi/agent-node` |
|
|
198
|
-
| @anthropic-ai/claude-agent-sdk | agent-sdk Agent(运行时依赖) | `npm i @anthropic-ai/claude-agent-sdk` |
|
|
199
|
-
| Bun | 启动 CommHub Server | `curl -fsSL https://bun.sh/install \| bash` |
|
|
200
179
|
|
|
201
180
|
## npm 包
|
|
202
181
|
|
|
203
|
-
| 包 | 说明 |
|
|
182
|
+
| 包 | 说明 | 版本 |
|
|
204
183
|
|---|------|------|
|
|
205
|
-
| [@sleep2agi/agent-network](https://www.npmjs.com/package/@sleep2agi/agent-network) | anet CLI + CommHub SDK |
|
|
206
|
-
| [@sleep2agi/agent-node](https://www.npmjs.com/package/@sleep2agi/agent-node) | Agent
|
|
207
|
-
| [@sleep2agi/commhub-server](https://www.npmjs.com/package/@sleep2agi/commhub-server) | CommHub
|
|
184
|
+
| [@sleep2agi/agent-network](https://www.npmjs.com/package/@sleep2agi/agent-network) | anet CLI + CommHub SDK + Channel 插件 | v0.0.48 |
|
|
185
|
+
| [@sleep2agi/agent-node](https://www.npmjs.com/package/@sleep2agi/agent-node) | Agent 运行时(MiniMax/书生/Claude) | v0.7.0 |
|
|
186
|
+
| [@sleep2agi/commhub-server](https://www.npmjs.com/package/@sleep2agi/commhub-server) | CommHub 通信服务器 | v0.4.3 |
|
|
208
187
|
|
|
209
188
|
## 文档
|
|
210
189
|
|
|
211
|
-
- [
|
|
212
|
-
- [
|
|
213
|
-
- [
|
|
190
|
+
- [快速上手](docs/anet-quickstart.md)
|
|
191
|
+
- [CLI 设计](docs/cli-design.md) — 命令 + 配置规范
|
|
192
|
+
- [架构设计](docs/architecture.md)
|
|
193
|
+
- [踩坑经验](docs/pitfalls.md) — Channel 插件开发注意事项
|
|
214
194
|
|
|
215
195
|
## License
|
|
216
196
|
|
package/dist/bin/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var QB=Object.defineProperty;var WB=(Q)=>Q;function XB(Q,W){this[Q]=WB.bind(null,W)}var YB=(Q,W)=>{for(var B in W)QB(Q,B,{get:W[B],enumerable:!0,configurable:!0,set:XB.bind(W,B)})};var ZB=(Q,W)=>()=>(Q&&(W=Q(Q=0)),W);var
|
|
3
|
-
`);Z=
|
|
4
|
-
`)}function
|
|
5
|
-
`)}function F(Q){let W=
|
|
6
|
-
`)}function
|
|
2
|
+
var QB=Object.defineProperty;var WB=(Q)=>Q;function XB(Q,W){this[Q]=WB.bind(null,W)}var YB=(Q,W)=>{for(var B in W)QB(Q,B,{get:W[B],enumerable:!0,configurable:!0,set:XB.bind(W,B)})};var ZB=(Q,W)=>()=>(Q&&(W=Q(Q=0)),W);var m={};YB(m,{default:()=>zB,CommHub:()=>b});import{EventEmitter as $B}from"events";import{hostname as g}from"os";var b,zB;var p=ZB(()=>{b=class b extends $B{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,W){let B={"Content-Type":"application/json",Accept:"application/json, text/event-stream"};if(this.token)B.Authorization=`Bearer ${this.token}`;let $=await(await fetch(`${this.url}/mcp`,{method:"POST",headers:B,body:JSON.stringify({jsonrpc:"2.0",id:Date.now(),method:"tools/call",params:{name:Q,arguments:W}})})).text(),Y=$.match(/data: (.+)/),U=Y?JSON.parse(Y[1]):JSON.parse($),Z=U?.result?.content?.[0]?.text;return Z?JSON.parse(Z):U}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,W,B="normal"){return this.call("send_task",{alias:Q,task:W,priority:B,from_session:this.alias})}async message(Q,W){return this.call("send_message",{alias:Q,message:W,from_session:this.alias})}async reply(Q,W,B="completed"){return this.call("reply",{task_id:Q,text:W,status:B})}async status(Q,W){return this.call("report_status",{resume_id:this.resumeId,alias:this.alias,status:Q,server:g(),hostname:g(),agent:this.agent,project_dir:process.cwd(),...W})}async getAllStatus(){return this.call("get_all_status",{})}async broadcast(Q,W){return this.call("broadcast",{message:Q,filter_server:W?.server,filter_status:W?.status})}async connectSSE(){let Q=encodeURIComponent(this.alias),W=`${this.url}/events/${Q}`,B=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(W,{headers:X,signal:this.sseAbort.signal});if(!$.ok||!$.body){this.log(`SSE failed: ${$.status}`),await this.sleep(B),B=Math.min(B*1.5,60000);continue}B=this.reconnectDelay;let Y=$.body.getReader(),U=new TextDecoder,Z="";while(this.running){let{done:z,value:N}=await Y.read();if(z)break;Z+=U.decode(N,{stream:!0});let K=Z.split(`
|
|
3
|
+
`);Z=K.pop()||"";for(let V of K){if(!V.startsWith("data: "))continue;try{let _=JSON.parse(V.slice(6));if(_.type==="connected"){this.log("SSE connected"),this.emit("connected");continue}if(_.type==="new_task"||_.type==="new_message"||_.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 ${B/1000}s...`),await this.sleep(B),B=Math.min(B*1.5,60000)}}async processInbox(){try{let W=(await this.call("get_inbox",{alias:this.alias,limit:10}))?.messages||[];for(let B of W)await this.call("ack_inbox",{alias:this.alias,message_id:B.id}),this.log(`← ${B.from_session}: ${B.content.slice(0,60)}`),this.emit("task",B),this.emit("message",B)}catch(Q){this.log(`inbox error: ${Q.message}`)}}sleep(Q){return new Promise((W)=>setTimeout(W,Q))}};zB=b});import{readFileSync as O,writeFileSync as E,existsSync as q,mkdirSync as M,readdirSync as D,statSync as w}from"fs";import{join as L}from"path";import{spawn as k,execSync as a}from"child_process";import{createInterface as KB}from"readline";var R=process.argv.slice(2),j=R[0],G=process.env.HOME||process.env.USERPROFILE||"~";function t(){return L(G,".anet","config.json")}function S(){return L(G,".anet","server","config.json")}function x(){return L(process.cwd(),".anet","nodes")}function n(){return A().token||process.env.COMMHUB_TOKEN||J().token||""}function u(Q){let W=Q||n();return W?{Authorization:`Bearer ${W}`}:{}}function J(){let Q=t();if(q(Q))try{return JSON.parse(O(Q,"utf-8"))}catch{}return{}}function i(Q){let W=L(G,".anet");M(W,{recursive:!0}),E(L(W,"config.json"),JSON.stringify(Q,null,2)+`
|
|
4
|
+
`)}function c(){let Q=S();if(q(Q))try{return JSON.parse(O(Q,"utf-8"))}catch{}return{}}function d(Q){let W=L(G,".anet","server");M(W,{recursive:!0}),E(L(W,"config.json"),JSON.stringify(Q,null,2)+`
|
|
5
|
+
`)}function F(Q){let W=L(x(),Q,"config.json");if(!q(W))return null;try{let B=JSON.parse(O(W,"utf-8")),X=J();return{...B,hub:B.hub||X.hub||"",token:B.token||X.token||"",env:{...B.env},flags:{...B.flags}}}catch{return null}}function v(Q,W){let B=L(x(),Q);M(B,{recursive:!0}),E(L(B,"config.json"),JSON.stringify(W,null,2)+`
|
|
6
|
+
`)}function y(){let Q=x();if(!q(Q))return[];return D(Q).filter((W)=>q(L(Q,W,"config.json")))}function A(){let Q={_channels:[],_envs:[]};for(let W=0;W<R.length;W++){if(R[W]==="--channel"&&R[W+1]){Q._channels.push(R[++W]);continue}if(R[W]==="--env"&&R[W+1]){Q._envs.push(R[++W]);continue}if(R[W].startsWith("--")&&R[W+1]&&!R[W+1].startsWith("--"))Q[R[W].slice(2)]=R[++W]}return Q}function l(){console.log(`
|
|
7
7
|
anet — AI Agent Network CLI
|
|
8
8
|
|
|
9
9
|
anet init Configure hub URL (global, once)
|
|
@@ -23,12 +23,12 @@ Quick start:
|
|
|
23
23
|
anet init --hub http://IP:9200
|
|
24
24
|
anet start 指挥室 # Claude Code Agent
|
|
25
25
|
anet start 小明 # MiniMax Agent (runtime: agent-sdk)
|
|
26
|
-
`)}async function
|
|
27
|
-
Saved to ${
|
|
28
|
-
`);try{
|
|
26
|
+
`)}async function UB(){let Q=A(),W=Q.hub;if(!W)W=await T("CommHub URL (e.g. http://YOUR_IP:9200)");if(!W)C(),console.error("Error: hub URL required"),process.exit(1);W=W.replace(/\/+$/,"");let B=Q.token||"";if(!B)B=await T("Auth token (empty to skip)");C();try{let Y=await(await fetch(`${W}/health`,{headers:B?{Authorization:`Bearer ${B}`}:{}})).json();console.log(`✅ CommHub v${Y.version} — ${Y.sessions} sessions, ${Y.sse_connections} SSE`)}catch($){console.error(`❌ Cannot reach ${W}: ${$.message}`),process.exit(1)}let X=J();if(X.hub=W,B)X.token=B;else if(!X.token)delete X.token;i(X),console.log(`
|
|
27
|
+
Saved to ${t()}`),console.log("Next: anet init project")}async function NB(){let Q=J(),W=Q.hub;if(!W)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let B=L(process.cwd(),".anet");M(B,{recursive:!0});let X=L(B,"node-server.ts");if(!q(X)){let V=[L(new URL(".",import.meta.url).pathname,"..","..","src","node-server.ts"),L(new URL(".",import.meta.url).pathname,"..","src","node-server.ts"),L(process.argv[1],"..","..","src","node-server.ts")],_=!1;for(let H of V)if(q(H)){E(X,O(H,"utf-8")),console.log(" ✅ .anet/node-server.ts"),_=!0;break}if(!_)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 $=L(B,"package.json");if(!q($)){E($,JSON.stringify({private:!0,dependencies:{"@modelcontextprotocol/sdk":"^1.12.0"}},null,2)+`
|
|
28
|
+
`);try{a("bun install",{cwd:B,stdio:"pipe"}),console.log(" ✅ Dependencies installed")}catch{console.log(" ⚠️ Run: cd .anet && bun install")}}let Y=L(B,".env"),U=Q.token||"",Z=`COMMHUB_URL=${W}
|
|
29
29
|
`;if(U)Z+=`COMMHUB_TOKEN=${U}
|
|
30
|
-
`;
|
|
31
|
-
`),console.log(".mcp.json: commhub → .anet/node-server.ts");else console.log(".mcp.json: commhub already set");let
|
|
30
|
+
`;E(Y,Z),console.log(`CommHub URL: ${W}${U?" (with token)":""}`);let z=L(process.cwd(),".mcp.json"),N={};if(q(z))try{N=JSON.parse(O(z,"utf-8"))}catch{}if(!N.mcpServers?.commhub)N.mcpServers=N.mcpServers||{},N.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},E(z,JSON.stringify(N,null,2)+`
|
|
31
|
+
`),console.log(".mcp.json: commhub → .anet/node-server.ts");else console.log(".mcp.json: commhub already set");let K=L(process.cwd(),"CLAUDE.md");if(!q(K))E(K,`# Agent Network (CommHub)
|
|
32
32
|
|
|
33
33
|
## 通信方式
|
|
34
34
|
|
|
@@ -67,32 +67,32 @@ commhub_get_all_status()
|
|
|
67
67
|
- 回复指挥室用 commhub_send_task(不是 commhub_reply,reply 不推送)
|
|
68
68
|
- 不要猜 alias,用 get_all_status 查
|
|
69
69
|
`),console.log("CLAUDE.md: created");else console.log("CLAUDE.md: already exists");console.log(`
|
|
70
|
-
✅ Project ready. Next: anet init profile <id> --alias <名字> --channel server:commhub`)}function
|
|
71
|
-
`),
|
|
70
|
+
✅ Project ready. Next: anet init profile <id> --alias <名字> --channel server:commhub`)}function LB(){let Q=R[2];if(!Q)console.error("Usage: anet init profile <id> --alias <名字> [--channel ...] [--env ...]"),process.exit(1);let W=J(),B=A(),X=B.alias||Q,$=B.hub||W.hub;if(!$)console.error("Run 'anet init' first to configure hub URL"),process.exit(1);let Y={};for(let V of B._envs){let _=V.indexOf("=");if(_>0)Y[V.slice(0,_)]=V.slice(_+1)}let U=B.runtime||"claude-code",Z={anet_version:"0.0.24",...B.name?{name:B.name}:{},runtime:U,alias:X,hub:$,...B.model?{model:B.model}:{},...B.tools?{tools:B.tools.split(",").map((V)=>V.trim())}:{},channels:B._channels.length>0?B._channels:U==="claude-code"?["server:commhub"]:[],env:Y,flags:{dangerouslySkipPermissions:!0,...U==="claude-code"?{teammateMode:B["teammate-mode"]||"in-process"}:{},...B["max-turns"]?{maxTurns:parseInt(B["max-turns"])}:{}},...B.resume?{resume:B.resume}:{},...B["resume-alias"]?{resumeAlias:B["resume-alias"]}:{}},z=L(G,".claude","channels","commhub"),N=process.cwd().replace(/\//g,"-"),K=L(z,N);if(M(K,{recursive:!0}),E(L(K,".env"),`COMMHUB_ALIAS=${X}
|
|
71
|
+
`),v(Q,Z),console.log(`
|
|
72
72
|
✅ Profile "${Q}" saved`),console.log(` alias: ${X}`),console.log(` channels: ${Z.channels.join(", ")}`),Object.keys(Y).length)console.log(` env: ${Object.keys(Y).join(", ")}`);console.log(`
|
|
73
|
-
Start: anet start ${Q}`)}var
|
|
73
|
+
Start: anet start ${Q}`)}var I=null;function RB(){if(!I)I=KB({input:process.stdin,output:process.stdout});return I}function C(){if(I)I.close(),I=null}function T(Q,W){let B=W?` [${W}]`:"";return new Promise((X)=>{RB().question(`${Q}${B}: `,($)=>{X($.trim()||W||"")})})}async function VB(Q){let W=J();console.log(`
|
|
74
74
|
Profile "${Q}" not found. Let's create it:
|
|
75
|
-
`);let B=await
|
|
76
|
-
Run 'anet init' first to configure hub URL`),process.exit(1);let
|
|
75
|
+
`);let B=await T("Runtime (claude-code / agent-sdk)","claude-code"),X=await T("Alias",Q),$,Y=[],U=[],Z="";if(B==="agent-sdk")$=await T("Model","MiniMax-M2.7"),Y=(await T("Tools (comma-separated)","Read,Bash,Grep")).split(",").map((H)=>H.trim()).filter(Boolean);else U=(await T("Channels (comma-separated)","server:commhub")).split(",").map((H)=>H.trim()).filter(Boolean),Z=await T("Teammate mode","in-process");let z=await T("Extra env (K=V, comma-separated, empty to skip)"),N={};if(z)for(let _ of z.split(",")){let H=_.trim().indexOf("=");if(H>0)N[_.trim().slice(0,H)]=_.trim().slice(H+1)}let K=W.hub;if(!K)console.error(`
|
|
76
|
+
Run 'anet init' first to configure hub URL`),process.exit(1);let V={anet_version:"0.0.23",alias:X,hub:K,runtime:B,...$?{model:$}:{},...Y.length?{tools:Y}:{},channels:U,env:N,flags:{dangerouslySkipPermissions:!0,...Z?{teammateMode:Z}:{}}};return v(Q,V),C(),console.log(`
|
|
77
77
|
✅ Profile "${Q}" saved
|
|
78
|
-
`),
|
|
79
|
-
`);try{
|
|
80
|
-
`),console.log("[anet] .mcp.json: added commhub");let
|
|
81
|
-
`;if(N)
|
|
82
|
-
`;
|
|
83
|
-
`),console.log("[anet] .mcp.json: added commhub channel server")}async function o(Q,W){let B=F(Q);if(!B)B=await
|
|
84
|
-
`),
|
|
85
|
-
`),console.log(`Quick setup: anet resume ${Q} --session <session-id>`),console.log(`Or create: anet init profile ${Q} --alias ${Q} --resume <session-id>`),process.exit(1);if(
|
|
86
|
-
`)}await o(Q,"resume")}function s(Q){let W=
|
|
78
|
+
`),V}function _B(Q){if((Q.runtime||"claude-code")!=="claude-code")return;if(!Q.channels?.some((V)=>V.includes("commhub")))return;let W=L(process.cwd(),".mcp.json"),B={};if(q(W))try{B=JSON.parse(O(W,"utf-8"))}catch{}let X=L(process.cwd(),".anet"),$=L(X,"node-server.ts"),Y=[L(new URL(".",import.meta.url).pathname,"..","..","src","node-server.ts"),L(new URL(".",import.meta.url).pathname,"..","src","node-server.ts"),L(process.argv[1],"..","..","src","node-server.ts")];for(let V of Y)if(q(V)){M(X,{recursive:!0});let _=O(V,"utf-8"),H=q($)?O($,"utf-8"):"";if(_!==H)E($,_),console.log("[anet] Updated .anet/node-server.ts");break}let U=L(X,"package.json");if(!q(U)){M(X,{recursive:!0}),E(U,JSON.stringify({private:!0,dependencies:{"@modelcontextprotocol/sdk":"^1.12.0"}},null,2)+`
|
|
79
|
+
`);try{a("bun install",{cwd:X,stdio:"pipe"})}catch{}}if(B.mcpServers=B.mcpServers||{},!Object.keys(B.mcpServers).some((V)=>V.includes("commhub")))B.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},E(W,JSON.stringify(B,null,2)+`
|
|
80
|
+
`),console.log("[anet] .mcp.json: added commhub");let z=L(X,".env"),N=Q.token||"",K=`COMMHUB_URL=${Q.hub||"http://127.0.0.1:9200"}
|
|
81
|
+
`;if(N)K+=`COMMHUB_TOKEN=${N}
|
|
82
|
+
`;E(z,K),B.mcpServers=B.mcpServers||{},B.mcpServers.commhub={type:"stdio",command:"bun",args:[".anet/node-server.ts"]},E(W,JSON.stringify(B,null,2)+`
|
|
83
|
+
`),console.log("[anet] .mcp.json: added commhub channel server")}async function o(Q,W){let B=F(Q);if(!B)B=await VB(Q);let X=B.runtime||"claude-code";console.log(`[anet] ${W==="start"?"Starting new":"Resuming"} "${Q}" (${B.alias}) [${X}]...
|
|
84
|
+
`),_B(B);let Y=B.token||"";if(X==="agent-sdk"){let U=["@sleep2agi/agent-node","--alias",B.alias,"--hub",B.hub];if(B.model)U.push("--model",B.model);if(B.tools?.length)U.push("--tools",B.tools.join(","));if(B.flags?.maxTurns)U.push("--max-turns",String(B.flags.maxTurns));let Z={...process.env,...Y?{COMMHUB_TOKEN:Y}:{}};for(let[N,K]of Object.entries(B.env))Z[N]=K.replace(/^~/,G);k("npx",U,{env:Z,stdio:"inherit",shell:!0}).on("exit",(N)=>process.exit(N||0))}else{let U={...process.env,COMMHUB_ALIAS:B.alias,...Y?{COMMHUB_TOKEN:Y}:{}};for(let[N,K]of Object.entries(B.env))U[N]=K.replace(/^~/,G);let Z=[];if(B.flags.dangerouslySkipPermissions)Z.push("--dangerously-skip-permissions");for(let N of B.channels)if(N.startsWith("server:"))Z.push("--dangerously-load-development-channels",N);else Z.push("--channels",N);if(B.flags.teammateMode)Z.push("--teammate-mode",B.flags.teammateMode);if(W==="resume"){let N=B.resume||B.resumeAlias||B.name||B.alias;Z.push("--resume",N)}Z.push("-n",B.name||B.alias),k("claude",Z,{env:U,stdio:"inherit",shell:!0}).on("exit",(N)=>process.exit(N||0))}}async function r(){let Q=R[1];if(!Q){s("start");return}await o(Q,"start")}async function qB(){let Q=R[1];if(!Q){s("resume");return}let W=F(Q);if(!W){let B=A(),X=J(),$=B.hub||X.hub,Y=B.session;if(!Y)console.log(`Profile "${Q}" not found.
|
|
85
|
+
`),console.log(`Quick setup: anet resume ${Q} --session <session-id>`),console.log(`Or create: anet init profile ${Q} --alias ${Q} --resume <session-id>`),process.exit(1);if(!$)console.error("Run 'anet init' first"),process.exit(1);W={runtime:"claude-code",alias:B.alias||Q,hub:$,channels:["server:commhub"],env:{},flags:{dangerouslySkipPermissions:!0,teammateMode:"in-process"},resume:Y},v(Q,W),console.log(`[anet] Created .anet/nodes/${Q}/config.json (resume: ${Y.slice(0,8)}...)
|
|
86
|
+
`)}await o(Q,"resume")}function s(Q){let W=y();if(W.length===0){console.log("No profiles. Run: anet init profile <id> --alias <名字>");return}console.log(`
|
|
87
87
|
Profiles:
|
|
88
88
|
`);for(let B of W){let X=F(B);console.log(` ${B}${X?.name?` (${X.name})`:""} → ${X?.alias} [${X?.channels.join(", ")}]`)}console.log(`
|
|
89
89
|
anet ${Q} <id>
|
|
90
|
-
`)}async function
|
|
90
|
+
`)}async function EB(){let Q=y();if(Q.length>0){console.log(`
|
|
91
91
|
Profiles:
|
|
92
|
-
`);for(let Z of Q){let
|
|
93
|
-
`);return}let
|
|
94
|
-
`),console.log(" SESSION PID NETWORK"),console.log(" ──────────────────── ─────── ─────────────────────");for(let Z of X){let
|
|
95
|
-
`);d({port:X,host
|
|
92
|
+
`);for(let Z of Q){let z=F(Z);console.log(` ${Z}${z?.name?` (${z.name})`:""} → ${z?.alias} [${z?.channels.join(", ")}]`)}console.log()}let W=process.cwd(),B=L(G,".claude","sessions"),X=[];if(q(B))for(let Z of D(B).filter((z)=>z.endsWith(".json")))try{let z=JSON.parse(O(L(B,Z),"utf-8"));if(z.cwd===W)X.push(z)}catch{}if(X.length===0&&Q.length===0){console.log("No sessions or profiles in this directory."),console.log(`Get started: anet init
|
|
93
|
+
`);return}let $=J(),Y=[],U={};if($.hub)try{let[Z,z]=await Promise.all([fetch(`${$.hub}/api/status`,{headers:u()}).then((N)=>N.json()),fetch(`${$.hub}/health`,{headers:u()}).then((N)=>N.json())]);Y=Z.sessions||[],U=z.sse_sessions||{}}catch{}if(X.length>0){console.log(`Sessions (${W}):
|
|
94
|
+
`),console.log(" SESSION PID NETWORK"),console.log(" ──────────────────── ─────── ─────────────────────");for(let Z of X){let z=Z.sessionId.slice(0,18),N=!1;try{process.kill(Z.pid,0),N=!0}catch{}let K="(not in network)",V=W.replace(/\//g,"-"),_=L(G,".claude","channels","commhub",V,".env");if(q(_)){let h=O(_,"utf-8").match(/COMMHUB_ALIAS=(.+)/);if(h){let P=h[1].trim(),f=Y.find((BB)=>BB.alias===P),e=U[P]?"●":"○";K=f?`${P} ${f.status} ${e}`:`${P} (not registered)`}}console.log(` ${z} ${(N?`${Z.pid}`:`${Z.pid}✕`).padEnd(7)} ${K}`)}console.log()}}async function HB(){let Q=J(),W=A(),B=process.env.COMMHUB_URL||W.hub||Q.hub||"http://127.0.0.1:9200",X=process.env.COMMHUB_ALIAS||W.alias;if(!X)console.error("Error: --alias required"),process.exit(1);let{CommHub:$}=await Promise.resolve().then(() => (p(),m)),Y=new $({url:B,alias:X});Y.on("task",async(U)=>{console.log(`[${X}] ← ${U.from_session}: ${U.content.slice(0,100)}`),await Y.send(U.from_session,`[${X}] 收到: ${U.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 ${B}`)}async function OB(){let Q=R[1];if(Q==="start"){let W=A(),B=c(),X=W.port||B.port||"9200",$=W.host||B.host||"0.0.0.0",Y=W.token||B.token||n();if(!Y)Y=crypto.randomUUID().replace(/-/g,""),console.log(`[anet] Generated auth token: ${Y}`),console.log(`[anet] Save this token — agents need it to connect.
|
|
95
|
+
`);d({port:X,host:$,token:Y});let U=J();if(!U.token)U.token=Y,i(U);console.log(`[anet] Starting CommHub Server on ${$}:${X}${Y?" (auth enabled)":""}...`);let Z={...process.env,PORT:X,HOST:$};if(Y)Z.COMMHUB_AUTH_TOKEN=Y;k("bunx",["@sleep2agi/commhub-server"],{env:Z,stdio:"inherit",shell:!0}).on("exit",(N)=>process.exit(N||0))}else if(Q==="config"){let W=A(),B=c();if(W.port)B.port=W.port;if(W.host)B.host=W.host;if(W.token)B.token=W.token;if(W.port||W.host||W.token)d(B),console.log(`Server config saved: ${S()}`);console.log(JSON.stringify(B,null,2))}else console.log(`
|
|
96
96
|
anet server <command>
|
|
97
97
|
|
|
98
98
|
start [options] Start CommHub Server
|
|
@@ -103,19 +103,43 @@ Options:
|
|
|
103
103
|
--host <host> Bind address (default: 0.0.0.0)
|
|
104
104
|
--token <token> Auth token
|
|
105
105
|
|
|
106
|
-
Config: ${
|
|
106
|
+
Config: ${S()}
|
|
107
107
|
First 'anet server start' saves config, after that just 'anet server start'.
|
|
108
108
|
|
|
109
109
|
Example:
|
|
110
110
|
anet server start --port 9200 --token my-secret # 首次,保存配置
|
|
111
111
|
anet server start # 之后直接启动
|
|
112
112
|
anet server config # 查看配置
|
|
113
|
-
`)}async function
|
|
114
|
-
`),console.log(` ✅ ${
|
|
115
|
-
Imported ${Z} session(s). Use: cd <project> && anet resume <alias>`)}function
|
|
116
|
-
Sessions in ${W} (${
|
|
117
|
-
`),console.log(" SESSION ID SIZE MODIFIED"),console.log(" ────────────────────────────────────── ──────── ────────────────");for(let Y of
|
|
113
|
+
`)}async function TB(){let Q=J(),B=A().hub||Q.hub;if(!B)console.error("Run 'anet init' first"),process.exit(1);let X=[];try{X=(await(await fetch(`${B}/api/status`,{headers:u()})).json()).sessions||[]}catch(z){console.error(`Cannot reach ${B}: ${z.message}`),process.exit(1)}if(X.length===0){console.log("No sessions in CommHub.");return}let $=X.filter((z)=>z.agent==="claude-code"&&z.project_dir);if($.length===0){console.log("No claude-code sessions found.");return}let Y=R[1],U=Y?$.filter((z)=>z.alias===Y):$;if(U.length===0){console.log(`No session found for "${Y}".`);return}let Z=0;for(let z of U){let N=z.project_dir,K=L(N,".anet","nodes",z.alias),V=L(K,"config.json");if(q(V)){console.log(` ⏭ ${z.alias} — already exists (${N})`);continue}if(!q(N)){console.log(` ⚠ ${z.alias} — project_dir not found: ${N}`);continue}let _={runtime:"claude-code",alias:z.alias,hub:B,channels:["server:commhub"],env:{},flags:{dangerouslySkipPermissions:!0,teammateMode:"in-process"},resume:z.resume_id};M(K,{recursive:!0}),E(V,JSON.stringify(_,null,2)+`
|
|
114
|
+
`),console.log(` ✅ ${z.alias} → ${N}/.anet/nodes/${z.alias}/config.json`),Z++}console.log(`
|
|
115
|
+
Imported ${Z} session(s). Use: cd <project> && anet resume <alias>`)}function JB(){let Q=R[1];if(Q==="ls"||Q==="list"||!Q){let W=process.cwd(),B=W.replace(/\//g,"-"),X=L(G,".claude","projects",B);if(!q(X)){console.log(`No sessions for ${W}`);return}let $=D(X).filter((Y)=>Y.endsWith(".jsonl")).sort((Y,U)=>{let Z=w(L(X,Y));return w(L(X,U)).mtimeMs-Z.mtimeMs});if($.length===0){console.log("No sessions.");return}console.log(`
|
|
116
|
+
Sessions in ${W} (${$.length} total):
|
|
117
|
+
`),console.log(" SESSION ID SIZE MODIFIED"),console.log(" ────────────────────────────────────── ──────── ────────────────");for(let Y of $){let U=Y.replace(".jsonl",""),Z=w(L(X,Y)),z=Z.size<1024?`${Z.size}B`:Z.size<1048576?`${(Z.size/1024).toFixed(0)}KB`:`${(Z.size/1024/1024).toFixed(1)}MB`,N=Z.mtime.toISOString().replace("T"," ").slice(0,16);console.log(` ${U} ${z.padStart(8)} ${N}`)}console.log()}else console.log(`
|
|
118
118
|
anet session <command>
|
|
119
119
|
|
|
120
120
|
ls List Claude Code sessions in current project
|
|
121
|
-
`)}
|
|
121
|
+
`)}async function MB(){let Q=R[1],W=A();if(Q==="add"){let B=R[2],X=R[3];if(!B||!X){console.log(`
|
|
122
|
+
anet channel add <type> <node-id> [options]
|
|
123
|
+
|
|
124
|
+
Types: telegram | wechat | feishu
|
|
125
|
+
|
|
126
|
+
Options:
|
|
127
|
+
--bot-token <token> Bot token
|
|
128
|
+
--allow <user-id> Allow user ID
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
anet channel add telegram 指挥室 --bot-token 123:ABC --allow 7612221352
|
|
132
|
+
anet channel add telegram 指挥室 # 交互式
|
|
133
|
+
`);return}let $=F(X);if(!$)console.error(`Node "${X}" not found. Create it first: anet start ${X}`),process.exit(1);let Y=W["bot-token"],U=W.allow;if(!Y)Y=await T(`${B} Bot Token`);if(!U)U=await T("Allow User ID");if(C(),!Y||!U)console.error("Error: bot-token and allow required"),process.exit(1);let Z=L(x(),X,"channels",B);M(Z,{recursive:!0}),M(L(Z,"inbox"),{recursive:!0});let z=B==="telegram"?"TELEGRAM_BOT_TOKEN":B==="wechat"?"WECHAT_BOT_TOKEN":"FEISHU_BOT_TOKEN";E(L(Z,".env"),`${z}=${Y}
|
|
134
|
+
`),E(L(Z,"access.json"),JSON.stringify({dmPolicy:"allowlist",allowFrom:[U],groups:{},pending:{}},null,2)+`
|
|
135
|
+
`);let N=B==="telegram"?"plugin:telegram@claude-plugins-official":B==="wechat"?"plugin:wechat":"plugin:feishu",K=B==="telegram"?"TELEGRAM_STATE_DIR":B==="wechat"?"WECHAT_STATE_DIR":"FEISHU_STATE_DIR";if(!$.channels.includes(N))$.channels.push(N);$.env[K]=Z,v(X,$),console.log(`
|
|
136
|
+
✅ ${B} channel added to "${X}"`),console.log(` ${Z}/`),console.log(" config.json updated")}else if(Q==="ls"){let B=R[2],X=B?[B]:y(),$=!1;for(let Y of X){let U=L(x(),Y,"channels");if(!q(U))continue;let Z=D(U).filter((z)=>{try{return w(L(U,z)).isDirectory()}catch{return!1}});if(Z.length===0)continue;if(!$)console.log(`
|
|
137
|
+
Node Channels:
|
|
138
|
+
`),$=!0;for(let z of Z){let N=L(U,z,"access.json"),K="";if(q(N))try{K=JSON.parse(O(N,"utf-8")).allowFrom?.join(", ")||""}catch{}console.log(` ${Y.padEnd(20)} ${z.padEnd(12)} allow: ${K||"(none)"}`)}}if(!$)console.log("No channels. Add one: anet channel add telegram <node-id>");console.log()}else console.log(`
|
|
139
|
+
anet channel <command>
|
|
140
|
+
|
|
141
|
+
add <type> <node-id> Add channel to a node
|
|
142
|
+
ls [node-id] List channels
|
|
143
|
+
|
|
144
|
+
Data: .anet/nodes/<node-id>/channels/<type>/
|
|
145
|
+
`)}switch(j){case"init":if(R[1]==="project")NB();else if(R[1]==="profile")LB();else UB();break;case"server":OB();break;case"start":r();break;case"resume":qB();break;case"import":TB();break;case"channel":MB();break;case"session":JB();break;case"ls":case"list":EB();break;case"run":HB();break;case"-v":case"--version":case"version":{let Q=JSON.parse(O(L(new URL(".",import.meta.url).pathname,"..","..","package.json"),"utf-8"));console.log(`anet v${Q.version}`);break}case"--help":case"-h":case void 0:l();break;default:if(F(j))R.unshift("start"),r();else console.error(`Unknown: ${j}`),l(),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.49",
|
|
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",
|
package/src/node-server.ts
CHANGED
|
@@ -86,6 +86,8 @@ function sleep(ms: number): Promise<void> {
|
|
|
86
86
|
log(`ENV: URL=${COMMHUB_URL} ALIAS=${ALIAS} RESUME_ID=${RESUME_ID.slice(0, 8)}... TMUX=${TMUX_NAME || "none"} CWD=${process.cwd()} PROJECT_ENV=${projectPath}`);
|
|
87
87
|
|
|
88
88
|
// ── MCP Server with Channel capability ──────────────
|
|
89
|
+
// name 不要拼 alias!Claude Code 用 meta.user 自动加 "· xxx" 后缀
|
|
90
|
+
// 参考: telegram 插件 name 也只是 "telegram",不是 "telegram · vansinhu"
|
|
89
91
|
const mcp = new Server(
|
|
90
92
|
{
|
|
91
93
|
name: "commhub-channel",
|
|
@@ -360,7 +362,7 @@ async function handleSSEEvent(event: any) {
|
|
|
360
362
|
meta: {
|
|
361
363
|
sender: event.from || "hub",
|
|
362
364
|
sender_id: "commhub",
|
|
363
|
-
user: event.from || "hub",
|
|
365
|
+
user: event.from || "hub", // Claude Code 用 meta.user 显示 "commhub · {user}"
|
|
364
366
|
priority: "normal",
|
|
365
367
|
},
|
|
366
368
|
},
|
|
@@ -386,7 +388,7 @@ async function handleSSEEvent(event: any) {
|
|
|
386
388
|
const meta: Record<string, string> = {
|
|
387
389
|
sender: msg.from_session || "hub",
|
|
388
390
|
sender_id: "commhub",
|
|
389
|
-
user: msg.from_session || "hub",
|
|
391
|
+
user: msg.from_session || "hub", // Claude Code 用 meta.user 显示 "commhub · {user}"
|
|
390
392
|
task_id: msg.id,
|
|
391
393
|
priority: msg.priority || "normal",
|
|
392
394
|
};
|