@backbencher/brain 0.0.0-stage → 0.1.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 +298 -0
- package/EMBEDDINGS.md +355 -0
- package/LICENSE +21 -0
- package/README.md +415 -2
- package/bin/bb +13 -0
- package/dist/analysis-exclusions-7ZJDXOVZ.js +35 -0
- package/dist/brain-index-health-KY7K552E.js +17 -0
- package/dist/brain-relationship-enrichment-QH7OOHUF.js +558 -0
- package/dist/brain.sql-EIM4WS4D.js +29 -0
- package/dist/chunk-2BSKD7O3.js +40 -0
- package/dist/chunk-2DE27DVC.js +1314 -0
- package/dist/chunk-2RRBMNBQ.js +322 -0
- package/dist/chunk-2RTN2DFU.js +711 -0
- package/dist/chunk-2WXYNLBA.js +29 -0
- package/dist/chunk-3CTFIJJB.js +17 -0
- package/dist/chunk-3LFTSMIV.js +174 -0
- package/dist/chunk-47HKX4AJ.js +930 -0
- package/dist/chunk-4H22TLOT.js +543 -0
- package/dist/chunk-5SD45FLC.js +818 -0
- package/dist/chunk-66A5ZFYA.js +1758 -0
- package/dist/chunk-6AIRWAO2.js +247 -0
- package/dist/chunk-6WNGD3NN.js +196 -0
- package/dist/chunk-7AN7U5IZ.js +371 -0
- package/dist/chunk-BHB7BYYI.js +626 -0
- package/dist/chunk-BQ3OVQ3P.js +125 -0
- package/dist/chunk-CW4XXSNN.js +1479 -0
- package/dist/chunk-D2Q4Y47A.js +53 -0
- package/dist/chunk-DIYAGQJL.js +34 -0
- package/dist/chunk-DYM4FQEE.js +90 -0
- package/dist/chunk-EWMWK7WN.js +75 -0
- package/dist/chunk-FVVQTSIM.js +3427 -0
- package/dist/chunk-FWSL4VG6.js +1290 -0
- package/dist/chunk-GBI7V7KY.js +69 -0
- package/dist/chunk-GEFFO6O2.js +152 -0
- package/dist/chunk-HOWAQXJO.js +236 -0
- package/dist/chunk-HQSPJDHI.js +613 -0
- package/dist/chunk-IFZNR5UJ.js +2863 -0
- package/dist/chunk-J4PMBXQQ.js +336 -0
- package/dist/chunk-KAET47ZL.js +125 -0
- package/dist/chunk-KCDECRJY.js +440 -0
- package/dist/chunk-KLK3PBTO.js +1119 -0
- package/dist/chunk-L5TDYKYK.js +76 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-OWISBAOF.js +61 -0
- package/dist/chunk-PJTNGD3Z.js +6924 -0
- package/dist/chunk-QLK6ED4Q.js +235 -0
- package/dist/chunk-R2GW4F6R.js +19 -0
- package/dist/chunk-RCMTFSEF.js +87 -0
- package/dist/chunk-RH7KC5HU.js +12050 -0
- package/dist/chunk-RM3PY6TG.js +376 -0
- package/dist/chunk-ROMM6KMC.js +433 -0
- package/dist/chunk-SK6YVHFZ.js +211 -0
- package/dist/chunk-SM7RFQRO.js +214 -0
- package/dist/chunk-UFE77L67.js +782 -0
- package/dist/chunk-UGR4QIGS.js +67 -0
- package/dist/chunk-UUNHHX4M.js +13 -0
- package/dist/chunk-V44PGPA6.js +2104 -0
- package/dist/chunk-VL52PL2T.js +71 -0
- package/dist/chunk-VURYFIHW.js +201 -0
- package/dist/chunk-XJKJYIPP.js +7436 -0
- package/dist/chunk-YI6FF2ZR.js +340 -0
- package/dist/cli/index.js +37 -0
- package/dist/config-HZAAYDP3.js +10 -0
- package/dist/content-search-642F7E5V.js +593 -0
- package/dist/database-TCTZDLE5.js +9 -0
- package/dist/doctor-GXCKSJOX.js +395 -0
- package/dist/edit-engine-KM7MPFEC.js +51 -0
- package/dist/engine-42TZFNIZ.js +11 -0
- package/dist/engine-LO6L4JLQ.js +18 -0
- package/dist/fingerprint-XDXFRWO6.js +33 -0
- package/dist/framework-intelligence-DWFCKV44.js +860 -0
- package/dist/freshness-3K6VU5OT.js +174 -0
- package/dist/graph-engine-PL5YA7BA.js +21 -0
- package/dist/graph_request-MM4REP6H.js +350 -0
- package/dist/impact_request-WDE47IZ5.js +68 -0
- package/dist/index-space-IUUN5WYV.js +15 -0
- package/dist/index-status-6BD7UBHR.js +34 -0
- package/dist/index.js +159 -0
- package/dist/instance-FO6QZRLH.js +19 -0
- package/dist/language-detection-DFCAU5DZ.js +11 -0
- package/dist/lsp/server.js +4301 -0
- package/dist/mcp/server.js +132 -0
- package/dist/onboard-DIQZV2NW.js +161 -0
- package/dist/override-companion-pass-XR7MHTP7.js +117 -0
- package/dist/parallel-indexer-4PW3AFXP.js +88 -0
- package/dist/pool-worker.js +1153 -0
- package/dist/project-health-ZS5TS2Z6.js +94 -0
- package/dist/project-index-5SDPHITE.js +57 -0
- package/dist/project-registry-DGZBIZS7.js +355 -0
- package/dist/real-bash-analyzer-SXSV67E2.js +2201 -0
- package/dist/real-c-analyzer-SY7FTZ6W.js +5942 -0
- package/dist/real-clean-analyzer-IGDHHLZK.js +3470 -0
- package/dist/real-clojure-analyzer-4OH7IWLB.js +2818 -0
- package/dist/real-cpp-analyzer-7LTDRJRJ.js +4101 -0
- package/dist/real-csharp-analyzer-KICU3TLH.js +3298 -0
- package/dist/real-css-analyzer-HPU277Q6.js +15 -0
- package/dist/real-dart-analyzer-7LZ5LCMW.js +3944 -0
- package/dist/real-elixir-analyzer-BE3MEVZO.js +2192 -0
- package/dist/real-erlang-analyzer-BZMAKYBK.js +1731 -0
- package/dist/real-fsharp-analyzer-5QLTOV3E.js +2390 -0
- package/dist/real-go-analyzer-POD7MZQR.js +4126 -0
- package/dist/real-graphql-analyzer-MAQRZ6QW.js +1442 -0
- package/dist/real-groovy-analyzer-MEWSUPMJ.js +2285 -0
- package/dist/real-haskell-analyzer-I5L2GR3Y.js +2533 -0
- package/dist/real-hcl-analyzer-DBOPXSWJ.js +725 -0
- package/dist/real-html-analyzer-O63XZLZD.js +1759 -0
- package/dist/real-java-analyzer-CHCW3EKA.js +4677 -0
- package/dist/real-javascript-analyzer-QAAJQYZE.js +14 -0
- package/dist/real-json-analyzer-UGBR7YQN.js +1640 -0
- package/dist/real-julia-analyzer-35Q4GHOZ.js +2085 -0
- package/dist/real-kotlin-analyzer-GRAYCWW2.js +3956 -0
- package/dist/real-lua-analyzer-5BAPDZDF.js +1957 -0
- package/dist/real-makefile-analyzer-YJKAHNAS.js +920 -0
- package/dist/real-markdown-analyzer-UKUQZO4F.js +1215 -0
- package/dist/real-matlab-analyzer-FVIKQZKG.js +480 -0
- package/dist/real-nim-analyzer-M7FAIABG.js +3603 -0
- package/dist/real-objective-c-analyzer-XMYQIGQI.js +1518 -0
- package/dist/real-perl-analyzer-ZLY6M2DZ.js +1865 -0
- package/dist/real-php-analyzer-KI2ZAM7X.js +9368 -0
- package/dist/real-powershell-analyzer-B3T5ZP6U.js +2915 -0
- package/dist/real-protobuf-analyzer-23OZ75XV.js +867 -0
- package/dist/real-python-analyzer-I4TIUB6D.js +4880 -0
- package/dist/real-r-analyzer-FJZJXXS6.js +2596 -0
- package/dist/real-ruby-analyzer-M3UFIVTQ.js +2429 -0
- package/dist/real-rust-analyzer-LVSONPAN.js +3359 -0
- package/dist/real-scala-analyzer-CWPI33Q2.js +3293 -0
- package/dist/real-scss-analyzer-TGRJQ7TH.js +1515 -0
- package/dist/real-solidity-analyzer-4P2VBVWA.js +2608 -0
- package/dist/real-sql-analyzer-J7DYZOZR.js +3840 -0
- package/dist/real-svelte-analyzer-NHEN2SX5.js +2532 -0
- package/dist/real-swift-analyzer-2NSYULWI.js +2311 -0
- package/dist/real-toml-analyzer-L3C66L7Q.js +929 -0
- package/dist/real-typescript-analyzer-FTC6ETRQ.js +14 -0
- package/dist/real-vb-analyzer-ORY7LAQS.js +3145 -0
- package/dist/real-vba-analyzer-JH2TTUFI.js +466 -0
- package/dist/real-vue-analyzer-OO4FUGBV.js +2680 -0
- package/dist/real-xml-analyzer-6CQVR477.js +2405 -0
- package/dist/real-yaml-analyzer-J6MQG3YX.js +3528 -0
- package/dist/real-zig-analyzer-EFKCPJNU.js +2430 -0
- package/dist/regex-search-OPTLKUN5.js +239 -0
- package/dist/rules-I453JTRC.js +152 -0
- package/dist/scoped-path-MJYWJZQX.js +11 -0
- package/dist/setup-show-Y5GB5KXA.js +15 -0
- package/dist/setup-wizard-G5YYR5T6.js +24 -0
- package/dist/symbol-search-KB4MU4SI.js +16 -0
- package/dist/taint-YOTN5X55.js +202 -0
- package/dist/taint-analyzer-N7YLZU3N.js +163 -0
- package/dist/tool-handlers-G4NAGJIH.js +72 -0
- package/dist/tool-registry-BPEJNSZG.js +17 -0
- package/dist/tool-validation-RF675A3C.js +32 -0
- package/dist/type-inference-4Y7WTMFE.js +13 -0
- package/dist/vector-binary-LMY4QGAN.js +19 -0
- package/dist/version-LXGNFO37.js +9 -0
- package/dist/wasm-NMWO3WXY.js +16 -0
- package/docs/EMBEDDINGS.md +132 -0
- package/docs/README.md +178 -0
- package/docs/data/m10-baseline.json +602 -0
- package/docs/data/m10-benchmark-baseline.json +57 -0
- package/docs/data/m10-benchmark-last-run.json +127 -0
- package/docs/data/m10-final-audit.json +215 -0
- package/docs/data/m12-final-audit.json +138 -0
- package/docs/data/m12-surface-matrix.json +50 -0
- package/docs/data/public-tools.json +1902 -0
- package/docs/indexing/DEAD-ASSETS.md +88 -0
- package/docs/indexing/INDEXER-REVIEW.md +634 -0
- package/docs/indexing/PERFORMANCE-FINDINGS.md +106 -0
- package/docs/indexing/README.md +427 -0
- package/docs/indexing/discovery-matrix.md +41 -0
- package/docs/indexing/embeddings.md +325 -0
- package/docs/indexing/environment-variables.md +77 -0
- package/docs/indexing/performance.md +323 -0
- package/docs/indexing/resume.md +345 -0
- package/docs/indexing/troubleshooting.md +391 -0
- package/docs/man/bb.1 +190 -0
- package/docs/tools/bb_analyze.md +147 -0
- package/docs/tools/bb_annotate.md +110 -0
- package/docs/tools/bb_annotations.md +106 -0
- package/docs/tools/bb_ask.md +101 -0
- package/docs/tools/bb_cross_language.md +103 -0
- package/docs/tools/bb_doc.md +169 -0
- package/docs/tools/bb_edit.md +185 -0
- package/docs/tools/bb_graph.md +169 -0
- package/docs/tools/bb_health.md +156 -0
- package/docs/tools/bb_impact.md +117 -0
- package/docs/tools/bb_index.md +108 -0
- package/docs/tools/bb_migrate.md +135 -0
- package/docs/tools/bb_patterns.md +140 -0
- package/docs/tools/bb_search.md +150 -0
- package/docs/tools/bb_security.md +100 -0
- package/docs/tools/bb_visualize.md +184 -0
- package/grammars/README.md +57 -0
- package/grammars/THIRD-PARTY-NOTICES.md +50 -0
- package/grammars/tree-sitter-c/LICENSE +21 -0
- package/grammars/tree-sitter-clojure/LICENSE.md +21 -0
- package/grammars/tree-sitter-cpp/LICENSE +21 -0
- package/grammars/tree-sitter-csharp/LICENSE +21 -0
- package/grammars/tree-sitter-css/LICENSE +21 -0
- package/grammars/tree-sitter-css/README.md +21 -0
- package/grammars/tree-sitter-dart/LICENSE +20 -0
- package/grammars/tree-sitter-dart-upstream/LICENSE +20 -0
- package/grammars/tree-sitter-dart-upstream/README.md +18 -0
- package/grammars/tree-sitter-elixir/LICENSE +176 -0
- package/grammars/tree-sitter-elixir/README.md +11 -0
- package/grammars/tree-sitter-fsharp/LICENSE +21 -0
- package/grammars/tree-sitter-fsharp/README.md +45 -0
- package/grammars/tree-sitter-graphql/LICENSE +21 -0
- package/grammars/tree-sitter-graphql/README.md +106 -0
- package/grammars/tree-sitter-groovy/LICENSE +21 -0
- package/grammars/tree-sitter-groovy/README.md +4 -0
- package/grammars/tree-sitter-haskell/LICENSE +21 -0
- package/grammars/tree-sitter-haskell/README.md +394 -0
- package/grammars/tree-sitter-hcl/CHANGELOG.md +102 -0
- package/grammars/tree-sitter-hcl/LICENSE +201 -0
- package/grammars/tree-sitter-hcl/README.md +39 -0
- package/grammars/tree-sitter-html/LICENSE +21 -0
- package/grammars/tree-sitter-html/README.md +21 -0
- package/grammars/tree-sitter-julia/LICENSE +21 -0
- package/grammars/tree-sitter-julia/README.md +24 -0
- package/grammars/tree-sitter-kotlin/LICENSE +9 -0
- package/grammars/tree-sitter-lua/LICENSE.md +21 -0
- package/grammars/tree-sitter-lua/README.md +29 -0
- package/grammars/tree-sitter-make/LICENSE +21 -0
- package/grammars/tree-sitter-matlab/LICENSE +19 -0
- package/grammars/tree-sitter-matlab/README.md +99 -0
- package/grammars/tree-sitter-nim/LICENSE +373 -0
- package/grammars/tree-sitter-nim/README.md +46 -0
- package/grammars/tree-sitter-objc/LICENSE +21 -0
- package/grammars/tree-sitter-objc/README.md +20 -0
- package/grammars/tree-sitter-perl/LICENSE +7 -0
- package/grammars/tree-sitter-perl/README.md +100 -0
- package/grammars/tree-sitter-powershell/LICENSE +21 -0
- package/grammars/tree-sitter-powershell/README.md +7 -0
- package/grammars/tree-sitter-proto/LICENSE +21 -0
- package/grammars/tree-sitter-proto/README.md +105 -0
- package/grammars/tree-sitter-r/LICENSE +9 -0
- package/grammars/tree-sitter-r/README.md +66 -0
- package/grammars/tree-sitter-ruby/LICENSE +22 -0
- package/grammars/tree-sitter-scss/LICENSE +21 -0
- package/grammars/tree-sitter-scss/README.md +17 -0
- package/grammars/tree-sitter-solidity/LICENSE +7 -0
- package/grammars/tree-sitter-sql/LICENSE +21 -0
- package/grammars/tree-sitter-typescript/LICENSE +21 -0
- package/grammars/tree-sitter-vba/LICENSE +21 -0
- package/grammars/tree-sitter-vba/README.md +738 -0
- package/grammars/tree-sitter-vbnet/LICENSE +21 -0
- package/grammars/tree-sitter-vbnet/README.md +322 -0
- package/grammars/tree-sitter-xml/LICENSE +19 -0
- package/grammars/tree-sitter-yaml/LICENSE +20 -0
- package/grammars/tree-sitter-yaml/README.md +21 -0
- package/package.json +184 -4
- package/wasm/dart.wasm +0 -0
- package/wasm/tree-sitter-bash.wasm +0 -0
- package/wasm/tree-sitter-c.wasm +0 -0
- package/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/wasm/tree-sitter-clean.wasm +0 -0
- package/wasm/tree-sitter-clojure.wasm +0 -0
- package/wasm/tree-sitter-core_schema.wasm +0 -0
- package/wasm/tree-sitter-cpp.wasm +0 -0
- package/wasm/tree-sitter-css.wasm +0 -0
- package/wasm/tree-sitter-dart.wasm +0 -0
- package/wasm/tree-sitter-elisp.wasm +0 -0
- package/wasm/tree-sitter-elixir.wasm +0 -0
- package/wasm/tree-sitter-elm.wasm +0 -0
- package/wasm/tree-sitter-embedded_template.wasm +0 -0
- package/wasm/tree-sitter-erlang.wasm +0 -0
- package/wasm/tree-sitter-fsharp.wasm +0 -0
- package/wasm/tree-sitter-fsharp_signature.wasm +0 -0
- package/wasm/tree-sitter-go.wasm +0 -0
- package/wasm/tree-sitter-graphql.wasm +0 -0
- package/wasm/tree-sitter-groovy.wasm +0 -0
- package/wasm/tree-sitter-haskell.wasm +0 -0
- package/wasm/tree-sitter-hcl.wasm +0 -0
- package/wasm/tree-sitter-html.wasm +0 -0
- package/wasm/tree-sitter-java.wasm +0 -0
- package/wasm/tree-sitter-javascript.wasm +0 -0
- package/wasm/tree-sitter-json.wasm +0 -0
- package/wasm/tree-sitter-json_schema.wasm +0 -0
- package/wasm/tree-sitter-julia.wasm +0 -0
- package/wasm/tree-sitter-kotlin.wasm +0 -0
- package/wasm/tree-sitter-lua.wasm +0 -0
- package/wasm/tree-sitter-make.wasm +0 -0
- package/wasm/tree-sitter-markdown.wasm +0 -0
- package/wasm/tree-sitter-markdown_inline.wasm +0 -0
- package/wasm/tree-sitter-matlab.wasm +0 -0
- package/wasm/tree-sitter-nim.wasm +0 -0
- package/wasm/tree-sitter-objc.wasm +0 -0
- package/wasm/tree-sitter-ocaml.wasm +0 -0
- package/wasm/tree-sitter-perl.wasm +0 -0
- package/wasm/tree-sitter-php.wasm +0 -0
- package/wasm/tree-sitter-php_only.wasm +0 -0
- package/wasm/tree-sitter-powershell.wasm +0 -0
- package/wasm/tree-sitter-protobuf.wasm +0 -0
- package/wasm/tree-sitter-python.wasm +0 -0
- package/wasm/tree-sitter-ql.wasm +0 -0
- package/wasm/tree-sitter-r.wasm +0 -0
- package/wasm/tree-sitter-rescript.wasm +0 -0
- package/wasm/tree-sitter-ruby.wasm +0 -0
- package/wasm/tree-sitter-rust.wasm +0 -0
- package/wasm/tree-sitter-scala.wasm +0 -0
- package/wasm/tree-sitter-scss.wasm +0 -0
- package/wasm/tree-sitter-solidity.wasm +0 -0
- package/wasm/tree-sitter-sql.wasm +0 -0
- package/wasm/tree-sitter-svelte.wasm +0 -0
- package/wasm/tree-sitter-swift.wasm +0 -0
- package/wasm/tree-sitter-systemrdl.wasm +0 -0
- package/wasm/tree-sitter-terraform.wasm +0 -0
- package/wasm/tree-sitter-tlaplus.wasm +0 -0
- package/wasm/tree-sitter-toml.wasm +0 -0
- package/wasm/tree-sitter-tsx.wasm +0 -0
- package/wasm/tree-sitter-typescript.wasm +0 -0
- package/wasm/tree-sitter-vba.wasm +0 -0
- package/wasm/tree-sitter-vbnet.wasm +0 -0
- package/wasm/tree-sitter-vue.wasm +0 -0
- package/wasm/tree-sitter-xml.wasm +0 -0
- package/wasm/tree-sitter-yaml.wasm +0 -0
- package/wasm/tree-sitter-zig.wasm +0 -0
- package/wasm/tree-sitter.wasm +0 -0
- package/wasm/tsx.wasm +0 -0
- package/wasm/typescript.wasm +0 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bbDataDir
|
|
3
|
+
} from "./chunk-2BSKD7O3.js";
|
|
4
|
+
|
|
5
|
+
// src/setup/setup-wizard.ts
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, statSync, mkdirSync, renameSync, chmodSync } from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import * as readline from "readline";
|
|
9
|
+
var PROVIDERS = [
|
|
10
|
+
{
|
|
11
|
+
id: "openrouter-free",
|
|
12
|
+
label: "OpenRouter (free tier)",
|
|
13
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
14
|
+
model: "liquid/lfm-2.5-embedding-350m:free",
|
|
15
|
+
keyHint: "openrouter.ai/keys \u2014 free key, rate-limited"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "nvidia-nim",
|
|
19
|
+
label: "NVIDIA NIM (free tier)",
|
|
20
|
+
baseURL: "https://integrate.api.nvidia.com/v1",
|
|
21
|
+
model: "nvidia/nemotron-3-embed-1b",
|
|
22
|
+
keyHint: "build.nvidia.com \u2014 free key, rate-limited"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "custom",
|
|
26
|
+
label: "Custom / paid provider",
|
|
27
|
+
baseURL: null,
|
|
28
|
+
model: null,
|
|
29
|
+
keyHint: "OpenAI-compatible /embeddings endpoint"
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
function readJson(p) {
|
|
33
|
+
try {
|
|
34
|
+
if (existsSync(p)) return JSON.parse(readFileSync(p, "utf8"));
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function hasEmbeddingConfig(cwd = process.cwd(), env = process.env) {
|
|
40
|
+
if (env.BB_EMBEDDING_BASE_URL && env.BB_EMBEDDING_MODEL) return { configured: true, source: "env" };
|
|
41
|
+
const home = readJson(path.join(bbDataDir(), "config.json"));
|
|
42
|
+
if (home && home.embedding_base_url && home.embedding_model) return { configured: true, source: "global" };
|
|
43
|
+
const local = readJson(path.join(cwd, "bb.json"));
|
|
44
|
+
if (local && local.embedding_base_url && local.embedding_model) return { configured: true, source: "project" };
|
|
45
|
+
if (home && home.setup_skipped === true) return { configured: false, source: "none", skipped: true };
|
|
46
|
+
return { configured: false, source: "none" };
|
|
47
|
+
}
|
|
48
|
+
function mapValidationError(status, bodyText) {
|
|
49
|
+
if (status === 401 || status === 403) {
|
|
50
|
+
return "Key rejected (401). Check the key and provider \u2014 copy-paste may have dropped or added characters.";
|
|
51
|
+
}
|
|
52
|
+
if (status === 429) {
|
|
53
|
+
return "Rate limit (429). The key looks valid; free-tier limit. It can be saved \u2014 indexing backs off and retries.";
|
|
54
|
+
}
|
|
55
|
+
if (status >= 500) {
|
|
56
|
+
return `Provider error (${status}). The key may be valid; the provider is temporarily failing. It can be saved \u2014 indexing retries automatically.`;
|
|
57
|
+
}
|
|
58
|
+
if (status === 404) {
|
|
59
|
+
return `Model not found (404). Check the model name: the provider's /embeddings endpoint does not recognize it.`;
|
|
60
|
+
}
|
|
61
|
+
return `Unexpected response (${status}): ${(bodyText || "").slice(0, 120)}`;
|
|
62
|
+
}
|
|
63
|
+
function firstEmbedding(response) {
|
|
64
|
+
if (typeof response !== "object" || response === null) return void 0;
|
|
65
|
+
if (!("data" in response)) return void 0;
|
|
66
|
+
const list = response.data;
|
|
67
|
+
if (!Array.isArray(list) || list.length === 0) return void 0;
|
|
68
|
+
const first = list[0];
|
|
69
|
+
if (typeof first !== "object" || first === null) return void 0;
|
|
70
|
+
if (!("embedding" in first)) return void 0;
|
|
71
|
+
return first.embedding;
|
|
72
|
+
}
|
|
73
|
+
async function validateKey(baseURL, apiKey, model, fetchImpl = fetch) {
|
|
74
|
+
const url = `${baseURL.replace(/\/+$/, "")}/embeddings`;
|
|
75
|
+
try {
|
|
76
|
+
const parsed = new URL(url);
|
|
77
|
+
if (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]" || parsed.hostname === "::1") {
|
|
78
|
+
const loopbackHosts = ["127.0.0.1", "localhost", "::1"];
|
|
79
|
+
for (const key of ["NO_PROXY", "no_proxy"]) {
|
|
80
|
+
const existing = process.env[key] ?? "";
|
|
81
|
+
const values = new Set(existing.split(",").map((v) => v.trim()).filter(Boolean));
|
|
82
|
+
for (const host of loopbackHosts) values.add(host);
|
|
83
|
+
process.env[key] = [...values].join(",");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
let res;
|
|
89
|
+
try {
|
|
90
|
+
res = await fetchImpl(url, {
|
|
91
|
+
method: "POST",
|
|
92
|
+
headers: {
|
|
93
|
+
"Content-Type": "application/json",
|
|
94
|
+
Authorization: `Bearer ${apiKey}`
|
|
95
|
+
// [pin]
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({ model, input: "test", encoding_format: "float" }),
|
|
98
|
+
signal: AbortSignal.timeout(15e3)
|
|
99
|
+
// [pin]
|
|
100
|
+
});
|
|
101
|
+
} catch (caught) {
|
|
102
|
+
const e = caught instanceof Error ? caught : caught;
|
|
103
|
+
if (e && (e.name === "TimeoutError" || e.name === "AbortError")) {
|
|
104
|
+
throw new Error("Provider did not respond (timeout). Check your internet connection and the base URL.");
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Could not reach the provider: ${e?.message || e}. Check the base URL.`);
|
|
107
|
+
}
|
|
108
|
+
if (res.status === 429 || res.status >= 500) {
|
|
109
|
+
return { softPass: true, status: res.status, model, fingerprint: fingerprintKey(apiKey) };
|
|
110
|
+
}
|
|
111
|
+
if (res.status < 200 || res.status >= 300) {
|
|
112
|
+
throw new Error(mapValidationError(res.status, await res.text().catch(() => "")));
|
|
113
|
+
}
|
|
114
|
+
let data;
|
|
115
|
+
try {
|
|
116
|
+
data = await res.json();
|
|
117
|
+
} catch {
|
|
118
|
+
throw new Error("Provider returned invalid JSON. The base URL must be a /v1 endpoint.");
|
|
119
|
+
}
|
|
120
|
+
const vec = firstEmbedding(data);
|
|
121
|
+
if (!Array.isArray(vec) || vec.length === 0) {
|
|
122
|
+
throw new Error("Provider returned no embedding. Check the base URL and model name.");
|
|
123
|
+
}
|
|
124
|
+
return { softPass: false, status: res.status, model, dimension: vec.length, fingerprint: fingerprintKey(apiKey) };
|
|
125
|
+
}
|
|
126
|
+
function fingerprintKey(key) {
|
|
127
|
+
return key && key.length >= 4 ? key.slice(-4) : "****";
|
|
128
|
+
}
|
|
129
|
+
function saveEmbeddingConfig(partial, dataDir = bbDataDir()) {
|
|
130
|
+
const dir = dataDir;
|
|
131
|
+
mkdirSync(dir, { recursive: true });
|
|
132
|
+
const cfgPath = path.join(dir, "config.json");
|
|
133
|
+
const existing = readJson(cfgPath) || {};
|
|
134
|
+
const merged = { ...existing, ...partial };
|
|
135
|
+
const tmp = cfgPath + ".tmp";
|
|
136
|
+
writeFileSync(tmp, JSON.stringify(merged, null, 2) + "\n", { mode: 384 });
|
|
137
|
+
renameSync(tmp, cfgPath);
|
|
138
|
+
fixConfigMode(cfgPath);
|
|
139
|
+
return cfgPath;
|
|
140
|
+
}
|
|
141
|
+
function fixConfigMode(cfgPath) {
|
|
142
|
+
try {
|
|
143
|
+
const mode = statSync(cfgPath).mode & 511;
|
|
144
|
+
if (mode !== 384) {
|
|
145
|
+
chmodSync(cfgPath, 384);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
function question(rl, prompt) {
|
|
153
|
+
return new Promise((resolve) => rl.question(prompt, resolve));
|
|
154
|
+
}
|
|
155
|
+
function hiddenQuestion(input, prompt) {
|
|
156
|
+
return new Promise((resolve) => {
|
|
157
|
+
const stdin = input;
|
|
158
|
+
const wasRaw = stdin.isRaw === true;
|
|
159
|
+
if (stdin.isTTY && stdin.setRawMode) stdin.setRawMode(true);
|
|
160
|
+
process.stdout.write(prompt);
|
|
161
|
+
let value = "";
|
|
162
|
+
const onKeypress = (ch, key) => {
|
|
163
|
+
if (key && key.name === "enter") {
|
|
164
|
+
cleanup();
|
|
165
|
+
process.stdout.write("\n");
|
|
166
|
+
resolve(value);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (key && key.name === "backspace") {
|
|
170
|
+
if (value.length > 0) {
|
|
171
|
+
value = value.slice(0, -1);
|
|
172
|
+
process.stdout.write("\b \b");
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (key && key.ctrl && key.name === "c") {
|
|
177
|
+
cleanup();
|
|
178
|
+
process.stdout.write("\n");
|
|
179
|
+
resolve(null);
|
|
180
|
+
process.exit(130);
|
|
181
|
+
}
|
|
182
|
+
if (ch && !key?.ctrl && !key?.meta) {
|
|
183
|
+
value += ch;
|
|
184
|
+
process.stdout.write("*");
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
const { emitKeypressEvents } = readline;
|
|
188
|
+
emitKeypressEvents(stdin);
|
|
189
|
+
stdin.on("keypress", onKeypress);
|
|
190
|
+
function cleanup() {
|
|
191
|
+
stdin.removeListener("keypress", onKeypress);
|
|
192
|
+
if (stdin.isTTY && stdin.setRawMode) stdin.setRawMode(wasRaw);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
async function runSetupWizard(io = { input: process.stdin, output: process.stdout }, deps = {}) {
|
|
197
|
+
const out = io.output;
|
|
198
|
+
const rl = readline.createInterface({ input: io.input, output: io.output });
|
|
199
|
+
const fetchImpl = deps.fetchImpl || fetch;
|
|
200
|
+
const dataDir = deps.dataDir || bbDataDir();
|
|
201
|
+
const save = deps.saveImpl || saveEmbeddingConfig;
|
|
202
|
+
const validate = deps.validateImpl || validateKey;
|
|
203
|
+
out.write("\nbb \u2014 embedding setup\n");
|
|
204
|
+
out.write("Semantic search (bb_search, bb_ask) works with embedding vectors.\n");
|
|
205
|
+
out.write("Without a key: local word vectors are used \u2014 weaker results.\n");
|
|
206
|
+
out.write("After adding a model, `bb index --force` is required to refresh the old index.\n\n");
|
|
207
|
+
const providers = PROVIDERS;
|
|
208
|
+
out.write("Choose an embedding provider:\n");
|
|
209
|
+
providers.forEach((p, i) => {
|
|
210
|
+
out.write(` ${i + 1}. ${p.label}
|
|
211
|
+
`);
|
|
212
|
+
if (p.baseURL) out.write(` ${p.model} \u2014 ${p.keyHint}
|
|
213
|
+
`);
|
|
214
|
+
else out.write(` ${p.keyHint}
|
|
215
|
+
`);
|
|
216
|
+
});
|
|
217
|
+
out.write(` ${providers.length + 1}. Skip \u2014 continue with local vectors
|
|
218
|
+
|
|
219
|
+
`);
|
|
220
|
+
const answer = (await question(rl, `Choice [1-${providers.length + 1}]: `)).trim();
|
|
221
|
+
const choice = parseInt(answer, 10);
|
|
222
|
+
if (!choice || choice < 1 || choice > providers.length + 1) {
|
|
223
|
+
out.write("Invalid choice \u2014 setup skipped.\n");
|
|
224
|
+
rl.close();
|
|
225
|
+
return { saved: false, skipped: true, reason: "invalid-choice" };
|
|
226
|
+
}
|
|
227
|
+
if (choice === providers.length + 1) {
|
|
228
|
+
out.write("Continuing with local word vectors. They give weaker results;\n");
|
|
229
|
+
out.write("when you add a real model, refresh the index with `bb index --force`.\n");
|
|
230
|
+
save({ setup_skipped: true }, dataDir);
|
|
231
|
+
rl.close();
|
|
232
|
+
return { saved: false, skipped: true, reason: "user-skipped" };
|
|
233
|
+
}
|
|
234
|
+
const provider = providers[choice - 1];
|
|
235
|
+
let baseURL = provider.baseURL;
|
|
236
|
+
let model = provider.model;
|
|
237
|
+
if (provider.id === "custom") {
|
|
238
|
+
const b = (await question(rl, "Base URL (e.g. https://api.openai.com/v1): ")).trim();
|
|
239
|
+
if (!b) {
|
|
240
|
+
out.write("Base URL is empty \u2014 setup skipped.\n");
|
|
241
|
+
rl.close();
|
|
242
|
+
return { saved: false, skipped: true, reason: "empty-base-url" };
|
|
243
|
+
}
|
|
244
|
+
baseURL = b;
|
|
245
|
+
const m = (await question(rl, "Model name (e.g. text-embedding-3-small): ")).trim();
|
|
246
|
+
if (!m) {
|
|
247
|
+
out.write("Model name is empty \u2014 setup skipped.\n");
|
|
248
|
+
rl.close();
|
|
249
|
+
return { saved: false, skipped: true, reason: "empty-model" };
|
|
250
|
+
}
|
|
251
|
+
model = m;
|
|
252
|
+
}
|
|
253
|
+
if (typeof baseURL !== "string" || typeof model !== "string") {
|
|
254
|
+
out.write("Provider entry has no base URL or model \u2014 setup skipped.\n");
|
|
255
|
+
rl.close();
|
|
256
|
+
return { saved: false, skipped: true, reason: "provider-incomplete" };
|
|
257
|
+
}
|
|
258
|
+
const key = await hiddenQuestion(io.input, "API key (input hidden): ");
|
|
259
|
+
rl.close();
|
|
260
|
+
if (!key || !key.trim()) {
|
|
261
|
+
out.write("Key is empty \u2014 setup skipped.\n");
|
|
262
|
+
return { saved: false, skipped: true, reason: "empty-key" };
|
|
263
|
+
}
|
|
264
|
+
out.write("Validating key\u2026\n");
|
|
265
|
+
let validation;
|
|
266
|
+
try {
|
|
267
|
+
validation = await validate(baseURL, key.trim(), model);
|
|
268
|
+
} catch (caught) {
|
|
269
|
+
const e = caught instanceof Error ? caught : typeof caught === "object" && caught !== null ? caught : {};
|
|
270
|
+
out.write(`\u2717 ${e.message}
|
|
271
|
+
`);
|
|
272
|
+
out.write("Key NOT saved. Fix the settings and try again with `bb setup`.\n");
|
|
273
|
+
return { saved: false, skipped: true, reason: "validation-failed", error: String(e.message) };
|
|
274
|
+
}
|
|
275
|
+
save({
|
|
276
|
+
embedding_base_url: baseURL,
|
|
277
|
+
embedding_api_key: key.trim(),
|
|
278
|
+
embedding_model: model
|
|
279
|
+
}, dataDir);
|
|
280
|
+
if (validation.softPass) {
|
|
281
|
+
out.write(`\u2713 Key saved (provider returned a rate limit \u2014 indexing retries automatically).
|
|
282
|
+
`);
|
|
283
|
+
} else {
|
|
284
|
+
out.write(`\u2713 Key saved and validated (${model}, ${validation.dimension} dimensions).
|
|
285
|
+
`);
|
|
286
|
+
}
|
|
287
|
+
out.write(`Saved to: ${path.join(dataDir, "config.json")} (only your account can read it)
|
|
288
|
+
`);
|
|
289
|
+
return {
|
|
290
|
+
saved: true,
|
|
291
|
+
provider: provider.id,
|
|
292
|
+
model,
|
|
293
|
+
fingerprint: validation.fingerprint,
|
|
294
|
+
softPass: validation.softPass === true
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
async function maybeRunFirstRunSetup(opts = {}) {
|
|
298
|
+
if (opts.noSetup) return { prompted: false, reason: "flag" };
|
|
299
|
+
const state = hasEmbeddingConfig(process.cwd(), process.env);
|
|
300
|
+
if (state.configured) return { prompted: false, reason: "configured" };
|
|
301
|
+
if (state.skipped) return { prompted: false, reason: "skipped" };
|
|
302
|
+
if (!process.stdin.isTTY) {
|
|
303
|
+
if (!opts.quiet) {
|
|
304
|
+
console.log("note: no embedding key \u2014 semantic search will use local vectors (weaker). Setup: bb setup");
|
|
305
|
+
}
|
|
306
|
+
return { prompted: false, reason: "non-tty" };
|
|
307
|
+
}
|
|
308
|
+
const result = await runSetupWizard();
|
|
309
|
+
return { prompted: true, ...result };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export {
|
|
313
|
+
PROVIDERS,
|
|
314
|
+
hasEmbeddingConfig,
|
|
315
|
+
mapValidationError,
|
|
316
|
+
validateKey,
|
|
317
|
+
fingerprintKey,
|
|
318
|
+
saveEmbeddingConfig,
|
|
319
|
+
fixConfigMode,
|
|
320
|
+
runSetupWizard,
|
|
321
|
+
maybeRunFirstRunSetup
|
|
322
|
+
};
|