llm-checker 3.5.9 → 3.5.11

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.
@@ -3194,7 +3194,7 @@ program
3194
3194
  }
3195
3195
  if (analysis.ollamaInfo.attemptedURL) {
3196
3196
  console.log(chalk.gray('Attempted URL: ' + analysis.ollamaInfo.attemptedURL));
3197
- console.log(chalk.gray('Set OLLAMA_HOST environment variable to use a different URL'));
3197
+ console.log(chalk.gray('Set OLLAMA_BASE_URL environment variable to use a different client URL'));
3198
3198
  }
3199
3199
  return;
3200
3200
  }
@@ -187,7 +187,7 @@ const ALLOWED_CLI_COMMANDS = new Set([
187
187
 
188
188
  const server = new McpServer({
189
189
  name: "llm-checker",
190
- version: "3.5.9",
190
+ version: "3.5.11",
191
191
  });
192
192
 
193
193
  // ============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-checker",
3
- "version": "3.5.9",
3
+ "version": "3.5.11",
4
4
  "description": "Intelligent CLI tool with AI-powered model selection that analyzes your hardware and recommends optimal LLM models for your system",
5
5
  "bin": {
6
6
  "llm-checker": "bin/cli.js",
@@ -15,12 +15,32 @@ class OllamaClient {
15
15
  this._pendingCheck = null;
16
16
  }
17
17
 
18
+ isWildcardBindHost(hostname) {
19
+ const normalized = String(hostname || '').trim().toLowerCase();
20
+ return normalized === '0.0.0.0' || normalized === '::' || normalized === '[::]';
21
+ }
22
+
18
23
  normalizeBaseURL(baseURL) {
19
24
  let normalized = String(baseURL || '').trim();
20
25
  if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {
21
26
  normalized = 'http://' + normalized;
22
27
  }
23
- return normalized.replace(/\/$/, '');
28
+
29
+ try {
30
+ const parsed = new URL(normalized);
31
+
32
+ if (!parsed.port) {
33
+ parsed.port = '11434';
34
+ }
35
+
36
+ if (this.isWildcardBindHost(parsed.hostname)) {
37
+ parsed.hostname = 'localhost';
38
+ }
39
+
40
+ return parsed.toString().replace(/\/$/, '');
41
+ } catch (error) {
42
+ return normalized.replace(/\/$/, '');
43
+ }
24
44
  }
25
45
 
26
46
  buildCandidateBaseURLs(baseURL = this.preferredBaseURL) {
@@ -35,7 +55,8 @@ class OllamaClient {
35
55
  candidates.push(ipv4.toString().replace(/\/$/, ''));
36
56
 
37
57
  const ipv6 = new URL(parsed.toString());
38
- ipv6.hostname = '::1';
58
+ // URL.hostname expects bracketed IPv6 literals when mutating an existing URL.
59
+ ipv6.hostname = '[::1]';
39
60
  candidates.push(ipv6.toString().replace(/\/$/, ''));
40
61
  }
41
62
  } catch (error) {
@@ -7,11 +7,68 @@ function getNodeFetch() {
7
7
  return nodeFetchPromise;
8
8
  }
9
9
 
10
- function fetchWithFallback(...args) {
10
+ function isNodeNativeFetch(fetchImpl) {
11
+ if (typeof fetchImpl !== 'function' || process.release?.name !== 'node') {
12
+ return false;
13
+ }
14
+
15
+ const source = Function.prototype.toString.call(fetchImpl);
16
+ return (
17
+ source.includes('internal/deps/undici/undici') ||
18
+ source.includes('lazy loading of undici module') ||
19
+ source.includes('[native code]')
20
+ );
21
+ }
22
+
23
+ function isRetryableNativeFetchError(error) {
24
+ const details = [
25
+ error?.message,
26
+ error?.code,
27
+ error?.errno,
28
+ error?.cause?.message,
29
+ error?.cause?.code,
30
+ error?.cause?.errno
31
+ ]
32
+ .filter(Boolean)
33
+ .map((value) => String(value).toLowerCase())
34
+ .join(' ');
35
+
36
+ return (
37
+ details.includes('fetch failed') ||
38
+ details.includes('econnrefused') ||
39
+ details.includes('econnreset') ||
40
+ details.includes('enotfound') ||
41
+ details.includes('eai_again') ||
42
+ details.includes('etimedout') ||
43
+ details.includes('timed out') ||
44
+ details.includes('enetunreach') ||
45
+ details.includes('ehostunreach') ||
46
+ details.includes('network') ||
47
+ details.includes('socket') ||
48
+ details.includes('connect')
49
+ );
50
+ }
51
+
52
+ async function fetchWithFallback(...args) {
11
53
  if (typeof globalThis.fetch === 'function') {
12
- return globalThis.fetch(...args);
54
+ if (!isNodeNativeFetch(globalThis.fetch)) {
55
+ return globalThis.fetch(...args);
56
+ }
57
+
58
+ try {
59
+ return await globalThis.fetch(...args);
60
+ } catch (error) {
61
+ if (!isRetryableNativeFetchError(error)) {
62
+ throw error;
63
+ }
64
+
65
+ const fetchImpl = await getNodeFetch();
66
+ return fetchImpl(...args);
67
+ }
13
68
  }
14
- return getNodeFetch().then((fetchImpl) => fetchImpl(...args));
69
+
70
+ const fetchImpl = await getNodeFetch();
71
+ return fetchImpl(...args);
15
72
  }
16
73
 
17
74
  module.exports = fetchWithFallback;