@juspay/neurolink 8.31.1 → 8.31.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [8.31.3](https://github.com/juspay/neurolink/compare/v8.31.2...v8.31.3) (2026-01-05)
2
+
3
+ ### Bug Fixes
4
+
5
+ - **(csv):** standardize rowCount to exclude empty lines across all formats (CSV-025) ([c898521](https://github.com/juspay/neurolink/commit/c8985212ff6bb43a6cefbf7a5551ed603b32bc55)), closes [#390](https://github.com/juspay/neurolink/issues/390)
6
+
7
+ ## [8.31.2](https://github.com/juspay/neurolink/compare/v8.31.1...v8.31.2) (2026-01-05)
8
+
9
+ ### Bug Fixes
10
+
11
+ - **(cli):** add path resolution for file inputs ([2c191c0](https://github.com/juspay/neurolink/commit/2c191c0d86dbec6018ea8b7d1b35c5bba8e7b0d8)), closes [#338](https://github.com/juspay/neurolink/issues/338)
12
+
1
13
  ## [8.31.1](https://github.com/juspay/neurolink/compare/v8.31.0...v8.31.1) (2026-01-05)
2
14
 
3
15
  ### Bug Fixes
@@ -7,6 +7,7 @@ import { normalizeEvaluationData } from "../../lib/utils/evaluationUtils.js";
7
7
  import { createThinkingConfigFromRecord } from "../../lib/utils/thinkingConfig.js";
8
8
  import { LoopSession } from "../loop/session.js";
9
9
  import { initializeCliParser } from "../parser.js";
10
+ import { resolveFilePaths } from "../utils/pathResolver.js";
10
11
  // Use TokenUsage from standard types - no local interface needed
11
12
  import { ContextFactory, } from "../../lib/types/contextTypes.js";
12
13
  import { ModelsCommandFactory } from "../commands/models.js";
@@ -300,38 +301,50 @@ export class CLICommandFactory {
300
301
  return undefined;
301
302
  }
302
303
  const imagePaths = Array.isArray(images) ? images : [images];
303
- // Return as-is - let the smart message builder handle URL vs file detection
304
- // URLs will be detected and appended to prompt text
304
+ // Resolve relative paths to absolute paths before returning
305
+ // URLs are preserved as-is by resolveFilePaths
305
306
  // File paths will be converted to base64 by the message builder
306
- return imagePaths;
307
+ return resolveFilePaths(imagePaths);
307
308
  }
308
309
  // Helper method to process CLI CSV files
309
310
  static processCliCSVFiles(csvFiles) {
310
311
  if (!csvFiles) {
311
312
  return undefined;
312
313
  }
313
- return Array.isArray(csvFiles) ? csvFiles : [csvFiles];
314
+ const paths = Array.isArray(csvFiles) ? csvFiles : [csvFiles];
315
+ // Resolve relative paths to absolute paths before returning
316
+ // URLs are preserved as-is by resolveFilePaths
317
+ return resolveFilePaths(paths);
314
318
  }
315
319
  // Helper method to process CLI PDF files
316
320
  static processCliPDFFiles(pdfFiles) {
317
321
  if (!pdfFiles) {
318
322
  return undefined;
319
323
  }
320
- return Array.isArray(pdfFiles) ? pdfFiles : [pdfFiles];
324
+ const paths = Array.isArray(pdfFiles) ? pdfFiles : [pdfFiles];
325
+ // Resolve relative paths to absolute paths before returning
326
+ // URLs are preserved as-is by resolveFilePaths
327
+ return resolveFilePaths(paths);
321
328
  }
322
329
  // Helper method to process CLI files with auto-detection
323
330
  static processCliFiles(files) {
324
331
  if (!files) {
325
332
  return undefined;
326
333
  }
327
- return Array.isArray(files) ? files : [files];
334
+ const paths = Array.isArray(files) ? files : [files];
335
+ // Resolve relative paths to absolute paths before returning
336
+ // URLs are preserved as-is by resolveFilePaths
337
+ return resolveFilePaths(paths);
328
338
  }
329
339
  // Helper method to process CLI video files
330
340
  static processCliVideoFiles(videoFiles) {
331
341
  if (!videoFiles) {
332
342
  return undefined;
333
343
  }
334
- return Array.isArray(videoFiles) ? videoFiles : [videoFiles];
344
+ const paths = Array.isArray(videoFiles) ? videoFiles : [videoFiles];
345
+ // Resolve relative paths to absolute paths before returning
346
+ // URLs are preserved as-is by resolveFilePaths
347
+ return resolveFilePaths(paths);
335
348
  }
336
349
  // Helper method to process common options
337
350
  static processOptions(argv) {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Path Resolution Utility for CLI
3
+ *
4
+ * Converts relative file paths to absolute paths while preserving URLs.
5
+ * This ensures consistent file access regardless of the current working directory.
6
+ */
7
+ /**
8
+ * Resolve a file path to an absolute path.
9
+ *
10
+ * - Relative paths (./images/chart.png, ../data/report.pdf) are resolved
11
+ * against the current working directory
12
+ * - Absolute paths (/home/user/file.txt, C:\\Users\\file.txt) are returned unchanged
13
+ * - URLs (http://..., https://...) are returned unchanged
14
+ *
15
+ * @param filePath - The file path to resolve
16
+ * @returns Resolved absolute path, or original URL/string
17
+ */
18
+ export declare function resolveFilePath(filePath: string): string;
19
+ /**
20
+ * Resolve multiple file paths to absolute paths.
21
+ *
22
+ * @param filePaths - Array of file paths to resolve
23
+ * @returns Array of resolved absolute paths (or URLs unchanged)
24
+ */
25
+ export declare function resolveFilePaths(filePaths: string[]): string[];
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Path Resolution Utility for CLI
3
+ *
4
+ * Converts relative file paths to absolute paths while preserving URLs.
5
+ * This ensures consistent file access regardless of the current working directory.
6
+ */
7
+ import path from "path";
8
+ /**
9
+ * Check if a string is an internet URL, file URL, or data URI
10
+ */
11
+ function isURL(str) {
12
+ const lower = str.toLowerCase();
13
+ return (lower.startsWith("http://") ||
14
+ lower.startsWith("https://") ||
15
+ lower.startsWith("file://") ||
16
+ lower.startsWith("data:"));
17
+ }
18
+ /**
19
+ * Resolve a file path to an absolute path.
20
+ *
21
+ * - Relative paths (./images/chart.png, ../data/report.pdf) are resolved
22
+ * against the current working directory
23
+ * - Absolute paths (/home/user/file.txt, C:\\Users\\file.txt) are returned unchanged
24
+ * - URLs (http://..., https://...) are returned unchanged
25
+ *
26
+ * @param filePath - The file path to resolve
27
+ * @returns Resolved absolute path, or original URL/string
28
+ */
29
+ export function resolveFilePath(filePath) {
30
+ // Handle empty string input
31
+ if (!filePath) {
32
+ return filePath;
33
+ }
34
+ // Normalize whitespace-only strings to empty string for consistent handling
35
+ if (!filePath.trim()) {
36
+ return "";
37
+ }
38
+ // Don't resolve URLs
39
+ if (isURL(filePath)) {
40
+ return filePath;
41
+ }
42
+ // Resolve relative/absolute paths against current working directory
43
+ // path.resolve handles both relative and absolute paths correctly:
44
+ // - Absolute paths are returned unchanged
45
+ // - Relative paths are resolved against process.cwd()
46
+ return path.resolve(process.cwd(), filePath);
47
+ }
48
+ /**
49
+ * Resolve multiple file paths to absolute paths.
50
+ *
51
+ * @param filePaths - Array of file paths to resolve
52
+ * @returns Array of resolved absolute paths (or URLs unchanged)
53
+ */
54
+ export function resolveFilePaths(filePaths) {
55
+ return filePaths.map(resolveFilePath);
56
+ }
57
+ //# sourceMappingURL=pathResolver.js.map
@@ -44,6 +44,7 @@ export type FileProcessingResult = {
44
44
  filename?: string;
45
45
  extension?: string | null;
46
46
  rowCount?: number;
47
+ totalLines?: number;
47
48
  columnCount?: number;
48
49
  columnNames?: string[];
49
50
  sampleData?: string | unknown[];
@@ -85,8 +85,12 @@ export class CSVProcessor {
85
85
  : lines;
86
86
  const limitedLines = csvLines.slice(0, 1 + maxRows); // header + data rows
87
87
  const limitedCSV = limitedLines.join("\n");
88
- const rowCount = limitedLines.length - 1; // Subtract header
89
- const originalRowCount = csvLines.length - 1; // Subtract header from original
88
+ const rowCount = limitedLines
89
+ .slice(1)
90
+ .filter((line) => line.trim() !== "").length;
91
+ const originalRowCount = csvLines
92
+ .slice(1)
93
+ .filter((line) => line.trim() !== "").length;
90
94
  const wasTruncated = rowCount < originalRowCount;
91
95
  if (wasTruncated) {
92
96
  logger.warn(`[CSVProcessor] CSV data truncated: showing ${rowCount} of ${originalRowCount} rows (limit: ${maxRows})`);
@@ -110,6 +114,7 @@ export class CSVProcessor {
110
114
  confidence: 100,
111
115
  size: content.length,
112
116
  rowCount,
117
+ totalLines: limitedLines.length,
113
118
  columnCount: (limitedLines[0] || "").split(",").length,
114
119
  extension,
115
120
  },
@@ -121,12 +126,26 @@ export class CSVProcessor {
121
126
  maxRows,
122
127
  });
123
128
  const rows = await this.parseCSVString(csvString, maxRows);
129
+ // Filter out empty rows (empty objects or rows with only whitespace values from blank lines)
130
+ const nonEmptyRows = rows.filter((row) => {
131
+ if (!row || typeof row !== "object") {
132
+ return false;
133
+ }
134
+ const keys = Object.keys(row);
135
+ if (keys.length === 0) {
136
+ return false;
137
+ }
138
+ // Check if all values are empty or whitespace-only
139
+ return !Object.values(row).every((val) => val === "" || (typeof val === "string" && val.trim() === ""));
140
+ });
124
141
  // Extract metadata from parsed results
125
- const rowCount = rows.length;
126
- const columnNames = rows.length > 0 ? Object.keys(rows[0]) : [];
142
+ const rowCount = nonEmptyRows.length;
143
+ const columnNames = nonEmptyRows.length > 0
144
+ ? Object.keys(nonEmptyRows[0])
145
+ : [];
127
146
  const columnCount = columnNames.length;
128
147
  const hasEmptyColumns = columnNames.some((col) => !col || col.trim() === "");
129
- const sampleRows = rows.slice(0, 3);
148
+ const sampleRows = nonEmptyRows.slice(0, 3);
130
149
  const sampleData = this.formatSampleData(sampleRows, sampleDataFormat, includeHeaders);
131
150
  if (hasEmptyColumns) {
132
151
  logger.warn("[CSVProcessor] CSV contains empty or blank column headers", {
@@ -138,7 +157,7 @@ export class CSVProcessor {
138
157
  }
139
158
  // Format parsed data
140
159
  logger.debug(`[CSVProcessor] Converting ${rowCount} rows to ${formatStyle} format`);
141
- const formatted = this.formatForLLM(rows, formatStyle, includeHeaders);
160
+ const formatted = this.formatForLLM(nonEmptyRows, formatStyle, includeHeaders);
142
161
  logger.info("[CSVProcessor] ✅ Processed CSV file", {
143
162
  formatStyle,
144
163
  rowCount,
@@ -44,6 +44,7 @@ export type FileProcessingResult = {
44
44
  filename?: string;
45
45
  extension?: string | null;
46
46
  rowCount?: number;
47
+ totalLines?: number;
47
48
  columnCount?: number;
48
49
  columnNames?: string[];
49
50
  sampleData?: string | unknown[];
@@ -85,8 +85,12 @@ export class CSVProcessor {
85
85
  : lines;
86
86
  const limitedLines = csvLines.slice(0, 1 + maxRows); // header + data rows
87
87
  const limitedCSV = limitedLines.join("\n");
88
- const rowCount = limitedLines.length - 1; // Subtract header
89
- const originalRowCount = csvLines.length - 1; // Subtract header from original
88
+ const rowCount = limitedLines
89
+ .slice(1)
90
+ .filter((line) => line.trim() !== "").length;
91
+ const originalRowCount = csvLines
92
+ .slice(1)
93
+ .filter((line) => line.trim() !== "").length;
90
94
  const wasTruncated = rowCount < originalRowCount;
91
95
  if (wasTruncated) {
92
96
  logger.warn(`[CSVProcessor] CSV data truncated: showing ${rowCount} of ${originalRowCount} rows (limit: ${maxRows})`);
@@ -110,6 +114,7 @@ export class CSVProcessor {
110
114
  confidence: 100,
111
115
  size: content.length,
112
116
  rowCount,
117
+ totalLines: limitedLines.length,
113
118
  columnCount: (limitedLines[0] || "").split(",").length,
114
119
  extension,
115
120
  },
@@ -121,12 +126,26 @@ export class CSVProcessor {
121
126
  maxRows,
122
127
  });
123
128
  const rows = await this.parseCSVString(csvString, maxRows);
129
+ // Filter out empty rows (empty objects or rows with only whitespace values from blank lines)
130
+ const nonEmptyRows = rows.filter((row) => {
131
+ if (!row || typeof row !== "object") {
132
+ return false;
133
+ }
134
+ const keys = Object.keys(row);
135
+ if (keys.length === 0) {
136
+ return false;
137
+ }
138
+ // Check if all values are empty or whitespace-only
139
+ return !Object.values(row).every((val) => val === "" || (typeof val === "string" && val.trim() === ""));
140
+ });
124
141
  // Extract metadata from parsed results
125
- const rowCount = rows.length;
126
- const columnNames = rows.length > 0 ? Object.keys(rows[0]) : [];
142
+ const rowCount = nonEmptyRows.length;
143
+ const columnNames = nonEmptyRows.length > 0
144
+ ? Object.keys(nonEmptyRows[0])
145
+ : [];
127
146
  const columnCount = columnNames.length;
128
147
  const hasEmptyColumns = columnNames.some((col) => !col || col.trim() === "");
129
- const sampleRows = rows.slice(0, 3);
148
+ const sampleRows = nonEmptyRows.slice(0, 3);
130
149
  const sampleData = this.formatSampleData(sampleRows, sampleDataFormat, includeHeaders);
131
150
  if (hasEmptyColumns) {
132
151
  logger.warn("[CSVProcessor] CSV contains empty or blank column headers", {
@@ -138,7 +157,7 @@ export class CSVProcessor {
138
157
  }
139
158
  // Format parsed data
140
159
  logger.debug(`[CSVProcessor] Converting ${rowCount} rows to ${formatStyle} format`);
141
- const formatted = this.formatForLLM(rows, formatStyle, includeHeaders);
160
+ const formatted = this.formatForLLM(nonEmptyRows, formatStyle, includeHeaders);
142
161
  logger.info("[CSVProcessor] ✅ Processed CSV file", {
143
162
  formatStyle,
144
163
  rowCount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "8.31.1",
3
+ "version": "8.31.3",
4
4
  "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.",
5
5
  "author": {
6
6
  "name": "Juspay Technologies",