@lark-apaas/devtool-kits 1.2.11-alpha.1 → 1.2.12-alpha.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/index.js CHANGED
@@ -1465,7 +1465,7 @@ function sendSimpleRedirect(req, res) {
1465
1465
  __name(sendSimpleRedirect, "sendSimpleRedirect");
1466
1466
 
1467
1467
  // src/middlewares/index.ts
1468
- import path6 from "path";
1468
+ import path7 from "path";
1469
1469
 
1470
1470
  // src/middlewares/openapi/router.ts
1471
1471
  import express from "express";
@@ -1809,8 +1809,8 @@ function hasSpecialPatterns(pattern) {
1809
1809
  return /[{*]/.test(pattern);
1810
1810
  }
1811
1811
  __name(hasSpecialPatterns, "hasSpecialPatterns");
1812
- function normalizePathForMatching(path7) {
1813
- return path7.replace(/\/+/g, "/").replace(/\/+$/, "");
1812
+ function normalizePathForMatching(path8) {
1813
+ return path8.replace(/\/+/g, "/").replace(/\/+$/, "");
1814
1814
  }
1815
1815
  __name(normalizePathForMatching, "normalizePathForMatching");
1816
1816
 
@@ -1908,194 +1908,10 @@ function serializeError(error) {
1908
1908
  __name(serializeError, "serializeError");
1909
1909
 
1910
1910
  // src/middlewares/dev-logs/controller.ts
1911
- import { join as join4 } from "path";
1912
-
1913
- // src/middlewares/dev-logs/services/file-reader.ts
1914
- import { promises as fs8 } from "fs";
1915
- async function readFileReverse(filePath, chunkSize, processLine) {
1916
- const handle = await fs8.open(filePath, "r");
1917
- try {
1918
- const stats = await handle.stat();
1919
- let position = stats.size;
1920
- let remainder = "";
1921
- while (position > 0) {
1922
- const length = Math.min(chunkSize, position);
1923
- position -= length;
1924
- const buffer = Buffer.alloc(length);
1925
- await handle.read(buffer, 0, length, position);
1926
- let chunk = buffer.toString("utf8");
1927
- if (remainder) {
1928
- chunk += remainder;
1929
- remainder = "";
1930
- }
1931
- const lines = chunk.split("\n");
1932
- remainder = lines.shift() ?? "";
1933
- for (let i = lines.length - 1; i >= 0; i -= 1) {
1934
- if (lines[i]) {
1935
- processLine(lines[i]);
1936
- }
1937
- }
1938
- }
1939
- if (remainder) {
1940
- processLine(remainder);
1941
- }
1942
- } finally {
1943
- await handle.close();
1944
- }
1945
- }
1946
- __name(readFileReverse, "readFileReverse");
1947
- function buildPaginatedResponse(items, page, pageSize) {
1948
- const totalItems = items.length;
1949
- const totalPages = totalItems === 0 ? 0 : Math.ceil(totalItems / pageSize);
1950
- const startIndex = (page - 1) * pageSize;
1951
- const endIndex = Math.min(startIndex + pageSize, totalItems);
1952
- const pagedItems = items.slice(startIndex, endIndex).map((builder) => ({
1953
- traceId: builder.traceId,
1954
- method: builder.method,
1955
- path: builder.path,
1956
- startTime: builder.startTime,
1957
- endTime: builder.endTime,
1958
- statusCode: builder.statusCode,
1959
- durationMs: builder.durationMs,
1960
- entries: builder.entries.slice().reverse()
1961
- }));
1962
- return {
1963
- page,
1964
- pageSize,
1965
- totalCalls: totalItems,
1966
- totalPages,
1967
- calls: pagedItems
1968
- };
1969
- }
1970
- __name(buildPaginatedResponse, "buildPaginatedResponse");
1971
-
1972
- // src/middlewares/dev-logs/services/parsers.ts
1973
- function generateUUID() {
1974
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1975
- const r = Math.random() * 16 | 0;
1976
- const v = c === "x" ? r : r & 3 | 8;
1977
- return v.toString(16);
1978
- });
1979
- }
1980
- __name(generateUUID, "generateUUID");
1981
- function mapPinoLevelToServerLogLevel(pinoLevel) {
1982
- if (typeof pinoLevel === "string") {
1983
- const lower = pinoLevel.toLowerCase();
1984
- if (lower === "fatal") return "fatal";
1985
- if (lower === "error") return "error";
1986
- if (lower === "warn" || lower === "warning") return "warn";
1987
- if (lower === "info" || lower === "log") return "log";
1988
- if (lower === "debug") return "debug";
1989
- if (lower === "trace" || lower === "verbose") return "verbose";
1990
- return "log";
1991
- }
1992
- if (pinoLevel >= 60) return "fatal";
1993
- if (pinoLevel >= 50) return "error";
1994
- if (pinoLevel >= 40) return "warn";
1995
- if (pinoLevel >= 30) return "log";
1996
- if (pinoLevel >= 20) return "debug";
1997
- return "verbose";
1998
- }
1999
- __name(mapPinoLevelToServerLogLevel, "mapPinoLevelToServerLogLevel");
2000
- function extractLogLevel(text) {
2001
- const lower = text.toLowerCase();
2002
- if (lower.includes("fatal") || lower.includes("critical")) return "fatal";
2003
- if (lower.includes("error") || lower.includes("<e>") || lower.includes("\u2716")) return "error";
2004
- if (lower.includes("warn") || lower.includes("warning") || lower.includes("<w>") || lower.includes("\u26A0")) return "warn";
2005
- if (lower.includes("debug") || lower.includes("<d>")) return "debug";
2006
- if (lower.includes("verbose") || lower.includes("trace")) return "verbose";
2007
- return "log";
2008
- }
2009
- __name(extractLogLevel, "extractLogLevel");
2010
- function parsePinoLog(line, source) {
2011
- try {
2012
- const pinoLog = JSON.parse(line);
2013
- const id = generateUUID();
2014
- return {
2015
- id,
2016
- level: mapPinoLevelToServerLogLevel(pinoLog.level),
2017
- timestamp: new Date(pinoLog.time).getTime(),
2018
- message: pinoLog.message || pinoLog.msg || "",
2019
- context: pinoLog.context || null,
2020
- traceId: pinoLog.trace_id || null,
2021
- userId: pinoLog.user_id || null,
2022
- appId: pinoLog.app_id || null,
2023
- tenantId: pinoLog.tenant_id || null,
2024
- stack: pinoLog.stack || null,
2025
- meta: {
2026
- pid: pinoLog.pid,
2027
- hostname: pinoLog.hostname,
2028
- path: pinoLog.path,
2029
- method: pinoLog.method,
2030
- statusCode: pinoLog.status_code,
2031
- durationMs: pinoLog.duration_ms,
2032
- ip: pinoLog.ip,
2033
- requestBody: pinoLog.request_body,
2034
- responseBody: pinoLog.response_body
2035
- },
2036
- tags: [
2037
- source
2038
- ]
2039
- };
2040
- } catch (error) {
2041
- return null;
2042
- }
2043
- }
2044
- __name(parsePinoLog, "parsePinoLog");
2045
- function parseStdLog(line, source) {
2046
- const id = generateUUID();
2047
- const match = line.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(server|client)\] (.*)$/);
2048
- if (!match) {
2049
- return {
2050
- id,
2051
- level: "log",
2052
- timestamp: Date.now(),
2053
- message: line,
2054
- context: null,
2055
- traceId: null,
2056
- userId: null,
2057
- appId: null,
2058
- tenantId: null,
2059
- stack: null,
2060
- meta: null,
2061
- tags: [
2062
- source
2063
- ]
2064
- };
2065
- }
2066
- const [, timeStr, , content] = match;
2067
- let timestamp;
2068
- try {
2069
- const isoStr = timeStr.replace(" ", "T");
2070
- timestamp = new Date(isoStr).getTime();
2071
- if (isNaN(timestamp)) {
2072
- timestamp = Date.now();
2073
- }
2074
- } catch (error) {
2075
- timestamp = Date.now();
2076
- }
2077
- const level = extractLogLevel(content);
2078
- return {
2079
- id,
2080
- level,
2081
- timestamp,
2082
- message: content,
2083
- context: null,
2084
- traceId: null,
2085
- userId: null,
2086
- appId: null,
2087
- tenantId: null,
2088
- stack: null,
2089
- meta: null,
2090
- tags: [
2091
- source
2092
- ]
2093
- };
2094
- }
2095
- __name(parseStdLog, "parseStdLog");
1911
+ import { join as join3 } from "path";
2096
1912
 
2097
- // src/middlewares/dev-logs/services/trace.service.ts
2098
- import { createReadStream } from "fs";
1913
+ // src/middlewares/dev-logs/services.ts
1914
+ import { createReadStream, promises as fs8 } from "fs";
2099
1915
  import { createInterface } from "readline";
2100
1916
  async function readLogEntriesByTrace(filePath, traceId, limit) {
2101
1917
  const exists = await fileExists(filePath);
@@ -2194,6 +2010,62 @@ async function readRecentTraceCalls(filePath, page, pageSize, pathFilter, method
2194
2010
  return buildPaginatedResponse(completedCalls, page, pageSize);
2195
2011
  }
2196
2012
  __name(readRecentTraceCalls, "readRecentTraceCalls");
2013
+ async function readFileReverse(filePath, chunkSize, processLine) {
2014
+ const handle = await fs8.open(filePath, "r");
2015
+ try {
2016
+ const stats = await handle.stat();
2017
+ let position = stats.size;
2018
+ let remainder = "";
2019
+ while (position > 0) {
2020
+ const length = Math.min(chunkSize, position);
2021
+ position -= length;
2022
+ const buffer = Buffer.alloc(length);
2023
+ await handle.read(buffer, 0, length, position);
2024
+ let chunk = buffer.toString("utf8");
2025
+ if (remainder) {
2026
+ chunk += remainder;
2027
+ remainder = "";
2028
+ }
2029
+ const lines = chunk.split("\n");
2030
+ remainder = lines.shift() ?? "";
2031
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
2032
+ if (lines[i]) {
2033
+ processLine(lines[i]);
2034
+ }
2035
+ }
2036
+ }
2037
+ if (remainder) {
2038
+ processLine(remainder);
2039
+ }
2040
+ } finally {
2041
+ await handle.close();
2042
+ }
2043
+ }
2044
+ __name(readFileReverse, "readFileReverse");
2045
+ function buildPaginatedResponse(items, page, pageSize) {
2046
+ const totalItems = items.length;
2047
+ const totalPages = totalItems === 0 ? 0 : Math.ceil(totalItems / pageSize);
2048
+ const startIndex = (page - 1) * pageSize;
2049
+ const endIndex = Math.min(startIndex + pageSize, totalItems);
2050
+ const pagedItems = items.slice(startIndex, endIndex).map((builder) => ({
2051
+ traceId: builder.traceId,
2052
+ method: builder.method,
2053
+ path: builder.path,
2054
+ startTime: builder.startTime,
2055
+ endTime: builder.endTime,
2056
+ statusCode: builder.statusCode,
2057
+ durationMs: builder.durationMs,
2058
+ entries: builder.entries.slice().reverse()
2059
+ }));
2060
+ return {
2061
+ page,
2062
+ pageSize,
2063
+ totalCalls: totalItems,
2064
+ totalPages,
2065
+ calls: pagedItems
2066
+ };
2067
+ }
2068
+ __name(buildPaginatedResponse, "buildPaginatedResponse");
2197
2069
  async function readLogFilePage(filePath, page, pageSize) {
2198
2070
  if (!await fileExists(filePath)) {
2199
2071
  return void 0;
@@ -2249,11 +2121,6 @@ async function readLogFilePage(filePath, page, pageSize) {
2249
2121
  };
2250
2122
  }
2251
2123
  __name(readLogFilePage, "readLogFilePage");
2252
-
2253
- // src/middlewares/dev-logs/services/server-log.service.ts
2254
- import { createReadStream as createReadStream2 } from "fs";
2255
- import { createInterface as createInterface2 } from "readline";
2256
- import { join as join3 } from "path";
2257
2124
  async function readServerLogs(logDir, options = {}) {
2258
2125
  const limit = options.limit || 100;
2259
2126
  const offset = options.offset || 0;
@@ -2296,19 +2163,20 @@ async function readServerLogs(logDir, options = {}) {
2296
2163
  }
2297
2164
  __name(readServerLogs, "readServerLogs");
2298
2165
  async function readLogsBySource(logDir, source) {
2166
+ const { join: join7 } = await import("path");
2299
2167
  let filePath;
2300
2168
  let parser;
2301
2169
  if (source === "server") {
2302
- filePath = join3(logDir, "server.log");
2170
+ filePath = join7(logDir, "server.log");
2303
2171
  parser = /* @__PURE__ */ __name((line) => parsePinoLog(line, "server"), "parser");
2304
2172
  } else if (source === "trace") {
2305
- filePath = join3(logDir, "trace.log");
2173
+ filePath = join7(logDir, "trace.log");
2306
2174
  parser = /* @__PURE__ */ __name((line) => parsePinoLog(line, "trace"), "parser");
2307
2175
  } else if (source === "server-std") {
2308
- filePath = join3(logDir, "server.std.log");
2176
+ filePath = join7(logDir, "server.std.log");
2309
2177
  parser = /* @__PURE__ */ __name((line) => parseStdLog(line, "server-std"), "parser");
2310
2178
  } else if (source === "client-std") {
2311
- filePath = join3(logDir, "client.std.log");
2179
+ filePath = join7(logDir, "client.std.log");
2312
2180
  parser = /* @__PURE__ */ __name((line) => parseStdLog(line, "client-std"), "parser");
2313
2181
  } else {
2314
2182
  console.warn(`[readLogsBySource] Unknown source: ${source}`);
@@ -2322,10 +2190,10 @@ async function readLogsBySource(logDir, source) {
2322
2190
  let stream = null;
2323
2191
  let rl = null;
2324
2192
  try {
2325
- stream = createReadStream2(filePath, {
2193
+ stream = createReadStream(filePath, {
2326
2194
  encoding: "utf8"
2327
2195
  });
2328
- rl = createInterface2({
2196
+ rl = createInterface({
2329
2197
  input: stream,
2330
2198
  crlfDelay: Infinity
2331
2199
  });
@@ -2353,11 +2221,130 @@ async function readLogsBySource(logDir, source) {
2353
2221
  return logs;
2354
2222
  }
2355
2223
  __name(readLogsBySource, "readLogsBySource");
2356
-
2357
- // src/middlewares/dev-logs/services/trigger.service.ts
2358
- import { createReadStream as createReadStream3 } from "fs";
2359
- import { createInterface as createInterface3 } from "readline";
2360
- async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
2224
+ function parsePinoLog(line, source) {
2225
+ try {
2226
+ const pinoLog = JSON.parse(line);
2227
+ const id = generateUUID();
2228
+ return {
2229
+ id,
2230
+ level: mapPinoLevelToServerLogLevel(pinoLog.level),
2231
+ timestamp: new Date(pinoLog.time).getTime(),
2232
+ message: pinoLog.message || pinoLog.msg || "",
2233
+ context: pinoLog.context || null,
2234
+ traceId: pinoLog.trace_id || null,
2235
+ userId: pinoLog.user_id || null,
2236
+ appId: pinoLog.app_id || null,
2237
+ tenantId: pinoLog.tenant_id || null,
2238
+ stack: pinoLog.stack || null,
2239
+ meta: {
2240
+ pid: pinoLog.pid,
2241
+ hostname: pinoLog.hostname,
2242
+ path: pinoLog.path,
2243
+ method: pinoLog.method,
2244
+ statusCode: pinoLog.status_code,
2245
+ durationMs: pinoLog.duration_ms,
2246
+ ip: pinoLog.ip,
2247
+ requestBody: pinoLog.request_body,
2248
+ responseBody: pinoLog.response_body
2249
+ },
2250
+ tags: [
2251
+ source
2252
+ ]
2253
+ };
2254
+ } catch (error) {
2255
+ return null;
2256
+ }
2257
+ }
2258
+ __name(parsePinoLog, "parsePinoLog");
2259
+ function parseStdLog(line, source) {
2260
+ const id = generateUUID();
2261
+ const match = line.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(server|client)\] (.*)$/);
2262
+ if (!match) {
2263
+ return {
2264
+ id,
2265
+ level: "log",
2266
+ timestamp: Date.now(),
2267
+ message: line,
2268
+ context: null,
2269
+ traceId: null,
2270
+ userId: null,
2271
+ appId: null,
2272
+ tenantId: null,
2273
+ stack: null,
2274
+ meta: null,
2275
+ tags: [
2276
+ source
2277
+ ]
2278
+ };
2279
+ }
2280
+ const [, timeStr, , content] = match;
2281
+ let timestamp;
2282
+ try {
2283
+ const isoStr = timeStr.replace(" ", "T");
2284
+ timestamp = new Date(isoStr).getTime();
2285
+ if (isNaN(timestamp)) {
2286
+ timestamp = Date.now();
2287
+ }
2288
+ } catch (error) {
2289
+ timestamp = Date.now();
2290
+ }
2291
+ const level = extractLogLevel(content);
2292
+ return {
2293
+ id,
2294
+ level,
2295
+ timestamp,
2296
+ message: content,
2297
+ context: null,
2298
+ traceId: null,
2299
+ userId: null,
2300
+ appId: null,
2301
+ tenantId: null,
2302
+ stack: null,
2303
+ meta: null,
2304
+ tags: [
2305
+ source
2306
+ ]
2307
+ };
2308
+ }
2309
+ __name(parseStdLog, "parseStdLog");
2310
+ function mapPinoLevelToServerLogLevel(pinoLevel) {
2311
+ if (typeof pinoLevel === "string") {
2312
+ const lower = pinoLevel.toLowerCase();
2313
+ if (lower === "fatal") return "fatal";
2314
+ if (lower === "error") return "error";
2315
+ if (lower === "warn" || lower === "warning") return "warn";
2316
+ if (lower === "info" || lower === "log") return "log";
2317
+ if (lower === "debug") return "debug";
2318
+ if (lower === "trace" || lower === "verbose") return "verbose";
2319
+ return "log";
2320
+ }
2321
+ if (pinoLevel >= 60) return "fatal";
2322
+ if (pinoLevel >= 50) return "error";
2323
+ if (pinoLevel >= 40) return "warn";
2324
+ if (pinoLevel >= 30) return "log";
2325
+ if (pinoLevel >= 20) return "debug";
2326
+ return "verbose";
2327
+ }
2328
+ __name(mapPinoLevelToServerLogLevel, "mapPinoLevelToServerLogLevel");
2329
+ function extractLogLevel(text) {
2330
+ const lower = text.toLowerCase();
2331
+ if (lower.includes("fatal") || lower.includes("critical")) return "fatal";
2332
+ if (lower.includes("error") || lower.includes("<e>") || lower.includes("\u2716")) return "error";
2333
+ if (lower.includes("warn") || lower.includes("warning") || lower.includes("<w>") || lower.includes("\u26A0")) return "warn";
2334
+ if (lower.includes("debug") || lower.includes("<d>")) return "debug";
2335
+ if (lower.includes("verbose") || lower.includes("trace")) return "verbose";
2336
+ return "log";
2337
+ }
2338
+ __name(extractLogLevel, "extractLogLevel");
2339
+ function generateUUID() {
2340
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
2341
+ const r = Math.random() * 16 | 0;
2342
+ const v = c === "x" ? r : r & 3 | 8;
2343
+ return v.toString(16);
2344
+ });
2345
+ }
2346
+ __name(generateUUID, "generateUUID");
2347
+ async function readTriggerList(filePath, trigger, path8, limit, triggerID) {
2361
2348
  if (!await fileExists(filePath)) {
2362
2349
  return void 0;
2363
2350
  }
@@ -2383,7 +2370,7 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
2383
2370
  if (alreadyAdded) {
2384
2371
  return false;
2385
2372
  }
2386
- const isAutomationTrigger = builder.path?.endsWith(path7);
2373
+ const isAutomationTrigger = builder.path?.endsWith(path8);
2387
2374
  if (!isAutomationTrigger) {
2388
2375
  return false;
2389
2376
  }
@@ -2437,176 +2424,64 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
2437
2424
  if (!builder.hasCompleted && (message.includes("HTTP request completed") || message.includes("HTTP request failed"))) {
2438
2425
  handleRequestCompleted(builder, entry, message);
2439
2426
  }
2440
- if (message.includes("HTTP request started") && !builder.startTime) {
2441
- builder.startTime = entry.time;
2442
- }
2443
- }, "processLogEntry");
2444
- const processLine = /* @__PURE__ */ __name((line) => {
2445
- const entry = parseLogLine2(line);
2446
- if (entry?.trace_id) {
2447
- processLogEntry(entry);
2448
- }
2449
- }, "processLine");
2450
- await readFileReverse(filePath, config.chunkSize, processLine);
2451
- return {
2452
- page: 1,
2453
- pageSize: completedCalls.length,
2454
- totalCalls: completedCalls.length,
2455
- totalPages: 1,
2456
- calls: completedCalls.map((builder) => ({
2457
- traceId: builder.traceId,
2458
- method: builder.method,
2459
- path: builder.path,
2460
- startTime: builder.startTime,
2461
- endTime: builder.endTime,
2462
- statusCode: builder.statusCode,
2463
- durationMs: builder.durationMs,
2464
- entries: builder.entries.slice().reverse()
2465
- }))
2466
- };
2467
- }
2468
- __name(readTriggerList, "readTriggerList");
2469
- async function readTriggerDetail(filePath, path7, instanceID) {
2470
- const exists = await fileExists(filePath);
2471
- if (!exists) {
2472
- return void 0;
2473
- }
2474
- const matches = [];
2475
- const stream = createReadStream3(filePath, {
2476
- encoding: "utf8"
2477
- });
2478
- const rl = createInterface3({
2479
- input: stream,
2480
- crlfDelay: Infinity
2481
- });
2482
- for await (const line of rl) {
2483
- const entry = parseLogLine2(line);
2484
- if (!entry) continue;
2485
- const isAutomationTrigger = entry.path?.endsWith(path7);
2486
- const hasInstanceID = entry.instance_id === instanceID && entry.trigger;
2487
- if (!isAutomationTrigger || !hasInstanceID) continue;
2488
- matches.push(entry);
2489
- }
2490
- rl.close();
2491
- stream.close();
2492
- return {
2493
- instanceID,
2494
- entries: matches
2495
- };
2496
- }
2497
- __name(readTriggerDetail, "readTriggerDetail");
2498
-
2499
- // src/middlewares/dev-logs/services/capability.service.ts
2500
- async function readCapabilityTraceList(filePath, capabilityId, limit) {
2501
- if (!await fileExists(filePath)) {
2502
- return void 0;
2503
- }
2504
- const config = {
2505
- chunkSize: 64 * 1024
2506
- };
2507
- const builders = /* @__PURE__ */ new Map();
2508
- const completedTraces = [];
2509
- const createCapabilityTraceBuilder = /* @__PURE__ */ __name((traceId, capId) => ({
2510
- traceId,
2511
- capabilityId: capId,
2512
- hasCompleted: false,
2513
- hasStartEntry: false
2514
- }), "createCapabilityTraceBuilder");
2515
- const shouldIncludeInCompletedTraces = /* @__PURE__ */ __name((builder) => {
2516
- const alreadyAdded = completedTraces.some((trace) => trace.traceId === builder.traceId);
2517
- if (alreadyAdded) {
2518
- return false;
2519
- }
2520
- return builder.capabilityId === capabilityId;
2521
- }, "shouldIncludeInCompletedTraces");
2522
- const updateBuilderMetadata = /* @__PURE__ */ __name((builder, entry) => {
2523
- if (entry.plugin_key && !builder.pluginKey) {
2524
- builder.pluginKey = String(entry.plugin_key);
2525
- }
2526
- if (entry.action && !builder.action) {
2527
- builder.action = String(entry.action);
2528
- }
2529
- const message = entry.message || "";
2530
- if (message.includes("Executing capability") && !builder.hasStartEntry) {
2531
- builder.hasStartEntry = true;
2532
- builder.startTime = entry.time;
2533
- if (entry.input) {
2534
- builder.input = String(entry.input);
2535
- }
2536
- }
2537
- if (message.includes("executed successfully")) {
2538
- builder.hasCompleted = true;
2539
- builder.endTime = entry.time;
2540
- builder.status = "success";
2541
- if (entry.output) {
2542
- builder.output = String(entry.output);
2543
- }
2544
- if (entry.duration_ms) {
2545
- builder.durationMs = Number(entry.duration_ms);
2546
- }
2547
- if (shouldIncludeInCompletedTraces(builder)) {
2548
- completedTraces.push(builder);
2549
- }
2550
- }
2551
- if (message.includes("execution failed")) {
2552
- builder.hasCompleted = true;
2553
- builder.endTime = entry.time;
2554
- builder.status = "failed";
2555
- if (entry.error) {
2556
- builder.error = {
2557
- message: String(entry.error)
2558
- };
2559
- }
2560
- if (entry.duration_ms) {
2561
- builder.durationMs = Number(entry.duration_ms);
2562
- }
2563
- if (shouldIncludeInCompletedTraces(builder)) {
2564
- completedTraces.push(builder);
2565
- }
2566
- }
2567
- }, "updateBuilderMetadata");
2568
- const processLogEntry = /* @__PURE__ */ __name((entry) => {
2569
- const { trace_id: traceId, capability_id: capId } = entry;
2570
- if (!traceId || !capId || capId !== capabilityId) return;
2571
- let builder = builders.get(traceId);
2572
- if (!builder) {
2573
- builder = createCapabilityTraceBuilder(traceId, capId);
2574
- builders.set(traceId, builder);
2427
+ if (message.includes("HTTP request started") && !builder.startTime) {
2428
+ builder.startTime = entry.time;
2575
2429
  }
2576
- updateBuilderMetadata(builder, entry);
2577
2430
  }, "processLogEntry");
2578
2431
  const processLine = /* @__PURE__ */ __name((line) => {
2579
2432
  const entry = parseLogLine2(line);
2580
- if (entry?.capability_id) {
2433
+ if (entry?.trace_id) {
2581
2434
  processLogEntry(entry);
2582
2435
  }
2583
2436
  }, "processLine");
2584
2437
  await readFileReverse(filePath, config.chunkSize, processLine);
2585
- completedTraces.sort((a, b) => {
2586
- const timeA = a.endTime ? new Date(a.endTime).getTime() : 0;
2587
- const timeB = b.endTime ? new Date(b.endTime).getTime() : 0;
2588
- return timeB - timeA;
2589
- });
2590
- const limitedTraces = limit ? completedTraces.slice(0, limit) : completedTraces;
2591
2438
  return {
2592
- capabilityId,
2593
- totalTraces: limitedTraces.length,
2594
- traces: limitedTraces.map((builder) => ({
2439
+ page: 1,
2440
+ pageSize: completedCalls.length,
2441
+ totalCalls: completedCalls.length,
2442
+ totalPages: 1,
2443
+ calls: completedCalls.map((builder) => ({
2595
2444
  traceId: builder.traceId,
2596
- capabilityId: builder.capabilityId,
2597
- pluginKey: builder.pluginKey,
2598
- action: builder.action,
2445
+ method: builder.method,
2446
+ path: builder.path,
2599
2447
  startTime: builder.startTime,
2600
2448
  endTime: builder.endTime,
2449
+ statusCode: builder.statusCode,
2601
2450
  durationMs: builder.durationMs,
2602
- status: builder.status || "failed",
2603
- input: builder.input,
2604
- output: builder.output,
2605
- error: builder.error
2451
+ entries: builder.entries.slice().reverse()
2606
2452
  }))
2607
2453
  };
2608
2454
  }
2609
- __name(readCapabilityTraceList, "readCapabilityTraceList");
2455
+ __name(readTriggerList, "readTriggerList");
2456
+ async function readTriggerDetail(filePath, path8, instanceID) {
2457
+ const exists = await fileExists(filePath);
2458
+ if (!exists) {
2459
+ return void 0;
2460
+ }
2461
+ const matches = [];
2462
+ const stream = createReadStream(filePath, {
2463
+ encoding: "utf8"
2464
+ });
2465
+ const rl = createInterface({
2466
+ input: stream,
2467
+ crlfDelay: Infinity
2468
+ });
2469
+ for await (const line of rl) {
2470
+ const entry = parseLogLine2(line);
2471
+ if (!entry) continue;
2472
+ const isAutomationTrigger = entry.path?.endsWith(path8);
2473
+ const hasInstanceID = entry.instance_id === instanceID && entry.trigger;
2474
+ if (!isAutomationTrigger || !hasInstanceID) continue;
2475
+ matches.push(entry);
2476
+ }
2477
+ rl.close();
2478
+ stream.close();
2479
+ return {
2480
+ instanceID,
2481
+ entries: matches
2482
+ };
2483
+ }
2484
+ __name(readTriggerDetail, "readTriggerDetail");
2610
2485
 
2611
2486
  // src/middlewares/dev-logs/controller.ts
2612
2487
  function handleNotFound(res, filePath, message = "Log file not found") {
@@ -2623,7 +2498,7 @@ function handleError(res, error, message = "Failed to read log file") {
2623
2498
  }
2624
2499
  __name(handleError, "handleError");
2625
2500
  function createGetTraceEntriesHandler(logDir) {
2626
- const appLogPath = join4(logDir, "server.log");
2501
+ const appLogPath = join3(logDir, "server.log");
2627
2502
  return async (req, res) => {
2628
2503
  const traceId = (req.params.traceId || "").trim();
2629
2504
  if (!traceId) {
@@ -2650,7 +2525,7 @@ function createGetTraceEntriesHandler(logDir) {
2650
2525
  }
2651
2526
  __name(createGetTraceEntriesHandler, "createGetTraceEntriesHandler");
2652
2527
  function createGetRecentTracesHandler(logDir) {
2653
- const traceLogPath = join4(logDir, "trace.log");
2528
+ const traceLogPath = join3(logDir, "trace.log");
2654
2529
  return async (req, res) => {
2655
2530
  const page = parsePositiveInt(req.query.page, 1);
2656
2531
  const pageSize = parseLimit(req.query.pageSize, 10, 100);
@@ -2727,7 +2602,7 @@ function createGetServerLogsHandler(logDir) {
2727
2602
  }
2728
2603
  __name(createGetServerLogsHandler, "createGetServerLogsHandler");
2729
2604
  function createGetTriggerListHandler(logDir) {
2730
- const traceLogPath = join4(logDir, "trace.log");
2605
+ const traceLogPath = join3(logDir, "trace.log");
2731
2606
  return async (req, res) => {
2732
2607
  const trigger = typeof req.query.trigger === "string" ? req.query.trigger.trim() : void 0;
2733
2608
  if (!trigger) {
@@ -2736,16 +2611,16 @@ function createGetTriggerListHandler(logDir) {
2736
2611
  });
2737
2612
  }
2738
2613
  const triggerID = typeof req.query.triggerID === "string" ? req.query.triggerID.trim() : void 0;
2739
- const path7 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
2614
+ const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
2740
2615
  const limit = parseLimit(req.query.limit, 10, 200);
2741
2616
  try {
2742
- const result = await readTriggerList(traceLogPath, trigger, path7, limit, triggerID);
2617
+ const result = await readTriggerList(traceLogPath, trigger, path8, limit, triggerID);
2743
2618
  if (!result) {
2744
2619
  return handleNotFound(res, traceLogPath);
2745
2620
  }
2746
2621
  res.json({
2747
2622
  file: getRelativePath(traceLogPath),
2748
- path: path7,
2623
+ path: path8,
2749
2624
  ...result
2750
2625
  });
2751
2626
  } catch (error) {
@@ -2755,7 +2630,7 @@ function createGetTriggerListHandler(logDir) {
2755
2630
  }
2756
2631
  __name(createGetTriggerListHandler, "createGetTriggerListHandler");
2757
2632
  function createGetTriggerDetailHandler(logDir) {
2758
- const traceLogPath = join4(logDir, "server.log");
2633
+ const traceLogPath = join3(logDir, "server.log");
2759
2634
  return async (req, res) => {
2760
2635
  const instanceID = (req.params.instanceID || "").trim();
2761
2636
  if (!instanceID) {
@@ -2763,9 +2638,9 @@ function createGetTriggerDetailHandler(logDir) {
2763
2638
  message: "instanceID is required"
2764
2639
  });
2765
2640
  }
2766
- const path7 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
2641
+ const path8 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
2767
2642
  try {
2768
- const result = await readTriggerDetail(traceLogPath, path7, instanceID);
2643
+ const result = await readTriggerDetail(traceLogPath, path8, instanceID);
2769
2644
  if (!result) {
2770
2645
  return handleNotFound(res, traceLogPath);
2771
2646
  }
@@ -2779,31 +2654,6 @@ function createGetTriggerDetailHandler(logDir) {
2779
2654
  };
2780
2655
  }
2781
2656
  __name(createGetTriggerDetailHandler, "createGetTriggerDetailHandler");
2782
- function createGetCapabilityTraceListHandler(logDir) {
2783
- const serverLogPath = join4(logDir, "server.log");
2784
- return async (req, res) => {
2785
- const capabilityId = typeof req.query.capability_id === "string" ? req.query.capability_id.trim() : void 0;
2786
- if (!capabilityId) {
2787
- return res.status(400).json({
2788
- message: "capability_id is required"
2789
- });
2790
- }
2791
- const limit = parseLimit(req.query.limit, 10, 200);
2792
- try {
2793
- const result = await readCapabilityTraceList(serverLogPath, capabilityId, limit);
2794
- if (!result) {
2795
- return handleNotFound(res, serverLogPath);
2796
- }
2797
- res.json({
2798
- file: getRelativePath(serverLogPath),
2799
- ...result
2800
- });
2801
- } catch (error) {
2802
- handleError(res, error, "Failed to read server log");
2803
- }
2804
- };
2805
- }
2806
- __name(createGetCapabilityTraceListHandler, "createGetCapabilityTraceListHandler");
2807
2657
 
2808
2658
  // src/middlewares/dev-logs/health.controller.ts
2809
2659
  import http2 from "http";
@@ -2872,6 +2722,521 @@ function createHealthCheckHandler(options = {}) {
2872
2722
  }
2873
2723
  __name(createHealthCheckHandler, "createHealthCheckHandler");
2874
2724
 
2725
+ // src/middlewares/dev-logs/sse/log-watcher.ts
2726
+ import * as fs9 from "fs";
2727
+ import * as path6 from "path";
2728
+ function mapPinoLevelToServerLogLevel2(pinoLevel) {
2729
+ if (typeof pinoLevel === "string") {
2730
+ const lower = pinoLevel.toLowerCase();
2731
+ if (lower === "fatal") return "fatal";
2732
+ if (lower === "error") return "error";
2733
+ if (lower === "warn" || lower === "warning") return "warn";
2734
+ if (lower === "info" || lower === "log") return "log";
2735
+ if (lower === "debug") return "debug";
2736
+ if (lower === "trace" || lower === "verbose") return "verbose";
2737
+ return "log";
2738
+ }
2739
+ if (pinoLevel >= 60) return "fatal";
2740
+ if (pinoLevel >= 50) return "error";
2741
+ if (pinoLevel >= 40) return "warn";
2742
+ if (pinoLevel >= 30) return "log";
2743
+ if (pinoLevel >= 20) return "debug";
2744
+ return "verbose";
2745
+ }
2746
+ __name(mapPinoLevelToServerLogLevel2, "mapPinoLevelToServerLogLevel");
2747
+ function extractLogLevel2(text) {
2748
+ const lower = text.toLowerCase();
2749
+ if (lower.includes("fatal") || lower.includes("critical")) return "fatal";
2750
+ if (lower.includes("error") || lower.includes("<e>") || lower.includes("\u2716")) return "error";
2751
+ if (lower.includes("warn") || lower.includes("warning") || lower.includes("<w>") || lower.includes("\u26A0")) return "warn";
2752
+ if (lower.includes("debug") || lower.includes("<d>")) return "debug";
2753
+ if (lower.includes("verbose") || lower.includes("trace")) return "verbose";
2754
+ return "log";
2755
+ }
2756
+ __name(extractLogLevel2, "extractLogLevel");
2757
+ function generateUUID2() {
2758
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
2759
+ const r = Math.random() * 16 | 0;
2760
+ const v = c === "x" ? r : r & 3 | 8;
2761
+ return v.toString(16);
2762
+ });
2763
+ }
2764
+ __name(generateUUID2, "generateUUID");
2765
+ function parsePinoLog2(line, source) {
2766
+ try {
2767
+ const pinoLog = JSON.parse(line);
2768
+ const id = generateUUID2();
2769
+ return {
2770
+ id,
2771
+ level: mapPinoLevelToServerLogLevel2(pinoLog.level),
2772
+ timestamp: new Date(pinoLog.time).getTime(),
2773
+ message: pinoLog.message || pinoLog.msg || "",
2774
+ context: pinoLog.context || null,
2775
+ traceId: pinoLog.trace_id || null,
2776
+ userId: pinoLog.user_id || null,
2777
+ appId: pinoLog.app_id || null,
2778
+ tenantId: pinoLog.tenant_id || null,
2779
+ stack: pinoLog.stack || null,
2780
+ meta: {
2781
+ pid: pinoLog.pid,
2782
+ hostname: pinoLog.hostname,
2783
+ path: pinoLog.path,
2784
+ method: pinoLog.method,
2785
+ statusCode: pinoLog.status_code,
2786
+ durationMs: pinoLog.duration_ms,
2787
+ ip: pinoLog.ip,
2788
+ requestBody: pinoLog.request_body,
2789
+ responseBody: pinoLog.response_body
2790
+ },
2791
+ tags: [
2792
+ source
2793
+ ]
2794
+ };
2795
+ } catch {
2796
+ return null;
2797
+ }
2798
+ }
2799
+ __name(parsePinoLog2, "parsePinoLog");
2800
+ function parseStdLog2(line, source) {
2801
+ const id = generateUUID2();
2802
+ const match = line.match(/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(server|client)\] (.*)$/);
2803
+ if (!match) {
2804
+ return {
2805
+ id,
2806
+ level: extractLogLevel2(line),
2807
+ timestamp: Date.now(),
2808
+ message: line,
2809
+ context: null,
2810
+ traceId: null,
2811
+ userId: null,
2812
+ appId: null,
2813
+ tenantId: null,
2814
+ stack: null,
2815
+ meta: null,
2816
+ tags: [
2817
+ source
2818
+ ]
2819
+ };
2820
+ }
2821
+ const [, timeStr, , content] = match;
2822
+ let timestamp;
2823
+ try {
2824
+ const isoStr = timeStr.replace(" ", "T");
2825
+ timestamp = new Date(isoStr).getTime();
2826
+ if (isNaN(timestamp)) {
2827
+ timestamp = Date.now();
2828
+ }
2829
+ } catch {
2830
+ timestamp = Date.now();
2831
+ }
2832
+ return {
2833
+ id,
2834
+ level: extractLogLevel2(content),
2835
+ timestamp,
2836
+ message: content,
2837
+ context: null,
2838
+ traceId: null,
2839
+ userId: null,
2840
+ appId: null,
2841
+ tenantId: null,
2842
+ stack: null,
2843
+ meta: null,
2844
+ tags: [
2845
+ source
2846
+ ]
2847
+ };
2848
+ }
2849
+ __name(parseStdLog2, "parseStdLog");
2850
+ var LogWatcher = class {
2851
+ static {
2852
+ __name(this, "LogWatcher");
2853
+ }
2854
+ logDir = "";
2855
+ watchers = /* @__PURE__ */ new Map();
2856
+ filePositions = /* @__PURE__ */ new Map();
2857
+ subscribers = /* @__PURE__ */ new Set();
2858
+ isRunning = false;
2859
+ debug = false;
2860
+ logFiles = [
2861
+ {
2862
+ fileName: "server.log",
2863
+ source: "server",
2864
+ parser: parsePinoLog2
2865
+ },
2866
+ {
2867
+ fileName: "trace.log",
2868
+ source: "trace",
2869
+ parser: parsePinoLog2
2870
+ },
2871
+ {
2872
+ fileName: "server.std.log",
2873
+ source: "server-std",
2874
+ parser: parseStdLog2
2875
+ },
2876
+ {
2877
+ fileName: "client.std.log",
2878
+ source: "client-std",
2879
+ parser: parseStdLog2
2880
+ }
2881
+ ];
2882
+ constructor(options = {}) {
2883
+ this.debug = options.debug ?? false;
2884
+ }
2885
+ log(...args) {
2886
+ if (this.debug) {
2887
+ console.log("[LogWatcher]", ...args);
2888
+ }
2889
+ }
2890
+ /**
2891
+ * Start watching log files
2892
+ */
2893
+ start(logDir) {
2894
+ if (this.isRunning) {
2895
+ this.log("Already running, ignoring start call");
2896
+ return;
2897
+ }
2898
+ this.logDir = logDir;
2899
+ this.isRunning = true;
2900
+ this.log(`Starting to watch log files in: ${logDir}`);
2901
+ for (const config of this.logFiles) {
2902
+ this.watchFile(config);
2903
+ }
2904
+ }
2905
+ /**
2906
+ * Stop watching all files
2907
+ */
2908
+ stop() {
2909
+ if (!this.isRunning) {
2910
+ return;
2911
+ }
2912
+ this.log("Stopping file watchers");
2913
+ this.isRunning = false;
2914
+ Array.from(this.watchers.entries()).forEach(([fileName, watcher]) => {
2915
+ watcher.close();
2916
+ this.log(`Closed watcher for: ${fileName}`);
2917
+ });
2918
+ this.watchers.clear();
2919
+ this.filePositions.clear();
2920
+ }
2921
+ /**
2922
+ * Subscribe to new log events
2923
+ */
2924
+ onLog(callback) {
2925
+ this.subscribers.add(callback);
2926
+ this.log(`Subscriber added, total: ${this.subscribers.size}`);
2927
+ return () => {
2928
+ this.subscribers.delete(callback);
2929
+ this.log(`Subscriber removed, total: ${this.subscribers.size}`);
2930
+ };
2931
+ }
2932
+ /**
2933
+ * Get subscriber count
2934
+ */
2935
+ getSubscriberCount() {
2936
+ return this.subscribers.size;
2937
+ }
2938
+ /**
2939
+ * Watch a single log file
2940
+ */
2941
+ watchFile(config) {
2942
+ const filePath = path6.join(this.logDir, config.fileName);
2943
+ if (!fs9.existsSync(filePath)) {
2944
+ this.log(`File not found, skipping: ${config.fileName}`);
2945
+ return;
2946
+ }
2947
+ try {
2948
+ const stats = fs9.statSync(filePath);
2949
+ this.filePositions.set(config.fileName, stats.size);
2950
+ this.log(`Initialized position for ${config.fileName}: ${stats.size} bytes`);
2951
+ } catch (error) {
2952
+ this.log(`Failed to get initial position for ${config.fileName}:`, error);
2953
+ this.filePositions.set(config.fileName, 0);
2954
+ }
2955
+ try {
2956
+ const watcher = fs9.watch(filePath, (eventType) => {
2957
+ if (eventType === "change") {
2958
+ this.handleFileChange(config);
2959
+ }
2960
+ });
2961
+ watcher.on("error", (error) => {
2962
+ this.log(`Watcher error for ${config.fileName}:`, error);
2963
+ this.restartWatcher(config);
2964
+ });
2965
+ this.watchers.set(config.fileName, watcher);
2966
+ this.log(`Started watching: ${config.fileName}`);
2967
+ } catch (error) {
2968
+ this.log(`Failed to start watcher for ${config.fileName}:`, error);
2969
+ }
2970
+ }
2971
+ /**
2972
+ * Restart a file watcher after error
2973
+ */
2974
+ restartWatcher(config) {
2975
+ const existingWatcher = this.watchers.get(config.fileName);
2976
+ if (existingWatcher) {
2977
+ existingWatcher.close();
2978
+ this.watchers.delete(config.fileName);
2979
+ }
2980
+ setTimeout(() => {
2981
+ if (this.isRunning) {
2982
+ this.log(`Restarting watcher for: ${config.fileName}`);
2983
+ this.watchFile(config);
2984
+ }
2985
+ }, 1e3);
2986
+ }
2987
+ /**
2988
+ * Handle file change event - read new content
2989
+ */
2990
+ handleFileChange(config) {
2991
+ const filePath = path6.join(this.logDir, config.fileName);
2992
+ const lastPosition = this.filePositions.get(config.fileName) || 0;
2993
+ try {
2994
+ const stats = fs9.statSync(filePath);
2995
+ const currentSize = stats.size;
2996
+ if (currentSize < lastPosition) {
2997
+ this.log(`File ${config.fileName} was truncated, resetting position`);
2998
+ this.filePositions.set(config.fileName, 0);
2999
+ this.handleFileChange(config);
3000
+ return;
3001
+ }
3002
+ if (currentSize === lastPosition) {
3003
+ return;
3004
+ }
3005
+ const readSize = currentSize - lastPosition;
3006
+ const buffer = Buffer.alloc(readSize);
3007
+ const fd = fs9.openSync(filePath, "r");
3008
+ try {
3009
+ fs9.readSync(fd, buffer, 0, readSize, lastPosition);
3010
+ } finally {
3011
+ fs9.closeSync(fd);
3012
+ }
3013
+ this.filePositions.set(config.fileName, currentSize);
3014
+ const content = buffer.toString("utf8");
3015
+ const lines = content.split("\n");
3016
+ for (const line of lines) {
3017
+ if (!line.trim()) continue;
3018
+ try {
3019
+ const log = config.parser(line, config.source);
3020
+ if (log) {
3021
+ this.notifySubscribers(log);
3022
+ }
3023
+ } catch {
3024
+ }
3025
+ }
3026
+ } catch (error) {
3027
+ this.log(`Error reading file ${config.fileName}:`, error);
3028
+ }
3029
+ }
3030
+ /**
3031
+ * Notify all subscribers of new log
3032
+ */
3033
+ notifySubscribers(log) {
3034
+ Array.from(this.subscribers).forEach((subscriber) => {
3035
+ try {
3036
+ subscriber(log);
3037
+ } catch (error) {
3038
+ this.log("Subscriber error:", error);
3039
+ }
3040
+ });
3041
+ }
3042
+ };
3043
+
3044
+ // src/middlewares/dev-logs/sse/client-manager.ts
3045
+ var ClientManager = class {
3046
+ static {
3047
+ __name(this, "ClientManager");
3048
+ }
3049
+ clients = /* @__PURE__ */ new Map();
3050
+ debug = false;
3051
+ constructor(options = {}) {
3052
+ this.debug = options.debug ?? false;
3053
+ }
3054
+ log(...args) {
3055
+ if (this.debug) {
3056
+ console.log("[ClientManager]", ...args);
3057
+ }
3058
+ }
3059
+ /**
3060
+ * Generate a unique client ID
3061
+ */
3062
+ generateClientId() {
3063
+ return `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
3064
+ }
3065
+ /**
3066
+ * Add a new client connection
3067
+ */
3068
+ addClient(id, res) {
3069
+ this.clients.set(id, {
3070
+ id,
3071
+ res,
3072
+ connectedAt: Date.now()
3073
+ });
3074
+ this.log(`Client connected: ${id}, total clients: ${this.clients.size}`);
3075
+ }
3076
+ /**
3077
+ * Remove a client connection
3078
+ */
3079
+ removeClient(id) {
3080
+ if (this.clients.has(id)) {
3081
+ this.clients.delete(id);
3082
+ this.log(`Client disconnected: ${id}, total clients: ${this.clients.size}`);
3083
+ }
3084
+ }
3085
+ /**
3086
+ * Get current client count
3087
+ */
3088
+ getClientCount() {
3089
+ return this.clients.size;
3090
+ }
3091
+ /**
3092
+ * Check if a client exists
3093
+ */
3094
+ hasClient(id) {
3095
+ return this.clients.has(id);
3096
+ }
3097
+ /**
3098
+ * Send SSE event to a specific client
3099
+ */
3100
+ sendToClient(clientId, event, data) {
3101
+ const client = this.clients.get(clientId);
3102
+ if (!client) {
3103
+ return false;
3104
+ }
3105
+ try {
3106
+ const message = this.formatSSEMessage(event, data);
3107
+ client.res.write(message);
3108
+ return true;
3109
+ } catch (error) {
3110
+ this.log(`Failed to send to client ${clientId}:`, error);
3111
+ this.removeClient(clientId);
3112
+ return false;
3113
+ }
3114
+ }
3115
+ /**
3116
+ * Broadcast SSE event to all clients
3117
+ */
3118
+ broadcast(event, data) {
3119
+ const message = this.formatSSEMessage(event, data);
3120
+ const failedClients = [];
3121
+ Array.from(this.clients.entries()).forEach(([id, client]) => {
3122
+ try {
3123
+ client.res.write(message);
3124
+ } catch (error) {
3125
+ this.log(`Broadcast failed for client ${id}:`, error);
3126
+ failedClients.push(id);
3127
+ }
3128
+ });
3129
+ failedClients.forEach((id) => {
3130
+ this.removeClient(id);
3131
+ });
3132
+ }
3133
+ /**
3134
+ * Format SSE message
3135
+ * SSE format: event: <event>\ndata: <json>\n\n
3136
+ */
3137
+ formatSSEMessage(event, data) {
3138
+ const jsonData = JSON.stringify(data);
3139
+ return `event: ${event}
3140
+ data: ${jsonData}
3141
+
3142
+ `;
3143
+ }
3144
+ /**
3145
+ * Get all client IDs
3146
+ */
3147
+ getClientIds() {
3148
+ return Array.from(this.clients.keys());
3149
+ }
3150
+ /**
3151
+ * Get client info
3152
+ */
3153
+ getClientInfo(id) {
3154
+ const client = this.clients.get(id);
3155
+ if (!client) {
3156
+ return null;
3157
+ }
3158
+ return {
3159
+ id: client.id,
3160
+ connectedAt: client.connectedAt
3161
+ };
3162
+ }
3163
+ /**
3164
+ * Close all client connections
3165
+ */
3166
+ closeAll() {
3167
+ this.log(`Closing all ${this.clients.size} clients`);
3168
+ Array.from(this.clients.entries()).forEach(([id, client]) => {
3169
+ try {
3170
+ client.res.end();
3171
+ } catch (error) {
3172
+ this.log(`Error closing client ${id}:`, error);
3173
+ }
3174
+ });
3175
+ this.clients.clear();
3176
+ }
3177
+ };
3178
+
3179
+ // src/middlewares/dev-logs/sse/sse.controller.ts
3180
+ function createSSEHandler(logDir, options = {}) {
3181
+ const { debug = false, heartbeatInterval = 3e4 } = options;
3182
+ const logWatcher = new LogWatcher({
3183
+ debug
3184
+ });
3185
+ const clientManager = new ClientManager({
3186
+ debug
3187
+ });
3188
+ const log = /* @__PURE__ */ __name((...args) => {
3189
+ if (debug) {
3190
+ console.log("[SSEHandler]", ...args);
3191
+ }
3192
+ }, "log");
3193
+ return (req, res) => {
3194
+ res.setHeader("Content-Type", "text/event-stream");
3195
+ res.setHeader("Cache-Control", "no-cache");
3196
+ res.setHeader("Connection", "keep-alive");
3197
+ res.setHeader("X-Accel-Buffering", "no");
3198
+ res.setTimeout(0);
3199
+ const clientId = clientManager.generateClientId();
3200
+ clientManager.addClient(clientId, res);
3201
+ log(`New SSE connection: ${clientId}`);
3202
+ if (clientManager.getClientCount() === 1) {
3203
+ log("First client connected, starting log watcher");
3204
+ logWatcher.start(logDir);
3205
+ }
3206
+ const unsubscribe = logWatcher.onLog((logEntry) => {
3207
+ clientManager.sendToClient(clientId, "log", logEntry);
3208
+ });
3209
+ clientManager.sendToClient(clientId, "connected", {
3210
+ clientId,
3211
+ timestamp: Date.now()
3212
+ });
3213
+ const heartbeat = setInterval(() => {
3214
+ const success = clientManager.sendToClient(clientId, "heartbeat", {
3215
+ timestamp: Date.now()
3216
+ });
3217
+ if (!success) {
3218
+ clearInterval(heartbeat);
3219
+ }
3220
+ }, heartbeatInterval);
3221
+ const cleanup = /* @__PURE__ */ __name(() => {
3222
+ log(`Client disconnected: ${clientId}`);
3223
+ clearInterval(heartbeat);
3224
+ unsubscribe();
3225
+ clientManager.removeClient(clientId);
3226
+ if (clientManager.getClientCount() === 0) {
3227
+ log("No more clients, stopping log watcher");
3228
+ logWatcher.stop();
3229
+ }
3230
+ }, "cleanup");
3231
+ req.on("close", cleanup);
3232
+ req.on("error", (error) => {
3233
+ log(`Client error ${clientId}:`, error);
3234
+ cleanup();
3235
+ });
3236
+ };
3237
+ }
3238
+ __name(createSSEHandler, "createSSEHandler");
3239
+
2875
3240
  // src/middlewares/dev-logs/router.ts
2876
3241
  function createDevLogRouter(options = {}) {
2877
3242
  const logDir = resolveLogDir(options.logDir);
@@ -2880,9 +3245,9 @@ function createDevLogRouter(options = {}) {
2880
3245
  router.get("/trace/recent", createGetRecentTracesHandler(logDir));
2881
3246
  router.get("/files/:fileName", createGetLogFileHandler(logDir));
2882
3247
  router.get("/server-logs", createGetServerLogsHandler(logDir));
3248
+ router.get("/server-logs/stream", createSSEHandler(logDir));
2883
3249
  router.get("/trace/trigger/list", createGetTriggerListHandler(logDir));
2884
3250
  router.get("/trace/trigger/:instanceID", createGetTriggerDetailHandler(logDir));
2885
- router.get("/trace/capability/list", createGetCapabilityTraceListHandler(logDir));
2886
3251
  router.get("/health", createHealthCheckHandler());
2887
3252
  return router;
2888
3253
  }
@@ -2942,11 +3307,11 @@ import express3 from "express";
2942
3307
 
2943
3308
  // src/middlewares/collect-logs/controller.ts
2944
3309
  import { join as join6 } from "path";
2945
- import fs10 from "fs";
3310
+ import fs11 from "fs";
2946
3311
 
2947
3312
  // src/middlewares/collect-logs/utils.ts
2948
3313
  import { isAbsolute as isAbsolute2, join as join5 } from "path";
2949
- import fs9 from "fs";
3314
+ import fs10 from "fs";
2950
3315
  function resolveLogDir2(provided) {
2951
3316
  if (!provided) {
2952
3317
  return join5(process.cwd(), "logs");
@@ -2955,8 +3320,8 @@ function resolveLogDir2(provided) {
2955
3320
  }
2956
3321
  __name(resolveLogDir2, "resolveLogDir");
2957
3322
  function ensureDir(dir) {
2958
- if (!fs9.existsSync(dir)) {
2959
- fs9.mkdirSync(dir, {
3323
+ if (!fs10.existsSync(dir)) {
3324
+ fs10.mkdirSync(dir, {
2960
3325
  recursive: true
2961
3326
  });
2962
3327
  }
@@ -2988,7 +3353,7 @@ function collectLogsHandler(logDir, fileName) {
2988
3353
  ...logContent,
2989
3354
  server_time: (/* @__PURE__ */ new Date()).toISOString()
2990
3355
  }) + "\n";
2991
- await fs10.promises.appendFile(filePath, logLine);
3356
+ await fs11.promises.appendFile(filePath, logLine);
2992
3357
  res.json({
2993
3358
  success: true
2994
3359
  });
@@ -3016,7 +3381,7 @@ function collectLogsBatchHandler(logDir, fileName) {
3016
3381
  server_time: (/* @__PURE__ */ new Date()).toISOString()
3017
3382
  }) + "\n");
3018
3383
  }
3019
- await fs10.promises.appendFile(filePath, logLines.join(""));
3384
+ await fs11.promises.appendFile(filePath, logLines.join(""));
3020
3385
  res.json({
3021
3386
  success: true
3022
3387
  });
@@ -3079,7 +3444,7 @@ function isGlobalMiddleware(middleware) {
3079
3444
  }
3080
3445
  __name(isGlobalMiddleware, "isGlobalMiddleware");
3081
3446
  function computeMountPath(basePath, mountPath) {
3082
- const routePath = path6.posix.join(basePath, mountPath);
3447
+ const routePath = path7.posix.join(basePath, mountPath);
3083
3448
  return routePath.startsWith("/") ? routePath : `/${routePath}`;
3084
3449
  }
3085
3450
  __name(computeMountPath, "computeMountPath");
@@ -3087,7 +3452,7 @@ function logMiddlewareRegistration(middleware, fullMountPath) {
3087
3452
  if (middleware.routes && middleware.routes.length > 0) {
3088
3453
  console.log(`[Middleware] Registered: ${middleware.name} at ${fullMountPath}`);
3089
3454
  middleware.routes.forEach((route) => {
3090
- const routePath = route.path === "/" ? fullMountPath : path6.posix.join(fullMountPath, route.path);
3455
+ const routePath = route.path === "/" ? fullMountPath : path7.posix.join(fullMountPath, route.path);
3091
3456
  console.log(` ${route.method} ${routePath} - ${route.description}`);
3092
3457
  });
3093
3458
  } else {