@bolloon/bolloon-agent 0.1.26 → 0.1.27

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/dist/index.js CHANGED
@@ -1426,9 +1426,9 @@ async function main() {
1426
1426
  const port = parseInt(process.env.PORT || '54188');
1427
1427
  const { createWebServer, openBrowser } = await import('./web/server.js');
1428
1428
  s.info(`启动 Web 服务端口 ${port}...`);
1429
- await createWebServer(port);
1430
- s.success(`浏览器已打开 → http://localhost:${port}`);
1431
- openBrowser(`http://localhost:${port}`);
1429
+ const { port: actualPort } = await createWebServer(port);
1430
+ s.success(`浏览器已打开 → http://localhost:${actualPort}`);
1431
+ openBrowser(`http://localhost:${actualPort}`);
1432
1432
  }
1433
1433
  else if (isNonInteractive) {
1434
1434
  console.log = originalLog;
@@ -2811,19 +2811,52 @@ export async function createWebServer(port = 3000, options = {}) {
2811
2811
  }
2812
2812
  // 安装自改总线 -> SSE 桥
2813
2813
  void installSelfImproveHook();
2814
- return new Promise((resolve) => {
2815
- server.listen(port, () => {
2816
- console.log(`Web 服务器启动完成: http://localhost:${port}`);
2817
- console.log('服务器已监听');
2818
- // 安装 chat bus -> SSE 桥 (供前端 inbox UI 实时刷新)
2819
- void installChatBusHook();
2820
- setInterval(() => {
2821
- for (const client of sseClients) {
2822
- client.res.write(': ping\n\n');
2814
+ // 端口冲突时自动找下一个可用端口(最多 10 次),避免 EADDRINUSE 直接崩溃
2815
+ return new Promise((resolve, reject) => {
2816
+ const maxAttempts = 10;
2817
+ const startPort = port;
2818
+ let currentPort = startPort;
2819
+ let attempt = 0;
2820
+ // 局部可变 server 引用 — listen 失败后必须重新 createServer 再 listen
2821
+ let currentServer = server;
2822
+ const tryListen = () => {
2823
+ currentServer.removeAllListeners('error');
2824
+ currentServer.once('error', onError);
2825
+ currentServer.listen(currentPort, () => {
2826
+ if (currentPort !== startPort) {
2827
+ console.warn(`⚠ 端口 ${startPort} 被占用,已自动切换到 ${currentPort}`);
2823
2828
  }
2824
- }, 30000);
2825
- resolve({ app, server });
2826
- });
2829
+ console.log(`Web 服务器启动完成: http://localhost:${currentPort}`);
2830
+ console.log('服务器已监听');
2831
+ // 安装 chat bus -> SSE 桥 (供前端 inbox UI 实时刷新)
2832
+ void installChatBusHook();
2833
+ setInterval(() => {
2834
+ for (const client of sseClients) {
2835
+ client.res.write(': ping\n\n');
2836
+ }
2837
+ }, 30000);
2838
+ resolve({ app, server: currentServer, port: currentPort });
2839
+ });
2840
+ };
2841
+ const onError = (err) => {
2842
+ if (err && err.code === 'EADDRINUSE' && attempt < maxAttempts - 1) {
2843
+ attempt += 1;
2844
+ const nextPort = currentPort + 1;
2845
+ console.log(`⚠ 端口 ${currentPort} 被占用,尝试 ${nextPort}...`);
2846
+ try {
2847
+ currentServer.close();
2848
+ }
2849
+ catch { /* ignore */ }
2850
+ // 重新创建 server 实例(listen 失败后原 server 无法再次 listen)
2851
+ currentServer = createServer(app);
2852
+ currentPort = nextPort;
2853
+ tryListen();
2854
+ }
2855
+ else {
2856
+ reject(err);
2857
+ }
2858
+ };
2859
+ tryListen();
2827
2860
  });
2828
2861
  }
2829
2862
  function broadcast(data, channelId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolloon/bolloon-agent",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "type": "module",
5
5
  "description": "P2P AI Document Agent - 全局安装后执行 `bolloon` 启动产品",
6
6
  "main": "dist/cli.js",
package/src/index.ts CHANGED
@@ -1629,10 +1629,10 @@ async function main() {
1629
1629
  const { createWebServer, openBrowser } = await import('./web/server.js');
1630
1630
 
1631
1631
  s.info(`启动 Web 服务端口 ${port}...`);
1632
- await createWebServer(port);
1632
+ const { port: actualPort } = await createWebServer(port);
1633
1633
 
1634
- s.success(`浏览器已打开 → http://localhost:${port}`);
1635
- openBrowser(`http://localhost:${port}`);
1634
+ s.success(`浏览器已打开 → http://localhost:${actualPort}`);
1635
+ openBrowser(`http://localhost:${actualPort}`);
1636
1636
  } else if (isNonInteractive) {
1637
1637
  console.log = originalLog;
1638
1638
  console.info = originalInfo;
package/src/web/server.ts CHANGED
@@ -3151,19 +3151,51 @@ app.get('/channels', async (_req, res) => {
3151
3151
  // 安装自改总线 -> SSE 桥
3152
3152
  void installSelfImproveHook();
3153
3153
 
3154
- return new Promise<{ app: express.Express; server: typeof server }>((resolve) => {
3155
- server.listen(port, () => {
3156
- console.log(`Web 服务器启动完成: http://localhost:${port}`);
3157
- console.log('服务器已监听');
3158
- // 安装 chat bus -> SSE 桥 (供前端 inbox UI 实时刷新)
3159
- void installChatBusHook();
3160
- setInterval(() => {
3161
- for (const client of sseClients) {
3162
- client.res.write(': ping\n\n');
3154
+ // 端口冲突时自动找下一个可用端口(最多 10 次),避免 EADDRINUSE 直接崩溃
3155
+ return new Promise<{ app: express.Express; server: ReturnType<typeof createServer>; port: number }>((resolve, reject) => {
3156
+ const maxAttempts = 10;
3157
+ const startPort = port;
3158
+ let currentPort = startPort;
3159
+ let attempt = 0;
3160
+ // 局部可变 server 引用 — listen 失败后必须重新 createServer 再 listen
3161
+ let currentServer: ReturnType<typeof createServer> = server;
3162
+
3163
+ const tryListen = () => {
3164
+ currentServer.removeAllListeners('error');
3165
+ currentServer.once('error', onError);
3166
+ currentServer.listen(currentPort, () => {
3167
+ if (currentPort !== startPort) {
3168
+ console.warn(`⚠ 端口 ${startPort} 被占用,已自动切换到 ${currentPort}`);
3163
3169
  }
3164
- }, 30000);
3165
- resolve({ app, server });
3166
- });
3170
+ console.log(`Web 服务器启动完成: http://localhost:${currentPort}`);
3171
+ console.log('服务器已监听');
3172
+ // 安装 chat bus -> SSE 桥 (供前端 inbox UI 实时刷新)
3173
+ void installChatBusHook();
3174
+ setInterval(() => {
3175
+ for (const client of sseClients) {
3176
+ client.res.write(': ping\n\n');
3177
+ }
3178
+ }, 30000);
3179
+ resolve({ app, server: currentServer, port: currentPort });
3180
+ });
3181
+ };
3182
+
3183
+ const onError = (err: NodeJS.ErrnoException) => {
3184
+ if (err && err.code === 'EADDRINUSE' && attempt < maxAttempts - 1) {
3185
+ attempt += 1;
3186
+ const nextPort = currentPort + 1;
3187
+ console.log(`⚠ 端口 ${currentPort} 被占用,尝试 ${nextPort}...`);
3188
+ try { currentServer.close(); } catch { /* ignore */ }
3189
+ // 重新创建 server 实例(listen 失败后原 server 无法再次 listen)
3190
+ currentServer = createServer(app);
3191
+ currentPort = nextPort;
3192
+ tryListen();
3193
+ } else {
3194
+ reject(err);
3195
+ }
3196
+ };
3197
+
3198
+ tryListen();
3167
3199
  });
3168
3200
  }
3169
3201