@hirohsu/user-web-feedback 2.6.0 → 2.7.0
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/cli.cjs +1785 -430
- package/dist/index.cjs +407 -11
- package/dist/static/settings.html +36 -0
- package/dist/static/settings.js +118 -0
- package/package.json +20 -19
package/dist/index.cjs
CHANGED
|
@@ -20661,6 +20661,7 @@ __export(database_exports, {
|
|
|
20661
20661
|
getPinnedPrompts: () => getPinnedPrompts,
|
|
20662
20662
|
getPromptById: () => getPromptById,
|
|
20663
20663
|
getRecentMCPServerErrors: () => getRecentMCPServerErrors,
|
|
20664
|
+
getSelfProbeSettings: () => getSelfProbeSettings,
|
|
20664
20665
|
getToolEnableConfigs: () => getToolEnableConfigs,
|
|
20665
20666
|
getUserPreferences: () => getUserPreferences,
|
|
20666
20667
|
initDatabase: () => initDatabase,
|
|
@@ -20676,6 +20677,7 @@ __export(database_exports, {
|
|
|
20676
20677
|
queryLogs: () => queryLogs,
|
|
20677
20678
|
queryMCPServerLogs: () => queryMCPServerLogs,
|
|
20678
20679
|
reorderPrompts: () => reorderPrompts,
|
|
20680
|
+
saveSelfProbeSettings: () => saveSelfProbeSettings,
|
|
20679
20681
|
setToolEnabled: () => setToolEnabled,
|
|
20680
20682
|
toggleMCPServerEnabled: () => toggleMCPServerEnabled,
|
|
20681
20683
|
togglePromptPin: () => togglePromptPin,
|
|
@@ -20974,6 +20976,14 @@ function createTables() {
|
|
|
20974
20976
|
}
|
|
20975
20977
|
} catch {
|
|
20976
20978
|
}
|
|
20979
|
+
db.exec(`
|
|
20980
|
+
CREATE TABLE IF NOT EXISTS self_probe_settings (
|
|
20981
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
20982
|
+
enabled INTEGER DEFAULT 0,
|
|
20983
|
+
interval_seconds INTEGER DEFAULT 300,
|
|
20984
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
20985
|
+
)
|
|
20986
|
+
`);
|
|
20977
20987
|
}
|
|
20978
20988
|
function initDefaultSettings() {
|
|
20979
20989
|
if (!db) throw new Error("Database not initialized");
|
|
@@ -22319,6 +22329,55 @@ function cleanupOldCLIExecutionLogs(daysToKeep = 7) {
|
|
|
22319
22329
|
`).run(cutoffDate.toISOString());
|
|
22320
22330
|
return result.changes;
|
|
22321
22331
|
}
|
|
22332
|
+
function getSelfProbeSettings() {
|
|
22333
|
+
const db2 = tryGetDb();
|
|
22334
|
+
if (!db2) return void 0;
|
|
22335
|
+
const row = db2.prepare(`
|
|
22336
|
+
SELECT
|
|
22337
|
+
id,
|
|
22338
|
+
enabled,
|
|
22339
|
+
interval_seconds as intervalSeconds,
|
|
22340
|
+
updated_at as updatedAt
|
|
22341
|
+
FROM self_probe_settings
|
|
22342
|
+
WHERE id = 1
|
|
22343
|
+
`).get();
|
|
22344
|
+
if (!row) return void 0;
|
|
22345
|
+
return {
|
|
22346
|
+
id: row.id,
|
|
22347
|
+
enabled: row.enabled === 1,
|
|
22348
|
+
intervalSeconds: row.intervalSeconds,
|
|
22349
|
+
updatedAt: row.updatedAt
|
|
22350
|
+
};
|
|
22351
|
+
}
|
|
22352
|
+
function saveSelfProbeSettings(settings) {
|
|
22353
|
+
const db2 = tryGetDb();
|
|
22354
|
+
if (!db2) throw new Error("Database not initialized");
|
|
22355
|
+
const existing = getSelfProbeSettings();
|
|
22356
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22357
|
+
if (existing) {
|
|
22358
|
+
db2.prepare(`
|
|
22359
|
+
UPDATE self_probe_settings
|
|
22360
|
+
SET enabled = COALESCE(?, enabled),
|
|
22361
|
+
interval_seconds = COALESCE(?, interval_seconds),
|
|
22362
|
+
updated_at = ?
|
|
22363
|
+
WHERE id = 1
|
|
22364
|
+
`).run(
|
|
22365
|
+
settings.enabled !== void 0 ? settings.enabled ? 1 : 0 : null,
|
|
22366
|
+
settings.intervalSeconds ?? null,
|
|
22367
|
+
now
|
|
22368
|
+
);
|
|
22369
|
+
} else {
|
|
22370
|
+
db2.prepare(`
|
|
22371
|
+
INSERT INTO self_probe_settings (id, enabled, interval_seconds, updated_at)
|
|
22372
|
+
VALUES (1, ?, ?, ?)
|
|
22373
|
+
`).run(
|
|
22374
|
+
settings.enabled ? 1 : 0,
|
|
22375
|
+
settings.intervalSeconds ?? 300,
|
|
22376
|
+
now
|
|
22377
|
+
);
|
|
22378
|
+
}
|
|
22379
|
+
return getSelfProbeSettings();
|
|
22380
|
+
}
|
|
22322
22381
|
var import_better_sqlite3, import_path2, import_fs2, import_crypto2, DB_DIR, DB_PATH, db, SYSTEM_PROMPT_VERSIONS, CURRENT_PROMPT_VERSION;
|
|
22323
22382
|
var init_database = __esm({
|
|
22324
22383
|
"src/utils/database.ts"() {
|
|
@@ -87562,7 +87621,7 @@ var helmet = Object.assign(
|
|
|
87562
87621
|
var import_compression = __toESM(require_compression(), 1);
|
|
87563
87622
|
var import_path6 = __toESM(require("path"), 1);
|
|
87564
87623
|
var import_fs7 = __toESM(require("fs"), 1);
|
|
87565
|
-
var
|
|
87624
|
+
var import_url2 = require("url");
|
|
87566
87625
|
init_logger();
|
|
87567
87626
|
|
|
87568
87627
|
// src/utils/port-manager.ts
|
|
@@ -89119,21 +89178,80 @@ var projectManager = ProjectManager.getInstance();
|
|
|
89119
89178
|
init_cjs_shims();
|
|
89120
89179
|
var import_fs4 = require("fs");
|
|
89121
89180
|
var import_path4 = require("path");
|
|
89181
|
+
var import_url = require("url");
|
|
89182
|
+
var import_meta = {};
|
|
89183
|
+
function getDirname() {
|
|
89184
|
+
try {
|
|
89185
|
+
if (typeof import_meta !== "undefined" && importMetaUrl) {
|
|
89186
|
+
return (0, import_path4.dirname)((0, import_url.fileURLToPath)(importMetaUrl));
|
|
89187
|
+
}
|
|
89188
|
+
} catch {
|
|
89189
|
+
}
|
|
89190
|
+
if (typeof __dirname !== "undefined") {
|
|
89191
|
+
return __dirname;
|
|
89192
|
+
}
|
|
89193
|
+
return process.cwd();
|
|
89194
|
+
}
|
|
89195
|
+
function findPackageJson(startPath) {
|
|
89196
|
+
let currentPath = startPath;
|
|
89197
|
+
const root = process.platform === "win32" ? currentPath.split("\\")[0] + "\\" : "/";
|
|
89198
|
+
while (currentPath !== root) {
|
|
89199
|
+
const pkgPath = (0, import_path4.join)(currentPath, "package.json");
|
|
89200
|
+
if ((0, import_fs4.existsSync)(pkgPath)) {
|
|
89201
|
+
return pkgPath;
|
|
89202
|
+
}
|
|
89203
|
+
const parentPath = (0, import_path4.dirname)(currentPath);
|
|
89204
|
+
if (parentPath === currentPath) {
|
|
89205
|
+
break;
|
|
89206
|
+
}
|
|
89207
|
+
currentPath = parentPath;
|
|
89208
|
+
}
|
|
89209
|
+
return null;
|
|
89210
|
+
}
|
|
89122
89211
|
function getPackageVersion() {
|
|
89123
89212
|
try {
|
|
89124
|
-
const
|
|
89125
|
-
|
|
89126
|
-
|
|
89127
|
-
];
|
|
89128
|
-
for (const pkgPath of possiblePaths) {
|
|
89213
|
+
const currentDir = getDirname();
|
|
89214
|
+
const pkgPath1 = findPackageJson(currentDir);
|
|
89215
|
+
if (pkgPath1) {
|
|
89129
89216
|
try {
|
|
89130
|
-
const pkgContent = (0, import_fs4.readFileSync)(
|
|
89217
|
+
const pkgContent = (0, import_fs4.readFileSync)(pkgPath1, "utf-8");
|
|
89131
89218
|
const pkg = JSON.parse(pkgContent);
|
|
89132
89219
|
if (pkg.version) {
|
|
89133
89220
|
return pkg.version;
|
|
89134
89221
|
}
|
|
89135
89222
|
} catch {
|
|
89136
|
-
|
|
89223
|
+
}
|
|
89224
|
+
}
|
|
89225
|
+
const cwd = process.cwd();
|
|
89226
|
+
if (cwd !== currentDir) {
|
|
89227
|
+
const pkgPath2 = findPackageJson(cwd);
|
|
89228
|
+
if (pkgPath2) {
|
|
89229
|
+
try {
|
|
89230
|
+
const pkgContent = (0, import_fs4.readFileSync)(pkgPath2, "utf-8");
|
|
89231
|
+
const pkg = JSON.parse(pkgContent);
|
|
89232
|
+
if (pkg.version) {
|
|
89233
|
+
return pkg.version;
|
|
89234
|
+
}
|
|
89235
|
+
} catch {
|
|
89236
|
+
}
|
|
89237
|
+
}
|
|
89238
|
+
}
|
|
89239
|
+
const possiblePaths = [
|
|
89240
|
+
(0, import_path4.join)(currentDir, "..", "..", "package.json"),
|
|
89241
|
+
(0, import_path4.join)(currentDir, "..", "package.json"),
|
|
89242
|
+
(0, import_path4.join)(process.cwd(), "package.json")
|
|
89243
|
+
];
|
|
89244
|
+
for (const pkgPath of possiblePaths) {
|
|
89245
|
+
if ((0, import_fs4.existsSync)(pkgPath)) {
|
|
89246
|
+
try {
|
|
89247
|
+
const pkgContent = (0, import_fs4.readFileSync)(pkgPath, "utf-8");
|
|
89248
|
+
const pkg = JSON.parse(pkgContent);
|
|
89249
|
+
if (pkg.version) {
|
|
89250
|
+
return pkg.version;
|
|
89251
|
+
}
|
|
89252
|
+
} catch {
|
|
89253
|
+
continue;
|
|
89254
|
+
}
|
|
89137
89255
|
}
|
|
89138
89256
|
}
|
|
89139
89257
|
return "0.0.0";
|
|
@@ -89302,6 +89420,127 @@ var InstanceLock = class {
|
|
|
89302
89420
|
|
|
89303
89421
|
// src/server/web-server.ts
|
|
89304
89422
|
init_database();
|
|
89423
|
+
|
|
89424
|
+
// src/utils/self-probe-service.ts
|
|
89425
|
+
init_cjs_shims();
|
|
89426
|
+
init_logger();
|
|
89427
|
+
init_database();
|
|
89428
|
+
var SelfProbeService = class {
|
|
89429
|
+
constructor(config2) {
|
|
89430
|
+
this.config = config2;
|
|
89431
|
+
this.enabled = config2.enableSelfProbe ?? false;
|
|
89432
|
+
this.intervalSeconds = config2.selfProbeIntervalSeconds ?? 300;
|
|
89433
|
+
}
|
|
89434
|
+
timer = null;
|
|
89435
|
+
lastProbeTime = null;
|
|
89436
|
+
probeCount = 0;
|
|
89437
|
+
enabled = false;
|
|
89438
|
+
intervalSeconds = 300;
|
|
89439
|
+
context = null;
|
|
89440
|
+
setContext(context) {
|
|
89441
|
+
this.context = context;
|
|
89442
|
+
}
|
|
89443
|
+
start() {
|
|
89444
|
+
if (this.timer) {
|
|
89445
|
+
this.stop();
|
|
89446
|
+
}
|
|
89447
|
+
const dbSettings = getSelfProbeSettings();
|
|
89448
|
+
if (dbSettings) {
|
|
89449
|
+
this.enabled = dbSettings.enabled;
|
|
89450
|
+
this.intervalSeconds = dbSettings.intervalSeconds;
|
|
89451
|
+
}
|
|
89452
|
+
if (!this.enabled) {
|
|
89453
|
+
logger.debug("Self-probe is disabled, not starting");
|
|
89454
|
+
return;
|
|
89455
|
+
}
|
|
89456
|
+
const intervalMs = this.intervalSeconds * 1e3;
|
|
89457
|
+
this.timer = setInterval(() => this.probe(), intervalMs);
|
|
89458
|
+
logger.info(`Self-probe started with interval: ${this.intervalSeconds}s`);
|
|
89459
|
+
}
|
|
89460
|
+
stop() {
|
|
89461
|
+
if (this.timer) {
|
|
89462
|
+
clearInterval(this.timer);
|
|
89463
|
+
this.timer = null;
|
|
89464
|
+
logger.info("Self-probe stopped");
|
|
89465
|
+
}
|
|
89466
|
+
}
|
|
89467
|
+
restart() {
|
|
89468
|
+
this.stop();
|
|
89469
|
+
this.start();
|
|
89470
|
+
}
|
|
89471
|
+
updateSettings(settings) {
|
|
89472
|
+
if (settings.enabled !== void 0) {
|
|
89473
|
+
this.enabled = settings.enabled;
|
|
89474
|
+
}
|
|
89475
|
+
if (settings.intervalSeconds !== void 0) {
|
|
89476
|
+
if (settings.intervalSeconds < 60 || settings.intervalSeconds > 600) {
|
|
89477
|
+
throw new Error("Interval must be between 60 and 600 seconds");
|
|
89478
|
+
}
|
|
89479
|
+
this.intervalSeconds = settings.intervalSeconds;
|
|
89480
|
+
}
|
|
89481
|
+
saveSelfProbeSettings(settings);
|
|
89482
|
+
if (this.enabled) {
|
|
89483
|
+
this.restart();
|
|
89484
|
+
} else {
|
|
89485
|
+
this.stop();
|
|
89486
|
+
}
|
|
89487
|
+
}
|
|
89488
|
+
async probe() {
|
|
89489
|
+
this.lastProbeTime = /* @__PURE__ */ new Date();
|
|
89490
|
+
this.probeCount++;
|
|
89491
|
+
try {
|
|
89492
|
+
if (this.context) {
|
|
89493
|
+
this.checkSocketIO();
|
|
89494
|
+
this.checkMCPStatus();
|
|
89495
|
+
this.triggerSessionCleanup();
|
|
89496
|
+
}
|
|
89497
|
+
logger.debug(`Self-probe #${this.probeCount} completed`);
|
|
89498
|
+
} catch (error2) {
|
|
89499
|
+
logger.warn("Self-probe encountered an issue:", error2);
|
|
89500
|
+
}
|
|
89501
|
+
}
|
|
89502
|
+
checkSocketIO() {
|
|
89503
|
+
if (!this.context) return;
|
|
89504
|
+
const connectedSockets = this.context.getSocketIOConnectionCount();
|
|
89505
|
+
logger.debug(`Self-probe: Socket.IO connected clients: ${connectedSockets}`);
|
|
89506
|
+
}
|
|
89507
|
+
checkMCPStatus() {
|
|
89508
|
+
if (!this.context) return;
|
|
89509
|
+
const mcpStatus = this.context.getMCPServerStatus();
|
|
89510
|
+
logger.debug(`Self-probe: MCP Server running: ${mcpStatus?.running ?? "N/A"}`);
|
|
89511
|
+
}
|
|
89512
|
+
triggerSessionCleanup() {
|
|
89513
|
+
if (!this.context) return;
|
|
89514
|
+
const sessionCount = this.context.getSessionCount();
|
|
89515
|
+
if (sessionCount > 0) {
|
|
89516
|
+
this.context.cleanupExpiredSessions();
|
|
89517
|
+
logger.debug(`Self-probe: Triggered session cleanup, active sessions: ${sessionCount}`);
|
|
89518
|
+
}
|
|
89519
|
+
}
|
|
89520
|
+
getStats() {
|
|
89521
|
+
return {
|
|
89522
|
+
enabled: this.enabled,
|
|
89523
|
+
intervalSeconds: this.intervalSeconds,
|
|
89524
|
+
lastProbeTime: this.lastProbeTime,
|
|
89525
|
+
probeCount: this.probeCount,
|
|
89526
|
+
isRunning: this.timer !== null
|
|
89527
|
+
};
|
|
89528
|
+
}
|
|
89529
|
+
isEnabled() {
|
|
89530
|
+
return this.enabled;
|
|
89531
|
+
}
|
|
89532
|
+
isRunning() {
|
|
89533
|
+
return this.timer !== null;
|
|
89534
|
+
}
|
|
89535
|
+
getProbeCount() {
|
|
89536
|
+
return this.probeCount;
|
|
89537
|
+
}
|
|
89538
|
+
getLastProbeTime() {
|
|
89539
|
+
return this.lastProbeTime;
|
|
89540
|
+
}
|
|
89541
|
+
};
|
|
89542
|
+
|
|
89543
|
+
// src/server/web-server.ts
|
|
89305
89544
|
init_crypto_helper();
|
|
89306
89545
|
init_ai_service();
|
|
89307
89546
|
init_mcp_client_manager();
|
|
@@ -89477,6 +89716,7 @@ var WebServer = class {
|
|
|
89477
89716
|
sseTransports = /* @__PURE__ */ new Map();
|
|
89478
89717
|
sseTransportsList = [];
|
|
89479
89718
|
dbInitialized = false;
|
|
89719
|
+
selfProbeService;
|
|
89480
89720
|
/**
|
|
89481
89721
|
* 延遲載入 ImageProcessor
|
|
89482
89722
|
*/
|
|
@@ -89518,6 +89758,7 @@ var WebServer = class {
|
|
|
89518
89758
|
this.config = config2;
|
|
89519
89759
|
this.portManager = new PortManager();
|
|
89520
89760
|
this.sessionStorage = new SessionStorage();
|
|
89761
|
+
this.selfProbeService = new SelfProbeService(config2);
|
|
89521
89762
|
this.app = (0, import_express.default)();
|
|
89522
89763
|
this.server = (0, import_http.createServer)(this.app);
|
|
89523
89764
|
this.io = new Server2(this.server, {
|
|
@@ -89558,7 +89799,7 @@ var WebServer = class {
|
|
|
89558
89799
|
* 支援多種執行環境:開發模式、打包模式、npx 執行模式
|
|
89559
89800
|
*/
|
|
89560
89801
|
getStaticAssetsPath() {
|
|
89561
|
-
const __filename2 = (0,
|
|
89802
|
+
const __filename2 = (0, import_url2.fileURLToPath)(importMetaUrl);
|
|
89562
89803
|
const __dirname3 = import_path6.default.dirname(__filename2);
|
|
89563
89804
|
const candidates = [];
|
|
89564
89805
|
candidates.push(import_path6.default.resolve(__dirname3, "static"));
|
|
@@ -89782,6 +90023,7 @@ var WebServer = class {
|
|
|
89782
90023
|
});
|
|
89783
90024
|
});
|
|
89784
90025
|
this.app.get("/api/health", (req, res) => {
|
|
90026
|
+
const selfProbeStats = this.selfProbeService.getStats();
|
|
89785
90027
|
res.json({
|
|
89786
90028
|
status: "ok",
|
|
89787
90029
|
pid: process.pid,
|
|
@@ -89789,7 +90031,14 @@ var WebServer = class {
|
|
|
89789
90031
|
uptime: process.uptime(),
|
|
89790
90032
|
version: VERSION,
|
|
89791
90033
|
activeSessions: this.sessionStorage.getSessionCount(),
|
|
89792
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
90034
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
90035
|
+
selfProbe: {
|
|
90036
|
+
enabled: selfProbeStats.enabled,
|
|
90037
|
+
isRunning: selfProbeStats.isRunning,
|
|
90038
|
+
probeCount: selfProbeStats.probeCount,
|
|
90039
|
+
lastProbeTime: selfProbeStats.lastProbeTime?.toISOString() ?? null,
|
|
90040
|
+
intervalSeconds: selfProbeStats.intervalSeconds
|
|
90041
|
+
}
|
|
89793
90042
|
});
|
|
89794
90043
|
});
|
|
89795
90044
|
this.app.get("/api/dashboard/overview", (req, res) => {
|
|
@@ -90317,6 +90566,46 @@ var WebServer = class {
|
|
|
90317
90566
|
});
|
|
90318
90567
|
}
|
|
90319
90568
|
});
|
|
90569
|
+
this.app.get("/api/settings/self-probe", (req, res) => {
|
|
90570
|
+
try {
|
|
90571
|
+
const settings = getSelfProbeSettings();
|
|
90572
|
+
const stats = this.selfProbeService.getStats();
|
|
90573
|
+
res.json({
|
|
90574
|
+
success: true,
|
|
90575
|
+
settings: settings ?? { enabled: false, intervalSeconds: 300 },
|
|
90576
|
+
stats
|
|
90577
|
+
});
|
|
90578
|
+
} catch (error2) {
|
|
90579
|
+
logger.error("\u7372\u53D6 Self-Probe \u8A2D\u5B9A\u5931\u6557:", error2);
|
|
90580
|
+
res.status(500).json({
|
|
90581
|
+
success: false,
|
|
90582
|
+
error: error2 instanceof Error ? error2.message : "\u7372\u53D6 Self-Probe \u8A2D\u5B9A\u5931\u6557"
|
|
90583
|
+
});
|
|
90584
|
+
}
|
|
90585
|
+
});
|
|
90586
|
+
this.app.post("/api/settings/self-probe", (req, res) => {
|
|
90587
|
+
try {
|
|
90588
|
+
const { enabled, intervalSeconds } = req.body;
|
|
90589
|
+
if (intervalSeconds !== void 0 && (intervalSeconds < 60 || intervalSeconds > 600)) {
|
|
90590
|
+
res.status(400).json({
|
|
90591
|
+
success: false,
|
|
90592
|
+
error: "Interval must be between 60 and 600 seconds"
|
|
90593
|
+
});
|
|
90594
|
+
return;
|
|
90595
|
+
}
|
|
90596
|
+
this.selfProbeService.updateSettings({ enabled, intervalSeconds });
|
|
90597
|
+
const settings = getSelfProbeSettings();
|
|
90598
|
+
const stats = this.selfProbeService.getStats();
|
|
90599
|
+
logger.info(`Self-Probe \u8A2D\u5B9A\u5DF2\u66F4\u65B0: enabled=${enabled}, interval=${intervalSeconds}s`);
|
|
90600
|
+
res.json({ success: true, settings, stats });
|
|
90601
|
+
} catch (error2) {
|
|
90602
|
+
logger.error("\u66F4\u65B0 Self-Probe \u8A2D\u5B9A\u5931\u6557:", error2);
|
|
90603
|
+
res.status(500).json({
|
|
90604
|
+
success: false,
|
|
90605
|
+
error: error2 instanceof Error ? error2.message : "\u66F4\u65B0 Self-Probe \u8A2D\u5B9A\u5931\u6557"
|
|
90606
|
+
});
|
|
90607
|
+
}
|
|
90608
|
+
});
|
|
90320
90609
|
this.app.get("/api/logs", (req, res) => {
|
|
90321
90610
|
try {
|
|
90322
90611
|
const options = {};
|
|
@@ -91674,6 +91963,13 @@ var WebServer = class {
|
|
|
91674
91963
|
}
|
|
91675
91964
|
logger.mcpServerStarted(this.port, serverUrl);
|
|
91676
91965
|
await this.autoStartMCPServers();
|
|
91966
|
+
this.selfProbeService.setContext({
|
|
91967
|
+
getSocketIOConnectionCount: () => this.io.sockets.sockets.size,
|
|
91968
|
+
getMCPServerStatus: () => this.mcpServerRef?.getStatus(),
|
|
91969
|
+
getSessionCount: () => this.sessionStorage.getSessionCount(),
|
|
91970
|
+
cleanupExpiredSessions: () => this.sessionStorage.cleanupExpiredSessions()
|
|
91971
|
+
});
|
|
91972
|
+
this.selfProbeService.start();
|
|
91677
91973
|
} catch (error2) {
|
|
91678
91974
|
logger.error("Web\u4F3A\u670D\u5668\u555F\u52D5\u5931\u6557:", error2);
|
|
91679
91975
|
throw new MCPError(
|
|
@@ -91850,6 +92146,7 @@ var WebServer = class {
|
|
|
91850
92146
|
logger.warn("\u7B49\u5F85\u6D3B\u8E8D\u6703\u8A71\u5B8C\u6210\u6642\u767C\u751F\u932F\u8AA4\u6216\u8D85\u6642\uFF0C\u5C07\u7E7C\u7E8C\u505C\u6B62\u6D41\u7A0B", waitErr);
|
|
91851
92147
|
}
|
|
91852
92148
|
}
|
|
92149
|
+
this.selfProbeService.stop();
|
|
91853
92150
|
this.sessionStorage.clear();
|
|
91854
92151
|
this.sessionStorage.stopCleanupTimer();
|
|
91855
92152
|
this.io.disconnectSockets(true);
|
|
@@ -91893,6 +92190,24 @@ var WebServer = class {
|
|
|
91893
92190
|
getPort() {
|
|
91894
92191
|
return this.port;
|
|
91895
92192
|
}
|
|
92193
|
+
/**
|
|
92194
|
+
* 取得 Self-Probe 服務實例
|
|
92195
|
+
*/
|
|
92196
|
+
getSelfProbeService() {
|
|
92197
|
+
return this.selfProbeService;
|
|
92198
|
+
}
|
|
92199
|
+
/**
|
|
92200
|
+
* 取得 Socket.IO 伺服器實例
|
|
92201
|
+
*/
|
|
92202
|
+
getIO() {
|
|
92203
|
+
return this.io;
|
|
92204
|
+
}
|
|
92205
|
+
/**
|
|
92206
|
+
* 取得 Session Storage 實例
|
|
92207
|
+
*/
|
|
92208
|
+
getSessionStorage() {
|
|
92209
|
+
return this.sessionStorage;
|
|
92210
|
+
}
|
|
91896
92211
|
};
|
|
91897
92212
|
|
|
91898
92213
|
// src/server/mcp-server.ts
|
|
@@ -92334,6 +92649,26 @@ var MCPServer = class {
|
|
|
92334
92649
|
// src/config/index.ts
|
|
92335
92650
|
init_cjs_shims();
|
|
92336
92651
|
var import_dotenv = __toESM(require_main2(), 1);
|
|
92652
|
+
|
|
92653
|
+
// src/shared/ipc-constants.ts
|
|
92654
|
+
init_cjs_shims();
|
|
92655
|
+
var SUPERVISOR_DEFAULTS = {
|
|
92656
|
+
MAX_RESTART_ATTEMPTS: 5,
|
|
92657
|
+
RESTART_DELAY_MS: 2e3,
|
|
92658
|
+
HEALTH_CHECK_INTERVAL_MS: 3e4,
|
|
92659
|
+
HEALTH_CHECK_TIMEOUT_MS: 5e3,
|
|
92660
|
+
REQUEST_TIMEOUT_MS: 3e4,
|
|
92661
|
+
CONSECUTIVE_FAILURES_BEFORE_RESTART: 3
|
|
92662
|
+
};
|
|
92663
|
+
var SUPERVISOR_ENV_KEYS = {
|
|
92664
|
+
ENABLED: "SUPERVISOR_ENABLED",
|
|
92665
|
+
MAX_RESTART_ATTEMPTS: "SUPERVISOR_MAX_RESTART_ATTEMPTS",
|
|
92666
|
+
RESTART_DELAY_MS: "SUPERVISOR_RESTART_DELAY_MS",
|
|
92667
|
+
HEALTH_CHECK_INTERVAL_MS: "SUPERVISOR_HEALTH_CHECK_INTERVAL_MS",
|
|
92668
|
+
HEALTH_CHECK_TIMEOUT_MS: "SUPERVISOR_HEALTH_CHECK_TIMEOUT_MS"
|
|
92669
|
+
};
|
|
92670
|
+
|
|
92671
|
+
// src/config/index.ts
|
|
92337
92672
|
(0, import_dotenv.config)();
|
|
92338
92673
|
function getEnvVar(key, defaultValue) {
|
|
92339
92674
|
return process.env[key] || defaultValue;
|
|
@@ -92356,6 +92691,27 @@ function getEnvBoolean(key, defaultValue) {
|
|
|
92356
92691
|
if (!value) return defaultValue;
|
|
92357
92692
|
return value.toLowerCase() === "true";
|
|
92358
92693
|
}
|
|
92694
|
+
function createSupervisorConfig() {
|
|
92695
|
+
return {
|
|
92696
|
+
enabled: getEnvBoolean(SUPERVISOR_ENV_KEYS.ENABLED, false),
|
|
92697
|
+
maxRestartAttempts: getEnvNumber(
|
|
92698
|
+
SUPERVISOR_ENV_KEYS.MAX_RESTART_ATTEMPTS,
|
|
92699
|
+
SUPERVISOR_DEFAULTS.MAX_RESTART_ATTEMPTS
|
|
92700
|
+
),
|
|
92701
|
+
restartDelayMs: getEnvNumber(
|
|
92702
|
+
SUPERVISOR_ENV_KEYS.RESTART_DELAY_MS,
|
|
92703
|
+
SUPERVISOR_DEFAULTS.RESTART_DELAY_MS
|
|
92704
|
+
),
|
|
92705
|
+
healthCheckIntervalMs: getEnvNumber(
|
|
92706
|
+
SUPERVISOR_ENV_KEYS.HEALTH_CHECK_INTERVAL_MS,
|
|
92707
|
+
SUPERVISOR_DEFAULTS.HEALTH_CHECK_INTERVAL_MS
|
|
92708
|
+
),
|
|
92709
|
+
healthCheckTimeoutMs: getEnvNumber(
|
|
92710
|
+
SUPERVISOR_ENV_KEYS.HEALTH_CHECK_TIMEOUT_MS,
|
|
92711
|
+
SUPERVISOR_DEFAULTS.HEALTH_CHECK_TIMEOUT_MS
|
|
92712
|
+
)
|
|
92713
|
+
};
|
|
92714
|
+
}
|
|
92359
92715
|
function createDefaultConfig() {
|
|
92360
92716
|
return {
|
|
92361
92717
|
apiKey: process.env["MCP_API_KEY"],
|
|
@@ -92387,7 +92743,12 @@ function createDefaultConfig() {
|
|
|
92387
92743
|
healthCheckTimeout: getEnvNumber("MCP_HEALTH_CHECK_TIMEOUT", 3e3),
|
|
92388
92744
|
forceNewInstance: getEnvBoolean("MCP_FORCE_NEW_INSTANCE", false),
|
|
92389
92745
|
// MCP Server 傳輸模式
|
|
92390
|
-
mcpTransport: getEnvVar("MCP_TRANSPORT", "stdio")
|
|
92746
|
+
mcpTransport: getEnvVar("MCP_TRANSPORT", "stdio"),
|
|
92747
|
+
// Self-Probe (Keep-Alive) 設定
|
|
92748
|
+
enableSelfProbe: getEnvBoolean("MCP_ENABLE_SELF_PROBE", false),
|
|
92749
|
+
selfProbeIntervalSeconds: getEnvNumber("MCP_SELF_PROBE_INTERVAL", 300),
|
|
92750
|
+
// Supervisor 配置
|
|
92751
|
+
supervisor: createSupervisorConfig()
|
|
92391
92752
|
};
|
|
92392
92753
|
}
|
|
92393
92754
|
function validateConfig(config2) {
|
|
@@ -92424,6 +92785,41 @@ function validateConfig(config2) {
|
|
|
92424
92785
|
"INVALID_LOG_LEVEL"
|
|
92425
92786
|
);
|
|
92426
92787
|
}
|
|
92788
|
+
if (config2.selfProbeIntervalSeconds !== void 0) {
|
|
92789
|
+
if (config2.selfProbeIntervalSeconds < 60 || config2.selfProbeIntervalSeconds > 600) {
|
|
92790
|
+
throw new MCPError(
|
|
92791
|
+
`Invalid self-probe interval: ${config2.selfProbeIntervalSeconds}. Must be between 60 and 600 seconds.`,
|
|
92792
|
+
"INVALID_SELF_PROBE_INTERVAL"
|
|
92793
|
+
);
|
|
92794
|
+
}
|
|
92795
|
+
}
|
|
92796
|
+
if (config2.supervisor) {
|
|
92797
|
+
const sup = config2.supervisor;
|
|
92798
|
+
if (sup.maxRestartAttempts < 0 || sup.maxRestartAttempts > 20) {
|
|
92799
|
+
throw new MCPError(
|
|
92800
|
+
`Invalid supervisor max restart attempts: ${sup.maxRestartAttempts}. Must be between 0 and 20.`,
|
|
92801
|
+
"INVALID_SUPERVISOR_CONFIG"
|
|
92802
|
+
);
|
|
92803
|
+
}
|
|
92804
|
+
if (sup.restartDelayMs < 100 || sup.restartDelayMs > 3e4) {
|
|
92805
|
+
throw new MCPError(
|
|
92806
|
+
`Invalid supervisor restart delay: ${sup.restartDelayMs}. Must be between 100ms and 30000ms.`,
|
|
92807
|
+
"INVALID_SUPERVISOR_CONFIG"
|
|
92808
|
+
);
|
|
92809
|
+
}
|
|
92810
|
+
if (sup.healthCheckIntervalMs < 5e3 || sup.healthCheckIntervalMs > 3e5) {
|
|
92811
|
+
throw new MCPError(
|
|
92812
|
+
`Invalid supervisor health check interval: ${sup.healthCheckIntervalMs}. Must be between 5s and 300s.`,
|
|
92813
|
+
"INVALID_SUPERVISOR_CONFIG"
|
|
92814
|
+
);
|
|
92815
|
+
}
|
|
92816
|
+
if (sup.healthCheckTimeoutMs < 1e3 || sup.healthCheckTimeoutMs > 3e4) {
|
|
92817
|
+
throw new MCPError(
|
|
92818
|
+
`Invalid supervisor health check timeout: ${sup.healthCheckTimeoutMs}. Must be between 1s and 30s.`,
|
|
92819
|
+
"INVALID_SUPERVISOR_CONFIG"
|
|
92820
|
+
);
|
|
92821
|
+
}
|
|
92822
|
+
}
|
|
92427
92823
|
}
|
|
92428
92824
|
function getConfig() {
|
|
92429
92825
|
const config2 = createDefaultConfig();
|
|
@@ -342,6 +342,42 @@
|
|
|
342
342
|
<button id="savePreferencesBtn" class="btn btn-primary">儲存偏好設定</button>
|
|
343
343
|
</div>
|
|
344
344
|
</section>
|
|
345
|
+
|
|
346
|
+
<!-- Self-Probe 設定 -->
|
|
347
|
+
<section class="settings-section">
|
|
348
|
+
<h2 class="section-title">
|
|
349
|
+
<span class="icon">💓</span>
|
|
350
|
+
自我探查 (Keep-Alive)
|
|
351
|
+
</h2>
|
|
352
|
+
|
|
353
|
+
<div class="form-group">
|
|
354
|
+
<div class="checkbox-group">
|
|
355
|
+
<input type="checkbox" id="enableSelfProbe" />
|
|
356
|
+
<label for="enableSelfProbe">啟用自我探查</label>
|
|
357
|
+
</div>
|
|
358
|
+
<p class="form-help">定期檢查服務狀態,防止因閒置被系統回收</p>
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
<div class="form-group" id="selfProbeIntervalGroup">
|
|
362
|
+
<label class="form-label" for="selfProbeInterval">探查間隔(秒)</label>
|
|
363
|
+
<input type="number" id="selfProbeInterval" class="form-input"
|
|
364
|
+
min="60" max="600" step="30" value="300">
|
|
365
|
+
<p class="form-help">60-600 秒,預設 300 秒(5 分鐘)</p>
|
|
366
|
+
</div>
|
|
367
|
+
|
|
368
|
+
<div class="form-group" id="selfProbeStatus" style="display: none;">
|
|
369
|
+
<label class="form-label">狀態資訊</label>
|
|
370
|
+
<div class="status-info" style="font-size: 13px; color: var(--text-muted); padding: 8px; background: var(--bg-secondary); border-radius: var(--radius-sm);">
|
|
371
|
+
<div id="selfProbeRunning">執行狀態: --</div>
|
|
372
|
+
<div id="selfProbeCount">探查次數: --</div>
|
|
373
|
+
<div id="selfProbeLastTime">上次探查: --</div>
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<div class="form-actions">
|
|
378
|
+
<button id="saveSelfProbeBtn" class="btn btn-primary">儲存設定</button>
|
|
379
|
+
</div>
|
|
380
|
+
</section>
|
|
345
381
|
</div>
|
|
346
382
|
|
|
347
383
|
<!-- Toast Container -->
|