@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.
- package/CHANGELOG.md +8 -0
- package/dist/auth/tokenStore.d.ts +0 -1
- package/dist/browser/neurolink.min.js +305 -305
- package/dist/cli/commands/proxy.js +333 -31
- package/dist/client/httpClient.d.ts +1 -2
- package/dist/client/sseClient.d.ts +1 -2
- package/dist/client/wsClient.d.ts +1 -2
- package/dist/core/toolEvents.d.ts +0 -1
- package/dist/lib/auth/tokenStore.d.ts +0 -1
- package/dist/lib/client/httpClient.d.ts +1 -2
- package/dist/lib/client/sseClient.d.ts +1 -2
- package/dist/lib/client/wsClient.d.ts +1 -2
- package/dist/lib/core/toolEvents.d.ts +0 -1
- package/dist/lib/memory/hippocampusInitializer.d.ts +1 -2
- package/dist/lib/memory/hippocampusInitializer.js +1 -1
- package/dist/lib/models/anthropicModels.d.ts +0 -1
- package/dist/lib/models/anthropicModels.js +1 -0
- package/dist/lib/neurolink.js +1 -1
- package/dist/lib/observability/exporterRegistry.d.ts +1 -2
- package/dist/lib/observability/retryPolicy.d.ts +0 -1
- package/dist/lib/observability/sampling/samplers.d.ts +0 -1
- package/dist/lib/observability/spanProcessor.d.ts +0 -1
- package/dist/lib/processors/errors/errorHelpers.d.ts +1 -1
- package/dist/lib/providers/googleNativeGemini3.d.ts +1 -2
- package/dist/lib/proxy/proxyHealth.d.ts +0 -1
- package/dist/lib/proxy/routingPolicy.d.ts +4 -30
- package/dist/lib/proxy/routingPolicy.js +14 -46
- package/dist/lib/proxy/usageStats.d.ts +0 -1
- package/dist/lib/proxy/usageStats.js +0 -7
- package/dist/lib/server/routes/claudeProxyRoutes.js +55 -95
- package/dist/lib/types/proxy.d.ts +2 -12
- package/dist/lib/utils/sanitizers/filename.d.ts +0 -1
- package/dist/memory/hippocampusInitializer.d.ts +1 -2
- package/dist/memory/hippocampusInitializer.js +1 -1
- package/dist/models/anthropicModels.d.ts +0 -1
- package/dist/models/anthropicModels.js +1 -0
- package/dist/neurolink.js +1 -1
- package/dist/observability/exporterRegistry.d.ts +1 -2
- package/dist/observability/retryPolicy.d.ts +0 -1
- package/dist/observability/sampling/samplers.d.ts +0 -1
- package/dist/observability/spanProcessor.d.ts +0 -1
- package/dist/processors/errors/errorHelpers.d.ts +1 -1
- package/dist/providers/googleNativeGemini3.d.ts +1 -2
- package/dist/proxy/proxyHealth.d.ts +0 -1
- package/dist/proxy/routingPolicy.d.ts +4 -30
- package/dist/proxy/routingPolicy.js +14 -46
- package/dist/proxy/usageStats.d.ts +0 -1
- package/dist/proxy/usageStats.js +0 -7
- package/dist/server/routes/claudeProxyRoutes.js +55 -95
- package/dist/types/proxy.d.ts +2 -12
- package/dist/utils/sanitizers/filename.d.ts +0 -1
- 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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
1307
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1324
|
-
|
|
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(/'/g, "'")
|
|
1856
|
+
.replace(/"/g, '"')
|
|
1857
|
+
.replace(/>/g, ">")
|
|
1858
|
+
.replace(/</g, "<")
|
|
1859
|
+
.replace(/&/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
|
-
|
|
1589
|
-
|
|
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>${
|
|
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,
|
|
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,
|
|
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,
|
|
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, };
|
|
@@ -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,
|
|
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,
|
|
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,
|
|
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
|
-
import { Hippocampus, type HippocampusConfig
|
|
2
|
-
export type { HippocampusConfig, StorageConfig };
|
|
1
|
+
import { Hippocampus, type HippocampusConfig } from "@juspay/hippocampus";
|
|
3
2
|
export declare function initializeHippocampus(config: HippocampusConfig): Hippocampus | null;
|
|
@@ -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
|
package/dist/lib/neurolink.js
CHANGED
|
@@ -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
|
|
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 "
|
|
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
|
*/
|
|
@@ -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 {};
|