@juspay/neurolink 9.54.4 → 9.54.6

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 (52) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/auth/tokenStore.d.ts +0 -1
  3. package/dist/browser/neurolink.min.js +305 -305
  4. package/dist/cli/commands/proxy.js +333 -31
  5. package/dist/client/httpClient.d.ts +1 -2
  6. package/dist/client/sseClient.d.ts +1 -2
  7. package/dist/client/wsClient.d.ts +1 -2
  8. package/dist/core/toolEvents.d.ts +0 -1
  9. package/dist/lib/auth/tokenStore.d.ts +0 -1
  10. package/dist/lib/client/httpClient.d.ts +1 -2
  11. package/dist/lib/client/sseClient.d.ts +1 -2
  12. package/dist/lib/client/wsClient.d.ts +1 -2
  13. package/dist/lib/core/toolEvents.d.ts +0 -1
  14. package/dist/lib/memory/hippocampusInitializer.d.ts +1 -2
  15. package/dist/lib/memory/hippocampusInitializer.js +1 -1
  16. package/dist/lib/models/anthropicModels.d.ts +0 -1
  17. package/dist/lib/models/anthropicModels.js +1 -0
  18. package/dist/lib/neurolink.js +1 -1
  19. package/dist/lib/observability/exporterRegistry.d.ts +1 -2
  20. package/dist/lib/observability/retryPolicy.d.ts +0 -1
  21. package/dist/lib/observability/sampling/samplers.d.ts +0 -1
  22. package/dist/lib/observability/spanProcessor.d.ts +0 -1
  23. package/dist/lib/processors/errors/errorHelpers.d.ts +1 -1
  24. package/dist/lib/providers/googleNativeGemini3.d.ts +1 -2
  25. package/dist/lib/proxy/proxyHealth.d.ts +0 -1
  26. package/dist/lib/proxy/routingPolicy.d.ts +4 -30
  27. package/dist/lib/proxy/routingPolicy.js +14 -46
  28. package/dist/lib/proxy/usageStats.d.ts +0 -1
  29. package/dist/lib/proxy/usageStats.js +0 -7
  30. package/dist/lib/server/routes/claudeProxyRoutes.js +55 -95
  31. package/dist/lib/types/proxy.d.ts +2 -12
  32. package/dist/lib/utils/sanitizers/filename.d.ts +0 -1
  33. package/dist/memory/hippocampusInitializer.d.ts +1 -2
  34. package/dist/memory/hippocampusInitializer.js +1 -1
  35. package/dist/models/anthropicModels.d.ts +0 -1
  36. package/dist/models/anthropicModels.js +1 -0
  37. package/dist/neurolink.js +1 -1
  38. package/dist/observability/exporterRegistry.d.ts +1 -2
  39. package/dist/observability/retryPolicy.d.ts +0 -1
  40. package/dist/observability/sampling/samplers.d.ts +0 -1
  41. package/dist/observability/spanProcessor.d.ts +0 -1
  42. package/dist/processors/errors/errorHelpers.d.ts +1 -1
  43. package/dist/providers/googleNativeGemini3.d.ts +1 -2
  44. package/dist/proxy/proxyHealth.d.ts +0 -1
  45. package/dist/proxy/routingPolicy.d.ts +4 -30
  46. package/dist/proxy/routingPolicy.js +14 -46
  47. package/dist/proxy/usageStats.d.ts +0 -1
  48. package/dist/proxy/usageStats.js +0 -7
  49. package/dist/server/routes/claudeProxyRoutes.js +55 -95
  50. package/dist/types/proxy.d.ts +2 -12
  51. package/dist/utils/sanitizers/filename.d.ts +0 -1
  52. package/package.json +1 -1
@@ -201,27 +201,200 @@ async function isProxyHealthy(host, port, timeoutMs) {
201
201
  return false;
202
202
  }
203
203
  }
204
+ // ---------------------------------------------------------------------------
205
+ // Stable entrypoint for launchd
206
+ // ---------------------------------------------------------------------------
207
+ /**
208
+ * Path to a small trampoline script that the plist invokes.
209
+ * The trampoline re-resolves `neurolink` via PATH on every launch,
210
+ * so launchd never gets pinned to a version-specific store path.
211
+ */
212
+ const TRAMPOLINE_DIR = join(homedir(), ".neurolink", "bin");
213
+ const TRAMPOLINE_PATH = join(TRAMPOLINE_DIR, "neurolink-proxy");
214
+ /**
215
+ * Verify a candidate bin path actually runs by invoking `--version` on it.
216
+ * Returns the version string on success, or undefined on any failure.
217
+ */
218
+ function probeBinVersion(binPath) {
219
+ try {
220
+ const { execFileSync } = _require("node:child_process");
221
+ const out = execFileSync(binPath, ["--version"], {
222
+ encoding: "utf8",
223
+ timeout: 5_000,
224
+ stdio: ["ignore", "pipe", "ignore"],
225
+ }).trim();
226
+ return out || undefined;
227
+ }
228
+ catch {
229
+ return undefined;
230
+ }
231
+ }
232
+ /**
233
+ * Write (or overwrite) the trampoline shell script.
234
+ *
235
+ * Defensive design: the trampoline tries multiple candidates in order and
236
+ * only `exec`s one whose `--version` check succeeds. If every PATH-based
237
+ * candidate is broken (stale shims, missing packages), it falls back to the
238
+ * baked-in `node + script` path that was verified to work at install time.
239
+ */
240
+ function writeTrampoline() {
241
+ const { writeFileSync, mkdirSync, existsSync, chmodSync } = _require("fs");
242
+ if (!existsSync(TRAMPOLINE_DIR)) {
243
+ mkdirSync(TRAMPOLINE_DIR, { recursive: true });
244
+ }
245
+ // Baked-in fallback: the specific node + JS script currently running
246
+ // (guaranteed to work, since we ARE running). Used only if all PATH-based
247
+ // candidates fail their --version probe.
248
+ const bakedNode = process.execPath;
249
+ const bakedScript = process.argv[1] ?? join(__dirname, "..", "index.js");
250
+ // Shell-escape the baked paths (they shouldn't contain quotes in practice,
251
+ // but be safe for paths with spaces).
252
+ const shEscape = (s) => `'${s.replace(/'/g, "'\\''")}'`;
253
+ const script = `#!/bin/sh
254
+ # Auto-generated by \`neurolink proxy install\` — do not edit.
255
+ # Resolves a working neurolink binary on every launchd invocation so the
256
+ # plist never gets pinned to a broken/stale shim.
257
+
258
+ # Probe a candidate: must be executable and respond to --version cleanly.
259
+ _try() {
260
+ [ -n "$1" ] && [ -x "$1" ] || return 1
261
+ "$1" --version >/dev/null 2>&1 || return 1
262
+ return 0
263
+ }
264
+
265
+ # 1. Explicit user override (escape hatch for broken environments).
266
+ if [ -n "\${NEUROLINK_BIN:-}" ]; then
267
+ if _try "$NEUROLINK_BIN"; then
268
+ exec "$NEUROLINK_BIN" "$@"
269
+ fi
270
+ echo "[neurolink-proxy] WARN: NEUROLINK_BIN=$NEUROLINK_BIN is not runnable, trying defaults" >&2
271
+ fi
272
+
273
+ # 2. PATH-based and common install locations. First working one wins.
274
+ for cand in \\
275
+ "$(command -v neurolink 2>/dev/null || true)" \\
276
+ "\${PNPM_HOME:-}/neurolink" \\
277
+ "$HOME/.local/share/pnpm/neurolink" \\
278
+ "$HOME/Library/pnpm/neurolink" \\
279
+ "/usr/local/bin/neurolink" \\
280
+ "/opt/homebrew/bin/neurolink"; do
281
+ if _try "$cand"; then
282
+ exec "$cand" "$@"
283
+ fi
284
+ done
285
+
286
+ # 3. Baked-in fallback: the exact node + script that worked at install time.
287
+ # Always valid at install time; may become stale after package updates
288
+ # (but at that point the PATH candidates above should work).
289
+ BAKED_NODE=${shEscape(bakedNode)}
290
+ BAKED_SCRIPT=${shEscape(bakedScript)}
291
+ if [ -x "$BAKED_NODE" ] && [ -f "$BAKED_SCRIPT" ]; then
292
+ exec "$BAKED_NODE" "$BAKED_SCRIPT" "$@"
293
+ fi
294
+
295
+ echo "[neurolink-proxy] FATAL: no working neurolink binary found." >&2
296
+ echo "[neurolink-proxy] Tried: PATH, \\$PNPM_HOME, \\$HOME/.local/share/pnpm, \\$HOME/Library/pnpm, /usr/local/bin, /opt/homebrew/bin, baked-in install path." >&2
297
+ echo "[neurolink-proxy] Fix: reinstall with 'pnpm add -g @juspay/neurolink' or set NEUROLINK_BIN=/path/to/working/neurolink." >&2
298
+ exit 127
299
+ `;
300
+ writeFileSync(TRAMPOLINE_PATH, script, { mode: 0o755 });
301
+ chmodSync(TRAMPOLINE_PATH, 0o755);
302
+ }
303
+ /**
304
+ * Resolve the `pnpm` binary defensively.
305
+ *
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.
311
+ *
312
+ * Honors `NEUROLINK_PNPM_PATH` as an escape hatch.
313
+ */
314
+ function resolveFullPnpmPath() {
315
+ const candidates = [];
316
+ // 1. User override
317
+ if (process.env.NEUROLINK_PNPM_PATH) {
318
+ candidates.push(process.env.NEUROLINK_PNPM_PATH);
319
+ }
320
+ // 2. PNPM_HOME (pnpm's own env variable)
321
+ if (process.env.PNPM_HOME) {
322
+ candidates.push(join(process.env.PNPM_HOME, "pnpm"));
323
+ }
324
+ // 3. `which pnpm` — whatever is on PATH
325
+ try {
326
+ const { execFileSync } = _require("node:child_process");
327
+ const whichOut = execFileSync("which", ["pnpm"], {
328
+ encoding: "utf8",
329
+ timeout: 5_000,
330
+ stdio: ["ignore", "pipe", "ignore"],
331
+ }).trim();
332
+ if (whichOut) {
333
+ candidates.push(whichOut);
334
+ }
335
+ }
336
+ catch {
337
+ // ignore
338
+ }
339
+ // 4. Common standalone installer locations
340
+ candidates.push(join(homedir(), ".local", "share", "pnpm", "pnpm"));
341
+ candidates.push(join(homedir(), "Library", "pnpm", "pnpm"));
342
+ // Dedupe while preserving order
343
+ const seen = new Set();
344
+ const unique = candidates.filter((p) => {
345
+ if (!p || seen.has(p)) {
346
+ return false;
347
+ }
348
+ seen.add(p);
349
+ return true;
350
+ });
351
+ // Probe each candidate
352
+ const tried = unique.map((path) => {
353
+ const version = probeBinVersion(path);
354
+ return { path, version, working: version !== undefined };
355
+ });
356
+ const working = tried.find((r) => r.working);
357
+ if (working) {
358
+ return {
359
+ bin: working.path,
360
+ resolved: true,
361
+ version: working.version,
362
+ tried,
363
+ };
364
+ }
365
+ return { bin: "pnpm", resolved: false, tried };
366
+ }
204
367
  function spawnFailOpenGuard(host, port, parentPid) {
368
+ // The guard runs the same version as this process, so process.argv[1]
369
+ // (the currently-running script) is correct here — no stale-path risk.
205
370
  const entryScript = process.argv[1];
206
371
  if (!entryScript) {
207
372
  return undefined;
208
373
  }
374
+ const args = [
375
+ entryScript,
376
+ "proxy",
377
+ "guard",
378
+ "--host",
379
+ host,
380
+ "--port",
381
+ String(port),
382
+ "--parent-pid",
383
+ String(parentPid),
384
+ "--quiet",
385
+ ];
386
+ // Write guard stdout/stderr to a log file instead of discarding them.
387
+ const { openSync, closeSync, mkdirSync, existsSync } = _require("fs");
388
+ const guardLogDir = join(homedir(), ".neurolink", "logs");
389
+ if (!existsSync(guardLogDir)) {
390
+ mkdirSync(guardLogDir, { recursive: true });
391
+ }
392
+ const guardLogPath = join(guardLogDir, "proxy-guard.log");
393
+ const logFd = openSync(guardLogPath, "a");
209
394
  try {
210
- const args = [
211
- entryScript,
212
- "proxy",
213
- "guard",
214
- "--host",
215
- host,
216
- "--port",
217
- String(port),
218
- "--parent-pid",
219
- String(parentPid),
220
- "--quiet",
221
- ];
222
395
  const child = spawn(process.execPath, args, {
223
396
  detached: true,
224
- stdio: "ignore",
397
+ stdio: ["ignore", logFd, logFd],
225
398
  });
226
399
  child.unref();
227
400
  return child.pid;
@@ -230,6 +403,9 @@ function spawnFailOpenGuard(host, port, parentPid) {
230
403
  logger.debug(`[proxy] failed to start fail-open guard: ${error instanceof Error ? error.message : String(error)}`);
231
404
  return undefined;
232
405
  }
406
+ finally {
407
+ closeSync(logFd); // parent closes its copy; child keeps the fd
408
+ }
233
409
  }
234
410
  async function runProxyTelemetryManager(command) {
235
411
  const { existsSync } = await import("fs");
@@ -568,10 +744,7 @@ async function createProxyStartApp(params) {
568
744
  success: account.successCount,
569
745
  errors: account.errorCount,
570
746
  rateLimits: account.rateLimitCount,
571
- backoffLevel: account.currentBackoffLevel,
572
- cooling: account.coolingUntil
573
- ? account.coolingUntil > Date.now()
574
- : false,
747
+ cooling: false, // No persistent cooldown — always active
575
748
  })),
576
749
  },
577
750
  config: params.proxyConfig
@@ -1294,34 +1467,113 @@ export const proxyGuardCommand = {
1294
1467
  logger.always(`[guard] WARNING: invalid version format "${result.latestVersion}", skipping`);
1295
1468
  return;
1296
1469
  }
1297
- logger.always(`[guard] traffic quiet, installing @juspay/neurolink@${result.latestVersion}...`);
1470
+ // Resolve pnpm to a deterministic path, validating that it actually
1471
+ // runs (some environments have broken shims on PATH).
1472
+ const pnpmResolution = resolveFullPnpmPath();
1473
+ // Log the full candidate list so operators can see why a particular
1474
+ // pnpm was chosen (or why none worked).
1475
+ logger.always(`[guard] pnpm candidates: ${pnpmResolution.tried
1476
+ .map((c) => `${c.path}(${c.working ? `v${c.version}` : "BROKEN"})`)
1477
+ .join(", ")}`);
1478
+ if (!pnpmResolution.resolved) {
1479
+ // Environmental problem, not version-specific — skip this cycle
1480
+ // without suppressing the version (so we retry on the next tick
1481
+ // once the user fixes pnpm).
1482
+ logger.always(`[guard] WARNING: no working pnpm found; skipping update cycle. Install pnpm or set NEUROLINK_PNPM_PATH.`);
1483
+ return;
1484
+ }
1485
+ logger.always(`[guard] traffic quiet, installing @juspay/neurolink@${result.latestVersion} via ${pnpmResolution.bin} (pnpm v${pnpmResolution.version})...`);
1298
1486
  const { execFileSync } = await import("node:child_process");
1299
1487
  try {
1300
- execFileSync("pnpm", ["add", "-g", `@juspay/neurolink@${result.latestVersion}`], {
1488
+ execFileSync(pnpmResolution.bin, ["add", "-g", `@juspay/neurolink@${result.latestVersion}`], {
1301
1489
  timeout: 120_000,
1302
1490
  stdio: "pipe",
1303
1491
  });
1304
1492
  }
1305
1493
  catch (installErr) {
1306
- logger.always(`[guard] WARNING: pnpm install failed: ${installErr instanceof Error ? installErr.message : String(installErr)}`);
1307
- suppressVersion(result.latestVersion, "install_failed");
1494
+ // Capture stderr for actionable diagnostics
1495
+ const stderr = installErr &&
1496
+ typeof installErr === "object" &&
1497
+ "stderr" in installErr
1498
+ ? String(installErr.stderr).slice(0, 500)
1499
+ : "";
1500
+ const msg = installErr instanceof Error
1501
+ ? installErr.message
1502
+ : String(installErr);
1503
+ logger.always(`[guard] WARNING: pnpm install failed: ${msg}${stderr ? `\n stderr: ${stderr}` : ""}`);
1504
+ suppressVersion(result.latestVersion, `install_failed: ${msg.slice(0, 200)}${stderr ? ` | stderr: ${stderr.slice(0, 200)}` : ""}`);
1308
1505
  return;
1309
1506
  }
1310
- // 4. Restart via launchctl
1507
+ // 4. Rewrite the launchd plist so it picks up the (possibly new)
1508
+ // stable bin path, then restart via launchctl.
1509
+ try {
1510
+ const { writeFileSync, existsSync: fsExists, mkdirSync: fsMkdir, } = await import("fs");
1511
+ if (!fsExists(PLIST_DIR)) {
1512
+ fsMkdir(PLIST_DIR, { recursive: true });
1513
+ }
1514
+ // Rewrite the trampoline and plist so the restarted service
1515
+ // resolves the newly installed binary via PATH.
1516
+ writeTrampoline();
1517
+ // Validate the trampoline actually resolves to the NEW version
1518
+ // before asking launchd to restart. If the install somehow left
1519
+ // PATH still pointing at the old version, don't kickstart.
1520
+ const probed = probeBinVersion(TRAMPOLINE_PATH);
1521
+ if (!probed) {
1522
+ logger.always(`[guard] WARNING: trampoline does not resolve to a working neurolink after install; skipping restart.`);
1523
+ suppressVersion(result.latestVersion, `trampoline_broken_after_install: ${TRAMPOLINE_PATH} --version failed`);
1524
+ return;
1525
+ }
1526
+ if (probed !== result.latestVersion) {
1527
+ // The trampoline resolves to a DIFFERENT version than what we
1528
+ // just installed. This means `pnpm add -g` installed into a
1529
+ // store that PATH doesn't reach (store mismatch), or PATH still
1530
+ // shadows with an older shim. Restarting would run the wrong
1531
+ // version — abort.
1532
+ logger.always(`[guard] ABORT: trampoline resolves to v${probed} but installed v${result.latestVersion}.`);
1533
+ logger.always(`[guard] pnpm used: ${pnpmResolution.bin} (v${pnpmResolution.version})`);
1534
+ logger.always(`[guard] This usually means pnpm's global store doesn't match the PATH-visible neurolink.`);
1535
+ logger.always(`[guard] Fix: run 'pnpm add -g @juspay/neurolink' with the SAME pnpm whose bin dir is on PATH.`);
1536
+ suppressVersion(result.latestVersion, `version_mismatch: trampoline=${probed} expected=${result.latestVersion} pnpm=${pnpmResolution.bin}(v${pnpmResolution.version})`);
1537
+ return;
1538
+ }
1539
+ const existingArgs = parseExistingPlistArgs();
1540
+ const updatedPlist = buildPlist(port, host, existingArgs.envFile, existingArgs.configFile);
1541
+ writeFileSync(PLIST_PATH, updatedPlist, "utf-8");
1542
+ logger.always(`[guard] trampoline (resolves to v${probed}) + plist rewritten at ${PLIST_PATH}`);
1543
+ }
1544
+ catch (plistErr) {
1545
+ logger.always(`[guard] WARNING: failed to rewrite plist (restart may use stale path): ${plistErr instanceof Error ? plistErr.message : String(plistErr)}`);
1546
+ // Continue with restart anyway — the stable bin symlink may still be correct
1547
+ }
1311
1548
  // Signal the health loop to not exit when it detects
1312
1549
  // the parent PID is gone — we're intentionally restarting.
1313
1550
  updateRestartInProgress = true;
1314
- logger.always(`[guard] restarting proxy via launchctl...`);
1551
+ logger.always(`[guard] restarting proxy via launchctl bootout/bootstrap...`);
1315
1552
  const uid = process.getuid?.() ?? 501;
1316
1553
  try {
1317
- execFileSync("launchctl", ["kickstart", "-k", `gui/${uid}/${PLIST_LABEL}`], {
1554
+ // bootout unloads the in-memory job definition. This is required
1555
+ // because `kickstart -k` reuses the cached plist and ignores any
1556
+ // on-disk changes (like the trampoline rewrite above).
1557
+ try {
1558
+ execFileSync("launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { timeout: 10_000, stdio: "pipe" });
1559
+ }
1560
+ catch {
1561
+ // Job may not be loaded (first install, or already unloaded)
1562
+ }
1563
+ // bootstrap loads the plist fresh from disk, picking up the
1564
+ // new trampoline-based ProgramArguments.
1565
+ execFileSync("launchctl", ["bootstrap", `gui/${uid}`, PLIST_PATH], {
1318
1566
  timeout: 10_000,
1319
1567
  stdio: "pipe",
1320
1568
  });
1321
1569
  }
1322
- catch {
1323
- logger.always(`[guard] WARNING: launchctl kickstart failed`);
1324
- suppressVersion(result.latestVersion, "restart_failed");
1570
+ catch (restartErr) {
1571
+ updateRestartInProgress = false;
1572
+ const msg = restartErr instanceof Error
1573
+ ? restartErr.message
1574
+ : String(restartErr);
1575
+ logger.always(`[guard] WARNING: launchctl bootstrap failed: ${msg}`);
1576
+ suppressVersion(result.latestVersion, `restart_failed: ${msg.slice(0, 200)}`);
1325
1577
  return;
1326
1578
  }
1327
1579
  // 5. Wait for healthy restart
@@ -1584,9 +1836,42 @@ function buildLaunchdPath() {
1584
1836
  }
1585
1837
  return [...segments].join(":");
1586
1838
  }
1839
+ /**
1840
+ * Parse the existing launchd plist to extract --env-file and --config values.
1841
+ * Used by the auto-updater to rewrite the plist with the same configuration.
1842
+ */
1843
+ function parseExistingPlistArgs() {
1844
+ try {
1845
+ const { readFileSync, existsSync: fsExists } = _require("fs");
1846
+ if (!fsExists(PLIST_PATH)) {
1847
+ return {};
1848
+ }
1849
+ const xml = readFileSync(PLIST_PATH, "utf-8");
1850
+ // Extract --env-file value: <string>--env-file</string>\n <string>VALUE</string>
1851
+ const envMatch = xml.match(/<string>--env-file<\/string>\s*<string>([^<]+)<\/string>/);
1852
+ const configMatch = xml.match(/<string>--config<\/string>\s*<string>([^<]+)<\/string>/);
1853
+ // Unescape XML entities so buildPlist() doesn't double-escape them.
1854
+ const unescapeXml = (value) => value
1855
+ ?.replace(/&apos;/g, "'")
1856
+ .replace(/&quot;/g, '"')
1857
+ .replace(/&gt;/g, ">")
1858
+ .replace(/&lt;/g, "<")
1859
+ .replace(/&amp;/g, "&");
1860
+ return {
1861
+ envFile: unescapeXml(envMatch?.[1]),
1862
+ configFile: unescapeXml(configMatch?.[1]),
1863
+ };
1864
+ }
1865
+ catch {
1866
+ return {};
1867
+ }
1868
+ }
1587
1869
  function buildPlist(port, host, envFile, configFile) {
1588
- const nodeExec = escapeXml(process.execPath);
1589
- const entryScript = escapeXml(process.argv[1] ?? join(__dirname, "..", "index.js"));
1870
+ // The plist invokes the trampoline script (a tiny shell wrapper at
1871
+ // ~/.neurolink/bin/neurolink-proxy) which re-resolves the real
1872
+ // `neurolink` binary via PATH on every launch. This way, launchd
1873
+ // is never pinned to a version-specific pnpm store path.
1874
+ const trampolinePath = escapeXml(TRAMPOLINE_PATH);
1590
1875
  const envFileArgs = envFile
1591
1876
  ? `
1592
1877
  <string>--env-file</string>
@@ -1607,8 +1892,7 @@ function buildPlist(port, host, envFile, configFile) {
1607
1892
 
1608
1893
  <key>ProgramArguments</key>
1609
1894
  <array>
1610
- <string>${nodeExec}</string>
1611
- <string>${entryScript}</string>
1895
+ <string>${trampolinePath}</string>
1612
1896
  <string>proxy</string>
1613
1897
  <string>start</string>
1614
1898
  <string>--port</string>
@@ -1709,6 +1993,24 @@ export const proxyInstallCommand = {
1709
1993
  if (!existsSync(PLIST_DIR)) {
1710
1994
  mkdirSync(PLIST_DIR, { recursive: true });
1711
1995
  }
1996
+ writeTrampoline();
1997
+ console.info(chalk.green(`✓ Trampoline written to ${TRAMPOLINE_PATH}`));
1998
+ // Sanity-check: run the trampoline itself and confirm it resolves to
1999
+ // a working neurolink binary. This catches environments where every
2000
+ // PATH-based candidate is broken AND the baked-in path is unreachable.
2001
+ const trampolineVersion = probeBinVersion(TRAMPOLINE_PATH);
2002
+ if (!trampolineVersion) {
2003
+ console.info(chalk.red(`✗ Trampoline validation failed: ${TRAMPOLINE_PATH} --version did not run cleanly.`));
2004
+ console.info(chalk.yellow(` The launchd service would not be able to start neurolink. Fix your install first.`));
2005
+ console.info(chalk.yellow(` Try: 'pnpm add -g @juspay/neurolink' or set NEUROLINK_BIN=/path/to/working/neurolink.`));
2006
+ process.exit(1);
2007
+ }
2008
+ if (trampolineVersion !== PROXY_VERSION) {
2009
+ console.info(chalk.red(`✗ Trampoline resolves to v${trampolineVersion} but this installer is v${PROXY_VERSION}.`));
2010
+ console.info(chalk.yellow(` PATH may shadow this installation with an older version. Fix your PATH or set NEUROLINK_BIN.`));
2011
+ process.exit(1);
2012
+ }
2013
+ console.info(chalk.green(`✓ Trampoline validated (resolves to neurolink v${trampolineVersion})`));
1712
2014
  const plist = buildPlist(port, host, envFile, configFile);
1713
2015
  writeFileSync(PLIST_PATH, plist, "utf-8");
1714
2016
  console.info(chalk.green(`✓ Plist written to ${PLIST_PATH}`));
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @module @neurolink/client
8
8
  */
9
- import type { ClientConfig, ClientRequestOptions, ClientApiResponse, ClientApiError, ClientRetryConfig, ClientMiddleware, ClientMiddlewareRequest, ClientMiddlewareResponse, ClientMiddlewareContext, ClientStreamCallbacks, ClientStreamEvent, ClientStreamResult, ClientGenerateRequestOptions, ClientGenerateResponse, ClientStreamRequestOptions, ClientAgentExecuteOptions, ClientAgentExecuteResult, ClientAgentInfo, ClientWorkflowExecuteOptions, ClientWorkflowExecuteResult, ClientWorkflowInfo, ClientToolInfo, ClientProviderStatus, ClientWebSocketOptions, ClientWebSocketState, ClientWebSocketMessageHandler, UnknownRecord } from "../types/index.js";
9
+ import type { ClientConfig, ClientRequestOptions, ClientApiResponse, ClientMiddleware, ClientStreamCallbacks, ClientStreamResult, ClientGenerateRequestOptions, ClientGenerateResponse, ClientStreamRequestOptions, ClientAgentExecuteOptions, ClientAgentExecuteResult, ClientAgentInfo, ClientWorkflowExecuteOptions, ClientWorkflowExecuteResult, ClientWorkflowInfo, ClientToolInfo, ClientProviderStatus, ClientWebSocketOptions, ClientWebSocketState, ClientWebSocketMessageHandler, UnknownRecord } from "../types/index.js";
10
10
  import { HttpError, ClientNetworkError, ClientTimeoutError } from "./errors.js";
11
11
  /**
12
12
  * Combine multiple AbortSignals into a single signal.
@@ -293,4 +293,3 @@ export declare class NeuroLinkClient {
293
293
  * ```
294
294
  */
295
295
  export declare function createClient(config: ClientConfig): NeuroLinkClient;
296
- export type { ClientConfig, ClientRequestOptions, ClientApiResponse, ClientApiError, ClientRetryConfig, ClientMiddleware, ClientMiddlewareRequest, ClientMiddlewareResponse, ClientMiddlewareContext, ClientStreamCallbacks, ClientStreamEvent, ClientStreamResult, ClientGenerateRequestOptions, ClientGenerateResponse, ClientStreamRequestOptions, ClientAgentExecuteOptions, ClientAgentExecuteResult, ClientAgentInfo, ClientWorkflowExecuteOptions, ClientWorkflowExecuteResult, ClientWorkflowInfo, ClientToolInfo, ClientProviderStatus, ClientWebSocketOptions, ClientWebSocketState, ClientWebSocketMessageHandler, };
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @module @neurolink/client/sseClient
9
9
  */
10
- import type { ClientStreamCallbacks, ClientStreamEvent as StreamEvent, ClientStreamResult as StreamResult, ClientApiError, SSEConfig, SSEEventHandlers, SSERequestOptions, SSEState } from "../types/index.js";
10
+ import type { ClientStreamCallbacks, SSEConfig, SSEEventHandlers, SSERequestOptions, SSEState } from "../types/index.js";
11
11
  /**
12
12
  * SSE streaming client for NeuroLink
13
13
  *
@@ -112,4 +112,3 @@ export declare class NeuroLinkSSE {
112
112
  * ```
113
113
  */
114
114
  export declare function createSSEClient(config: SSEConfig): NeuroLinkSSE;
115
- export type { ClientStreamCallbacks, StreamEvent, StreamResult, ClientApiError, };
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @module @neurolink/client/wsClient
9
9
  */
10
- import type { ClientStreamCallbacks, ClientStreamEvent as StreamEvent, ClientStreamResult as StreamResult, ClientApiError, WebSocketEventHandlers, ClientClientWebSocketState, ClientWebSocketMessage, ClientWebSocketConfig } from "../types/index.js";
10
+ import type { ClientStreamCallbacks, WebSocketEventHandlers, ClientClientWebSocketState, ClientWebSocketMessage, ClientWebSocketConfig } from "../types/index.js";
11
11
  /**
12
12
  * WebSocket streaming client for NeuroLink
13
13
  *
@@ -119,4 +119,3 @@ export declare class NeuroLinkWebSocket {
119
119
  * ```
120
120
  */
121
121
  export declare function createWebSocketClient(config: ClientWebSocketConfig): NeuroLinkWebSocket;
122
- export type { ClientStreamCallbacks, StreamEvent, StreamResult, ClientApiError, };
@@ -1,3 +1,2 @@
1
1
  import type { ToolEventPayload } from "../types/index.js";
2
- export type { ToolEventPayload };
3
2
  export declare function createToolEventPayload(toolName: string, payload?: Omit<ToolEventPayload, "tool" | "toolName">): ToolEventPayload;
@@ -13,7 +13,6 @@
13
13
  * - Cross-platform support (Unix/macOS/Windows)
14
14
  */
15
15
  import type { StoredOAuthTokens, TokenRefresher } from "../types/index.js";
16
- export type { StoredOAuthTokens, TokenRefresher };
17
16
  /**
18
17
  * Secure token storage for OAuth tokens with multi-provider support
19
18
  *
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @module @neurolink/client
8
8
  */
9
- import type { ClientConfig, ClientRequestOptions, ClientApiResponse, ClientApiError, ClientRetryConfig, ClientMiddleware, ClientMiddlewareRequest, ClientMiddlewareResponse, ClientMiddlewareContext, ClientStreamCallbacks, ClientStreamEvent, ClientStreamResult, ClientGenerateRequestOptions, ClientGenerateResponse, ClientStreamRequestOptions, ClientAgentExecuteOptions, ClientAgentExecuteResult, ClientAgentInfo, ClientWorkflowExecuteOptions, ClientWorkflowExecuteResult, ClientWorkflowInfo, ClientToolInfo, ClientProviderStatus, ClientWebSocketOptions, ClientWebSocketState, ClientWebSocketMessageHandler, UnknownRecord } from "../types/index.js";
9
+ import type { ClientConfig, ClientRequestOptions, ClientApiResponse, ClientMiddleware, ClientStreamCallbacks, ClientStreamResult, ClientGenerateRequestOptions, ClientGenerateResponse, ClientStreamRequestOptions, ClientAgentExecuteOptions, ClientAgentExecuteResult, ClientAgentInfo, ClientWorkflowExecuteOptions, ClientWorkflowExecuteResult, ClientWorkflowInfo, ClientToolInfo, ClientProviderStatus, ClientWebSocketOptions, ClientWebSocketState, ClientWebSocketMessageHandler, UnknownRecord } from "../types/index.js";
10
10
  import { HttpError, ClientNetworkError, ClientTimeoutError } from "./errors.js";
11
11
  /**
12
12
  * Combine multiple AbortSignals into a single signal.
@@ -293,4 +293,3 @@ export declare class NeuroLinkClient {
293
293
  * ```
294
294
  */
295
295
  export declare function createClient(config: ClientConfig): NeuroLinkClient;
296
- export type { ClientConfig, ClientRequestOptions, ClientApiResponse, ClientApiError, ClientRetryConfig, ClientMiddleware, ClientMiddlewareRequest, ClientMiddlewareResponse, ClientMiddlewareContext, ClientStreamCallbacks, ClientStreamEvent, ClientStreamResult, ClientGenerateRequestOptions, ClientGenerateResponse, ClientStreamRequestOptions, ClientAgentExecuteOptions, ClientAgentExecuteResult, ClientAgentInfo, ClientWorkflowExecuteOptions, ClientWorkflowExecuteResult, ClientWorkflowInfo, ClientToolInfo, ClientProviderStatus, ClientWebSocketOptions, ClientWebSocketState, ClientWebSocketMessageHandler, };
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @module @neurolink/client/sseClient
9
9
  */
10
- import type { ClientStreamCallbacks, ClientStreamEvent as StreamEvent, ClientStreamResult as StreamResult, ClientApiError, SSEConfig, SSEEventHandlers, SSERequestOptions, SSEState } from "../types/index.js";
10
+ import type { ClientStreamCallbacks, SSEConfig, SSEEventHandlers, SSERequestOptions, SSEState } from "../types/index.js";
11
11
  /**
12
12
  * SSE streaming client for NeuroLink
13
13
  *
@@ -112,4 +112,3 @@ export declare class NeuroLinkSSE {
112
112
  * ```
113
113
  */
114
114
  export declare function createSSEClient(config: SSEConfig): NeuroLinkSSE;
115
- export type { ClientStreamCallbacks, StreamEvent, StreamResult, ClientApiError, };
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @module @neurolink/client/wsClient
9
9
  */
10
- import type { ClientStreamCallbacks, ClientStreamEvent as StreamEvent, ClientStreamResult as StreamResult, ClientApiError, WebSocketEventHandlers, ClientClientWebSocketState, ClientWebSocketMessage, ClientWebSocketConfig } from "../types/index.js";
10
+ import type { ClientStreamCallbacks, WebSocketEventHandlers, ClientClientWebSocketState, ClientWebSocketMessage, ClientWebSocketConfig } from "../types/index.js";
11
11
  /**
12
12
  * WebSocket streaming client for NeuroLink
13
13
  *
@@ -119,4 +119,3 @@ export declare class NeuroLinkWebSocket {
119
119
  * ```
120
120
  */
121
121
  export declare function createWebSocketClient(config: ClientWebSocketConfig): NeuroLinkWebSocket;
122
- export type { ClientStreamCallbacks, StreamEvent, StreamResult, ClientApiError, };
@@ -1,3 +1,2 @@
1
1
  import type { ToolEventPayload } from "../types/index.js";
2
- export type { ToolEventPayload };
3
2
  export declare function createToolEventPayload(toolName: string, payload?: Omit<ToolEventPayload, "tool" | "toolName">): ToolEventPayload;
@@ -1,3 +1,2 @@
1
- import { Hippocampus, type HippocampusConfig, type StorageConfig } from "@juspay/hippocampus";
2
- export type { HippocampusConfig, StorageConfig };
1
+ import { Hippocampus, type HippocampusConfig } from "@juspay/hippocampus";
3
2
  export declare function initializeHippocampus(config: HippocampusConfig): Hippocampus | null;
@@ -1,4 +1,4 @@
1
- import { Hippocampus, } from "@juspay/hippocampus";
1
+ import { Hippocampus } from "@juspay/hippocampus";
2
2
  import { logger } from "../utils/logger.js";
3
3
  export function initializeHippocampus(config) {
4
4
  try {
@@ -6,7 +6,6 @@
6
6
  */
7
7
  import type { ClaudeSubscriptionTier, AnthropicModelMetadata } from "../types/index.js";
8
8
  import { ModelAccessError } from "../types/index.js";
9
- export type { ClaudeSubscriptionTier, AnthropicModelMetadata };
10
9
  export { ModelAccessError };
11
10
  /**
12
11
  * Anthropic Claude model identifiers
@@ -5,6 +5,7 @@
5
5
  * model capabilities, and provides helper functions for tier-based access control.
6
6
  */
7
7
  import { ModelAccessError } from "../types/index.js";
8
+ // Re-export runtime value for convenience
8
9
  export { ModelAccessError };
9
10
  // ============================================================================
10
11
  // ANTHROPIC MODEL ENUM
@@ -47,7 +47,7 @@ import { ToolRouter } from "./mcp/routing/index.js";
47
47
  import { directToolsServer } from "./mcp/servers/agent/directToolsServer.js";
48
48
  import { inferAnnotations, isSafeToRetry } from "./mcp/toolAnnotations.js";
49
49
  import { MCPToolRegistry } from "./mcp/toolRegistry.js";
50
- import { initializeHippocampus, } from "./memory/hippocampusInitializer.js";
50
+ import { initializeHippocampus } from "./memory/hippocampusInitializer.js";
51
51
  import { createMemoryRetrievalTools } from "./memory/memoryRetrievalTools.js";
52
52
  import { getMetricsAggregator, MetricsAggregator, } from "./observability/metricsAggregator.js";
53
53
  import { SpanStatus, SpanType, CircuitBreakerOpenError, ConversationMemoryError, AuthenticationError, AuthorizationError, InvalidModelError, } from "./types/index.js";
@@ -3,8 +3,7 @@
3
3
  * Manages multiple observability exporters with circuit breaker protection
4
4
  */
5
5
  import type { BaseExporter } from "./exporters/baseExporter.js";
6
- import type { Sampler } from "./sampling/samplers.js";
7
- import type { ExporterHealthStatus, ExportResult, SpanData } from "../types/index.js";
6
+ import type { ExporterHealthStatus, ExportResult, Sampler, SpanData } from "../types/index.js";
8
7
  /**
9
8
  * Circuit breaker state for an exporter
10
9
  */
@@ -3,7 +3,6 @@
3
3
  * Configurable retry strategies for observability exporters
4
4
  */
5
5
  import type { RetryPolicy, RetryContext, RetryDecision } from "../types/index.js";
6
- export type { RetryPolicy };
7
6
  /**
8
7
  * Base retry policy with common configuration
9
8
  */
@@ -3,7 +3,6 @@
3
3
  * Control which spans are exported for production optimization
4
4
  */
5
5
  import type { SamplerConfig, SamplingRule, SpanData, Sampler } from "../../types/index.js";
6
- export type { Sampler };
7
6
  /**
8
7
  * Always sample all spans
9
8
  */
@@ -4,7 +4,6 @@
4
4
  * Fills the 9% gap in pattern compliance
5
5
  */
6
6
  import type { SpanAttributes, SpanData, SpanProcessor } from "../types/index.js";
7
- export type { SpanProcessor };
8
7
  /**
9
8
  * No-op processor that passes spans through unchanged
10
9
  */
@@ -9,7 +9,6 @@
9
9
  */
10
10
  import type { FileProcessingError } from "../../types/index.js";
11
11
  import { FileErrorCode } from "./FileErrorCode.js";
12
- export type { FileProcessingError };
13
12
  /**
14
13
  * Summary of file processing operations.
15
14
  */
@@ -149,3 +148,4 @@ export declare function combineSummaries(summaries: FileProcessingSummary[]): Fi
149
148
  * @returns Delay in milliseconds before next retry
150
149
  */
151
150
  export declare function getRetryDelay(error: unknown, attempt: number, baseDelayMs?: number): number;
151
+ export {};