@juspay/neurolink 9.35.0 → 9.36.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 CHANGED
@@ -1,3 +1,9 @@
1
+ ## [9.36.0](https://github.com/juspay/neurolink/compare/v9.35.0...v9.36.0) (2026-03-28)
2
+
3
+ ### Features
4
+
5
+ - **(proxy):** add auto-update with traffic-aware graceful restart ([4a11a78](https://github.com/juspay/neurolink/commit/4a11a783adb4424d7a303b298c6cf9989cd4ed63))
6
+
1
7
  ## [9.35.0](https://github.com/juspay/neurolink/compare/v9.34.0...v9.35.0) (2026-03-28)
2
8
 
3
9
  ### Features
@@ -934,16 +934,21 @@ export const proxyGuardCommand = {
934
934
  const UPDATE_CHECK_INTERVAL_MS = 2 * 60 * 60 * 1000; // 2 hours
935
935
  const QUIET_THRESHOLD_MS = 120 * 1000; // 2 minutes of silence
936
936
  const UPDATE_TIMEOUT_MS = 30 * 1000; // 30 seconds to come healthy
937
- // Get running version from /health endpoint
937
+ // Get running version from /health endpoint (with timeout to avoid hanging)
938
938
  let runningVersion = PROXY_VERSION; // fallback
939
939
  try {
940
- const healthResp = await fetch(`http://${host}:${port}/health`);
940
+ const healthResp = await fetch(`http://${host}:${port}/health`, {
941
+ signal: AbortSignal.timeout(5_000),
942
+ });
941
943
  const healthData = (await healthResp.json());
942
944
  runningVersion = healthData.version ?? PROXY_VERSION;
943
945
  }
944
946
  catch {
945
947
  /* use fallback */
946
948
  }
949
+ // Auto-update only works on macOS with launchd. On other platforms,
950
+ // there's no restart mechanism, so skip the update loop entirely.
951
+ const canAutoUpdate = process.platform === "darwin" && (await isLaunchdManaging());
947
952
  let updateInProgress = false;
948
953
  let updateRestartInProgress = false;
949
954
  const runUpdateCheck = async () => {
@@ -1014,7 +1019,7 @@ export const proxyGuardCommand = {
1014
1019
  logger.always(`[guard] restarting proxy via launchctl...`);
1015
1020
  const uid = process.getuid?.() ?? 501;
1016
1021
  try {
1017
- execFileSync("launchctl", ["kickstart", "-k", `gui/${uid}/com.neurolink.proxy`], {
1022
+ execFileSync("launchctl", ["kickstart", "-k", `gui/${uid}/${PLIST_LABEL}`], {
1018
1023
  timeout: 10_000,
1019
1024
  stdio: "pipe",
1020
1025
  });
@@ -1065,8 +1070,10 @@ export const proxyGuardCommand = {
1065
1070
  }
1066
1071
  };
1067
1072
  // Run first check after a short delay, then on interval
1068
- setTimeout(runUpdateCheck, 30_000);
1069
- setInterval(runUpdateCheck, UPDATE_CHECK_INTERVAL_MS);
1073
+ if (canAutoUpdate) {
1074
+ setTimeout(runUpdateCheck, 30_000);
1075
+ setInterval(runUpdateCheck, UPDATE_CHECK_INTERVAL_MS);
1076
+ }
1070
1077
  const startedAt = Date.now();
1071
1078
  let parentStatus = getProcessStatus(parentPid);
1072
1079
  let consecutiveUnhealthy = 0;
@@ -12,12 +12,25 @@ const DEFAULT_QUIET_THRESHOLD_MS = 120_000;
12
12
  /** Maximum bytes to read from the tail of the log file. */
13
13
  const TAIL_READ_SIZE = 4096;
14
14
  /**
15
- * Build the path to today's proxy debug log file.
15
+ * Build the path to a proxy debug log file for a given date.
16
16
  * Format: ~/.neurolink/logs/proxy-debug-YYYY-MM-DD.jsonl
17
17
  */
18
- function getTodayLogPath() {
19
- const today = new Date().toISOString().split("T")[0];
20
- return join(homedir(), ".neurolink", "logs", `proxy-debug-${today}.jsonl`);
18
+ function getLogPathForDate(date) {
19
+ const dateStr = date.toISOString().split("T")[0];
20
+ return join(homedir(), ".neurolink", "logs", `proxy-debug-${dateStr}.jsonl`);
21
+ }
22
+ /**
23
+ * Get the most relevant log file path. Uses today's log if it exists,
24
+ * otherwise falls back to yesterday's to handle the midnight rollover case
25
+ * (last request at 23:59, update check at 00:01).
26
+ */
27
+ function getActiveLogPath() {
28
+ const todayPath = getLogPathForDate(new Date());
29
+ if (existsSync(todayPath)) {
30
+ return todayPath;
31
+ }
32
+ const yesterday = new Date(Date.now() - 86_400_000);
33
+ return getLogPathForDate(yesterday);
21
34
  }
22
35
  /**
23
36
  * Read the last complete line(s) from a file efficiently.
@@ -82,7 +95,7 @@ export function checkTrafficQuiet(quietThresholdMs = DEFAULT_QUIET_THRESHOLD_MS)
82
95
  lastActivityAt: null,
83
96
  silenceDurationMs: Infinity,
84
97
  };
85
- const logPath = getTodayLogPath();
98
+ const logPath = getActiveLogPath();
86
99
  if (!existsSync(logPath)) {
87
100
  return noActivityResult;
88
101
  }
@@ -67,9 +67,12 @@ export function loadUpdateState(stateFilePath) {
67
67
  const content = fs.readFileSync(filePath, "utf8");
68
68
  const parsed = JSON.parse(content);
69
69
  // Minimal shape check — reject valid JSON that isn't an UpdateState
70
+ // Note: typeof null === "object", so we check both
70
71
  if (typeof parsed !== "object" ||
71
72
  parsed === null ||
72
73
  typeof parsed.suppressedVersions !== "object" ||
74
+ parsed.suppressedVersions === null ||
75
+ Array.isArray(parsed.suppressedVersions) ||
73
76
  typeof parsed.lastCheckAt !== "string") {
74
77
  return getDefaultUpdateState();
75
78
  }
@@ -12,12 +12,25 @@ const DEFAULT_QUIET_THRESHOLD_MS = 120_000;
12
12
  /** Maximum bytes to read from the tail of the log file. */
13
13
  const TAIL_READ_SIZE = 4096;
14
14
  /**
15
- * Build the path to today's proxy debug log file.
15
+ * Build the path to a proxy debug log file for a given date.
16
16
  * Format: ~/.neurolink/logs/proxy-debug-YYYY-MM-DD.jsonl
17
17
  */
18
- function getTodayLogPath() {
19
- const today = new Date().toISOString().split("T")[0];
20
- return join(homedir(), ".neurolink", "logs", `proxy-debug-${today}.jsonl`);
18
+ function getLogPathForDate(date) {
19
+ const dateStr = date.toISOString().split("T")[0];
20
+ return join(homedir(), ".neurolink", "logs", `proxy-debug-${dateStr}.jsonl`);
21
+ }
22
+ /**
23
+ * Get the most relevant log file path. Uses today's log if it exists,
24
+ * otherwise falls back to yesterday's to handle the midnight rollover case
25
+ * (last request at 23:59, update check at 00:01).
26
+ */
27
+ function getActiveLogPath() {
28
+ const todayPath = getLogPathForDate(new Date());
29
+ if (existsSync(todayPath)) {
30
+ return todayPath;
31
+ }
32
+ const yesterday = new Date(Date.now() - 86_400_000);
33
+ return getLogPathForDate(yesterday);
21
34
  }
22
35
  /**
23
36
  * Read the last complete line(s) from a file efficiently.
@@ -82,7 +95,7 @@ export function checkTrafficQuiet(quietThresholdMs = DEFAULT_QUIET_THRESHOLD_MS)
82
95
  lastActivityAt: null,
83
96
  silenceDurationMs: Infinity,
84
97
  };
85
- const logPath = getTodayLogPath();
98
+ const logPath = getActiveLogPath();
86
99
  if (!existsSync(logPath)) {
87
100
  return noActivityResult;
88
101
  }
@@ -67,9 +67,12 @@ export function loadUpdateState(stateFilePath) {
67
67
  const content = fs.readFileSync(filePath, "utf8");
68
68
  const parsed = JSON.parse(content);
69
69
  // Minimal shape check — reject valid JSON that isn't an UpdateState
70
+ // Note: typeof null === "object", so we check both
70
71
  if (typeof parsed !== "object" ||
71
72
  parsed === null ||
72
73
  typeof parsed.suppressedVersions !== "object" ||
74
+ parsed.suppressedVersions === null ||
75
+ Array.isArray(parsed.suppressedVersions) ||
73
76
  typeof parsed.lastCheckAt !== "string") {
74
77
  return getDefaultUpdateState();
75
78
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.35.0",
3
+ "version": "9.36.0",
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",