kirograph 0.27.1 → 0.28.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/README.md +35 -2
- package/dist/bin/commands/annotations.js +56 -0
- package/dist/bin/commands/annotations.js.map +7 -0
- package/dist/bin/commands/ast-rewrite.js +55 -0
- package/dist/bin/commands/ast-rewrite.js.map +7 -0
- package/dist/bin/commands/bench.js +103 -0
- package/dist/bin/commands/bench.js.map +7 -0
- package/dist/bin/commands/branch.js +133 -0
- package/dist/bin/commands/branch.js.map +7 -0
- package/dist/bin/commands/callees.js +76 -0
- package/dist/bin/commands/callees.js.map +7 -0
- package/dist/bin/commands/callers.js +76 -0
- package/dist/bin/commands/callers.js.map +7 -0
- package/dist/bin/commands/changelog.js +55 -0
- package/dist/bin/commands/changelog.js.map +7 -0
- package/dist/bin/commands/circular-deps.js +68 -0
- package/dist/bin/commands/circular-deps.js.map +7 -0
- package/dist/bin/commands/commit-context.js +55 -0
- package/dist/bin/commands/commit-context.js.map +7 -0
- package/dist/bin/commands/complexity.js +60 -0
- package/dist/bin/commands/complexity.js.map +7 -0
- package/dist/bin/commands/cost.js +146 -0
- package/dist/bin/commands/cost.js.map +7 -0
- package/dist/bin/commands/dependency-depth.js +55 -0
- package/dist/bin/commands/dependency-depth.js.map +7 -0
- package/dist/bin/commands/diff-context.js +55 -0
- package/dist/bin/commands/diff-context.js.map +7 -0
- package/dist/bin/commands/distribution.js +56 -0
- package/dist/bin/commands/distribution.js.map +7 -0
- package/dist/bin/commands/doc-coverage.js +56 -0
- package/dist/bin/commands/doc-coverage.js.map +7 -0
- package/dist/bin/commands/doctor.js +202 -0
- package/dist/bin/commands/doctor.js.map +7 -0
- package/dist/bin/commands/dsm.js +56 -0
- package/dist/bin/commands/dsm.js.map +7 -0
- package/dist/bin/commands/gini.js +55 -0
- package/dist/bin/commands/gini.js.map +7 -0
- package/dist/bin/commands/god-class.js +56 -0
- package/dist/bin/commands/god-class.js.map +7 -0
- package/dist/bin/commands/health.js +55 -0
- package/dist/bin/commands/health.js.map +7 -0
- package/dist/bin/commands/help.js +224 -45
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/impact.js +76 -0
- package/dist/bin/commands/impact.js.map +7 -0
- package/dist/bin/commands/inheritance-depth.js +56 -0
- package/dist/bin/commands/inheritance-depth.js.map +7 -0
- package/dist/bin/commands/insert-at.js +55 -0
- package/dist/bin/commands/insert-at.js.map +7 -0
- package/dist/bin/commands/largest.js +56 -0
- package/dist/bin/commands/largest.js.map +7 -0
- package/dist/bin/commands/manifest.js +158 -0
- package/dist/bin/commands/manifest.js.map +7 -0
- package/dist/bin/commands/module-api.js +57 -0
- package/dist/bin/commands/module-api.js.map +7 -0
- package/dist/bin/commands/monitor.js +91 -0
- package/dist/bin/commands/monitor.js.map +7 -0
- package/dist/bin/commands/multi-replace.js +66 -0
- package/dist/bin/commands/multi-replace.js.map +7 -0
- package/dist/bin/commands/pr-context.js +55 -0
- package/dist/bin/commands/pr-context.js.map +7 -0
- package/dist/bin/commands/query.js +19 -1
- package/dist/bin/commands/query.js.map +3 -3
- package/dist/bin/commands/rank.js +56 -0
- package/dist/bin/commands/rank.js.map +7 -0
- package/dist/bin/commands/read.js +1 -1
- package/dist/bin/commands/read.js.map +2 -2
- package/dist/bin/commands/recursion.js +56 -0
- package/dist/bin/commands/recursion.js.map +7 -0
- package/dist/bin/commands/rename-preview.js +56 -0
- package/dist/bin/commands/rename-preview.js.map +7 -0
- package/dist/bin/commands/session.js +60 -0
- package/dist/bin/commands/session.js.map +7 -0
- package/dist/bin/commands/simplify-scan.js +56 -0
- package/dist/bin/commands/simplify-scan.js.map +7 -0
- package/dist/bin/commands/str-replace.js +55 -0
- package/dist/bin/commands/str-replace.js.map +7 -0
- package/dist/bin/commands/test-coverage.js +59 -0
- package/dist/bin/commands/test-coverage.js.map +7 -0
- package/dist/bin/commands/test-map.js +56 -0
- package/dist/bin/commands/test-map.js.map +7 -0
- package/dist/bin/commands/test-risk.js +59 -0
- package/dist/bin/commands/test-risk.js.map +7 -0
- package/dist/bin/commands/type-hierarchy.js +76 -0
- package/dist/bin/commands/type-hierarchy.js.map +7 -0
- package/dist/bin/commands/unused-imports.js +55 -0
- package/dist/bin/commands/unused-imports.js.map +7 -0
- package/dist/bin/commands/upgrade.js +78 -0
- package/dist/bin/commands/upgrade.js.map +7 -0
- package/dist/bin/installer/common.js +19 -9
- package/dist/bin/installer/common.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +147 -12
- package/dist/bin/installer/config-prompt.js.map +3 -3
- package/dist/bin/installer/index.js +62 -6
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/instructions.js +14 -25
- package/dist/bin/installer/instructions.js.map +2 -2
- package/dist/bin/installer/mcp.js.map +2 -2
- package/dist/bin/installer/steering.js +569 -100
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/aider.js +3 -3
- package/dist/bin/installer/targets/aider.js.map +2 -2
- package/dist/bin/installer/targets/amp.js +3 -3
- package/dist/bin/installer/targets/amp.js.map +2 -2
- package/dist/bin/installer/targets/antigravity.js +4 -4
- package/dist/bin/installer/targets/antigravity.js.map +2 -2
- package/dist/bin/installer/targets/augment.js +3 -3
- package/dist/bin/installer/targets/augment.js.map +2 -2
- package/dist/bin/installer/targets/claude.js +3 -3
- package/dist/bin/installer/targets/claude.js.map +2 -2
- package/dist/bin/installer/targets/cline.js +4 -4
- package/dist/bin/installer/targets/cline.js.map +2 -2
- package/dist/bin/installer/targets/codex.js +4 -4
- package/dist/bin/installer/targets/codex.js.map +2 -2
- package/dist/bin/installer/targets/continue.js +4 -4
- package/dist/bin/installer/targets/continue.js.map +2 -2
- package/dist/bin/installer/targets/copilot-cli.js +4 -4
- package/dist/bin/installer/targets/copilot-cli.js.map +2 -2
- package/dist/bin/installer/targets/copilot.js +4 -4
- package/dist/bin/installer/targets/copilot.js.map +2 -2
- package/dist/bin/installer/targets/cursor.js +5 -5
- package/dist/bin/installer/targets/cursor.js.map +2 -2
- package/dist/bin/installer/targets/devin.js +4 -4
- package/dist/bin/installer/targets/devin.js.map +2 -2
- package/dist/bin/installer/targets/gemini-cli.js +4 -4
- package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
- package/dist/bin/installer/targets/generic.js +2 -2
- package/dist/bin/installer/targets/generic.js.map +2 -2
- package/dist/bin/installer/targets/goose.js +3 -4
- package/dist/bin/installer/targets/goose.js.map +3 -3
- package/dist/bin/installer/targets/index.js.map +1 -1
- package/dist/bin/installer/targets/junie.js +4 -4
- package/dist/bin/installer/targets/junie.js.map +2 -2
- package/dist/bin/installer/targets/kilo.js +4 -4
- package/dist/bin/installer/targets/kilo.js.map +2 -2
- package/dist/bin/installer/targets/kiro.js +27 -3
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/installer/targets/opencode.js +3 -3
- package/dist/bin/installer/targets/opencode.js.map +2 -2
- package/dist/bin/installer/targets/openhands.js +3 -3
- package/dist/bin/installer/targets/openhands.js.map +2 -2
- package/dist/bin/installer/targets/qoder.js +2 -2
- package/dist/bin/installer/targets/qoder.js.map +2 -2
- package/dist/bin/installer/targets/qwen.js +2 -2
- package/dist/bin/installer/targets/qwen.js.map +2 -2
- package/dist/bin/installer/targets/replit.js +3 -3
- package/dist/bin/installer/targets/replit.js.map +2 -2
- package/dist/bin/installer/targets/roo.js +4 -4
- package/dist/bin/installer/targets/roo.js.map +2 -2
- package/dist/bin/installer/targets/tabnine.js +3 -3
- package/dist/bin/installer/targets/tabnine.js.map +2 -2
- package/dist/bin/installer/targets/trae.js +3 -3
- package/dist/bin/installer/targets/trae.js.map +2 -2
- package/dist/bin/installer/targets/warp.js +4 -4
- package/dist/bin/installer/targets/warp.js.map +2 -2
- package/dist/bin/installer/targets/windsurf.js +4 -4
- package/dist/bin/installer/targets/windsurf.js.map +2 -2
- package/dist/bin/kirograph.js +83 -1
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/config.js +30 -8
- package/dist/config.js.map +2 -2
- package/dist/core/branch-manager.js +111 -0
- package/dist/core/branch-manager.js.map +7 -0
- package/dist/core/pipeline.js +3 -3
- package/dist/core/pipeline.js.map +2 -2
- package/dist/data/pixelrag-bridge.js +93 -0
- package/dist/data/pixelrag-bridge.js.map +7 -0
- package/dist/data/pixelrag-manager.js +416 -0
- package/dist/data/pixelrag-manager.js.map +7 -0
- package/dist/db/database.js +34 -7
- package/dist/db/database.js.map +2 -2
- package/dist/db/schema.sql +7 -1
- package/dist/extraction/complexity.js +116 -0
- package/dist/extraction/complexity.js.map +7 -0
- package/dist/extraction/extractor.js +52 -1
- package/dist/extraction/extractor.js.map +2 -2
- package/dist/graph/git-context.js +229 -0
- package/dist/graph/git-context.js.map +7 -0
- package/dist/index.js +4 -2
- package/dist/index.js.map +2 -2
- package/dist/mcp/cache.js +1 -1
- package/dist/mcp/cache.js.map +2 -2
- package/dist/mcp/handler.js +446 -0
- package/dist/mcp/handler.js.map +7 -0
- package/dist/mcp/handlers/architecture.js +297 -0
- package/dist/mcp/handlers/architecture.js.map +7 -0
- package/dist/mcp/handlers/branch.js +175 -0
- package/dist/mcp/handlers/branch.js.map +7 -0
- package/dist/mcp/handlers/code-health.js +563 -0
- package/dist/mcp/handlers/code-health.js.map +7 -0
- package/dist/mcp/handlers/complexity.js +267 -0
- package/dist/mcp/handlers/complexity.js.map +7 -0
- package/dist/mcp/handlers/core.js +611 -0
- package/dist/mcp/handlers/core.js.map +7 -0
- package/dist/mcp/handlers/data.js +337 -0
- package/dist/mcp/handlers/data.js.map +7 -0
- package/dist/mcp/handlers/docs.js +159 -0
- package/dist/mcp/handlers/docs.js.map +7 -0
- package/dist/mcp/handlers/edit-primitives.js +200 -0
- package/dist/mcp/handlers/edit-primitives.js.map +7 -0
- package/dist/mcp/handlers/git-context.js +286 -0
- package/dist/mcp/handlers/git-context.js.map +7 -0
- package/dist/mcp/handlers/memory.js +363 -0
- package/dist/mcp/handlers/memory.js.map +7 -0
- package/dist/mcp/handlers/patterns.js +310 -0
- package/dist/mcp/handlers/patterns.js.map +7 -0
- package/dist/mcp/handlers/security.js +699 -0
- package/dist/mcp/handlers/security.js.map +7 -0
- package/dist/mcp/handlers/utils.js +97 -0
- package/dist/mcp/handlers/utils.js.map +7 -0
- package/dist/mcp/handlers/watchmen.js +104 -0
- package/dist/mcp/handlers/watchmen.js.map +7 -0
- package/dist/mcp/handlers/wiki.js +242 -0
- package/dist/mcp/handlers/wiki.js.map +7 -0
- package/dist/mcp/server.js +72 -3
- package/dist/mcp/server.js.map +2 -2
- package/dist/mcp/tool-names.js +136 -10
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +846 -3019
- package/dist/mcp/tools.js.map +3 -3
- package/dist/types.js.map +2 -2
- package/package.json +36 -10
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var pixelrag_manager_exports = {};
|
|
30
|
+
__export(pixelrag_manager_exports, {
|
|
31
|
+
buildIndex: () => buildIndex,
|
|
32
|
+
checkRam: () => checkRam,
|
|
33
|
+
detectPython: () => detectPython,
|
|
34
|
+
detectWSL2: () => detectWSL2,
|
|
35
|
+
downloadPixelRAGModel: () => downloadPixelRAGModel,
|
|
36
|
+
ensurePixelRAGInstalled: () => ensurePixelRAGInstalled,
|
|
37
|
+
ensurePython: () => ensurePython,
|
|
38
|
+
getFlaggedPdfs: () => getFlaggedPdfs,
|
|
39
|
+
installPixelRAG: () => installPixelRAG,
|
|
40
|
+
isPixelRAGInstalled: () => isPixelRAGInstalled,
|
|
41
|
+
startServer: () => startServer
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(pixelrag_manager_exports);
|
|
44
|
+
var fs = __toESM(require("fs"));
|
|
45
|
+
var os = __toESM(require("os"));
|
|
46
|
+
var path = __toESM(require("path"));
|
|
47
|
+
var import_child_process = require("child_process");
|
|
48
|
+
const PIXELRAG_ENV = {
|
|
49
|
+
...process.env,
|
|
50
|
+
OMP_NUM_THREADS: "1",
|
|
51
|
+
KMP_DUPLICATE_LIB_OK: "TRUE"
|
|
52
|
+
};
|
|
53
|
+
function detectPython() {
|
|
54
|
+
for (const bin of ["python3", "python"]) {
|
|
55
|
+
try {
|
|
56
|
+
const result = (0, import_child_process.spawnSync)(bin, ["--version"], { encoding: "utf8", timeout: 5e3 });
|
|
57
|
+
if (result.status !== 0) continue;
|
|
58
|
+
const out = (result.stdout + result.stderr).trim();
|
|
59
|
+
const match = out.match(/Python (\d+)\.(\d+)/);
|
|
60
|
+
if (!match) continue;
|
|
61
|
+
const [, major, minor] = match;
|
|
62
|
+
if (Number(major) >= 3 && Number(minor) >= 12) return bin;
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function ensurePython() {
|
|
69
|
+
const bin = detectPython();
|
|
70
|
+
if (!bin) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
"PixelRAG (visual PDF search) requires Python 3.12+.\nInstall from https://python.org or via brew: brew install python@3.12"
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return bin;
|
|
76
|
+
}
|
|
77
|
+
function getPixelRAGBin(python) {
|
|
78
|
+
try {
|
|
79
|
+
const exe = (0, import_child_process.spawnSync)(python, ["-c", "import sys; print(sys.executable)"], {
|
|
80
|
+
encoding: "utf8",
|
|
81
|
+
timeout: 5e3
|
|
82
|
+
}).stdout?.trim() ?? "";
|
|
83
|
+
if (exe) {
|
|
84
|
+
const candidate = path.join(path.dirname(exe), "pixelrag");
|
|
85
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const which = (0, import_child_process.spawnSync)("which", ["pixelrag"], { encoding: "utf8", timeout: 3e3 });
|
|
91
|
+
if (which.status === 0) {
|
|
92
|
+
const bin = which.stdout.trim();
|
|
93
|
+
if (bin) return bin;
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
for (const loc of ["/opt/homebrew/bin/pixelrag", "/usr/local/bin/pixelrag"]) {
|
|
98
|
+
if (fs.existsSync(loc)) return loc;
|
|
99
|
+
}
|
|
100
|
+
throw new Error(
|
|
101
|
+
"pixelrag binary not found.\nRun: pip install pixelrag[index,serve] pixelrag-render[pdf]\nAlso requires poppler: brew install poppler"
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
function detectWSL2() {
|
|
105
|
+
try {
|
|
106
|
+
const version = fs.readFileSync("/proc/version", "utf8");
|
|
107
|
+
return /microsoft|WSL/i.test(version);
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function freeRam() {
|
|
113
|
+
return os.freemem();
|
|
114
|
+
}
|
|
115
|
+
const WARN_RAM_BYTES = 4 * 1024 * 1024 * 1024;
|
|
116
|
+
const BLOCK_RAM_BYTES = 2 * 1024 * 1024 * 1024;
|
|
117
|
+
function checkRam() {
|
|
118
|
+
const free = freeRam();
|
|
119
|
+
if (free < BLOCK_RAM_BYTES) return "block";
|
|
120
|
+
if (free < WARN_RAM_BYTES) return "warn";
|
|
121
|
+
return "ok";
|
|
122
|
+
}
|
|
123
|
+
function isPixelRAGInstalled(python) {
|
|
124
|
+
try {
|
|
125
|
+
const bin = getPixelRAGBin(python);
|
|
126
|
+
const result = (0, import_child_process.spawnSync)(bin, ["--help"], { encoding: "utf8", timeout: 5e3 });
|
|
127
|
+
return result.status === 0;
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
function installPixelRAG(python) {
|
|
133
|
+
console.log(" Installing PixelRAG (pip install pixelrag[index,serve] pixelrag-render[pdf])\u2026");
|
|
134
|
+
let exePath = "";
|
|
135
|
+
try {
|
|
136
|
+
exePath = (0, import_child_process.spawnSync)(python, ["-c", "import sys; print(sys.executable)"], {
|
|
137
|
+
encoding: "utf8",
|
|
138
|
+
timeout: 5e3
|
|
139
|
+
}).stdout?.trim() ?? "";
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
const extraArgs = exePath.includes("/opt/homebrew/") || exePath.includes("/usr/local/") ? ["--break-system-packages"] : [];
|
|
143
|
+
const result = (0, import_child_process.spawnSync)(
|
|
144
|
+
python,
|
|
145
|
+
["-m", "pip", "install", "pixelrag[index,serve]", "pixelrag-render[pdf]", ...extraArgs],
|
|
146
|
+
{ stdio: "inherit", timeout: 3e5 }
|
|
147
|
+
);
|
|
148
|
+
if (result.status !== 0) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
"pip install pixelrag failed. Check your Python environment.\nAlso ensure poppler is installed: brew install poppler"
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function downloadPixelRAGModel(python) {
|
|
155
|
+
const pixelragBin = getPixelRAGBin(python);
|
|
156
|
+
console.log(" Pre-downloading Qwen3-VL-Embedding-2B model (~4 GB)\u2026");
|
|
157
|
+
const result = (0, import_child_process.spawnSync)(
|
|
158
|
+
pixelragBin,
|
|
159
|
+
["embed", "--help"],
|
|
160
|
+
// lightweight call that triggers model cache check
|
|
161
|
+
{ stdio: "inherit", timeout: 18e5, env: PIXELRAG_ENV }
|
|
162
|
+
);
|
|
163
|
+
if (result.status !== 0) {
|
|
164
|
+
console.warn(" \u26A0 Model pre-download may have failed \u2014 it will download on first use.");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function ensurePixelRAGInstalled(python) {
|
|
168
|
+
if (!isPixelRAGInstalled(python)) {
|
|
169
|
+
installPixelRAG(python);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function readManifest(manifestPath) {
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
175
|
+
} catch {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function writeManifest(manifestPath, entries) {
|
|
180
|
+
fs.writeFileSync(manifestPath, JSON.stringify(entries, null, 2));
|
|
181
|
+
}
|
|
182
|
+
function buildManifestEntries(absPaths) {
|
|
183
|
+
return absPaths.map((p) => {
|
|
184
|
+
try {
|
|
185
|
+
const st = fs.statSync(p);
|
|
186
|
+
return { path: p, mtime: st.mtimeMs, size: st.size };
|
|
187
|
+
} catch {
|
|
188
|
+
return { path: p, mtime: 0, size: 0 };
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function manifestIsStale(current, fresh) {
|
|
193
|
+
if (current.length !== fresh.length) return true;
|
|
194
|
+
const map = new Map(current.map((e) => [e.path, e]));
|
|
195
|
+
for (const f of fresh) {
|
|
196
|
+
const c = map.get(f.path);
|
|
197
|
+
if (!c || c.mtime !== f.mtime || c.size !== f.size) return true;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
function getFlaggedPdfs(rawDb, projectRoot) {
|
|
202
|
+
const datasets = rawDb.all(
|
|
203
|
+
`SELECT id, file_path FROM data_datasets WHERE format = 'pdf'`
|
|
204
|
+
);
|
|
205
|
+
const flagged = [];
|
|
206
|
+
for (const ds of datasets) {
|
|
207
|
+
const tableName = `data_rows_${ds.id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
208
|
+
try {
|
|
209
|
+
const rows = rawDb.all(
|
|
210
|
+
`SELECT COUNT(*) as cnt FROM "${tableName}" WHERE needs_ocr = 'true' OR has_columns = 'true'`
|
|
211
|
+
);
|
|
212
|
+
if (rows[0]?.cnt > 0) {
|
|
213
|
+
const absPath = path.isAbsolute(ds.file_path) ? ds.file_path : path.join(projectRoot, ds.file_path);
|
|
214
|
+
flagged.push(absPath);
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return flagged;
|
|
220
|
+
}
|
|
221
|
+
function buildIndex(opts) {
|
|
222
|
+
const { python, flaggedPdfs, kirographDir, force } = opts;
|
|
223
|
+
const indexDir = path.join(kirographDir, "pixelrag-index");
|
|
224
|
+
const stagingDir = path.join(kirographDir, "pixelrag-staging");
|
|
225
|
+
const manifestFile = path.join(kirographDir, "pixelrag-manifest.json");
|
|
226
|
+
const idMapFile = path.join(kirographDir, "pixelrag-id-map.json");
|
|
227
|
+
if (flaggedPdfs.length === 0) {
|
|
228
|
+
console.log(" PixelRAG: no visually complex PDFs found \u2014 skipping index build.");
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const freshEntries = buildManifestEntries(flaggedPdfs);
|
|
232
|
+
const currentEntries = readManifest(manifestFile);
|
|
233
|
+
const indexExists = fs.existsSync(indexDir);
|
|
234
|
+
if (!force && indexExists && !manifestIsStale(currentEntries, freshEntries)) {
|
|
235
|
+
console.log(` PixelRAG: index up to date (${flaggedPdfs.length} PDF${flaggedPdfs.length > 1 ? "s" : ""}).`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const pixelragBin = getPixelRAGBin(python);
|
|
239
|
+
if (fs.existsSync(stagingDir)) {
|
|
240
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
241
|
+
}
|
|
242
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
243
|
+
const idMap = {};
|
|
244
|
+
for (let i = 0; i < flaggedPdfs.length; i++) {
|
|
245
|
+
fs.symlinkSync(flaggedPdfs[i], path.join(stagingDir, `${i}.pdf`));
|
|
246
|
+
idMap[String(i)] = flaggedPdfs[i];
|
|
247
|
+
}
|
|
248
|
+
const tileEstimate = flaggedPdfs.length * 40;
|
|
249
|
+
const cpuMinutes = Math.round(tileEstimate * 10 / 60);
|
|
250
|
+
const mpsMinutes = Math.round(tileEstimate * 2 / 60);
|
|
251
|
+
console.log(
|
|
252
|
+
` Building PixelRAG index: ~${tileEstimate} tiles (~${cpuMinutes} min on CPU, ~${mpsMinutes} min on MPS)\u2026`
|
|
253
|
+
);
|
|
254
|
+
const args = [
|
|
255
|
+
"index",
|
|
256
|
+
"build",
|
|
257
|
+
"--source",
|
|
258
|
+
stagingDir,
|
|
259
|
+
"--source-type",
|
|
260
|
+
"pdf",
|
|
261
|
+
"--output",
|
|
262
|
+
indexDir,
|
|
263
|
+
"--device",
|
|
264
|
+
"cpu"
|
|
265
|
+
];
|
|
266
|
+
if (force) args.push("--force");
|
|
267
|
+
const result = (0, import_child_process.spawnSync)(pixelragBin, args, {
|
|
268
|
+
stdio: "inherit",
|
|
269
|
+
timeout: 72e5,
|
|
270
|
+
// 2h hard limit
|
|
271
|
+
env: PIXELRAG_ENV
|
|
272
|
+
});
|
|
273
|
+
try {
|
|
274
|
+
fs.rmSync(stagingDir, { recursive: true, force: true });
|
|
275
|
+
} catch {
|
|
276
|
+
}
|
|
277
|
+
if (result.status !== 0) {
|
|
278
|
+
throw new Error("PixelRAG index build failed. Check Python output above for details.");
|
|
279
|
+
}
|
|
280
|
+
fs.writeFileSync(idMapFile, JSON.stringify(idMap, null, 2));
|
|
281
|
+
writeManifest(manifestFile, freshEntries);
|
|
282
|
+
console.log(` \u2713 PixelRAG index built (${flaggedPdfs.length} PDF${flaggedPdfs.length > 1 ? "s" : ""}).`);
|
|
283
|
+
}
|
|
284
|
+
let _serverProcess = null;
|
|
285
|
+
let _cleanupRegistered = false;
|
|
286
|
+
function registerCleanup() {
|
|
287
|
+
if (_cleanupRegistered) return;
|
|
288
|
+
_cleanupRegistered = true;
|
|
289
|
+
const cleanup = () => {
|
|
290
|
+
if (_serverProcess) {
|
|
291
|
+
try {
|
|
292
|
+
_serverProcess.kill("SIGTERM");
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
setTimeout(() => {
|
|
296
|
+
try {
|
|
297
|
+
_serverProcess?.kill("SIGKILL");
|
|
298
|
+
} catch {
|
|
299
|
+
}
|
|
300
|
+
}, 5e3).unref();
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
process.on("exit", cleanup);
|
|
304
|
+
process.on("SIGINT", () => {
|
|
305
|
+
cleanup();
|
|
306
|
+
process.exit(0);
|
|
307
|
+
});
|
|
308
|
+
process.on("SIGTERM", () => {
|
|
309
|
+
cleanup();
|
|
310
|
+
process.exit(0);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
async function pollHealth(endpoint, maxAttempts, intervalMs) {
|
|
314
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
315
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
316
|
+
try {
|
|
317
|
+
const res = await fetch(`${endpoint}/health`, { signal: AbortSignal.timeout(2e3) });
|
|
318
|
+
if (res.ok) return true;
|
|
319
|
+
if (res.status !== 404 && res.status !== 503) {
|
|
320
|
+
console.warn(` \u26A0 Port ${endpoint}: unexpected response (${res.status}). Another process may be on this port.`);
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
} catch (e) {
|
|
324
|
+
const err = e;
|
|
325
|
+
if (err.code && err.code !== "ECONNREFUSED" && err.code !== "ECONNRESET" && err.code !== "UND_ERR_CONNECT_TIMEOUT") {
|
|
326
|
+
console.warn(` \u26A0 PixelRAG health check: ${String(e)}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if ((i + 1) % 8 === 0) {
|
|
330
|
+
console.log(` PixelRAG loading model\u2026 ${Math.round((i + 1) * intervalMs / 1e3)}s elapsed`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
async function startServer(python, port, kirographDir) {
|
|
336
|
+
const endpoint = `http://localhost:${port}`;
|
|
337
|
+
const indexDir = path.join(kirographDir, "pixelrag-index");
|
|
338
|
+
const ramState = checkRam();
|
|
339
|
+
if (ramState === "block") {
|
|
340
|
+
throw new Error(
|
|
341
|
+
`PixelRAG server requires at least 4 GB free RAM.
|
|
342
|
+
Current free: ${(freeRam() / 1024 / 1024 / 1024).toFixed(1)} GB.
|
|
343
|
+
Close other applications and retry, or disable enableVisualPDF.`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (ramState === "warn") {
|
|
347
|
+
console.warn(
|
|
348
|
+
` \u26A0 Low RAM: ${(freeRam() / 1024 / 1024 / 1024).toFixed(1)} GB free. PixelRAG may degrade system performance.`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
if (detectWSL2()) {
|
|
352
|
+
console.warn(
|
|
353
|
+
" \u26A0 WSL2 detected. Visual PDF search has known limitations:\n \u2022 Ensure your project is on the Linux filesystem (/home/...), not /mnt/c/...\n \u2022 Allocate at least 8 GB to WSL2 in %USERPROFILE%\\.wslconfig\n \u2022 CUDA requires updated NVIDIA drivers with WSL2 support"
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
const res = await fetch(`${endpoint}/health`, { signal: AbortSignal.timeout(2e3) });
|
|
358
|
+
if (res.ok) {
|
|
359
|
+
console.log(` PixelRAG server already running on port ${port}.`);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
} catch {
|
|
363
|
+
}
|
|
364
|
+
if (!fs.existsSync(indexDir)) {
|
|
365
|
+
console.warn(
|
|
366
|
+
` \u26A0 PixelRAG index not found at ${indexDir}.
|
|
367
|
+
Run: kirograph index \u2014 to build it first.`
|
|
368
|
+
);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const pixelragBin = getPixelRAGBin(python);
|
|
372
|
+
console.log(` Starting PixelRAG server on port ${port}\u2026`);
|
|
373
|
+
_serverProcess = (0, import_child_process.spawn)(
|
|
374
|
+
pixelragBin,
|
|
375
|
+
[
|
|
376
|
+
"serve",
|
|
377
|
+
"--index-dir",
|
|
378
|
+
indexDir,
|
|
379
|
+
"--tiles-dir",
|
|
380
|
+
path.join(indexDir, "tiles"),
|
|
381
|
+
"--articles-json",
|
|
382
|
+
path.join(indexDir, "articles.json"),
|
|
383
|
+
"--port",
|
|
384
|
+
String(port)
|
|
385
|
+
],
|
|
386
|
+
{ stdio: ["ignore", "ignore", "ignore"], detached: false, env: PIXELRAG_ENV }
|
|
387
|
+
);
|
|
388
|
+
_serverProcess.on("error", (err) => {
|
|
389
|
+
console.warn(` \u26A0 PixelRAG server error: ${err.message}`);
|
|
390
|
+
});
|
|
391
|
+
registerCleanup();
|
|
392
|
+
const ready = await pollHealth(endpoint, 90, 2e3);
|
|
393
|
+
if (ready) {
|
|
394
|
+
console.log(` \u2713 PixelRAG server ready on port ${port}.`);
|
|
395
|
+
} else {
|
|
396
|
+
console.warn(
|
|
397
|
+
` \u26A0 PixelRAG server did not respond within 3 minutes.
|
|
398
|
+
Visual PDF search calls will fail until it is ready.`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
403
|
+
0 && (module.exports = {
|
|
404
|
+
buildIndex,
|
|
405
|
+
checkRam,
|
|
406
|
+
detectPython,
|
|
407
|
+
detectWSL2,
|
|
408
|
+
downloadPixelRAGModel,
|
|
409
|
+
ensurePixelRAGInstalled,
|
|
410
|
+
ensurePython,
|
|
411
|
+
getFlaggedPdfs,
|
|
412
|
+
installPixelRAG,
|
|
413
|
+
isPixelRAGInstalled,
|
|
414
|
+
startServer
|
|
415
|
+
});
|
|
416
|
+
//# sourceMappingURL=pixelrag-manager.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/data/pixelrag-manager.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * PixelRAG lifecycle manager.\n *\n * Handles: Python check, pip install, index build (with manifest staleness),\n * server spawn + health poll, cleanup on exit.\n *\n * EXPERIMENTAL: This module is experimental and may change without notice.\n */\n\nimport * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport { spawnSync, spawn } from 'child_process';\nimport type { ChildProcess } from 'child_process';\nimport type { PixelRAGManifestEntry } from './types';\n\n// Environment variables required to prevent FAISS/PyTorch OpenMP conflict on macOS.\n// Without OMP_NUM_THREADS=1 the two runtimes clash and segfault during model loading.\nconst PIXELRAG_ENV = {\n ...process.env,\n OMP_NUM_THREADS: '1',\n KMP_DUPLICATE_LIB_OK: 'TRUE',\n};\n\n// \u2500\u2500 Python detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Returns the python3 binary name if available and \u2265 3.12, otherwise null. */\nexport function detectPython(): string | null {\n for (const bin of ['python3', 'python']) {\n try {\n const result = spawnSync(bin, ['--version'], { encoding: 'utf8', timeout: 5000 });\n if (result.status !== 0) continue;\n const out = (result.stdout + result.stderr).trim();\n const match = out.match(/Python (\\d+)\\.(\\d+)/);\n if (!match) continue;\n const [, major, minor] = match;\n if (Number(major) >= 3 && Number(minor) >= 12) return bin;\n } catch { /* not found */ }\n }\n return null;\n}\n\nexport function ensurePython(): string {\n const bin = detectPython();\n if (!bin) {\n throw new Error(\n 'PixelRAG (visual PDF search) requires Python 3.12+.\\n' +\n 'Install from https://python.org or via brew: brew install python@3.12',\n );\n }\n return bin;\n}\n\n// \u2500\u2500 pixelrag binary detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Finds the `pixelrag` CLI binary.\n * Searches: next to the Python executable, PATH, common brew paths.\n */\nfunction getPixelRAGBin(python: string): string {\n // Look next to the Python executable (covers pip-installed scripts)\n try {\n const exe = spawnSync(python, ['-c', 'import sys; print(sys.executable)'], {\n encoding: 'utf8', timeout: 5000,\n }).stdout?.trim() ?? '';\n if (exe) {\n const candidate = path.join(path.dirname(exe), 'pixelrag');\n if (fs.existsSync(candidate)) return candidate;\n }\n } catch { /* ignore */ }\n\n // Try pixelrag in PATH\n try {\n const which = spawnSync('which', ['pixelrag'], { encoding: 'utf8', timeout: 3000 });\n if (which.status === 0) {\n const bin = which.stdout.trim();\n if (bin) return bin;\n }\n } catch { /* ignore */ }\n\n // Common brew locations\n for (const loc of ['/opt/homebrew/bin/pixelrag', '/usr/local/bin/pixelrag']) {\n if (fs.existsSync(loc)) return loc;\n }\n\n throw new Error(\n 'pixelrag binary not found.\\n' +\n 'Run: pip install pixelrag[index,serve] pixelrag-render[pdf]\\n' +\n 'Also requires poppler: brew install poppler',\n );\n}\n\n// \u2500\u2500 WSL2 / platform checks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function detectWSL2(): boolean {\n try {\n const version = fs.readFileSync('/proc/version', 'utf8');\n return /microsoft|WSL/i.test(version);\n } catch {\n return false;\n }\n}\n\n/** Returns free RAM in bytes. */\nfunction freeRam(): number {\n return os.freemem();\n}\n\nconst WARN_RAM_BYTES = 4 * 1024 * 1024 * 1024; // 4 GB\nconst BLOCK_RAM_BYTES = 2 * 1024 * 1024 * 1024; // 2 GB\n\nexport function checkRam(): 'ok' | 'warn' | 'block' {\n const free = freeRam();\n if (free < BLOCK_RAM_BYTES) return 'block';\n if (free < WARN_RAM_BYTES) return 'warn';\n return 'ok';\n}\n\n// \u2500\u2500 PixelRAG installation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function isPixelRAGInstalled(python: string): boolean {\n // Check the binary first (most reliable)\n try {\n const bin = getPixelRAGBin(python);\n const result = spawnSync(bin, ['--help'], { encoding: 'utf8', timeout: 5000 });\n return result.status === 0;\n } catch { /* binary not found */ }\n return false;\n}\n\nexport function installPixelRAG(python: string): void {\n console.log(' Installing PixelRAG (pip install pixelrag[index,serve] pixelrag-render[pdf])\u2026');\n\n // Detect brew-managed Python \u2014 requires --break-system-packages\n let exePath = '';\n try {\n exePath = spawnSync(python, ['-c', 'import sys; print(sys.executable)'], {\n encoding: 'utf8', timeout: 5000,\n }).stdout?.trim() ?? '';\n } catch { /* ignore */ }\n const extraArgs = (exePath.includes('/opt/homebrew/') || exePath.includes('/usr/local/'))\n ? ['--break-system-packages']\n : [];\n\n const result = spawnSync(\n python,\n ['-m', 'pip', 'install', 'pixelrag[index,serve]', 'pixelrag-render[pdf]', ...extraArgs],\n { stdio: 'inherit', timeout: 300_000 },\n );\n if (result.status !== 0) {\n throw new Error(\n 'pip install pixelrag failed. Check your Python environment.\\n' +\n 'Also ensure poppler is installed: brew install poppler',\n );\n }\n}\n\nexport function downloadPixelRAGModel(python: string): void {\n // The model is downloaded automatically by pixelrag on first use;\n // this function pre-warms the cache at install time.\n const pixelragBin = getPixelRAGBin(python);\n console.log(' Pre-downloading Qwen3-VL-Embedding-2B model (~4 GB)\u2026');\n const result = spawnSync(\n pixelragBin,\n ['embed', '--help'], // lightweight call that triggers model cache check\n { stdio: 'inherit', timeout: 1800_000, env: PIXELRAG_ENV },\n );\n if (result.status !== 0) {\n console.warn(' \u26A0 Model pre-download may have failed \u2014 it will download on first use.');\n }\n}\n\nexport function ensurePixelRAGInstalled(python: string): void {\n if (!isPixelRAGInstalled(python)) {\n installPixelRAG(python);\n }\n}\n\n// \u2500\u2500 Manifest / staleness \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction readManifest(manifestPath: string): PixelRAGManifestEntry[] {\n try {\n return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n } catch {\n return [];\n }\n}\n\nfunction writeManifest(manifestPath: string, entries: PixelRAGManifestEntry[]): void {\n fs.writeFileSync(manifestPath, JSON.stringify(entries, null, 2));\n}\n\nfunction buildManifestEntries(absPaths: string[]): PixelRAGManifestEntry[] {\n return absPaths.map(p => {\n try {\n const st = fs.statSync(p);\n return { path: p, mtime: st.mtimeMs, size: st.size };\n } catch {\n return { path: p, mtime: 0, size: 0 };\n }\n });\n}\n\nfunction manifestIsStale(\n current: PixelRAGManifestEntry[],\n fresh: PixelRAGManifestEntry[],\n): boolean {\n if (current.length !== fresh.length) return true;\n const map = new Map(current.map(e => [e.path, e]));\n for (const f of fresh) {\n const c = map.get(f.path);\n if (!c || c.mtime !== f.mtime || c.size !== f.size) return true;\n }\n return false;\n}\n\n// \u2500\u2500 Get flagged PDFs from kirograph DB \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Queries the kirograph data DB for PDF datasets that have at least one page\n * with needs_ocr='true' or has_columns='true' (complex visual layout).\n * Returns absolute paths.\n */\nexport function getFlaggedPdfs(\n rawDb: { all: (sql: string, params?: unknown[]) => unknown[] },\n projectRoot: string,\n): string[] {\n const datasets = rawDb.all(\n `SELECT id, file_path FROM data_datasets WHERE format = 'pdf'`,\n ) as { id: string; file_path: string }[];\n\n const flagged: string[] = [];\n for (const ds of datasets) {\n const tableName = `data_rows_${ds.id.replace(/[^a-zA-Z0-9_]/g, '_')}`;\n try {\n const rows = rawDb.all(\n `SELECT COUNT(*) as cnt FROM \"${tableName}\" WHERE needs_ocr = 'true' OR has_columns = 'true'`,\n ) as { cnt: number }[];\n if (rows[0]?.cnt > 0) {\n const absPath = path.isAbsolute(ds.file_path)\n ? ds.file_path\n : path.join(projectRoot, ds.file_path);\n flagged.push(absPath);\n }\n } catch { /* table not yet created \u2014 skip */ }\n }\n return flagged;\n}\n\n// \u2500\u2500 Index build \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface BuildIndexOptions {\n python: string;\n flaggedPdfs: string[];\n projectRoot: string;\n kirographDir: string;\n force?: boolean;\n}\n\nexport function buildIndex(opts: BuildIndexOptions): void {\n const { python, flaggedPdfs, kirographDir, force } = opts;\n const indexDir = path.join(kirographDir, 'pixelrag-index');\n const stagingDir = path.join(kirographDir, 'pixelrag-staging');\n const manifestFile = path.join(kirographDir, 'pixelrag-manifest.json');\n const idMapFile = path.join(kirographDir, 'pixelrag-id-map.json');\n\n if (flaggedPdfs.length === 0) {\n console.log(' PixelRAG: no visually complex PDFs found \u2014 skipping index build.');\n return;\n }\n\n const freshEntries = buildManifestEntries(flaggedPdfs);\n const currentEntries = readManifest(manifestFile);\n const indexExists = fs.existsSync(indexDir);\n\n if (!force && indexExists && !manifestIsStale(currentEntries, freshEntries)) {\n console.log(` PixelRAG: index up to date (${flaggedPdfs.length} PDF${flaggedPdfs.length > 1 ? 's' : ''}).`);\n return;\n }\n\n const pixelragBin = getPixelRAGBin(python);\n\n // pixelrag PDFSource expects numerically-named files (0.pdf, 1.pdf, \u2026) so that\n // article IDs are integers. We stage symlinks and keep an id\u2192path map.\n if (fs.existsSync(stagingDir)) {\n fs.rmSync(stagingDir, { recursive: true, force: true });\n }\n fs.mkdirSync(stagingDir, { recursive: true });\n\n const idMap: Record<string, string> = {};\n for (let i = 0; i < flaggedPdfs.length; i++) {\n fs.symlinkSync(flaggedPdfs[i], path.join(stagingDir, `${i}.pdf`));\n idMap[String(i)] = flaggedPdfs[i];\n }\n\n const tileEstimate = flaggedPdfs.length * 40;\n const cpuMinutes = Math.round(tileEstimate * 10 / 60);\n const mpsMinutes = Math.round(tileEstimate * 2 / 60);\n console.log(\n ` Building PixelRAG index: ~${tileEstimate} tiles` +\n ` (~${cpuMinutes} min on CPU, ~${mpsMinutes} min on MPS)\u2026`,\n );\n\n const args = [\n 'index', 'build',\n '--source', stagingDir,\n '--source-type', 'pdf',\n '--output', indexDir,\n '--device', 'cpu',\n ];\n if (force) args.push('--force');\n\n const result = spawnSync(pixelragBin, args, {\n stdio: 'inherit',\n timeout: 7200_000, // 2h hard limit\n env: PIXELRAG_ENV,\n });\n\n // Clean up staging regardless of outcome\n try { fs.rmSync(stagingDir, { recursive: true, force: true }); } catch { /* best-effort */ }\n\n if (result.status !== 0) {\n throw new Error('PixelRAG index build failed. Check Python output above for details.');\n }\n\n // Persist id map and manifest\n fs.writeFileSync(idMapFile, JSON.stringify(idMap, null, 2));\n writeManifest(manifestFile, freshEntries);\n console.log(` \u2713 PixelRAG index built (${flaggedPdfs.length} PDF${flaggedPdfs.length > 1 ? 's' : ''}).`);\n}\n\n// \u2500\u2500 Server lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nlet _serverProcess: ChildProcess | null = null;\nlet _cleanupRegistered = false;\n\nfunction registerCleanup(): void {\n if (_cleanupRegistered) return;\n _cleanupRegistered = true;\n\n const cleanup = (): void => {\n if (_serverProcess) {\n try { _serverProcess.kill('SIGTERM'); } catch { /* best-effort */ }\n setTimeout(() => {\n try { _serverProcess?.kill('SIGKILL'); } catch { /* best-effort */ }\n }, 5000).unref();\n }\n };\n\n process.on('exit', cleanup);\n process.on('SIGINT', () => { cleanup(); process.exit(0); });\n process.on('SIGTERM', () => { cleanup(); process.exit(0); });\n}\n\nasync function pollHealth(endpoint: string, maxAttempts: number, intervalMs: number): Promise<boolean> {\n for (let i = 0; i < maxAttempts; i++) {\n await new Promise(r => setTimeout(r, intervalMs));\n try {\n const res = await fetch(`${endpoint}/health`, { signal: AbortSignal.timeout(2000) });\n if (res.ok) return true;\n if (res.status !== 404 && res.status !== 503) {\n console.warn(` \u26A0 Port ${endpoint}: unexpected response (${res.status}). Another process may be on this port.`);\n return false;\n }\n } catch (e: unknown) {\n const err = e as { code?: string };\n if (err.code && err.code !== 'ECONNREFUSED' && err.code !== 'ECONNRESET' && err.code !== 'UND_ERR_CONNECT_TIMEOUT') {\n console.warn(` \u26A0 PixelRAG health check: ${String(e)}`);\n }\n }\n if ((i + 1) % 8 === 0) {\n console.log(` PixelRAG loading model\u2026 ${Math.round((i + 1) * intervalMs / 1000)}s elapsed`);\n }\n }\n return false;\n}\n\nexport async function startServer(python: string, port: number, kirographDir: string): Promise<void> {\n const endpoint = `http://localhost:${port}`;\n const indexDir = path.join(kirographDir, 'pixelrag-index');\n\n const ramState = checkRam();\n if (ramState === 'block') {\n throw new Error(\n `PixelRAG server requires at least 4 GB free RAM.\\n` +\n `Current free: ${(freeRam() / 1024 / 1024 / 1024).toFixed(1)} GB.\\n` +\n `Close other applications and retry, or disable enableVisualPDF.`,\n );\n }\n if (ramState === 'warn') {\n console.warn(\n ` \u26A0 Low RAM: ${(freeRam() / 1024 / 1024 / 1024).toFixed(1)} GB free. ` +\n `PixelRAG may degrade system performance.`,\n );\n }\n\n if (detectWSL2()) {\n console.warn(\n ' \u26A0 WSL2 detected. Visual PDF search has known limitations:\\n' +\n ' \u2022 Ensure your project is on the Linux filesystem (/home/...), not /mnt/c/...\\n' +\n ' \u2022 Allocate at least 8 GB to WSL2 in %USERPROFILE%\\\\.wslconfig\\n' +\n ' \u2022 CUDA requires updated NVIDIA drivers with WSL2 support',\n );\n }\n\n // Check if already running\n try {\n const res = await fetch(`${endpoint}/health`, { signal: AbortSignal.timeout(2000) });\n if (res.ok) {\n console.log(` PixelRAG server already running on port ${port}.`);\n return;\n }\n } catch { /* not running */ }\n\n if (!fs.existsSync(indexDir)) {\n console.warn(\n ` \u26A0 PixelRAG index not found at ${indexDir}.\\n` +\n ` Run: kirograph index \u2014 to build it first.`,\n );\n return;\n }\n\n const pixelragBin = getPixelRAGBin(python);\n console.log(` Starting PixelRAG server on port ${port}\u2026`);\n\n _serverProcess = spawn(\n pixelragBin,\n [\n 'serve',\n '--index-dir', indexDir,\n '--tiles-dir', path.join(indexDir, 'tiles'),\n '--articles-json', path.join(indexDir, 'articles.json'),\n '--port', String(port),\n ],\n { stdio: ['ignore', 'ignore', 'ignore'], detached: false, env: PIXELRAG_ENV },\n );\n\n _serverProcess.on('error', err => {\n console.warn(` \u26A0 PixelRAG server error: ${err.message}`);\n });\n\n registerCleanup();\n\n // Poll /health \u2014 startup only, 90 attempts \u00D7 2s = 3 minutes max\n const ready = await pollHealth(endpoint, 90, 2000);\n if (ready) {\n console.log(` \u2713 PixelRAG server ready on port ${port}.`);\n } else {\n console.warn(\n ` \u26A0 PixelRAG server did not respond within 3 minutes.\\n` +\n ` Visual PDF search calls will fail until it is ready.`,\n );\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/db/database.js
CHANGED
|
@@ -34,7 +34,7 @@ module.exports = __toCommonJS(database_exports);
|
|
|
34
34
|
var path = __toESM(require("path"));
|
|
35
35
|
var fs = __toESM(require("fs"));
|
|
36
36
|
const { Database } = require("node-sqlite3-wasm");
|
|
37
|
-
const CURRENT_SCHEMA_VERSION =
|
|
37
|
+
const CURRENT_SCHEMA_VERSION = 2;
|
|
38
38
|
class GraphDatabase {
|
|
39
39
|
constructor(projectRoot) {
|
|
40
40
|
this.projectRoot = projectRoot;
|
|
@@ -179,10 +179,25 @@ Or delete the lock manually: ${lockPath}`
|
|
|
179
179
|
runMigrations() {
|
|
180
180
|
const versionRow = this.db.get("SELECT version FROM schema_versions ORDER BY version DESC LIMIT 1");
|
|
181
181
|
const currentVersion = versionRow ? versionRow.version : 0;
|
|
182
|
-
if (currentVersion <
|
|
182
|
+
if (currentVersion < 2) {
|
|
183
|
+
for (const col of [
|
|
184
|
+
"complexity_cyclomatic INTEGER",
|
|
185
|
+
"complexity_cognitive INTEGER",
|
|
186
|
+
"complexity_halstead TEXT",
|
|
187
|
+
"maintainability_index REAL",
|
|
188
|
+
"nesting_depth INTEGER"
|
|
189
|
+
]) {
|
|
190
|
+
try {
|
|
191
|
+
this.db.run(`ALTER TABLE nodes ADD COLUMN ${col}`);
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
this.db.run("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)", [2, Date.now()]);
|
|
196
|
+
}
|
|
197
|
+
if (currentVersion < 1) {
|
|
183
198
|
this.db.run(
|
|
184
199
|
"INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)",
|
|
185
|
-
[
|
|
200
|
+
[1, Date.now()]
|
|
186
201
|
);
|
|
187
202
|
}
|
|
188
203
|
try {
|
|
@@ -235,8 +250,10 @@ Or delete the lock manually: ${lockPath}`
|
|
|
235
250
|
start_line, end_line, start_column, end_column,
|
|
236
251
|
docstring, signature, visibility,
|
|
237
252
|
is_exported, is_async, is_static, is_abstract,
|
|
238
|
-
decorators, type_parameters, updated_at
|
|
239
|
-
|
|
253
|
+
decorators, type_parameters, updated_at,
|
|
254
|
+
complexity_cyclomatic, complexity_cognitive, complexity_halstead,
|
|
255
|
+
maintainability_index, nesting_depth)
|
|
256
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
240
257
|
[
|
|
241
258
|
node.id,
|
|
242
259
|
node.kind,
|
|
@@ -257,7 +274,12 @@ Or delete the lock manually: ${lockPath}`
|
|
|
257
274
|
node.isAbstract ? 1 : 0,
|
|
258
275
|
node.decorators ? JSON.stringify(node.decorators) : null,
|
|
259
276
|
node.typeParameters ? JSON.stringify(node.typeParameters) : null,
|
|
260
|
-
node.updatedAt
|
|
277
|
+
node.updatedAt,
|
|
278
|
+
node.complexityCyclomatic ?? null,
|
|
279
|
+
node.complexityCognitive ?? null,
|
|
280
|
+
node.complexityHalstead ?? null,
|
|
281
|
+
node.maintainabilityIndex ?? null,
|
|
282
|
+
node.nestingDepth ?? null
|
|
261
283
|
]
|
|
262
284
|
);
|
|
263
285
|
this.db.run(
|
|
@@ -362,7 +384,12 @@ Or delete the lock manually: ${lockPath}`
|
|
|
362
384
|
isAbstract: row.is_abstract === 1,
|
|
363
385
|
decorators: row.decorators ? JSON.parse(row.decorators) : void 0,
|
|
364
386
|
typeParameters: row.type_parameters ? JSON.parse(row.type_parameters) : void 0,
|
|
365
|
-
updatedAt: row.updated_at
|
|
387
|
+
updatedAt: row.updated_at,
|
|
388
|
+
complexityCyclomatic: row.complexity_cyclomatic ?? void 0,
|
|
389
|
+
complexityCognitive: row.complexity_cognitive ?? void 0,
|
|
390
|
+
complexityHalstead: row.complexity_halstead ?? void 0,
|
|
391
|
+
maintainabilityIndex: row.maintainability_index ?? void 0,
|
|
392
|
+
nestingDepth: row.nesting_depth ?? void 0
|
|
366
393
|
};
|
|
367
394
|
}
|
|
368
395
|
// ── Edges ──────────────────────────────────────────────────────────────────
|