gitnexus 1.6.6-rc.123 → 1.6.6-rc.124

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.
@@ -23,6 +23,7 @@ import fs from 'fs/promises';
23
23
  import { cliError } from './cli-message.js';
24
24
  import { formatElapsed } from './format-elapsed.js';
25
25
  import { isHfDownloadFailure } from '../core/embeddings/hf-env.js';
26
+ import { isLocalEmbeddingRuntimeBlockerMessage } from '../core/embeddings/runtime-support.js';
26
27
  import { warnIfNpm11NpxRisk } from './resolve-invocation.js';
27
28
  // Capture stderr.write at module load BEFORE anything (LadybugDB native
28
29
  // init, progress bar, console redirection) can monkey-patch it. The
@@ -903,6 +904,19 @@ const analyzeCommandImpl = async (inputPath, options) => {
903
904
  process.exitCode = 1;
904
905
  return;
905
906
  }
907
+ // Local embedding runtime unsupported on this platform (macOS Intel ships no
908
+ // darwin/x64 ONNX native binding, #1515). The guard threw before importing
909
+ // transformers.js, so this is a clean, actionable GitNexus message. Checked
910
+ // before the network-heuristic isHfDownloadFailure branch below (and before
911
+ // the generic module-not-found "installation may be corrupt" hint) so the
912
+ // explicit platform message always takes priority.
913
+ if (isLocalEmbeddingRuntimeBlockerMessage(msg)) {
914
+ cliError(` ${msg.replace(/\n/g, '\n ')}\n`, {
915
+ recoveryHint: 'local-embedding-unsupported',
916
+ });
917
+ process.exitCode = 1;
918
+ return;
919
+ }
906
920
  // HF download failure — show clean guidance without the raw stack trace.
907
921
  // Checked before writeFatalToStderr so the user sees one focused message
908
922
  // rather than a stack-trace dump followed by a second remediation block.
@@ -12,7 +12,7 @@ import { type CliMessageKey, type CliMessageVars } from './i18n/index.js';
12
12
  * Consumers can import this type to narrow log-record `recoveryHint`
13
13
  * fields without restating the literal list.
14
14
  */
15
- export type RecoveryHint = 'wal-corruption' | 'wal-checkpoint-threshold' | 'heap-oom-respawn' | 'native-worker-abort' | 'hf-endpoint-unreachable' | 'large-repo' | 'npm-resolution' | 'module-not-found';
15
+ export type RecoveryHint = 'wal-corruption' | 'wal-checkpoint-threshold' | 'heap-oom-respawn' | 'native-worker-abort' | 'hf-endpoint-unreachable' | 'local-embedding-unsupported' | 'large-repo' | 'npm-resolution' | 'module-not-found';
16
16
  /**
17
17
  * Common shape for the optional structured-field bag passed to
18
18
  * `cliError`/`cliWarn`/`cliInfo`. Typed so the `recoveryHint` slot is
@@ -1,3 +1,21 @@
1
1
  export declare function displayWidth(value: string): number;
2
2
  export declare function padDisplayEnd(value: string, columns: number): string;
3
+ /**
4
+ * Embedding-runtime support status for the `doctor` Embeddings section.
5
+ * Pure and DI-friendly so it can be unit-tested without running the whole
6
+ * command. Delegates the platform decision to
7
+ * {@link getLocalEmbeddingRuntimeBlocker} so the wording stays in one place.
8
+ *
9
+ * - HTTP mode: always supported (never touches the native runtime).
10
+ * - Local mode on an unsupported platform (macOS Intel, #1515): reports the
11
+ * blocker as `detail` so the caller can surface the full guidance.
12
+ */
13
+ export declare function localEmbeddingDoctorStatus(opts: {
14
+ httpMode: boolean;
15
+ platform?: NodeJS.Platform;
16
+ arch?: NodeJS.Architecture;
17
+ }): {
18
+ status: string;
19
+ detail: string | null;
20
+ };
3
21
  export declare const doctorCommand: () => Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import { getRuntimeCapabilities, getRuntimeFingerprint } from '../core/platform/capabilities.js';
2
2
  import { resolveEmbeddingConfig } from '../core/embeddings/config.js';
3
3
  import { isHttpMode } from '../core/embeddings/http-client.js';
4
+ import { getLocalEmbeddingRuntimeBlocker } from '../core/embeddings/runtime-support.js';
4
5
  import { checkLbugNative } from '../core/lbug/native-check.js';
5
6
  import { getExtensionInstallPolicy } from '../core/lbug/extension-loader.js';
6
7
  import { t } from './i18n/index.js';
@@ -42,6 +43,28 @@ export function padDisplayEnd(value, columns) {
42
43
  return value + ' '.repeat(Math.max(0, columns - displayWidth(value)));
43
44
  }
44
45
  const label = (key, width) => padDisplayEnd(t(key), width);
46
+ /**
47
+ * Embedding-runtime support status for the `doctor` Embeddings section.
48
+ * Pure and DI-friendly so it can be unit-tested without running the whole
49
+ * command. Delegates the platform decision to
50
+ * {@link getLocalEmbeddingRuntimeBlocker} so the wording stays in one place.
51
+ *
52
+ * - HTTP mode: always supported (never touches the native runtime).
53
+ * - Local mode on an unsupported platform (macOS Intel, #1515): reports the
54
+ * blocker as `detail` so the caller can surface the full guidance.
55
+ */
56
+ export function localEmbeddingDoctorStatus(opts) {
57
+ if (opts.httpMode) {
58
+ return { status: '✓ http endpoint configured', detail: null };
59
+ }
60
+ const platform = opts.platform ?? process.platform;
61
+ const arch = opts.arch ?? process.arch;
62
+ const blocker = getLocalEmbeddingRuntimeBlocker({ platform, arch });
63
+ if (blocker) {
64
+ return { status: `✗ local embeddings unavailable on ${platform}/${arch}`, detail: blocker };
65
+ }
66
+ return { status: '✓ local embeddings supported', detail: null };
67
+ }
45
68
  export const doctorCommand = async () => {
46
69
  const fingerprint = getRuntimeFingerprint();
47
70
  const capabilities = getRuntimeCapabilities();
@@ -87,4 +110,13 @@ export const doctorCommand = async () => {
87
110
  console.log(` ${label('doctor.labels.threads', 12)}${embeddingConfig.threads}`);
88
111
  console.log(` ${label('doctor.labels.batch', 12)}${t('doctor.nodes', { count: embeddingConfig.batchSize })}`);
89
112
  console.log(` ${label('doctor.labels.subBatch', 12)}${t('doctor.chunks', { count: embeddingConfig.subBatchSize })}`);
113
+ // Surface local-runtime support so macOS Intel users see up front that local
114
+ // embeddings can't load here (the bundled ONNX Runtime ships no darwin/x64
115
+ // native binding, #1515) — rather than discovering it only when
116
+ // `analyze --embeddings` fails. Literal label like the 'native' line above.
117
+ const support = localEmbeddingDoctorStatus({ httpMode: isHttpMode() });
118
+ console.log(` ${padDisplayEnd('Support:', 12)}${support.status}`);
119
+ if (support.detail) {
120
+ process.stderr.write(`\n${support.detail.replace(/^/gm, ' ')}\n\n`);
121
+ }
90
122
  };
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Uses snowflake-arctic-embed-xs by default (22M params, 384 dims, ~90MB)
8
8
  */
9
- import { type FeatureExtractionPipeline } from '@huggingface/transformers';
9
+ import type { FeatureExtractionPipeline } from '@huggingface/transformers';
10
10
  import { type EmbeddingConfig, type ModelProgress } from './types.js';
11
11
  /**
12
12
  * Progress callback type for model loading
@@ -12,7 +12,6 @@
12
12
  if (!process.env.ORT_LOG_LEVEL) {
13
13
  process.env.ORT_LOG_LEVEL = '3';
14
14
  }
15
- import { pipeline, env, } from '@huggingface/transformers';
16
15
  import { existsSync } from 'fs';
17
16
  import { execFileSync } from 'child_process';
18
17
  import { join, dirname } from 'path';
@@ -21,6 +20,7 @@ import { DEFAULT_EMBEDDING_CONFIG } from './types.js';
21
20
  import { isHttpMode, getHttpDimensions, httpEmbed } from './http-client.js';
22
21
  import { resolveEmbeddingConfig } from './config.js';
23
22
  import { applyHfEnvOverrides, isHfDownloadFailure, withHfDownloadRetry } from './hf-env.js';
23
+ import { getLocalEmbeddingRuntimeBlocker } from './runtime-support.js';
24
24
  import { logger } from '../logger.js';
25
25
  /**
26
26
  * Check whether the onnxruntime-node package that @huggingface/transformers
@@ -117,6 +117,16 @@ export const initEmbedder = async (onProgress, config = {}, forceDevice) => {
117
117
  throw new Error('initEmbedder() should not be called in HTTP mode. ' +
118
118
  'Use embedText()/embedBatch() which handle HTTP transparently.');
119
119
  }
120
+ // Fail fast on platforms where the bundled native ONNX Runtime binding is not
121
+ // shipped (macOS Intel, #1515). Must run before any transformers.js /
122
+ // onnxruntime-node import or resolution — otherwise the native module load
123
+ // crashes with a raw "Cannot find module ...onnxruntime_binding.node" that
124
+ // ONNX_WEB_BACKEND=wasm cannot rescue (#1516). HTTP mode was already handled
125
+ // above, so this only blocks the local-runtime path.
126
+ const runtimeBlocker = getLocalEmbeddingRuntimeBlocker();
127
+ if (runtimeBlocker) {
128
+ throw new Error(runtimeBlocker);
129
+ }
120
130
  // Return existing instance if available
121
131
  if (embedderInstance) {
122
132
  return embedderInstance;
@@ -135,6 +145,9 @@ export const initEmbedder = async (onProgress, config = {}, forceDevice) => {
135
145
  const requestedDevice = forceDevice || (finalConfig.device === 'auto' ? gpuDevice : finalConfig.device);
136
146
  initPromise = (async () => {
137
147
  try {
148
+ // Lazy-load transformers.js only after the runtime guard has passed, so
149
+ // unsupported platforms never reach the native ONNX import (#1515).
150
+ const { pipeline, env } = await import('@huggingface/transformers');
138
151
  // Configure transformers.js environment
139
152
  env.allowLocalModels = false;
140
153
  // Bridge user-controlled env vars to transformers.js: HF_HOME →
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Local embedding runtime support guard.
3
+ *
4
+ * The bundled local embedding stack (`@huggingface/transformers` →
5
+ * `onnxruntime-node`) only ships native ONNX Runtime bindings for a subset of
6
+ * platform/arch pairs. On macOS Intel (`darwin`/`x64`), `onnxruntime-node`
7
+ * ships no `bin/napi-v6/darwin/x64/onnxruntime_binding.node`, so *importing*
8
+ * transformers.js throws a raw `Cannot find module ...onnxruntime_binding.node`
9
+ * before any device/backend selection can run (#1515). `ONNX_WEB_BACKEND=wasm`
10
+ * cannot rescue this — the failure is at native-module import time, not backend
11
+ * selection (#1516).
12
+ *
13
+ * This module is intentionally free of any native or transformers.js import (at
14
+ * module scope or inside its functions) so it can be consulted *before* the
15
+ * dynamic import that would crash. HTTP embedding mode never touches the native
16
+ * runtime, so callers in HTTP mode must skip this guard.
17
+ */
18
+ export interface LocalEmbeddingRuntimeOptions {
19
+ platform?: NodeJS.Platform;
20
+ arch?: NodeJS.Architecture;
21
+ }
22
+ /**
23
+ * Return a human-readable explanation when the *local* embedding runtime cannot
24
+ * load on this platform, or `null` when local embeddings are expected to work.
25
+ *
26
+ * Only `darwin`/`x64` is blocked today: it is the one platform/arch pair where
27
+ * the bundled `onnxruntime-node` ships no native binding (#1515). Every other
28
+ * platform returns `null` and follows the normal device-probe path, so genuine
29
+ * ONNX failures on supported platforms are never masked by this message.
30
+ *
31
+ * Accepts an explicit `{ platform, arch }` for testing; defaults to the current
32
+ * process values.
33
+ */
34
+ export declare const getLocalEmbeddingRuntimeBlocker: (options?: LocalEmbeddingRuntimeOptions) => string | null;
35
+ /**
36
+ * True when `message` is the macOS-Intel local-embedding blocker produced by
37
+ * {@link getLocalEmbeddingRuntimeBlocker}. Lets the CLI surface a clean,
38
+ * actionable message instead of a raw stack trace, without coupling to the
39
+ * full wording.
40
+ */
41
+ export declare const isLocalEmbeddingRuntimeBlockerMessage: (message: string) => boolean;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Local embedding runtime support guard.
3
+ *
4
+ * The bundled local embedding stack (`@huggingface/transformers` →
5
+ * `onnxruntime-node`) only ships native ONNX Runtime bindings for a subset of
6
+ * platform/arch pairs. On macOS Intel (`darwin`/`x64`), `onnxruntime-node`
7
+ * ships no `bin/napi-v6/darwin/x64/onnxruntime_binding.node`, so *importing*
8
+ * transformers.js throws a raw `Cannot find module ...onnxruntime_binding.node`
9
+ * before any device/backend selection can run (#1515). `ONNX_WEB_BACKEND=wasm`
10
+ * cannot rescue this — the failure is at native-module import time, not backend
11
+ * selection (#1516).
12
+ *
13
+ * This module is intentionally free of any native or transformers.js import (at
14
+ * module scope or inside its functions) so it can be consulted *before* the
15
+ * dynamic import that would crash. HTTP embedding mode never touches the native
16
+ * runtime, so callers in HTTP mode must skip this guard.
17
+ */
18
+ /**
19
+ * Stable lead line of the macOS-Intel blocker message. Also used to recognise
20
+ * the thrown error in the CLI error handler without coupling to the full
21
+ * wording (see {@link isLocalEmbeddingRuntimeBlockerMessage}).
22
+ */
23
+ const LOCAL_EMBEDDING_BLOCKER_LEAD = 'Local semantic embeddings are unavailable on macOS Intel (darwin/x64).';
24
+ /**
25
+ * Return a human-readable explanation when the *local* embedding runtime cannot
26
+ * load on this platform, or `null` when local embeddings are expected to work.
27
+ *
28
+ * Only `darwin`/`x64` is blocked today: it is the one platform/arch pair where
29
+ * the bundled `onnxruntime-node` ships no native binding (#1515). Every other
30
+ * platform returns `null` and follows the normal device-probe path, so genuine
31
+ * ONNX failures on supported platforms are never masked by this message.
32
+ *
33
+ * Accepts an explicit `{ platform, arch }` for testing; defaults to the current
34
+ * process values.
35
+ */
36
+ export const getLocalEmbeddingRuntimeBlocker = (options = {}) => {
37
+ const platform = options.platform ?? process.platform;
38
+ const arch = options.arch ?? process.arch;
39
+ if (platform === 'darwin' && arch === 'x64') {
40
+ return [
41
+ LOCAL_EMBEDDING_BLOCKER_LEAD,
42
+ 'The bundled ONNX Runtime package (onnxruntime-node) does not ship a',
43
+ 'darwin/x64 native binding, so the local embedding model cannot load here.',
44
+ 'ONNX_WEB_BACKEND=wasm does not help: the failure happens while importing',
45
+ 'the native runtime, before any backend can be selected. Forcing',
46
+ 'GITNEXUS_EMBEDDING_DEVICE=wasm (or cpu) does not help either, for the same reason.',
47
+ '',
48
+ 'Use one of these instead:',
49
+ ' - Run analyze without --embeddings (all other indexing still works).',
50
+ ' - Point GITNEXUS_EMBEDDING_URL (with GITNEXUS_EMBEDDING_MODEL) at an',
51
+ ' OpenAI-compatible /v1/embeddings endpoint to embed over HTTP.',
52
+ ' - Run GitNexus on Linux or in Docker, where the native binding ships.',
53
+ ' - Run GitNexus on Apple Silicon (darwin/arm64), which ships a binding.',
54
+ ' - Use a future GitNexus build that restores darwin/x64 ONNX support.',
55
+ ].join('\n');
56
+ }
57
+ return null;
58
+ };
59
+ /**
60
+ * True when `message` is the macOS-Intel local-embedding blocker produced by
61
+ * {@link getLocalEmbeddingRuntimeBlocker}. Lets the CLI surface a clean,
62
+ * actionable message instead of a raw stack trace, without coupling to the
63
+ * full wording.
64
+ */
65
+ export const isLocalEmbeddingRuntimeBlockerMessage = (message) => message.includes(LOCAL_EMBEDDING_BLOCKER_LEAD);
@@ -4,7 +4,7 @@
4
4
  * Singleton factory for transformers.js embedding pipeline.
5
5
  * For MCP, we only need to compute query embeddings, not batch embed.
6
6
  */
7
- import { type FeatureExtractionPipeline } from '@huggingface/transformers';
7
+ import type { FeatureExtractionPipeline } from '@huggingface/transformers';
8
8
  /**
9
9
  * Initialize the embedding model (lazy, on first search)
10
10
  */
@@ -4,10 +4,10 @@
4
4
  * Singleton factory for transformers.js embedding pipeline.
5
5
  * For MCP, we only need to compute query embeddings, not batch embed.
6
6
  */
7
- import { pipeline, env } from '@huggingface/transformers';
8
7
  import { isHttpMode, getHttpDimensions, httpEmbedQuery, } from '../../core/embeddings/http-client.js';
9
8
  import { resolveEmbeddingConfig } from '../../core/embeddings/config.js';
10
9
  import { applyHfEnvOverrides, isHfDownloadFailure, withHfDownloadRetry, } from '../../core/embeddings/hf-env.js';
10
+ import { getLocalEmbeddingRuntimeBlocker } from '../../core/embeddings/runtime-support.js';
11
11
  import { silenceStdout, restoreStdout, realStderrWrite } from '../../core/lbug/pool-adapter.js';
12
12
  import { logger } from '../../core/logger.js';
13
13
  // Model config
@@ -23,6 +23,15 @@ export const initEmbedder = async () => {
23
23
  if (isHttpMode()) {
24
24
  throw new Error('initEmbedder() should not be called in HTTP mode.');
25
25
  }
26
+ // Fail fast on platforms where the bundled native ONNX Runtime binding is not
27
+ // shipped (macOS Intel, #1515). Must run before any transformers.js /
28
+ // onnxruntime-node import or resolution — otherwise the native module load
29
+ // crashes with a raw "Cannot find module ...onnxruntime_binding.node" that
30
+ // ONNX_WEB_BACKEND=wasm cannot rescue (#1516).
31
+ const runtimeBlocker = getLocalEmbeddingRuntimeBlocker();
32
+ if (runtimeBlocker) {
33
+ throw new Error(runtimeBlocker);
34
+ }
26
35
  if (embedderInstance) {
27
36
  return embedderInstance;
28
37
  }
@@ -32,6 +41,9 @@ export const initEmbedder = async () => {
32
41
  isInitializing = true;
33
42
  initPromise = (async () => {
34
43
  try {
44
+ // Lazy-load transformers.js only after the runtime guard has passed, so
45
+ // unsupported platforms never reach the native ONNX import (#1515).
46
+ const { pipeline, env } = await import('@huggingface/transformers');
35
47
  env.allowLocalModels = false;
36
48
  // Bridge user-controlled env vars to transformers.js: HF_HOME →
37
49
  // env.cacheDir, HF_ENDPOINT → env.remoteHost (#1205). Centralised in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.6-rc.123",
3
+ "version": "1.6.6-rc.124",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",