gitnexus 1.6.10-rc.2 → 1.6.10-rc.21
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/README.md +56 -12
- package/dist/cli/analyze.js +166 -5
- package/dist/cli/clean.js +17 -3
- package/dist/cli/cli-message.d.ts +1 -1
- package/dist/cli/doctor.d.ts +18 -0
- package/dist/cli/doctor.js +76 -3
- package/dist/cli/embeddings.d.ts +12 -0
- package/dist/cli/embeddings.js +49 -0
- package/dist/cli/help-i18n.js +4 -0
- package/dist/cli/i18n/en.d.ts +10 -5
- package/dist/cli/i18n/en.js +10 -5
- package/dist/cli/i18n/resources.d.ts +15 -5
- package/dist/cli/i18n/zh-CN.d.ts +5 -0
- package/dist/cli/i18n/zh-CN.js +10 -5
- package/dist/cli/index.js +12 -1
- package/dist/core/augmentation/engine.js +4 -3
- package/dist/core/embeddings/embedder.js +16 -2
- package/dist/core/embeddings/embedding-pipeline.d.ts +20 -0
- package/dist/core/embeddings/embedding-pipeline.js +10 -2
- package/dist/core/embeddings/http-client.d.ts +28 -1
- package/dist/core/embeddings/http-client.js +107 -15
- package/dist/core/embeddings/node-module-compat.d.ts +23 -0
- package/dist/core/embeddings/node-module-compat.js +23 -0
- package/dist/core/embeddings/onnxruntime-common-resolver.js +6 -3
- package/dist/core/embeddings/onnxruntime-node-resolver.js +31 -5
- package/dist/core/embeddings/runtime-install.d.ts +119 -0
- package/dist/core/embeddings/runtime-install.js +372 -0
- package/dist/core/embeddings/runtime-support.d.ts +48 -17
- package/dist/core/embeddings/runtime-support.js +96 -0
- package/dist/core/group/extractors/http-patterns/python.js +246 -108
- package/dist/core/group/extractors/manifest-extractor.js +4 -0
- package/dist/core/incremental/escalation-gate.d.ts +38 -0
- package/dist/core/incremental/escalation-gate.js +48 -0
- package/dist/core/incremental/shadow-candidates.d.ts +3 -3
- package/dist/core/incremental/shadow-candidates.js +3 -3
- package/dist/core/incremental/subgraph-extract.d.ts +2 -2
- package/dist/core/incremental/subgraph-extract.js +2 -2
- package/dist/core/ingestion/cobol/jcl-processor.js +9 -8
- package/dist/core/ingestion/cobol-processor.js +27 -26
- package/dist/core/ingestion/community-processor.d.ts +51 -1
- package/dist/core/ingestion/community-processor.js +316 -63
- package/dist/core/ingestion/emit-references.js +3 -2
- package/dist/core/ingestion/languages/cpp/captures.js +157 -74
- package/dist/core/ingestion/markdown-processor.js +3 -2
- package/dist/core/ingestion/parsing-processor.d.ts +3 -1
- package/dist/core/ingestion/parsing-processor.js +4 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +42 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.d.ts +83 -0
- package/dist/core/ingestion/route-extractors/constant-resolver.js +131 -0
- package/dist/core/ingestion/route-extractors/python-const-resolver.d.ts +75 -0
- package/dist/core/ingestion/route-extractors/python-const-resolver.js +299 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
- package/dist/core/ingestion/tree-sitter-queries.js +12 -1
- package/dist/core/ingestion/utils/line-base.d.ts +20 -0
- package/dist/core/ingestion/utils/line-base.js +20 -0
- package/dist/core/ingestion/utils/symbol-labels.d.ts +21 -0
- package/dist/core/ingestion/utils/symbol-labels.js +45 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +34 -0
- package/dist/core/ingestion/workers/parse-worker.js +40 -5
- package/dist/core/ingestion/workers/result-merge.js +4 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +14 -0
- package/dist/core/ingestion/workers/worker-pool.js +85 -5
- package/dist/core/lbug/csv-generator.js +8 -2
- package/dist/core/lbug/cypher-escape.d.ts +20 -0
- package/dist/core/lbug/cypher-escape.js +20 -0
- package/dist/core/lbug/extension-load-error.d.ts +67 -0
- package/dist/core/lbug/extension-load-error.js +320 -0
- package/dist/core/lbug/extension-loader.d.ts +17 -2
- package/dist/core/lbug/extension-loader.js +38 -13
- package/dist/core/lbug/lbug-adapter.d.ts +147 -1
- package/dist/core/lbug/lbug-adapter.js +390 -70
- package/dist/core/lbug/lbug-config.d.ts +28 -0
- package/dist/core/lbug/lbug-config.js +114 -4
- package/dist/core/lbug/native-check.d.ts +22 -0
- package/dist/core/lbug/native-check.js +66 -0
- package/dist/core/lbug/pool-adapter.js +10 -1
- package/dist/core/lbug/sidecar-recovery.d.ts +172 -0
- package/dist/core/lbug/sidecar-recovery.js +418 -16
- package/dist/core/platform/capabilities.js +27 -1
- package/dist/core/run-analyze.js +470 -108
- package/dist/core/search/fts-indexes.d.ts +7 -0
- package/dist/core/search/fts-indexes.js +37 -0
- package/dist/core/tree-sitter/safe-parse.d.ts +8 -3
- package/dist/core/tree-sitter/safe-parse.js +21 -7
- package/dist/core/wiki/graph-queries.js +6 -5
- package/dist/mcp/core/embedder.js +16 -2
- package/dist/mcp/local/line-display.d.ts +22 -0
- package/dist/mcp/local/line-display.js +3 -0
- package/dist/mcp/local/local-backend.d.ts +14 -0
- package/dist/mcp/local/local-backend.js +63 -19
- package/dist/mcp/local/pdg-impact.d.ts +5 -3
- package/dist/mcp/local/pdg-impact.js +5 -2
- package/dist/mcp/resources.js +1 -0
- package/dist/mcp/tools.js +1 -1
- package/dist/server/analyze-launch.d.ts +6 -0
- package/dist/server/analyze-launch.js +82 -6
- package/dist/server/api.d.ts +30 -0
- package/dist/server/api.js +63 -21
- package/dist/storage/parse-cache.js +1 -1
- package/dist/storage/repo-manager.d.ts +65 -1
- package/dist/storage/repo-manager.js +18 -1
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +50 -3
- package/hooks/claude/gitnexus-hook.cjs +59 -9
- package/package.json +5 -3
- package/scripts/cross-platform-tests.ts +43 -0
- package/scripts/ensure-fts.ts +32 -0
- package/scripts/install-duckdb-extension.mjs +85 -24
- package/scripts/run-cross-platform.ts +46 -6
- package/scripts/shard-arg.ts +30 -0
- package/web/assets/ProcessFlowModal-Dna7GkwO.js +22 -0
- package/web/assets/{agent-Dli_x7bU.js → agent-DNYDy-KA.js} +49 -49
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-BVWFvYz2.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-BU5ZRITg.js +36 -0
- package/web/assets/{blockDiagram-7IZFK4PR-BkWhpzWB.js → blockDiagram-7IZFK4PR-Dut1BpTV.js} +2 -2
- package/web/assets/{c4Diagram-Y2BXMSZH-DtBvpzWD.js → c4Diagram-Y2BXMSZH-DOXVs7-C.js} +2 -2
- package/web/assets/chunk-2T2R6R2M-pkw2oXHH.js +4 -0
- package/web/assets/chunk-2UTLFMKG-CXlhF3Pt.js +1 -0
- package/web/assets/chunk-3SSMPTDK-rPsRM1x0.js +321 -0
- package/web/assets/chunk-4R4BOZG6-Be_F0JoX.js +159 -0
- package/web/assets/{chunk-6764PJDD-DpIS7hxD.js → chunk-6764PJDD-C7xxk063.js} +1 -1
- package/web/assets/{chunk-67TQ5CYL-4sNPJU1q.js → chunk-67TQ5CYL-zxhDWgyF.js} +3 -3
- package/web/assets/chunk-7FYTHRHK-CsmYIswB.js +32 -0
- package/web/assets/chunk-7J6CGLKN-AmkN8jJN.js +10 -0
- package/web/assets/chunk-7W6UQGC5-pyKGssV3.js +1 -0
- package/web/assets/chunk-C62D2QBJ-3gdL8EKc.js +1 -0
- package/web/assets/chunk-CEXFNPSA-CjwtMDkc.js +1 -0
- package/web/assets/{chunk-INKRHTLW-Nu4ri9P-.js → chunk-INKRHTLW-D9BM9HWZ.js} +1 -1
- package/web/assets/chunk-J5EP6P6S-L19oz55R.js +1 -0
- package/web/assets/{chunk-KGFNY3KK-JGMJvKCK.js → chunk-KGFNY3KK-tD2F6wxK.js} +1 -1
- package/web/assets/chunk-KGYTTC2M-zm6n242I.js +1 -0
- package/web/assets/{chunk-KRXBNO2N-BW5o0KUN.js → chunk-KRXBNO2N-C-UNuKKf.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-CfLLCywY.js → chunk-LCXTWHL2-Ba9JK-6F.js} +2 -2
- package/web/assets/{chunk-LII3EMHJ-D-jm-dLa.js → chunk-LII3EMHJ-EGx-eBIu.js} +1 -1
- package/web/assets/chunk-LRIF4GLE-C3_PqUBq.js +1 -0
- package/web/assets/{chunk-QA3QBVWF-BgRW3SXd.js → chunk-QA3QBVWF-D4cSJBf6.js} +1 -1
- package/web/assets/chunk-RERM46MO-B5dAdWpP.js +1 -0
- package/web/assets/{chunk-RG4AUYOV-CqWzWsV3.js → chunk-RG4AUYOV-OqBBMwuQ.js} +4 -4
- package/web/assets/chunk-RKZBBQEN-MyIQbGgD.js +1 -0
- package/web/assets/chunk-RLI5ZMPA-CkXZ0y0U.js +1 -0
- package/web/assets/{chunk-T2UQINTJ-DSBqkzH7.js → chunk-T2UQINTJ-BJusEiGe.js} +1 -1
- package/web/assets/chunk-UP6H54XL-BG-n7qDp.js +1 -0
- package/web/assets/chunk-UXSXWOXI-CW1_r5cS.js +1 -0
- package/web/assets/{chunk-UY5QBCOK-DyF0vyGd.js → chunk-UY5QBCOK-BCpUlARQ.js} +1 -1
- package/web/assets/chunk-VU6ZFW4Y-BeXVk45_.js +2 -0
- package/web/assets/{chunk-W44A43WB-JqMLIpR0.js → chunk-W44A43WB-BN8EQc_u.js} +1 -1
- package/web/assets/{chunk-ZXARS5L4-CxN8oiwI.js → chunk-ZXARS5L4-BI_qR2Nm.js} +1 -1
- package/web/assets/classDiagram-KGZ6W3CR-D9zSIg0x.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-D9zSIg0x.js +1 -0
- package/web/assets/cose-bilkent-UX7MHV2Q-BmnZ7Mvj.js +1 -0
- package/web/assets/dagre-ND4H6XIP-CpcV5kbl.js +4 -0
- package/web/assets/diagram-3NCE3AQN-6SMxBRpj.js +43 -0
- package/web/assets/diagram-GF46GFSD-BZEdNcRZ.js +24 -0
- package/web/assets/{diagram-HNR7UZ2L-DFCfd5LI.js → diagram-HNR7UZ2L-DxKW2a2Y.js} +2 -2
- package/web/assets/diagram-QXG6HAR7-HfTHcXX_.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-CyijUS8r.js +10 -0
- package/web/assets/{erDiagram-L5TCEMPS-8X1y6R4Y.js → erDiagram-L5TCEMPS-D0fvpWEX.js} +3 -3
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-CgFBM7e-.js +1 -0
- package/web/assets/flowDiagram-H6V6AXG4-Bvr9bbhk.js +162 -0
- package/web/assets/{ganttDiagram-JCBTUEKG-D3iL2Aet.js → ganttDiagram-JCBTUEKG-TzODrLzv.js} +2 -2
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DCJv7SII.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-ONQSwJIY.js +106 -0
- package/web/assets/index-B4eB4dNZ.js +575 -0
- package/web/assets/index-CX_fADmQ.css +2 -0
- package/web/assets/info-J43DQDTF-KCYPFFUO-BfNy-9Nd.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-DIPVUIQ6.js +2 -0
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-DfYpxnE5.js → ishikawaDiagram-BNXS4ZKH-BLjivjXL.js} +3 -3
- package/web/assets/{journeyDiagram-M6C3CM3L-CvArszLo.js → journeyDiagram-M6C3CM3L-Cetgs3NH.js} +3 -3
- package/web/assets/{kanban-definition-75IXJCU3-CpfVLLf9.js → kanban-definition-75IXJCU3-DMlCaIpf.js} +5 -5
- package/web/assets/{katex-K3KEBU37-CTc5BslQ.js → katex-K3KEBU37-qRQBy-EZ.js} +2 -2
- package/web/assets/{mindmap-definition-2TDM6QVE-DypvbOMQ.js → mindmap-definition-2TDM6QVE-B4GbXQBc.js} +3 -3
- package/web/assets/packet-YPE3B663-LP52Z2RK-D4qkeG9k.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-B_yMKyQa.js +1 -0
- package/web/assets/{pieDiagram-CU6KROY3-s27EFWgP.js → pieDiagram-CU6KROY3-D3YC_xyg.js} +2 -2
- package/web/assets/{quadrantDiagram-VICAPDV7-yXSZ2lLz.js → quadrantDiagram-VICAPDV7-B1TQnlm0.js} +2 -2
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DeD6yN_0.js +1 -0
- package/web/assets/{requirementDiagram-JXO7QTGE-CWhqInuf.js → requirementDiagram-JXO7QTGE-f8wczLQf.js} +3 -3
- package/web/assets/rolldown-runtime-QTnfLwEv.js +1 -0
- package/web/assets/{sankeyDiagram-URQDO5SZ-1qEjlFIB.js → sankeyDiagram-URQDO5SZ-BTxzG5j0.js} +2 -2
- package/web/assets/{sequenceDiagram-VS2MUI6T-RyTlZ5cR.js → sequenceDiagram-VS2MUI6T-DuamM-IR.js} +5 -5
- package/web/assets/src-BNCXICdA.js +1 -0
- package/web/assets/stateDiagram-7D4R322I-Ch2pEUcw.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-7ANuoZLL.js +1 -0
- package/web/assets/{timeline-definition-O6YCAMPW-DrrqLYAf.js → timeline-definition-O6YCAMPW-07BR8R8G.js} +3 -3
- package/web/assets/treeView-BLDUP644-QA4HXRO3-CDuSYnd4.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-imazucyJ.js +1 -0
- package/web/assets/{vennDiagram-MWXL3ELB-B0RkWAim.js → vennDiagram-MWXL3ELB-BQX2ossk.js} +4 -4
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-DqrT9tB8.js +1 -0
- package/web/assets/{wardleyDiagram-CUQ6CDDI-boUxiQ5F.js → wardleyDiagram-CUQ6CDDI-CGIJBBiK.js} +3 -3
- package/web/assets/{xychartDiagram-N2JHSOCM-BOLm0eNm.js → xychartDiagram-N2JHSOCM-B3HNMuWp.js} +4 -4
- package/web/index.html +17 -10
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +0 -1
- package/web/assets/architectureDiagram-UL44E2DR-dEkqUZN9.js +0 -36
- package/web/assets/chunk-3SSMPTDK-B4_etUhr.js +0 -321
- package/web/assets/chunk-7W6UQGC5-KyEG0HQg.js +0 -1
- package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +0 -161
- package/web/assets/classDiagram-KGZ6W3CR-CMne4tG9.js +0 -1
- package/web/assets/classDiagram-v2-72OJOZXJ-CMne4tG9.js +0 -1
- package/web/assets/cose-bilkent-UX7MHV2Q-D71wNYRJ.js +0 -1
- package/web/assets/dagre-ND4H6XIP-Co5rRx9X.js +0 -4
- package/web/assets/diagram-3NCE3AQN-DBp4O00j.js +0 -43
- package/web/assets/diagram-GF46GFSD-6N_8yS0i.js +0 -24
- package/web/assets/diagram-QXG6HAR7-Di093rFg.js +0 -24
- package/web/assets/diagram-WEQXMOUZ-DZWi4SUN.js +0 -10
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +0 -1
- package/web/assets/flowDiagram-H6V6AXG4-CMpY8Ufn.js +0 -162
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +0 -1
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +0 -106
- package/web/assets/index-KctnI3Vq.js +0 -635
- package/web/assets/index-_lgn7hs5.css +0 -2
- package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +0 -1
- package/web/assets/infoDiagram-3YFTVSEB-BaLMZkUg.js +0 -2
- package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +0 -1
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +0 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +0 -1
- package/web/assets/stateDiagram-7D4R322I-DTjVfX4A.js +0 -1
- package/web/assets/stateDiagram-v2-36443NZ5-xY9_k9_Q.js +0 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +0 -1
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +0 -1
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +0 -1
|
@@ -17,10 +17,26 @@ const HTTP_RETRY_BACKOFF_MS = 1_000;
|
|
|
17
17
|
const HTTP_BATCH_SIZE = 64;
|
|
18
18
|
const DEFAULT_DIMS = 384;
|
|
19
19
|
const HTTP_BREAKER_KEY = 'embeddings-http';
|
|
20
|
+
/**
|
|
21
|
+
* Stable lead of the {@link readConfig} malformed-`GITNEXUS_EMBEDDING_DIMS`
|
|
22
|
+
* error. `readConfig` throws a plain `Error` (not an {@link HttpEmbeddingError})
|
|
23
|
+
* because this is a *config* mistake, not an endpoint failure — so the CLI
|
|
24
|
+
* recognizes it by this lead ({@link isHttpEmbeddingDimsError}) and prints a
|
|
25
|
+
* clean config message instead of a raw stack dump. See #2385.
|
|
26
|
+
*/
|
|
27
|
+
const EMBEDDING_DIMS_ENV_ERROR_LEAD = 'GITNEXUS_EMBEDDING_DIMS must be a positive integer';
|
|
28
|
+
/**
|
|
29
|
+
* @internal Exported for the CLI analyze error handler. True when `message` is
|
|
30
|
+
* the {@link readConfig} malformed-DIMS config error (a plain `Error`).
|
|
31
|
+
*/
|
|
32
|
+
export const isHttpEmbeddingDimsError = (message) => message.includes(EMBEDDING_DIMS_ENV_ERROR_LEAD);
|
|
20
33
|
/**
|
|
21
34
|
* Build config from the current process.env snapshot.
|
|
22
35
|
* Returns null when GITNEXUS_EMBEDDING_URL + GITNEXUS_EMBEDDING_MODEL are unset.
|
|
23
36
|
* Not cached — env vars are read fresh so late configuration takes effect.
|
|
37
|
+
* Validates GITNEXUS_EMBEDDING_DIMS and throws on a malformed value; callers
|
|
38
|
+
* that only need to know whether HTTP mode is *configured* must use
|
|
39
|
+
* {@link isHttpMode} (a presence probe that never throws), not this.
|
|
24
40
|
*/
|
|
25
41
|
const readConfig = () => {
|
|
26
42
|
const baseUrl = process.env.GITNEXUS_EMBEDDING_URL;
|
|
@@ -31,11 +47,11 @@ const readConfig = () => {
|
|
|
31
47
|
let dimensions;
|
|
32
48
|
if (rawDims !== undefined) {
|
|
33
49
|
if (!/^\d+$/.test(rawDims)) {
|
|
34
|
-
throw new Error(
|
|
50
|
+
throw new Error(`${EMBEDDING_DIMS_ENV_ERROR_LEAD}, got "${rawDims}"`);
|
|
35
51
|
}
|
|
36
52
|
const parsed = parseInt(rawDims, 10);
|
|
37
53
|
if (parsed <= 0) {
|
|
38
|
-
throw new Error(
|
|
54
|
+
throw new Error(`${EMBEDDING_DIMS_ENV_ERROR_LEAD}, got "${rawDims}"`);
|
|
39
55
|
}
|
|
40
56
|
dimensions = parsed;
|
|
41
57
|
}
|
|
@@ -47,9 +63,15 @@ const readConfig = () => {
|
|
|
47
63
|
};
|
|
48
64
|
};
|
|
49
65
|
/**
|
|
50
|
-
*
|
|
66
|
+
* Whether HTTP embedding mode is active — i.e. both `GITNEXUS_EMBEDDING_URL` and
|
|
67
|
+
* `GITNEXUS_EMBEDDING_MODEL` are set. A pure presence probe: it deliberately does
|
|
68
|
+
* NOT call {@link readConfig}, so it never throws on a malformed
|
|
69
|
+
* `GITNEXUS_EMBEDDING_DIMS`. This lets its ~13 call sites (analyze, doctor,
|
|
70
|
+
* run-analyze, embedder, mcp) probe the mode without a defensive try/catch; the
|
|
71
|
+
* DIMS value is validated where it is actually used (`readConfig` in
|
|
72
|
+
* `httpEmbed`/`httpEmbedQuery`), surfacing a recognizable config error. See #2385.
|
|
51
73
|
*/
|
|
52
|
-
export const isHttpMode = () =>
|
|
74
|
+
export const isHttpMode = () => Boolean(process.env.GITNEXUS_EMBEDDING_URL && process.env.GITNEXUS_EMBEDDING_MODEL);
|
|
53
75
|
/**
|
|
54
76
|
* Return the configured embedding dimensions for HTTP mode, or undefined
|
|
55
77
|
* if HTTP mode is not active or no explicit dimensions are set.
|
|
@@ -70,6 +92,63 @@ export const safeUrl = (url) => {
|
|
|
70
92
|
return '<invalid-url>';
|
|
71
93
|
}
|
|
72
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Strip credentials from an underlying transport error message before it is
|
|
97
|
+
* surfaced. A credential-bearing endpoint URL (`https://user:secret@host/v1`)
|
|
98
|
+
* makes undici throw `TypeError: Request cannot be constructed from a URL that
|
|
99
|
+
* includes credentials: <that full URL>`; interpolating `err.message` verbatim
|
|
100
|
+
* would re-leak the secret to stderr + logs even though the URL argument is
|
|
101
|
+
* already masked with {@link safeUrl}. First swap the exact configured `url` for
|
|
102
|
+
* its masked form, then strip any residual `scheme://userinfo@` the transport may
|
|
103
|
+
* have echoed in a normalized (non-exact) form. See #2385.
|
|
104
|
+
*/
|
|
105
|
+
const sanitizeReason = (reason, url) => reason
|
|
106
|
+
.split(url)
|
|
107
|
+
.join(safeUrl(url))
|
|
108
|
+
.replace(/([a-z][a-z0-9+.-]*:\/\/)[^/@\s]*@/gi, '$1');
|
|
109
|
+
/**
|
|
110
|
+
* Error thrown by this module's HTTP embedding path (`httpEmbedBatch` /
|
|
111
|
+
* `httpEmbed` / `httpEmbedQuery`) for any endpoint failure — a
|
|
112
|
+
* connection/timeout/DNS error, an open circuit, a non-OK status, an
|
|
113
|
+
* unparseable or wrong-shape response body, an empty response, or a dimension
|
|
114
|
+
* mismatch.
|
|
115
|
+
*
|
|
116
|
+
* Carrying a distinct type (rather than a plain `Error`) lets the CLI tell a
|
|
117
|
+
* *custom endpoint* failure apart from a HuggingFace *model download* failure
|
|
118
|
+
* without matching message text: the two share the same underlying network
|
|
119
|
+
* substrings (`fetch failed`, `ECONNREFUSED`, …), which is exactly why
|
|
120
|
+
* `isNetworkFetchError` in `hf-env.ts` cannot tell them apart. Keying on the
|
|
121
|
+
* type instead of the message is also locale-proof and survives message
|
|
122
|
+
* rewording. The human-readable `.message` (built with `safeUrl` and the
|
|
123
|
+
* underlying reason) is what the CLI surfaces to the user. See #2385.
|
|
124
|
+
*/
|
|
125
|
+
export class HttpEmbeddingError extends Error {
|
|
126
|
+
constructor(message, options) {
|
|
127
|
+
super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);
|
|
128
|
+
this.name = 'HttpEmbeddingError';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* @internal Exported for the CLI analyze error handler and unit tests.
|
|
133
|
+
*
|
|
134
|
+
* Type-guard for {@link HttpEmbeddingError}. The `name` fallback keeps the
|
|
135
|
+
* check working across module-realm boundaries where `instanceof` can fail
|
|
136
|
+
* (two loaded copies of the class) — mirroring the codebase's existing
|
|
137
|
+
* `err.name === 'TimeoutError'` idiom. Matches on the stable class
|
|
138
|
+
* discriminator, never on the human-readable (potentially localized) message.
|
|
139
|
+
*/
|
|
140
|
+
export const isHttpEmbeddingError = (err) => err instanceof HttpEmbeddingError || (err instanceof Error && err.name === 'HttpEmbeddingError');
|
|
141
|
+
/**
|
|
142
|
+
* Runtime guard for a single response item. The `Array.isArray(data.data)` shape
|
|
143
|
+
* check only validates the outer array — a 200 body like `{"data":[null]}` passes
|
|
144
|
+
* it, then crashes at `new Float32Array(item.embedding)` (`httpEmbed`) or
|
|
145
|
+
* `items[0].embedding` (`httpEmbedQuery`) with a raw `TypeError` that escapes the
|
|
146
|
+
* typed boundary, landing on the CLI's generic stack-dump path — the exact class
|
|
147
|
+
* #2385 closes. Validate each item so every wrong-shape body stays classifiable.
|
|
148
|
+
*/
|
|
149
|
+
const isEmbeddingItem = (item) => typeof item === 'object' &&
|
|
150
|
+
item !== null &&
|
|
151
|
+
Array.isArray(item.embedding);
|
|
73
152
|
/**
|
|
74
153
|
* Send a single batch of texts to the embedding endpoint with retry.
|
|
75
154
|
*
|
|
@@ -111,23 +190,36 @@ const httpEmbedBatch = async (url, batch, model, apiKey, batchIndex = 0, dimensi
|
|
|
111
190
|
}
|
|
112
191
|
catch (err) {
|
|
113
192
|
if (err instanceof CircuitOpenError) {
|
|
114
|
-
throw new
|
|
193
|
+
throw new HttpEmbeddingError(`Embedding endpoint circuit open (${safeUrl(url)}, batch ${batchIndex}): retry in ${Math.ceil(err.retryAfterMs / 1000)}s`, { cause: err });
|
|
115
194
|
}
|
|
116
195
|
if (err instanceof DOMException && err.name === 'TimeoutError') {
|
|
117
|
-
throw new
|
|
196
|
+
throw new HttpEmbeddingError(`Embedding request timed out after ${HTTP_TIMEOUT_MS}ms (${safeUrl(url)}, batch ${batchIndex})`, { cause: err });
|
|
118
197
|
}
|
|
119
198
|
if (err instanceof ResilientFetchExhaustedError) {
|
|
120
|
-
throw new
|
|
199
|
+
throw new HttpEmbeddingError(`Embedding endpoint returned ${err.response.status} (${safeUrl(url)}, batch ${batchIndex})`, { cause: err });
|
|
121
200
|
}
|
|
122
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
123
|
-
throw new
|
|
201
|
+
const reason = sanitizeReason(err instanceof Error ? err.message : String(err), url);
|
|
202
|
+
throw new HttpEmbeddingError(`Embedding request failed (${safeUrl(url)}, batch ${batchIndex}): ${reason}`, { cause: err });
|
|
124
203
|
}
|
|
125
204
|
if (!resp.ok) {
|
|
126
205
|
// resilientFetch already retried 5xx/429; any non-OK response here is
|
|
127
206
|
// a terminal client error (4xx other than 429).
|
|
128
|
-
throw new
|
|
207
|
+
throw new HttpEmbeddingError(`Embedding endpoint returned ${resp.status} (${safeUrl(url)}, batch ${batchIndex})`);
|
|
208
|
+
}
|
|
209
|
+
// A reachable-but-wrong endpoint (e.g. a captive portal or a non-embeddings
|
|
210
|
+
// service) can answer 200 with an HTML/truncated body. Parse inside the
|
|
211
|
+
// typed-error boundary so that lands as an endpoint failure the CLI can
|
|
212
|
+
// classify, not a raw SyntaxError/TypeError on the generic stack-dump path.
|
|
213
|
+
let data;
|
|
214
|
+
try {
|
|
215
|
+
data = (await resp.json());
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
throw new HttpEmbeddingError(`Embedding endpoint returned an unparseable response (${safeUrl(url)}, batch ${batchIndex})`, { cause: err });
|
|
219
|
+
}
|
|
220
|
+
if (!Array.isArray(data?.data) || !data.data.every(isEmbeddingItem)) {
|
|
221
|
+
throw new HttpEmbeddingError(`Embedding endpoint returned an unexpected response shape (${safeUrl(url)}, batch ${batchIndex})`);
|
|
129
222
|
}
|
|
130
|
-
const data = (await resp.json());
|
|
131
223
|
return data.data;
|
|
132
224
|
};
|
|
133
225
|
/**
|
|
@@ -150,7 +242,7 @@ export const httpEmbed = async (texts) => {
|
|
|
150
242
|
const batchIndex = Math.floor(i / HTTP_BATCH_SIZE);
|
|
151
243
|
const items = await httpEmbedBatch(url, batch, config.model, config.apiKey, batchIndex, config.dimensions);
|
|
152
244
|
if (items.length !== batch.length) {
|
|
153
|
-
throw new
|
|
245
|
+
throw new HttpEmbeddingError(`Embedding endpoint returned ${items.length} vectors for ${batch.length} texts ` +
|
|
154
246
|
`(${safeUrl(url)}, batch ${batchIndex})`);
|
|
155
247
|
}
|
|
156
248
|
for (const item of items) {
|
|
@@ -162,7 +254,7 @@ export const httpEmbed = async (texts) => {
|
|
|
162
254
|
const hint = config.dimensions
|
|
163
255
|
? 'Update GITNEXUS_EMBEDDING_DIMS to match your model output.'
|
|
164
256
|
: `Set GITNEXUS_EMBEDDING_DIMS=${vec.length} to match your model output.`;
|
|
165
|
-
throw new
|
|
257
|
+
throw new HttpEmbeddingError(`Embedding dimension mismatch: endpoint returned ${vec.length}d vector, ` +
|
|
166
258
|
`but expected ${expected}d. ${hint}`);
|
|
167
259
|
}
|
|
168
260
|
allVectors.push(vec);
|
|
@@ -184,7 +276,7 @@ export const httpEmbedQuery = async (text) => {
|
|
|
184
276
|
const url = `${config.baseUrl}/embeddings`;
|
|
185
277
|
const items = await httpEmbedBatch(url, [text], config.model, config.apiKey, 0, config.dimensions);
|
|
186
278
|
if (!items.length) {
|
|
187
|
-
throw new
|
|
279
|
+
throw new HttpEmbeddingError(`Embedding endpoint returned empty response (${safeUrl(url)})`);
|
|
188
280
|
}
|
|
189
281
|
const embedding = items[0].embedding;
|
|
190
282
|
// Same dimension checks as httpEmbed — catch mismatches before they
|
|
@@ -194,7 +286,7 @@ export const httpEmbedQuery = async (text) => {
|
|
|
194
286
|
const hint = config.dimensions
|
|
195
287
|
? 'Update GITNEXUS_EMBEDDING_DIMS to match your model output.'
|
|
196
288
|
: `Set GITNEXUS_EMBEDDING_DIMS=${embedding.length} to match your model output.`;
|
|
197
|
-
throw new
|
|
289
|
+
throw new HttpEmbeddingError(`Embedding dimension mismatch: endpoint returned ${embedding.length}d vector, ` +
|
|
198
290
|
`but expected ${expected}d. ${hint}`);
|
|
199
291
|
}
|
|
200
292
|
return embedding;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single access point for `module.registerHooks` (#2372).
|
|
3
|
+
*
|
|
4
|
+
* `module.registerHooks` — the synchronous ESM/CJS resolution-hook API the
|
|
5
|
+
* embedding-stack resolvers rely on — was added in Node 22.15.0 (and 23.5.0 on
|
|
6
|
+
* the 23.x line). The gitnexus engines floor is `>=22.0.0`, which admits Node
|
|
7
|
+
* 22.0–22.14 AND 23.0–23.4, where the export is absent.
|
|
8
|
+
*
|
|
9
|
+
* In this `"type": "module"` package, a *static named* import of a missing
|
|
10
|
+
* builtin export (`import { registerHooks } from 'node:module'`) is a
|
|
11
|
+
* `SyntaxError` at ESM link time — thrown before any `typeof registerHooks`
|
|
12
|
+
* guard in the module body can run, so every module carrying that import fails
|
|
13
|
+
* to load on those Node versions. This module owns the only namespace import of
|
|
14
|
+
* `node:module` and hands callers a value-or-`undefined` they guard at runtime,
|
|
15
|
+
* so the graceful-degradation path is finally reachable.
|
|
16
|
+
*
|
|
17
|
+
* `@types/node` types `registerHooks` as always-present, so `nodeModule.registerHooks`
|
|
18
|
+
* would type as defined while being `undefined` at runtime on older Node. The
|
|
19
|
+
* `Partial` narrow surfaces the real optionality without an `any` cast.
|
|
20
|
+
*/
|
|
21
|
+
import * as nodeModule from 'node:module';
|
|
22
|
+
/** `module.registerHooks` if this Node exposes it (>=22.15 / >=23.5), else `undefined`. */
|
|
23
|
+
export declare const getRegisterHooks: () => typeof nodeModule.registerHooks | undefined;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single access point for `module.registerHooks` (#2372).
|
|
3
|
+
*
|
|
4
|
+
* `module.registerHooks` — the synchronous ESM/CJS resolution-hook API the
|
|
5
|
+
* embedding-stack resolvers rely on — was added in Node 22.15.0 (and 23.5.0 on
|
|
6
|
+
* the 23.x line). The gitnexus engines floor is `>=22.0.0`, which admits Node
|
|
7
|
+
* 22.0–22.14 AND 23.0–23.4, where the export is absent.
|
|
8
|
+
*
|
|
9
|
+
* In this `"type": "module"` package, a *static named* import of a missing
|
|
10
|
+
* builtin export (`import { registerHooks } from 'node:module'`) is a
|
|
11
|
+
* `SyntaxError` at ESM link time — thrown before any `typeof registerHooks`
|
|
12
|
+
* guard in the module body can run, so every module carrying that import fails
|
|
13
|
+
* to load on those Node versions. This module owns the only namespace import of
|
|
14
|
+
* `node:module` and hands callers a value-or-`undefined` they guard at runtime,
|
|
15
|
+
* so the graceful-degradation path is finally reachable.
|
|
16
|
+
*
|
|
17
|
+
* `@types/node` types `registerHooks` as always-present, so `nodeModule.registerHooks`
|
|
18
|
+
* would type as defined while being `undefined` at runtime on older Node. The
|
|
19
|
+
* `Partial` narrow surfaces the real optionality without an `any` cast.
|
|
20
|
+
*/
|
|
21
|
+
import * as nodeModule from 'node:module';
|
|
22
|
+
/** `module.registerHooks` if this Node exposes it (>=22.15 / >=23.5), else `undefined`. */
|
|
23
|
+
export const getRegisterHooks = () => nodeModule.registerHooks;
|
|
@@ -57,11 +57,12 @@
|
|
|
57
57
|
* as before — fine on hoisted layouts. Any failure during installation is
|
|
58
58
|
* swallowed.
|
|
59
59
|
*/
|
|
60
|
-
import {
|
|
60
|
+
import { createRequire } from 'node:module';
|
|
61
61
|
import { pathToFileURL } from 'node:url';
|
|
62
62
|
import { join } from 'node:path';
|
|
63
63
|
import { getEffectiveOnnxRuntimeNodeDir } from './onnxruntime-node-resolver.js';
|
|
64
64
|
import { logger } from '../logger.js';
|
|
65
|
+
import { getRegisterHooks } from './node-module-compat.js';
|
|
65
66
|
let attempted = false;
|
|
66
67
|
/**
|
|
67
68
|
* Compute the file: URL the hook redirects `onnxruntime-common` to.
|
|
@@ -98,8 +99,10 @@ export const ensureOnnxRuntimeCommonResolvable = () => {
|
|
|
98
99
|
// initEmbedder() call, and the hook is process-global — once is enough.
|
|
99
100
|
attempted = true;
|
|
100
101
|
try {
|
|
101
|
-
// Node < 22.15 (the gitnexus engines floor is >= 22.0.0): no
|
|
102
|
-
// hooks API. Degrade gracefully — the import still works on
|
|
102
|
+
// Node < 22.15 / < 23.5 (the gitnexus engines floor is >= 22.0.0): no
|
|
103
|
+
// synchronous hooks API. Degrade gracefully — the import still works on
|
|
104
|
+
// hoisted layouts.
|
|
105
|
+
const registerHooks = getRegisterHooks();
|
|
103
106
|
if (typeof registerHooks !== 'function')
|
|
104
107
|
return;
|
|
105
108
|
const redirectUrl = resolveOnnxRuntimeCommonUrl();
|
|
@@ -51,12 +51,14 @@
|
|
|
51
51
|
* loaded (the probe uses CJS `require.resolve`, which an ESM hook does not
|
|
52
52
|
* affect) — keeping probe and runtime consistent.
|
|
53
53
|
*/
|
|
54
|
-
import {
|
|
54
|
+
import { createRequire } from 'node:module';
|
|
55
55
|
import { pathToFileURL } from 'node:url';
|
|
56
56
|
import { existsSync } from 'node:fs';
|
|
57
57
|
import { join, dirname } from 'node:path';
|
|
58
58
|
import { execFileSync } from 'node:child_process';
|
|
59
59
|
import { logger } from '../logger.js';
|
|
60
|
+
import { getEmbeddingRuntimeDir } from './runtime-install.js';
|
|
61
|
+
import { getRegisterHooks } from './node-module-compat.js';
|
|
60
62
|
const require = createRequire(import.meta.url);
|
|
61
63
|
/**
|
|
62
64
|
* Read a shared object's NEEDED entries, tolerating ldd's non-zero exit when a
|
|
@@ -152,7 +154,18 @@ const resolveDefaultOrtNodeDir = () => {
|
|
|
152
154
|
return dirname(createRequire(transformersMain).resolve('onnxruntime-node/package.json'));
|
|
153
155
|
}
|
|
154
156
|
catch {
|
|
155
|
-
|
|
157
|
+
// On-demand runtime prefix (#2370): when the optional stack was pruned at
|
|
158
|
+
// install time and fetched on demand, the copy that actually loads (via
|
|
159
|
+
// ensureEmbeddingStackResolvable's fallback hook) lives in the prefix — so
|
|
160
|
+
// it IS the effective default and must be the one the CUDA probe inspects.
|
|
161
|
+
try {
|
|
162
|
+
const prefixRequire = createRequire(join(getEmbeddingRuntimeDir(), 'noop.js'));
|
|
163
|
+
const transformersMain = prefixRequire.resolve('@huggingface/transformers');
|
|
164
|
+
return dirname(createRequire(transformersMain).resolve('onnxruntime-node/package.json'));
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
156
169
|
}
|
|
157
170
|
};
|
|
158
171
|
/** gitnexus' own direct top-level onnxruntime-node dir. */
|
|
@@ -161,7 +174,19 @@ const resolveOurOrtNodeDir = () => {
|
|
|
161
174
|
return dirname(require.resolve('onnxruntime-node/package.json'));
|
|
162
175
|
}
|
|
163
176
|
catch {
|
|
164
|
-
|
|
177
|
+
// On-demand runtime prefix (#2370): when gitnexus' own onnxruntime-node was
|
|
178
|
+
// pruned at install time and fetched on demand, the prefix copy IS our
|
|
179
|
+
// effective top-level build — so the CUDA-major redirect must be able to
|
|
180
|
+
// target it (mirrors resolveDefaultOrtNodeDir's fallback above). Without
|
|
181
|
+
// this, `embeddings install --cuda` on a pruned install downloads the GPU
|
|
182
|
+
// binaries but the probe still can't see them and embeddings run on CPU.
|
|
183
|
+
try {
|
|
184
|
+
const prefixRequire = createRequire(join(getEmbeddingRuntimeDir(), 'noop.js'));
|
|
185
|
+
return dirname(prefixRequire.resolve('onnxruntime-node/package.json'));
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
165
190
|
}
|
|
166
191
|
};
|
|
167
192
|
let cached = null;
|
|
@@ -176,7 +201,7 @@ const decide = () => {
|
|
|
176
201
|
// major is still probed: a CUDA-12 host on Node 22.0–22.14 whose default
|
|
177
202
|
// build already matches must keep auto-selecting the GPU exactly as it did
|
|
178
203
|
// before this redirect existed.
|
|
179
|
-
const canRedirect = typeof
|
|
204
|
+
const canRedirect = typeof getRegisterHooks() === 'function';
|
|
180
205
|
const systemMajor = detectSystemCudaMajor();
|
|
181
206
|
// `defaultDir` resolving is NOT a precondition for checking `ourDir` below —
|
|
182
207
|
// if transformers' own resolution fails outright (defaultMajor stays null),
|
|
@@ -264,8 +289,9 @@ export const ensureOnnxRuntimeNodeMatchesSystem = () => {
|
|
|
264
289
|
return;
|
|
265
290
|
attempted = true;
|
|
266
291
|
try {
|
|
292
|
+
const registerHooks = getRegisterHooks();
|
|
267
293
|
if (typeof registerHooks !== 'function')
|
|
268
|
-
return; // Node < 22.15: graceful no-op
|
|
294
|
+
return; // Node < 22.15 / < 23.5: graceful no-op
|
|
269
295
|
const d = decide();
|
|
270
296
|
if (!d.redirect || !d.effectiveDir)
|
|
271
297
|
return;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/** Shorter deadline for analyze's auto-install (interactive; must not stall the index run). */
|
|
2
|
+
export declare const ANALYZE_EMBEDDING_INSTALL_TIMEOUT_MS: number;
|
|
3
|
+
/**
|
|
4
|
+
* Deadline for the on-demand npm install. An explicit
|
|
5
|
+
* `GITNEXUS_EMBEDDING_INSTALL_TIMEOUT_MS` always wins (so a user on a slow
|
|
6
|
+
* mirror can raise it); otherwise `defaultMs` applies. The default is generous
|
|
7
|
+
* (the ONNX stack is a large registry fetch), but latency-sensitive callers
|
|
8
|
+
* (analyze's auto-install) pass a shorter `defaultMs` so a blackholed proxy
|
|
9
|
+
* can't stall the whole run for the full ten minutes. Mirrors
|
|
10
|
+
* `getExtensionInstallTimeoutMs`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const getEmbeddingInstallTimeoutMs: (defaultMs?: number) => number;
|
|
13
|
+
/**
|
|
14
|
+
* User-level prefix the on-demand stack installs into. The env override is
|
|
15
|
+
* `path.resolve`d once here (the single chokepoint) so a relative or empty
|
|
16
|
+
* value can't poison the probes downstream — `createRequire` throws
|
|
17
|
+
* `ERR_INVALID_ARG_VALUE` on a relative anchor, which otherwise made every
|
|
18
|
+
* resolution report "not installed" and reinstall on every run. An empty or
|
|
19
|
+
* whitespace-only value falls through to the default.
|
|
20
|
+
*/
|
|
21
|
+
export declare const getEmbeddingRuntimeDir: () => string;
|
|
22
|
+
/**
|
|
23
|
+
* The version specs to install — read from gitnexus' own package.json
|
|
24
|
+
* `optionalDependencies` so the on-demand install can never drift from what a
|
|
25
|
+
* normal install would have provided. (The manifest ships in the tarball even
|
|
26
|
+
* when npm pruned the packages themselves.)
|
|
27
|
+
*/
|
|
28
|
+
export declare const getEmbeddingStackSpecs: () => Record<string, string>;
|
|
29
|
+
export interface EmbeddingRuntimeResolution {
|
|
30
|
+
/** 'package': the normally-installed copy; 'runtime-prefix': the on-demand copy. */
|
|
31
|
+
source: 'package' | 'runtime-prefix';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether a runtime-prefix-sourced stack can actually be loaded on this Node
|
|
35
|
+
* (#2372). The prefix mechanism re-anchors bare specifiers via
|
|
36
|
+
* `module.registerHooks`, absent before Node 22.15 / 23.5 — so on 22.0–22.14 and
|
|
37
|
+
* 23.0–23.4 a populated prefix exists but the ESM loader can never reach it. A
|
|
38
|
+
* package-sourced stack never needs the hook and is unaffected. CLI code
|
|
39
|
+
* consumes this predicate (never the compat module directly) to keep messaging
|
|
40
|
+
* truthful instead of promising a prefix runtime the loader can't use.
|
|
41
|
+
*/
|
|
42
|
+
export declare const isPrefixRuntimeLoadable: () => boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Where the embedding stack resolves from, or `null` when it is not (fully)
|
|
45
|
+
* installed. Resolution only — nothing is imported, so this never loads native
|
|
46
|
+
* code and is safe on every platform.
|
|
47
|
+
*/
|
|
48
|
+
export declare const resolveEmbeddingRuntime: () => EmbeddingRuntimeResolution | null;
|
|
49
|
+
/**
|
|
50
|
+
* Idempotently register the resolution fallback that redirects the embedding
|
|
51
|
+
* stack's bare specifiers to the runtime prefix when normal resolution fails.
|
|
52
|
+
* Mirrors the onnxruntime-common fallback hook (#307): try the default
|
|
53
|
+
* resolution first so a real, package-manager-installed copy always wins, and
|
|
54
|
+
* only re-anchor at the prefix on ERR_MODULE_NOT_FOUND.
|
|
55
|
+
*
|
|
56
|
+
* Must be registered BEFORE the CUDA-13 redirect hook
|
|
57
|
+
* (`ensureOnnxRuntimeNodeMatchesSystem`) — `registerHooks` runs the most
|
|
58
|
+
* recently registered hook first, so registering this one earliest makes it
|
|
59
|
+
* the last-resort fallback in the chain.
|
|
60
|
+
*/
|
|
61
|
+
export declare const ensureEmbeddingStackResolvable: () => void;
|
|
62
|
+
export interface EmbeddingInstallOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Also fetch the CUDA GPU binaries: runs onnxruntime-node's postinstall
|
|
65
|
+
* (NuGet download — set GLOBAL_AGENT_HTTPS_PROXY behind a proxy). Default
|
|
66
|
+
* false: `--ignore-scripts` + ONNXRUNTIME_NODE_INSTALL=skip, so the install
|
|
67
|
+
* touches only the npm registry and CPU embeddings work everywhere.
|
|
68
|
+
*/
|
|
69
|
+
cuda?: boolean;
|
|
70
|
+
/** Progress sink for npm's output lines. */
|
|
71
|
+
onOutput?: (line: string) => void;
|
|
72
|
+
}
|
|
73
|
+
/** Pure command builder, exported for tests. */
|
|
74
|
+
export declare const buildEmbeddingInstallCommand: (opts?: EmbeddingInstallOptions) => {
|
|
75
|
+
args: string[];
|
|
76
|
+
env: NodeJS.ProcessEnv;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Quote a single argument for the Windows `cmd.exe` shell (#2372). npm is a
|
|
80
|
+
* `.cmd` shim, so the spawn must go through a shell (EINVAL otherwise since
|
|
81
|
+
* CVE-2024-27980), and Node does NOT escape args under `shell: true` — a spaced
|
|
82
|
+
* `--prefix` path splits, and cmd eats the `^` in `@pkg@^1.0.0` semver ranges.
|
|
83
|
+
*
|
|
84
|
+
* Rules (validated against Node source, MS cmd/CRT docs, BatBadBut, Rust std):
|
|
85
|
+
* reject NUL/CR/LF and embedded `"` (both unrepresentable/unsafe at the cmd
|
|
86
|
+
* layer, and `"` is illegal in Windows paths and npm specs); wrap in double
|
|
87
|
+
* quotes when empty or containing whitespace/metacharacters; double the trailing
|
|
88
|
+
* backslash run so the added closing quote is not itself escaped (`C:\` →
|
|
89
|
+
* `"C:\\"`). `^` is literal inside cmd double quotes across all three parse
|
|
90
|
+
* layers (cmd `/c` → npm.cmd's `%*` re-parse → node CRT argv). Two documented
|
|
91
|
+
* ceilings quoting can't close: a defined `%VAR%` expands once at the first cmd
|
|
92
|
+
* parse, and `!` expands only under registry-enabled delayed expansion — both
|
|
93
|
+
* are the env-var owner's trust, out of the malicious-repo threat model.
|
|
94
|
+
*/
|
|
95
|
+
export declare const quoteWin32Arg: (arg: string) => string;
|
|
96
|
+
/**
|
|
97
|
+
* Compose a full `cmd.exe` command line: the command stays unquoted (so
|
|
98
|
+
* PATH/PATHEXT resolves a bare name or `.cmd` shim), args are individually
|
|
99
|
+
* quoted. Passing this as spawn's first (only) string argument — no args array
|
|
100
|
+
* — yields a byte-identical `cmd.exe /d /s /c "…"` line while avoiding DEP0190
|
|
101
|
+
* (the runtime deprecation warning Node >=24 emits for
|
|
102
|
+
* `spawn(file, args, {shell:true})`). Exported generically so the real-cmd.exe
|
|
103
|
+
* round-trip test drives the exact same composition the npm spawn uses.
|
|
104
|
+
*/
|
|
105
|
+
export declare const composeWin32Command: (command: string, args: string[]) => string;
|
|
106
|
+
/** {@link composeWin32Command} for the on-demand npm install (`npm` stays unquoted). */
|
|
107
|
+
export declare const composeWin32NpmCommand: (args: string[]) => string;
|
|
108
|
+
/**
|
|
109
|
+
* Install (or update) the embedding stack into the runtime prefix via the
|
|
110
|
+
* user's npm — registry, mirror, and proxy configuration all apply. Rejects
|
|
111
|
+
* with npm's tail output on failure or timeout.
|
|
112
|
+
*
|
|
113
|
+
* The child is bounded by `timeoutMs` (default {@link getEmbeddingInstallTimeoutMs})
|
|
114
|
+
* and SIGKILLed — with its grandchildren — if it overruns, so a blackholed
|
|
115
|
+
* proxy (the exact #2370 environment) can't hang the caller forever. It is also
|
|
116
|
+
* killed if the parent exits mid-install, so a leftover npm can't keep writing
|
|
117
|
+
* into the shared prefix.
|
|
118
|
+
*/
|
|
119
|
+
export declare const installEmbeddingRuntime: (opts?: EmbeddingInstallOptions, timeoutMs?: number) => Promise<void>;
|