@juspay/neurolink 9.60.0 → 9.60.1

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,9 @@
1
+ ## [9.60.1](https://github.com/juspay/neurolink/compare/v9.60.0...v9.60.1) (2026-04-30)
2
+
3
+ ### Bug Fixes
4
+
5
+ - **(proxy):** validate pnpm global store compatibility before auto-update install ([ac573ad](https://github.com/juspay/neurolink/commit/ac573adc688aa28376d4b39ffa8e6bb7539cb40e))
6
+
1
7
  ## [9.60.0](https://github.com/juspay/neurolink/compare/v9.59.6...v9.60.0) (2026-04-30)
2
8
 
3
9
  ### Features
@@ -300,14 +300,42 @@ exit 127
300
300
  writeFileSync(TRAMPOLINE_PATH, script, { mode: 0o755 });
301
301
  chmodSync(TRAMPOLINE_PATH, 0o755);
302
302
  }
303
+ /**
304
+ * Check whether a pnpm binary can install into the global store.
305
+ *
306
+ * Multiple pnpm major versions can coexist (e.g., standalone v8 + nvm v10).
307
+ * They use different store layouts (`store/v10` vs `store/v10/v3`), so a
308
+ * pnpm that passes `--version` may still fail `pnpm add -g` with
309
+ * ERR_PNPM_UNEXPECTED_STORE. We detect this by running `pnpm root -g` and
310
+ * checking whether the resolved global root directory actually exists on disk.
311
+ */
312
+ function canInstallGlobally(pnpmPath) {
313
+ try {
314
+ const { execFileSync } = _require("node:child_process");
315
+ const { existsSync } = _require("fs");
316
+ const globalRoot = execFileSync(pnpmPath, ["root", "-g"], {
317
+ encoding: "utf8",
318
+ timeout: 10_000,
319
+ stdio: ["ignore", "pipe", "ignore"],
320
+ }).trim();
321
+ // If the global root exists, this pnpm version is compatible with
322
+ // the current store layout and can install packages there.
323
+ return !!globalRoot && existsSync(globalRoot);
324
+ }
325
+ catch {
326
+ return false;
327
+ }
328
+ }
303
329
  /**
304
330
  * Resolve the `pnpm` binary defensively.
305
331
  *
306
- * Tries multiple candidates in order of preference and validates each by
307
- * running `--version`. Returns the first one that actually works, along
308
- * with a list of all candidates tried (for diagnostics). This defends
309
- * against environments where `which pnpm` returns a broken shim or an
310
- * incompatible version.
332
+ * Tries multiple candidates in order of preference. Each candidate must:
333
+ * 1. Respond to `pnpm --version` (binary works)
334
+ * 2. Have a compatible global store (`pnpm root -g` points to an existing dir)
335
+ *
336
+ * This defends against environments with multiple pnpm major versions
337
+ * (e.g., standalone v8 + nvm v10) where the wrong one would fail with
338
+ * ERR_PNPM_UNEXPECTED_STORE on `pnpm add -g`.
311
339
  *
312
340
  * Honors `NEUROLINK_PNPM_PATH` as an escape hatch.
313
341
  */
@@ -348,17 +376,32 @@ function resolveFullPnpmPath() {
348
376
  seen.add(p);
349
377
  return true;
350
378
  });
351
- // Probe each candidate
379
+ // Probe each candidate: must pass --version AND have a compatible global store
352
380
  const tried = unique.map((path) => {
353
381
  const version = probeBinVersion(path);
354
- return { path, version, working: version !== undefined };
382
+ const working = version !== undefined;
383
+ const globalStoreOk = working ? canInstallGlobally(path) : false;
384
+ return { path, version, working, globalStoreOk };
355
385
  });
356
- const working = tried.find((r) => r.working);
357
- if (working) {
386
+ // Prefer a candidate that can actually install globally
387
+ const fullyWorking = tried.find((r) => r.working && r.globalStoreOk);
388
+ if (fullyWorking) {
389
+ return {
390
+ bin: fullyWorking.path,
391
+ resolved: true,
392
+ version: fullyWorking.version,
393
+ tried,
394
+ };
395
+ }
396
+ // Fall back to any candidate that at least responds to --version
397
+ // (better than nothing — the install may still fail, but will be
398
+ // caught and suppressed by the caller)
399
+ const anyWorking = tried.find((r) => r.working);
400
+ if (anyWorking) {
358
401
  return {
359
- bin: working.path,
402
+ bin: anyWorking.path,
360
403
  resolved: true,
361
- version: working.version,
404
+ version: anyWorking.version,
362
405
  tried,
363
406
  };
364
407
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.60.0",
3
+ "version": "9.60.1",
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": {