@juspay/neurolink 9.51.0 → 9.51.2

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.
@@ -1209,6 +1209,155 @@ async function downloadImageFromUrl(url) {
1209
1209
  throw new Error(`Failed to download image from ${url}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
1210
1210
  }
1211
1211
  }
1212
+ /**
1213
+ * Get MIME type from file extension
1214
+ */
1215
+ function getMimeTypeFromExtension(filePath) {
1216
+ const ext = filePath.toLowerCase().split(".").pop();
1217
+ switch (ext) {
1218
+ case "png":
1219
+ return "image/png";
1220
+ case "gif":
1221
+ return "image/gif";
1222
+ case "webp":
1223
+ return "image/webp";
1224
+ case "bmp":
1225
+ return "image/bmp";
1226
+ case "tiff":
1227
+ case "tif":
1228
+ return "image/tiff";
1229
+ default:
1230
+ return "image/jpeg";
1231
+ }
1232
+ }
1233
+ /**
1234
+ * Detect MIME type from buffer magic bytes
1235
+ * Returns undefined if format cannot be detected
1236
+ */
1237
+ function detectMimeTypeFromBuffer(buffer) {
1238
+ // JPEG: FF D8 FF
1239
+ if (buffer.length >= 3 &&
1240
+ buffer[0] === 0xff &&
1241
+ buffer[1] === 0xd8 &&
1242
+ buffer[2] === 0xff) {
1243
+ return "image/jpeg";
1244
+ }
1245
+ // PNG: 89 50 4E 47 0D 0A 1A 0A
1246
+ if (buffer.length >= 8 &&
1247
+ buffer[0] === 0x89 &&
1248
+ buffer[1] === 0x50 &&
1249
+ buffer[2] === 0x4e &&
1250
+ buffer[3] === 0x47 &&
1251
+ buffer[4] === 0x0d &&
1252
+ buffer[5] === 0x0a &&
1253
+ buffer[6] === 0x1a &&
1254
+ buffer[7] === 0x0a) {
1255
+ return "image/png";
1256
+ }
1257
+ // GIF: 47 49 46 38 (37|39) 61
1258
+ if (buffer.length >= 6 &&
1259
+ buffer[0] === 0x47 &&
1260
+ buffer[1] === 0x49 &&
1261
+ buffer[2] === 0x46 &&
1262
+ buffer[3] === 0x38 &&
1263
+ (buffer[4] === 0x37 || buffer[4] === 0x39) &&
1264
+ buffer[5] === 0x61) {
1265
+ return "image/gif";
1266
+ }
1267
+ // WebP: 52 49 46 46 ?? ?? ?? ?? 57 45 42 50
1268
+ if (buffer.length >= 12 &&
1269
+ buffer[0] === 0x52 &&
1270
+ buffer[1] === 0x49 &&
1271
+ buffer[2] === 0x46 &&
1272
+ buffer[3] === 0x46 &&
1273
+ buffer[8] === 0x57 &&
1274
+ buffer[9] === 0x45 &&
1275
+ buffer[10] === 0x42 &&
1276
+ buffer[11] === 0x50) {
1277
+ return "image/webp";
1278
+ }
1279
+ // BMP: 42 4D
1280
+ if (buffer.length >= 2 && buffer[0] === 0x42 && buffer[1] === 0x4d) {
1281
+ return "image/bmp";
1282
+ }
1283
+ // TIFF: (49 49 2A 00) or (4D 4D 00 2A)
1284
+ if (buffer.length >= 4 &&
1285
+ ((buffer[0] === 0x49 &&
1286
+ buffer[1] === 0x49 &&
1287
+ buffer[2] === 0x2a &&
1288
+ buffer[3] === 0x00) ||
1289
+ (buffer[0] === 0x4d &&
1290
+ buffer[1] === 0x4d &&
1291
+ buffer[2] === 0x00 &&
1292
+ buffer[3] === 0x2a))) {
1293
+ return "image/tiff";
1294
+ }
1295
+ return undefined;
1296
+ }
1297
+ /**
1298
+ * Convert file path to raw base64 string.
1299
+ * Returns raw base64 (not a data: URI) to avoid SSRF validation in AI SDK v6.
1300
+ */
1301
+ function convertFilePathToBase64(filePath) {
1302
+ if (!existsSync(filePath)) {
1303
+ throw new Error(`Image file not found: ${filePath}`);
1304
+ }
1305
+ const buffer = readFileSync(filePath);
1306
+ return buffer.toString("base64");
1307
+ }
1308
+ /**
1309
+ * Process a single image input and convert to raw base64 format.
1310
+ * IMPORTANT: Returns raw base64 (not a data: URI) to avoid SSRF validation
1311
+ * in Vercel AI SDK v6. The SDK calls `new URL(image)` on string values;
1312
+ * a data: URI is a valid URL, causing the SDK to "download" it and hit
1313
+ * validateDownloadUrl which throws "URL scheme must be http or https, got data:".
1314
+ * Passing raw base64 avoids this because `new URL(base64string)` throws and
1315
+ * the SDK treats the string as inline base64 data instead.
1316
+ */
1317
+ function processImageToBase64(image, index) {
1318
+ let imageData;
1319
+ let mimeType = "image/jpeg"; // Default mime type
1320
+ if (typeof image === "string") {
1321
+ if (image.startsWith("data:")) {
1322
+ // Data URI (including downloaded URLs) - extract mime type and raw base64
1323
+ const match = image.match(/^data:([^;]+);base64,(.+)$/);
1324
+ if (match) {
1325
+ mimeType = match[1];
1326
+ imageData = match[2]; // Raw base64 only — NOT the full data: URI
1327
+ }
1328
+ else {
1329
+ imageData = image;
1330
+ }
1331
+ }
1332
+ else if (isInternetUrl(image)) {
1333
+ // This should not happen as URLs are processed separately
1334
+ throw new Error(`Unprocessed URL found in actualImages: ${image}`);
1335
+ }
1336
+ else {
1337
+ // File path string - convert to raw base64
1338
+ try {
1339
+ imageData = convertFilePathToBase64(image);
1340
+ mimeType = getMimeTypeFromExtension(image);
1341
+ }
1342
+ catch (error) {
1343
+ MultimodalLogger.logError("FILE_PATH_CONVERSION", error, {
1344
+ index,
1345
+ filePath: image,
1346
+ });
1347
+ throw new Error(`Failed to convert file path to base64: ${image}. ${error}`, { cause: error });
1348
+ }
1349
+ }
1350
+ }
1351
+ else {
1352
+ // Buffer - convert to raw base64 with proper MIME type detection
1353
+ const detectedMimeType = detectMimeTypeFromBuffer(image);
1354
+ if (detectedMimeType) {
1355
+ mimeType = detectedMimeType;
1356
+ }
1357
+ imageData = image.toString("base64");
1358
+ }
1359
+ return { imageData, mimeType };
1360
+ }
1212
1361
  /**
1213
1362
  * Convert simple images format to Vercel AI SDK format with smart auto-detection
1214
1363
  * - URLs: Downloaded and converted to base64 for Vercel AI SDK compatibility
@@ -1270,80 +1419,8 @@ async function convertSimpleImagesToProviderFormat(text, images, provider, _mode
1270
1419
  // Process all images (including downloaded URLs) for Vercel AI SDK
1271
1420
  actualImages.forEach(({ data: image }, index) => {
1272
1421
  try {
1273
- // Vercel AI SDK v6 expects { type: 'image', image: Buffer | string, mimeType?: string }
1274
- // IMPORTANT: The `image` field must be raw base64 or a Buffer — NOT a data: URI string.
1275
- // The AI SDK v6's download pipeline calls `new URL(image)` on string values. A data: URI
1276
- // is a valid URL, so the SDK tries to "download" it, which hits SSRF validation
1277
- // (validateDownloadUrl) and throws "URL scheme must be http or https, got data:".
1278
- // Passing raw base64 avoids this because `new URL(base64string)` throws and the SDK
1279
- // treats the string as inline base64 data instead.
1280
- let imageData;
1281
- let mimeType = "image/jpeg"; // Default mime type
1282
- if (typeof image === "string") {
1283
- if (image.startsWith("data:")) {
1284
- // Data URI (including downloaded URLs) - extract mime type and raw base64
1285
- const match = image.match(/^data:([^;]+);base64,(.+)$/);
1286
- if (match) {
1287
- mimeType = match[1];
1288
- imageData = match[2]; // Raw base64 only — NOT the full data: URI
1289
- }
1290
- else {
1291
- imageData = image;
1292
- }
1293
- }
1294
- else if (isInternetUrl(image)) {
1295
- // This should not happen as URLs are processed separately above
1296
- // But handle it gracefully just in case
1297
- throw new Error(`Unprocessed URL found in actualImages: ${image}`);
1298
- }
1299
- else {
1300
- // File path string - convert to base64
1301
- try {
1302
- if (existsSync(image)) {
1303
- const buffer = readFileSync(image);
1304
- const base64 = buffer.toString("base64");
1305
- // Detect mime type from file extension
1306
- const ext = image.toLowerCase().split(".").pop();
1307
- switch (ext) {
1308
- case "png":
1309
- mimeType = "image/png";
1310
- break;
1311
- case "gif":
1312
- mimeType = "image/gif";
1313
- break;
1314
- case "webp":
1315
- mimeType = "image/webp";
1316
- break;
1317
- case "bmp":
1318
- mimeType = "image/bmp";
1319
- break;
1320
- case "tiff":
1321
- case "tif":
1322
- mimeType = "image/tiff";
1323
- break;
1324
- default:
1325
- mimeType = "image/jpeg";
1326
- break;
1327
- }
1328
- imageData = base64; // Raw base64 only
1329
- }
1330
- else {
1331
- throw new Error(`Image file not found: ${image}`);
1332
- }
1333
- }
1334
- catch (error) {
1335
- MultimodalLogger.logError("FILE_PATH_CONVERSION", error, {
1336
- index,
1337
- filePath: image,
1338
- });
1339
- throw new Error(`Failed to convert file path to base64: ${image}. ${error}`, { cause: error });
1340
- }
1341
- }
1342
- }
1343
- else {
1344
- // Buffer - convert to raw base64
1345
- imageData = image.toString("base64");
1346
- }
1422
+ // Use helper function to process image and reduce nesting depth
1423
+ const { imageData, mimeType } = processImageToBase64(image, index);
1347
1424
  content.push({
1348
1425
  type: "image",
1349
1426
  image: imageData,
@@ -353,6 +353,7 @@ export function getNormalizedConfig(config) {
353
353
  const defaultUserSessionsPrefix = keyPrefix.replace(/conversation:?$/, "user:sessions:");
354
354
  let host = config.host || "localhost";
355
355
  let port = config.port || 6379;
356
+ let username = config.username || "";
356
357
  let password = config.password || "";
357
358
  let db = config.db || 0;
358
359
  let url = config.url;
@@ -361,13 +362,14 @@ export function getNormalizedConfig(config) {
361
362
  const parsedUrl = new URL(url);
362
363
  host = parsedUrl.hostname;
363
364
  port = parsedUrl.port ? parseInt(parsedUrl.port) : 6379;
365
+ username = parsedUrl.username || username;
364
366
  password = parsedUrl.password || password;
365
367
  db = parsedUrl.pathname
366
368
  ? parseInt(parsedUrl.pathname.replace("/", "")) || 0
367
369
  : 0;
368
370
  }
369
371
  catch (e) {
370
- const sanitizedUrl = url.replace(/:\/\/[^:]+:[^@]+@/, "://[redacted]@");
372
+ const sanitizedUrl = url.replace(/:\/\/[^@]+@/, "://[redacted]@");
371
373
  logger.warn("[redisUtils] Failed to parse Redis URL, falling back to component-based connection", {
372
374
  url: sanitizedUrl,
373
375
  error: e instanceof Error ? e.message : String(e),
@@ -380,6 +382,7 @@ export function getNormalizedConfig(config) {
380
382
  host,
381
383
  port,
382
384
  password,
385
+ username,
383
386
  db,
384
387
  keyPrefix,
385
388
  userSessionsKeyPrefix: config.userSessionsKeyPrefix || defaultUserSessionsPrefix,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.51.0",
3
+ "version": "9.51.2",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 13 providers: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure, Hugging Face, Ollama, and Mistral AI.",
6
6
  "author": {