blun-king-cli 9.1.36 → 9.1.37
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/bin/launcher-runtime.js +12 -0
- package/bin/native-module-repair.js +201 -0
- package/package.json +1 -1
package/bin/launcher-runtime.js
CHANGED
|
@@ -23,6 +23,7 @@ const {
|
|
|
23
23
|
} = require('./standard-tools-bootstrap');
|
|
24
24
|
const { CORE_LOADED_MESSAGE } = require('./core-bootstrap');
|
|
25
25
|
const { prepareManagedNodeRuntime } = require('./node-runtime');
|
|
26
|
+
const { repairConfiguredNativeModules } = require('./native-module-repair');
|
|
26
27
|
const { acquireSharedRuntimeLease, tryAcquireUpdateLease } = require('./update-lease');
|
|
27
28
|
const { runExplicitUpdate, runUpdateNotice } = require('./update-notice');
|
|
28
29
|
const {
|
|
@@ -388,6 +389,17 @@ async function runLauncher(options = {}) {
|
|
|
388
389
|
installTelegramForProfile(PKG, blunDir);
|
|
389
390
|
}
|
|
390
391
|
|
|
392
|
+
const nativeModules = repairConfiguredNativeModules({
|
|
393
|
+
blunDir,
|
|
394
|
+
callerCwd,
|
|
395
|
+
onRepairStart() {
|
|
396
|
+
process.stdout.write('BLUN richtet native Node.js-Module fuer die neue Laufzeit ein.\n');
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
if (nativeModules.repaired.length > 0) {
|
|
400
|
+
process.stdout.write('Native Node.js-Module sind wieder einsatzbereit.\n');
|
|
401
|
+
}
|
|
402
|
+
|
|
391
403
|
// --- 6. Start -----------------------------------------------------------
|
|
392
404
|
const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
|
|
393
405
|
env.BLUN_HOME = blunDir;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('node:child_process');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
|
|
7
|
+
const SQLITE_PROBE = [
|
|
8
|
+
"const Database = require('better-sqlite3');",
|
|
9
|
+
"const db = new Database(':memory:');",
|
|
10
|
+
"const row = db.prepare('SELECT 1 AS ok').get();",
|
|
11
|
+
'db.close();',
|
|
12
|
+
"if (row?.ok !== 1) throw new Error('SQLITE_PROBE_FAILED');",
|
|
13
|
+
].join(' ');
|
|
14
|
+
|
|
15
|
+
function commandFailure(result) {
|
|
16
|
+
return String(result?.stderr || result?.stdout || result?.error?.message || '').trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function packageHasDependency(document, dependency) {
|
|
20
|
+
return ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']
|
|
21
|
+
.some((field) => Object.hasOwn(document?.[field] || {}, dependency));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function nearestPackageDirectory(startDir) {
|
|
25
|
+
let current = path.resolve(startDir);
|
|
26
|
+
while (true) {
|
|
27
|
+
if (fs.existsSync(path.join(current, 'package.json'))) return current;
|
|
28
|
+
const parent = path.dirname(current);
|
|
29
|
+
if (parent === current) return null;
|
|
30
|
+
current = parent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function projectRoot(startDir) {
|
|
35
|
+
let current = path.resolve(startDir);
|
|
36
|
+
while (true) {
|
|
37
|
+
if (fs.existsSync(path.join(current, '.git'))) return current;
|
|
38
|
+
const parent = path.dirname(current);
|
|
39
|
+
if (parent === current) return path.resolve(startDir);
|
|
40
|
+
current = parent;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readMcpDocument(configPath) {
|
|
45
|
+
if (!fs.existsSync(configPath)) return null;
|
|
46
|
+
try {
|
|
47
|
+
const document = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
48
|
+
return document && typeof document === 'object' ? document : null;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function serverProjectDirectory(server, configPath) {
|
|
55
|
+
if (!server || typeof server !== 'object' || server.enabled === false) return null;
|
|
56
|
+
if (typeof server.cwd === 'string' && server.cwd.trim()) {
|
|
57
|
+
const cwd = server.cwd.trim();
|
|
58
|
+
return nearestPackageDirectory(
|
|
59
|
+
path.isAbsolute(cwd) ? cwd : path.resolve(path.dirname(configPath), cwd),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const args = Array.isArray(server.args) ? server.args : [];
|
|
63
|
+
const script = args.find((arg) => typeof arg === 'string' && /\.[cm]?js$/iu.test(arg));
|
|
64
|
+
if (!script) return null;
|
|
65
|
+
const scriptPath = path.isAbsolute(script)
|
|
66
|
+
? script
|
|
67
|
+
: path.resolve(path.dirname(configPath), script);
|
|
68
|
+
return nearestPackageDirectory(path.dirname(scriptPath));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function configuredNativeProjectPaths({ blunDir, callerCwd }) {
|
|
72
|
+
const root = projectRoot(callerCwd);
|
|
73
|
+
const configPaths = [...new Set([
|
|
74
|
+
path.join(blunDir, 'mcp.json'),
|
|
75
|
+
path.join(root, '.mcp.json'),
|
|
76
|
+
path.join(callerCwd, '.blun', 'mcp.json'),
|
|
77
|
+
].map((entry) => path.resolve(entry)))];
|
|
78
|
+
const candidates = [nearestPackageDirectory(callerCwd)];
|
|
79
|
+
for (const configPath of configPaths) {
|
|
80
|
+
const document = readMcpDocument(configPath);
|
|
81
|
+
const servers = document?.mcpServers;
|
|
82
|
+
if (!servers || typeof servers !== 'object' || Array.isArray(servers)) continue;
|
|
83
|
+
for (const server of Object.values(servers)) {
|
|
84
|
+
candidates.push(serverProjectDirectory(server, configPath));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
const projects = [];
|
|
90
|
+
for (const candidate of candidates) {
|
|
91
|
+
if (!candidate) continue;
|
|
92
|
+
const absolute = path.resolve(candidate);
|
|
93
|
+
const key = process.platform === 'win32' ? absolute.toLowerCase() : absolute;
|
|
94
|
+
if (seen.has(key)) continue;
|
|
95
|
+
seen.add(key);
|
|
96
|
+
projects.push(absolute);
|
|
97
|
+
}
|
|
98
|
+
return projects;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function probeBetterSqlite({ nodeBinary, projectDir, spawnSyncImpl }) {
|
|
102
|
+
return spawnSyncImpl(nodeBinary, ['-e', SQLITE_PROBE], {
|
|
103
|
+
cwd: projectDir,
|
|
104
|
+
encoding: 'utf8',
|
|
105
|
+
timeout: 30_000,
|
|
106
|
+
windowsHide: true,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function npmInvocation(nodeBinary) {
|
|
111
|
+
const candidates = [
|
|
112
|
+
path.join(path.dirname(nodeBinary), 'node_modules', 'npm', 'bin', 'npm-cli.js'),
|
|
113
|
+
path.resolve(path.dirname(nodeBinary), '..', 'lib', 'node_modules', 'npm', 'bin', 'npm-cli.js'),
|
|
114
|
+
];
|
|
115
|
+
const npmCli = candidates.find((candidate) => fs.existsSync(candidate));
|
|
116
|
+
if (!npmCli) {
|
|
117
|
+
throw new Error('npm-cli.js wurde fuer die aktive Node.js-Laufzeit nicht gefunden.');
|
|
118
|
+
}
|
|
119
|
+
return { args: [npmCli], command: nodeBinary };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function repairNativeProject({
|
|
123
|
+
nodeBinary = process.execPath,
|
|
124
|
+
npmCommand,
|
|
125
|
+
onRepairStart,
|
|
126
|
+
projectDir,
|
|
127
|
+
spawnSyncImpl = spawnSync,
|
|
128
|
+
}) {
|
|
129
|
+
if (typeof projectDir !== 'string' || !path.isAbsolute(projectDir)) {
|
|
130
|
+
throw new Error('Der Projektpfad fuer die native Ladeprobe muss absolut sein.');
|
|
131
|
+
}
|
|
132
|
+
const absoluteProjectDir = path.resolve(projectDir);
|
|
133
|
+
const packagePath = path.join(absoluteProjectDir, 'package.json');
|
|
134
|
+
let packageDocument;
|
|
135
|
+
try {
|
|
136
|
+
packageDocument = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
137
|
+
} catch {
|
|
138
|
+
return { kind: 'skipped', reason: 'package_unreadable' };
|
|
139
|
+
}
|
|
140
|
+
if (!packageHasDependency(packageDocument, 'better-sqlite3')) {
|
|
141
|
+
return { kind: 'skipped', reason: 'dependency_missing' };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const before = probeBetterSqlite({ nodeBinary, projectDir: absoluteProjectDir, spawnSyncImpl });
|
|
145
|
+
if (before.status === 0) return { kind: 'healthy' };
|
|
146
|
+
|
|
147
|
+
const npm = npmCommand === undefined
|
|
148
|
+
? npmInvocation(nodeBinary)
|
|
149
|
+
: { args: [], command: npmCommand };
|
|
150
|
+
onRepairStart?.(absoluteProjectDir);
|
|
151
|
+
const rebuilt = spawnSyncImpl(npm.command, [...npm.args, 'rebuild', 'better-sqlite3'], {
|
|
152
|
+
cwd: absoluteProjectDir,
|
|
153
|
+
encoding: 'utf8',
|
|
154
|
+
timeout: 300_000,
|
|
155
|
+
windowsHide: true,
|
|
156
|
+
});
|
|
157
|
+
if (rebuilt.status !== 0) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Native Node.js-Module konnten nicht repariert werden: ${commandFailure(rebuilt) || 'ohne Fehlertext'}`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const after = probeBetterSqlite({ nodeBinary, projectDir: absoluteProjectDir, spawnSyncImpl });
|
|
164
|
+
if (after.status !== 0) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Ladeprobe nach dem Rebuild fehlgeschlagen: ${commandFailure(after) || 'ohne Fehlertext'}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
return { kind: 'repaired' };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function repairConfiguredNativeModules({
|
|
173
|
+
blunDir,
|
|
174
|
+
callerCwd,
|
|
175
|
+
nodeBinary = process.execPath,
|
|
176
|
+
onRepairStart,
|
|
177
|
+
projectPaths,
|
|
178
|
+
repairProject = repairNativeProject,
|
|
179
|
+
spawnSyncImpl = spawnSync,
|
|
180
|
+
}) {
|
|
181
|
+
const projects = projectPaths || configuredNativeProjectPaths({ blunDir, callerCwd });
|
|
182
|
+
const result = { checked: projects.length, healthy: [], repaired: [], skipped: [] };
|
|
183
|
+
for (const projectDir of projects) {
|
|
184
|
+
const firstResult = repairProject({
|
|
185
|
+
nodeBinary,
|
|
186
|
+
projectDir,
|
|
187
|
+
spawnSyncImpl,
|
|
188
|
+
onRepairStart,
|
|
189
|
+
});
|
|
190
|
+
if (firstResult.kind === 'healthy') result.healthy.push(projectDir);
|
|
191
|
+
else if (firstResult.kind === 'repaired') result.repaired.push(projectDir);
|
|
192
|
+
else result.skipped.push(projectDir);
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
configuredNativeProjectPaths,
|
|
199
|
+
repairConfiguredNativeModules,
|
|
200
|
+
repairNativeProject,
|
|
201
|
+
};
|