@bolloon/bolloon-agent 0.1.17 → 0.1.18

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.
@@ -3,6 +3,7 @@ import { createServer } from 'http';
3
3
  import { join, dirname } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import * as fs from 'fs/promises';
6
+ import * as fsSync from 'fs';
6
7
  import * as path from 'path';
7
8
  import { createHyperswarmCommunicator, createTopic, KeyManager, AgentAuthManager, } from '@diap/sdk';
8
9
  import { documentReader } from '../documents/reader.js';
@@ -402,7 +403,25 @@ export async function createWebServer(port = 3000) {
402
403
  res.sendFile(join(webRoot, 'index.html'));
403
404
  });
404
405
  app.get('/api-config', (req, res) => {
405
- res.sendFile(join(webRoot, 'api-config.html'));
406
+ // 防御: sendFile 在文件缺失时会异步抛 NotFoundError, 这里用同步读 + send 兜底
407
+ const filePath = join(webRoot, 'api-config.html');
408
+ if (!fsSync.existsSync(filePath)) {
409
+ // 回退到 SPA 主页, 避免 404 崩溃
410
+ return res.status(404).type('text/plain').send('api-config.html not found; please run `npm run build:web`');
411
+ }
412
+ res.sendFile(filePath, (err) => {
413
+ if (err && !res.headersSent) {
414
+ console.error('[api-config] sendFile failed:', err.message);
415
+ res.status(500).type('text/plain').send('api-config.html send error: ' + err.message);
416
+ }
417
+ });
418
+ });
419
+ // 全局兜底: 任何 next(err) 走到这里, 给出结构化 4xx/5xx 而不是默认 HTML
420
+ app.use((err, req, res, _next) => {
421
+ console.error('[server] unhandled error on', req.method, req.path, '-', err?.message || err);
422
+ if (res.headersSent)
423
+ return;
424
+ res.status(err?.status || 500).type('text/plain').send(`Error ${err?.status || 500}: ${err?.message || 'internal error'} on ${req.method} ${req.path}`);
406
425
  });
407
426
  app.get('/events', (req, res) => {
408
427
  const channelId = req.query.channelId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bolloon/bolloon-agent",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "description": "P2P AI Document Agent - 全局安装后执行 `bolloon` 启动产品",
6
6
  "main": "dist/cli.js",
@@ -32,7 +32,7 @@
32
32
  "src/constraint-runtime"
33
33
  ],
34
34
  "dependencies": {
35
- "@bolloon/bolloon-agent": "^0.1.17",
35
+ "@bolloon/bolloon-agent": "^0.1.18",
36
36
  "@bolloon/constraint-runtime": "0.1.0",
37
37
  "@chainsafe/libp2p-noise": "^17.0.0",
38
38
  "@chainsafe/libp2p-yamux": "^8.0.1",
package/src/web/server.ts CHANGED
@@ -3,6 +3,7 @@ import { createServer } from 'http';
3
3
  import { join, dirname } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import * as fs from 'fs/promises';
6
+ import * as fsSync from 'fs';
6
7
  import * as path from 'path';
7
8
  import {
8
9
  HyperswarmCommunicator,
@@ -554,7 +555,27 @@ export async function createWebServer(port: number = 3000) {
554
555
  });
555
556
 
556
557
  app.get('/api-config', (req, res) => {
557
- res.sendFile(join(webRoot, 'api-config.html'));
558
+ // 防御: sendFile 在文件缺失时会异步抛 NotFoundError, 这里用同步读 + send 兜底
559
+ const filePath = join(webRoot, 'api-config.html');
560
+ if (!fsSync.existsSync(filePath)) {
561
+ // 回退到 SPA 主页, 避免 404 崩溃
562
+ return res.status(404).type('text/plain').send('api-config.html not found; please run `npm run build:web`');
563
+ }
564
+ res.sendFile(filePath, (err) => {
565
+ if (err && !res.headersSent) {
566
+ console.error('[api-config] sendFile failed:', err.message);
567
+ res.status(500).type('text/plain').send('api-config.html send error: ' + err.message);
568
+ }
569
+ });
570
+ });
571
+
572
+ // 全局兜底: 任何 next(err) 走到这里, 给出结构化 4xx/5xx 而不是默认 HTML
573
+ app.use((err: any, req: express.Request, res: express.Response, _next: express.NextFunction) => {
574
+ console.error('[server] unhandled error on', req.method, req.path, '-', err?.message || err);
575
+ if (res.headersSent) return;
576
+ res.status(err?.status || 500).type('text/plain').send(
577
+ `Error ${err?.status || 500}: ${err?.message || 'internal error'} on ${req.method} ${req.path}`
578
+ );
558
579
  });
559
580
 
560
581
  app.get('/events', (req, res) => {