botversion-sdk 1.0.4 → 1.0.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/cli/generator.js CHANGED
@@ -7,510 +7,69 @@ const fs = require("fs");
7
7
  // ─── EXPRESS CODE GENERATION ──────────────────────────────────────────────────
8
8
 
9
9
  function generateExpressInit(info, apiKey) {
10
- const { moduleSystem, isTypeScript, auth } = info;
10
+ const { moduleSystem, isTypeScript } = info;
11
11
  const isESM = moduleSystem === "esm";
12
12
 
13
- // FIX #2: Detect the actual app variable name used in the entry file
14
13
  const appVarName = info.appVarName || "app";
15
14
 
16
15
  const importLine = isESM
17
16
  ? `import BotVersion from 'botversion-sdk';`
18
17
  : `const BotVersion = require('botversion-sdk');`;
19
18
 
20
- const getUserContext = generateExpressUserContext(auth);
19
+ const apiKeyValue = `process.env.BOTVERSION_API_KEY`;
21
20
 
22
21
  const initBlock = `
23
22
  // BotVersion AI Agent — auto-added by botversion-sdk init
24
23
  ${importLine}
25
24
 
26
25
  BotVersion.init(${appVarName}, {
27
- apiKey: process.env.BOTVERSION_API_KEY,
28
- });
29
-
30
- ${appVarName}.post('/api/botversion/chat', (req, res) => {
31
- BotVersion.chat(req, res);
26
+ apiKey: ${apiKeyValue},
27
+ cwd: __dirname,
32
28
  });
33
29
  `;
34
30
 
35
31
  return {
36
32
  initBlock: initBlock.trim(),
37
- helperCode: getUserContext.helperCode || null,
38
- imports: getUserContext.imports || null,
39
33
  };
40
34
  }
41
35
 
42
- function generateExpressUserContext(auth) {
43
- if (!auth || !auth.name) {
44
- return {
45
- option: `
46
- // getUserContext: (req) => ({ userId: req.user?.id, email: req.user?.email }),`,
47
- helperCode: null,
48
- imports: null,
49
- };
50
- }
51
-
52
- switch (auth.name) {
53
- case "passport":
54
- return {
55
- option: `
56
- getUserContext: (req) => ({
57
- userId: req.user?.id,
58
- email: req.user?.email,
59
- role: req.user?.role,
60
- }),`,
61
- helperCode: null,
62
- imports: null,
63
- };
64
-
65
- case "jwt":
66
- return {
67
- option: `
68
- getUserContext: (req) => {
69
- // Decoded by your JWT middleware — adjust fields as needed
70
- return {
71
- userId: req.user?.id || req.user?.sub,
72
- email: req.user?.email,
73
- role: req.user?.role,
74
- };
75
- },`,
76
- helperCode: null,
77
- imports: null,
78
- };
79
-
80
- case "express-session":
81
- return {
82
- option: `
83
- getUserContext: (req) => ({
84
- userId: req.session?.user?.id,
85
- email: req.session?.user?.email,
86
- role: req.session?.user?.role,
87
- }),`,
88
- helperCode: null,
89
- imports: null,
90
- };
91
-
92
- default:
93
- return {
94
- option: `
95
- // getUserContext: (req) => ({ userId: req.user?.id, email: req.user?.email }),`,
96
- helperCode: null,
97
- imports: null,
98
- };
99
- }
100
- }
101
-
102
36
  // ─── NEXT.JS INSTRUMENTATION FILE ────────────────────────────────────────────
103
-
104
37
  function generateInstrumentationFile(info, apiKey) {
105
38
  const { next, moduleSystem } = info;
39
+ const isESM = moduleSystem === "esm";
106
40
 
107
- // FIX #3: Only include pagesDir if Pages Router actually exists
108
- // FIX #8: Use dynamic import instead of require() to support ESM projects
109
41
  const hasPagesRouter = next?.pagesRouter;
110
42
  const hasAppRouter = next?.appRouter;
111
43
 
112
- const pagesDirLine = hasPagesRouter
113
- ? next?.srcDir
114
- ? `path.join(process.cwd(), 'src', 'pages')`
115
- : `path.join(process.cwd(), 'pages')`
116
- : null;
117
-
118
- const appDirLine = hasAppRouter
119
- ? next?.srcDir
120
- ? `path.join(process.cwd(), 'src', 'app')`
121
- : `path.join(process.cwd(), 'app')`
122
- : null;
123
-
124
- // Build pagesDir option only if Pages Router exists
125
- const pagesDirOption = pagesDirLine
126
- ? `\n pagesDir: ${pagesDirLine},`
127
- : "";
128
-
129
- // Build appDir option only if App Router exists (for future scanner support)
130
- const appDirOption = appDirLine ? `\n appDir: ${appDirLine},` : "";
44
+ // Both App Router and Pages Router use pagesDir option
45
+ const pagesDirOption = "";
131
46
 
132
- return `export async function register() {
47
+ // ESM version
48
+ if (isESM) {
49
+ return `export async function register() {
133
50
  if (process.env.NEXT_RUNTIME === 'nodejs') {
134
- // FIX: Use dynamic import to support both CJS and ESM projects
135
51
  const { default: BotVersion } = await import('botversion-sdk');
136
- const { default: path } = await import('path');
137
52
 
138
53
  BotVersion.init({
139
- apiKey: process.env.BOTVERSION_API_KEY,${pagesDirOption}${appDirOption}
54
+ apiKey: process.env.BOTVERSION_API_KEY,
140
55
  });
141
56
  }
142
57
  }
143
58
  `;
144
- }
145
-
146
- // ─── NEXT.JS CHAT ROUTE — PAGES ROUTER ───────────────────────────────────────
147
-
148
- function generateNextPagesChatRoute(info) {
149
- const { auth } = info;
150
-
151
- switch (auth.name) {
152
- case "next-auth":
153
- return generateNextAuthPagesRoute(info);
154
- case "clerk":
155
- return generateClerkPagesRoute(info);
156
- case "supabase":
157
- return generateSupabasePagesRoute(info);
158
- default:
159
- return generateAuthlessPagesRoute(info);
160
- }
161
- }
162
-
163
- function generateNextAuthPagesRoute(info) {
164
- const { nextAuthConfig, auth, next, generateTs } = info;
165
- const isV5 = auth.version === "v5";
166
-
167
- // Compute correct relative import path for authOptions
168
- const chatFileDir = path.join(next.baseDir, "pages", "api", "botversion");
169
-
170
- let authImportPath = "../auth/[...nextauth]";
171
- if (nextAuthConfig) {
172
- const rel = path
173
- .relative(chatFileDir, nextAuthConfig.path)
174
- .replace(/\\/g, "/")
175
- .replace(/\.(js|ts)$/, "");
176
- authImportPath = rel.startsWith(".") ? rel : "./" + rel;
177
- }
178
-
179
- if (isV5) {
180
- return `import BotVersion from 'botversion-sdk';
181
- import { auth } from '${authImportPath}';
182
-
183
- export default BotVersion.nextHandler({
184
- apiKey: process.env.BOTVERSION_API_KEY,
185
- getSession: (req, res) => auth(),
186
- });
187
- `;
188
- }
189
-
190
- if (generateTs) {
191
- return `import BotVersion from 'botversion-sdk';
192
- import { getServerSession } from 'next-auth';
193
- import { authOptions } from '${authImportPath}';
194
- import type { NextApiRequest, NextApiResponse } from 'next';
195
-
196
- export default BotVersion.nextHandler({
197
- apiKey: process.env.BOTVERSION_API_KEY,
198
- getSession: async (req: NextApiRequest, res: NextApiResponse) => {
199
- return getServerSession(req, res, authOptions);
200
- },
201
- });
202
- `;
203
- }
204
-
205
- return `import BotVersion from 'botversion-sdk';
206
- import { getServerSession } from 'next-auth';
207
- import { authOptions } from '${authImportPath}';
208
-
209
- export default BotVersion.nextHandler({
210
- apiKey: process.env.BOTVERSION_API_KEY,
211
- getSession: (req, res) => getServerSession(req, res, authOptions),
212
- });
213
- `;
214
- }
215
-
216
- function generateClerkPagesRoute(info) {
217
- const { auth } = info;
218
- const pkg = auth.package || "@clerk/nextjs";
219
-
220
- return `import BotVersion from 'botversion-sdk';
221
- import { getAuth } from '${pkg}/server';
222
-
223
- export default BotVersion.nextHandler({
224
- apiKey: process.env.BOTVERSION_API_KEY,
225
- getSession: async (req, res) => {
226
- const { userId } = getAuth(req);
227
- return { user: { id: userId } };
228
- },
229
- });
230
- `;
231
- }
232
-
233
- function generateSupabasePagesRoute(info) {
234
- return `import BotVersion from 'botversion-sdk';
235
- import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs';
236
-
237
- export default BotVersion.nextHandler({
238
- apiKey: process.env.BOTVERSION_API_KEY,
239
- getSession: async (req, res) => {
240
- const supabase = createServerSupabaseClient({ req, res });
241
- const { data: { session } } = await supabase.auth.getSession();
242
- return { user: session?.user ?? null };
243
- },
244
- });
245
- `;
246
- }
247
-
248
- function generateAuthlessPagesRoute(info) {
249
- const { auth, isTypeScript } = info;
250
-
251
- const comment =
252
- auth.name && !auth.supported
253
- ? `// TODO: We detected ${auth.name} but don't have automatic support yet.\n// Add your own getSession below to pass user context to the agent.\n// See: https://docs.botversion.com/auth\n`
254
- : "";
255
-
256
- if (isTypeScript) {
257
- return `${comment}import BotVersion from 'botversion-sdk';
258
- import type { NextApiRequest, NextApiResponse } from 'next';
259
-
260
- // No auth library detected — agent will work without user context
261
- // To add user context: uncomment and implement getSession below
262
- export default BotVersion.nextHandler({
263
- apiKey: process.env.BOTVERSION_API_KEY,
264
- // getSession: async (req: NextApiRequest, res: NextApiResponse) => {
265
- // return { user: { id: 'user-id', email: 'user@example.com' } };
266
- // },
267
- });
268
- `;
269
- }
270
-
271
- return `${comment}import BotVersion from 'botversion-sdk';
272
-
273
- // No auth library detected — agent will work without user context
274
- // To add user context: uncomment and implement getSession below
275
- export default BotVersion.nextHandler({
276
- apiKey: process.env.BOTVERSION_API_KEY,
277
- // getSession: async (req, res) => {
278
- // return { user: { id: 'user-id', email: 'user@example.com' } };
279
- // },
280
- });
281
- `;
282
- }
283
-
284
- // ─── NEXT.JS CHAT ROUTE — APP ROUTER ─────────────────────────────────────────
285
-
286
- function generateNextAppChatRoute(info) {
287
- const { auth } = info;
288
-
289
- switch (auth.name) {
290
- case "next-auth":
291
- return generateNextAuthAppRoute(info);
292
- case "clerk":
293
- return generateClerkAppRoute(info);
294
- case "supabase":
295
- return generateSupabaseAppRoute(info);
296
- default:
297
- return generateAuthlessAppRoute(info);
298
59
  }
299
- }
300
60
 
301
- function generateNextAuthAppRoute(info) {
302
- const { auth, isTypeScript, next, nextAuthConfig } = info;
303
- const isV5 = auth.version === "v5";
304
-
305
- // FIX #5: Compute the correct relative import path instead of hardcoding @/lib/auth
306
- const chatFileDir = path.join(
307
- next.baseDir,
308
- "app",
309
- "api",
310
- "botversion",
311
- "chat",
312
- );
313
-
314
- let authImportPath = "@/lib/auth"; // fallback alias
315
- if (nextAuthConfig) {
316
- const rel = path
317
- .relative(chatFileDir, nextAuthConfig.path)
318
- .replace(/\\/g, "/")
319
- .replace(/\.(js|ts)$/, "");
320
- authImportPath = rel.startsWith(".") ? rel : "./" + rel;
321
- }
61
+ // CommonJS version
62
+ return `async function register() {
63
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
64
+ const BotVersion = require('botversion-sdk');
322
65
 
323
- const typeAnnotation = isTypeScript ? ": NextRequest" : "";
324
- const nextRequestImport = isTypeScript
325
- ? `import { NextRequest, NextResponse } from 'next/server';\n`
326
- : `import { NextResponse } from 'next/server';\n`;
327
-
328
- if (isV5) {
329
- return `import BotVersion from 'botversion-sdk';
330
- import { auth } from '${authImportPath}';
331
- ${nextRequestImport}
332
- // FIX #1: appRouterHandler is implemented here directly since App Router
333
- // does not support the nextHandler() Pages pattern
334
- export async function POST(req${typeAnnotation}) {
335
- try {
336
- const session = await auth();
337
- const body = await req.json();
338
-
339
- const result = await BotVersion.nextHandler({
66
+ BotVersion.init({
340
67
  apiKey: process.env.BOTVERSION_API_KEY,
341
- getSession: async () => session,
342
- })({ ...req, body }, { json: (d) => d, status: () => ({ json: (d) => d }) });
343
-
344
- return NextResponse.json(result);
345
- } catch (err) {
346
- console.error('[BotVersion] App Router handler error:', err);
347
- return NextResponse.json({ error: 'Agent error' }, { status: 500 });
348
- }
349
- }
350
- `;
351
- }
352
-
353
- return `import BotVersion from 'botversion-sdk';
354
- import { getServerSession } from 'next-auth';
355
- import { authOptions } from '${authImportPath}';
356
- ${nextRequestImport}
357
- export async function POST(req${typeAnnotation}) {
358
- try {
359
- const session = await getServerSession(authOptions);
360
- const body = await req.json();
361
-
362
- const userContext = {
363
- userId: session?.user?.id,
364
- email: session?.user?.email,
365
- name: session?.user?.name,
366
- };
367
-
368
- // Forward to BotVersion platform directly
369
- const response = await fetch(\`\${process.env.BOTVERSION_PLATFORM_URL || 'https://chatbusiness-two.vercel.app'}/api/chatbot/widget-chat\`, {
370
- method: 'POST',
371
- headers: { 'Content-Type': 'application/json' },
372
- body: JSON.stringify({
373
- chatbotId: body.chatbotId,
374
- publicKey: body.publicKey,
375
- query: body.message,
376
- previousChats: body.conversationHistory || [],
377
- pageContext: body.pageContext || {},
378
- userContext,
379
- }),
380
- });
381
-
382
- const data = await response.json();
383
- return NextResponse.json(data);
384
- } catch (err) {
385
- console.error('[BotVersion] App Router handler error:', err);
386
- return NextResponse.json({ error: 'Agent error' }, { status: 500 });
387
- }
388
- }
389
- `;
390
- }
391
-
392
- function generateClerkAppRoute(info) {
393
- const { isTypeScript } = info;
394
- const typeAnnotation = isTypeScript ? ": NextRequest" : "";
395
- const nextRequestImport = isTypeScript
396
- ? `import { NextRequest, NextResponse } from 'next/server';\n`
397
- : `import { NextResponse } from 'next/server';\n`;
398
-
399
- return `import BotVersion from 'botversion-sdk';
400
- import { auth } from '@clerk/nextjs/server';
401
- ${nextRequestImport}
402
- export async function POST(req${typeAnnotation}) {
403
- try {
404
- // FIX #6: auth() is async in Clerk v5+ — must be awaited
405
- const { userId } = await auth();
406
- const body = await req.json();
407
-
408
- const response = await fetch(\`\${process.env.BOTVERSION_PLATFORM_URL || 'https://chatbusiness-two.vercel.app'}/api/chatbot/widget-chat\`, {
409
- method: 'POST',
410
- headers: { 'Content-Type': 'application/json' },
411
- body: JSON.stringify({
412
- chatbotId: body.chatbotId,
413
- publicKey: body.publicKey,
414
- query: body.message,
415
- previousChats: body.conversationHistory || [],
416
- pageContext: body.pageContext || {},
417
- userContext: { userId },
418
- }),
419
- });
420
-
421
- const data = await response.json();
422
- return NextResponse.json(data);
423
- } catch (err) {
424
- console.error('[BotVersion] App Router handler error:', err);
425
- return NextResponse.json({ error: 'Agent error' }, { status: 500 });
426
- }
427
- }
428
- `;
429
- }
430
-
431
- function generateSupabaseAppRoute(info) {
432
- const { isTypeScript } = info;
433
- const typeAnnotation = isTypeScript ? ": NextRequest" : "";
434
- const nextRequestImport = isTypeScript
435
- ? `import { NextRequest, NextResponse } from 'next/server';\n`
436
- : `import { NextResponse } from 'next/server';\n`;
437
-
438
- return `import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
439
- import { cookies } from 'next/headers';
440
- ${nextRequestImport}
441
- export async function POST(req${typeAnnotation}) {
442
- try {
443
- const supabase = createRouteHandlerClient({ cookies });
444
- const { data: { session } } = await supabase.auth.getSession();
445
- const body = await req.json();
446
-
447
- const response = await fetch(\`\${process.env.BOTVERSION_PLATFORM_URL || 'https://chatbusiness-two.vercel.app'}/api/chatbot/widget-chat\`, {
448
- method: 'POST',
449
- headers: { 'Content-Type': 'application/json' },
450
- body: JSON.stringify({
451
- chatbotId: body.chatbotId,
452
- publicKey: body.publicKey,
453
- query: body.message,
454
- previousChats: body.conversationHistory || [],
455
- pageContext: body.pageContext || {},
456
- userContext: {
457
- userId: session?.user?.id,
458
- email: session?.user?.email,
459
- },
460
- }),
461
68
  });
462
-
463
- const data = await response.json();
464
- return NextResponse.json(data);
465
- } catch (err) {
466
- console.error('[BotVersion] App Router handler error:', err);
467
- return NextResponse.json({ error: 'Agent error' }, { status: 500 });
468
69
  }
469
70
  }
470
- `;
471
- }
472
-
473
- function generateAuthlessAppRoute(info) {
474
- const { auth, isTypeScript } = info;
475
- const typeAnnotation = isTypeScript ? ": NextRequest" : "";
476
- const nextRequestImport = isTypeScript
477
- ? `import { NextRequest, NextResponse } from 'next/server';\n`
478
- : `import { NextResponse } from 'next/server';\n`;
479
-
480
- const comment =
481
- auth.name && !auth.supported
482
- ? `// TODO: We detected ${auth.name} but don't have automatic support yet.\n// Add your own user context below.\n// See: https://docs.botversion.com/auth\n\n`
483
- : "";
484
-
485
- return `${comment}${nextRequestImport}
486
- export async function POST(req${typeAnnotation}) {
487
- try {
488
- const body = await req.json();
489
-
490
- // No auth detected — agent works without user context
491
- // Add userContext here if needed:
492
- // const userContext = { userId: '...', email: '...' };
493
-
494
- const response = await fetch(\`\${process.env.BOTVERSION_PLATFORM_URL || 'https://chatbusiness-two.vercel.app'}/api/chatbot/widget-chat\`, {
495
- method: 'POST',
496
- headers: { 'Content-Type': 'application/json' },
497
- body: JSON.stringify({
498
- chatbotId: body.chatbotId,
499
- publicKey: body.publicKey,
500
- query: body.message,
501
- previousChats: body.conversationHistory || [],
502
- pageContext: body.pageContext || {},
503
- userContext: {},
504
- }),
505
- });
506
71
 
507
- const data = await response.json();
508
- return NextResponse.json(data);
509
- } catch (err) {
510
- console.error('[BotVersion] App Router handler error:', err);
511
- return NextResponse.json({ error: 'Agent error' }, { status: 500 });
512
- }
513
- }
72
+ module.exports = { register };
514
73
  `;
515
74
  }
516
75
 
@@ -535,7 +94,6 @@ function generateNextConfigPatch(cwd, nextVersion) {
535
94
 
536
95
  // Skip instrumentationHook for Next.js 14.1+ (enabled by default)
537
96
  if (nextVersion && nextVersion.major >= 14) {
538
- // Check minor version too
539
97
  const rawVersion = nextVersion.raw || "";
540
98
  const match = rawVersion.match(/(\d+)\.(\d+)/);
541
99
  const minor = match ? parseInt(match[2], 10) : 0;
@@ -550,35 +108,27 @@ function generateNextConfigPatch(cwd, nextVersion) {
550
108
 
551
109
  let patched = configContent;
552
110
 
553
- // Add to existing experimental block
554
111
  if (configContent.includes("experimental")) {
555
112
  patched = configContent.replace(
556
113
  /experimental\s*:\s*\{/,
557
114
  "experimental: {\n instrumentationHook: true,",
558
115
  );
559
-
560
- // FIX #7: Handle next.config.mjs style — export default { ... }
561
116
  } else if (/export\s+default\s+\{/.test(configContent)) {
562
117
  patched = configContent.replace(
563
118
  /export\s+default\s+\{/,
564
119
  "export default {\n experimental: {\n instrumentationHook: true,\n },",
565
120
  );
566
-
567
- // Handle const nextConfig = { ... } style (next.config.js)
568
121
  } else if (/const\s+nextConfig\s*=\s*\{/.test(configContent)) {
569
122
  patched = configContent.replace(
570
123
  /const\s+nextConfig\s*=\s*\{/,
571
124
  "const nextConfig = {\n experimental: {\n instrumentationHook: true,\n },",
572
125
  );
573
-
574
- // Handle module.exports = { ... } style
575
126
  } else if (/module\.exports\s*=\s*\{/.test(configContent)) {
576
127
  patched = configContent.replace(
577
128
  /module\.exports\s*=\s*\{/,
578
129
  "module.exports = {\n experimental: {\n instrumentationHook: true,\n },",
579
130
  );
580
131
  } else {
581
- // Cannot safely patch — return null so caller prompts manual step
582
132
  return null;
583
133
  }
584
134
 
@@ -595,20 +145,15 @@ Fastify support is coming soon. For now, add this manually:
595
145
  const BotVersion = require('botversion-sdk');
596
146
 
597
147
  // After registering all your routes:
598
- BotVersion.init({ apiKey: '${apiKey}' });
599
-
600
- fastify.post('/api/botversion/chat', async (request, reply) => {
601
- // Implement using BotVersion.agentChat() directly
602
- // See: https://docs.botversion.com/fastify
603
- });
148
+ BotVersion.init({ apiKey: process.env.BOTVERSION_API_KEY });
604
149
  `,
605
150
  koa: `
606
151
  Koa support is coming soon. For now, add this manually:
607
- See: https://docs.botversion.com/koa
152
+ See: https://botversion.com/docs
608
153
  `,
609
154
  "@nestjs/core": `
610
155
  NestJS support is coming soon. For now, add this manually:
611
- See: https://docs.botversion.com/nestjs
156
+ See: https://botversion.com/docs
612
157
  `,
613
158
  };
614
159
 
@@ -616,7 +161,7 @@ NestJS support is coming soon. For now, add this manually:
616
161
  instructions[framework] ||
617
162
  `
618
163
  This framework is not yet supported automatically.
619
- Visit https://docs.botversion.com for manual setup instructions.
164
+ Visit https://botversion.com/docs for manual setup instructions.
620
165
  `
621
166
  );
622
167
  }
@@ -630,15 +175,12 @@ function generateScriptTag(projectInfo) {
630
175
  data-api-url="${projectInfo.apiUrl}"
631
176
  data-project-id="${projectInfo.projectId}"
632
177
  data-public-key="${projectInfo.publicKey}"
633
- data-proxy-url="/api/botversion/chat"
634
178
  ></script>`;
635
179
  }
636
180
 
637
181
  module.exports = {
638
182
  generateExpressInit,
639
183
  generateInstrumentationFile,
640
- generateNextPagesChatRoute,
641
- generateNextAppChatRoute,
642
184
  generateManualInstructions,
643
185
  generateNextConfigPatch,
644
186
  generateScriptTag,