@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 +12 -0
- package/dist/cli/factories/commandFactory.js +20 -7
- package/dist/cli/utils/pathResolver.d.ts +25 -0
- package/dist/cli/utils/pathResolver.js +57 -0
- package/dist/lib/types/fileTypes.d.ts +1 -0
- package/dist/lib/utils/csvProcessor.js +25 -6
- package/dist/types/fileTypes.d.ts +1 -0
- package/dist/utils/csvProcessor.js +25 -6
- package/package.json +1 -1
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
|
-
//
|
|
304
|
-
// URLs
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -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
|
|
89
|
-
|
|
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 =
|
|
126
|
-
const columnNames =
|
|
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 =
|
|
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(
|
|
160
|
+
const formatted = this.formatForLLM(nonEmptyRows, formatStyle, includeHeaders);
|
|
142
161
|
logger.info("[CSVProcessor] ✅ Processed CSV file", {
|
|
143
162
|
formatStyle,
|
|
144
163
|
rowCount,
|
|
@@ -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
|
|
89
|
-
|
|
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 =
|
|
126
|
-
const columnNames =
|
|
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 =
|
|
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(
|
|
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.
|
|
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",
|