mcmodding-mcp 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/LICENSE +21 -0
- package/README.md +324 -0
- package/dist/db-versioning.d.ts +31 -0
- package/dist/db-versioning.d.ts.map +1 -0
- package/dist/db-versioning.js +206 -0
- package/dist/db-versioning.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +202 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer/chunker.d.ts +32 -0
- package/dist/indexer/chunker.d.ts.map +1 -0
- package/dist/indexer/chunker.js +157 -0
- package/dist/indexer/chunker.js.map +1 -0
- package/dist/indexer/crawler.d.ts +28 -0
- package/dist/indexer/crawler.d.ts.map +1 -0
- package/dist/indexer/crawler.js +550 -0
- package/dist/indexer/crawler.js.map +1 -0
- package/dist/indexer/embeddings.d.ts +56 -0
- package/dist/indexer/embeddings.d.ts.map +1 -0
- package/dist/indexer/embeddings.js +200 -0
- package/dist/indexer/embeddings.js.map +1 -0
- package/dist/indexer/sitemap.d.ts +35 -0
- package/dist/indexer/sitemap.d.ts.map +1 -0
- package/dist/indexer/sitemap.js +263 -0
- package/dist/indexer/sitemap.js.map +1 -0
- package/dist/indexer/store.d.ts +237 -0
- package/dist/indexer/store.d.ts.map +1 -0
- package/dist/indexer/store.js +857 -0
- package/dist/indexer/store.js.map +1 -0
- package/dist/indexer/types.d.ts +77 -0
- package/dist/indexer/types.d.ts.map +1 -0
- package/dist/indexer/types.js +2 -0
- package/dist/indexer/types.js.map +1 -0
- package/dist/indexer/updater.d.ts +52 -0
- package/dist/indexer/updater.d.ts.map +1 -0
- package/dist/indexer/updater.js +263 -0
- package/dist/indexer/updater.js.map +1 -0
- package/dist/services/concept-service.d.ts +49 -0
- package/dist/services/concept-service.d.ts.map +1 -0
- package/dist/services/concept-service.js +554 -0
- package/dist/services/concept-service.js.map +1 -0
- package/dist/services/example-service.d.ts +52 -0
- package/dist/services/example-service.d.ts.map +1 -0
- package/dist/services/example-service.js +302 -0
- package/dist/services/example-service.js.map +1 -0
- package/dist/services/search-service.d.ts +54 -0
- package/dist/services/search-service.d.ts.map +1 -0
- package/dist/services/search-service.js +519 -0
- package/dist/services/search-service.js.map +1 -0
- package/dist/services/search-utils.d.ts +34 -0
- package/dist/services/search-utils.d.ts.map +1 -0
- package/dist/services/search-utils.js +239 -0
- package/dist/services/search-utils.js.map +1 -0
- package/dist/tools/explainConcept.d.ts +9 -0
- package/dist/tools/explainConcept.d.ts.map +1 -0
- package/dist/tools/explainConcept.js +93 -0
- package/dist/tools/explainConcept.js.map +1 -0
- package/dist/tools/getExample.d.ts +20 -0
- package/dist/tools/getExample.d.ts.map +1 -0
- package/dist/tools/getExample.js +88 -0
- package/dist/tools/getExample.js.map +1 -0
- package/dist/tools/getMinecraftVersion.d.ts +6 -0
- package/dist/tools/getMinecraftVersion.d.ts.map +1 -0
- package/dist/tools/getMinecraftVersion.js +57 -0
- package/dist/tools/getMinecraftVersion.js.map +1 -0
- package/dist/tools/searchDocs.d.ts +15 -0
- package/dist/tools/searchDocs.d.ts.map +1 -0
- package/dist/tools/searchDocs.js +144 -0
- package/dist/tools/searchDocs.js.map +1 -0
- package/package.json +111 -0
- package/scripts/postinstall.js +859 -0
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCModding-MCP Postinstall Script
|
|
5
|
+
* Downloads the documentation database during npm installation
|
|
6
|
+
* with a stunning visual CLI experience
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import crypto from 'crypto';
|
|
12
|
+
import https from 'https';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
|
|
18
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
19
|
+
// CONFIGURATION
|
|
20
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
21
|
+
|
|
22
|
+
const CONFIG = {
|
|
23
|
+
repoUrl: 'https://api.github.com/repos/OGMatrix/mcmodding-mcp/releases/latest',
|
|
24
|
+
dataDir: path.join(__dirname, '..', 'data'),
|
|
25
|
+
dbFileName: 'mcmodding-docs.db',
|
|
26
|
+
manifestFileName: 'manifest.json',
|
|
27
|
+
userAgent: 'mcmodding-mcp-installer',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
31
|
+
// ANSI COLORS & STYLES
|
|
32
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
33
|
+
|
|
34
|
+
const isColorSupported = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
35
|
+
|
|
36
|
+
const c = {
|
|
37
|
+
// Reset
|
|
38
|
+
reset: isColorSupported ? '\x1b[0m' : '',
|
|
39
|
+
// Styles
|
|
40
|
+
bold: isColorSupported ? '\x1b[1m' : '',
|
|
41
|
+
dim: isColorSupported ? '\x1b[2m' : '',
|
|
42
|
+
italic: isColorSupported ? '\x1b[3m' : '',
|
|
43
|
+
underline: isColorSupported ? '\x1b[4m' : '',
|
|
44
|
+
// Colors
|
|
45
|
+
black: isColorSupported ? '\x1b[30m' : '',
|
|
46
|
+
red: isColorSupported ? '\x1b[31m' : '',
|
|
47
|
+
green: isColorSupported ? '\x1b[32m' : '',
|
|
48
|
+
yellow: isColorSupported ? '\x1b[33m' : '',
|
|
49
|
+
blue: isColorSupported ? '\x1b[34m' : '',
|
|
50
|
+
magenta: isColorSupported ? '\x1b[35m' : '',
|
|
51
|
+
cyan: isColorSupported ? '\x1b[36m' : '',
|
|
52
|
+
white: isColorSupported ? '\x1b[37m' : '',
|
|
53
|
+
// Bright colors
|
|
54
|
+
brightBlack: isColorSupported ? '\x1b[90m' : '',
|
|
55
|
+
brightRed: isColorSupported ? '\x1b[91m' : '',
|
|
56
|
+
brightGreen: isColorSupported ? '\x1b[92m' : '',
|
|
57
|
+
brightYellow: isColorSupported ? '\x1b[93m' : '',
|
|
58
|
+
brightBlue: isColorSupported ? '\x1b[94m' : '',
|
|
59
|
+
brightMagenta: isColorSupported ? '\x1b[95m' : '',
|
|
60
|
+
brightCyan: isColorSupported ? '\x1b[96m' : '',
|
|
61
|
+
brightWhite: isColorSupported ? '\x1b[97m' : '',
|
|
62
|
+
// Backgrounds
|
|
63
|
+
bgBlack: isColorSupported ? '\x1b[40m' : '',
|
|
64
|
+
bgRed: isColorSupported ? '\x1b[41m' : '',
|
|
65
|
+
bgGreen: isColorSupported ? '\x1b[42m' : '',
|
|
66
|
+
bgYellow: isColorSupported ? '\x1b[43m' : '',
|
|
67
|
+
bgBlue: isColorSupported ? '\x1b[44m' : '',
|
|
68
|
+
bgMagenta: isColorSupported ? '\x1b[45m' : '',
|
|
69
|
+
bgCyan: isColorSupported ? '\x1b[46m' : '',
|
|
70
|
+
bgWhite: isColorSupported ? '\x1b[47m' : '',
|
|
71
|
+
// Cursor
|
|
72
|
+
clearLine: isColorSupported ? '\x1b[2K' : '',
|
|
73
|
+
cursorUp: isColorSupported ? '\x1b[1A' : '',
|
|
74
|
+
cursorHide: isColorSupported ? '\x1b[?25l' : '',
|
|
75
|
+
cursorShow: isColorSupported ? '\x1b[?25h' : '',
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
79
|
+
// UNICODE SYMBOLS & BOX DRAWING
|
|
80
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
81
|
+
|
|
82
|
+
const sym = {
|
|
83
|
+
// Box drawing (double line)
|
|
84
|
+
topLeft: '╔',
|
|
85
|
+
topRight: '╗',
|
|
86
|
+
bottomLeft: '╚',
|
|
87
|
+
bottomRight: '╝',
|
|
88
|
+
horizontal: '═',
|
|
89
|
+
vertical: '║',
|
|
90
|
+
// Box drawing (single line)
|
|
91
|
+
sTopLeft: '┌',
|
|
92
|
+
sTopRight: '┐',
|
|
93
|
+
sBottomLeft: '└',
|
|
94
|
+
sBottomRight: '┘',
|
|
95
|
+
sHorizontal: '─',
|
|
96
|
+
sVertical: '│',
|
|
97
|
+
// Progress bar
|
|
98
|
+
barFull: '█',
|
|
99
|
+
barThreeQuarter: '▓',
|
|
100
|
+
barHalf: '▒',
|
|
101
|
+
barQuarter: '░',
|
|
102
|
+
barEmpty: '░',
|
|
103
|
+
// Status symbols
|
|
104
|
+
check: '✔',
|
|
105
|
+
cross: '✖',
|
|
106
|
+
warning: '⚠',
|
|
107
|
+
info: 'ℹ',
|
|
108
|
+
star: '★',
|
|
109
|
+
sparkle: '✨',
|
|
110
|
+
rocket: '🚀',
|
|
111
|
+
package: '📦',
|
|
112
|
+
database: '🗄️',
|
|
113
|
+
download: '⬇',
|
|
114
|
+
shield: '🛡️',
|
|
115
|
+
clock: '⏱',
|
|
116
|
+
lightning: '⚡',
|
|
117
|
+
cube: '◆',
|
|
118
|
+
arrow: '→',
|
|
119
|
+
arrowRight: '▶',
|
|
120
|
+
dot: '●',
|
|
121
|
+
circle: '○',
|
|
122
|
+
diamond: '◇',
|
|
123
|
+
// Minecraft themed
|
|
124
|
+
pickaxe: '⛏',
|
|
125
|
+
gear: '⚙',
|
|
126
|
+
book: '📖',
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
130
|
+
// UTILITY FUNCTIONS
|
|
131
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
132
|
+
|
|
133
|
+
function getTerminalWidth() {
|
|
134
|
+
return process.stdout.columns || 80;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function centerText(text, width) {
|
|
138
|
+
const cleanText = text.replace(/\x1b\[[0-9;]*m/g, '');
|
|
139
|
+
const totalPadding = Math.max(0, width - cleanText.length);
|
|
140
|
+
const leftPadding = Math.floor(totalPadding / 2);
|
|
141
|
+
const rightPadding = totalPadding - leftPadding;
|
|
142
|
+
return ' '.repeat(leftPadding) + text + ' '.repeat(rightPadding);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function padRight(text, width) {
|
|
146
|
+
const cleanText = text.replace(/\x1b\[[0-9;]*m/g, '');
|
|
147
|
+
const padding = Math.max(0, width - cleanText.length);
|
|
148
|
+
return text + ' '.repeat(padding);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function formatBytes(bytes) {
|
|
152
|
+
if (bytes === 0) return '0 B';
|
|
153
|
+
const k = 1024;
|
|
154
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
155
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
156
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function formatSpeed(bytesPerSecond) {
|
|
160
|
+
return formatBytes(bytesPerSecond) + '/s';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function formatTime(seconds) {
|
|
164
|
+
if (seconds < 60) return `${Math.round(seconds)}s`;
|
|
165
|
+
const mins = Math.floor(seconds / 60);
|
|
166
|
+
const secs = Math.round(seconds % 60);
|
|
167
|
+
return `${mins}m ${secs}s`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function sleep(ms) {
|
|
171
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
175
|
+
// VISUAL COMPONENTS
|
|
176
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
177
|
+
|
|
178
|
+
function printBanner() {
|
|
179
|
+
const width = Math.min(getTerminalWidth(), 72);
|
|
180
|
+
const innerWidth = width - 2;
|
|
181
|
+
|
|
182
|
+
console.log();
|
|
183
|
+
console.log(
|
|
184
|
+
c.brightCyan + sym.topLeft + sym.horizontal.repeat(width - 2) + sym.topRight + c.reset
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// ASCII Art Logo
|
|
188
|
+
const logo = [
|
|
189
|
+
`${c.brightGreen} __ __ ___ __ __ _ _ _ ${c.reset}`,
|
|
190
|
+
`${c.brightGreen} | \\/ |/ __| | \\/ | ___ __| | __| (_)_ __ __ _ ${c.reset}`,
|
|
191
|
+
`${c.brightGreen} | |\\/| | | | |\\/| |/ _ \\ / _\` |/ _\` | | '_ \\ / _\` |${c.reset}`,
|
|
192
|
+
`${c.brightGreen} | | | | |___ | | | | (_) | (_| | (_| | | | | | (_| |${c.reset}`,
|
|
193
|
+
`${c.brightGreen} |_| |_|\\____||_| |_|\\___/ \\__,_|\\__,_|_|_| |_|\\__, |${c.reset}`,
|
|
194
|
+
`${c.brightGreen} |___/ ${c.reset}`,
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
logo.forEach((line) => {
|
|
198
|
+
console.log(
|
|
199
|
+
c.brightCyan +
|
|
200
|
+
sym.vertical +
|
|
201
|
+
c.reset +
|
|
202
|
+
centerText(line, innerWidth) +
|
|
203
|
+
c.brightCyan +
|
|
204
|
+
sym.vertical +
|
|
205
|
+
c.reset
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Subtitle
|
|
210
|
+
console.log(c.brightCyan + sym.vertical + ' '.repeat(innerWidth) + sym.vertical + c.reset);
|
|
211
|
+
const subtitle = `${c.brightMagenta}${sym.pickaxe} ${c.bold}Minecraft Modding Documentation${c.reset}${c.brightMagenta} ${sym.pickaxe}${c.reset}`;
|
|
212
|
+
console.log(
|
|
213
|
+
c.brightCyan +
|
|
214
|
+
sym.vertical +
|
|
215
|
+
c.reset +
|
|
216
|
+
centerText(subtitle, innerWidth) +
|
|
217
|
+
c.brightCyan +
|
|
218
|
+
sym.vertical +
|
|
219
|
+
c.reset
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const subtitle2 = `${c.dim}Model Context Protocol Server${c.reset}`;
|
|
223
|
+
console.log(
|
|
224
|
+
c.brightCyan +
|
|
225
|
+
sym.vertical +
|
|
226
|
+
c.reset +
|
|
227
|
+
centerText(subtitle2, innerWidth) +
|
|
228
|
+
c.brightCyan +
|
|
229
|
+
sym.vertical +
|
|
230
|
+
c.reset
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
console.log(
|
|
234
|
+
c.brightCyan + sym.bottomLeft + sym.horizontal.repeat(width - 2) + sym.bottomRight + c.reset
|
|
235
|
+
);
|
|
236
|
+
console.log();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function printSectionHeader(title, icon = sym.arrowRight) {
|
|
240
|
+
const width = Math.min(getTerminalWidth(), 72);
|
|
241
|
+
console.log();
|
|
242
|
+
console.log(
|
|
243
|
+
c.brightBlue +
|
|
244
|
+
sym.sTopLeft +
|
|
245
|
+
sym.sHorizontal.repeat(2) +
|
|
246
|
+
c.reset +
|
|
247
|
+
` ${c.bold}${icon} ${title}${c.reset} ` +
|
|
248
|
+
c.brightBlue +
|
|
249
|
+
sym.sHorizontal.repeat(Math.max(0, width - title.length - 10)) +
|
|
250
|
+
sym.sTopRight +
|
|
251
|
+
c.reset
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function printSectionFooter() {
|
|
256
|
+
const width = Math.min(getTerminalWidth(), 72);
|
|
257
|
+
console.log(
|
|
258
|
+
c.brightBlue + sym.sBottomLeft + sym.sHorizontal.repeat(width - 2) + sym.sBottomRight + c.reset
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function createProgressBar(progress, width = 40, showGradient = true) {
|
|
263
|
+
const filled = Math.round(progress * width);
|
|
264
|
+
const empty = width - filled;
|
|
265
|
+
|
|
266
|
+
let bar = '';
|
|
267
|
+
if (showGradient && isColorSupported) {
|
|
268
|
+
// Gradient effect from green to cyan
|
|
269
|
+
for (let i = 0; i < filled; i++) {
|
|
270
|
+
const ratio = i / width;
|
|
271
|
+
if (ratio < 0.33) bar += c.green + sym.barFull;
|
|
272
|
+
else if (ratio < 0.66) bar += c.brightGreen + sym.barFull;
|
|
273
|
+
else bar += c.brightCyan + sym.barFull;
|
|
274
|
+
}
|
|
275
|
+
bar += c.reset;
|
|
276
|
+
} else {
|
|
277
|
+
bar = c.brightGreen + sym.barFull.repeat(filled) + c.reset;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
bar += c.dim + sym.barEmpty.repeat(empty) + c.reset;
|
|
281
|
+
return bar;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
class ProgressDisplay {
|
|
285
|
+
constructor() {
|
|
286
|
+
this.lines = 0;
|
|
287
|
+
this.startTime = Date.now();
|
|
288
|
+
this.lastUpdate = 0;
|
|
289
|
+
this.speeds = [];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
clear() {
|
|
293
|
+
if (isColorSupported) {
|
|
294
|
+
for (let i = 0; i < this.lines; i++) {
|
|
295
|
+
process.stdout.write(c.cursorUp + c.clearLine);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
this.lines = 0;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
calculateSpeed(downloaded, elapsed) {
|
|
302
|
+
if (elapsed === 0) return 0;
|
|
303
|
+
const currentSpeed = downloaded / elapsed;
|
|
304
|
+
this.speeds.push(currentSpeed);
|
|
305
|
+
if (this.speeds.length > 5) this.speeds.shift();
|
|
306
|
+
return this.speeds.reduce((a, b) => a + b, 0) / this.speeds.length;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
update(downloaded, total, phase = 'download') {
|
|
310
|
+
const now = Date.now();
|
|
311
|
+
if (now - this.lastUpdate < 50) return; // Throttle updates
|
|
312
|
+
this.lastUpdate = now;
|
|
313
|
+
|
|
314
|
+
this.clear();
|
|
315
|
+
|
|
316
|
+
const width = Math.min(getTerminalWidth(), 72);
|
|
317
|
+
const barWidth = Math.max(20, width - 35);
|
|
318
|
+
const progress = total > 0 ? downloaded / total : 0;
|
|
319
|
+
const percent = Math.round(progress * 100);
|
|
320
|
+
const elapsed = (now - this.startTime) / 1000;
|
|
321
|
+
const speed = this.calculateSpeed(downloaded, elapsed);
|
|
322
|
+
const eta = speed > 0 ? (total - downloaded) / speed : 0;
|
|
323
|
+
|
|
324
|
+
const lines = [];
|
|
325
|
+
|
|
326
|
+
// Status line with icon
|
|
327
|
+
const statusIcon = phase === 'download' ? sym.download : sym.shield;
|
|
328
|
+
const statusText = phase === 'download' ? 'Downloading database...' : 'Verifying integrity...';
|
|
329
|
+
lines.push(` ${c.brightYellow}${statusIcon}${c.reset} ${c.bold}${statusText}${c.reset}`);
|
|
330
|
+
|
|
331
|
+
// Progress bar
|
|
332
|
+
const bar = createProgressBar(progress, barWidth);
|
|
333
|
+
const percentStr = `${percent}%`.padStart(4);
|
|
334
|
+
lines.push(
|
|
335
|
+
` ${c.dim}[${c.reset}${bar}${c.dim}]${c.reset} ${c.brightWhite}${percentStr}${c.reset}`
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// Stats line
|
|
339
|
+
const downloadedStr = formatBytes(downloaded);
|
|
340
|
+
const totalStr = formatBytes(total);
|
|
341
|
+
const speedStr = phase === 'download' ? formatSpeed(speed) : '';
|
|
342
|
+
const etaStr = phase === 'download' && eta > 0 ? `ETA: ${formatTime(eta)}` : '';
|
|
343
|
+
|
|
344
|
+
let statsLine = ` ${c.dim}${sym.cube}${c.reset} ${c.cyan}${downloadedStr}${c.reset} ${c.dim}/${c.reset} ${c.cyan}${totalStr}${c.reset}`;
|
|
345
|
+
if (speedStr)
|
|
346
|
+
statsLine += ` ${c.dim}${sym.lightning}${c.reset} ${c.brightMagenta}${speedStr}${c.reset}`;
|
|
347
|
+
if (etaStr) statsLine += ` ${c.dim}${sym.clock}${c.reset} ${c.yellow}${etaStr}${c.reset}`;
|
|
348
|
+
|
|
349
|
+
lines.push(statsLine);
|
|
350
|
+
|
|
351
|
+
// Print all lines
|
|
352
|
+
lines.forEach((line) => {
|
|
353
|
+
console.log(line);
|
|
354
|
+
this.lines++;
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
finish(success = true, message = '') {
|
|
359
|
+
this.clear();
|
|
360
|
+
const icon = success ? c.brightGreen + sym.check : c.brightRed + sym.cross;
|
|
361
|
+
const color = success ? c.brightGreen : c.brightRed;
|
|
362
|
+
console.log(` ${icon}${c.reset} ${color}${message}${c.reset}`);
|
|
363
|
+
this.lines = 1;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function printStepIndicator(step, total, description, status = 'pending') {
|
|
368
|
+
const icons = {
|
|
369
|
+
pending: c.dim + sym.circle + c.reset,
|
|
370
|
+
active: c.brightYellow + sym.dot + c.reset,
|
|
371
|
+
done: c.brightGreen + sym.check + c.reset,
|
|
372
|
+
error: c.brightRed + sym.cross + c.reset,
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const colors = {
|
|
376
|
+
pending: c.dim,
|
|
377
|
+
active: c.brightWhite,
|
|
378
|
+
done: c.green,
|
|
379
|
+
error: c.red,
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
console.log(
|
|
383
|
+
` ${icons[status]} ${colors[status]}Step ${step}/${total}: ${description}${c.reset}`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function printWelcomeScreen() {
|
|
388
|
+
const width = Math.min(getTerminalWidth(), 72);
|
|
389
|
+
const innerWidth = width - 4;
|
|
390
|
+
|
|
391
|
+
console.log();
|
|
392
|
+
console.log(
|
|
393
|
+
c.brightGreen + ' ' + sym.sparkle + ' Installation Complete! ' + sym.sparkle + c.reset
|
|
394
|
+
);
|
|
395
|
+
console.log();
|
|
396
|
+
|
|
397
|
+
// Welcome box
|
|
398
|
+
console.log(
|
|
399
|
+
c.green + ' ' + sym.topLeft + sym.horizontal.repeat(width - 4) + sym.topRight + c.reset
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
const welcomeLines = [
|
|
403
|
+
'',
|
|
404
|
+
`${c.bold}${c.brightWhite}Welcome to MCModding-MCP!${c.reset}`,
|
|
405
|
+
'',
|
|
406
|
+
`${c.dim}Your AI assistant now has access to comprehensive${c.reset}`,
|
|
407
|
+
`${c.dim}Minecraft modding documentation for:${c.reset}`,
|
|
408
|
+
'',
|
|
409
|
+
` ${c.brightGreen}${sym.check}${c.reset} ${c.cyan}Fabric${c.reset} - Lightweight modding toolchain`,
|
|
410
|
+
` ${c.brightGreen}${sym.check}${c.reset} ${c.magenta}NeoForge${c.reset} - Community-driven mod loader`,
|
|
411
|
+
'',
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
welcomeLines.forEach((line) => {
|
|
415
|
+
const paddedLine = centerText(line, innerWidth);
|
|
416
|
+
console.log(
|
|
417
|
+
c.green + ' ' + sym.vertical + c.reset + paddedLine + c.green + sym.vertical + c.reset
|
|
418
|
+
);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
console.log(
|
|
422
|
+
c.green + ' ' + sym.bottomLeft + sym.horizontal.repeat(width - 4) + sym.bottomRight + c.reset
|
|
423
|
+
);
|
|
424
|
+
console.log();
|
|
425
|
+
|
|
426
|
+
// Quick start section
|
|
427
|
+
console.log(
|
|
428
|
+
c.brightBlue +
|
|
429
|
+
' ' +
|
|
430
|
+
sym.sTopLeft +
|
|
431
|
+
sym.sHorizontal.repeat(2) +
|
|
432
|
+
c.reset +
|
|
433
|
+
` ${c.bold}${sym.rocket} Quick Start${c.reset} ` +
|
|
434
|
+
c.brightBlue +
|
|
435
|
+
sym.sHorizontal.repeat(width - 20) +
|
|
436
|
+
sym.sTopRight +
|
|
437
|
+
c.reset
|
|
438
|
+
);
|
|
439
|
+
console.log(c.brightBlue + ' ' + sym.sVertical + c.reset);
|
|
440
|
+
|
|
441
|
+
const quickStart = [
|
|
442
|
+
[`${c.yellow}Configure Claude Desktop:${c.reset}`, ''],
|
|
443
|
+
[
|
|
444
|
+
`${c.dim}Add to your ${c.reset}${c.cyan}claude_desktop_config.json${c.reset}${c.dim}:${c.reset}`,
|
|
445
|
+
'',
|
|
446
|
+
],
|
|
447
|
+
['', ''],
|
|
448
|
+
[` ${c.brightBlack}{${c.reset}`, ''],
|
|
449
|
+
[` ${c.brightBlue}"mcpServers"${c.reset}: {`, ''],
|
|
450
|
+
[` ${c.brightGreen}"mcmodding"${c.reset}: {`, ''],
|
|
451
|
+
[` ${c.brightMagenta}"command"${c.reset}: ${c.yellow}"npx"${c.reset},`, ''],
|
|
452
|
+
[` ${c.brightMagenta}"args"${c.reset}: [${c.yellow}"mcmodding-mcp"${c.reset}]`, ''],
|
|
453
|
+
[` }`, ''],
|
|
454
|
+
[` }`, ''],
|
|
455
|
+
[` ${c.brightBlack}}${c.reset}`, ''],
|
|
456
|
+
];
|
|
457
|
+
|
|
458
|
+
quickStart.forEach(([line]) => {
|
|
459
|
+
console.log(c.brightBlue + ' ' + sym.sVertical + c.reset + ' ' + line);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
console.log(c.brightBlue + ' ' + sym.sVertical + c.reset);
|
|
463
|
+
console.log(
|
|
464
|
+
c.brightBlue +
|
|
465
|
+
' ' +
|
|
466
|
+
sym.sBottomLeft +
|
|
467
|
+
sym.sHorizontal.repeat(width - 4) +
|
|
468
|
+
sym.sBottomRight +
|
|
469
|
+
c.reset
|
|
470
|
+
);
|
|
471
|
+
console.log();
|
|
472
|
+
|
|
473
|
+
// Available tools section
|
|
474
|
+
console.log(
|
|
475
|
+
c.brightMagenta +
|
|
476
|
+
' ' +
|
|
477
|
+
sym.sTopLeft +
|
|
478
|
+
sym.sHorizontal.repeat(2) +
|
|
479
|
+
c.reset +
|
|
480
|
+
` ${c.bold}${sym.gear} Available Tools${c.reset} ` +
|
|
481
|
+
c.brightMagenta +
|
|
482
|
+
sym.sHorizontal.repeat(width - 24) +
|
|
483
|
+
sym.sTopRight +
|
|
484
|
+
c.reset
|
|
485
|
+
);
|
|
486
|
+
console.log(c.brightMagenta + ' ' + sym.sVertical + c.reset);
|
|
487
|
+
|
|
488
|
+
const tools = [
|
|
489
|
+
[`${c.brightCyan}search_docs${c.reset}`, 'Search documentation with semantic understanding'],
|
|
490
|
+
[`${c.brightCyan}get_document${c.reset}`, 'Retrieve full documentation pages'],
|
|
491
|
+
[`${c.brightCyan}list_categories${c.reset}`, 'Browse available documentation categories'],
|
|
492
|
+
[`${c.brightCyan}get_code_examples${c.reset}`, 'Find relevant code snippets and examples'],
|
|
493
|
+
];
|
|
494
|
+
|
|
495
|
+
tools.forEach(([name, desc]) => {
|
|
496
|
+
console.log(c.brightMagenta + ' ' + sym.sVertical + c.reset + ` ${sym.arrowRight} ${name}`);
|
|
497
|
+
console.log(c.brightMagenta + ' ' + sym.sVertical + c.reset + ` ${c.dim}${desc}${c.reset}`);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
console.log(c.brightMagenta + ' ' + sym.sVertical + c.reset);
|
|
501
|
+
console.log(
|
|
502
|
+
c.brightMagenta +
|
|
503
|
+
' ' +
|
|
504
|
+
sym.sBottomLeft +
|
|
505
|
+
sym.sHorizontal.repeat(width - 4) +
|
|
506
|
+
sym.sBottomRight +
|
|
507
|
+
c.reset
|
|
508
|
+
);
|
|
509
|
+
console.log();
|
|
510
|
+
|
|
511
|
+
// Footer links
|
|
512
|
+
console.log(c.dim + ' ' + sym.sHorizontal.repeat(width - 4) + c.reset);
|
|
513
|
+
console.log();
|
|
514
|
+
console.log(
|
|
515
|
+
` ${c.dim}${sym.book}${c.reset} ${c.brightBlue}GitHub:${c.reset} ${c.underline}https://github.com/OGMatrix/mcmodding-mcp${c.reset}`
|
|
516
|
+
);
|
|
517
|
+
console.log(
|
|
518
|
+
` ${c.dim}${sym.warning}${c.reset} ${c.brightBlue}Issues:${c.reset} ${c.underline}https://github.com/OGMatrix/mcmodding-mcp/issues${c.reset}`
|
|
519
|
+
);
|
|
520
|
+
console.log();
|
|
521
|
+
console.log(c.dim + ' ' + sym.sHorizontal.repeat(width - 4) + c.reset);
|
|
522
|
+
console.log();
|
|
523
|
+
console.log(
|
|
524
|
+
` ${c.brightGreen}${sym.sparkle}${c.reset} ${c.italic}Happy modding!${c.reset} ${c.brightGreen}${sym.sparkle}${c.reset}`
|
|
525
|
+
);
|
|
526
|
+
console.log();
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
530
|
+
// NETWORK FUNCTIONS
|
|
531
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
532
|
+
|
|
533
|
+
function httpsGet(url, options = {}) {
|
|
534
|
+
return new Promise((resolve, reject) => {
|
|
535
|
+
const req = https.get(
|
|
536
|
+
url,
|
|
537
|
+
{
|
|
538
|
+
headers: {
|
|
539
|
+
'User-Agent': CONFIG.userAgent,
|
|
540
|
+
Accept: 'application/vnd.github.v3+json',
|
|
541
|
+
...options.headers,
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
(res) => {
|
|
545
|
+
// Handle redirects
|
|
546
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
547
|
+
httpsGet(res.headers.location, options).then(resolve).catch(reject);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (res.statusCode !== 200) {
|
|
552
|
+
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (options.stream) {
|
|
557
|
+
resolve(res);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
let data = '';
|
|
562
|
+
res.on('data', (chunk) => (data += chunk));
|
|
563
|
+
res.on('end', () => resolve(data));
|
|
564
|
+
res.on('error', reject);
|
|
565
|
+
}
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
req.on('error', reject);
|
|
569
|
+
req.setTimeout(30000, () => {
|
|
570
|
+
req.destroy();
|
|
571
|
+
reject(new Error('Request timeout'));
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
async function downloadWithProgress(url, destPath, onProgress) {
|
|
577
|
+
return new Promise((resolve, reject) => {
|
|
578
|
+
const file = fs.createWriteStream(destPath);
|
|
579
|
+
|
|
580
|
+
const makeRequest = (requestUrl) => {
|
|
581
|
+
const urlObj = new URL(requestUrl);
|
|
582
|
+
const options = {
|
|
583
|
+
hostname: urlObj.hostname,
|
|
584
|
+
path: urlObj.pathname + urlObj.search,
|
|
585
|
+
headers: {
|
|
586
|
+
'User-Agent': CONFIG.userAgent,
|
|
587
|
+
},
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
https
|
|
591
|
+
.get(options, (res) => {
|
|
592
|
+
// Handle redirects
|
|
593
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
594
|
+
makeRequest(res.headers.location);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (res.statusCode !== 200) {
|
|
599
|
+
file.close();
|
|
600
|
+
fs.unlinkSync(destPath);
|
|
601
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const total = parseInt(res.headers['content-length'], 10) || 0;
|
|
606
|
+
let downloaded = 0;
|
|
607
|
+
|
|
608
|
+
res.on('data', (chunk) => {
|
|
609
|
+
downloaded += chunk.length;
|
|
610
|
+
file.write(chunk);
|
|
611
|
+
if (onProgress) onProgress(downloaded, total);
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
res.on('end', () => {
|
|
615
|
+
file.end();
|
|
616
|
+
resolve({ downloaded, total });
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
res.on('error', (err) => {
|
|
620
|
+
file.close();
|
|
621
|
+
fs.unlinkSync(destPath);
|
|
622
|
+
reject(err);
|
|
623
|
+
});
|
|
624
|
+
})
|
|
625
|
+
.on('error', (err) => {
|
|
626
|
+
file.close();
|
|
627
|
+
if (fs.existsSync(destPath)) fs.unlinkSync(destPath);
|
|
628
|
+
reject(err);
|
|
629
|
+
});
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
makeRequest(url);
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
637
|
+
// MAIN INSTALLATION LOGIC
|
|
638
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
639
|
+
|
|
640
|
+
async function fetchReleaseInfo() {
|
|
641
|
+
const response = await httpsGet(CONFIG.repoUrl);
|
|
642
|
+
return JSON.parse(response);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
async function fetchManifest(manifestUrl) {
|
|
646
|
+
const response = await httpsGet(manifestUrl);
|
|
647
|
+
return JSON.parse(response);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
async function calculateFileHash(filePath) {
|
|
651
|
+
return new Promise((resolve, reject) => {
|
|
652
|
+
const hash = crypto.createHash('sha256');
|
|
653
|
+
const stream = fs.createReadStream(filePath);
|
|
654
|
+
stream.on('data', (data) => hash.update(data));
|
|
655
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
|
656
|
+
stream.on('error', reject);
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
async function verifyWithProgress(filePath, expectedHash, progress) {
|
|
661
|
+
return new Promise((resolve, reject) => {
|
|
662
|
+
const hash = crypto.createHash('sha256');
|
|
663
|
+
const stats = fs.statSync(filePath);
|
|
664
|
+
const total = stats.size;
|
|
665
|
+
let processed = 0;
|
|
666
|
+
|
|
667
|
+
const stream = fs.createReadStream(filePath);
|
|
668
|
+
|
|
669
|
+
stream.on('data', (chunk) => {
|
|
670
|
+
hash.update(chunk);
|
|
671
|
+
processed += chunk.length;
|
|
672
|
+
progress.update(processed, total, 'verify');
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
stream.on('end', () => {
|
|
676
|
+
const actualHash = hash.digest('hex');
|
|
677
|
+
resolve(actualHash === expectedHash);
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
stream.on('error', reject);
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
async function main() {
|
|
685
|
+
// Hide cursor during installation
|
|
686
|
+
if (isColorSupported) process.stdout.write(c.cursorHide);
|
|
687
|
+
|
|
688
|
+
// Ensure cursor is shown on exit
|
|
689
|
+
const cleanup = () => {
|
|
690
|
+
if (isColorSupported) process.stdout.write(c.cursorShow);
|
|
691
|
+
};
|
|
692
|
+
process.on('exit', cleanup);
|
|
693
|
+
process.on('SIGINT', () => {
|
|
694
|
+
cleanup();
|
|
695
|
+
process.exit(1);
|
|
696
|
+
});
|
|
697
|
+
process.on('SIGTERM', () => {
|
|
698
|
+
cleanup();
|
|
699
|
+
process.exit(1);
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
try {
|
|
703
|
+
printBanner();
|
|
704
|
+
|
|
705
|
+
printSectionHeader('Installation Progress', sym.package);
|
|
706
|
+
console.log();
|
|
707
|
+
|
|
708
|
+
// Step 1: Check for existing database
|
|
709
|
+
printStepIndicator(1, 4, 'Checking existing installation...', 'active');
|
|
710
|
+
await sleep(300);
|
|
711
|
+
|
|
712
|
+
const dbPath = path.join(CONFIG.dataDir, CONFIG.dbFileName);
|
|
713
|
+
const manifestPath = path.join(CONFIG.dataDir, CONFIG.manifestFileName);
|
|
714
|
+
|
|
715
|
+
if (fs.existsSync(dbPath) && fs.existsSync(manifestPath)) {
|
|
716
|
+
process.stdout.write(c.cursorUp + c.clearLine);
|
|
717
|
+
printStepIndicator(1, 4, 'Existing database found - skipping download', 'done');
|
|
718
|
+
console.log();
|
|
719
|
+
printSectionFooter();
|
|
720
|
+
printWelcomeScreen();
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
process.stdout.write(c.cursorUp + c.clearLine);
|
|
725
|
+
printStepIndicator(1, 4, 'No existing database - will download', 'done');
|
|
726
|
+
|
|
727
|
+
// Step 2: Fetch release information
|
|
728
|
+
printStepIndicator(2, 4, 'Fetching latest release information...', 'active');
|
|
729
|
+
|
|
730
|
+
let release, manifest;
|
|
731
|
+
try {
|
|
732
|
+
release = await fetchReleaseInfo();
|
|
733
|
+
const manifestAsset = release.assets.find((a) => a.name === 'db-manifest.json');
|
|
734
|
+
if (!manifestAsset) throw new Error('No manifest found in release');
|
|
735
|
+
|
|
736
|
+
manifest = await fetchManifest(manifestAsset.browser_download_url);
|
|
737
|
+
} catch (error) {
|
|
738
|
+
process.stdout.write(c.cursorUp + c.clearLine);
|
|
739
|
+
printStepIndicator(2, 4, `Failed to fetch release info: ${error.message}`, 'error');
|
|
740
|
+
console.log();
|
|
741
|
+
console.log(
|
|
742
|
+
c.yellow + ` ${sym.warning} The database will be downloaded on first use.${c.reset}`
|
|
743
|
+
);
|
|
744
|
+
printSectionFooter();
|
|
745
|
+
printWelcomeScreen();
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
process.stdout.write(c.cursorUp + c.clearLine);
|
|
750
|
+
printStepIndicator(
|
|
751
|
+
2,
|
|
752
|
+
4,
|
|
753
|
+
`Found database v${manifest.version} (${formatBytes(manifest.size)})`,
|
|
754
|
+
'done'
|
|
755
|
+
);
|
|
756
|
+
|
|
757
|
+
// Step 3: Download database
|
|
758
|
+
printStepIndicator(3, 4, 'Downloading database...', 'active');
|
|
759
|
+
console.log();
|
|
760
|
+
|
|
761
|
+
// Ensure data directory exists
|
|
762
|
+
if (!fs.existsSync(CONFIG.dataDir)) {
|
|
763
|
+
fs.mkdirSync(CONFIG.dataDir, { recursive: true });
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const tempPath = dbPath + '.tmp';
|
|
767
|
+
const progress = new ProgressDisplay();
|
|
768
|
+
|
|
769
|
+
try {
|
|
770
|
+
await downloadWithProgress(manifest.downloadUrl, tempPath, (downloaded, total) => {
|
|
771
|
+
progress.update(downloaded, total || manifest.size, 'download');
|
|
772
|
+
});
|
|
773
|
+
progress.finish(true, 'Download complete!');
|
|
774
|
+
} catch (error) {
|
|
775
|
+
progress.finish(false, `Download failed: ${error.message}`);
|
|
776
|
+
if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath);
|
|
777
|
+
console.log();
|
|
778
|
+
console.log(
|
|
779
|
+
c.yellow + ` ${sym.warning} The database will be downloaded on first use.${c.reset}`
|
|
780
|
+
);
|
|
781
|
+
printSectionFooter();
|
|
782
|
+
printWelcomeScreen();
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Clear the step indicator and update
|
|
787
|
+
process.stdout.write(c.cursorUp + c.cursorUp + c.clearLine);
|
|
788
|
+
printStepIndicator(3, 4, 'Database downloaded successfully', 'done');
|
|
789
|
+
process.stdout.write(c.cursorUp + c.clearLine);
|
|
790
|
+
process.stdout.write('\n'); // Move past the progress line
|
|
791
|
+
|
|
792
|
+
// Step 4: Verify integrity
|
|
793
|
+
printStepIndicator(4, 4, 'Verifying file integrity...', 'active');
|
|
794
|
+
console.log();
|
|
795
|
+
|
|
796
|
+
const verifyProgress = new ProgressDisplay();
|
|
797
|
+
|
|
798
|
+
try {
|
|
799
|
+
const isValid = await verifyWithProgress(tempPath, manifest.hash, verifyProgress);
|
|
800
|
+
|
|
801
|
+
if (!isValid) {
|
|
802
|
+
verifyProgress.finish(false, 'Hash verification failed!');
|
|
803
|
+
if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath);
|
|
804
|
+
console.log();
|
|
805
|
+
console.log(
|
|
806
|
+
c.yellow + ` ${sym.warning} The database will be downloaded on first use.${c.reset}`
|
|
807
|
+
);
|
|
808
|
+
printSectionFooter();
|
|
809
|
+
printWelcomeScreen();
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
verifyProgress.finish(true, 'Integrity verified!');
|
|
814
|
+
|
|
815
|
+
// Move temp file to final location
|
|
816
|
+
fs.renameSync(tempPath, dbPath);
|
|
817
|
+
|
|
818
|
+
// Save manifest
|
|
819
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
820
|
+
} catch (error) {
|
|
821
|
+
verifyProgress.finish(false, `Verification failed: ${error.message}`);
|
|
822
|
+
if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath);
|
|
823
|
+
console.log();
|
|
824
|
+
console.log(
|
|
825
|
+
c.yellow + ` ${sym.warning} The database will be downloaded on first use.${c.reset}`
|
|
826
|
+
);
|
|
827
|
+
printSectionFooter();
|
|
828
|
+
printWelcomeScreen();
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Clear and update final step
|
|
833
|
+
process.stdout.write(c.cursorUp + c.cursorUp + c.clearLine);
|
|
834
|
+
printStepIndicator(4, 4, 'Database verified and installed', 'done');
|
|
835
|
+
console.log();
|
|
836
|
+
|
|
837
|
+
printSectionFooter();
|
|
838
|
+
|
|
839
|
+
// Show welcome screen
|
|
840
|
+
printWelcomeScreen();
|
|
841
|
+
} catch (error) {
|
|
842
|
+
console.error();
|
|
843
|
+
console.error(c.brightRed + ` ${sym.cross} Installation error: ${error.message}${c.reset}`);
|
|
844
|
+
console.error();
|
|
845
|
+
console.log(
|
|
846
|
+
c.yellow + ` ${sym.warning} The database will be downloaded on first use.${c.reset}`
|
|
847
|
+
);
|
|
848
|
+
console.log();
|
|
849
|
+
printWelcomeScreen();
|
|
850
|
+
} finally {
|
|
851
|
+
cleanup();
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// Run the installer
|
|
856
|
+
main().catch((error) => {
|
|
857
|
+
console.error('Fatal error:', error);
|
|
858
|
+
process.exit(0); // Don't fail npm install
|
|
859
|
+
});
|