@bolloon/bolloon-agent 0.1.42 → 0.2.1

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.
@@ -0,0 +1,248 @@
1
+ /**
2
+ * P2P 工具调用模块
3
+ * 支持文件和本地信息的 P2P 传递
4
+ */
5
+ // 工具类型枚举
6
+ export var P2PToolType;
7
+ (function (P2PToolType) {
8
+ P2PToolType["FILE_TRANSFER"] = "file_transfer";
9
+ P2PToolType["LOCAL_INFO"] = "local_info";
10
+ P2PToolType["SYSTEM_INFO"] = "system_info";
11
+ P2PToolType["FILE_LIST"] = "file_list";
12
+ })(P2PToolType || (P2PToolType = {}));
13
+ // 文件传输
14
+ export async function transferFile(targetDid, fileInfo, messageId) {
15
+ try {
16
+ const payload = {
17
+ type: 'file',
18
+ tool: P2PToolType.FILE_TRANSFER,
19
+ data: {
20
+ name: fileInfo.name,
21
+ size: fileInfo.size,
22
+ mimeType: fileInfo.mimeType,
23
+ content: fileInfo.content
24
+ }
25
+ };
26
+ const res = await fetch('/api/message-p2p', {
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify({
30
+ targetDid,
31
+ content: JSON.stringify(payload),
32
+ type: 'file'
33
+ })
34
+ });
35
+ if (res.ok) {
36
+ const data = await res.json();
37
+ return {
38
+ success: true,
39
+ messageId: data.messageId || messageId
40
+ };
41
+ }
42
+ else {
43
+ return { success: false, error: '文件传输失败' };
44
+ }
45
+ }
46
+ catch (e) {
47
+ return { success: false, error: e.message };
48
+ }
49
+ }
50
+ // 本地信息查询
51
+ export async function queryLocalInfo(targetDid, query) {
52
+ try {
53
+ const payload = {
54
+ type: 'info_query',
55
+ tool: P2PToolType.LOCAL_INFO,
56
+ query: query
57
+ };
58
+ const res = await fetch('/api/message-p2p', {
59
+ method: 'POST',
60
+ headers: { 'Content-Type': 'application/json' },
61
+ body: JSON.stringify({
62
+ targetDid,
63
+ content: JSON.stringify(payload),
64
+ type: 'ai-dialogue'
65
+ })
66
+ });
67
+ if (res.ok) {
68
+ const data = await res.json();
69
+ return {
70
+ success: true,
71
+ data: data.response,
72
+ messageId: data.messageId
73
+ };
74
+ }
75
+ else {
76
+ return { success: false, error: '信息查询失败' };
77
+ }
78
+ }
79
+ catch (e) {
80
+ return { success: false, error: e.message };
81
+ }
82
+ }
83
+ // 系统信息请求
84
+ export async function getSystemInfo(targetDid) {
85
+ try {
86
+ const payload = {
87
+ type: 'system_info_request',
88
+ tool: P2PToolType.SYSTEM_INFO
89
+ };
90
+ const res = await fetch('/api/message-p2p', {
91
+ method: 'POST',
92
+ headers: { 'Content-Type': 'application/json' },
93
+ body: JSON.stringify({
94
+ targetDid,
95
+ content: JSON.stringify(payload),
96
+ type: 'ai-dialogue'
97
+ })
98
+ });
99
+ if (res.ok) {
100
+ const data = await res.json();
101
+ return {
102
+ success: true,
103
+ data: data.response,
104
+ messageId: data.messageId
105
+ };
106
+ }
107
+ else {
108
+ return { success: false, error: '系统信息获取失败' };
109
+ }
110
+ }
111
+ catch (e) {
112
+ return { success: false, error: e.message };
113
+ }
114
+ }
115
+ // 文件列表请求
116
+ export async function listFiles(targetDid, path = '/') {
117
+ try {
118
+ const payload = {
119
+ type: 'file_list_request',
120
+ tool: P2PToolType.FILE_LIST,
121
+ path: path
122
+ };
123
+ const res = await fetch('/api/message-p2p', {
124
+ method: 'POST',
125
+ headers: { 'Content-Type': 'application/json' },
126
+ body: JSON.stringify({
127
+ targetDid,
128
+ content: JSON.stringify(payload),
129
+ type: 'ai-dialogue'
130
+ })
131
+ });
132
+ if (res.ok) {
133
+ const data = await res.json();
134
+ return {
135
+ success: true,
136
+ data: data.response,
137
+ messageId: data.messageId
138
+ };
139
+ }
140
+ else {
141
+ return { success: false, error: '文件列表获取失败' };
142
+ }
143
+ }
144
+ catch (e) {
145
+ return { success: false, error: e.message };
146
+ }
147
+ }
148
+ // 工具调用入口
149
+ export async function executeP2PTool(request) {
150
+ const { toolName, payload, targetDid } = request;
151
+ if (!targetDid) {
152
+ return { success: false, error: '未指定目标节点' };
153
+ }
154
+ switch (toolName) {
155
+ case P2PToolType.FILE_TRANSFER:
156
+ return transferFile(targetDid, payload.data);
157
+ case P2PToolType.LOCAL_INFO:
158
+ return queryLocalInfo(targetDid, payload.data);
159
+ case P2PToolType.SYSTEM_INFO:
160
+ return getSystemInfo(targetDid);
161
+ case P2PToolType.FILE_LIST:
162
+ return listFiles(targetDid, payload.data?.path || '/');
163
+ default:
164
+ return { success: false, error: `未知工具: ${toolName}` };
165
+ }
166
+ }
167
+ // 本地系统信息获取 (用于响应来自远程的请求)
168
+ export function getLocalSystemInfo() {
169
+ const memUsage = process.memoryUsage();
170
+ const cpuCores = require('os').cpus();
171
+ return {
172
+ platform: process.platform,
173
+ arch: process.arch,
174
+ nodeVersion: process.version,
175
+ memory: {
176
+ total: memUsage.heapTotal,
177
+ free: memUsage.heapTotal - memUsage.heapUsed,
178
+ used: memUsage.heapUsed
179
+ },
180
+ cpu: {
181
+ cores: cpuCores.length,
182
+ model: cpuCores[0]?.model || 'Unknown'
183
+ },
184
+ uptime: process.uptime()
185
+ };
186
+ }
187
+ // 本地文件列表 (用于响应来自远程的请求)
188
+ export function getLocalFileList(dirPath) {
189
+ try {
190
+ // Dynamic import for ESM compatibility
191
+ const pathModule = require('path');
192
+ const fs = require('fs');
193
+ const fullPath = pathModule.resolve(dirPath);
194
+ const files = fs.readdirSync(fullPath);
195
+ const fileList = files.map((name) => {
196
+ const fullFilePath = pathModule.join(fullPath, name);
197
+ let stat;
198
+ try {
199
+ stat = fs.statSync(fullFilePath);
200
+ }
201
+ catch {
202
+ return null;
203
+ }
204
+ return {
205
+ name: name,
206
+ size: stat.size,
207
+ isDirectory: stat.isDirectory(),
208
+ modified: stat.mtime.toISOString()
209
+ };
210
+ }).filter(Boolean);
211
+ return {
212
+ success: true,
213
+ path: fullPath,
214
+ files: fileList
215
+ };
216
+ }
217
+ catch (e) {
218
+ return {
219
+ success: false,
220
+ path: dirPath,
221
+ error: e.message
222
+ };
223
+ }
224
+ }
225
+ // 导出工具名称列表
226
+ export const P2P_TOOLS = [
227
+ {
228
+ name: P2PToolType.FILE_TRANSFER,
229
+ description: '传输文件到远程节点',
230
+ parameters: ['targetDid', 'fileInfo']
231
+ },
232
+ {
233
+ name: P2PToolType.LOCAL_INFO,
234
+ description: '查询远程节点本地信息',
235
+ parameters: ['targetDid', 'query']
236
+ },
237
+ {
238
+ name: P2PToolType.SYSTEM_INFO,
239
+ description: '获取远程节点系统信息',
240
+ parameters: ['targetDid']
241
+ },
242
+ {
243
+ name: P2PToolType.FILE_LIST,
244
+ description: '列出远程节点目录文件',
245
+ parameters: ['targetDid', 'path']
246
+ }
247
+ ];
248
+ console.log('[P2P Tools] 工具模块已加载');
@@ -5209,16 +5209,23 @@ export async function createWebServer(port = 3000, options = {}) {
5209
5209
  const startPort = port;
5210
5210
  let currentPort = startPort;
5211
5211
  let attempt = 0;
5212
+ // 2026-06-24: 默认 loopback bind,避免在 LAN 上暴露 (CORS 已经 * 了)。
5213
+ // Electron 包装里强制 127.0.0.1; CLI 用户想 LAN 访问可显式 BOLLOON_HOST=0.0.0.0
5214
+ const bindHost = options.host ?? process.env.BOLLOON_HOST ?? '127.0.0.1';
5212
5215
  // 局部可变 server 引用 — listen 失败后必须重新 createServer 再 listen
5213
5216
  let currentServer = server;
5214
5217
  const tryListen = () => {
5215
5218
  currentServer.removeAllListeners('error');
5216
5219
  currentServer.once('error', onError);
5217
- currentServer.listen(currentPort, () => {
5220
+ currentServer.listen(currentPort, bindHost, () => {
5218
5221
  if (currentPort !== startPort) {
5219
5222
  console.warn(`⚠ 端口 ${startPort} 被占用,已自动切换到 ${currentPort}`);
5220
5223
  }
5221
- console.log(`Web 服务器启动完成: http://localhost:${currentPort}`);
5224
+ // 2026-06-24: BOLLOON_PORT=NNNN 是 Electron 主进程解析的契约行 (parseable),
5225
+ // 旧 marker ('服务器已监听') 也保留给日志/调试看。
5226
+ console.log(`BOLLOON_PORT=${currentPort}`);
5227
+ console.log(`BOLLOON_HOST=${bindHost}`);
5228
+ console.log(`Web 服务器启动完成: http://${bindHost}:${currentPort}`);
5222
5229
  console.log('服务器已监听');
5223
5230
  // 安装 chat bus -> SSE 桥 (供前端 inbox UI 实时刷新)
5224
5231
  void installChatBusHook();