botversion-sdk 1.0.3 → 1.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/bin/init.js +184 -135
- package/cli/detector.js +360 -173
- package/cli/generator.js +18 -476
- package/cli/prompts.js +40 -42
- package/cli/writer.js +118 -42
- package/client.js +214 -36
- package/index.js +74 -391
- package/interceptor.js +112 -124
- package/package.json +3 -2
- package/scanner.js +792 -27
package/cli/writer.js
CHANGED
|
@@ -30,6 +30,11 @@ function injectBeforeListen(filePath, codeToInject, appVarName) {
|
|
|
30
30
|
const lines = content.split("\n");
|
|
31
31
|
const listenRegex = new RegExp(`${appVarName}\\.listen\\s*\\(`);
|
|
32
32
|
|
|
33
|
+
// Check if BotVersion already exists in file
|
|
34
|
+
if (content.includes("botversion-sdk") || content.includes("BotVersion")) {
|
|
35
|
+
return { success: false, reason: "already_exists" };
|
|
36
|
+
}
|
|
37
|
+
|
|
33
38
|
let listenLineIndex = -1;
|
|
34
39
|
for (let i = 0; i < lines.length; i++) {
|
|
35
40
|
if (listenRegex.test(lines[i])) {
|
|
@@ -39,7 +44,6 @@ function injectBeforeListen(filePath, codeToInject, appVarName) {
|
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
if (listenLineIndex === -1) {
|
|
42
|
-
// app.listen not found — append to end of file
|
|
43
47
|
return {
|
|
44
48
|
success: false,
|
|
45
49
|
reason: "no_listen",
|
|
@@ -47,22 +51,64 @@ function injectBeforeListen(filePath, codeToInject, appVarName) {
|
|
|
47
51
|
};
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
// Check if
|
|
51
|
-
if
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
// ── NEW: Check if app.listen() is inside if (require.main === module) ──
|
|
55
|
+
// If so, inject before that if block instead of inside it
|
|
56
|
+
let insertIndex = listenLineIndex;
|
|
57
|
+
|
|
58
|
+
for (let i = listenLineIndex; i >= 0; i--) {
|
|
59
|
+
const trimmed = lines[i].trim();
|
|
60
|
+
|
|
61
|
+
// Found if (require.main === module) block
|
|
62
|
+
if (
|
|
63
|
+
/if\s*\(\s*require\.main\s*===?\s*module\s*\)/.test(trimmed) ||
|
|
64
|
+
/if\s*\(\s*module\s*===?\s*require\.main\s*\)/.test(trimmed)
|
|
65
|
+
) {
|
|
66
|
+
insertIndex = i;
|
|
67
|
+
// Keep walking back to check for 404/error handlers before this
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
54
70
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
// Found a 404 handler — inject before this instead
|
|
72
|
+
if (
|
|
73
|
+
/app\.use\s*\(\s*\(req,\s*res\)\s*=>/.test(trimmed) ||
|
|
74
|
+
/app\.use\s*\(\s*function\s*\(req,\s*res\)/.test(trimmed)
|
|
75
|
+
) {
|
|
76
|
+
insertIndex = i;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
58
79
|
|
|
59
|
-
|
|
80
|
+
// Found a global error handler — inject before this instead
|
|
81
|
+
if (
|
|
82
|
+
/app\.use\s*\(\s*\(err,\s*req,\s*res,\s*next\)/.test(trimmed) ||
|
|
83
|
+
/app\.use\s*\(\s*function\s*\(err,\s*req,\s*res,\s*next\)/.test(trimmed)
|
|
84
|
+
) {
|
|
85
|
+
insertIndex = i;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
60
88
|
|
|
89
|
+
// Stop walking back if we hit a top-level non-empty statement
|
|
90
|
+
if (
|
|
91
|
+
i < listenLineIndex &&
|
|
92
|
+
trimmed !== "" &&
|
|
93
|
+
trimmed !== "}" &&
|
|
94
|
+
trimmed !== "})" &&
|
|
95
|
+
trimmed !== "});" &&
|
|
96
|
+
lines[i].match(/^[^\s]/)
|
|
97
|
+
) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Insert the code block at the correct position
|
|
103
|
+
const before = lines.slice(0, insertIndex);
|
|
104
|
+
const after = lines.slice(insertIndex);
|
|
105
|
+
const injectedLines = ["", ...codeToInject.split("\n"), ""];
|
|
61
106
|
const newContent = [...before, ...injectedLines, ...after].join("\n");
|
|
107
|
+
|
|
62
108
|
const backup = backupFile(filePath);
|
|
63
109
|
fs.writeFileSync(filePath, newContent, "utf8");
|
|
64
110
|
|
|
65
|
-
return { success: true, lineNumber:
|
|
111
|
+
return { success: true, lineNumber: insertIndex + 1, backup };
|
|
66
112
|
}
|
|
67
113
|
|
|
68
114
|
// ─── APPEND CODE TO END OF FILE ──────────────────────────────────────────────
|
|
@@ -175,30 +221,6 @@ function injectBeforeExport(filePath, codeToInject, appVarName) {
|
|
|
175
221
|
return { success: true, backup };
|
|
176
222
|
}
|
|
177
223
|
|
|
178
|
-
// ─── MERGE INTO EXISTING MIDDLEWARE (Next.js) ────────────────────────────────
|
|
179
|
-
|
|
180
|
-
function mergeIntoMiddleware(middlewarePath, authName) {
|
|
181
|
-
const content = fs.readFileSync(middlewarePath, "utf8");
|
|
182
|
-
|
|
183
|
-
// If BotVersion already there, skip
|
|
184
|
-
if (content.includes("botversion")) {
|
|
185
|
-
return { success: false, reason: "already_exists" };
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// We don't auto-modify complex middleware — too risky
|
|
189
|
-
// Instead return a comment to add manually
|
|
190
|
-
const comment = `
|
|
191
|
-
// BotVersion: No changes needed to middleware.
|
|
192
|
-
// Your auth (${authName}) is handled in pages/api/botversion/chat.js
|
|
193
|
-
`;
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
success: true,
|
|
197
|
-
noModification: true,
|
|
198
|
-
note: comment.trim(),
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
224
|
// ─── WRITE SUMMARY OF ALL CHANGES ────────────────────────────────────────────
|
|
203
225
|
|
|
204
226
|
function writeSummary(changes) {
|
|
@@ -212,25 +234,25 @@ function writeSummary(changes) {
|
|
|
212
234
|
|
|
213
235
|
if (changes.modified && changes.modified.length > 0) {
|
|
214
236
|
lines.push(" Modified files:");
|
|
215
|
-
changes.modified.forEach((f) => lines.push(`
|
|
237
|
+
changes.modified.forEach((f) => lines.push(` [Modified] ${f}`));
|
|
216
238
|
lines.push("");
|
|
217
239
|
}
|
|
218
240
|
|
|
219
241
|
if (changes.created && changes.created.length > 0) {
|
|
220
242
|
lines.push(" Created files:");
|
|
221
|
-
changes.created.forEach((f) => lines.push(`
|
|
243
|
+
changes.created.forEach((f) => lines.push(` [Created] ${f}`));
|
|
222
244
|
lines.push("");
|
|
223
245
|
}
|
|
224
246
|
|
|
225
247
|
if (changes.backups && changes.backups.length > 0) {
|
|
226
248
|
lines.push(" Backups created:");
|
|
227
|
-
changes.backups.forEach((f) => lines.push(`
|
|
249
|
+
changes.backups.forEach((f) => lines.push(` [Backup] ${f}`));
|
|
228
250
|
lines.push("");
|
|
229
251
|
}
|
|
230
252
|
|
|
231
253
|
if (changes.manual && changes.manual.length > 0) {
|
|
232
|
-
lines.push("
|
|
233
|
-
changes.manual.forEach((m) => lines.push(`
|
|
254
|
+
lines.push(" Manual steps needed:");
|
|
255
|
+
changes.manual.forEach((m) => lines.push(` [Manual] ${m}`));
|
|
234
256
|
lines.push("");
|
|
235
257
|
}
|
|
236
258
|
|
|
@@ -305,7 +327,6 @@ function injectIntoNextApp(filePath, content, scriptTag, backup) {
|
|
|
305
327
|
data-api-url="${extractAttr(scriptTag, "data-api-url")}"
|
|
306
328
|
data-project-id="${extractAttr(scriptTag, "data-project-id")}"
|
|
307
329
|
data-public-key="${extractAttr(scriptTag, "data-public-key")}"
|
|
308
|
-
data-proxy-url="/api/botversion/chat"
|
|
309
330
|
strategy="afterInteractive"
|
|
310
331
|
/>`;
|
|
311
332
|
|
|
@@ -393,7 +414,6 @@ function injectIntoNextLayout(filePath, content, scriptTag, backup) {
|
|
|
393
414
|
data-api-url="${extractAttr(scriptTag, "data-api-url")}"
|
|
394
415
|
data-project-id="${extractAttr(scriptTag, "data-project-id")}"
|
|
395
416
|
data-public-key="${extractAttr(scriptTag, "data-public-key")}"
|
|
396
|
-
data-proxy-url="/api/botversion/chat"
|
|
397
417
|
strategy="afterInteractive"
|
|
398
418
|
/>`;
|
|
399
419
|
|
|
@@ -415,6 +435,19 @@ function injectIntoNextLayout(filePath, content, scriptTag, backup) {
|
|
|
415
435
|
return { success: true, backup };
|
|
416
436
|
}
|
|
417
437
|
|
|
438
|
+
function injectAtTop(filePath, codeToInject) {
|
|
439
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
440
|
+
|
|
441
|
+
if (content.includes("dotenv")) {
|
|
442
|
+
return { success: false, reason: "already_exists" };
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const newContent = codeToInject + "\n" + content;
|
|
446
|
+
backupFile(filePath);
|
|
447
|
+
fs.writeFileSync(filePath, newContent, "utf8");
|
|
448
|
+
return { success: true };
|
|
449
|
+
}
|
|
450
|
+
|
|
418
451
|
// ─── HELPER: extract attribute value from script tag string ──────────────────
|
|
419
452
|
|
|
420
453
|
function extractAttr(scriptTag, attr) {
|
|
@@ -422,14 +455,57 @@ function extractAttr(scriptTag, attr) {
|
|
|
422
455
|
return match ? match[1] : "";
|
|
423
456
|
}
|
|
424
457
|
|
|
458
|
+
// ─── WRITE API KEY TO .ENV FILE ───────────────────────────────────────────────
|
|
459
|
+
|
|
460
|
+
function writeEnvKey(cwd, key, value, framework) {
|
|
461
|
+
const envCandidates =
|
|
462
|
+
framework === "next"
|
|
463
|
+
? [".env.local", ".env", ".env.development", ".env.development.local"]
|
|
464
|
+
: [".env", ".env.development", ".env.local"];
|
|
465
|
+
|
|
466
|
+
let envPath = null;
|
|
467
|
+
|
|
468
|
+
// Use existing .env file if found
|
|
469
|
+
for (const candidate of envCandidates) {
|
|
470
|
+
const fullPath = path.join(cwd, candidate);
|
|
471
|
+
if (fs.existsSync(fullPath)) {
|
|
472
|
+
envPath = fullPath;
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// No .env file found — create .env
|
|
478
|
+
if (!envPath) {
|
|
479
|
+
envPath = path.join(cwd, ".env");
|
|
480
|
+
fs.writeFileSync(envPath, "", "utf8");
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const content = fs.readFileSync(envPath, "utf8");
|
|
484
|
+
|
|
485
|
+
// Key already exists — skip
|
|
486
|
+
if (content.includes(key)) {
|
|
487
|
+
return { success: false, reason: "already_exists", path: envPath };
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Append key to file
|
|
491
|
+
const newLine =
|
|
492
|
+
content === "" || content.endsWith("\n")
|
|
493
|
+
? `${key}=${value}\n`
|
|
494
|
+
: `\n${key}=${value}\n`;
|
|
495
|
+
|
|
496
|
+
fs.appendFileSync(envPath, newLine, "utf8");
|
|
497
|
+
return { success: true, path: envPath };
|
|
498
|
+
}
|
|
499
|
+
|
|
425
500
|
module.exports = {
|
|
426
501
|
writeFile,
|
|
427
502
|
backupFile,
|
|
428
503
|
injectBeforeListen,
|
|
429
504
|
appendToFile,
|
|
430
505
|
createFile,
|
|
431
|
-
mergeIntoMiddleware,
|
|
432
506
|
writeSummary,
|
|
433
507
|
injectBeforeExport,
|
|
434
508
|
injectScriptTag,
|
|
509
|
+
injectAtTop,
|
|
510
|
+
writeEnvKey,
|
|
435
511
|
};
|
package/client.js
CHANGED
|
@@ -11,7 +11,6 @@ const url = require("url");
|
|
|
11
11
|
*/
|
|
12
12
|
function BotVersionClient(options) {
|
|
13
13
|
this.apiKey = options.apiKey;
|
|
14
|
-
this.chatbotId = options.chatbotId;
|
|
15
14
|
this.platformUrl = options.platformUrl || "http://localhost:3000";
|
|
16
15
|
this.debug = options.debug || false;
|
|
17
16
|
this.timeout = options.timeout || 30000;
|
|
@@ -19,13 +18,26 @@ function BotVersionClient(options) {
|
|
|
19
18
|
// Batch queue for endpoint registration
|
|
20
19
|
this._queue = [];
|
|
21
20
|
this._flushTimer = null;
|
|
22
|
-
this._flushDelay = options.flushDelay || 3000;
|
|
21
|
+
this._flushDelay = options.flushDelay || 3000;
|
|
22
|
+
this._ws = null;
|
|
23
|
+
this._executor = null;
|
|
23
24
|
var self = this;
|
|
24
25
|
process.on("beforeExit", function () {
|
|
25
26
|
if (self._queue.length > 0) self._flush();
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Register the executor function — called by interceptor
|
|
32
|
+
* So WebSocket can forward calls to user's backend
|
|
33
|
+
*/
|
|
34
|
+
BotVersionClient.prototype.setExecutor = function (executorFn) {
|
|
35
|
+
this._executor = executorFn;
|
|
36
|
+
if (this.debug) {
|
|
37
|
+
console.log("[BotVersion SDK] ✅ Executor registered");
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
29
41
|
/**
|
|
30
42
|
* Register multiple endpoints at once (batched)
|
|
31
43
|
*/
|
|
@@ -42,10 +54,8 @@ BotVersionClient.prototype.registerEndpoints = function (endpoints) {
|
|
|
42
54
|
);
|
|
43
55
|
}
|
|
44
56
|
|
|
45
|
-
// Add to queue
|
|
46
57
|
self._queue = self._queue.concat(endpoints);
|
|
47
58
|
|
|
48
|
-
// Schedule flush
|
|
49
59
|
if (!self._flushTimer) {
|
|
50
60
|
self._flushTimer = setTimeout(function () {
|
|
51
61
|
self._flush();
|
|
@@ -55,6 +65,37 @@ BotVersionClient.prototype.registerEndpoints = function (endpoints) {
|
|
|
55
65
|
return Promise.resolve();
|
|
56
66
|
};
|
|
57
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Register endpoints immediately — no batching
|
|
70
|
+
* Used during initial scan
|
|
71
|
+
*/
|
|
72
|
+
BotVersionClient.prototype.registerEndpointsNow = function (endpoints) {
|
|
73
|
+
var self = this;
|
|
74
|
+
if (!endpoints || endpoints.length === 0) return Promise.resolve();
|
|
75
|
+
|
|
76
|
+
return self
|
|
77
|
+
._post("/api/sdk/register-endpoints", {
|
|
78
|
+
workspaceKey: self.apiKey,
|
|
79
|
+
endpoints: endpoints,
|
|
80
|
+
})
|
|
81
|
+
.then(function (data) {
|
|
82
|
+
if (self.debug) {
|
|
83
|
+
console.log(
|
|
84
|
+
"[BotVersion SDK] ✅ Registered",
|
|
85
|
+
endpoints.length,
|
|
86
|
+
"endpoints immediately",
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return data;
|
|
90
|
+
})
|
|
91
|
+
.catch(function (err) {
|
|
92
|
+
console.warn(
|
|
93
|
+
"[BotVersion SDK] ⚠ Failed to register endpoints:",
|
|
94
|
+
err.message,
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
58
99
|
/**
|
|
59
100
|
* Flush the queue — send all batched endpoints at once
|
|
60
101
|
*/
|
|
@@ -115,6 +156,33 @@ BotVersionClient.prototype.updateEndpoint = function (endpoint) {
|
|
|
115
156
|
});
|
|
116
157
|
};
|
|
117
158
|
|
|
159
|
+
BotVersionClient.prototype.registerRoutePatterns = function (patterns) {
|
|
160
|
+
var self = this;
|
|
161
|
+
if (!patterns || patterns.length === 0) return Promise.resolve();
|
|
162
|
+
|
|
163
|
+
if (self.debug) {
|
|
164
|
+
console.log(
|
|
165
|
+
"[BotVersion SDK] Sending",
|
|
166
|
+
patterns.length,
|
|
167
|
+
"route patterns to platform",
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return self
|
|
172
|
+
._post("/api/sdk/register-route-patterns", {
|
|
173
|
+
workspaceKey: self.apiKey,
|
|
174
|
+
patterns: patterns,
|
|
175
|
+
})
|
|
176
|
+
.catch(function (err) {
|
|
177
|
+
if (self.debug) {
|
|
178
|
+
console.warn(
|
|
179
|
+
"[BotVersion SDK] Failed to register route patterns:",
|
|
180
|
+
err.message,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
|
|
118
186
|
/**
|
|
119
187
|
* Get all registered endpoints for this workspace
|
|
120
188
|
*/
|
|
@@ -126,37 +194,6 @@ BotVersionClient.prototype.getEndpoints = function () {
|
|
|
126
194
|
);
|
|
127
195
|
};
|
|
128
196
|
|
|
129
|
-
// Send user message to agent
|
|
130
|
-
BotVersionClient.prototype.agentChat = function (payload) {
|
|
131
|
-
console.log("[BotVersion] agentChat payload:", JSON.stringify(payload));
|
|
132
|
-
console.log("[BotVersion] this.chatbotId:", this.chatbotId);
|
|
133
|
-
console.log("[BotVersion] this.apiKey:", this.apiKey);
|
|
134
|
-
return this._post(
|
|
135
|
-
"/api/chatbot/widget-chat",
|
|
136
|
-
Object.assign({
|
|
137
|
-
chatbotId: payload.chatbotId, // ← use payload, not this.chatbotId
|
|
138
|
-
publicKey: payload.publicKey,
|
|
139
|
-
query: payload.message,
|
|
140
|
-
previousChats: payload.conversationHistory,
|
|
141
|
-
pageContext: payload.pageContext,
|
|
142
|
-
userContext: payload.userContext,
|
|
143
|
-
}),
|
|
144
|
-
);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// Send API call result back to agent
|
|
148
|
-
BotVersionClient.prototype.agentToolResult = function (
|
|
149
|
-
sessionToken,
|
|
150
|
-
result,
|
|
151
|
-
sessionData,
|
|
152
|
-
) {
|
|
153
|
-
return this._post("/api/sdk/agent-tool-result", {
|
|
154
|
-
sessionToken,
|
|
155
|
-
sessionData: sessionData || null,
|
|
156
|
-
result,
|
|
157
|
-
});
|
|
158
|
-
};
|
|
159
|
-
|
|
160
197
|
/**
|
|
161
198
|
* Make a POST request to the platform
|
|
162
199
|
*/
|
|
@@ -176,7 +213,7 @@ BotVersionClient.prototype._post = function (path, data) {
|
|
|
176
213
|
method: "POST",
|
|
177
214
|
headers: {
|
|
178
215
|
"Content-Type": "application/json",
|
|
179
|
-
"Content-Length": Buffer.byteLength(body),
|
|
216
|
+
"Content-Length": String(Buffer.byteLength(body)),
|
|
180
217
|
"X-BotVersion-SDK": "1.0.0",
|
|
181
218
|
},
|
|
182
219
|
timeout: self.timeout,
|
|
@@ -287,4 +324,145 @@ BotVersionClient.prototype._get = function (path) {
|
|
|
287
324
|
});
|
|
288
325
|
};
|
|
289
326
|
|
|
327
|
+
/**
|
|
328
|
+
* Connect to platform via WebSocket — establishes persistent connection
|
|
329
|
+
* Auto-reconnects every 5 seconds if connection drops
|
|
330
|
+
*/
|
|
331
|
+
BotVersionClient.prototype.connect = function () {
|
|
332
|
+
var self = this;
|
|
333
|
+
// Run in next tick so it doesn't block initialization
|
|
334
|
+
setImmediate(function () {
|
|
335
|
+
self._wsLoop();
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
BotVersionClient.prototype._wsLoop = function () {
|
|
340
|
+
var self = this;
|
|
341
|
+
|
|
342
|
+
var wsUrl = self.platformUrl
|
|
343
|
+
.replace("https://", "wss://")
|
|
344
|
+
.replace("http://", "ws://")
|
|
345
|
+
.replace(":3000", ":3001");
|
|
346
|
+
wsUrl = wsUrl + "?apiKey=" + encodeURIComponent(self.apiKey);
|
|
347
|
+
|
|
348
|
+
var WebSocket;
|
|
349
|
+
try {
|
|
350
|
+
WebSocket = require("ws");
|
|
351
|
+
} catch (e) {
|
|
352
|
+
console.error(
|
|
353
|
+
"[BotVersion SDK] ❌ 'ws' package not installed. Run: npm install ws",
|
|
354
|
+
);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function attempt() {
|
|
359
|
+
var ws = new WebSocket(wsUrl);
|
|
360
|
+
self._ws = ws;
|
|
361
|
+
|
|
362
|
+
ws.on("open", function () {
|
|
363
|
+
if (self.debug) {
|
|
364
|
+
console.log("[BotVersion SDK] ✅ WebSocket connected to platform");
|
|
365
|
+
}
|
|
366
|
+
// Identify this SDK to the platform
|
|
367
|
+
ws.send(
|
|
368
|
+
JSON.stringify({
|
|
369
|
+
type: "IDENTIFY",
|
|
370
|
+
apiKey: self.apiKey,
|
|
371
|
+
}),
|
|
372
|
+
);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
ws.on("message", function (data) {
|
|
376
|
+
try {
|
|
377
|
+
var msg = JSON.parse(data.toString());
|
|
378
|
+
|
|
379
|
+
if (msg.type === "EXECUTE_CALL") {
|
|
380
|
+
self._handleExecuteCall(msg);
|
|
381
|
+
}
|
|
382
|
+
} catch (e) {
|
|
383
|
+
if (self.debug) {
|
|
384
|
+
console.warn("[BotVersion SDK] ⚠ Error parsing message:", e.message);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
ws.on("error", function (err) {
|
|
390
|
+
if (self.debug) {
|
|
391
|
+
console.warn("[BotVersion SDK] ⚠ WebSocket error:", err.message);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
ws.on("close", function () {
|
|
396
|
+
if (self.debug) {
|
|
397
|
+
console.log(
|
|
398
|
+
"[BotVersion SDK] WebSocket closed — reconnecting in 5s...",
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
self._ws = null;
|
|
402
|
+
// Auto-reconnect after 5 seconds
|
|
403
|
+
setTimeout(attempt, 5000);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
attempt();
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
BotVersionClient.prototype._handleExecuteCall = function (data) {
|
|
411
|
+
var self = this;
|
|
412
|
+
|
|
413
|
+
var callId = data.callId;
|
|
414
|
+
var method = data.method;
|
|
415
|
+
var path = data.path;
|
|
416
|
+
var body = data.body;
|
|
417
|
+
var cookies = data.cookies || "";
|
|
418
|
+
var headers = data.headers || {};
|
|
419
|
+
var baseUrl = data.baseUrl || "http://127.0.0.1:3000";
|
|
420
|
+
|
|
421
|
+
if (!self._executor) {
|
|
422
|
+
self._sendCallResult(callId, {
|
|
423
|
+
status: 500,
|
|
424
|
+
ok: false,
|
|
425
|
+
data: { error: "No executor registered" },
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
self
|
|
431
|
+
._executor(method, path, body, cookies, headers, baseUrl)
|
|
432
|
+
.then(function (result) {
|
|
433
|
+
self._sendCallResult(callId, result);
|
|
434
|
+
})
|
|
435
|
+
.catch(function (err) {
|
|
436
|
+
self._sendCallResult(callId, {
|
|
437
|
+
status: 500,
|
|
438
|
+
ok: false,
|
|
439
|
+
data: { error: err.message },
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
BotVersionClient.prototype._sendCallResult = function (callId, result) {
|
|
445
|
+
var self = this;
|
|
446
|
+
try {
|
|
447
|
+
if (self._ws && self._ws.readyState === 1) {
|
|
448
|
+
// 1 = OPEN
|
|
449
|
+
self._ws.send(
|
|
450
|
+
JSON.stringify({
|
|
451
|
+
type: "CALL_RESULT",
|
|
452
|
+
callId: callId,
|
|
453
|
+
result: result,
|
|
454
|
+
}),
|
|
455
|
+
);
|
|
456
|
+
} else if (self.debug) {
|
|
457
|
+
console.warn(
|
|
458
|
+
"[BotVersion SDK] ⚠ WebSocket not open — could not send CALL_RESULT",
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
} catch (e) {
|
|
462
|
+
if (self.debug) {
|
|
463
|
+
console.warn("[BotVersion SDK] ⚠ Failed to send CALL_RESULT:", e.message);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
290
468
|
module.exports = BotVersionClient;
|