@juspay/neurolink 12.36.0 → 12.37.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.
- package/CHANGELOG.md +2 -2
- package/dist/agent/directTools.js +56 -22
- package/dist/browser/neurolink.min.js +374 -374
- package/dist/proxy/argumentParsing.d.ts +12 -0
- package/dist/proxy/argumentParsing.js +23 -0
- package/dist/proxy/codexOutboundFallback.d.ts +10 -6
- package/dist/proxy/codexOutboundFallback.js +163 -43
- package/dist/proxy/codexResponsesFormat.d.ts +125 -0
- package/dist/proxy/codexResponsesFormat.js +591 -0
- package/dist/proxy/codexToAnthropicFallback.d.ts +54 -0
- package/dist/proxy/codexToAnthropicFallback.js +408 -0
- package/dist/proxy/proxyTracer.d.ts +21 -0
- package/dist/proxy/proxyTracer.js +45 -0
- package/dist/proxy/streamLimits.d.ts +16 -0
- package/dist/proxy/streamLimits.js +16 -0
- package/dist/types/codex.d.ts +113 -1
- package/dist/types/file.d.ts +12 -0
- package/dist/types/proxy.d.ts +12 -1
- package/dist/utils/csvProcessor.d.ts +3 -3
- package/dist/utils/csvProcessor.js +53 -5
- package/docs-site/static/search-index.json +12 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
## [12.
|
|
1
|
+
## [12.37.0](https://github.com/juspay/neurolink/compare/v12.36.1...v12.37.0) (2026-09-28)
|
|
2
2
|
|
|
3
3
|
### Features
|
|
4
4
|
|
|
5
|
-
- **(proxy):** translate
|
|
5
|
+
- **(proxy):** translate Anthropic responses and tool calls to the Codex wire format ([bef0c19](https://github.com/juspay/neurolink/commit/bef0c19db259cd2d5f6ee4816ef6d9bbf72c8271)), closes [#1842](https://github.com/juspay/neurolink/issues/1842)
|
|
6
6
|
|
|
7
7
|
## [11.2.3](https://github.com/juspay/neurolink/compare/v11.2.2...v11.2.3) (2026-08-19)
|
|
8
8
|
|
|
@@ -40,6 +40,14 @@ function withRoots(description, policy) {
|
|
|
40
40
|
* `/home/app-evil` against `/home/app` gets through. A path that does not
|
|
41
41
|
* exist yet is checked on its nearest existing ancestor, which is what a
|
|
42
42
|
* write needs. Relative paths resolve against the first root.
|
|
43
|
+
*
|
|
44
|
+
* The result is a REAL path, and every consumer opens it with O_NOFOLLOW (or
|
|
45
|
+
* O_EXCL), so a link swapped in at the final component after this check is
|
|
46
|
+
* refused by the kernel — on Windows, which has no O_NOFOLLOW, only the
|
|
47
|
+
* O_EXCL create path keeps that guarantee. A directory component swapped for
|
|
48
|
+
* a link in that same window is not caught anywhere: Node exposes no
|
|
49
|
+
* openat/O_PATH walk, so that race is accepted here exactly as
|
|
50
|
+
* `FileDetector.loadFromPath` accepts it.
|
|
43
51
|
*/
|
|
44
52
|
function resolveWithinFileToolRoots(target, policy) {
|
|
45
53
|
const roots = rootsOf(policy);
|
|
@@ -65,6 +73,28 @@ function resolveWithinFileToolRoots(target, policy) {
|
|
|
65
73
|
}
|
|
66
74
|
/** Unavailable on Windows, where it is 0 and has no effect. */
|
|
67
75
|
const O_NOFOLLOW = fs.constants.O_NOFOLLOW ?? 0;
|
|
76
|
+
const SENSITIVE_SYSTEM_PATH_PREFIXES = [
|
|
77
|
+
"/etc/",
|
|
78
|
+
"/sys/",
|
|
79
|
+
"/proc/",
|
|
80
|
+
"/dev/",
|
|
81
|
+
"/root/",
|
|
82
|
+
"/.ssh/",
|
|
83
|
+
"/private/etc/",
|
|
84
|
+
"/private/var/",
|
|
85
|
+
"c:/windows/",
|
|
86
|
+
"c:/program files/",
|
|
87
|
+
"c:/programdata/",
|
|
88
|
+
];
|
|
89
|
+
/**
|
|
90
|
+
* Denylist of system directories analyzeCSV refuses even inside a permitted
|
|
91
|
+
* root. Expects an already-resolved real path, so what is judged is where the
|
|
92
|
+
* file actually lives rather than the name it was asked for by.
|
|
93
|
+
*/
|
|
94
|
+
function isSensitiveSystemPath(realPath) {
|
|
95
|
+
const normalized = realPath.toLowerCase().replace(/\\/g, "/");
|
|
96
|
+
return SENSITIVE_SYSTEM_PATH_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
97
|
+
}
|
|
68
98
|
/**
|
|
69
99
|
* Write without following a symlink at the final path component. The root
|
|
70
100
|
* check ran on the resolved path, so a link that appears there afterwards, or
|
|
@@ -238,6 +268,7 @@ function createReadFileTool(policy) {
|
|
|
238
268
|
},
|
|
239
269
|
});
|
|
240
270
|
}
|
|
271
|
+
/** List one directory inside the roots; entries are lstat'd, never followed. */
|
|
241
272
|
function createListDirectoryTool(policy) {
|
|
242
273
|
return tool({
|
|
243
274
|
description: withRoots("List files and directories in a specified directory", policy),
|
|
@@ -263,12 +294,20 @@ function createListDirectoryTool(policy) {
|
|
|
263
294
|
const filteredItems = includeHidden
|
|
264
295
|
? items
|
|
265
296
|
: items.filter((item) => !item.startsWith("."));
|
|
297
|
+
// lstat, not stat: an entry that is a symlink is reported as one, with
|
|
298
|
+
// its own metadata. Following it would leak the size and mtime of a
|
|
299
|
+
// target outside the roots, and a dangling link would fail the whole
|
|
300
|
+
// listing with ENOENT.
|
|
266
301
|
const itemDetails = filteredItems.map((item) => {
|
|
267
302
|
const itemPath = path.join(resolvedPath, item);
|
|
268
|
-
const stats = fs.
|
|
303
|
+
const stats = fs.lstatSync(itemPath);
|
|
269
304
|
return {
|
|
270
305
|
name: item,
|
|
271
|
-
type: stats.
|
|
306
|
+
type: stats.isSymbolicLink()
|
|
307
|
+
? "symlink"
|
|
308
|
+
: stats.isDirectory()
|
|
309
|
+
? "directory"
|
|
310
|
+
: "file",
|
|
272
311
|
size: stats.isFile() ? stats.size : undefined,
|
|
273
312
|
lastModified: stats.mtime.toISOString(),
|
|
274
313
|
};
|
|
@@ -336,6 +375,10 @@ function createWriteFileTool(policy) {
|
|
|
336
375
|
},
|
|
337
376
|
});
|
|
338
377
|
}
|
|
378
|
+
/**
|
|
379
|
+
* Execute one analyzeCSV call: contain the path, parse the file without
|
|
380
|
+
* following a final-component link, then run the requested aggregation.
|
|
381
|
+
*/
|
|
339
382
|
async function runAnalyzeCSV({ filePath, operation, column, maxRows = 1000 }, policy) {
|
|
340
383
|
const startTime = Date.now();
|
|
341
384
|
logger.info(`[analyzeCSV] 🚀 START: file=${filePath}, operation=${operation}, column=${column}, maxRows=${maxRows}`);
|
|
@@ -353,7 +396,9 @@ async function runAnalyzeCSV({ filePath, operation, column, maxRows = 1000 }, po
|
|
|
353
396
|
logger.info(`[analyzeCSV] Starting CSV parsing (max ${maxRows} rows)...`);
|
|
354
397
|
// #384: parseCSVFile now returns validated Record<string, string |
|
|
355
398
|
// undefined>[] rows, so the previous unchecked cast is unnecessary.
|
|
356
|
-
|
|
399
|
+
// The root check ran on the real path; O_NOFOLLOW refuses a link swapped
|
|
400
|
+
// in at the final component afterwards, as readFile does.
|
|
401
|
+
const rows = await CSVProcessor.parseCSVFile(resolvedPath, maxRows, undefined, { followSymlinks: false });
|
|
357
402
|
logger.info(`[analyzeCSV] ✅ CSV parsing complete: ${rows.length} rows`);
|
|
358
403
|
if (rows.length === 0) {
|
|
359
404
|
logger.warn(`[analyzeCSV] No data rows found`);
|
|
@@ -557,6 +602,7 @@ async function runAnalyzeCSV({ filePath, operation, column, maxRows = 1000 }, po
|
|
|
557
602
|
};
|
|
558
603
|
}
|
|
559
604
|
}
|
|
605
|
+
/** The analyzeCSV tool bound to `policy`; its schema refuses paths the roots would. */
|
|
560
606
|
function createAnalyzeCsvTool(policy) {
|
|
561
607
|
return tool({
|
|
562
608
|
description: withRoots("Analyze CSV file for accurate counting, aggregation, and statistical analysis. Use this for precise data operations like counting rows by column, calculating sums/averages, finding min/max values, etc. The tool reads the file directly - do NOT pass CSV content.", policy),
|
|
@@ -564,25 +610,13 @@ function createAnalyzeCsvTool(policy) {
|
|
|
564
610
|
filePath: z
|
|
565
611
|
.string()
|
|
566
612
|
.refine((inputPath) => {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
"/proc/",
|
|
575
|
-
"/dev/",
|
|
576
|
-
"/root/",
|
|
577
|
-
"/.ssh/",
|
|
578
|
-
"/private/etc/",
|
|
579
|
-
"/private/var/",
|
|
580
|
-
"c:/windows/",
|
|
581
|
-
"c:/program files/",
|
|
582
|
-
"c:/programdata/",
|
|
583
|
-
];
|
|
584
|
-
return (!sensitivePatterns.some((pattern) => normalizedPath.startsWith(pattern)) &&
|
|
585
|
-
resolveWithinFileToolRoots(inputPath, policy).path !== undefined);
|
|
613
|
+
// Contain first, then apply the system-directory denylist to the
|
|
614
|
+
// REAL path the guard resolved: a relative path is thereby judged
|
|
615
|
+
// against the tool root rather than process.cwd(), and a symlink
|
|
616
|
+
// into /etc under a wide root is seen for where it points.
|
|
617
|
+
const contained = resolveWithinFileToolRoots(inputPath, policy);
|
|
618
|
+
return (contained.path !== undefined &&
|
|
619
|
+
!isSensitiveSystemPath(contained.path));
|
|
586
620
|
}, {
|
|
587
621
|
message: "Invalid file path: access to system directories or paths outside the tool roots is not allowed",
|
|
588
622
|
})
|