@lbroth/rothunter 1.0.0-rc.1
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/LICENSE +21 -0
- package/README.md +141 -0
- package/dist/adapters/llm.d.ts +68 -0
- package/dist/adapters/llm.d.ts.map +1 -0
- package/dist/adapters/llm.js +189 -0
- package/dist/adapters/llm.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +81 -0
- package/dist/config.js.map +1 -0
- package/dist/detector-registry.d.ts +32 -0
- package/dist/detector-registry.d.ts.map +1 -0
- package/dist/detector-registry.js +74 -0
- package/dist/detector-registry.js.map +1 -0
- package/dist/detectors/api-race.d.ts +6 -0
- package/dist/detectors/api-race.d.ts.map +1 -0
- package/dist/detectors/api-race.js +222 -0
- package/dist/detectors/api-race.js.map +1 -0
- package/dist/detectors/bad-config.d.ts +6 -0
- package/dist/detectors/bad-config.d.ts.map +1 -0
- package/dist/detectors/bad-config.js +529 -0
- package/dist/detectors/bad-config.js.map +1 -0
- package/dist/detectors/console-log-prod.d.ts +6 -0
- package/dist/detectors/console-log-prod.d.ts.map +1 -0
- package/dist/detectors/console-log-prod.js +72 -0
- package/dist/detectors/console-log-prod.js.map +1 -0
- package/dist/detectors/dead-api.d.ts +10 -0
- package/dist/detectors/dead-api.d.ts.map +1 -0
- package/dist/detectors/dead-api.js +115 -0
- package/dist/detectors/dead-api.js.map +1 -0
- package/dist/detectors/dead-export.d.ts +12 -0
- package/dist/detectors/dead-export.d.ts.map +1 -0
- package/dist/detectors/dead-export.js +140 -0
- package/dist/detectors/dead-export.js.map +1 -0
- package/dist/detectors/dead-handler.d.ts +12 -0
- package/dist/detectors/dead-handler.d.ts.map +1 -0
- package/dist/detectors/dead-handler.js +40 -0
- package/dist/detectors/dead-handler.js.map +1 -0
- package/dist/detectors/dead-module.d.ts +14 -0
- package/dist/detectors/dead-module.d.ts.map +1 -0
- package/dist/detectors/dead-module.js +50 -0
- package/dist/detectors/dead-module.js.map +1 -0
- package/dist/detectors/deep-nesting.d.ts +12 -0
- package/dist/detectors/deep-nesting.d.ts.map +1 -0
- package/dist/detectors/deep-nesting.js +133 -0
- package/dist/detectors/deep-nesting.js.map +1 -0
- package/dist/detectors/duplicate-function.d.ts +9 -0
- package/dist/detectors/duplicate-function.d.ts.map +1 -0
- package/dist/detectors/duplicate-function.js +199 -0
- package/dist/detectors/duplicate-function.js.map +1 -0
- package/dist/detectors/duplicate-type.d.ts +9 -0
- package/dist/detectors/duplicate-type.d.ts.map +1 -0
- package/dist/detectors/duplicate-type.js +166 -0
- package/dist/detectors/duplicate-type.js.map +1 -0
- package/dist/detectors/hot-hub-file.d.ts +11 -0
- package/dist/detectors/hot-hub-file.d.ts.map +1 -0
- package/dist/detectors/hot-hub-file.js +42 -0
- package/dist/detectors/hot-hub-file.js.map +1 -0
- package/dist/detectors/long-file.d.ts +12 -0
- package/dist/detectors/long-file.d.ts.map +1 -0
- package/dist/detectors/long-file.js +82 -0
- package/dist/detectors/long-file.js.map +1 -0
- package/dist/detectors/long-function.d.ts +12 -0
- package/dist/detectors/long-function.d.ts.map +1 -0
- package/dist/detectors/long-function.js +45 -0
- package/dist/detectors/long-function.js.map +1 -0
- package/dist/detectors/magic-numbers.d.ts +10 -0
- package/dist/detectors/magic-numbers.d.ts.map +1 -0
- package/dist/detectors/magic-numbers.js +332 -0
- package/dist/detectors/magic-numbers.js.map +1 -0
- package/dist/detectors/mutable-globals.d.ts +6 -0
- package/dist/detectors/mutable-globals.d.ts.map +1 -0
- package/dist/detectors/mutable-globals.js +95 -0
- package/dist/detectors/mutable-globals.js.map +1 -0
- package/dist/detectors/mutation.d.ts +11 -0
- package/dist/detectors/mutation.d.ts.map +1 -0
- package/dist/detectors/mutation.js +397 -0
- package/dist/detectors/mutation.js.map +1 -0
- package/dist/detectors/public-any.d.ts +6 -0
- package/dist/detectors/public-any.d.ts.map +1 -0
- package/dist/detectors/public-any.js +52 -0
- package/dist/detectors/public-any.js.map +1 -0
- package/dist/detectors/race-condition.d.ts +6 -0
- package/dist/detectors/race-condition.d.ts.map +1 -0
- package/dist/detectors/race-condition.js +608 -0
- package/dist/detectors/race-condition.js.map +1 -0
- package/dist/detectors/shared-db-write.d.ts +6 -0
- package/dist/detectors/shared-db-write.d.ts.map +1 -0
- package/dist/detectors/shared-db-write.js +656 -0
- package/dist/detectors/shared-db-write.js.map +1 -0
- package/dist/detectors/silent-catch.d.ts +6 -0
- package/dist/detectors/silent-catch.d.ts.map +1 -0
- package/dist/detectors/silent-catch.js +167 -0
- package/dist/detectors/silent-catch.js.map +1 -0
- package/dist/detectors/similar-functions.d.ts +15 -0
- package/dist/detectors/similar-functions.d.ts.map +1 -0
- package/dist/detectors/similar-functions.js +334 -0
- package/dist/detectors/similar-functions.js.map +1 -0
- package/dist/detectors/skip-tests.d.ts +6 -0
- package/dist/detectors/skip-tests.d.ts.map +1 -0
- package/dist/detectors/skip-tests.js +69 -0
- package/dist/detectors/skip-tests.js.map +1 -0
- package/dist/detectors/todo-comments.d.ts +29 -0
- package/dist/detectors/todo-comments.d.ts.map +1 -0
- package/dist/detectors/todo-comments.js +154 -0
- package/dist/detectors/todo-comments.js.map +1 -0
- package/dist/detectors/unused-deps.d.ts +8 -0
- package/dist/detectors/unused-deps.d.ts.map +1 -0
- package/dist/detectors/unused-deps.js +115 -0
- package/dist/detectors/unused-deps.js.map +1 -0
- package/dist/extraction/api-race-confirmer.d.ts +31 -0
- package/dist/extraction/api-race-confirmer.d.ts.map +1 -0
- package/dist/extraction/api-race-confirmer.js +110 -0
- package/dist/extraction/api-race-confirmer.js.map +1 -0
- package/dist/extraction/llm-confirmer.d.ts +25 -0
- package/dist/extraction/llm-confirmer.d.ts.map +1 -0
- package/dist/extraction/llm-confirmer.js +118 -0
- package/dist/extraction/llm-confirmer.js.map +1 -0
- package/dist/extraction/mutation-confirmer.d.ts +30 -0
- package/dist/extraction/mutation-confirmer.d.ts.map +1 -0
- package/dist/extraction/mutation-confirmer.js +73 -0
- package/dist/extraction/mutation-confirmer.js.map +1 -0
- package/dist/extraction/prompt-chunking.d.ts +37 -0
- package/dist/extraction/prompt-chunking.d.ts.map +1 -0
- package/dist/extraction/prompt-chunking.js +61 -0
- package/dist/extraction/prompt-chunking.js.map +1 -0
- package/dist/extraction/race-confirmer.d.ts +28 -0
- package/dist/extraction/race-confirmer.d.ts.map +1 -0
- package/dist/extraction/race-confirmer.js +68 -0
- package/dist/extraction/race-confirmer.js.map +1 -0
- package/dist/extraction/shared-db-write-confirmer.d.ts +31 -0
- package/dist/extraction/shared-db-write-confirmer.d.ts.map +1 -0
- package/dist/extraction/shared-db-write-confirmer.js +141 -0
- package/dist/extraction/shared-db-write-confirmer.js.map +1 -0
- package/dist/extraction/triage-confirmer.d.ts +59 -0
- package/dist/extraction/triage-confirmer.d.ts.map +1 -0
- package/dist/extraction/triage-confirmer.js +104 -0
- package/dist/extraction/triage-confirmer.js.map +1 -0
- package/dist/graph/cfg.d.ts +45 -0
- package/dist/graph/cfg.d.ts.map +1 -0
- package/dist/graph/cfg.js +198 -0
- package/dist/graph/cfg.js.map +1 -0
- package/dist/graph/decorator-entries.d.ts +2 -0
- package/dist/graph/decorator-entries.d.ts.map +1 -0
- package/dist/graph/decorator-entries.js +89 -0
- package/dist/graph/decorator-entries.js.map +1 -0
- package/dist/graph/entry-points.d.ts +12 -0
- package/dist/graph/entry-points.d.ts.map +1 -0
- package/dist/graph/entry-points.js +282 -0
- package/dist/graph/entry-points.js.map +1 -0
- package/dist/graph/handler-conventions.d.ts +2 -0
- package/dist/graph/handler-conventions.d.ts.map +1 -0
- package/dist/graph/handler-conventions.js +26 -0
- package/dist/graph/handler-conventions.js.map +1 -0
- package/dist/graph/iac-entries.d.ts +2 -0
- package/dist/graph/iac-entries.d.ts.map +1 -0
- package/dist/graph/iac-entries.js +123 -0
- package/dist/graph/iac-entries.js.map +1 -0
- package/dist/graph/import-graph.d.ts +48 -0
- package/dist/graph/import-graph.d.ts.map +1 -0
- package/dist/graph/import-graph.js +86 -0
- package/dist/graph/import-graph.js.map +1 -0
- package/dist/graph/monorepo-detect.d.ts +3 -0
- package/dist/graph/monorepo-detect.d.ts.map +1 -0
- package/dist/graph/monorepo-detect.js +166 -0
- package/dist/graph/monorepo-detect.js.map +1 -0
- package/dist/graph/tsconfig-paths.d.ts +23 -0
- package/dist/graph/tsconfig-paths.d.ts.map +1 -0
- package/dist/graph/tsconfig-paths.js +217 -0
- package/dist/graph/tsconfig-paths.js.map +1 -0
- package/dist/multi-workspace-scanner.d.ts +13 -0
- package/dist/multi-workspace-scanner.d.ts.map +1 -0
- package/dist/multi-workspace-scanner.js +130 -0
- package/dist/multi-workspace-scanner.js.map +1 -0
- package/dist/normalizers/type-normalizer.d.ts +16 -0
- package/dist/normalizers/type-normalizer.d.ts.map +1 -0
- package/dist/normalizers/type-normalizer.js +189 -0
- package/dist/normalizers/type-normalizer.js.map +1 -0
- package/dist/parsers/typescript-parser.d.ts +57 -0
- package/dist/parsers/typescript-parser.d.ts.map +1 -0
- package/dist/parsers/typescript-parser.js +502 -0
- package/dist/parsers/typescript-parser.js.map +1 -0
- package/dist/reporter/json-reporter.d.ts +12 -0
- package/dist/reporter/json-reporter.d.ts.map +1 -0
- package/dist/reporter/json-reporter.js +28 -0
- package/dist/reporter/json-reporter.js.map +1 -0
- package/dist/reporter/markdown-reporter.d.ts +11 -0
- package/dist/reporter/markdown-reporter.d.ts.map +1 -0
- package/dist/reporter/markdown-reporter.js +77 -0
- package/dist/reporter/markdown-reporter.js.map +1 -0
- package/dist/rothunter.d.ts +125 -0
- package/dist/rothunter.d.ts.map +1 -0
- package/dist/rothunter.js +1038 -0
- package/dist/rothunter.js.map +1 -0
- package/dist/server/false-positives.d.ts +34 -0
- package/dist/server/false-positives.d.ts.map +1 -0
- package/dist/server/false-positives.js +85 -0
- package/dist/server/false-positives.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +1529 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/marked-to-fix.d.ts +16 -0
- package/dist/server/marked-to-fix.d.ts.map +1 -0
- package/dist/server/marked-to-fix.js +36 -0
- package/dist/server/marked-to-fix.js.map +1 -0
- package/dist/server/scan-store.d.ts +147 -0
- package/dist/server/scan-store.d.ts.map +1 -0
- package/dist/server/scan-store.js +291 -0
- package/dist/server/scan-store.js.map +1 -0
- package/dist/server/settings-store.d.ts +28 -0
- package/dist/server/settings-store.d.ts.map +1 -0
- package/dist/server/settings-store.js +46 -0
- package/dist/server/settings-store.js.map +1 -0
- package/dist/server/workspace-store.d.ts +39 -0
- package/dist/server/workspace-store.d.ts.map +1 -0
- package/dist/server/workspace-store.js +108 -0
- package/dist/server/workspace-store.js.map +1 -0
- package/dist/types/detector-input.d.ts +37 -0
- package/dist/types/detector-input.d.ts.map +1 -0
- package/dist/types/detector-input.js +2 -0
- package/dist/types/detector-input.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/clustering.d.ts +14 -0
- package/dist/utils/clustering.d.ts.map +1 -0
- package/dist/utils/clustering.js +56 -0
- package/dist/utils/clustering.js.map +1 -0
- package/dist/utils/gitignore.d.ts +32 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/gitignore.js +122 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/dist/utils/hash.d.ts +11 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +14 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/ignore-annotation.d.ts +28 -0
- package/dist/utils/ignore-annotation.d.ts.map +1 -0
- package/dist/utils/ignore-annotation.js +46 -0
- package/dist/utils/ignore-annotation.js.map +1 -0
- package/dist/utils/llm-json.d.ts +2 -0
- package/dist/utils/llm-json.d.ts.map +1 -0
- package/dist/utils/llm-json.js +53 -0
- package/dist/utils/llm-json.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +4 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/project-conventions.d.ts +2 -0
- package/dist/utils/project-conventions.d.ts.map +1 -0
- package/dist/utils/project-conventions.js +108 -0
- package/dist/utils/project-conventions.js.map +1 -0
- package/dist/utils/regex.d.ts +9 -0
- package/dist/utils/regex.d.ts.map +1 -0
- package/dist/utils/regex.js +11 -0
- package/dist/utils/regex.js.map +1 -0
- package/dist/utils/snippet.d.ts +20 -0
- package/dist/utils/snippet.d.ts.map +1 -0
- package/dist/utils/snippet.js +28 -0
- package/dist/utils/snippet.js.map +1 -0
- package/dist/utils/source-reader.d.ts +19 -0
- package/dist/utils/source-reader.d.ts.map +1 -0
- package/dist/utils/source-reader.js +32 -0
- package/dist/utils/source-reader.js.map +1 -0
- package/logo.png +0 -0
- package/package.json +92 -0
- package/scripts/start-llm.mjs +161 -0
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lbroth/rothunter",
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
4
|
+
"description": "Self-hosted code-hygiene engine for TypeScript / JavaScript codebases. Deterministic detectors + local LLM verdicts + dashboard.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "lBroth",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/lBroth/rothunter.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/lBroth/rothunter/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/lBroth/rothunter#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"static-analysis",
|
|
17
|
+
"code-quality",
|
|
18
|
+
"typescript",
|
|
19
|
+
"race-condition",
|
|
20
|
+
"dead-code",
|
|
21
|
+
"duplicate-detection",
|
|
22
|
+
"monorepo",
|
|
23
|
+
"llm",
|
|
24
|
+
"self-hosted"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "dist/rothunter.js",
|
|
28
|
+
"types": "dist/rothunter.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/rothunter.d.ts",
|
|
32
|
+
"default": "./dist/rothunter.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"scripts/start-llm.mjs",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"logo.png"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=24"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc",
|
|
47
|
+
"build:ui": "cd src/ui && npm install && npm run build",
|
|
48
|
+
"prepublishOnly": "npm run build",
|
|
49
|
+
"setup": "npm install && cd src/ui && npm install",
|
|
50
|
+
"server": "tsx watch src/server/index.ts",
|
|
51
|
+
"server:lan": "ROTHUNTER_HOST=0.0.0.0 tsx watch src/server/index.ts",
|
|
52
|
+
"ui": "cd src/ui && npm run dev",
|
|
53
|
+
"llm": "node scripts/start-llm.mjs",
|
|
54
|
+
"dev": "concurrently -k -n SERVER,UI -c blue,magenta \"npm:server\" \"npm:ui\"",
|
|
55
|
+
"dev:full": "concurrently -k -n LLM,SERVER,UI -c green,blue,magenta \"npm:llm\" \"npm:server\" \"npm:ui\"",
|
|
56
|
+
"dev:lan": "concurrently -k -n SERVER,UI -c blue,magenta \"npm:server:lan\" \"npm:ui\"",
|
|
57
|
+
"dev:full:lan": "concurrently -k -n LLM,SERVER,UI -c green,blue,magenta \"npm:llm\" \"npm:server:lan\" \"npm:ui\"",
|
|
58
|
+
"docker": "docker compose -f src/docker/docker-compose.yml up --build",
|
|
59
|
+
"lint": "eslint .",
|
|
60
|
+
"lint:fix": "eslint . --fix",
|
|
61
|
+
"format": "prettier --write .",
|
|
62
|
+
"format:check": "prettier --check .",
|
|
63
|
+
"depcheck": "depcheck",
|
|
64
|
+
"test": "NODE_OPTIONS='--experimental-vm-modules' jest"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@fastify/static": "^9.1.3",
|
|
68
|
+
"fastify": "^5.8.5",
|
|
69
|
+
"ignore": "^7.0.5",
|
|
70
|
+
"pino": "^10.3.1",
|
|
71
|
+
"ts-morph": "^28.0.0",
|
|
72
|
+
"zod": "^4.4.3"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@eslint/js": "^10.0.1",
|
|
76
|
+
"@jest/globals": "^30.4.1",
|
|
77
|
+
"@resvg/resvg-js": "^2.6.2",
|
|
78
|
+
"@types/jest": "^30.0.0",
|
|
79
|
+
"@types/node": "^24.0.0",
|
|
80
|
+
"concurrently": "^9.2.1",
|
|
81
|
+
"depcheck": "^1.4.7",
|
|
82
|
+
"eslint": "^10.4.0",
|
|
83
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
84
|
+
"globals": "^17.6.0",
|
|
85
|
+
"jest": "^30.4.2",
|
|
86
|
+
"prettier": "^3.8.3",
|
|
87
|
+
"ts-jest": "^29.4.10",
|
|
88
|
+
"tsx": "^4.22.3",
|
|
89
|
+
"typescript": "^5.9.3",
|
|
90
|
+
"typescript-eslint": "^8.59.4"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Launch the LLM sidecar for the dev loop.
|
|
3
|
+
//
|
|
4
|
+
// Strategy — auto-detect the fastest backend available on this host:
|
|
5
|
+
//
|
|
6
|
+
// 1. `llama-server` on PATH → run native llama.cpp (uses Metal on
|
|
7
|
+
// macOS, CUDA / Vulkan on Linux when the binary was built with
|
|
8
|
+
// GPU support).
|
|
9
|
+
// 2. Docker Desktop available → fall back to the docker-compose
|
|
10
|
+
// `rothunter-llm` service. Slower on macOS (no Metal in the
|
|
11
|
+
// Linux VM) but works on any platform with Docker.
|
|
12
|
+
//
|
|
13
|
+
// Set ROTHUNTER_LLM_BACKEND=llamacpp|docker to force a specific
|
|
14
|
+
// backend; otherwise we pick the first available from the list above.
|
|
15
|
+
//
|
|
16
|
+
// The script blocks until its child exits — `npm run dev:full` runs it
|
|
17
|
+
// alongside the server + UI via `concurrently -k` so killing one
|
|
18
|
+
// tears the whole stack down.
|
|
19
|
+
|
|
20
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
21
|
+
import { existsSync, mkdirSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
22
|
+
import { homedir } from 'node:os';
|
|
23
|
+
import { resolve, join } from 'node:path';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
|
|
26
|
+
const REPO_ROOT = resolve(fileURLToPath(import.meta.url), '..', '..');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Reject env values that would let a hostile environment inject extra
|
|
30
|
+
* llama-server flags via the `spawn(..., [args])` argv. `spawn` does
|
|
31
|
+
* not invoke a shell, so true shell injection is already blocked, but
|
|
32
|
+
* a value starting with `-` would still be interpreted as a flag
|
|
33
|
+
* (e.g. setting `ROTHUNTER_LLM_PORT='--alias --rpc 0.0.0.0:5000'`).
|
|
34
|
+
* Tight allow-lists per env: model repo / file paths, integer port,
|
|
35
|
+
* IPv4-ish host.
|
|
36
|
+
*/
|
|
37
|
+
function requireEnv(name, fallback, pattern) {
|
|
38
|
+
const v = process.env[name] ?? fallback;
|
|
39
|
+
if (!pattern.test(v)) {
|
|
40
|
+
console.error(`[llm] refusing to launch — env ${name}=${JSON.stringify(v)} does not match ${pattern}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
return v;
|
|
44
|
+
}
|
|
45
|
+
const MODEL_LLAMACPP_REPO = requireEnv(
|
|
46
|
+
'ROTHUNTER_LLM_MODEL',
|
|
47
|
+
'bartowski/Qwen2.5-Coder-14B-Instruct-GGUF',
|
|
48
|
+
/^[A-Za-z0-9._\-/]+$/,
|
|
49
|
+
);
|
|
50
|
+
const MODEL_LLAMACPP_FILE = requireEnv(
|
|
51
|
+
'ROTHUNTER_LLM_MODEL_FILE',
|
|
52
|
+
'Qwen2.5-Coder-14B-Instruct-Q4_K_M.gguf',
|
|
53
|
+
/^[A-Za-z0-9._\-/]+$/,
|
|
54
|
+
);
|
|
55
|
+
const PORT = requireEnv('ROTHUNTER_LLM_PORT', '8080', /^\d{1,5}$/);
|
|
56
|
+
// Loopback by default — the llama-server endpoint has no auth, so
|
|
57
|
+
// `--host 0.0.0.0` would let anyone on the LAN use the GPU and read
|
|
58
|
+
// the prompts we send. Override with `ROTHUNTER_LLM_HOST=0.0.0.0`
|
|
59
|
+
// only when you intentionally want LAN access (and ideally only
|
|
60
|
+
// behind a reverse proxy / VPN).
|
|
61
|
+
const HOST = requireEnv('ROTHUNTER_LLM_HOST', '127.0.0.1', /^[0-9.:a-fA-F]+$/);
|
|
62
|
+
|
|
63
|
+
function has(cmd) {
|
|
64
|
+
return spawnSync('which', [cmd], { stdio: 'ignore' }).status === 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function hasDocker() {
|
|
68
|
+
return spawnSync('docker', ['version'], { stdio: 'ignore' }).status === 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function pickBackend() {
|
|
72
|
+
const forced = process.env.ROTHUNTER_LLM_BACKEND;
|
|
73
|
+
if (forced) return forced;
|
|
74
|
+
if (has('llama-server')) return 'llamacpp';
|
|
75
|
+
if (hasDocker()) return 'docker';
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Marker file the server reads to know which model + port this script
|
|
81
|
+
* launched. Lets the server pick the right model id without the
|
|
82
|
+
* operator having to mirror env vars between processes.
|
|
83
|
+
*/
|
|
84
|
+
const MARKER_DIR = join(homedir(), '.rothunter');
|
|
85
|
+
const MARKER_PATH = join(MARKER_DIR, 'llm-active.json');
|
|
86
|
+
|
|
87
|
+
function writeMarker(payload) {
|
|
88
|
+
try {
|
|
89
|
+
mkdirSync(MARKER_DIR, { recursive: true });
|
|
90
|
+
writeFileSync(MARKER_PATH, JSON.stringify(payload, null, 2));
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.warn(`[llm] failed to write marker file: ${err.message}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function clearMarker() {
|
|
97
|
+
try { unlinkSync(MARKER_PATH); } catch { /* already gone */ }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function run(cmd, args, marker) {
|
|
101
|
+
console.log(`[llm] launching: ${cmd} ${args.join(' ')}`);
|
|
102
|
+
if (marker) writeMarker(marker);
|
|
103
|
+
const child = spawn(cmd, args, { stdio: 'inherit' });
|
|
104
|
+
child.on('exit', (code) => {
|
|
105
|
+
clearMarker();
|
|
106
|
+
process.exit(code ?? 0);
|
|
107
|
+
});
|
|
108
|
+
const forward = (sig) => () => {
|
|
109
|
+
clearMarker();
|
|
110
|
+
child.kill(sig);
|
|
111
|
+
};
|
|
112
|
+
process.on('SIGINT', forward('SIGINT'));
|
|
113
|
+
process.on('SIGTERM', forward('SIGTERM'));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const backend = pickBackend();
|
|
117
|
+
switch (backend) {
|
|
118
|
+
case 'llamacpp':
|
|
119
|
+
console.log(`[llm] native llama-server — ${MODEL_LLAMACPP_REPO}`);
|
|
120
|
+
run(
|
|
121
|
+
'llama-server',
|
|
122
|
+
[
|
|
123
|
+
'--hf-repo', MODEL_LLAMACPP_REPO,
|
|
124
|
+
'--hf-file', MODEL_LLAMACPP_FILE,
|
|
125
|
+
'--port', PORT,
|
|
126
|
+
'--host', HOST,
|
|
127
|
+
'--jinja',
|
|
128
|
+
'-c', '8192',
|
|
129
|
+
'-n', '256',
|
|
130
|
+
],
|
|
131
|
+
{ backend: 'llamacpp', model: MODEL_LLAMACPP_REPO, port: PORT },
|
|
132
|
+
);
|
|
133
|
+
break;
|
|
134
|
+
case 'docker': {
|
|
135
|
+
const compose = resolve(REPO_ROOT, 'src/docker/docker-compose.yml');
|
|
136
|
+
if (!existsSync(compose)) {
|
|
137
|
+
console.error(`[llm] docker-compose file not found at ${compose}`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
console.log('[llm] docker fallback — slower on macOS (no Metal in the VM)');
|
|
141
|
+
run(
|
|
142
|
+
'docker',
|
|
143
|
+
['compose', '-f', compose, 'up', 'rothunter-llm'],
|
|
144
|
+
{ backend: 'docker', model: MODEL_LLAMACPP_REPO, port: PORT },
|
|
145
|
+
);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
default:
|
|
149
|
+
console.error(
|
|
150
|
+
[
|
|
151
|
+
'[llm] no LLM backend available. Install ONE of:',
|
|
152
|
+
' • brew install llama.cpp (native llama-server, recommended)',
|
|
153
|
+
' • Docker Desktop (docker-compose sidecar fallback)',
|
|
154
|
+
'',
|
|
155
|
+
'Or set ROTHUNTER_LLM_BASE_URL to an existing OpenAI-compatible',
|
|
156
|
+
'endpoint (vLLM, OpenRouter, LM Studio, …) and start the server /',
|
|
157
|
+
'UI directly with `npm run server` and `npm run ui`.',
|
|
158
|
+
].join('\n'),
|
|
159
|
+
);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|