botversion-sdk 1.0.5 → 1.1.6
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 +3 -3
- package/cli/generator.js +3 -3
- package/cli/writer.js +1 -1
- package/client.js +38 -223
- package/index.js +285 -211
- package/interceptor.js +451 -118
- package/package.json +4 -3
- package/scanner.js +1281 -100
package/bin/init.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
// botversion-sdk/bin/init.js
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
@@ -102,7 +102,7 @@ async function main() {
|
|
|
102
102
|
if (!args.key) {
|
|
103
103
|
error("API key is required.");
|
|
104
104
|
log(`\n Usage: npx botversion-sdk init --key YOUR_WORKSPACE_KEY\n`);
|
|
105
|
-
log(` Get your key from: https://
|
|
105
|
+
log(` Get your key from: https://botversion.com/settings\n`);
|
|
106
106
|
process.exit(1);
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -113,7 +113,7 @@ async function main() {
|
|
|
113
113
|
step("Fetching project info from platform...");
|
|
114
114
|
let projectInfo;
|
|
115
115
|
try {
|
|
116
|
-
projectInfo = await fetchProjectInfo(args.key, "
|
|
116
|
+
projectInfo = await fetchProjectInfo(args.key, "https://botversion.com");
|
|
117
117
|
success(`Project found — ID: ${projectInfo.projectId}`);
|
|
118
118
|
} catch (err) {
|
|
119
119
|
error(err.message);
|
package/cli/generator.js
CHANGED
|
@@ -149,11 +149,11 @@ Fastify support is coming soon. For now, add this manually:
|
|
|
149
149
|
`,
|
|
150
150
|
koa: `
|
|
151
151
|
Koa support is coming soon. For now, add this manually:
|
|
152
|
-
See: https://
|
|
152
|
+
See: https://botversion.com/docs
|
|
153
153
|
`,
|
|
154
154
|
"@nestjs/core": `
|
|
155
155
|
NestJS support is coming soon. For now, add this manually:
|
|
156
|
-
See: https://
|
|
156
|
+
See: https://botversion.com/docs
|
|
157
157
|
`,
|
|
158
158
|
};
|
|
159
159
|
|
|
@@ -161,7 +161,7 @@ NestJS support is coming soon. For now, add this manually:
|
|
|
161
161
|
instructions[framework] ||
|
|
162
162
|
`
|
|
163
163
|
This framework is not yet supported automatically.
|
|
164
|
-
Visit https://
|
|
164
|
+
Visit https://botversion.com/docs for manual setup instructions.
|
|
165
165
|
`
|
|
166
166
|
);
|
|
167
167
|
}
|
package/cli/writer.js
CHANGED
|
@@ -257,7 +257,7 @@ function writeSummary(changes) {
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
lines.push(" Next: Restart your server and test the chat widget.");
|
|
260
|
-
lines.push(" Docs: https://
|
|
260
|
+
lines.push(" Docs: https://botversion.com/docs");
|
|
261
261
|
lines.push("");
|
|
262
262
|
|
|
263
263
|
return lines.join("\n");
|
package/client.js
CHANGED
|
@@ -11,7 +11,7 @@ const url = require("url");
|
|
|
11
11
|
*/
|
|
12
12
|
function BotVersionClient(options) {
|
|
13
13
|
this.apiKey = options.apiKey;
|
|
14
|
-
this.platformUrl = options.platformUrl || "
|
|
14
|
+
this.platformUrl = options.platformUrl || "https://botversion.com";
|
|
15
15
|
this.debug = options.debug || false;
|
|
16
16
|
this.timeout = options.timeout || 30000;
|
|
17
17
|
|
|
@@ -19,25 +19,12 @@ function BotVersionClient(options) {
|
|
|
19
19
|
this._queue = [];
|
|
20
20
|
this._flushTimer = null;
|
|
21
21
|
this._flushDelay = options.flushDelay || 3000;
|
|
22
|
-
this._ws = null;
|
|
23
|
-
this._executor = null;
|
|
24
22
|
var self = this;
|
|
25
23
|
process.on("beforeExit", function () {
|
|
26
24
|
if (self._queue.length > 0) self._flush();
|
|
27
25
|
});
|
|
28
26
|
}
|
|
29
27
|
|
|
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
|
-
|
|
41
28
|
/**
|
|
42
29
|
* Register multiple endpoints at once (batched)
|
|
43
30
|
*/
|
|
@@ -46,14 +33,6 @@ BotVersionClient.prototype.registerEndpoints = function (endpoints) {
|
|
|
46
33
|
|
|
47
34
|
if (!endpoints || endpoints.length === 0) return Promise.resolve();
|
|
48
35
|
|
|
49
|
-
if (self.debug) {
|
|
50
|
-
console.log(
|
|
51
|
-
"[BotVersion SDK] Queuing",
|
|
52
|
-
endpoints.length,
|
|
53
|
-
"endpoints for registration",
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
36
|
self._queue = self._queue.concat(endpoints);
|
|
58
37
|
|
|
59
38
|
if (!self._flushTimer) {
|
|
@@ -79,20 +58,7 @@ BotVersionClient.prototype.registerEndpointsNow = function (endpoints) {
|
|
|
79
58
|
endpoints: endpoints,
|
|
80
59
|
})
|
|
81
60
|
.then(function (data) {
|
|
82
|
-
if (self.debug) {
|
|
83
|
-
console.log(
|
|
84
|
-
"[BotVersion SDK] ✅ Registered",
|
|
85
|
-
endpoints.length,
|
|
86
|
-
"endpoints immediately",
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
61
|
return data;
|
|
90
|
-
})
|
|
91
|
-
.catch(function (err) {
|
|
92
|
-
console.warn(
|
|
93
|
-
"[BotVersion SDK] ⚠ Failed to register endpoints:",
|
|
94
|
-
err.message,
|
|
95
|
-
);
|
|
96
62
|
});
|
|
97
63
|
};
|
|
98
64
|
|
|
@@ -108,36 +74,13 @@ BotVersionClient.prototype._flush = function () {
|
|
|
108
74
|
var toSend = self._queue.slice();
|
|
109
75
|
self._queue = [];
|
|
110
76
|
|
|
111
|
-
if (self.debug) {
|
|
112
|
-
console.log(
|
|
113
|
-
"[BotVersion SDK] Flushing",
|
|
114
|
-
toSend.length,
|
|
115
|
-
"endpoints to platform",
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
77
|
self
|
|
120
78
|
._post("/api/sdk/register-endpoints", {
|
|
121
79
|
workspaceKey: self.apiKey,
|
|
122
80
|
endpoints: toSend,
|
|
123
81
|
})
|
|
124
|
-
.then(function (data) {
|
|
125
|
-
|
|
126
|
-
console.log(
|
|
127
|
-
"[BotVersion SDK] Registered",
|
|
128
|
-
data.succeeded,
|
|
129
|
-
"endpoints successfully",
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
.catch(function (err) {
|
|
134
|
-
if (self.debug) {
|
|
135
|
-
console.warn(
|
|
136
|
-
"[BotVersion SDK] Failed to register endpoints:",
|
|
137
|
-
err.message,
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
82
|
+
.then(function (data) {})
|
|
83
|
+
.catch(function (err) {});
|
|
141
84
|
};
|
|
142
85
|
|
|
143
86
|
/**
|
|
@@ -160,27 +103,10 @@ BotVersionClient.prototype.registerRoutePatterns = function (patterns) {
|
|
|
160
103
|
var self = this;
|
|
161
104
|
if (!patterns || patterns.length === 0) return Promise.resolve();
|
|
162
105
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
});
|
|
106
|
+
return self._post("/api/sdk/register-route-patterns", {
|
|
107
|
+
workspaceKey: self.apiKey,
|
|
108
|
+
patterns: patterns,
|
|
109
|
+
});
|
|
184
110
|
};
|
|
185
111
|
|
|
186
112
|
/**
|
|
@@ -228,6 +154,19 @@ BotVersionClient.prototype._post = function (path, data) {
|
|
|
228
154
|
|
|
229
155
|
res.on("end", function () {
|
|
230
156
|
try {
|
|
157
|
+
if (!responseData || responseData.trim() === "") {
|
|
158
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
159
|
+
resolve({});
|
|
160
|
+
return;
|
|
161
|
+
} else {
|
|
162
|
+
reject(
|
|
163
|
+
new Error(
|
|
164
|
+
"Platform returned " + res.statusCode + ": empty response",
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
231
170
|
var parsed = JSON.parse(responseData);
|
|
232
171
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
233
172
|
resolve(parsed);
|
|
@@ -292,6 +231,19 @@ BotVersionClient.prototype._get = function (path) {
|
|
|
292
231
|
|
|
293
232
|
res.on("end", function () {
|
|
294
233
|
try {
|
|
234
|
+
if (!responseData || responseData.trim() === "") {
|
|
235
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
236
|
+
resolve({});
|
|
237
|
+
return;
|
|
238
|
+
} else {
|
|
239
|
+
reject(
|
|
240
|
+
new Error(
|
|
241
|
+
"Platform returned " + res.statusCode + ": empty response",
|
|
242
|
+
),
|
|
243
|
+
);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
295
247
|
var parsed = JSON.parse(responseData);
|
|
296
248
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
297
249
|
resolve(parsed);
|
|
@@ -306,7 +258,11 @@ BotVersionClient.prototype._get = function (path) {
|
|
|
306
258
|
);
|
|
307
259
|
}
|
|
308
260
|
} catch (e) {
|
|
309
|
-
|
|
261
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
262
|
+
resolve({});
|
|
263
|
+
} else {
|
|
264
|
+
reject(new Error("Invalid JSON response from platform"));
|
|
265
|
+
}
|
|
310
266
|
}
|
|
311
267
|
});
|
|
312
268
|
});
|
|
@@ -324,145 +280,4 @@ BotVersionClient.prototype._get = function (path) {
|
|
|
324
280
|
});
|
|
325
281
|
};
|
|
326
282
|
|
|
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
|
-
|
|
468
283
|
module.exports = BotVersionClient;
|