@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.d.mts
CHANGED
|
@@ -1650,6 +1650,10 @@ declare class SuperatomSDK {
|
|
|
1650
1650
|
private userManager;
|
|
1651
1651
|
private dashboardManager;
|
|
1652
1652
|
private reportManager;
|
|
1653
|
+
private pingInterval;
|
|
1654
|
+
private lastPong;
|
|
1655
|
+
private readonly PING_INTERVAL_MS;
|
|
1656
|
+
private readonly PONG_TIMEOUT_MS;
|
|
1653
1657
|
constructor(config: SuperatomSDKConfig);
|
|
1654
1658
|
/**
|
|
1655
1659
|
* Initialize PromptLoader and load prompts into memory
|
|
@@ -1717,6 +1721,19 @@ declare class SuperatomSDK {
|
|
|
1717
1721
|
*/
|
|
1718
1722
|
addCollection<TParams = any, TResult = any>(collectionName: string, operation: CollectionOperation | string, handler: CollectionHandler<TParams, TResult>): void;
|
|
1719
1723
|
private handleReconnect;
|
|
1724
|
+
/**
|
|
1725
|
+
* Start heartbeat to keep WebSocket connection alive
|
|
1726
|
+
* Sends PING every 3 minutes to prevent idle timeout from cloud infrastructure
|
|
1727
|
+
*/
|
|
1728
|
+
private startHeartbeat;
|
|
1729
|
+
/**
|
|
1730
|
+
* Stop the heartbeat interval
|
|
1731
|
+
*/
|
|
1732
|
+
private stopHeartbeat;
|
|
1733
|
+
/**
|
|
1734
|
+
* Handle PONG response from server
|
|
1735
|
+
*/
|
|
1736
|
+
private handlePong;
|
|
1720
1737
|
private storeComponents;
|
|
1721
1738
|
/**
|
|
1722
1739
|
* Set tools for the SDK instance
|
package/dist/index.d.ts
CHANGED
|
@@ -1650,6 +1650,10 @@ declare class SuperatomSDK {
|
|
|
1650
1650
|
private userManager;
|
|
1651
1651
|
private dashboardManager;
|
|
1652
1652
|
private reportManager;
|
|
1653
|
+
private pingInterval;
|
|
1654
|
+
private lastPong;
|
|
1655
|
+
private readonly PING_INTERVAL_MS;
|
|
1656
|
+
private readonly PONG_TIMEOUT_MS;
|
|
1653
1657
|
constructor(config: SuperatomSDKConfig);
|
|
1654
1658
|
/**
|
|
1655
1659
|
* Initialize PromptLoader and load prompts into memory
|
|
@@ -1717,6 +1721,19 @@ declare class SuperatomSDK {
|
|
|
1717
1721
|
*/
|
|
1718
1722
|
addCollection<TParams = any, TResult = any>(collectionName: string, operation: CollectionOperation | string, handler: CollectionHandler<TParams, TResult>): void;
|
|
1719
1723
|
private handleReconnect;
|
|
1724
|
+
/**
|
|
1725
|
+
* Start heartbeat to keep WebSocket connection alive
|
|
1726
|
+
* Sends PING every 3 minutes to prevent idle timeout from cloud infrastructure
|
|
1727
|
+
*/
|
|
1728
|
+
private startHeartbeat;
|
|
1729
|
+
/**
|
|
1730
|
+
* Stop the heartbeat interval
|
|
1731
|
+
*/
|
|
1732
|
+
private stopHeartbeat;
|
|
1733
|
+
/**
|
|
1734
|
+
* Handle PONG response from server
|
|
1735
|
+
*/
|
|
1736
|
+
private handlePong;
|
|
1720
1737
|
private storeComponents;
|
|
1721
1738
|
/**
|
|
1722
1739
|
* Set tools for the SDK instance
|
package/dist/index.js
CHANGED
|
@@ -4832,20 +4832,25 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
|
|
|
4832
4832
|
logCollector?.info("Generating text response with query execution capability...");
|
|
4833
4833
|
const tools = [{
|
|
4834
4834
|
name: "execute_query",
|
|
4835
|
-
description: "Executes a SQL query against the database
|
|
4835
|
+
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.",
|
|
4836
4836
|
input_schema: {
|
|
4837
4837
|
type: "object",
|
|
4838
4838
|
properties: {
|
|
4839
|
-
|
|
4839
|
+
sql: {
|
|
4840
4840
|
type: "string",
|
|
4841
|
-
description: "
|
|
4841
|
+
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."
|
|
4842
|
+
},
|
|
4843
|
+
params: {
|
|
4844
|
+
type: "object",
|
|
4845
|
+
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.',
|
|
4846
|
+
additionalProperties: true
|
|
4842
4847
|
},
|
|
4843
4848
|
reasoning: {
|
|
4844
4849
|
type: "string",
|
|
4845
4850
|
description: "Brief explanation of what this query does and why it answers the user's question."
|
|
4846
4851
|
}
|
|
4847
4852
|
},
|
|
4848
|
-
required: ["
|
|
4853
|
+
required: ["sql"],
|
|
4849
4854
|
additionalProperties: false
|
|
4850
4855
|
}
|
|
4851
4856
|
}];
|
|
@@ -4940,14 +4945,18 @@ ${JSON.stringify(tool.requiredFields || [], null, 2)}`;
|
|
|
4940
4945
|
} : void 0;
|
|
4941
4946
|
const toolHandler = async (toolName, toolInput) => {
|
|
4942
4947
|
if (toolName === "execute_query") {
|
|
4943
|
-
let
|
|
4948
|
+
let sql = toolInput.sql;
|
|
4949
|
+
const params = toolInput.params || {};
|
|
4944
4950
|
const reasoning = toolInput.reasoning;
|
|
4945
4951
|
const { ensureQueryLimit: ensureQueryLimit2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
4946
|
-
|
|
4947
|
-
const queryKey =
|
|
4952
|
+
sql = ensureQueryLimit2(sql, 32, 32);
|
|
4953
|
+
const queryKey = sql.toLowerCase().replace(/\s+/g, " ").trim();
|
|
4948
4954
|
const attempts = (queryAttempts.get(queryKey) || 0) + 1;
|
|
4949
4955
|
queryAttempts.set(queryKey, attempts);
|
|
4950
|
-
logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${
|
|
4956
|
+
logger.info(`[${this.getProviderName()}] Executing query (attempt ${attempts}/${MAX_QUERY_ATTEMPTS}): ${sql.substring(0, 100)}...`);
|
|
4957
|
+
if (Object.keys(params).length > 0) {
|
|
4958
|
+
logger.info(`[${this.getProviderName()}] Query params: ${JSON.stringify(params)}`);
|
|
4959
|
+
}
|
|
4951
4960
|
if (reasoning) {
|
|
4952
4961
|
logCollector?.info(`Query reasoning: ${reasoning}`);
|
|
4953
4962
|
}
|
|
@@ -4969,6 +4978,8 @@ Please try rephrasing your question or simplifying your request.
|
|
|
4969
4978
|
}
|
|
4970
4979
|
try {
|
|
4971
4980
|
if (wrappedStreamCallback) {
|
|
4981
|
+
const paramsDisplay = Object.keys(params).length > 0 ? `
|
|
4982
|
+
**Parameters:** ${JSON.stringify(params)}` : "";
|
|
4972
4983
|
if (attempts === 1) {
|
|
4973
4984
|
wrappedStreamCallback(`
|
|
4974
4985
|
|
|
@@ -4982,8 +4993,8 @@ Please try rephrasing your question or simplifying your request.
|
|
|
4982
4993
|
}
|
|
4983
4994
|
wrappedStreamCallback(`\u{1F4DD} **Generated SQL Query:**
|
|
4984
4995
|
\`\`\`sql
|
|
4985
|
-
${
|
|
4986
|
-
|
|
4996
|
+
${sql}
|
|
4997
|
+
\`\`\`${paramsDisplay}
|
|
4987
4998
|
|
|
4988
4999
|
`);
|
|
4989
5000
|
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
@@ -5002,8 +5013,8 @@ ${query}
|
|
|
5002
5013
|
}
|
|
5003
5014
|
wrappedStreamCallback(`\u{1F4DD} **Corrected SQL Query:**
|
|
5004
5015
|
\`\`\`sql
|
|
5005
|
-
${
|
|
5006
|
-
|
|
5016
|
+
${sql}
|
|
5017
|
+
\`\`\`${paramsDisplay}
|
|
5007
5018
|
|
|
5008
5019
|
`);
|
|
5009
5020
|
wrappedStreamCallback(`\u26A1 **Executing query...**
|
|
@@ -5013,13 +5024,14 @@ ${query}
|
|
|
5013
5024
|
}
|
|
5014
5025
|
logCollector?.logQuery(
|
|
5015
5026
|
`Executing SQL query (attempt ${attempts})`,
|
|
5016
|
-
|
|
5027
|
+
{ sql, params },
|
|
5017
5028
|
{ reasoning, attempt: attempts }
|
|
5018
5029
|
);
|
|
5019
5030
|
if (!collections || !collections["database"] || !collections["database"]["execute"]) {
|
|
5020
5031
|
throw new Error("Database collection not registered. Please register database.execute collection to execute queries.");
|
|
5021
5032
|
}
|
|
5022
|
-
const
|
|
5033
|
+
const queryPayload = Object.keys(params).length > 0 ? { sql: JSON.stringify({ sql, values: params }) } : { sql };
|
|
5034
|
+
const result2 = await collections["database"]["execute"](queryPayload);
|
|
5023
5035
|
const data = result2?.data || result2;
|
|
5024
5036
|
const rowCount = result2?.count ?? (Array.isArray(data) ? data.length : "N/A");
|
|
5025
5037
|
logger.info(`[${this.getProviderName()}] Query executed successfully, rows returned: ${rowCount}`);
|
|
@@ -9781,6 +9793,7 @@ init_logger();
|
|
|
9781
9793
|
var SDK_VERSION = "0.0.8";
|
|
9782
9794
|
var DEFAULT_WS_URL = "wss://ws.superatom.ai/websocket";
|
|
9783
9795
|
var SuperatomSDK = class {
|
|
9796
|
+
// 3.5 minutes (PING_INTERVAL + 30s grace)
|
|
9784
9797
|
constructor(config) {
|
|
9785
9798
|
this.ws = null;
|
|
9786
9799
|
this.messageHandlers = /* @__PURE__ */ new Map();
|
|
@@ -9791,6 +9804,12 @@ var SuperatomSDK = class {
|
|
|
9791
9804
|
this.collections = {};
|
|
9792
9805
|
this.components = [];
|
|
9793
9806
|
this.tools = [];
|
|
9807
|
+
// Heartbeat properties for keeping WebSocket connection alive
|
|
9808
|
+
this.pingInterval = null;
|
|
9809
|
+
this.lastPong = Date.now();
|
|
9810
|
+
this.PING_INTERVAL_MS = 18e4;
|
|
9811
|
+
// 3 minutes
|
|
9812
|
+
this.PONG_TIMEOUT_MS = 21e4;
|
|
9794
9813
|
if (config.logLevel) {
|
|
9795
9814
|
logger.setLogLevel(config.logLevel);
|
|
9796
9815
|
}
|
|
@@ -9904,6 +9923,7 @@ var SuperatomSDK = class {
|
|
|
9904
9923
|
this.connected = true;
|
|
9905
9924
|
this.reconnectAttempts = 0;
|
|
9906
9925
|
logger.info("WebSocket connected successfully");
|
|
9926
|
+
this.startHeartbeat();
|
|
9907
9927
|
resolve();
|
|
9908
9928
|
});
|
|
9909
9929
|
this.ws.addEventListener("message", (event) => {
|
|
@@ -9932,6 +9952,9 @@ var SuperatomSDK = class {
|
|
|
9932
9952
|
const message = IncomingMessageSchema.parse(parsed);
|
|
9933
9953
|
logger.debug("Received message:", message.type);
|
|
9934
9954
|
switch (message.type) {
|
|
9955
|
+
case "PONG":
|
|
9956
|
+
this.handlePong();
|
|
9957
|
+
break;
|
|
9935
9958
|
case "DATA_REQ":
|
|
9936
9959
|
handleDataRequest(parsed, this.collections, (msg) => this.send(msg)).catch((error) => {
|
|
9937
9960
|
logger.error("Failed to handle data request:", error);
|
|
@@ -10054,6 +10077,7 @@ var SuperatomSDK = class {
|
|
|
10054
10077
|
* Disconnect from the WebSocket service
|
|
10055
10078
|
*/
|
|
10056
10079
|
disconnect() {
|
|
10080
|
+
this.stopHeartbeat();
|
|
10057
10081
|
if (this.ws) {
|
|
10058
10082
|
this.ws.close();
|
|
10059
10083
|
this.ws = null;
|
|
@@ -10064,6 +10088,7 @@ var SuperatomSDK = class {
|
|
|
10064
10088
|
* Cleanup and disconnect - stops reconnection attempts and closes the connection
|
|
10065
10089
|
*/
|
|
10066
10090
|
async destroy() {
|
|
10091
|
+
this.stopHeartbeat();
|
|
10067
10092
|
this.maxReconnectAttempts = 0;
|
|
10068
10093
|
this.reconnectAttempts = 0;
|
|
10069
10094
|
this.messageHandlers.clear();
|
|
@@ -10105,6 +10130,59 @@ var SuperatomSDK = class {
|
|
|
10105
10130
|
logger.error("Max reconnection attempts reached");
|
|
10106
10131
|
}
|
|
10107
10132
|
}
|
|
10133
|
+
/**
|
|
10134
|
+
* Start heartbeat to keep WebSocket connection alive
|
|
10135
|
+
* Sends PING every 3 minutes to prevent idle timeout from cloud infrastructure
|
|
10136
|
+
*/
|
|
10137
|
+
startHeartbeat() {
|
|
10138
|
+
this.stopHeartbeat();
|
|
10139
|
+
this.lastPong = Date.now();
|
|
10140
|
+
this.pingInterval = setInterval(() => {
|
|
10141
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
|
|
10142
|
+
logger.warn("WebSocket not open during heartbeat check, stopping heartbeat");
|
|
10143
|
+
this.stopHeartbeat();
|
|
10144
|
+
return;
|
|
10145
|
+
}
|
|
10146
|
+
const timeSinceLastPong = Date.now() - this.lastPong;
|
|
10147
|
+
if (timeSinceLastPong > this.PONG_TIMEOUT_MS) {
|
|
10148
|
+
logger.warn(`No PONG received for ${timeSinceLastPong}ms, connection may be dead. Reconnecting...`);
|
|
10149
|
+
this.stopHeartbeat();
|
|
10150
|
+
this.ws.close();
|
|
10151
|
+
return;
|
|
10152
|
+
}
|
|
10153
|
+
try {
|
|
10154
|
+
const pingMessage = {
|
|
10155
|
+
type: "PING",
|
|
10156
|
+
from: { type: this.type },
|
|
10157
|
+
payload: { timestamp: Date.now() }
|
|
10158
|
+
};
|
|
10159
|
+
this.ws.send(JSON.stringify(pingMessage));
|
|
10160
|
+
logger.debug("Heartbeat PING sent");
|
|
10161
|
+
} catch (error) {
|
|
10162
|
+
logger.error("Failed to send PING:", error);
|
|
10163
|
+
this.stopHeartbeat();
|
|
10164
|
+
this.ws.close();
|
|
10165
|
+
}
|
|
10166
|
+
}, this.PING_INTERVAL_MS);
|
|
10167
|
+
logger.info(`Heartbeat started (PING every ${this.PING_INTERVAL_MS / 1e3}s)`);
|
|
10168
|
+
}
|
|
10169
|
+
/**
|
|
10170
|
+
* Stop the heartbeat interval
|
|
10171
|
+
*/
|
|
10172
|
+
stopHeartbeat() {
|
|
10173
|
+
if (this.pingInterval) {
|
|
10174
|
+
clearInterval(this.pingInterval);
|
|
10175
|
+
this.pingInterval = null;
|
|
10176
|
+
logger.debug("Heartbeat stopped");
|
|
10177
|
+
}
|
|
10178
|
+
}
|
|
10179
|
+
/**
|
|
10180
|
+
* Handle PONG response from server
|
|
10181
|
+
*/
|
|
10182
|
+
handlePong() {
|
|
10183
|
+
this.lastPong = Date.now();
|
|
10184
|
+
logger.debug("Heartbeat PONG received");
|
|
10185
|
+
}
|
|
10108
10186
|
storeComponents(components) {
|
|
10109
10187
|
this.components = components;
|
|
10110
10188
|
}
|