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/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
|
|
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
|
|
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:
|
|
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
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
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 || 'http://localhost:3000'}/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 || 'http://localhost:3000'}/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 || 'http://localhost:3000'}/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 || 'http://localhost:3000'}/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
|
-
|
|
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,12 +145,7 @@ 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:
|
|
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:
|
|
@@ -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,
|
package/cli/prompts.js
CHANGED
|
@@ -89,53 +89,53 @@ async function promptEntryPoint() {
|
|
|
89
89
|
return filePath;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
async function promptAuthLibrary() {
|
|
93
|
-
const choices = [
|
|
94
|
-
{
|
|
95
|
-
label: "next-auth v4",
|
|
96
|
-
value: { name: "next-auth", version: "v4", supported: true },
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
label: "next-auth v5 (Auth.js)",
|
|
100
|
-
value: { name: "next-auth", version: "v5", supported: true },
|
|
101
|
-
},
|
|
102
|
-
{ label: "Clerk", value: { name: "clerk", supported: true } },
|
|
103
|
-
{ label: "Supabase Auth", value: { name: "supabase", supported: true } },
|
|
104
|
-
{ label: "Passport.js", value: { name: "passport", supported: true } },
|
|
105
|
-
{ label: "JWT (jsonwebtoken)", value: { name: "jwt", supported: true } },
|
|
106
|
-
{
|
|
107
|
-
label: "express-session",
|
|
108
|
-
value: { name: "express-session", supported: true },
|
|
109
|
-
},
|
|
110
|
-
{ label: "Other / Custom", value: { name: "custom", supported: false } },
|
|
111
|
-
{ label: "No auth", value: { name: null, supported: false } },
|
|
112
|
-
];
|
|
113
|
-
|
|
114
|
-
const choice = await askChoice(
|
|
115
|
-
"We couldn't detect your auth library. Which one are you using?",
|
|
116
|
-
choices,
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
return choice.value;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function promptNextAuthConfigPath() {
|
|
123
|
-
console.log(
|
|
124
|
-
"\n ⚠️ We couldn't find your authOptions location automatically.",
|
|
125
|
-
);
|
|
126
|
-
const filePath = await ask(
|
|
127
|
-
" Enter the path to your authOptions file (e.g. lib/auth.ts): ",
|
|
128
|
-
);
|
|
129
|
-
return filePath;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
92
|
async function promptForce(conflictFile) {
|
|
133
93
|
console.log(`\n ⚠️ File already exists: ${conflictFile}`);
|
|
134
94
|
return confirm(" Overwrite it? (a backup will be created)", false);
|
|
135
95
|
}
|
|
136
96
|
|
|
97
|
+
const CONFIG_FILES = [
|
|
98
|
+
"next.config.js",
|
|
99
|
+
"next.config.ts",
|
|
100
|
+
"vite.config.js",
|
|
101
|
+
"vite.config.ts",
|
|
102
|
+
"webpack.config.js",
|
|
103
|
+
"webpack.config.ts",
|
|
104
|
+
"nuxt.config.js",
|
|
105
|
+
"nuxt.config.ts",
|
|
106
|
+
];
|
|
107
|
+
|
|
137
108
|
async function promptMissingListenCall(entryPoint) {
|
|
109
|
+
const path = require("path");
|
|
110
|
+
const entryFileName = path.basename(entryPoint || "");
|
|
111
|
+
const isConfigFile = CONFIG_FILES.includes(entryFileName);
|
|
112
|
+
|
|
138
113
|
console.log(`\n ⚠️ We couldn't find app.listen() in ${entryPoint}`);
|
|
114
|
+
|
|
115
|
+
if (isConfigFile) {
|
|
116
|
+
console.log(
|
|
117
|
+
`\n ❌ "${entryFileName}" is a config file, not a server file.`,
|
|
118
|
+
);
|
|
119
|
+
console.log(" Appending server code here would break your project.");
|
|
120
|
+
console.log(" Options:");
|
|
121
|
+
|
|
122
|
+
const choices = [
|
|
123
|
+
{
|
|
124
|
+
label: "Enter the correct server file path manually",
|
|
125
|
+
value: "manual_path",
|
|
126
|
+
},
|
|
127
|
+
{ label: "Skip — I'll add it manually", value: "skip" },
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const choice = await askChoice("How would you like to proceed?", choices);
|
|
131
|
+
if (choice.value === "manual_path") {
|
|
132
|
+
const filePath = await ask(" Enter file path: ");
|
|
133
|
+
return { action: "manual_path", filePath };
|
|
134
|
+
}
|
|
135
|
+
return { action: choice.value };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Normal flow — entry point is not a config file
|
|
139
139
|
console.log(" Options:");
|
|
140
140
|
const choices = [
|
|
141
141
|
{ label: "Append to end of file", value: "append" },
|
|
@@ -158,8 +158,6 @@ module.exports = {
|
|
|
158
158
|
confirm,
|
|
159
159
|
promptMonorepoPackage,
|
|
160
160
|
promptEntryPoint,
|
|
161
|
-
promptAuthLibrary,
|
|
162
|
-
promptNextAuthConfigPath,
|
|
163
161
|
promptForce,
|
|
164
162
|
promptMissingListenCall,
|
|
165
163
|
};
|