@lark-apaas/devtool-kits 1.2.11-alpha.1 → 1.2.11-alpha.2

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.cjs CHANGED
@@ -1850,8 +1850,8 @@ function hasSpecialPatterns(pattern) {
1850
1850
  return /[{*]/.test(pattern);
1851
1851
  }
1852
1852
  __name(hasSpecialPatterns, "hasSpecialPatterns");
1853
- function normalizePathForMatching(path7) {
1854
- return path7.replace(/\/+/g, "/").replace(/\/+$/, "");
1853
+ function normalizePathForMatching(path8) {
1854
+ return path8.replace(/\/+/g, "/").replace(/\/+$/, "");
1855
1855
  }
1856
1856
  __name(normalizePathForMatching, "normalizePathForMatching");
1857
1857
 
@@ -2398,7 +2398,7 @@ __name(readLogsBySource, "readLogsBySource");
2398
2398
  // src/middlewares/dev-logs/services/trigger.service.ts
2399
2399
  var import_node_fs10 = require("fs");
2400
2400
  var import_node_readline3 = require("readline");
2401
- async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
2401
+ async function readTriggerList(filePath, trigger, path8, limit, triggerID) {
2402
2402
  if (!await fileExists(filePath)) {
2403
2403
  return void 0;
2404
2404
  }
@@ -2424,7 +2424,7 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
2424
2424
  if (alreadyAdded) {
2425
2425
  return false;
2426
2426
  }
2427
- const isAutomationTrigger = builder.path?.endsWith(path7);
2427
+ const isAutomationTrigger = builder.path?.endsWith(path8);
2428
2428
  if (!isAutomationTrigger) {
2429
2429
  return false;
2430
2430
  }
@@ -2507,7 +2507,7 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
2507
2507
  };
2508
2508
  }
2509
2509
  __name(readTriggerList, "readTriggerList");
2510
- async function readTriggerDetail(filePath, path7, instanceID) {
2510
+ async function readTriggerDetail(filePath, path8, instanceID) {
2511
2511
  const exists = await fileExists(filePath);
2512
2512
  if (!exists) {
2513
2513
  return void 0;
@@ -2523,7 +2523,7 @@ async function readTriggerDetail(filePath, path7, instanceID) {
2523
2523
  for await (const line of rl) {
2524
2524
  const entry = parseLogLine2(line);
2525
2525
  if (!entry) continue;
2526
- const isAutomationTrigger = entry.path?.endsWith(path7);
2526
+ const isAutomationTrigger = entry.path?.endsWith(path8);
2527
2527
  const hasInstanceID = entry.instance_id === instanceID && entry.trigger;
2528
2528
  if (!isAutomationTrigger || !hasInstanceID) continue;
2529
2529
  matches.push(entry);
@@ -2777,16 +2777,16 @@ function createGetTriggerListHandler(logDir) {
2777
2777
  });
2778
2778
  }
2779
2779
  const triggerID = typeof req.query.triggerID === "string" ? req.query.triggerID.trim() : void 0;
2780
- const path7 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
2780
+ const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
2781
2781
  const limit = parseLimit(req.query.limit, 10, 200);
2782
2782
  try {
2783
- const result = await readTriggerList(traceLogPath, trigger, path7, limit, triggerID);
2783
+ const result = await readTriggerList(traceLogPath, trigger, path8, limit, triggerID);
2784
2784
  if (!result) {
2785
2785
  return handleNotFound(res, traceLogPath);
2786
2786
  }
2787
2787
  res.json({
2788
2788
  file: getRelativePath(traceLogPath),
2789
- path: path7,
2789
+ path: path8,
2790
2790
  ...result
2791
2791
  });
2792
2792
  } catch (error) {
@@ -2804,9 +2804,9 @@ function createGetTriggerDetailHandler(logDir) {
2804
2804
  message: "instanceID is required"
2805
2805
  });
2806
2806
  }
2807
- const path7 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
2807
+ const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
2808
2808
  try {
2809
- const result = await readTriggerDetail(traceLogPath, path7, instanceID);
2809
+ const result = await readTriggerDetail(traceLogPath, path8, instanceID);
2810
2810
  if (!result) {
2811
2811
  return handleNotFound(res, traceLogPath);
2812
2812
  }
@@ -2913,6 +2913,521 @@ function createHealthCheckHandler(options = {}) {
2913
2913
  }
2914
2914
  __name(createHealthCheckHandler, "createHealthCheckHandler");
2915
2915
 
2916
+ // src/middlewares/dev-logs/sse/log-watcher.ts
2917
+ var fs9 = __toESM(require("fs"), 1);
2918
+ var path6 = __toESM(require("path"), 1);
2919
+ function mapPinoLevelToServerLogLevel2(pinoLevel) {
2920
+ if (typeof pinoLevel === "string") {
2921
+ const lower = pinoLevel.toLowerCase();
2922
+ if (lower === "fatal") return "fatal";
2923
+ if (lower === "error") return "error";
2924
+ if (lower === "warn" || lower === "warning") return "warn";
2925
+ if (lower === "info" || lower === "log") return "log";
2926
+ if (lower === "debug") return "debug";
2927
+ if (lower === "trace" || lower === "verbose") return "verbose";
2928
+ return "log";
2929
+ }
2930
+ if (pinoLevel >= 60) return "fatal";
2931
+ if (pinoLevel >= 50) return "error";
2932
+ if (pinoLevel >= 40) return "warn";
2933
+ if (pinoLevel >= 30) return "log";
2934
+ if (pinoLevel >= 20) return "debug";
2935
+ return "verbose";
2936
+ }
2937
+ __name(mapPinoLevelToServerLogLevel2, "mapPinoLevelToServerLogLevel");
2938
+ function extractLogLevel2(text) {
2939
+ const lower = text.toLowerCase();
2940
+ if (lower.includes("fatal") || lower.includes("critical")) return "fatal";
2941
+ if (lower.includes("error") || lower.includes("<e>") || lower.includes("\u2716")) return "error";
2942
+ if (lower.includes("warn") || lower.includes("warning") || lower.includes("<w>") || lower.includes("\u26A0")) return "warn";
2943
+ if (lower.includes("debug") || lower.includes("<d>")) return "debug";
2944
+ if (lower.includes("verbose") || lower.includes("trace")) return "verbose";
2945
+ return "log";
2946
+ }
2947
+ __name(extractLogLevel2, "extractLogLevel");
2948
+ function generateUUID2() {
2949
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
2950
+ const r = Math.random() * 16 | 0;
2951
+ const v = c === "x" ? r : r & 3 | 8;
2952
+ return v.toString(16);
2953
+ });
2954
+ }
2955
+ __name(generateUUID2, "generateUUID");
2956
+ function parsePinoLog2(line, source) {
2957
+ try {
2958
+ const pinoLog = JSON.parse(line);
2959
+ const id = generateUUID2();
2960
+ return {
2961
+ id,
2962
+ level: mapPinoLevelToServerLogLevel2(pinoLog.level),
2963
+ timestamp: new Date(pinoLog.time).getTime(),
2964
+ message: pinoLog.message || pinoLog.msg || "",
2965
+ context: pinoLog.context || null,
2966
+ traceId: pinoLog.trace_id || null,
2967
+ userId: pinoLog.user_id || null,
2968
+ appId: pinoLog.app_id || null,
2969
+ tenantId: pinoLog.tenant_id || null,
2970
+ stack: pinoLog.stack || null,
2971
+ meta: {
2972
+ pid: pinoLog.pid,
2973
+ hostname: pinoLog.hostname,
2974
+ path: pinoLog.path,
2975
+ method: pinoLog.method,
2976
+ statusCode: pinoLog.status_code,
2977
+ durationMs: pinoLog.duration_ms,
2978
+ ip: pinoLog.ip,
2979
+ requestBody: pinoLog.request_body,
2980
+ responseBody: pinoLog.response_body
2981
+ },
2982
+ tags: [
2983
+ source
2984
+ ]
2985
+ };
2986
+ } catch {
2987
+ return null;
2988
+ }
2989
+ }
2990
+ __name(parsePinoLog2, "parsePinoLog");
2991
+ function parseStdLog2(line, source) {
2992
+ const id = generateUUID2();
2993
+ const match = line.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(server|client)\] (.*)$/);
2994
+ if (!match) {
2995
+ return {
2996
+ id,
2997
+ level: extractLogLevel2(line),
2998
+ timestamp: Date.now(),
2999
+ message: line,
3000
+ context: null,
3001
+ traceId: null,
3002
+ userId: null,
3003
+ appId: null,
3004
+ tenantId: null,
3005
+ stack: null,
3006
+ meta: null,
3007
+ tags: [
3008
+ source
3009
+ ]
3010
+ };
3011
+ }
3012
+ const [, timeStr, , content] = match;
3013
+ let timestamp;
3014
+ try {
3015
+ const isoStr = timeStr.replace(" ", "T");
3016
+ timestamp = new Date(isoStr).getTime();
3017
+ if (isNaN(timestamp)) {
3018
+ timestamp = Date.now();
3019
+ }
3020
+ } catch {
3021
+ timestamp = Date.now();
3022
+ }
3023
+ return {
3024
+ id,
3025
+ level: extractLogLevel2(content),
3026
+ timestamp,
3027
+ message: content,
3028
+ context: null,
3029
+ traceId: null,
3030
+ userId: null,
3031
+ appId: null,
3032
+ tenantId: null,
3033
+ stack: null,
3034
+ meta: null,
3035
+ tags: [
3036
+ source
3037
+ ]
3038
+ };
3039
+ }
3040
+ __name(parseStdLog2, "parseStdLog");
3041
+ var LogWatcher = class {
3042
+ static {
3043
+ __name(this, "LogWatcher");
3044
+ }
3045
+ logDir = "";
3046
+ watchers = /* @__PURE__ */ new Map();
3047
+ filePositions = /* @__PURE__ */ new Map();
3048
+ subscribers = /* @__PURE__ */ new Set();
3049
+ isRunning = false;
3050
+ debug = false;
3051
+ logFiles = [
3052
+ {
3053
+ fileName: "server.log",
3054
+ source: "server",
3055
+ parser: parsePinoLog2
3056
+ },
3057
+ {
3058
+ fileName: "trace.log",
3059
+ source: "trace",
3060
+ parser: parsePinoLog2
3061
+ },
3062
+ {
3063
+ fileName: "server.std.log",
3064
+ source: "server-std",
3065
+ parser: parseStdLog2
3066
+ },
3067
+ {
3068
+ fileName: "client.std.log",
3069
+ source: "client-std",
3070
+ parser: parseStdLog2
3071
+ }
3072
+ ];
3073
+ constructor(options = {}) {
3074
+ this.debug = options.debug ?? false;
3075
+ }
3076
+ log(...args) {
3077
+ if (this.debug) {
3078
+ console.log("[LogWatcher]", ...args);
3079
+ }
3080
+ }
3081
+ /**
3082
+ * Start watching log files
3083
+ */
3084
+ start(logDir) {
3085
+ if (this.isRunning) {
3086
+ this.log("Already running, ignoring start call");
3087
+ return;
3088
+ }
3089
+ this.logDir = logDir;
3090
+ this.isRunning = true;
3091
+ this.log(`Starting to watch log files in: ${logDir}`);
3092
+ for (const config of this.logFiles) {
3093
+ this.watchFile(config);
3094
+ }
3095
+ }
3096
+ /**
3097
+ * Stop watching all files
3098
+ */
3099
+ stop() {
3100
+ if (!this.isRunning) {
3101
+ return;
3102
+ }
3103
+ this.log("Stopping file watchers");
3104
+ this.isRunning = false;
3105
+ Array.from(this.watchers.entries()).forEach(([fileName, watcher]) => {
3106
+ watcher.close();
3107
+ this.log(`Closed watcher for: ${fileName}`);
3108
+ });
3109
+ this.watchers.clear();
3110
+ this.filePositions.clear();
3111
+ }
3112
+ /**
3113
+ * Subscribe to new log events
3114
+ */
3115
+ onLog(callback) {
3116
+ this.subscribers.add(callback);
3117
+ this.log(`Subscriber added, total: ${this.subscribers.size}`);
3118
+ return () => {
3119
+ this.subscribers.delete(callback);
3120
+ this.log(`Subscriber removed, total: ${this.subscribers.size}`);
3121
+ };
3122
+ }
3123
+ /**
3124
+ * Get subscriber count
3125
+ */
3126
+ getSubscriberCount() {
3127
+ return this.subscribers.size;
3128
+ }
3129
+ /**
3130
+ * Watch a single log file
3131
+ */
3132
+ watchFile(config) {
3133
+ const filePath = path6.join(this.logDir, config.fileName);
3134
+ if (!fs9.existsSync(filePath)) {
3135
+ this.log(`File not found, skipping: ${config.fileName}`);
3136
+ return;
3137
+ }
3138
+ try {
3139
+ const stats = fs9.statSync(filePath);
3140
+ this.filePositions.set(config.fileName, stats.size);
3141
+ this.log(`Initialized position for ${config.fileName}: ${stats.size} bytes`);
3142
+ } catch (error) {
3143
+ this.log(`Failed to get initial position for ${config.fileName}:`, error);
3144
+ this.filePositions.set(config.fileName, 0);
3145
+ }
3146
+ try {
3147
+ const watcher = fs9.watch(filePath, (eventType) => {
3148
+ if (eventType === "change") {
3149
+ this.handleFileChange(config);
3150
+ }
3151
+ });
3152
+ watcher.on("error", (error) => {
3153
+ this.log(`Watcher error for ${config.fileName}:`, error);
3154
+ this.restartWatcher(config);
3155
+ });
3156
+ this.watchers.set(config.fileName, watcher);
3157
+ this.log(`Started watching: ${config.fileName}`);
3158
+ } catch (error) {
3159
+ this.log(`Failed to start watcher for ${config.fileName}:`, error);
3160
+ }
3161
+ }
3162
+ /**
3163
+ * Restart a file watcher after error
3164
+ */
3165
+ restartWatcher(config) {
3166
+ const existingWatcher = this.watchers.get(config.fileName);
3167
+ if (existingWatcher) {
3168
+ existingWatcher.close();
3169
+ this.watchers.delete(config.fileName);
3170
+ }
3171
+ setTimeout(() => {
3172
+ if (this.isRunning) {
3173
+ this.log(`Restarting watcher for: ${config.fileName}`);
3174
+ this.watchFile(config);
3175
+ }
3176
+ }, 1e3);
3177
+ }
3178
+ /**
3179
+ * Handle file change event - read new content
3180
+ */
3181
+ handleFileChange(config) {
3182
+ const filePath = path6.join(this.logDir, config.fileName);
3183
+ const lastPosition = this.filePositions.get(config.fileName) || 0;
3184
+ try {
3185
+ const stats = fs9.statSync(filePath);
3186
+ const currentSize = stats.size;
3187
+ if (currentSize < lastPosition) {
3188
+ this.log(`File ${config.fileName} was truncated, resetting position`);
3189
+ this.filePositions.set(config.fileName, 0);
3190
+ this.handleFileChange(config);
3191
+ return;
3192
+ }
3193
+ if (currentSize === lastPosition) {
3194
+ return;
3195
+ }
3196
+ const readSize = currentSize - lastPosition;
3197
+ const buffer = Buffer.alloc(readSize);
3198
+ const fd = fs9.openSync(filePath, "r");
3199
+ try {
3200
+ fs9.readSync(fd, buffer, 0, readSize, lastPosition);
3201
+ } finally {
3202
+ fs9.closeSync(fd);
3203
+ }
3204
+ this.filePositions.set(config.fileName, currentSize);
3205
+ const content = buffer.toString("utf8");
3206
+ const lines = content.split("\n");
3207
+ for (const line of lines) {
3208
+ if (!line.trim()) continue;
3209
+ try {
3210
+ const log = config.parser(line, config.source);
3211
+ if (log) {
3212
+ this.notifySubscribers(log);
3213
+ }
3214
+ } catch {
3215
+ }
3216
+ }
3217
+ } catch (error) {
3218
+ this.log(`Error reading file ${config.fileName}:`, error);
3219
+ }
3220
+ }
3221
+ /**
3222
+ * Notify all subscribers of new log
3223
+ */
3224
+ notifySubscribers(log) {
3225
+ Array.from(this.subscribers).forEach((subscriber) => {
3226
+ try {
3227
+ subscriber(log);
3228
+ } catch (error) {
3229
+ this.log("Subscriber error:", error);
3230
+ }
3231
+ });
3232
+ }
3233
+ };
3234
+
3235
+ // src/middlewares/dev-logs/sse/client-manager.ts
3236
+ var ClientManager = class {
3237
+ static {
3238
+ __name(this, "ClientManager");
3239
+ }
3240
+ clients = /* @__PURE__ */ new Map();
3241
+ debug = false;
3242
+ constructor(options = {}) {
3243
+ this.debug = options.debug ?? false;
3244
+ }
3245
+ log(...args) {
3246
+ if (this.debug) {
3247
+ console.log("[ClientManager]", ...args);
3248
+ }
3249
+ }
3250
+ /**
3251
+ * Generate a unique client ID
3252
+ */
3253
+ generateClientId() {
3254
+ return `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
3255
+ }
3256
+ /**
3257
+ * Add a new client connection
3258
+ */
3259
+ addClient(id, res) {
3260
+ this.clients.set(id, {
3261
+ id,
3262
+ res,
3263
+ connectedAt: Date.now()
3264
+ });
3265
+ this.log(`Client connected: ${id}, total clients: ${this.clients.size}`);
3266
+ }
3267
+ /**
3268
+ * Remove a client connection
3269
+ */
3270
+ removeClient(id) {
3271
+ if (this.clients.has(id)) {
3272
+ this.clients.delete(id);
3273
+ this.log(`Client disconnected: ${id}, total clients: ${this.clients.size}`);
3274
+ }
3275
+ }
3276
+ /**
3277
+ * Get current client count
3278
+ */
3279
+ getClientCount() {
3280
+ return this.clients.size;
3281
+ }
3282
+ /**
3283
+ * Check if a client exists
3284
+ */
3285
+ hasClient(id) {
3286
+ return this.clients.has(id);
3287
+ }
3288
+ /**
3289
+ * Send SSE event to a specific client
3290
+ */
3291
+ sendToClient(clientId, event, data) {
3292
+ const client = this.clients.get(clientId);
3293
+ if (!client) {
3294
+ return false;
3295
+ }
3296
+ try {
3297
+ const message = this.formatSSEMessage(event, data);
3298
+ client.res.write(message);
3299
+ return true;
3300
+ } catch (error) {
3301
+ this.log(`Failed to send to client ${clientId}:`, error);
3302
+ this.removeClient(clientId);
3303
+ return false;
3304
+ }
3305
+ }
3306
+ /**
3307
+ * Broadcast SSE event to all clients
3308
+ */
3309
+ broadcast(event, data) {
3310
+ const message = this.formatSSEMessage(event, data);
3311
+ const failedClients = [];
3312
+ Array.from(this.clients.entries()).forEach(([id, client]) => {
3313
+ try {
3314
+ client.res.write(message);
3315
+ } catch (error) {
3316
+ this.log(`Broadcast failed for client ${id}:`, error);
3317
+ failedClients.push(id);
3318
+ }
3319
+ });
3320
+ failedClients.forEach((id) => {
3321
+ this.removeClient(id);
3322
+ });
3323
+ }
3324
+ /**
3325
+ * Format SSE message
3326
+ * SSE format: event: <event>\ndata: <json>\n\n
3327
+ */
3328
+ formatSSEMessage(event, data) {
3329
+ const jsonData = JSON.stringify(data);
3330
+ return `event: ${event}
3331
+ data: ${jsonData}
3332
+
3333
+ `;
3334
+ }
3335
+ /**
3336
+ * Get all client IDs
3337
+ */
3338
+ getClientIds() {
3339
+ return Array.from(this.clients.keys());
3340
+ }
3341
+ /**
3342
+ * Get client info
3343
+ */
3344
+ getClientInfo(id) {
3345
+ const client = this.clients.get(id);
3346
+ if (!client) {
3347
+ return null;
3348
+ }
3349
+ return {
3350
+ id: client.id,
3351
+ connectedAt: client.connectedAt
3352
+ };
3353
+ }
3354
+ /**
3355
+ * Close all client connections
3356
+ */
3357
+ closeAll() {
3358
+ this.log(`Closing all ${this.clients.size} clients`);
3359
+ Array.from(this.clients.entries()).forEach(([id, client]) => {
3360
+ try {
3361
+ client.res.end();
3362
+ } catch (error) {
3363
+ this.log(`Error closing client ${id}:`, error);
3364
+ }
3365
+ });
3366
+ this.clients.clear();
3367
+ }
3368
+ };
3369
+
3370
+ // src/middlewares/dev-logs/sse/sse.controller.ts
3371
+ function createSSEHandler(logDir, options = {}) {
3372
+ const { debug = false, heartbeatInterval = 3e4 } = options;
3373
+ const logWatcher = new LogWatcher({
3374
+ debug
3375
+ });
3376
+ const clientManager = new ClientManager({
3377
+ debug
3378
+ });
3379
+ const log = /* @__PURE__ */ __name((...args) => {
3380
+ if (debug) {
3381
+ console.log("[SSEHandler]", ...args);
3382
+ }
3383
+ }, "log");
3384
+ return (req, res) => {
3385
+ res.setHeader("Content-Type", "text/event-stream");
3386
+ res.setHeader("Cache-Control", "no-cache");
3387
+ res.setHeader("Connection", "keep-alive");
3388
+ res.setHeader("X-Accel-Buffering", "no");
3389
+ res.setTimeout(0);
3390
+ const clientId = clientManager.generateClientId();
3391
+ clientManager.addClient(clientId, res);
3392
+ log(`New SSE connection: ${clientId}`);
3393
+ if (clientManager.getClientCount() === 1) {
3394
+ log("First client connected, starting log watcher");
3395
+ logWatcher.start(logDir);
3396
+ }
3397
+ const unsubscribe = logWatcher.onLog((logEntry) => {
3398
+ clientManager.sendToClient(clientId, "log", logEntry);
3399
+ });
3400
+ clientManager.sendToClient(clientId, "connected", {
3401
+ clientId,
3402
+ timestamp: Date.now()
3403
+ });
3404
+ const heartbeat = setInterval(() => {
3405
+ const success = clientManager.sendToClient(clientId, "heartbeat", {
3406
+ timestamp: Date.now()
3407
+ });
3408
+ if (!success) {
3409
+ clearInterval(heartbeat);
3410
+ }
3411
+ }, heartbeatInterval);
3412
+ const cleanup = /* @__PURE__ */ __name(() => {
3413
+ log(`Client disconnected: ${clientId}`);
3414
+ clearInterval(heartbeat);
3415
+ unsubscribe();
3416
+ clientManager.removeClient(clientId);
3417
+ if (clientManager.getClientCount() === 0) {
3418
+ log("No more clients, stopping log watcher");
3419
+ logWatcher.stop();
3420
+ }
3421
+ }, "cleanup");
3422
+ req.on("close", cleanup);
3423
+ req.on("error", (error) => {
3424
+ log(`Client error ${clientId}:`, error);
3425
+ cleanup();
3426
+ });
3427
+ };
3428
+ }
3429
+ __name(createSSEHandler, "createSSEHandler");
3430
+
2916
3431
  // src/middlewares/dev-logs/router.ts
2917
3432
  function createDevLogRouter(options = {}) {
2918
3433
  const logDir = resolveLogDir(options.logDir);
@@ -2921,6 +3436,7 @@ function createDevLogRouter(options = {}) {
2921
3436
  router.get("/trace/recent", createGetRecentTracesHandler(logDir));
2922
3437
  router.get("/files/:fileName", createGetLogFileHandler(logDir));
2923
3438
  router.get("/server-logs", createGetServerLogsHandler(logDir));
3439
+ router.get("/server-logs/stream", createSSEHandler(logDir));
2924
3440
  router.get("/trace/trigger/list", createGetTriggerListHandler(logDir));
2925
3441
  router.get("/trace/trigger/:instanceID", createGetTriggerDetailHandler(logDir));
2926
3442
  router.get("/trace/capability/list", createGetCapabilityTraceListHandler(logDir));