@juspay/neurolink 10.11.2 → 10.12.0

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/adapters/audioFormatSupport.d.ts +53 -0
  3. package/dist/adapters/audioFormatSupport.js +200 -0
  4. package/dist/browser/neurolink.min.js +399 -398
  5. package/dist/cli/commands/auth.d.ts +8 -1
  6. package/dist/cli/commands/auth.js +185 -6
  7. package/dist/cli/factories/authCommandFactory.js +7 -1
  8. package/dist/lib/adapters/audioFormatSupport.d.ts +53 -0
  9. package/dist/lib/adapters/audioFormatSupport.js +201 -0
  10. package/dist/lib/processors/archive/ArchiveProcessor.d.ts +37 -0
  11. package/dist/lib/processors/archive/ArchiveProcessor.js +347 -32
  12. package/dist/lib/providers/googleAiStudio/client.d.ts +17 -0
  13. package/dist/lib/providers/googleAiStudio/client.js +45 -19
  14. package/dist/lib/providers/googleNativeGemini3/utils.d.ts +22 -1
  15. package/dist/lib/providers/googleNativeGemini3/utils.js +54 -0
  16. package/dist/lib/providers/googleVertex/client.js +3 -0
  17. package/dist/lib/proxy/accountQuota.d.ts +6 -0
  18. package/dist/lib/proxy/accountQuota.js +19 -2
  19. package/dist/lib/proxy/accountUsage.d.ts +45 -0
  20. package/dist/lib/proxy/accountUsage.js +289 -0
  21. package/dist/lib/server/routes/claudeProxyRoutes.d.ts +15 -1
  22. package/dist/lib/server/routes/claudeProxyRoutes.js +166 -0
  23. package/dist/lib/types/cli.d.ts +12 -0
  24. package/dist/lib/types/file.d.ts +41 -0
  25. package/dist/lib/types/generate.d.ts +12 -1
  26. package/dist/lib/types/processor.d.ts +20 -1
  27. package/dist/lib/types/providers.d.ts +7 -0
  28. package/dist/lib/types/proxy.d.ts +101 -0
  29. package/dist/lib/utils/fileDetector.d.ts +27 -0
  30. package/dist/lib/utils/fileDetector.js +130 -7
  31. package/dist/lib/utils/imageProcessor.js +31 -0
  32. package/dist/lib/utils/messageBuilder.d.ts +0 -9
  33. package/dist/lib/utils/messageBuilder.js +380 -56
  34. package/dist/processors/archive/ArchiveProcessor.d.ts +37 -0
  35. package/dist/processors/archive/ArchiveProcessor.js +347 -32
  36. package/dist/providers/googleAiStudio/client.d.ts +17 -0
  37. package/dist/providers/googleAiStudio/client.js +45 -19
  38. package/dist/providers/googleNativeGemini3/utils.d.ts +22 -1
  39. package/dist/providers/googleNativeGemini3/utils.js +54 -0
  40. package/dist/providers/googleVertex/client.js +3 -0
  41. package/dist/proxy/accountQuota.d.ts +6 -0
  42. package/dist/proxy/accountQuota.js +19 -2
  43. package/dist/proxy/accountUsage.d.ts +45 -0
  44. package/dist/proxy/accountUsage.js +288 -0
  45. package/dist/server/routes/claudeProxyRoutes.d.ts +15 -1
  46. package/dist/server/routes/claudeProxyRoutes.js +166 -0
  47. package/dist/types/cli.d.ts +12 -0
  48. package/dist/types/file.d.ts +41 -0
  49. package/dist/types/generate.d.ts +12 -1
  50. package/dist/types/processor.d.ts +20 -1
  51. package/dist/types/providers.d.ts +7 -0
  52. package/dist/types/proxy.d.ts +101 -0
  53. package/dist/utils/fileDetector.d.ts +27 -0
  54. package/dist/utils/fileDetector.js +130 -7
  55. package/dist/utils/imageProcessor.js +31 -0
  56. package/dist/utils/messageBuilder.d.ts +0 -9
  57. package/dist/utils/messageBuilder.js +380 -56
  58. package/package.json +3 -2
@@ -290,6 +290,13 @@ export class FileDetector {
290
290
  // These default ensure consistent timeout behavior across all file-detection logic.
291
291
  static DEFAULT_NETWORK_TIMEOUT = 30000; // 30 seconds
292
292
  static DEFAULT_HEAD_TIMEOUT = 5000; // 5 seconds
293
+ /**
294
+ * Ceiling on an in-process document parse (unzip + XML walk). Generous
295
+ * relative to the work, because the cost of firing early on a large but
296
+ * legitimate file is a lost extraction, while the cost of never firing is a
297
+ * held request.
298
+ */
299
+ static DEFAULT_DOCUMENT_TIMEOUT = 30000; // 30 seconds
293
300
  /**
294
301
  * Auto-detect file type and process in one call
295
302
  *
@@ -404,7 +411,14 @@ export class FileDetector {
404
411
  if (Buffer.isBuffer(input)) {
405
412
  return "buffer";
406
413
  }
407
- return "unknown-input";
414
+ // Everything left is a `FileWithMetadata`, which states its own name, and
415
+ // `withResolvedExtension` reads this to recover an extension when a
416
+ // content-based strategy reported none. Falling straight through to
417
+ // "unknown-input" threw that name away, so an `.odp`, `.rtf` or `.tar`
418
+ // supplied as bytes-plus-name lost the extension its processor routes on.
419
+ // Still defensive about the value: the type says required, callers are
420
+ // untyped JavaScript often enough.
421
+ return input?.filename || "unknown-input";
408
422
  }
409
423
  /**
410
424
  * Derive byte size from FileInput for tracing.
@@ -661,7 +675,7 @@ export class FileDetector {
661
675
  source: FileDetector.deriveInputSource(input),
662
676
  metadata: {
663
677
  confidence: 95,
664
- filename: FileDetector.deriveInputFilename(input),
678
+ filename: options?.filenameHint || FileDetector.deriveInputFilename(input),
665
679
  size: FileDetector.deriveInputSize(input),
666
680
  },
667
681
  };
@@ -684,13 +698,68 @@ export class FileDetector {
684
698
  }
685
699
  if (result.metadata.confidence >= confidenceThreshold) {
686
700
  logger.info(`[FileDetector] Type: ${result.type} (${result.metadata.confidence}%)`);
687
- return result;
701
+ return FileDetector.withResolvedExtension(result, input, options);
688
702
  }
689
703
  }
690
704
  // Below-threshold detection is the common case for any file under the
691
705
  // ContentHeuristic ceiling — a debug detail, not a warning-worthy anomaly.
692
706
  logger.debug(`[FileDetector] Best-effort type below threshold: ${best?.type ?? "unknown"} (${best?.metadata.confidence ?? 0}%, threshold ${confidenceThreshold}%)`);
693
- return best;
707
+ return FileDetector.withResolvedExtension(best, input, options);
708
+ }
709
+ /**
710
+ * Fill in `extension` from the input's name when detection did not set it.
711
+ *
712
+ * Content-based strategies identify a type from magic bytes and legitimately
713
+ * have no extension to report, so they return null. That is fine for the type
714
+ * itself but not for routing: several processors are chosen by extension
715
+ * *after* detection has settled the type, because one routing type covers
716
+ * several formats — `docx` covers .docx, .odt and .rtf.
717
+ *
718
+ * With a null extension those branches were unreachable. An .rtf scored high
719
+ * on its `{\\rtf1` signature, arrived as type "docx" with no extension, and
720
+ * fell through to the Word processor, which cannot read RTF — so a file whose
721
+ * dedicated processor extracts it perfectly reported "Could not extract
722
+ * content". The extension was known the whole time; it was simply dropped on
723
+ * the way through.
724
+ *
725
+ * Only fills a gap — a strategy that did determine an extension keeps it, so
726
+ * content still wins over a lying filename.
727
+ */
728
+ static withResolvedExtension(result, input, options) {
729
+ if (!result) {
730
+ return result;
731
+ }
732
+ // The caller's hint outranks a name derived from the input, because on the
733
+ // unified path the input has already been unwrapped to a bare Buffer and
734
+ // derives to the literal "buffer" — carrying no extension at all.
735
+ const filename = options?.filenameHint ||
736
+ result.metadata?.filename ||
737
+ FileDetector.deriveInputFilename(input);
738
+ if (!filename) {
739
+ return result;
740
+ }
741
+ // Split on both separators so a Windows-style path on a POSIX host still
742
+ // yields its basename, then take the final suffix.
743
+ const base = filename.split(/[\\/]/).pop() ?? filename;
744
+ const dot = base.lastIndexOf(".");
745
+ const extension = result.extension ??
746
+ (dot > 0 && dot < base.length - 1
747
+ ? base.slice(dot + 1).toLowerCase()
748
+ : null);
749
+ // The name is carried alongside the extension for the same reason. A
750
+ // content strategy reports no filename, so every processor keyed on one
751
+ // received the literal fallback "archive" — and archive format detection
752
+ // reads the name, because TAR has no magic bytes at offset 0 (its "ustar"
753
+ // marker sits at byte 257). A .tar therefore arrived as an unidentifiable
754
+ // archive and reported "Could not extract content", while the same bytes
755
+ // handed to the processor WITH their name extract perfectly.
756
+ const metadata = result.metadata && !result.metadata.filename
757
+ ? { ...result.metadata, filename: base }
758
+ : result.metadata;
759
+ if (extension === result.extension && metadata === result.metadata) {
760
+ return result;
761
+ }
762
+ return { ...result, extension, metadata };
694
763
  }
695
764
  /**
696
765
  * Load file content from various sources
@@ -1343,6 +1412,40 @@ export class FileDetector {
1343
1412
  static async processPptxFile(content, detection) {
1344
1413
  const pptxFilename = detection.metadata.filename || "presentation";
1345
1414
  try {
1415
+ // ODP is an OpenDocument package, not OOXML — the PPTX reader finds no
1416
+ // ppt/slides parts in it and returns nothing. It reaches this branch at
1417
+ // all because one routing type ("pptx") covers every presentation
1418
+ // format, the same way "docx" covers .odt.
1419
+ if (detection.extension?.toLowerCase() === "odp") {
1420
+ const { openDocumentProcessor } = await import("../processors/document/OpenDocumentProcessor.js");
1421
+ // Bounded per the project's async-timeout guideline: this unzips and
1422
+ // parses attacker-supplied bytes, and a stalled parse would otherwise
1423
+ // hold the request open with no ceiling. On timeout the throw lands in
1424
+ // this block's existing catch, which degrades to the placeholder.
1425
+ const odpResult = await withTimeout(openDocumentProcessor.processFile({
1426
+ id: pptxFilename,
1427
+ name: pptxFilename,
1428
+ mimetype: detection.mimeType ||
1429
+ "application/vnd.oasis.opendocument.presentation",
1430
+ size: content.length,
1431
+ buffer: content,
1432
+ }), FileDetector.DEFAULT_DOCUMENT_TIMEOUT);
1433
+ // Gated on success rather than on text, because a presentation of
1434
+ // nothing but images is a legitimate ODP that extracts to an empty
1435
+ // string. Requiring text sent that file on to the PPTX reader, which
1436
+ // cannot read OpenDocument at all — so a successful extraction was
1437
+ // discarded in favour of a guaranteed failure. Matches how the ODT and
1438
+ // ODS branches degrade.
1439
+ if (odpResult.success && odpResult.data) {
1440
+ return {
1441
+ type: "pptx",
1442
+ content: odpResult.data.textContent ||
1443
+ FileDetector.formatInformativePlaceholder("Presentation", pptxFilename, content, detection),
1444
+ mimeType: detection.mimeType,
1445
+ metadata: detection.metadata,
1446
+ };
1447
+ }
1448
+ }
1346
1449
  const { PptxProcessor } = await import("../processors/document/PptxProcessor.js");
1347
1450
  const pptxResult = await PptxProcessor.extractText(content);
1348
1451
  if (pptxResult) {
@@ -2003,13 +2106,33 @@ class MagicBytesStrategy {
2003
2106
  if (input.length >= 6 && input.toString("latin1", 0, 5) === "#!AMR") {
2004
2107
  return this.result("audio", "audio/amr", 95);
2005
2108
  }
2006
- // JPEG 2000: 12-byte signature box.
2007
- if (input.length >= 8 &&
2109
+ // JPEG 2000: the full 12-byte signature box, trailing 0D 0A 87 0A
2110
+ // included. Those four bytes are a deliberate line-ending probe — CR LF, a
2111
+ // high byte, LF — that any transfer which mangles newlines or strips the
2112
+ // eighth bit will visibly corrupt, so checking only the length and brand
2113
+ // accepts exactly the damaged files the signature exists to reject.
2114
+ if (input.length >= 12 &&
2008
2115
  input[0] === 0x00 &&
2009
2116
  input[1] === 0x00 &&
2010
2117
  input[2] === 0x00 &&
2011
2118
  input[3] === 0x0c &&
2012
- input.toString("latin1", 4, 8) === "jP ") {
2119
+ input.toString("latin1", 4, 8) === "jP " &&
2120
+ input[8] === 0x0d &&
2121
+ input[9] === 0x0a &&
2122
+ input[10] === 0x87 &&
2123
+ input[11] === 0x0a) {
2124
+ return this.result("image", "image/jp2", 95);
2125
+ }
2126
+ // The other shape JPEG 2000 ships in: a bare codestream (.j2k/.j2c) opening
2127
+ // with the SOC + SIZ markers. `ImageProcessor.detectImageType` learned both
2128
+ // shapes; this strategy knew only the container, so a codestream uploaded
2129
+ // as bytes-plus-filename was typed "unknown" and delivered as a binary
2130
+ // blob — the codestream branch over there was unreachable from this path.
2131
+ if (input.length >= 4 &&
2132
+ input[0] === 0xff &&
2133
+ input[1] === 0x4f &&
2134
+ input[2] === 0xff &&
2135
+ input[3] === 0x51) {
2013
2136
  return this.result("image", "image/jp2", 95);
2014
2137
  }
2015
2138
  // MP3: ID3 tag
@@ -468,6 +468,37 @@ export class ImageProcessor {
468
468
  if (isoBmffMimeType) {
469
469
  return isoBmffMimeType;
470
470
  }
471
+ // JPEG 2000, in both shapes it ships in. The JP2 container opens with
472
+ // the 12-byte signature box `00 00 00 0C 6A 50 20 20 0D 0A 87 0A`; a
473
+ // bare codestream (.j2k/.j2c) opens with the SOC+SIZ markers FF 4F FF
474
+ // 51. Checked BEFORE ICO because the container's first three bytes are
475
+ // 00 00 00, which is one byte away from ICO's 00 00 01 00 and shares
476
+ // its leading zeros — an ordering mistake here would classify every
477
+ // JPEG 2000 as an icon rather than merely failing to recognise it.
478
+ // All twelve bytes are checked, not just the length and brand: the
479
+ // trailing 0D 0A 87 0A is the signature's whole point. It is a
480
+ // line-ending probe — CR LF, a high byte, LF — that a transfer which
481
+ // mangles newlines or strips the eighth bit will visibly corrupt, so
482
+ // skipping it accepts exactly the damaged files it exists to reject.
483
+ if (input.length >= 12 &&
484
+ input[0] === 0x00 &&
485
+ input[1] === 0x00 &&
486
+ input[2] === 0x00 &&
487
+ input[3] === 0x0c &&
488
+ input.subarray(4, 8).toString("latin1") === "jP " &&
489
+ input[8] === 0x0d &&
490
+ input[9] === 0x0a &&
491
+ input[10] === 0x87 &&
492
+ input[11] === 0x0a) {
493
+ return "image/jp2";
494
+ }
495
+ if (input.length >= 4 &&
496
+ input[0] === 0xff &&
497
+ input[1] === 0x4f &&
498
+ input[2] === 0xff &&
499
+ input[3] === 0x51) {
500
+ return "image/jp2";
501
+ }
471
502
  // ICO: 00 00 01 00 (icon type=1)
472
503
  if (input[0] === 0x00 &&
473
504
  input[1] === 0x00 &&
@@ -37,15 +37,6 @@ export declare function mergeMediaFileAliases<TFile>(input: {
37
37
  audioFiles?: Array<Buffer | string>;
38
38
  videoFiles?: Array<Buffer | string>;
39
39
  }): void;
40
- /**
41
- * Process the unified files array with auto-detection.
42
- * Handles lazy file registration, full processing, and preview injection.
43
- *
44
- * Exported so providers that bypass BaseProvider.generate() (e.g.
45
- * GoogleVertex's native @google/genai path) can still preprocess
46
- * `input.files` — without this, mimetype-hint and text-file inputs
47
- * would silently never reach the model on those paths.
48
- */
49
40
  export declare function processUnifiedFilesArray(options: GenerateOptions, maxSize: number, provider: string): Promise<void>;
50
41
  /**
51
42
  * Build multimodal message array with image support