@superatomai/sdk-node 0.0.3 → 0.0.5

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
@@ -441,7 +441,8 @@ __export(index_exports, {
441
441
  ThreadManager: () => ThreadManager,
442
442
  UIBlock: () => UIBlock,
443
443
  UILogCollector: () => UILogCollector,
444
- UserManager: () => UserManager
444
+ UserManager: () => UserManager,
445
+ logger: () => logger
445
446
  });
446
447
  module.exports = __toCommonJS(index_exports);
447
448
 
@@ -641,6 +642,18 @@ var DSLRendererPropsSchema2 = import_zod2.z.object({
641
642
  });
642
643
 
643
644
  // src/types.ts
645
+ var UserSchema = import_zod3.z.object({
646
+ username: import_zod3.z.string().min(1, "Username is required"),
647
+ email: import_zod3.z.string().email("Invalid email format").optional(),
648
+ password: import_zod3.z.string().min(1, "Password is required"),
649
+ fullname: import_zod3.z.string().optional(),
650
+ role: import_zod3.z.string().optional(),
651
+ wsIds: import_zod3.z.array(import_zod3.z.string()).optional()
652
+ // Only in memory, not persisted to file
653
+ });
654
+ var UsersDataSchema = import_zod3.z.object({
655
+ users: import_zod3.z.array(UserSchema)
656
+ });
644
657
  var MessageParticipantSchema = import_zod3.z.object({
645
658
  id: import_zod3.z.string().optional(),
646
659
  type: import_zod3.z.string().optional()
@@ -741,7 +754,10 @@ var UsersRequestPayloadSchema = import_zod3.z.object({
741
754
  operation: import_zod3.z.enum(["create", "update", "delete", "getAll", "getOne"]),
742
755
  data: import_zod3.z.object({
743
756
  username: import_zod3.z.string().optional(),
744
- password: import_zod3.z.string().optional()
757
+ email: import_zod3.z.string().email("Invalid email format").optional(),
758
+ password: import_zod3.z.string().optional(),
759
+ fullname: import_zod3.z.string().optional(),
760
+ role: import_zod3.z.string().optional()
745
761
  }).optional()
746
762
  });
747
763
  var UsersRequestMessageSchema = import_zod3.z.object({
@@ -807,20 +823,91 @@ var ReportsRequestMessageSchema = import_zod3.z.object({
807
823
 
808
824
  // src/utils/logger.ts
809
825
  var PREFIX = "[SuperatomSDK]";
810
- var logger = {
811
- info: (...args) => {
812
- console.log(PREFIX, ...args);
813
- },
814
- error: (...args) => {
815
- console.error(PREFIX, ...args);
816
- },
817
- warn: (...args) => {
818
- console.warn(PREFIX, ...args);
819
- },
820
- debug: (...args) => {
821
- console.log(PREFIX, "[DEBUG]", ...args);
826
+ var LOG_LEVEL_PRIORITY = {
827
+ errors: 0,
828
+ warnings: 1,
829
+ info: 2,
830
+ verbose: 3
831
+ };
832
+ var MESSAGE_LEVEL_PRIORITY = {
833
+ error: 0,
834
+ warn: 1,
835
+ info: 2,
836
+ debug: 3
837
+ };
838
+ var Logger = class {
839
+ constructor() {
840
+ const envLevel = (process.env.SUPERATOM_LOG_LEVEL || "info").toLowerCase();
841
+ if (this.isValidLogLevel(envLevel)) {
842
+ this.currentLevel = envLevel;
843
+ } else {
844
+ this.currentLevel = "info";
845
+ console.warn(
846
+ `${PREFIX} Invalid log level "${envLevel}". Using default "info". Valid levels: errors, warnings, info, verbose`
847
+ );
848
+ }
849
+ this.currentLevelPriority = LOG_LEVEL_PRIORITY[this.currentLevel];
850
+ }
851
+ /**
852
+ * Check if a string is a valid log level
853
+ */
854
+ isValidLogLevel(level) {
855
+ return level === "errors" || level === "warnings" || level === "info" || level === "verbose";
856
+ }
857
+ /**
858
+ * Check if a message should be logged based on current log level
859
+ */
860
+ shouldLog(messageLevel) {
861
+ const messagePriority = MESSAGE_LEVEL_PRIORITY[messageLevel];
862
+ return messagePriority <= this.currentLevelPriority;
863
+ }
864
+ /**
865
+ * Get current log level
866
+ */
867
+ getLogLevel() {
868
+ return this.currentLevel;
869
+ }
870
+ /**
871
+ * Set log level programmatically
872
+ */
873
+ setLogLevel(level) {
874
+ this.currentLevel = level;
875
+ this.currentLevelPriority = LOG_LEVEL_PRIORITY[level];
876
+ }
877
+ /**
878
+ * Log info message (shown for info and verbose levels)
879
+ */
880
+ info(...args) {
881
+ if (this.shouldLog("info")) {
882
+ console.log(PREFIX, ...args);
883
+ }
884
+ }
885
+ /**
886
+ * Log error message (shown for all levels)
887
+ */
888
+ error(...args) {
889
+ if (this.shouldLog("error")) {
890
+ console.error(PREFIX, ...args);
891
+ }
892
+ }
893
+ /**
894
+ * Log warning message (shown for warnings, info, and verbose levels)
895
+ */
896
+ warn(...args) {
897
+ if (this.shouldLog("warn")) {
898
+ console.warn(PREFIX, ...args);
899
+ }
900
+ }
901
+ /**
902
+ * Log debug message (only shown for verbose level)
903
+ */
904
+ debug(...args) {
905
+ if (this.shouldLog("debug")) {
906
+ console.log(PREFIX, "[DEBUG]", ...args);
907
+ }
822
908
  }
823
909
  };
910
+ var logger = new Logger();
824
911
 
825
912
  // src/threads/uiblock.ts
826
913
  var import_crypto = require("crypto");
@@ -855,13 +942,15 @@ var UIBlock = class {
855
942
  * @param generatedComponentMetadata - Optional metadata about the generated component
856
943
  * @param actions - Optional array of available actions
857
944
  * @param id - Optional custom ID, generates UUID if not provided
945
+ * @param textResponse - Optional text response from LLM
858
946
  */
859
- constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id) {
947
+ constructor(userQuestion, componentData = {}, generatedComponentMetadata = {}, actions = [], id, textResponse = null) {
860
948
  this.id = id || (0, import_crypto.randomUUID)();
861
949
  this.userQuestion = userQuestion;
862
950
  this.componentData = componentData;
863
951
  this.generatedComponentMetadata = generatedComponentMetadata;
864
952
  this.actions = actions;
953
+ this.textResponse = textResponse;
865
954
  this.createdAt = /* @__PURE__ */ new Date();
866
955
  }
867
956
  /**
@@ -980,6 +1069,18 @@ var UIBlock = class {
980
1069
  const processedData = this.processDataForStorage(data);
981
1070
  this.componentData = { ...this.componentData, ...processedData };
982
1071
  }
1072
+ /**
1073
+ * Get text response
1074
+ */
1075
+ getTextResponse() {
1076
+ return this.textResponse;
1077
+ }
1078
+ /**
1079
+ * Set or update text response
1080
+ */
1081
+ setTextResponse(textResponse) {
1082
+ this.textResponse = textResponse;
1083
+ }
983
1084
  /**
984
1085
  * Get all actions (only if they are resolved, not if fetching)
985
1086
  */
@@ -1063,6 +1164,7 @@ var UIBlock = class {
1063
1164
  userQuestion: this.userQuestion,
1064
1165
  generatedComponentMetadata: this.generatedComponentMetadata,
1065
1166
  componentData: this.componentData,
1167
+ textResponse: this.textResponse,
1066
1168
  actions: actionsValue,
1067
1169
  isFetchingActions: this.actions instanceof Promise,
1068
1170
  createdAt: this.createdAt.toISOString()
@@ -1497,9 +1599,9 @@ function getUserManager() {
1497
1599
  }
1498
1600
  return currentUserManager;
1499
1601
  }
1500
- function findUserByUsername(username) {
1602
+ function findUserByUsernameOrEmail(identifier) {
1501
1603
  const manager = getUserManager();
1502
- const user = manager.getUser(username);
1604
+ const user = manager.getUserByUsernameOrEmail(identifier);
1503
1605
  return user || null;
1504
1606
  }
1505
1607
  function addWsIdToUser(username, wsId) {
@@ -1521,60 +1623,81 @@ async function cleanupUserStorage() {
1521
1623
 
1522
1624
  // src/auth/validator.ts
1523
1625
  function validateUser(credentials) {
1524
- const { username, password } = credentials;
1525
- if (!username || !password) {
1526
- logger.warn("Validation failed: Username and password are required");
1626
+ const { username, email, password } = credentials;
1627
+ const identifier = username || email;
1628
+ logger.debug("[validateUser] Starting user validation");
1629
+ logger.debug(`[validateUser] Username provided: ${username ? "\u2713" : "\u2717"}, Email provided: ${email ? "\u2713" : "\u2717"}, Password provided: ${password ? "\u2713" : "\u2717"}`);
1630
+ if (!identifier || !password) {
1631
+ logger.warn("[validateUser] Validation failed: Username/email and password are required");
1527
1632
  return {
1528
1633
  success: false,
1529
- error: "Username and password are required"
1634
+ error: "Username or email and password are required"
1530
1635
  };
1531
1636
  }
1532
- const user = findUserByUsername(username);
1637
+ logger.debug(`[validateUser] Looking up user by identifier: ${identifier}`);
1638
+ const user = findUserByUsernameOrEmail(identifier);
1533
1639
  if (!user) {
1534
- logger.warn(`Validation failed: User not found - ${username}`);
1640
+ logger.warn(`[validateUser] Validation failed: User not found - ${identifier}`);
1535
1641
  return {
1536
1642
  success: false,
1537
- error: "Invalid username"
1643
+ error: "Invalid username or email"
1538
1644
  };
1539
1645
  }
1646
+ logger.debug(`[validateUser] User found: ${user.username}, verifying password`);
1540
1647
  const hashedPassword = hashPassword(user.password);
1541
1648
  if (hashedPassword !== password) {
1542
- logger.warn(`Validation failed: Invalid password for user - ${username}`);
1649
+ logger.warn(`[validateUser] Validation failed: Invalid password for user - ${user.username}`);
1650
+ logger.debug(`[validateUser] Password hash mismatch for user: ${user.username}`);
1543
1651
  return {
1544
1652
  success: false,
1545
1653
  error: "Invalid password"
1546
1654
  };
1547
1655
  }
1548
- logger.debug(`User validated successfully: ${username}`);
1656
+ logger.info(`[validateUser] \u2713 User validated successfully: ${user.username}`);
1657
+ logger.debug(`[validateUser] Returning user data for: ${user.username}`);
1549
1658
  return {
1550
1659
  success: true,
1551
- data: user.username
1660
+ data: user.username,
1661
+ username: user.username
1552
1662
  };
1553
1663
  }
1554
1664
  function authenticateAndStoreWsId(credentials, wsId) {
1665
+ const identifier = credentials.username || credentials.email;
1666
+ logger.debug("[authenticateAndStoreWsId] Starting authentication and WebSocket ID storage");
1667
+ logger.debug("[authenticateAndStoreWsId] Validating user credentials");
1555
1668
  const validationResult = validateUser(credentials);
1556
1669
  if (!validationResult.success) {
1670
+ logger.warn(`[authenticateAndStoreWsId] User validation failed for: ${identifier}`);
1557
1671
  return validationResult;
1558
1672
  }
1559
- const stored = addWsIdToUser(credentials.username, wsId);
1560
- if (!stored) {
1561
- logger.error(`Failed to store WebSocket ID for user: ${credentials.username}`);
1562
- return {
1563
- success: false,
1564
- error: "Failed to store user session"
1565
- };
1566
- }
1567
- logger.info(`WebSocket ID stored for user: ${credentials.username}`);
1673
+ const username = validationResult.username;
1674
+ logger.info(`[authenticateAndStoreWsId] User ${username} validated, storing WebSocket ID`);
1675
+ logger.debug(`[authenticateAndStoreWsId] Calling addWsIdToUser for ${username}`);
1676
+ addWsIdToUser(username, wsId);
1677
+ logger.debug(`[authenticateAndStoreWsId] WebSocket ID ${wsId} associated with user ${username}`);
1568
1678
  return validationResult;
1569
1679
  }
1570
1680
  function verifyAuthToken(authToken) {
1571
1681
  try {
1682
+ logger.debug("[verifyAuthToken] Starting token verification");
1683
+ logger.debug("[verifyAuthToken] Decoding base64 token");
1572
1684
  const decodedString = Buffer.from(authToken, "base64").toString("utf-8");
1685
+ logger.debug("[verifyAuthToken] Parsing decoded token as JSON");
1573
1686
  const credentials = JSON.parse(decodedString);
1574
- logger.debug("Token decoded and parsed successfully");
1575
- return validateUser(credentials);
1687
+ logger.debug("[verifyAuthToken] Token decoded and parsed successfully");
1688
+ logger.debug(`[verifyAuthToken] Token contains username: ${credentials.username ? "\u2713" : "\u2717"}`);
1689
+ logger.debug("[verifyAuthToken] Validating credentials from token");
1690
+ const result = validateUser(credentials);
1691
+ if (result.success) {
1692
+ logger.info(`[verifyAuthToken] \u2713 Token verified successfully for user: ${credentials.username || "unknown"}`);
1693
+ } else {
1694
+ logger.warn(`[verifyAuthToken] Token verification failed: ${result.error}`);
1695
+ }
1696
+ return result;
1576
1697
  } catch (error) {
1577
- logger.error("Failed to verify auth token:", error);
1698
+ const errorMsg = error instanceof Error ? error.message : String(error);
1699
+ logger.error(`[verifyAuthToken] Failed to verify auth token: ${errorMsg}`);
1700
+ logger.debug("[verifyAuthToken] Token verification error details:", error);
1578
1701
  return {
1579
1702
  success: false,
1580
1703
  error: "Invalid token format"
@@ -1585,36 +1708,49 @@ function verifyAuthToken(authToken) {
1585
1708
  // src/handlers/auth-login-requests.ts
1586
1709
  async function handleAuthLoginRequest(data, sendMessage) {
1587
1710
  try {
1711
+ logger.debug("[AUTH_LOGIN_REQ] Parsing incoming auth login request");
1588
1712
  const authRequest = AuthLoginRequestMessageSchema.parse(data);
1589
1713
  const { id, payload } = authRequest;
1590
1714
  const login_data = payload.login_data;
1591
1715
  const wsId = authRequest.from.id;
1716
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Processing auth login request from client: ${wsId}`);
1717
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data present: ${!!login_data}`);
1592
1718
  if (!login_data) {
1719
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Login data not found in request`);
1593
1720
  sendDataResponse2(id, {
1594
1721
  success: false,
1595
1722
  error: "Login data not found"
1596
1723
  }, sendMessage, wsId);
1597
1724
  return;
1598
1725
  }
1726
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Decoding base64 login data`);
1599
1727
  let loginData;
1600
1728
  try {
1601
1729
  loginData = decodeBase64ToJson(login_data);
1730
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Login data decoded successfully`);
1602
1731
  } catch (error) {
1732
+ const errorMsg = error instanceof Error ? error.message : String(error);
1733
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Failed to decode login data: ${errorMsg}`);
1734
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Decode error details:`, error);
1603
1735
  sendDataResponse2(id, {
1604
1736
  success: false,
1605
1737
  error: "Invalid login data format"
1606
1738
  }, sendMessage, wsId);
1607
1739
  return;
1608
1740
  }
1609
- const { username, password } = loginData;
1610
- if (!username) {
1741
+ const { username, email, password } = loginData;
1742
+ const identifier = username || email;
1743
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Validating credentials - username: ${username ? "\u2713" : "\u2717"}, email: ${email ? "\u2713" : "\u2717"}, password: ${password ? "\u2713" : "\u2717"}`);
1744
+ if (!identifier) {
1745
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Username or email not found in login data`);
1611
1746
  sendDataResponse2(id, {
1612
1747
  success: false,
1613
- error: "Username not found in login data"
1748
+ error: "Username or email is required"
1614
1749
  }, sendMessage, wsId);
1615
1750
  return;
1616
1751
  }
1617
1752
  if (!password) {
1753
+ logger.error(`[AUTH_LOGIN_REQ ${id}] Password not found in login data`);
1618
1754
  sendDataResponse2(id, {
1619
1755
  success: false,
1620
1756
  error: "Password not found in login data"
@@ -1622,20 +1758,46 @@ async function handleAuthLoginRequest(data, sendMessage) {
1622
1758
  return;
1623
1759
  }
1624
1760
  if (!wsId) {
1761
+ logger.error(`[AUTH_LOGIN_REQ ${id}] WebSocket ID not found in request`);
1625
1762
  sendDataResponse2(id, {
1626
1763
  success: false,
1627
1764
  error: "WebSocket ID not found"
1628
1765
  }, sendMessage, wsId);
1629
1766
  return;
1630
1767
  }
1768
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Credentials validated, authenticating user: ${identifier}`);
1769
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] WebSocket ID: ${wsId}`);
1770
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Calling authenticateAndStoreWsId for user: ${identifier}`);
1631
1771
  const authResult = authenticateAndStoreWsId(
1632
- { username, password },
1772
+ { username, email, password },
1633
1773
  wsId
1634
1774
  );
1775
+ logger.info(`[AUTH_LOGIN_REQ ${id}] Authentication result for ${identifier}: ${authResult.success ? "success" : "failed"}`);
1776
+ if (!authResult.success) {
1777
+ logger.warn(`[AUTH_LOGIN_REQ ${id}] Authentication failed for ${identifier}: ${authResult.error}`);
1778
+ } else {
1779
+ logger.info(`[AUTH_LOGIN_REQ ${id}] User ${authResult.username || identifier} authenticated successfully`);
1780
+ }
1781
+ logger.debug(`[AUTH_LOGIN_REQ ${id}] Sending auth response to client`);
1635
1782
  sendDataResponse2(id, authResult, sendMessage, wsId);
1783
+ logger.info(`[AUTH_LOGIN_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth login request completed`);
1636
1784
  return;
1637
1785
  } catch (error) {
1638
- logger.error("Failed to handle auth login request:", error);
1786
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
1787
+ const errorStack = error instanceof Error ? error.stack : void 0;
1788
+ logger.error(`[AUTH_LOGIN_REQ] Failed to handle auth login request: ${errorMessage}`);
1789
+ logger.debug(`[AUTH_LOGIN_REQ] Error stack trace:`, errorStack);
1790
+ try {
1791
+ const parsedData = data;
1792
+ if (parsedData?.id) {
1793
+ sendDataResponse2(parsedData.id, {
1794
+ success: false,
1795
+ error: `Internal error: ${errorMessage}`
1796
+ }, sendMessage, parsedData.from?.id);
1797
+ }
1798
+ } catch (sendError) {
1799
+ logger.error("[AUTH_LOGIN_REQ] Failed to send error response:", sendError);
1800
+ }
1639
1801
  }
1640
1802
  }
1641
1803
  function sendDataResponse2(id, res, sendMessage, clientId) {
@@ -1651,17 +1813,27 @@ function sendDataResponse2(id, res, sendMessage, clientId) {
1651
1813
  ...res
1652
1814
  }
1653
1815
  };
1816
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Sending ${res.success ? "successful" : "failed"} auth response to client: ${clientId}`);
1817
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
1818
+ if (res.error) {
1819
+ logger.debug(`[AUTH_LOGIN_RES ${id}] Error message: ${res.error}`);
1820
+ }
1654
1821
  sendMessage(response);
1655
1822
  }
1656
1823
 
1657
1824
  // src/handlers/auth-verify-request.ts
1658
1825
  async function handleAuthVerifyRequest(data, sendMessage) {
1659
1826
  try {
1827
+ logger.debug("[AUTH_VERIFY_REQ] Parsing incoming auth verify request");
1660
1828
  const authRequest = AuthVerifyRequestMessageSchema.parse(data);
1661
1829
  const { id, payload } = authRequest;
1662
1830
  const token = payload.token;
1663
1831
  const wsId = authRequest.from.id;
1832
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Processing auth verify request from client: ${wsId}`);
1833
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Token present: ${!!token}`);
1834
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Token length: ${token ? token.length : 0} characters`);
1664
1835
  if (!token) {
1836
+ logger.error(`[AUTH_VERIFY_REQ ${id}] Token not found in request`);
1665
1837
  sendDataResponse3(id, {
1666
1838
  success: false,
1667
1839
  error: "Token not found"
@@ -1669,17 +1841,45 @@ async function handleAuthVerifyRequest(data, sendMessage) {
1669
1841
  return;
1670
1842
  }
1671
1843
  if (!wsId) {
1844
+ logger.error(`[AUTH_VERIFY_REQ ${id}] WebSocket ID not found in request`);
1672
1845
  sendDataResponse3(id, {
1673
1846
  success: false,
1674
1847
  error: "WebSocket ID not found"
1675
1848
  }, sendMessage, wsId);
1676
1849
  return;
1677
1850
  }
1851
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token validation starting`);
1852
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] WebSocket ID: ${wsId}`);
1853
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Calling verifyAuthToken`);
1854
+ const startTime = Date.now();
1678
1855
  const authResult = verifyAuthToken(token);
1856
+ const verificationTime = Date.now() - startTime;
1857
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token verification completed in ${verificationTime}ms - ${authResult.success ? "valid" : "invalid"}`);
1858
+ if (!authResult.success) {
1859
+ logger.warn(`[AUTH_VERIFY_REQ ${id}] Token verification failed: ${authResult.error}`);
1860
+ } else {
1861
+ logger.info(`[AUTH_VERIFY_REQ ${id}] Token verified successfully for user: ${authResult.data || "unknown"}`);
1862
+ }
1863
+ logger.debug(`[AUTH_VERIFY_REQ ${id}] Sending verification response to client`);
1679
1864
  sendDataResponse3(id, authResult, sendMessage, wsId);
1865
+ logger.info(`[AUTH_VERIFY_REQ ${id}] ${authResult.success ? "\u2713" : "\u2717"} Auth verify request completed`);
1680
1866
  return;
1681
1867
  } catch (error) {
1682
- logger.error("Failed to handle auth verify request:", error);
1868
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
1869
+ const errorStack = error instanceof Error ? error.stack : void 0;
1870
+ logger.error(`[AUTH_VERIFY_REQ] Failed to handle auth verify request: ${errorMessage}`);
1871
+ logger.debug(`[AUTH_VERIFY_REQ] Error stack trace:`, errorStack);
1872
+ try {
1873
+ const parsedData = data;
1874
+ if (parsedData?.id) {
1875
+ sendDataResponse3(parsedData.id, {
1876
+ success: false,
1877
+ error: `Internal error: ${errorMessage}`
1878
+ }, sendMessage, parsedData.from?.id);
1879
+ }
1880
+ } catch (sendError) {
1881
+ logger.error("[AUTH_VERIFY_REQ] Failed to send error response:", sendError);
1882
+ }
1683
1883
  }
1684
1884
  }
1685
1885
  function sendDataResponse3(id, res, sendMessage, clientId) {
@@ -1695,6 +1895,14 @@ function sendDataResponse3(id, res, sendMessage, clientId) {
1695
1895
  ...res
1696
1896
  }
1697
1897
  };
1898
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Sending ${res.success ? "successful" : "failed"} verification response to client: ${clientId}`);
1899
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Response payload size: ${JSON.stringify(response).length} bytes`);
1900
+ if (res.error) {
1901
+ logger.debug(`[AUTH_VERIFY_RES ${id}] Error message: ${res.error}`);
1902
+ }
1903
+ if (res.data) {
1904
+ logger.debug(`[AUTH_VERIFY_RES ${id}] User verified: ${res.data}`);
1905
+ }
1698
1906
  sendMessage(response);
1699
1907
  }
1700
1908
 
@@ -1744,6 +1952,47 @@ function ensureQueryLimit(query, defaultLimit = 50) {
1744
1952
  }
1745
1953
  return trimmedQuery;
1746
1954
  }
1955
+ function fixScalarSubqueries(query) {
1956
+ if (!query || query.trim().length === 0) {
1957
+ return query;
1958
+ }
1959
+ let modifiedQuery = query;
1960
+ let hasChanges = false;
1961
+ const scalarOperatorPattern = /([=<>!]=?|<>)\s*\(\s*SELECT\s/gi;
1962
+ const matches = [...modifiedQuery.matchAll(scalarOperatorPattern)];
1963
+ for (let i = matches.length - 1; i >= 0; i--) {
1964
+ const match = matches[i];
1965
+ const startPos = match.index + match[0].length - "SELECT ".length;
1966
+ let parenDepth = 1;
1967
+ let endPos = startPos;
1968
+ let foundEnd = false;
1969
+ for (let j = startPos; j < modifiedQuery.length; j++) {
1970
+ const char = modifiedQuery[j];
1971
+ if (char === "(") parenDepth++;
1972
+ if (char === ")") {
1973
+ parenDepth--;
1974
+ if (parenDepth === 0) {
1975
+ endPos = j;
1976
+ foundEnd = true;
1977
+ break;
1978
+ }
1979
+ }
1980
+ }
1981
+ if (!foundEnd) continue;
1982
+ const subquery = modifiedQuery.substring(startPos, endPos);
1983
+ if (/\bLIMIT\s+\d+/i.test(subquery)) {
1984
+ continue;
1985
+ }
1986
+ const fixedSubquery = subquery.trim() + " LIMIT 1";
1987
+ modifiedQuery = modifiedQuery.substring(0, startPos) + fixedSubquery + modifiedQuery.substring(endPos);
1988
+ hasChanges = true;
1989
+ console.warn(`\u26A0\uFE0F Fixed scalar subquery: added LIMIT 1 to prevent multiple row error`);
1990
+ }
1991
+ if (hasChanges) {
1992
+ console.log("\u2713 Query validated and fixed for PostgreSQL scalar subquery compatibility");
1993
+ }
1994
+ return modifiedQuery;
1995
+ }
1747
1996
 
1748
1997
  // src/userResponse/schema.ts
1749
1998
  var import_path = __toESM(require("path"));
@@ -1906,7 +2155,8 @@ var PromptLoader = class {
1906
2155
  "single-component",
1907
2156
  "mutli-component",
1908
2157
  "actions",
1909
- "container-metadata"
2158
+ "container-metadata",
2159
+ "text-response"
1910
2160
  ];
1911
2161
  for (const promptType of promptTypes) {
1912
2162
  try {
@@ -2258,7 +2508,9 @@ var BaseLLM = class {
2258
2508
  needsMultipleComponents: result.needsMultipleComponents || false
2259
2509
  };
2260
2510
  } catch (error) {
2261
- console.error("Error classifying user question:", error);
2511
+ const errorMsg = error instanceof Error ? error.message : String(error);
2512
+ logger.error(`[${this.getProviderName()}] Error classifying user question: ${errorMsg}`);
2513
+ logger.debug(`[${this.getProviderName()}] Classification error details:`, error);
2262
2514
  throw error;
2263
2515
  }
2264
2516
  }
@@ -2296,6 +2548,7 @@ var BaseLLM = class {
2296
2548
  );
2297
2549
  const props = result.props || originalProps;
2298
2550
  if (props && props.query) {
2551
+ props.query = fixScalarSubqueries(props.query);
2299
2552
  props.query = ensureQueryLimit(props.query, this.defaultLimit);
2300
2553
  }
2301
2554
  if (props && props.query) {
@@ -2322,7 +2575,9 @@ var BaseLLM = class {
2322
2575
  modifications: result.modifications || []
2323
2576
  };
2324
2577
  } catch (error) {
2325
- console.error(`Error validating/modifying props with ${this.getProviderName()}:`, error);
2578
+ const errorMsg = error instanceof Error ? error.message : String(error);
2579
+ logger.error(`[${this.getProviderName()}] Error validating/modifying props: ${errorMsg}`);
2580
+ logger.debug(`[${this.getProviderName()}] Props validation error details:`, error);
2326
2581
  throw error;
2327
2582
  }
2328
2583
  }
@@ -2442,7 +2697,9 @@ var BaseLLM = class {
2442
2697
  isGenerated: true
2443
2698
  };
2444
2699
  } catch (error) {
2445
- console.error("Error generating analytical component:", error);
2700
+ const errorMsg = error instanceof Error ? error.message : String(error);
2701
+ logger.error(`[${this.getProviderName()}] Error generating analytical component: ${errorMsg}`);
2702
+ logger.debug(`[${this.getProviderName()}] Analytical component generation error details:`, error);
2446
2703
  throw error;
2447
2704
  }
2448
2705
  }
@@ -2484,7 +2741,9 @@ var BaseLLM = class {
2484
2741
  description: result.description || `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
2485
2742
  };
2486
2743
  } catch (error) {
2487
- console.error("Error generating container metadata:", error);
2744
+ const errorMsg = error instanceof Error ? error.message : String(error);
2745
+ logger.error(`[${this.getProviderName()}] Error generating container metadata: ${errorMsg}`);
2746
+ logger.debug(`[${this.getProviderName()}] Container metadata error details:`, error);
2488
2747
  return {
2489
2748
  title: `${userPrompt} - Dashboard`,
2490
2749
  description: `Multi-component dashboard showing ${visualizationTypes.join(", ")}`
@@ -2536,24 +2795,24 @@ var BaseLLM = class {
2536
2795
  component = components[componentIndex - 1];
2537
2796
  }
2538
2797
  const matchedMsg = `${this.getProviderName()} matched component: ${component?.name || "None"}`;
2539
- console.log("\u2713", matchedMsg);
2798
+ logger.info(`[${this.getProviderName()}] \u2713 ${matchedMsg}`);
2540
2799
  logCollector?.info(matchedMsg);
2541
2800
  if (result.alternativeMatches && result.alternativeMatches.length > 0) {
2542
- console.log(" Alternative matches:");
2801
+ logger.debug(`[${this.getProviderName()}] Alternative matches found: ${result.alternativeMatches.length}`);
2543
2802
  const altMatches = result.alternativeMatches.map(
2544
2803
  (alt) => `${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`
2545
2804
  ).join(" | ");
2546
2805
  logCollector?.info(`Alternative matches: ${altMatches}`);
2547
2806
  result.alternativeMatches.forEach((alt) => {
2548
- console.log(` - ${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`);
2807
+ logger.debug(`[${this.getProviderName()}] - ${components[alt.index - 1]?.name} (${alt.score}%): ${alt.reason}`);
2549
2808
  });
2550
2809
  }
2551
2810
  if (!component) {
2552
2811
  const noMatchMsg = `No matching component found (confidence: ${confidence}%)`;
2553
- console.log("\u2717", noMatchMsg);
2812
+ logger.warn(`[${this.getProviderName()}] \u2717 ${noMatchMsg}`);
2554
2813
  logCollector?.warn(noMatchMsg);
2555
2814
  const genMsg = "Attempting to match component from analytical question...";
2556
- console.log("\u2713", genMsg);
2815
+ logger.info(`[${this.getProviderName()}] \u2713 ${genMsg}`);
2557
2816
  logCollector?.info(genMsg);
2558
2817
  const generatedResult = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2559
2818
  if (generatedResult.component) {
@@ -2614,8 +2873,10 @@ var BaseLLM = class {
2614
2873
  confidence
2615
2874
  };
2616
2875
  } catch (error) {
2617
- console.error(`Error matching component with ${this.getProviderName()}:`, error);
2618
- logCollector?.error(`Error matching component: ${error.message}`);
2876
+ const errorMsg = error instanceof Error ? error.message : String(error);
2877
+ logger.error(`[${this.getProviderName()}] Error matching component: ${errorMsg}`);
2878
+ logger.debug(`[${this.getProviderName()}] Component matching error details:`, error);
2879
+ logCollector?.error(`Error matching component: ${errorMsg}`);
2619
2880
  throw error;
2620
2881
  }
2621
2882
  }
@@ -2646,7 +2907,9 @@ var BaseLLM = class {
2646
2907
  isGenerated: true
2647
2908
  };
2648
2909
  } catch (error) {
2649
- console.error("Error matching multiple analytical components:", error);
2910
+ const errorMsg = error instanceof Error ? error.message : String(error);
2911
+ logger.error(`[${this.getProviderName()}] Error matching multiple analytical components: ${errorMsg}`);
2912
+ logger.debug(`[${this.getProviderName()}] Multiple components matching error details:`, error);
2650
2913
  return {
2651
2914
  components: [],
2652
2915
  reasoning: "Error occurred while matching components",
@@ -2725,17 +2988,91 @@ var BaseLLM = class {
2725
2988
  isGenerated: true
2726
2989
  };
2727
2990
  } catch (error) {
2728
- console.error("Error generating multi-component response:", error);
2991
+ const errorMsg = error instanceof Error ? error.message : String(error);
2992
+ logger.error(`[${this.getProviderName()}] Error generating multi-component response: ${errorMsg}`);
2993
+ logger.debug(`[${this.getProviderName()}] Multi-component response error details:`, error);
2729
2994
  throw error;
2730
2995
  }
2731
2996
  }
2732
2997
  /**
2733
- * Main orchestration function that classifies question and routes to appropriate handler
2734
- * This is the NEW recommended entry point for handling user requests
2735
- * ALWAYS returns a SINGLE component (wraps multiple in MultiComponentContainer)
2998
+ * Generate text-based response for user question
2999
+ * This provides conversational text responses instead of component generation
3000
+ */
3001
+ async generateTextResponse(userPrompt, apiKey, logCollector, conversationHistory) {
3002
+ const errors = [];
3003
+ logger.debug(`[${this.getProviderName()}] Starting text response generation`);
3004
+ logger.debug(`[${this.getProviderName()}] User prompt: "${userPrompt.substring(0, 50)}..."`);
3005
+ try {
3006
+ const prompts = await promptLoader.loadPrompts("text-response", {
3007
+ USER_PROMPT: userPrompt,
3008
+ CONVERSATION_HISTORY: conversationHistory || "No previous conversation"
3009
+ });
3010
+ logger.debug(`[${this.getProviderName()}] Loaded text-response prompts`);
3011
+ logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
3012
+ logCollector?.info("Generating text response...");
3013
+ const result = await LLM.stream(
3014
+ {
3015
+ sys: prompts.system,
3016
+ user: prompts.user
3017
+ },
3018
+ {
3019
+ model: this.model,
3020
+ maxTokens: 1e3,
3021
+ temperature: 0.7,
3022
+ apiKey: this.getApiKey(apiKey)
3023
+ },
3024
+ true
3025
+ // Parse as JSON
3026
+ );
3027
+ logger.info(`[${this.getProviderName()}] Text response generated successfully`);
3028
+ logger.debug(`[${this.getProviderName()}] Response type: ${result.responseType}, Confidence: ${result.confidence}`);
3029
+ logCollector?.info(`Text response: ${result.text?.substring(0, 100)}${result.text?.length > 100 ? "..." : ""}`);
3030
+ logCollector?.logExplanation(
3031
+ "Text response generated",
3032
+ result.reasoning || "Generated text response for user question",
3033
+ {
3034
+ responseType: result.responseType,
3035
+ confidence: result.confidence,
3036
+ textLength: result.text?.length || 0
3037
+ }
3038
+ );
3039
+ return {
3040
+ success: true,
3041
+ data: {
3042
+ text: result.text || "I apologize, but I was unable to generate a response.",
3043
+ responseType: result.responseType || "answer",
3044
+ confidence: result.confidence || 0.5,
3045
+ reasoning: result.reasoning || "No reasoning provided"
3046
+ },
3047
+ errors: []
3048
+ };
3049
+ } catch (error) {
3050
+ const errorMsg = error instanceof Error ? error.message : String(error);
3051
+ logger.error(`[${this.getProviderName()}] Error generating text response: ${errorMsg}`);
3052
+ logger.debug(`[${this.getProviderName()}] Text response generation error details:`, error);
3053
+ logCollector?.error(`Error generating text response: ${errorMsg}`);
3054
+ errors.push(errorMsg);
3055
+ return {
3056
+ success: false,
3057
+ errors,
3058
+ data: {
3059
+ text: "I apologize, but I encountered an error while processing your question. Please try rephrasing or ask something else.",
3060
+ responseType: "error",
3061
+ confidence: 0,
3062
+ reasoning: `Error: ${errorMsg}`
3063
+ }
3064
+ };
3065
+ }
3066
+ }
3067
+ /**
3068
+ * Generate component response for user question
3069
+ * This provides conversational component suggestions based on user question
3070
+ * Supports component generation and matching
2736
3071
  */
2737
- async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory) {
3072
+ async generateComponentResponse(userPrompt, components, apiKey, logCollector, conversationHistory) {
3073
+ const errors = [];
2738
3074
  try {
3075
+ logger.info(`[${this.getProviderName()}] Using component response mode`);
2739
3076
  const classifyMsg = "Classifying user question...";
2740
3077
  logCollector?.info(classifyMsg);
2741
3078
  const classification = await this.classifyUserQuestion(userPrompt, apiKey, logCollector, conversationHistory);
@@ -2745,33 +3082,47 @@ var BaseLLM = class {
2745
3082
  if (classification.visualizations.length > 1) {
2746
3083
  const multiMsg = `Matching ${classification.visualizations.length} components for types: ${classification.visualizations.join(", ")}`;
2747
3084
  logCollector?.info(multiMsg);
2748
- const matchedComponents = [];
2749
- for (const vizType of classification.visualizations) {
3085
+ const componentPromises = classification.visualizations.map((vizType) => {
2750
3086
  logCollector?.info(`Matching component for type: ${vizType}`);
2751
- const result = await this.generateAnalyticalComponent(
3087
+ return this.generateAnalyticalComponent(
2752
3088
  userPrompt,
2753
3089
  components,
2754
3090
  vizType,
2755
3091
  apiKey,
2756
3092
  logCollector,
2757
3093
  conversationHistory
2758
- );
2759
- if (result.component) {
2760
- matchedComponents.push(result.component);
2761
- logCollector?.info(`Matched: ${result.component.name}`);
3094
+ ).then((result) => ({ vizType, result }));
3095
+ });
3096
+ const settledResults = await Promise.allSettled(componentPromises);
3097
+ const matchedComponents = [];
3098
+ for (const settledResult of settledResults) {
3099
+ if (settledResult.status === "fulfilled") {
3100
+ const { vizType, result } = settledResult.value;
3101
+ if (result.component) {
3102
+ matchedComponents.push(result.component);
3103
+ logCollector?.info(`Matched: ${result.component.name}`);
3104
+ logger.info("Component : ", result.component.name, " props: ", result.component.props);
3105
+ } else {
3106
+ logCollector?.warn(`Failed to match component for type: ${vizType}`);
3107
+ }
2762
3108
  } else {
2763
- logCollector?.warn(`Failed to match component for type: ${vizType}`);
3109
+ logCollector?.warn(`Error matching component: ${settledResult.reason?.message || "Unknown error"}`);
2764
3110
  }
2765
3111
  }
3112
+ logger.debug(`[${this.getProviderName()}] Matched ${matchedComponents.length} components for multi-component container`);
2766
3113
  if (matchedComponents.length === 0) {
2767
3114
  return {
2768
- component: null,
2769
- reasoning: "Failed to match any components for the requested visualization types",
2770
- method: "classification-multi-failed",
2771
- questionType: classification.questionType,
2772
- needsMultipleComponents: true,
2773
- propsModified: false,
2774
- queryModified: false
3115
+ success: true,
3116
+ data: {
3117
+ component: null,
3118
+ reasoning: "Failed to match any components for the requested visualization types",
3119
+ method: "classification-multi-failed",
3120
+ questionType: classification.questionType,
3121
+ needsMultipleComponents: true,
3122
+ propsModified: false,
3123
+ queryModified: false
3124
+ },
3125
+ errors: []
2775
3126
  };
2776
3127
  }
2777
3128
  logCollector?.info("Generating container metadata...");
@@ -2801,38 +3152,50 @@ var BaseLLM = class {
2801
3152
  };
2802
3153
  logCollector?.info(`Created multi-component container with ${matchedComponents.length} components: "${containerMetadata.title}"`);
2803
3154
  return {
2804
- component: containerComponent,
2805
- reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
2806
- method: "classification-multi-generated",
2807
- questionType: classification.questionType,
2808
- needsMultipleComponents: true,
2809
- propsModified: false,
2810
- queryModified: false
3155
+ success: true,
3156
+ data: {
3157
+ component: containerComponent,
3158
+ reasoning: `Matched ${matchedComponents.length} components for visualization types: ${classification.visualizations.join(", ")}`,
3159
+ method: "classification-multi-generated",
3160
+ questionType: classification.questionType,
3161
+ needsMultipleComponents: true,
3162
+ propsModified: false,
3163
+ queryModified: false
3164
+ },
3165
+ errors: []
2811
3166
  };
2812
3167
  } else if (classification.visualizations.length === 1) {
2813
3168
  const vizType = classification.visualizations[0];
2814
3169
  logCollector?.info(`Matching single component for type: ${vizType}`);
2815
3170
  const result = await this.generateAnalyticalComponent(userPrompt, components, vizType, apiKey, logCollector, conversationHistory);
2816
3171
  return {
2817
- component: result.component,
2818
- reasoning: result.reasoning,
2819
- method: "classification-generated",
2820
- questionType: classification.questionType,
2821
- needsMultipleComponents: false,
2822
- propsModified: false,
2823
- queryModified: false
3172
+ success: true,
3173
+ data: {
3174
+ component: result.component,
3175
+ reasoning: result.reasoning,
3176
+ method: "classification-generated",
3177
+ questionType: classification.questionType,
3178
+ needsMultipleComponents: false,
3179
+ propsModified: false,
3180
+ queryModified: false
3181
+ },
3182
+ errors: []
2824
3183
  };
2825
3184
  } else {
2826
3185
  logCollector?.info("No specific visualization type - matching from all components");
2827
3186
  const result = await this.generateAnalyticalComponent(userPrompt, components, void 0, apiKey, logCollector, conversationHistory);
2828
3187
  return {
2829
- component: result.component,
2830
- reasoning: result.reasoning,
2831
- method: "classification-generated-auto",
2832
- questionType: classification.questionType,
2833
- needsMultipleComponents: false,
2834
- propsModified: false,
2835
- queryModified: false
3188
+ success: true,
3189
+ data: {
3190
+ component: result.component,
3191
+ reasoning: result.reasoning,
3192
+ method: "classification-generated-auto",
3193
+ questionType: classification.questionType,
3194
+ needsMultipleComponents: false,
3195
+ propsModified: false,
3196
+ queryModified: false
3197
+ },
3198
+ errors: []
2836
3199
  };
2837
3200
  }
2838
3201
  } else if (classification.questionType === "data_modification" || classification.questionType === "general") {
@@ -2840,28 +3203,109 @@ var BaseLLM = class {
2840
3203
  logCollector?.info(matchMsg);
2841
3204
  const matchResult = await this.matchComponent(userPrompt, components, apiKey, logCollector, conversationHistory);
2842
3205
  return {
2843
- component: matchResult.component,
2844
- reasoning: matchResult.reasoning,
2845
- method: "classification-matched",
2846
- questionType: classification.questionType,
2847
- needsMultipleComponents: false,
2848
- propsModified: matchResult.propsModified,
2849
- queryModified: matchResult.queryModified
3206
+ success: true,
3207
+ data: {
3208
+ component: matchResult.component,
3209
+ reasoning: matchResult.reasoning,
3210
+ method: "classification-matched",
3211
+ questionType: classification.questionType,
3212
+ needsMultipleComponents: false,
3213
+ propsModified: matchResult.propsModified,
3214
+ queryModified: matchResult.queryModified
3215
+ },
3216
+ errors: []
2850
3217
  };
2851
3218
  } else {
2852
3219
  logCollector?.info("General question - no component needed");
2853
3220
  return {
2854
- component: null,
2855
- reasoning: "General question - no component needed",
2856
- method: "classification-general",
2857
- questionType: classification.questionType,
2858
- needsMultipleComponents: false
3221
+ success: true,
3222
+ data: {
3223
+ component: null,
3224
+ reasoning: "General question - no component needed",
3225
+ method: "classification-general",
3226
+ questionType: classification.questionType,
3227
+ needsMultipleComponents: false,
3228
+ propsModified: false,
3229
+ queryModified: false
3230
+ },
3231
+ errors: []
2859
3232
  };
2860
3233
  }
2861
3234
  } catch (error) {
2862
- logCollector?.error(`Error handling user request: ${error.message}`);
2863
- throw error;
3235
+ const errorMsg = error instanceof Error ? error.message : String(error);
3236
+ logger.error(`[${this.getProviderName()}] Error generating component response: ${errorMsg}`);
3237
+ logger.debug(`[${this.getProviderName()}] Component response generation error details:`, error);
3238
+ logCollector?.error(`Error generating component response: ${errorMsg}`);
3239
+ errors.push(errorMsg);
3240
+ return {
3241
+ success: false,
3242
+ errors,
3243
+ data: void 0
3244
+ };
3245
+ }
3246
+ }
3247
+ /**
3248
+ * Main orchestration function that classifies question and routes to appropriate handler
3249
+ * This is the NEW recommended entry point for handling user requests
3250
+ * Supports both component generation and text response modes
3251
+ *
3252
+ * @param responseMode - 'component' for component generation (default), 'text' for text responses
3253
+ */
3254
+ async handleUserRequest(userPrompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") {
3255
+ logger.info(`[${this.getProviderName()}] handleUserRequest called with responseMode: ${responseMode}`);
3256
+ if (responseMode === "text") {
3257
+ logger.info(`[${this.getProviderName()}] Using text response mode`);
3258
+ logCollector?.info("Generating text response...");
3259
+ const textResponse = await this.generateTextResponse(
3260
+ userPrompt,
3261
+ apiKey,
3262
+ logCollector,
3263
+ conversationHistory
3264
+ );
3265
+ if (!textResponse.success) {
3266
+ logger.error(`[${this.getProviderName()}] Text response generation failed`);
3267
+ return textResponse;
3268
+ }
3269
+ logger.info(`[${this.getProviderName()}] Text response generated successfully`);
3270
+ return {
3271
+ success: true,
3272
+ data: {
3273
+ textResponse: textResponse.data.text,
3274
+ text: textResponse.data.text,
3275
+ responseType: textResponse.data.responseType,
3276
+ confidence: textResponse.data.confidence,
3277
+ reasoning: textResponse.data.reasoning,
3278
+ method: `${this.getProviderName()}-text-response`
3279
+ },
3280
+ errors: []
3281
+ };
2864
3282
  }
3283
+ const componentResponse = await this.generateComponentResponse(
3284
+ userPrompt,
3285
+ components,
3286
+ apiKey,
3287
+ logCollector,
3288
+ conversationHistory
3289
+ );
3290
+ if (!componentResponse.success) {
3291
+ logger.error(`[${this.getProviderName()}] Component response generation failed`);
3292
+ return componentResponse;
3293
+ }
3294
+ logger.info(`[${this.getProviderName()}] Component response generated successfully`);
3295
+ return {
3296
+ success: true,
3297
+ data: {
3298
+ component: componentResponse.data.component,
3299
+ reasoning: componentResponse.data.reasoning,
3300
+ method: componentResponse.data.method,
3301
+ // Preserve the original method name
3302
+ questionType: componentResponse.data.questionType,
3303
+ needsMultipleComponents: componentResponse.data.needsMultipleComponents,
3304
+ propsModified: componentResponse.data.propsModified,
3305
+ queryModified: componentResponse.data.queryModified
3306
+ },
3307
+ errors: []
3308
+ };
2865
3309
  }
2866
3310
  /**
2867
3311
  * Generate next questions that the user might ask based on the original prompt and generated component
@@ -2907,8 +3351,10 @@ var BaseLLM = class {
2907
3351
  );
2908
3352
  return nextQuestions;
2909
3353
  } catch (error) {
2910
- console.error(`Error generating next questions with ${this.getProviderName()}:`, error);
2911
- logCollector?.error(`Error generating next questions: ${error.message}`);
3354
+ const errorMsg = error instanceof Error ? error.message : String(error);
3355
+ logger.error(`[${this.getProviderName()}] Error generating next questions: ${errorMsg}`);
3356
+ logger.debug(`[${this.getProviderName()}] Next questions generation error details:`, error);
3357
+ logCollector?.error(`Error generating next questions: ${errorMsg}`);
2912
3358
  return [];
2913
3359
  }
2914
3360
  }
@@ -2972,112 +3418,130 @@ function getLLMProviders() {
2972
3418
  return DEFAULT_PROVIDERS;
2973
3419
  }
2974
3420
  }
2975
- var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
2976
- const msg = "Using Anthropic Claude matching method...";
2977
- console.log(msg);
3421
+ var useAnthropicMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
3422
+ logger.debug("[useAnthropicMethod] Initializing Anthropic Claude matching method");
3423
+ logger.debug(`[useAnthropicMethod] Response mode: ${responseMode}`);
3424
+ const msg = `Using Anthropic Claude ${responseMode === "text" ? "text response" : "matching"} method...`;
2978
3425
  logCollector?.info(msg);
2979
- if (components.length === 0) {
3426
+ if (responseMode === "component" && components.length === 0) {
2980
3427
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3428
+ logger.error("[useAnthropicMethod] No components available");
2981
3429
  logCollector?.error(emptyMsg);
2982
- return { success: false, reason: emptyMsg };
2983
- }
2984
- try {
2985
- const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
2986
- return { success: true, data: matchResult };
2987
- } catch (error) {
2988
- const errorMsg = error instanceof Error ? error.message : String(error);
2989
- logCollector?.error(`Anthropic method failed: ${errorMsg}`);
2990
- throw error;
3430
+ return { success: false, errors: [emptyMsg] };
2991
3431
  }
3432
+ logger.debug(`[useAnthropicMethod] Processing with ${components.length} components`);
3433
+ const matchResult = await anthropicLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
3434
+ logger.info(`[useAnthropicMethod] Successfully generated ${responseMode} using Anthropic`);
3435
+ return matchResult;
2992
3436
  };
2993
- var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory) => {
2994
- const msg = "Using Groq LLM matching method...";
2995
- console.log(msg);
3437
+ var useGroqMethod = async (prompt, components, apiKey, logCollector, conversationHistory, responseMode = "component") => {
3438
+ logger.debug("[useGroqMethod] Initializing Groq LLM matching method");
3439
+ logger.debug(`[useGroqMethod] Response mode: ${responseMode}`);
3440
+ const msg = `Using Groq LLM ${responseMode === "text" ? "text response" : "matching"} method...`;
3441
+ logger.info(msg);
2996
3442
  logCollector?.info(msg);
2997
- if (components.length === 0) {
3443
+ if (responseMode === "component" && components.length === 0) {
2998
3444
  const emptyMsg = "Components not loaded in memory. Please ensure components are fetched first.";
3445
+ logger.error("[useGroqMethod] No components available");
2999
3446
  logCollector?.error(emptyMsg);
3000
- return { success: false, reason: emptyMsg };
3001
- }
3002
- try {
3003
- const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory);
3004
- return { success: true, data: matchResult };
3005
- } catch (error) {
3006
- const errorMsg = error instanceof Error ? error.message : String(error);
3007
- logCollector?.error(`Groq method failed: ${errorMsg}`);
3008
- throw error;
3447
+ return { success: false, errors: [emptyMsg] };
3009
3448
  }
3449
+ logger.debug(`[useGroqMethod] Processing with ${components.length} components`);
3450
+ const matchResult = await groqLLM.handleUserRequest(prompt, components, apiKey, logCollector, conversationHistory, responseMode);
3451
+ logger.info(`[useGroqMethod] Successfully generated ${responseMode} using Groq`);
3452
+ return matchResult;
3010
3453
  };
3011
3454
  var getUserResponseFromCache = async (prompt) => {
3012
3455
  return false;
3013
3456
  };
3014
3457
  var get_user_response = async (prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) => {
3458
+ const responseMode = "component";
3459
+ logger.debug(`[get_user_response] Starting user response generation for prompt: "${prompt.substring(0, 50)}..."`);
3460
+ logger.debug("[get_user_response] Checking cache for existing response");
3015
3461
  const userResponse = await getUserResponseFromCache(prompt);
3016
3462
  if (userResponse) {
3463
+ logger.info("[get_user_response] User response found in cache - returning cached result");
3017
3464
  logCollector?.info("User response found in cache");
3018
3465
  return {
3019
3466
  success: true,
3020
- data: userResponse
3467
+ data: userResponse,
3468
+ errors: []
3021
3469
  };
3022
3470
  }
3471
+ logger.debug("[get_user_response] No cached response found, proceeding with LLM providers");
3023
3472
  const providers = llmProviders || getLLMProviders();
3024
3473
  const errors = [];
3025
3474
  const providerOrder = providers.join(", ");
3026
3475
  logCollector?.info(`LLM Provider order: [${providerOrder}]`);
3027
3476
  if (conversationHistory && conversationHistory.length > 0) {
3028
- logCollector?.info(`Using conversation history with ${conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length} previous exchanges`);
3477
+ const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
3478
+ logger.debug(`[get_user_response] Using conversation history with ${exchangeCount} previous exchanges`);
3479
+ logCollector?.info(`Using conversation history with ${exchangeCount} previous exchanges`);
3480
+ } else {
3481
+ logger.debug("[get_user_response] No conversation history available");
3029
3482
  }
3030
3483
  for (let i = 0; i < providers.length; i++) {
3031
3484
  const provider = providers[i];
3032
3485
  const isLastProvider = i === providers.length - 1;
3033
- try {
3034
- const attemptMsg = `Attempting provider: ${provider} (${i + 1}/${providers.length})`;
3035
- logCollector?.info(attemptMsg);
3036
- let result;
3037
- if (provider === "anthropic") {
3038
- result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory);
3039
- } else if (provider === "groq") {
3040
- result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory);
3041
- } else {
3042
- continue;
3043
- }
3044
- if (result.success) {
3045
- const successMsg = `Success with provider: ${provider}`;
3046
- logCollector?.info(successMsg);
3047
- return result;
3048
- } else {
3049
- errors.push({ provider, error: result.reason || "Unknown error" });
3050
- const warnMsg = `Provider ${provider} returned unsuccessful result: ${result.reason}`;
3051
- logCollector?.warn(warnMsg);
3052
- }
3053
- } catch (error) {
3054
- const errorMessage = error.message;
3055
- errors.push({ provider, error: errorMessage });
3056
- const errorMsg = `Provider ${provider} failed: ${errorMessage}`;
3057
- logCollector?.error(errorMsg);
3486
+ const attemptMsg = `Attempting provider: ${provider} (${i + 1}/${providers.length})`;
3487
+ logCollector?.info(attemptMsg);
3488
+ let result;
3489
+ if (provider === "anthropic") {
3490
+ result = await useAnthropicMethod(prompt, components, anthropicApiKey, logCollector, conversationHistory, responseMode);
3491
+ } else if (provider === "groq") {
3492
+ result = await useGroqMethod(prompt, components, groqApiKey, logCollector, conversationHistory, responseMode);
3493
+ } else {
3494
+ logger.warn(`[get_user_response] Unknown provider: ${provider} - skipping`);
3495
+ errors.push(`Unknown provider: ${provider}`);
3496
+ continue;
3497
+ }
3498
+ if (result.success) {
3499
+ const successMsg = `Success with provider: ${provider} result: ${JSON.stringify(result)}`;
3500
+ logger.info(`${successMsg}`);
3501
+ logCollector?.info(successMsg);
3502
+ return result;
3503
+ } else {
3504
+ const providerErrors = result.errors.map((err) => `${provider}: ${err}`);
3505
+ errors.push(...providerErrors);
3506
+ const warnMsg = `Provider ${provider} returned unsuccessful result: ${result.errors.join(", ")}`;
3507
+ logger.warn(`[get_user_response] ${warnMsg}`);
3508
+ logCollector?.warn(warnMsg);
3058
3509
  if (!isLastProvider) {
3059
3510
  const fallbackMsg = "Falling back to next provider...";
3511
+ logger.info(`[get_user_response] ${fallbackMsg}`);
3060
3512
  logCollector?.info(fallbackMsg);
3061
- continue;
3062
3513
  }
3063
3514
  }
3064
3515
  }
3065
- const errorSummary = errors.map((e) => `${e.provider}: ${e.error}`).join("; ");
3066
- const failureMsg = `All LLM providers failed. Errors: ${errorSummary}`;
3067
- logCollector?.error(failureMsg);
3516
+ const failureMsg = `All LLM providers failed`;
3517
+ logger.error(`[get_user_response] ${failureMsg}. Errors: ${errors.join("; ")}`);
3518
+ logCollector?.error(`${failureMsg}. Errors: ${errors.join("; ")}`);
3068
3519
  return {
3069
3520
  success: false,
3070
- reason: failureMsg
3521
+ errors
3071
3522
  };
3072
3523
  };
3073
3524
 
3074
3525
  // src/utils/log-collector.ts
3526
+ var LOG_LEVEL_PRIORITY2 = {
3527
+ errors: 0,
3528
+ warnings: 1,
3529
+ info: 2,
3530
+ verbose: 3
3531
+ };
3532
+ var MESSAGE_LEVEL_PRIORITY2 = {
3533
+ error: 0,
3534
+ warn: 1,
3535
+ info: 2,
3536
+ debug: 3
3537
+ };
3075
3538
  var UILogCollector = class {
3076
3539
  constructor(clientId, sendMessage, uiBlockId) {
3077
3540
  this.logs = [];
3078
3541
  this.uiBlockId = uiBlockId || null;
3079
3542
  this.clientId = clientId;
3080
3543
  this.sendMessage = sendMessage;
3544
+ this.currentLogLevel = logger.getLogLevel();
3081
3545
  }
3082
3546
  /**
3083
3547
  * Check if logging is enabled (uiBlockId is provided)
@@ -3085,10 +3549,22 @@ var UILogCollector = class {
3085
3549
  isEnabled() {
3086
3550
  return this.uiBlockId !== null;
3087
3551
  }
3552
+ /**
3553
+ * Check if a message should be logged based on current log level
3554
+ */
3555
+ shouldLog(messageLevel) {
3556
+ const currentLevelPriority = LOG_LEVEL_PRIORITY2[this.currentLogLevel];
3557
+ const messagePriority = MESSAGE_LEVEL_PRIORITY2[messageLevel];
3558
+ return messagePriority <= currentLevelPriority;
3559
+ }
3088
3560
  /**
3089
3561
  * Add a log entry with timestamp and immediately send to runtime
3562
+ * Only logs that pass the log level filter are captured and sent
3090
3563
  */
3091
3564
  addLog(level, message, type, data) {
3565
+ if (!this.shouldLog(level)) {
3566
+ return;
3567
+ }
3092
3568
  const log = {
3093
3569
  timestamp: Date.now(),
3094
3570
  level,
@@ -3098,6 +3574,20 @@ var UILogCollector = class {
3098
3574
  };
3099
3575
  this.logs.push(log);
3100
3576
  this.sendLogImmediately(log);
3577
+ switch (level) {
3578
+ case "error":
3579
+ logger.error("UILogCollector:", log);
3580
+ break;
3581
+ case "warn":
3582
+ logger.warn("UILogCollector:", log);
3583
+ break;
3584
+ case "info":
3585
+ logger.info("UILogCollector:", log);
3586
+ break;
3587
+ case "debug":
3588
+ logger.debug("UILogCollector:", log);
3589
+ break;
3590
+ }
3101
3591
  }
3102
3592
  /**
3103
3593
  * Send a single log to runtime immediately
@@ -3228,100 +3718,131 @@ var CONTEXT_CONFIG = {
3228
3718
 
3229
3719
  // src/handlers/user-prompt-request.ts
3230
3720
  var processedMessageIds = /* @__PURE__ */ new Set();
3231
- async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3232
- try {
3233
- const userPromptRequest = UserPromptRequestMessageSchema.parse(data);
3234
- const { id, payload } = userPromptRequest;
3235
- const prompt = payload.prompt;
3236
- const SA_RUNTIME = payload.SA_RUNTIME;
3237
- const wsId = userPromptRequest.from.id || "unknown";
3238
- logger.info(`[REQUEST ${id}] Processing user prompt: "${prompt.substring(0, 50)}..."`);
3239
- if (processedMessageIds.has(id)) {
3240
- logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
3241
- return;
3242
- }
3243
- processedMessageIds.add(id);
3244
- if (processedMessageIds.size > 100) {
3245
- const firstId = processedMessageIds.values().next().value;
3246
- if (firstId) {
3247
- processedMessageIds.delete(firstId);
3721
+ var get_user_request = async (data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) => {
3722
+ const errors = [];
3723
+ logger.debug("[USER_PROMPT_REQ] Parsing incoming message data");
3724
+ const parseResult = UserPromptRequestMessageSchema.safeParse(data);
3725
+ if (!parseResult.success) {
3726
+ const zodError = parseResult.error;
3727
+ zodError.errors.forEach((err) => {
3728
+ errors.push(`${err.path.join(".")}: ${err.message}`);
3729
+ });
3730
+ return { success: false, errors };
3731
+ }
3732
+ const userPromptRequest = parseResult.data;
3733
+ const { id, payload } = userPromptRequest;
3734
+ const prompt = payload.prompt;
3735
+ const SA_RUNTIME = payload.SA_RUNTIME;
3736
+ const wsId = userPromptRequest.from.id || "unknown";
3737
+ logger.debug(`[REQUEST ${id}] Full request details - wsId: ${wsId}, prompt length: ${prompt.length}`);
3738
+ if (processedMessageIds.has(id)) {
3739
+ logger.warn(`[REQUEST ${id}] Duplicate request detected - ignoring`);
3740
+ }
3741
+ processedMessageIds.add(id);
3742
+ logger.debug(`[REQUEST ${id}] Message ID marked as processed (${processedMessageIds.size} total)`);
3743
+ if (processedMessageIds.size > 100) {
3744
+ const firstId = processedMessageIds.values().next().value;
3745
+ if (firstId) {
3746
+ processedMessageIds.delete(firstId);
3747
+ logger.debug(`[REQUEST ${id}] Cleaned up old message ID from cache`);
3748
+ }
3749
+ }
3750
+ if (!SA_RUNTIME) {
3751
+ errors.push("SA_RUNTIME is required");
3752
+ }
3753
+ const threadId = SA_RUNTIME?.threadId;
3754
+ const existingUiBlockId = SA_RUNTIME?.uiBlockId;
3755
+ if (!threadId) {
3756
+ errors.push("threadId in SA_RUNTIME is required");
3757
+ }
3758
+ if (!existingUiBlockId) {
3759
+ errors.push("uiBlockId in SA_RUNTIME is required");
3760
+ }
3761
+ if (!prompt) {
3762
+ errors.push("Prompt not found");
3763
+ }
3764
+ if (!components || components.length === 0) {
3765
+ errors.push("Components not found");
3766
+ }
3767
+ if (errors.length > 0) {
3768
+ return { success: false, errors, id, wsId };
3769
+ }
3770
+ const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
3771
+ const threadManager = ThreadManager.getInstance();
3772
+ let thread = threadManager.getThread(threadId);
3773
+ if (!thread) {
3774
+ thread = threadManager.createThread(threadId);
3775
+ logger.info(`Created new thread: ${threadId}`);
3776
+ }
3777
+ logCollector.info(`Starting user prompt request with ${components.length} components`);
3778
+ const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
3779
+ logger.info("conversationHistory", conversationHistory);
3780
+ const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
3781
+ logger.info("llm userResponse", userResponse);
3782
+ logCollector.info("User prompt request completed");
3783
+ const uiBlockId = existingUiBlockId;
3784
+ if (!userResponse.success) {
3785
+ logger.error(`User prompt request failed with errors: ${userResponse.errors.join(", ")}`);
3786
+ return {
3787
+ success: false,
3788
+ data: userResponse.data,
3789
+ errors: userResponse.errors,
3790
+ uiBlockId,
3791
+ threadId,
3792
+ id,
3793
+ wsId
3794
+ };
3795
+ }
3796
+ let component = null;
3797
+ let textResponse = null;
3798
+ if (userResponse.data) {
3799
+ if (typeof userResponse.data === "object") {
3800
+ if ("component" in userResponse.data) {
3801
+ component = userResponse.data.component;
3802
+ }
3803
+ if ("textResponse" in userResponse.data) {
3804
+ textResponse = userResponse.data.textResponse;
3248
3805
  }
3249
3806
  }
3250
- if (!SA_RUNTIME) {
3251
- sendDataResponse4(id, {
3252
- success: false,
3253
- error: "SA_RUNTIME is required"
3254
- }, sendMessage, wsId);
3255
- return;
3256
- }
3257
- const threadId = SA_RUNTIME.threadId;
3258
- const existingUiBlockId = SA_RUNTIME.uiBlockId;
3259
- if (!threadId) {
3260
- sendDataResponse4(id, {
3261
- success: false,
3262
- error: "threadId in SA_RUNTIME is required"
3263
- }, sendMessage, wsId);
3264
- return;
3265
- }
3266
- if (!existingUiBlockId) {
3267
- sendDataResponse4(id, {
3268
- success: false,
3269
- error: "uiBlockId in SA_RUNTIME is required"
3270
- }, sendMessage, wsId);
3271
- return;
3272
- }
3273
- const logCollector = new UILogCollector(wsId, sendMessage, existingUiBlockId);
3274
- if (!prompt) {
3275
- sendDataResponse4(id, {
3276
- success: false,
3277
- error: "Prompt not found"
3278
- }, sendMessage, wsId);
3279
- return;
3280
- }
3281
- if (!components || components.length === 0) {
3282
- sendDataResponse4(id, {
3283
- success: false,
3284
- error: "Components not found"
3285
- }, sendMessage, wsId);
3286
- return;
3287
- }
3288
- logCollector.info(`Starting user prompt request with ${components.length} components`);
3289
- const threadManager = ThreadManager.getInstance();
3290
- let thread = threadManager.getThread(threadId);
3291
- if (!thread) {
3292
- thread = threadManager.createThread(threadId);
3293
- logger.info(`Created new thread: ${threadId}`);
3294
- }
3295
- const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, existingUiBlockId);
3296
- const userResponse = await get_user_response(prompt, components, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory);
3297
- logCollector.info("User prompt request completed");
3298
- if (userResponse.success && userResponse.data && typeof userResponse.data === "object" && "component" in userResponse.data) {
3299
- const component = userResponse.data.component;
3300
- const uiBlockId = existingUiBlockId;
3301
- const uiBlock = new UIBlock(
3302
- prompt,
3303
- {},
3304
- // componentData: initially empty, will be filled later
3305
- component || {},
3306
- // generatedComponentMetadata: full component object (ComponentSchema)
3307
- [],
3308
- // actions: empty initially
3309
- uiBlockId
3310
- );
3311
- thread.addUIBlock(uiBlock);
3312
- logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
3313
- sendDataResponse4(id, {
3314
- ...userResponse,
3315
- uiBlockId,
3316
- threadId
3317
- }, sendMessage, wsId);
3318
- } else {
3319
- sendDataResponse4(id, userResponse, sendMessage, wsId);
3320
- }
3321
- return;
3322
- } catch (error) {
3323
- logger.error("Failed to handle user prompt request:", error);
3324
3807
  }
3808
+ const uiBlock = new UIBlock(
3809
+ prompt,
3810
+ {},
3811
+ // componentData: initially empty, will be filled later
3812
+ component,
3813
+ // generatedComponentMetadata: full component object (ComponentSchema)
3814
+ [],
3815
+ // actions: empty initially
3816
+ uiBlockId,
3817
+ textResponse
3818
+ // textResponse: text response from LLM
3819
+ );
3820
+ thread.addUIBlock(uiBlock);
3821
+ logger.info(`Created UIBlock: ${uiBlockId} in Thread: ${threadId}`);
3822
+ return {
3823
+ success: userResponse.success,
3824
+ data: userResponse.data,
3825
+ errors: userResponse.errors,
3826
+ uiBlockId,
3827
+ threadId,
3828
+ id,
3829
+ wsId
3830
+ };
3831
+ };
3832
+ async function handleUserPromptRequest(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3833
+ const response = await get_user_request(data, components, sendMessage, anthropicApiKey, groqApiKey, llmProviders);
3834
+ sendDataResponse4(
3835
+ response.id || data.id,
3836
+ {
3837
+ success: response.success,
3838
+ errors: response.errors,
3839
+ data: response.data,
3840
+ uiBlockId: response.uiBlockId,
3841
+ threadId: response.threadId
3842
+ },
3843
+ sendMessage,
3844
+ response.wsId || data.from?.id
3845
+ );
3325
3846
  }
3326
3847
  function sendDataResponse4(id, res, sendMessage, clientId) {
3327
3848
  const response = {
@@ -3336,6 +3857,7 @@ function sendDataResponse4(id, res, sendMessage, clientId) {
3336
3857
  ...res
3337
3858
  }
3338
3859
  };
3860
+ logger.info("sending user prompt response", response);
3339
3861
  sendMessage(response);
3340
3862
  }
3341
3863
 
@@ -3433,12 +3955,27 @@ function sendResponse(id, res, sendMessage, clientId) {
3433
3955
  // src/userResponse/next-questions.ts
3434
3956
  async function generateNextQuestions(originalUserPrompt, component, componentData, anthropicApiKey, groqApiKey, llmProviders, logCollector, conversationHistory) {
3435
3957
  try {
3958
+ logger.debug("[generateNextQuestions] Starting next questions generation");
3959
+ logger.debug(`[generateNextQuestions] User prompt: "${originalUserPrompt?.substring(0, 50)}..."`);
3960
+ logger.debug(`[generateNextQuestions] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
3961
+ logger.debug(`[generateNextQuestions] Component data available: ${componentData ? "yes" : "no"}`);
3436
3962
  const providers = llmProviders || ["anthropic"];
3437
- for (const provider of providers) {
3963
+ logger.info(`[generateNextQuestions] Using LLM providers: [${providers.join(", ")}]`);
3964
+ if (conversationHistory && conversationHistory.length > 0) {
3965
+ const exchangeCount = conversationHistory.split("\n").filter((l) => l.startsWith("Q")).length;
3966
+ logger.debug(`[generateNextQuestions] Using conversation history with ${exchangeCount} previous exchanges`);
3967
+ } else {
3968
+ logger.debug("[generateNextQuestions] No conversation history available");
3969
+ }
3970
+ for (let i = 0; i < providers.length; i++) {
3971
+ const provider = providers[i];
3972
+ const isLastProvider = i === providers.length - 1;
3438
3973
  try {
3439
- logger.info(`Generating next questions using provider: ${provider}`);
3974
+ logger.info(`[generateNextQuestions] Attempting provider: ${provider} (${i + 1}/${providers.length})`);
3975
+ logCollector?.info(`Generating questions with ${provider}...`);
3440
3976
  let result = [];
3441
3977
  if (provider === "groq") {
3978
+ logger.debug("[generateNextQuestions] Using Groq LLM for next questions");
3442
3979
  result = await groqLLM.generateNextQuestions(
3443
3980
  originalUserPrompt,
3444
3981
  component,
@@ -3448,6 +3985,7 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3448
3985
  conversationHistory
3449
3986
  );
3450
3987
  } else {
3988
+ logger.debug("[generateNextQuestions] Using Anthropic LLM for next questions");
3451
3989
  result = await anthropicLLM.generateNextQuestions(
3452
3990
  originalUserPrompt,
3453
3991
  component,
@@ -3458,21 +3996,39 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3458
3996
  );
3459
3997
  }
3460
3998
  if (result && result.length > 0) {
3461
- logger.info(`Successfully generated ${result.length} questions with ${provider}`);
3999
+ logger.info(`[generateNextQuestions] Successfully generated ${result.length} questions with ${provider}`);
4000
+ logger.debug(`[generateNextQuestions] Questions: ${JSON.stringify(result)}`);
4001
+ logCollector?.info(`Generated ${result.length} follow-up questions`);
3462
4002
  return result;
3463
4003
  }
3464
- logger.warn(`No questions generated from ${provider}, trying next provider...`);
4004
+ const warnMsg = `No questions generated from ${provider}${!isLastProvider ? ", trying next provider..." : ""}`;
4005
+ logger.warn(`[generateNextQuestions] ${warnMsg}`);
4006
+ if (!isLastProvider) {
4007
+ logCollector?.warn(warnMsg);
4008
+ }
3465
4009
  } catch (providerError) {
3466
- logger.warn(`Provider ${provider} failed:`, providerError);
3467
- logCollector?.warn(`Provider ${provider} failed, trying next provider...`);
4010
+ const errorMsg = providerError instanceof Error ? providerError.message : String(providerError);
4011
+ logger.error(`[generateNextQuestions] Provider ${provider} failed: ${errorMsg}`);
4012
+ logger.debug(`[generateNextQuestions] Provider error details:`, providerError);
4013
+ if (!isLastProvider) {
4014
+ const fallbackMsg = `Provider ${provider} failed, trying next provider...`;
4015
+ logger.info(`[generateNextQuestions] ${fallbackMsg}`);
4016
+ logCollector?.warn(fallbackMsg);
4017
+ } else {
4018
+ logCollector?.error(`Failed to generate questions with ${provider}`);
4019
+ }
3468
4020
  continue;
3469
4021
  }
3470
4022
  }
3471
- logger.warn("All providers failed or returned no questions");
4023
+ logger.warn("[generateNextQuestions] All providers failed or returned no questions");
4024
+ logCollector?.warn("Unable to generate follow-up questions");
3472
4025
  return [];
3473
4026
  } catch (error) {
3474
- logger.error("Error generating next questions:", error);
3475
- logCollector?.error(`Error generating next questions: ${error.message}`);
4027
+ const errorMsg = error instanceof Error ? error.message : String(error);
4028
+ const errorStack = error instanceof Error ? error.stack : void 0;
4029
+ logger.error(`[generateNextQuestions] Error generating next questions: ${errorMsg}`);
4030
+ logger.debug("[generateNextQuestions] Error stack trace:", errorStack);
4031
+ logCollector?.error(`Error generating next questions: ${errorMsg}`);
3476
4032
  return [];
3477
4033
  }
3478
4034
  }
@@ -3480,11 +4036,15 @@ async function generateNextQuestions(originalUserPrompt, component, componentDat
3480
4036
  // src/handlers/actions-request.ts
3481
4037
  async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiKey, llmProviders) {
3482
4038
  try {
4039
+ logger.debug("[ACTIONS_REQ] Parsing incoming actions request");
3483
4040
  const actionsRequest = ActionsRequestMessageSchema.parse(data);
3484
4041
  const { id, payload } = actionsRequest;
3485
4042
  const { SA_RUNTIME } = payload;
3486
4043
  const wsId = actionsRequest.from.id || "unknown";
4044
+ logger.info(`[ACTIONS_REQ ${id}] Processing actions request from client: ${wsId}`);
4045
+ logger.debug(`[ACTIONS_REQ ${id}] Request payload:`, JSON.stringify(payload, null, 2).substring(0, 200));
3487
4046
  if (!SA_RUNTIME) {
4047
+ logger.error(`[ACTIONS_REQ ${id}] SA_RUNTIME missing from request`);
3488
4048
  sendResponse2(id, {
3489
4049
  success: false,
3490
4050
  error: "SA_RUNTIME with threadId and uiBlockId is required"
@@ -3493,31 +4053,54 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3493
4053
  }
3494
4054
  const uiBlockId = SA_RUNTIME.uiBlockId;
3495
4055
  const threadId = SA_RUNTIME.threadId;
4056
+ logger.debug(`[ACTIONS_REQ ${id}] SA_RUNTIME validated - threadId: ${threadId}, uiBlockId: ${uiBlockId}`);
4057
+ logger.debug(`[ACTIONS_REQ ${id}] Retrieving thread: ${threadId}`);
3496
4058
  const threadManager = ThreadManager.getInstance();
3497
4059
  const thread = threadManager.getThread(threadId);
3498
4060
  if (!thread) {
4061
+ logger.error(`[ACTIONS_REQ ${id}] Thread '${threadId}' not found`);
3499
4062
  sendResponse2(id, {
3500
4063
  success: false,
3501
4064
  error: `Thread '${threadId}' not found`
3502
4065
  }, sendMessage, wsId);
3503
4066
  return;
3504
4067
  }
4068
+ logger.debug(`[ACTIONS_REQ ${id}] Thread found with ${thread.getUIBlocks().length} UIBlocks`);
4069
+ logger.debug(`[ACTIONS_REQ ${id}] Retrieving UIBlock: ${uiBlockId}`);
3505
4070
  const uiBlock = thread.getUIBlock(uiBlockId);
3506
4071
  if (!uiBlock) {
4072
+ logger.error(`[ACTIONS_REQ ${id}] UIBlock '${uiBlockId}' not found in thread '${threadId}'`);
3507
4073
  sendResponse2(id, {
3508
4074
  success: false,
3509
4075
  error: `UIBlock '${uiBlockId}' not found in thread '${threadId}'`
3510
4076
  }, sendMessage, wsId);
3511
4077
  return;
3512
4078
  }
4079
+ logger.info(`[ACTIONS_REQ ${id}] UIBlock retrieved successfully`);
4080
+ logger.debug(`[ACTIONS_REQ ${id}] Creating UILogCollector for uiBlockId: ${uiBlockId}`);
3513
4081
  const logCollector = new UILogCollector(wsId, sendMessage, uiBlockId);
4082
+ logger.info(`[ACTIONS_REQ ${id}] UILogCollector initialized`);
4083
+ logger.debug(`[ACTIONS_REQ ${id}] Extracting data from UIBlock`);
3514
4084
  const userQuestion = uiBlock.getUserQuestion();
3515
4085
  const component = uiBlock.getComponentMetadata();
3516
4086
  const componentData = uiBlock.getComponentData();
4087
+ logger.debug(`[ACTIONS_REQ ${id}] User question: "${userQuestion?.substring(0, 50)}..."`);
4088
+ logger.debug(`[ACTIONS_REQ ${id}] Component: ${component?.name || "unknown"} (${component?.type || "unknown"})`);
4089
+ logger.debug(`[ACTIONS_REQ ${id}] Component data available: ${componentData ? "yes" : "no"}`);
4090
+ logger.debug(`[ACTIONS_REQ ${id}] Extracting conversation history (max ${CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS} blocks)`);
3517
4091
  const conversationHistory = thread.getConversationContext(CONTEXT_CONFIG.MAX_CONVERSATION_CONTEXT_BLOCKS, uiBlockId);
4092
+ const historyLineCount = conversationHistory.split("\n").filter((l) => l.trim()).length;
4093
+ logger.info(`[ACTIONS_REQ ${id}] Conversation history extracted: ${historyLineCount} lines`);
4094
+ logger.debug(`[ACTIONS_REQ ${id}] Conversation history preview:
4095
+ ${conversationHistory.substring(0, 200)}...`);
3518
4096
  logCollector.info(`Generating actions for UIBlock: ${uiBlockId}`);
3519
- logger.info(`Generating actions for component: ${component?.name || "unknown"}`);
4097
+ logger.info(`[ACTIONS_REQ ${id}] Generating actions for component: ${component?.name || "unknown"}`);
4098
+ logger.debug(`[ACTIONS_REQ ${id}] Checking if actions are already cached`);
4099
+ const startTime = Date.now();
3520
4100
  const actions = await uiBlock.getOrFetchActions(async () => {
4101
+ logger.info(`[ACTIONS_REQ ${id}] Actions not cached, generating new actions...`);
4102
+ logCollector.info("Generating follow-up questions...");
4103
+ logger.info(`[ACTIONS_REQ ${id}] Starting next questions generation with ${llmProviders?.join(", ") || "default"} providers`);
3521
4104
  const nextQuestions = await generateNextQuestions(
3522
4105
  userQuestion,
3523
4106
  component,
@@ -3528,14 +4111,28 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3528
4111
  logCollector,
3529
4112
  conversationHistory
3530
4113
  );
3531
- return nextQuestions.map((question, index) => ({
4114
+ logger.info(`[ACTIONS_REQ ${id}] Generated ${nextQuestions.length} questions`);
4115
+ logger.debug(`[ACTIONS_REQ ${id}] Questions: ${JSON.stringify(nextQuestions)}`);
4116
+ logger.debug(`[ACTIONS_REQ ${id}] Converting questions to actions format`);
4117
+ const convertedActions = nextQuestions.map((question, index) => ({
3532
4118
  id: `action_${index}_${Date.now()}`,
3533
4119
  name: question,
3534
4120
  type: "next_question",
3535
4121
  question
3536
4122
  }));
4123
+ logger.debug(`[ACTIONS_REQ ${id}] Converted ${convertedActions.length} actions`);
4124
+ return convertedActions;
3537
4125
  });
3538
- logCollector.info(`Generated ${actions.length} actions successfully`);
4126
+ const processingTime = Date.now() - startTime;
4127
+ logger.info(`[ACTIONS_REQ ${id}] Actions retrieved in ${processingTime}ms - ${actions.length} actions total`);
4128
+ if (actions.length > 0) {
4129
+ logCollector.info(`Generated ${actions.length} follow-up questions successfully`);
4130
+ logger.debug(`[ACTIONS_REQ ${id}] Actions: ${actions.map((a) => a.name).join(", ")}`);
4131
+ } else {
4132
+ logger.warn(`[ACTIONS_REQ ${id}] No actions generated`);
4133
+ logCollector.warn("No follow-up questions could be generated");
4134
+ }
4135
+ logger.debug(`[ACTIONS_REQ ${id}] Sending successful response to client`);
3539
4136
  sendResponse2(id, {
3540
4137
  success: true,
3541
4138
  data: {
@@ -3546,12 +4143,26 @@ async function handleActionsRequest(data, sendMessage, anthropicApiKey, groqApiK
3546
4143
  threadId
3547
4144
  }
3548
4145
  }, sendMessage, wsId);
4146
+ logger.info(`[ACTIONS_REQ ${id}] \u2713 Actions request completed successfully`);
3549
4147
  } catch (error) {
3550
- logger.error("Failed to handle actions request:", error);
4148
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
4149
+ const errorStack = error instanceof Error ? error.stack : void 0;
4150
+ logger.error(`[ACTIONS_REQ] Failed to handle actions request: ${errorMessage}`);
4151
+ logger.debug(`[ACTIONS_REQ] Error stack trace:`, errorStack);
4152
+ try {
4153
+ const parsedData = data;
4154
+ if (parsedData?.id && parsedData?.from?.id) {
4155
+ const logCollector = parsedData?.payload?.SA_RUNTIME?.uiBlockId ? new UILogCollector(parsedData.from.id, sendMessage, parsedData.payload.SA_RUNTIME.uiBlockId) : void 0;
4156
+ logCollector?.error(`Failed to generate actions: ${errorMessage}`);
4157
+ }
4158
+ } catch (logError) {
4159
+ logger.debug("[ACTIONS_REQ] Failed to send error logs to UI:", logError);
4160
+ }
3551
4161
  sendResponse2(null, {
3552
4162
  success: false,
3553
- error: error instanceof Error ? error.message : "Unknown error occurred"
4163
+ error: errorMessage
3554
4164
  }, sendMessage);
4165
+ logger.info("[ACTIONS_REQ] \u2717 Actions request completed with errors");
3555
4166
  }
3556
4167
  }
3557
4168
  function sendResponse2(id, res, sendMessage, clientId) {
@@ -3567,6 +4178,11 @@ function sendResponse2(id, res, sendMessage, clientId) {
3567
4178
  ...res
3568
4179
  }
3569
4180
  };
4181
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.success ? "successful" : "failed"} response to client`);
4182
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Response payload size: ${JSON.stringify(response).length} bytes`);
4183
+ if (res.data?.actions) {
4184
+ logger.debug(`[ACTIONS_RES ${id || "unknown"}] Sending ${res.data.actions.length} actions`);
4185
+ }
3570
4186
  sendMessage(response);
3571
4187
  }
3572
4188
 
@@ -3595,7 +4211,10 @@ async function handleUsersRequest(data, sendMessage) {
3595
4211
  const { id, payload, from } = request;
3596
4212
  const { operation, data: requestData } = payload;
3597
4213
  const username = requestData?.username;
4214
+ const email = requestData?.email;
3598
4215
  const password = requestData?.password;
4216
+ const fullname = requestData?.fullname;
4217
+ const role = requestData?.role;
3599
4218
  if (from.type !== "admin") {
3600
4219
  sendResponse3(id, {
3601
4220
  success: false,
@@ -3607,10 +4226,10 @@ async function handleUsersRequest(data, sendMessage) {
3607
4226
  const userManager = getUserManager();
3608
4227
  switch (operation) {
3609
4228
  case "create":
3610
- await handleCreate(id, username, password, userManager, sendMessage, from.id);
4229
+ await handleCreate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3611
4230
  break;
3612
4231
  case "update":
3613
- await handleUpdate(id, username, password, userManager, sendMessage, from.id);
4232
+ await handleUpdate(id, { username, email, password, fullname, role }, userManager, sendMessage, from.id);
3614
4233
  break;
3615
4234
  case "delete":
3616
4235
  await handleDelete(id, username, userManager, sendMessage, from.id);
@@ -3635,7 +4254,8 @@ async function handleUsersRequest(data, sendMessage) {
3635
4254
  }, sendMessage);
3636
4255
  }
3637
4256
  }
3638
- async function handleCreate(id, username, password, userManager, sendMessage, clientId) {
4257
+ async function handleCreate(id, userData, userManager, sendMessage, clientId) {
4258
+ const { username, email, password, fullname, role } = userData;
3639
4259
  if (!username || username.trim().length === 0) {
3640
4260
  sendResponse3(id, {
3641
4261
  success: false,
@@ -3650,6 +4270,16 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3650
4270
  }, sendMessage, clientId);
3651
4271
  return;
3652
4272
  }
4273
+ if (email && email.trim().length > 0) {
4274
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4275
+ if (!emailRegex.test(email)) {
4276
+ sendResponse3(id, {
4277
+ success: false,
4278
+ error: "Invalid email format"
4279
+ }, sendMessage, clientId);
4280
+ return;
4281
+ }
4282
+ }
3653
4283
  if (userManager.userExists(username)) {
3654
4284
  sendResponse3(id, {
3655
4285
  success: false,
@@ -3657,25 +4287,41 @@ async function handleCreate(id, username, password, userManager, sendMessage, cl
3657
4287
  }, sendMessage, clientId);
3658
4288
  return;
3659
4289
  }
3660
- let wsIds = [];
3661
- if (clientId) {
3662
- wsIds.push(clientId);
4290
+ if (email && userManager.getUserByEmail(email)) {
4291
+ sendResponse3(id, {
4292
+ success: false,
4293
+ error: `User with email '${email}' already exists`
4294
+ }, sendMessage, clientId);
4295
+ return;
3663
4296
  }
3664
- const newUser = userManager.createUser({
4297
+ const newUserData = {
3665
4298
  username,
3666
- password,
3667
- wsIds
3668
- });
3669
- logger.info(`User created by admin: ${username}`);
4299
+ password
4300
+ };
4301
+ if (email && email.trim().length > 0) {
4302
+ newUserData.email = email.trim();
4303
+ }
4304
+ if (fullname && fullname.trim().length > 0) {
4305
+ newUserData.fullname = fullname.trim();
4306
+ }
4307
+ if (role && role.trim().length > 0) {
4308
+ newUserData.role = role.trim();
4309
+ }
4310
+ const newUser = userManager.createUser(newUserData);
4311
+ logger.info(`User created by admin: ${username}${email ? ` (${email})` : ""}`);
3670
4312
  sendResponse3(id, {
3671
4313
  success: true,
3672
4314
  data: {
3673
4315
  username: newUser.username,
4316
+ email: newUser.email,
4317
+ fullname: newUser.fullname,
4318
+ role: newUser.role,
3674
4319
  message: `User '${username}' created successfully`
3675
4320
  }
3676
4321
  }, sendMessage, clientId);
3677
4322
  }
3678
- async function handleUpdate(id, username, password, userManager, sendMessage, clientId) {
4323
+ async function handleUpdate(id, userData, userManager, sendMessage, clientId) {
4324
+ const { username, email, password, fullname, role } = userData;
3679
4325
  if (!username || username.trim().length === 0) {
3680
4326
  sendResponse3(id, {
3681
4327
  success: false,
@@ -3691,13 +4337,42 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3691
4337
  return;
3692
4338
  }
3693
4339
  const updates = {};
3694
- if (password && password.trim().length > 0) {
3695
- updates.password = password;
4340
+ if (email !== void 0) {
4341
+ if (email.trim().length > 0) {
4342
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4343
+ if (!emailRegex.test(email)) {
4344
+ sendResponse3(id, {
4345
+ success: false,
4346
+ error: "Invalid email format"
4347
+ }, sendMessage, clientId);
4348
+ return;
4349
+ }
4350
+ const existingUser = userManager.getUserByEmail(email);
4351
+ if (existingUser && existingUser.username !== username) {
4352
+ sendResponse3(id, {
4353
+ success: false,
4354
+ error: `Email '${email}' is already used by another user`
4355
+ }, sendMessage, clientId);
4356
+ return;
4357
+ }
4358
+ updates.email = email.trim();
4359
+ } else {
4360
+ updates.email = void 0;
4361
+ }
4362
+ }
4363
+ if (password !== void 0 && password.trim().length > 0) {
4364
+ updates.password = password.trim();
4365
+ }
4366
+ if (fullname !== void 0) {
4367
+ updates.fullname = fullname.trim().length > 0 ? fullname.trim() : void 0;
4368
+ }
4369
+ if (role !== void 0) {
4370
+ updates.role = role.trim().length > 0 ? role.trim() : void 0;
3696
4371
  }
3697
4372
  if (Object.keys(updates).length === 0) {
3698
4373
  sendResponse3(id, {
3699
4374
  success: false,
3700
- error: "No fields to update. Please provide password or other valid fields."
4375
+ error: "No fields to update. Please provide at least one field to update."
3701
4376
  }, sendMessage, clientId);
3702
4377
  return;
3703
4378
  }
@@ -3707,6 +4382,9 @@ async function handleUpdate(id, username, password, userManager, sendMessage, cl
3707
4382
  success: true,
3708
4383
  data: {
3709
4384
  username: updatedUser.username,
4385
+ email: updatedUser.email,
4386
+ fullname: updatedUser.fullname,
4387
+ role: updatedUser.role,
3710
4388
  message: `User '${username}' updated successfully`
3711
4389
  }
3712
4390
  }, sendMessage, clientId);
@@ -3747,6 +4425,9 @@ async function handleGetAll(id, userManager, sendMessage, clientId) {
3747
4425
  const users = userManager.getAllUsers();
3748
4426
  const sanitizedUsers = users.map((user) => ({
3749
4427
  username: user.username,
4428
+ email: user.email,
4429
+ fullname: user.fullname,
4430
+ role: user.role,
3750
4431
  wsIds: user.wsIds || []
3751
4432
  }));
3752
4433
  logger.info(`Admin retrieved all users (count: ${sanitizedUsers.length})`);
@@ -3777,6 +4458,9 @@ async function handleGetOne(id, username, userManager, sendMessage, clientId) {
3777
4458
  const user = userManager.getUser(username);
3778
4459
  const sanitizedUser = {
3779
4460
  username: user.username,
4461
+ email: user.email,
4462
+ fullname: user.fullname,
4463
+ role: user.role,
3780
4464
  wsIds: user.wsIds || []
3781
4465
  };
3782
4466
  logger.info(`Admin retrieved user: ${username}`);
@@ -4275,8 +4959,9 @@ var UserManager = class {
4275
4959
  return;
4276
4960
  }
4277
4961
  const fileContent = import_fs3.default.readFileSync(this.filePath, "utf-8");
4278
- const data = JSON.parse(fileContent);
4279
- this.users = Array.isArray(data.users) ? data.users : [];
4962
+ const rawData = JSON.parse(fileContent);
4963
+ const validatedData = UsersDataSchema.parse(rawData);
4964
+ this.users = validatedData.users;
4280
4965
  this.hasChanged = false;
4281
4966
  logger.debug(`Loaded ${this.users.length} users from file`);
4282
4967
  } catch (error) {
@@ -4296,10 +4981,14 @@ var UserManager = class {
4296
4981
  if (!import_fs3.default.existsSync(dir)) {
4297
4982
  import_fs3.default.mkdirSync(dir, { recursive: true });
4298
4983
  }
4299
- const data = { users: this.users };
4984
+ const usersToSave = this.users.map((user) => {
4985
+ const { wsIds, ...userWithoutWsIds } = user;
4986
+ return userWithoutWsIds;
4987
+ });
4988
+ const data = { users: usersToSave };
4300
4989
  import_fs3.default.writeFileSync(this.filePath, JSON.stringify(data, null, 4));
4301
4990
  this.hasChanged = false;
4302
- logger.debug(`Synced ${this.users.length} users to file`);
4991
+ logger.debug(`Synced ${this.users.length} users to file (wsIds excluded)`);
4303
4992
  } catch (error) {
4304
4993
  logger.error("Failed to save users to file:", error);
4305
4994
  throw new Error(`Failed to save users to file: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -4346,13 +5035,17 @@ var UserManager = class {
4346
5035
  * @returns The created user
4347
5036
  */
4348
5037
  createUser(user) {
4349
- if (this.users.some((u) => u.username === user.username)) {
4350
- throw new Error(`User with username ${user.username} already exists`);
5038
+ const validatedUser = UserSchema.parse(user);
5039
+ if (this.users.some((u) => u.username === validatedUser.username)) {
5040
+ throw new Error(`User with username ${validatedUser.username} already exists`);
4351
5041
  }
4352
- this.users.push(user);
5042
+ if (validatedUser.email && this.users.some((u) => u.email === validatedUser.email)) {
5043
+ throw new Error(`User with email ${validatedUser.email} already exists`);
5044
+ }
5045
+ this.users.push(validatedUser);
4353
5046
  this.hasChanged = true;
4354
- logger.debug(`User created: ${user.username}`);
4355
- return user;
5047
+ logger.debug(`User created: ${validatedUser.username}`);
5048
+ return validatedUser;
4356
5049
  }
4357
5050
  /**
4358
5051
  * Read a user by username
@@ -4362,6 +5055,22 @@ var UserManager = class {
4362
5055
  getUser(username) {
4363
5056
  return this.users.find((u) => u.username === username);
4364
5057
  }
5058
+ /**
5059
+ * Read a user by email
5060
+ * @param email - Email to retrieve
5061
+ * @returns The user if found, undefined otherwise
5062
+ */
5063
+ getUserByEmail(email) {
5064
+ return this.users.find((u) => u.email === email);
5065
+ }
5066
+ /**
5067
+ * Find user by username or email
5068
+ * @param identifier - Username or email to search for
5069
+ * @returns The user if found, undefined otherwise
5070
+ */
5071
+ getUserByUsernameOrEmail(identifier) {
5072
+ return this.users.find((u) => u.username === identifier || u.email === identifier);
5073
+ }
4365
5074
  /**
4366
5075
  * Read all users
4367
5076
  * @returns Array of all users
@@ -4450,7 +5159,6 @@ var UserManager = class {
4450
5159
  }
4451
5160
  if (!user.wsIds.includes(wsId)) {
4452
5161
  user.wsIds.push(wsId);
4453
- this.hasChanged = true;
4454
5162
  logger.debug(`WebSocket ID added to user ${username}: ${wsId}`);
4455
5163
  }
4456
5164
  return true;
@@ -4472,7 +5180,6 @@ var UserManager = class {
4472
5180
  const initialLength = user.wsIds.length;
4473
5181
  user.wsIds = user.wsIds.filter((id) => id !== wsId);
4474
5182
  if (user.wsIds.length < initialLength) {
4475
- this.hasChanged = true;
4476
5183
  logger.debug(`WebSocket ID removed from user ${username}: ${wsId}`);
4477
5184
  }
4478
5185
  return true;
@@ -5022,6 +5729,9 @@ var SuperatomSDK = class {
5022
5729
  this.maxReconnectAttempts = 5;
5023
5730
  this.collections = {};
5024
5731
  this.components = [];
5732
+ if (config.logLevel) {
5733
+ logger.setLogLevel(config.logLevel);
5734
+ }
5025
5735
  this.apiKey = config.apiKey;
5026
5736
  this.projectId = config.projectId;
5027
5737
  this.userId = config.userId || "anonymous";
@@ -5325,6 +6035,7 @@ var SuperatomSDK = class {
5325
6035
  ThreadManager,
5326
6036
  UIBlock,
5327
6037
  UILogCollector,
5328
- UserManager
6038
+ UserManager,
6039
+ logger
5329
6040
  });
5330
6041
  //# sourceMappingURL=index.js.map