@superatomai/sdk-node 0.0.29 → 0.0.31
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/README.md +942 -942
- package/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +92 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +92 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +49 -48
package/dist/index.mjs
CHANGED
|
@@ -4788,20 +4788,25 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
|
|
|
4788
4788
|
logCollector?.info("Generating text response with query execution capability...");
|
|
4789
4789
|
const tools = [{
|
|
4790
4790
|
name: "execute_query",
|
|
4791
|
-
description: "Executes a SQL query against the database
|
|
4791
|
+
description: "Executes a parameterized SQL query against the database. CRITICAL: NEVER hardcode literal values in WHERE/HAVING conditions - ALWAYS use $paramName placeholders and pass actual values in params object.",
|
|
4792
4792
|
input_schema: {
|
|
4793
4793
|
type: "object",
|
|
4794
4794
|
properties: {
|
|
4795
|
-
|
|
4795
|
+
sql: {
|
|
4796
4796
|
type: "string",
|
|
4797
|
-
description: "
|
|
4797
|
+
description: "SQL query with $paramName placeholders for ALL literal values in WHERE/HAVING conditions. NEVER hardcode values like WHERE status = 'Delivered' - instead use WHERE status = $status. Table names, column names, and SQL keywords stay as-is."
|
|
4798
|
+
},
|
|
4799
|
+
params: {
|
|
4800
|
+
type: "object",
|
|
4801
|
+
description: 'REQUIRED when SQL has WHERE/HAVING conditions. Maps each $paramName placeholder (without $) to its actual value. Pattern: WHERE col = $name \u2192 params: { "name": "value" }. Every placeholder in SQL MUST have a corresponding entry here.',
|
|
4802
|
+
additionalProperties: true
|
|
4798
4803
|
},
|
|
4799
4804
|
reasoning: {
|
|
4800
4805
|
type: "string",
|
|
4801
4806
|
description: "Brief explanation of what this query does and why it answers the user's question."
|
|
4802
4807
|
}
|
|
4803
4808
|
},
|
|
4804
|
-
required: ["
|
|
4809
|
+
required: ["sql"],
|
|
4805
4810
|
additionalProperties: false
|
|
4806
4811
|
}
|
|
4807
4812
|
}];
|
|
@@ -4896,14 +4901,18 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
|
|
|
4896
4901
|
} : void 0;
|
|
4897
4902
|
const toolHandler = async (toolName, toolInput) => {
|
|
4898
4903
|
if (toolName === "execute_query") {
|
|
4899
|
-
let
|
|
4904
|
+
let sql = toolInput.sql;
|
|
4905
|
+
const params = toolInput.params || {};
|
|
4900
4906
|
const reasoning = toolInput.reasoning;
|
|
4901
4907
|
const { ensureQueryLimit: ensureQueryLimit2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
4902
|
-
|
|
4903
|
-
const queryKey =
|
|
4908
|
+
sql = ensureQueryLimit2(sql, 32, 32);
|
|
4909
|
+
const queryKey = sql.toLowerCase().replace(/\s+/g, " ").trim();
|
|
4904
4910
|
const attempts = (queryAttempts.get(queryKey) || 0) + 1;
|
|
4905
4911
|
queryAttempts.set(queryKey, attempts);
|
|
4906
|
-
logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${
|
|
4912
|
+
logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${sql.substring(0, 100)}...`);
|
|
4913
|
+
if (Object.keys(params).length > 0) {
|
|
4914
|
+
logger.info(`[${this.getProviderName()}] Query params: ${JSON.stringify(params)}`);
|
|
4915
|
+
}
|
|
4907
4916
|
if (reasoning) {
|
|
4908
4917
|
logCollector?.info(`Query reasoning: ${reasoning}`);
|
|
4909
4918
|
}
|
|
@@ -4925,6 +4934,8 @@ Please try rephrasing your question or simplifying your request.
|
|
|
4925
4934
|
}
|
|
4926
4935
|
try {
|
|
4927
4936
|
if (wrappedStreamCallback) {
|
|
4937
|
+
const paramsDisplay = Object.keys(params).length > 0 ? `
|
|
4938
|
+
**Parameters:** ${JSON.stringify(params)}` : "";
|
|
4928
4939
|
if (attempts === 1) {
|
|
4929
4940
|
wrappedStreamCallback(`
|
|
4930
4941
|
|
|
@@ -4938,8 +4949,8 @@ Please try rephrasing your question or simplifying your request.
|
|
|
4938
4949
|
}
|
|
4939
4950
|
wrappedStreamCallback(`\u{1F4DD} **Generated SQL Query:**
|
|
4940
4951
|
\`\`\`sql
|
|
4941
|
-
${
|
|
4942
|
-
|
|
4952
|
+
${sql}
|
|
4953
|
+
\`\`\`${paramsDisplay}
|
|
4943
4954
|
|
|
4944
4955
|
`);
|
|
4945
4956
|
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
@@ -4958,8 +4969,8 @@ ${query}
|
|
|
4958
4969
|
}
|
|
4959
4970
|
wrappedStreamCallback(`\u{1F4DD} **Corrected SQL Query:**
|
|
4960
4971
|
\`\`\`sql
|
|
4961
|
-
${
|
|
4962
|
-
|
|
4972
|
+
${sql}
|
|
4973
|
+
\`\`\`${paramsDisplay}
|
|
4963
4974
|
|
|
4964
4975
|
`);
|
|
4965
4976
|
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
@@ -4969,13 +4980,14 @@ ${query}
|
|
|
4969
4980
|
}
|
|
4970
4981
|
logCollector?.logQuery(
|
|
4971
4982
|
`Executing SQL query (attempt ${attempts})`,
|
|
4972
|
-
|
|
4983
|
+
{ sql, params },
|
|
4973
4984
|
{ reasoning, attempt: attempts }
|
|
4974
4985
|
);
|
|
4975
4986
|
if (!collections || !collections["database"] || !collections["database"]["execute"]) {
|
|
4976
4987
|
throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
|
|
4977
4988
|
}
|
|
4978
|
-
const
|
|
4989
|
+
const queryPayload = Object.keys(params).length > 0 ? { sql: JSON.stringify({ sql, values: params }) } : { sql };
|
|
4990
|
+
const result2 = await collections["database"]["execute"](queryPayload);
|
|
4979
4991
|
const data = result2?.data || result2;
|
|
4980
4992
|
const rowCount = result2?.count ?? (Array.isArray(data) ? data.length : "N/A");
|
|
4981
4993
|
logger.info(`[${this.getProviderName()}] Query executed successfully, rows returned: ${rowCount}`);
|
|
@@ -9737,6 +9749,7 @@ init_logger();
|
|
|
9737
9749
|
var SDK_VERSION = "0.0.8";
|
|
9738
9750
|
var DEFAULT_WS_URL = "wss://ws.superatom.ai/websocket";
|
|
9739
9751
|
var SuperatomSDK = class {
|
|
9752
|
+
// 3.5 minutes (PING_INTERVAL + 30s grace)
|
|
9740
9753
|
constructor(config) {
|
|
9741
9754
|
this.ws = null;
|
|
9742
9755
|
this.messageHandlers = /* @__PURE__ */ new Map();
|
|
@@ -9747,6 +9760,12 @@ var SuperatomSDK = class {
|
|
|
9747
9760
|
this.collections = {};
|
|
9748
9761
|
this.components = [];
|
|
9749
9762
|
this.tools = [];
|
|
9763
|
+
// Heartbeat properties for keeping WebSocket connection alive
|
|
9764
|
+
this.pingInterval = null;
|
|
9765
|
+
this.lastPong = Date.now();
|
|
9766
|
+
this.PING_INTERVAL_MS = 18e4;
|
|
9767
|
+
// 3 minutes
|
|
9768
|
+
this.PONG_TIMEOUT_MS = 21e4;
|
|
9750
9769
|
if (config.logLevel) {
|
|
9751
9770
|
logger.setLogLevel(config.logLevel);
|
|
9752
9771
|
}
|
|
@@ -9860,6 +9879,7 @@ var SuperatomSDK = class {
|
|
|
9860
9879
|
this.connected = true;
|
|
9861
9880
|
this.reconnectAttempts = 0;
|
|
9862
9881
|
logger.info("WebSocket connected successfully");
|
|
9882
|
+
this.startHeartbeat();
|
|
9863
9883
|
resolve();
|
|
9864
9884
|
});
|
|
9865
9885
|
this.ws.addEventListener("message", (event) => {
|
|
@@ -9888,6 +9908,9 @@ var SuperatomSDK = class {
|
|
|
9888
9908
|
const message = IncomingMessageSchema.parse(parsed);
|
|
9889
9909
|
logger.debug("Received message:", message.type);
|
|
9890
9910
|
switch (message.type) {
|
|
9911
|
+
case "PONG":
|
|
9912
|
+
this.handlePong();
|
|
9913
|
+
break;
|
|
9891
9914
|
case "DATA_REQ":
|
|
9892
9915
|
handleDataRequest(parsed, this.collections, (msg) => this.send(msg)).catch((error) => {
|
|
9893
9916
|
logger.error("Failed to handle data request:", error);
|
|
@@ -10010,6 +10033,7 @@ var SuperatomSDK = class {
|
|
|
10010
10033
|
* Disconnect from the WebSocket service
|
|
10011
10034
|
*/
|
|
10012
10035
|
disconnect() {
|
|
10036
|
+
this.stopHeartbeat();
|
|
10013
10037
|
if (this.ws) {
|
|
10014
10038
|
this.ws.close();
|
|
10015
10039
|
this.ws = null;
|
|
@@ -10020,6 +10044,7 @@ var SuperatomSDK = class {
|
|
|
10020
10044
|
* Cleanup and disconnect - stops reconnection attempts and closes the connection
|
|
10021
10045
|
*/
|
|
10022
10046
|
async destroy() {
|
|
10047
|
+
this.stopHeartbeat();
|
|
10023
10048
|
this.maxReconnectAttempts = 0;
|
|
10024
10049
|
this.reconnectAttempts = 0;
|
|
10025
10050
|
this.messageHandlers.clear();
|
|
@@ -10061,6 +10086,59 @@ var SuperatomSDK = class {
|
|
|
10061
10086
|
logger.error("Max reconnection attempts reached");
|
|
10062
10087
|
}
|
|
10063
10088
|
}
|
|
10089
|
+
/**
|
|
10090
|
+
* Start heartbeat to keep WebSocket connection alive
|
|
10091
|
+
* Sends PING every 3 minutes to prevent idle timeout from cloud infrastructure
|
|
10092
|
+
*/
|
|
10093
|
+
startHeartbeat() {
|
|
10094
|
+
this.stopHeartbeat();
|
|
10095
|
+
this.lastPong = Date.now();
|
|
10096
|
+
this.pingInterval = setInterval(() => {
|
|
10097
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
|
|
10098
|
+
logger.warn("WebSocket not open during heartbeat check, stopping heartbeat");
|
|
10099
|
+
this.stopHeartbeat();
|
|
10100
|
+
return;
|
|
10101
|
+
}
|
|
10102
|
+
const timeSinceLastPong = Date.now() - this.lastPong;
|
|
10103
|
+
if (timeSinceLastPong > this.PONG_TIMEOUT_MS) {
|
|
10104
|
+
logger.warn(`No PONG received for ${timeSinceLastPong}ms, connection may be dead. Reconnecting...`);
|
|
10105
|
+
this.stopHeartbeat();
|
|
10106
|
+
this.ws.close();
|
|
10107
|
+
return;
|
|
10108
|
+
}
|
|
10109
|
+
try {
|
|
10110
|
+
const pingMessage = {
|
|
10111
|
+
type: "PING",
|
|
10112
|
+
from: { type: this.type },
|
|
10113
|
+
payload: { timestamp: Date.now() }
|
|
10114
|
+
};
|
|
10115
|
+
this.ws.send(JSON.stringify(pingMessage));
|
|
10116
|
+
logger.debug("Heartbeat PING sent");
|
|
10117
|
+
} catch (error) {
|
|
10118
|
+
logger.error("Failed to send PING:", error);
|
|
10119
|
+
this.stopHeartbeat();
|
|
10120
|
+
this.ws.close();
|
|
10121
|
+
}
|
|
10122
|
+
}, this.PING_INTERVAL_MS);
|
|
10123
|
+
logger.info(`Heartbeat started (PING every ${this.PING_INTERVAL_MS / 1e3}s)`);
|
|
10124
|
+
}
|
|
10125
|
+
/**
|
|
10126
|
+
* Stop the heartbeat interval
|
|
10127
|
+
*/
|
|
10128
|
+
stopHeartbeat() {
|
|
10129
|
+
if (this.pingInterval) {
|
|
10130
|
+
clearInterval(this.pingInterval);
|
|
10131
|
+
this.pingInterval = null;
|
|
10132
|
+
logger.debug("Heartbeat stopped");
|
|
10133
|
+
}
|
|
10134
|
+
}
|
|
10135
|
+
/**
|
|
10136
|
+
* Handle PONG response from server
|
|
10137
|
+
*/
|
|
10138
|
+
handlePong() {
|
|
10139
|
+
this.lastPong = Date.now();
|
|
10140
|
+
logger.debug("Heartbeat PONG received");
|
|
10141
|
+
}
|
|
10064
10142
|
storeComponents(components) {
|
|
10065
10143
|
this.components = components;
|
|
10066
10144
|
}
|