blun-king-cli 9.1.626 → 9.1.628-pruefpaket.modules.r7
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/core-bootstrap.js +93 -92
- package/bin/launcher-mode.js +126 -93
- package/bin/launcher-runtime.js +71 -7
- package/bin/native-command-runner.js +72 -0
- package/bin/native-module-repair.js +26 -20
- package/blun.mjs +371 -527
- package/package.json +1 -1
- package/worker-host.mjs +2 -2
package/bin/core-bootstrap.js
CHANGED
|
@@ -1,92 +1,93 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs');
|
|
4
|
-
const path = require('node:path');
|
|
5
|
-
const { pathToFileURL } = require('node:url');
|
|
6
|
-
|
|
7
|
-
const { acquireSharedRuntimeLease } = require('./update-lease');
|
|
8
|
-
const { installWindowsNodeCrashDump } = require('./windows-node-crash-dump.cjs');
|
|
9
|
-
const { recordRuntimeExit } = require('./runtime-exit-ledger.cjs');
|
|
10
|
-
const { isStandaloneHelpRequest } = require('./launcher-mode');
|
|
11
|
-
|
|
12
|
-
const CORE_LOADED_MESSAGE = 'blun-core-bootstrap-loaded';
|
|
13
|
-
|
|
14
|
-
function bindInteractiveTerminalOwner() {
|
|
15
|
-
if (process.platform !== 'win32' || process.env.BLUN_TERMINAL_OWNER !== '1' || !process.channel) return;
|
|
16
|
-
const stop = () => {
|
|
17
|
-
setTimeout(() => process.exit(143), 5000).unref();
|
|
18
|
-
if (!process.emit('SIGTERM')) process.exit(143);
|
|
19
|
-
};
|
|
20
|
-
process.once('disconnect', stop);
|
|
21
|
-
if (!process.connected) stop();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function runCoreBootstrap() {
|
|
25
|
-
bindInteractiveTerminalOwner();
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
leaseResult.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
process.
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { pathToFileURL } = require('node:url');
|
|
6
|
+
|
|
7
|
+
const { acquireSharedRuntimeLease } = require('./update-lease');
|
|
8
|
+
const { installWindowsNodeCrashDump } = require('./windows-node-crash-dump.cjs');
|
|
9
|
+
const { recordRuntimeExit } = require('./runtime-exit-ledger.cjs');
|
|
10
|
+
const { isStandaloneHelpRequest, isReadOnlyInspectionRequest } = require('./launcher-mode');
|
|
11
|
+
|
|
12
|
+
const CORE_LOADED_MESSAGE = 'blun-core-bootstrap-loaded';
|
|
13
|
+
|
|
14
|
+
function bindInteractiveTerminalOwner() {
|
|
15
|
+
if (process.platform !== 'win32' || process.env.BLUN_TERMINAL_OWNER !== '1' || !process.channel) return;
|
|
16
|
+
const stop = () => {
|
|
17
|
+
setTimeout(() => process.exit(143), 5000).unref();
|
|
18
|
+
if (!process.emit('SIGTERM')) process.exit(143);
|
|
19
|
+
};
|
|
20
|
+
process.once('disconnect', stop);
|
|
21
|
+
if (!process.connected) stop();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function runCoreBootstrap() {
|
|
25
|
+
bindInteractiveTerminalOwner();
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const informational = isStandaloneHelpRequest(args) || isReadOnlyInspectionRequest(args);
|
|
28
|
+
let diagnostics;
|
|
29
|
+
if (!informational) {
|
|
30
|
+
try { diagnostics = installWindowsNodeCrashDump({ homeDir: process.env.BLUN_HOME }); } catch {}
|
|
31
|
+
}
|
|
32
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
33
|
+
const mainPath = path.join(packageRoot, 'blun.mjs');
|
|
34
|
+
let leaseResult;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
leaseResult = await acquireSharedRuntimeLease({ packageRoot });
|
|
38
|
+
if (!leaseResult.acquired) throw new Error('RUNTIME_PROTECTION_UNAVAILABLE');
|
|
39
|
+
leaseResult.lease.assertOwned();
|
|
40
|
+
process.argv = [process.execPath, mainPath, ...process.argv.slice(2)];
|
|
41
|
+
await import(pathToFileURL(mainPath).href);
|
|
42
|
+
leaseResult.lease.assertOwned();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (!informational) {
|
|
45
|
+
try {
|
|
46
|
+
if (diagnostics?.enabled) diagnostics.write(error, 'bootstrap-failure');
|
|
47
|
+
else recordRuntimeExit({ homeDir: process.env.BLUN_HOME, source: 'bootstrap',
|
|
48
|
+
kind: 'bootstrap-failure', phase: 'startup', error });
|
|
49
|
+
} catch {}
|
|
50
|
+
}
|
|
51
|
+
try { diagnostics?.dispose(); } catch {}
|
|
52
|
+
try { await leaseResult?.lease?.release(); }
|
|
53
|
+
catch (leaseError) {
|
|
54
|
+
if (!informational) {
|
|
55
|
+
try {
|
|
56
|
+
recordRuntimeExit({ homeDir: process.env.BLUN_HOME, source: 'bootstrap',
|
|
57
|
+
kind: 'lease-release-failure', phase: 'cleanup', error: leaseError });
|
|
58
|
+
} catch {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The bundled CLI loads package assets lazily after module evaluation. Keep
|
|
65
|
+
// the install lease for this process's lifetime, without keeping print/help
|
|
66
|
+
// commands alive solely because of the owner server.
|
|
67
|
+
leaseResult.lease.unref();
|
|
68
|
+
|
|
69
|
+
if (process.connected) {
|
|
70
|
+
try {
|
|
71
|
+
process.send({ type: CORE_LOADED_MESSAGE }, () => {
|
|
72
|
+
if (process.env.BLUN_TERMINAL_OWNER === '1') {
|
|
73
|
+
process.channel?.unref();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
process.disconnect();
|
|
78
|
+
} catch {}
|
|
79
|
+
});
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (require.main === module) {
|
|
85
|
+
// oxlint-disable-next-line unicorn/prefer-top-level-await -- Worker entry is CommonJS.
|
|
86
|
+
void runCoreBootstrap().catch((error) => {
|
|
87
|
+
const message = error && error.message ? error.message : String(error);
|
|
88
|
+
fs.writeSync(process.stderr.fd, `BLUN Start fehlgeschlagen: ${message}\n`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { CORE_LOADED_MESSAGE, runCoreBootstrap };
|
package/bin/launcher-mode.js
CHANGED
|
@@ -1,93 +1,126 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs');
|
|
4
|
-
const path = require('node:path');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
'',
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { parseArgs } = require('node:util');
|
|
6
|
+
|
|
7
|
+
const LAUNCHER_MODES = Object.freeze({
|
|
8
|
+
BLUN: 'blun',
|
|
9
|
+
KING: 'king',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const SETUP_INSPECTION_OPTIONS = new Set([
|
|
13
|
+
'--json', '--reference', '--proposal', '--validate', '--document', '--query', '--target',
|
|
14
|
+
]);
|
|
15
|
+
const CONFIG_INSPECTION_COMMANDS = new Set(['validate', 'inspect', 'preview']);
|
|
16
|
+
|
|
17
|
+
const LAUNCHER_HELP_TEXT = [
|
|
18
|
+
'',
|
|
19
|
+
'Launchers:',
|
|
20
|
+
' blun Starts the CLI without automatically attaching Telegram.',
|
|
21
|
+
' king Starts the same CLI and automatically attaches the configured Telegram channel.',
|
|
22
|
+
' Both use the same version, account, model, and commands.',
|
|
23
|
+
' blun tools list shows optional tools; enable writes only explicit choices.',
|
|
24
|
+
'',
|
|
25
|
+
].join('\n');
|
|
26
|
+
|
|
27
|
+
function launcherModeFromArgv(argv) {
|
|
28
|
+
const entry = argv[1] || '';
|
|
29
|
+
const filename = entry.replaceAll('\\', '/').split('/').pop() || '';
|
|
30
|
+
const command = filename.replace(/\.(?:c|m)?js$/i, '').toLowerCase();
|
|
31
|
+
return command === LAUNCHER_MODES.KING ? LAUNCHER_MODES.KING : LAUNCHER_MODES.BLUN;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function runtimePolicyEnvironment(blunDir) {
|
|
35
|
+
if (blunDir === undefined) return {};
|
|
36
|
+
let policy;
|
|
37
|
+
try {
|
|
38
|
+
if (!path.isAbsolute(blunDir)) throw new Error('Invalid home');
|
|
39
|
+
const file = path.join(blunDir, 'runtime-policy.json');
|
|
40
|
+
const stat = fs.lstatSync(file);
|
|
41
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 4096) throw new Error('Invalid file');
|
|
42
|
+
policy = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error.code === 'ENOENT') return {};
|
|
45
|
+
throw new Error('Invalid BLUN runtime policy');
|
|
46
|
+
}
|
|
47
|
+
const review = policy?.responseReview;
|
|
48
|
+
if (policy?.schema !== 'blun.runtime-policy/v1'
|
|
49
|
+
|| Object.keys(policy).sort().join(',') !== 'responseReview,schema'
|
|
50
|
+
|| !review || Object.keys(review).sort().join(',') !== 'guardVersion,language,required'
|
|
51
|
+
|| review.required !== true || typeof review.language !== 'string'
|
|
52
|
+
|| !/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(review.language)
|
|
53
|
+
|| /^(auto|all)$/i.test(review.language)
|
|
54
|
+
|| typeof review.guardVersion !== 'string' || !/^\d+\.\d+\.\d+$/.test(review.guardVersion)) {
|
|
55
|
+
throw new Error('Invalid BLUN runtime policy');
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
BLUN_LANGUAGE_GUARD_MANDATORY: '1',
|
|
59
|
+
BLUN_LANGUAGE_GUARD_LANGUAGE: review.language,
|
|
60
|
+
BLUN_LANGUAGE_GUARD_VERSION: review.guardVersion,
|
|
61
|
+
BLUN_TELEGRAM_LANGUAGE_GATE: 'required',
|
|
62
|
+
BLUN_TELEGRAM_OUTPUT_LOCALE: review.language,
|
|
63
|
+
AGENTSPINE_STATE_DIR: path.join(blunDir, 'state', 'agent-spine'),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createLauncherEnvironment(sourceEnv, mode, publicPackageVersion, blunDir) {
|
|
68
|
+
return {
|
|
69
|
+
...sourceEnv,
|
|
70
|
+
...runtimePolicyEnvironment(blunDir),
|
|
71
|
+
BLUN_NO_AUTO_UPDATE: '1',
|
|
72
|
+
BLUN_PUBLIC_PACKAGE_VERSION: publicPackageVersion,
|
|
73
|
+
BLUN_TELEGRAM_ATTACH: mode === LAUNCHER_MODES.KING ? 'on' : 'off',
|
|
74
|
+
BLUN_TELEGRAM_HEADLESS: 'off',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isNpmPackageUpdateRequest(args) {
|
|
79
|
+
if (!Array.isArray(args) || args.length !== 1) return false;
|
|
80
|
+
const command = String(args[0] || '').toLowerCase();
|
|
81
|
+
return command === 'update' || command === 'upgrade';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isStandaloneHelpRequest(args) {
|
|
85
|
+
return Array.isArray(args) && args.length === 1 && (args[0] === '--help' || args[0] === '-h');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isReadOnlySetupRequest(args) {
|
|
89
|
+
return Array.isArray(args) && args[0] === 'setup'
|
|
90
|
+
&& args.slice(1).some(value => typeof value === 'string' && SETUP_INSPECTION_OPTIONS.has(value.split('=', 1)[0]));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function isReadOnlyInspectionRequest(args) {
|
|
94
|
+
return isReadOnlySetupRequest(args)
|
|
95
|
+
|| (Array.isArray(args) && args[0] === 'config' && CONFIG_INSPECTION_COMMANDS.has(args[1]))
|
|
96
|
+
|| (Array.isArray(args) && args[0] === 'docs')
|
|
97
|
+
|| isReadOnlyDoctorRequest(args);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function isReadOnlyDoctorRequest(args) {
|
|
101
|
+
if (!Array.isArray(args) || args[0] !== 'doctor') return false;
|
|
102
|
+
try {
|
|
103
|
+
const { positionals } = parseArgs({ args: args.slice(1), allowPositionals: true,
|
|
104
|
+
options: { json: { type: 'boolean' }, schema: { type: 'boolean' }, help: { type: 'boolean', short: 'h' } } });
|
|
105
|
+
if (positionals.length === 0) return true;
|
|
106
|
+
if (positionals[0] === 'config' || positionals[0] === 'tui') return positionals.length <= 2;
|
|
107
|
+
return positionals[0] === 'mcp' && positionals.length === 2;
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function launcherHelpText() {
|
|
114
|
+
return LAUNCHER_HELP_TEXT;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
LAUNCHER_MODES,
|
|
119
|
+
createLauncherEnvironment,
|
|
120
|
+
isNpmPackageUpdateRequest,
|
|
121
|
+
isStandaloneHelpRequest,
|
|
122
|
+
isReadOnlySetupRequest,
|
|
123
|
+
isReadOnlyInspectionRequest,
|
|
124
|
+
launcherHelpText,
|
|
125
|
+
launcherModeFromArgv,
|
|
126
|
+
};
|
package/bin/launcher-runtime.js
CHANGED
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
createLauncherEnvironment,
|
|
14
14
|
isNpmPackageUpdateRequest,
|
|
15
15
|
isStandaloneHelpRequest,
|
|
16
|
+
isReadOnlyInspectionRequest,
|
|
16
17
|
launcherHelpText,
|
|
17
18
|
launcherModeFromArgv,
|
|
18
19
|
} = require('./launcher-mode');
|
|
@@ -50,6 +51,34 @@ function resolveLauncherPrivatePaths(options = {}) {
|
|
|
50
51
|
const env = options.env || process.env;
|
|
51
52
|
const platform = options.platform || process.platform;
|
|
52
53
|
const configuredHome = String(env.BLUN_HOME || '').trim();
|
|
54
|
+
const stateDir = String(env.BLUN_STATE_DIR || '').trim();
|
|
55
|
+
if (stateDir) {
|
|
56
|
+
const paths = platform === 'win32' ? path.win32 : path.posix;
|
|
57
|
+
if (!paths.isAbsolute(stateDir) || (platform === 'win32' && !/^[A-Za-z]:[\\/]/u.test(stateDir))) {
|
|
58
|
+
throw new Error('BLUN_STATE_DIR must be an absolute local path.');
|
|
59
|
+
}
|
|
60
|
+
const blunHome = paths.resolve(stateDir);
|
|
61
|
+
const fsImpl = options.fsImpl || fs;
|
|
62
|
+
let ancestor = blunHome;
|
|
63
|
+
for (;;) {
|
|
64
|
+
try {
|
|
65
|
+
const stat = fsImpl.lstatSync(ancestor);
|
|
66
|
+
const realpath = fsImpl.realpathSync.native || fsImpl.realpathSync;
|
|
67
|
+
const canonical = realpath(ancestor);
|
|
68
|
+
const normalize = platform === 'win32' ? normalizeWindowsPath : paths.normalize;
|
|
69
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || normalize(canonical) !== normalize(ancestor)) {
|
|
70
|
+
throw new Error('BLUN_STATE_DIR must not traverse a symbolic link or junction.');
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error.code !== 'ENOENT') throw error;
|
|
75
|
+
const parent = paths.dirname(ancestor);
|
|
76
|
+
if (parent === ancestor) throw error;
|
|
77
|
+
ancestor = parent;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { blunHome, userHome: (options.homedir || os.homedir)() };
|
|
81
|
+
}
|
|
53
82
|
if (platform !== 'win32') {
|
|
54
83
|
const userHome = (options.homedir || os.homedir)();
|
|
55
84
|
return {
|
|
@@ -369,6 +398,22 @@ async function runLauncher(options = {}) {
|
|
|
369
398
|
return;
|
|
370
399
|
}
|
|
371
400
|
|
|
401
|
+
if (isReadOnlyInspectionRequest(ARGS)) {
|
|
402
|
+
const compatibilityError = unsupportedNodeMessage(process.versions.node, process.platform);
|
|
403
|
+
if (compatibilityError !== null) {
|
|
404
|
+
process.stderr.write(`${compatibilityError}\n`);
|
|
405
|
+
process.exitCode = 1;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const blunDir = resolveLauncherPrivatePaths().blunHome;
|
|
409
|
+
require('./verify-bundled-agent-sources.cjs').verifyBundledAgentSources(PKG);
|
|
410
|
+
const env = createLauncherEnvironment(process.env, LAUNCHER_MODES.BLUN, readPackageVersion(), blunDir);
|
|
411
|
+
env.BLUN_HOME = blunDir;
|
|
412
|
+
if (String(process.env.BLUN_STATE_DIR || '').trim()) env.BLUN_SHARED_HOME = blunDir;
|
|
413
|
+
process.exitCode = await runInformationalCore(ARGS, env, callerCwd, releaseNotice);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
372
417
|
let privatePaths;
|
|
373
418
|
const getPrivatePaths = () => {
|
|
374
419
|
privatePaths ||= resolveLauncherPrivatePaths();
|
|
@@ -453,13 +498,31 @@ async function runLauncher(options = {}) {
|
|
|
453
498
|
installTelegramForProfile(PKG, blunDir);
|
|
454
499
|
}
|
|
455
500
|
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
501
|
+
const nativeOwner = new AbortController();
|
|
502
|
+
let nativeStopSignal;
|
|
503
|
+
const nativeSignals = new Map();
|
|
504
|
+
for (const signal of ['SIGHUP', 'SIGINT', 'SIGTERM']) {
|
|
505
|
+
const handler = () => { nativeStopSignal = signal; nativeOwner.abort(); };
|
|
506
|
+
nativeSignals.set(signal, handler);
|
|
507
|
+
process.once(signal, handler);
|
|
508
|
+
}
|
|
509
|
+
let nativeModules;
|
|
510
|
+
try {
|
|
511
|
+
nativeModules = await repairConfiguredNativeModules({
|
|
512
|
+
blunDir,
|
|
513
|
+
callerCwd,
|
|
514
|
+
signal: nativeOwner.signal,
|
|
515
|
+
onRepairStart() {
|
|
516
|
+
process.stdout.write('BLUN richtet native Node.js-Module fuer die neue Laufzeit ein.\n');
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
} catch (error) {
|
|
520
|
+
if (!nativeOwner.signal.aborted) throw error;
|
|
521
|
+
process.exitCode = nativeStopSignal === 'SIGINT' ? 130 : nativeStopSignal === 'SIGHUP' ? 129 : 143;
|
|
522
|
+
return;
|
|
523
|
+
} finally {
|
|
524
|
+
for (const [signal, handler] of nativeSignals) process.removeListener(signal, handler);
|
|
525
|
+
}
|
|
463
526
|
if (nativeModules.repaired.length > 0) {
|
|
464
527
|
process.stdout.write('Native Node.js-Module sind wieder einsatzbereit.\n');
|
|
465
528
|
}
|
|
@@ -467,6 +530,7 @@ async function runLauncher(options = {}) {
|
|
|
467
530
|
// --- 6. Start -----------------------------------------------------------
|
|
468
531
|
const env = createLauncherEnvironment(process.env, mode, readPackageVersion(), blunDir);
|
|
469
532
|
env.BLUN_HOME = blunDir;
|
|
533
|
+
if (String(process.env.BLUN_STATE_DIR || '').trim()) env.BLUN_SHARED_HOME = blunDir;
|
|
470
534
|
process.exitCode = await superviseProtectedCore(
|
|
471
535
|
ARGS,
|
|
472
536
|
env,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('node:child_process');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { StringDecoder } = require('node:string_decoder');
|
|
6
|
+
|
|
7
|
+
const MAX_DIAGNOSTIC_BYTES = 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
async function stopOwnedProcessTree(child) {
|
|
10
|
+
if (!child.pid || child.exitCode !== null || child.signalCode !== null) return;
|
|
11
|
+
if (process.platform !== 'win32') {
|
|
12
|
+
try { process.kill(-child.pid, 'SIGKILL'); } catch { child.kill('SIGKILL'); }
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
await new Promise(resolve => {
|
|
16
|
+
const fallback = () => {
|
|
17
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
18
|
+
try { child.kill('SIGKILL'); } catch {}
|
|
19
|
+
}
|
|
20
|
+
resolve();
|
|
21
|
+
};
|
|
22
|
+
const killer = spawn(path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'taskkill.exe'),
|
|
23
|
+
['/PID', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' });
|
|
24
|
+
killer.once('error', fallback);
|
|
25
|
+
killer.once('close', fallback);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function runNativeCommand(command, args, { cwd, signal, spawnImpl = spawn }) {
|
|
30
|
+
signal?.throwIfAborted();
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const child = spawnImpl(command, args, { cwd, windowsHide: true,
|
|
33
|
+
detached: process.platform !== 'win32', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
34
|
+
let error;
|
|
35
|
+
let stopping;
|
|
36
|
+
let cancelled = false;
|
|
37
|
+
const collect = stream => {
|
|
38
|
+
const decoder = new StringDecoder('utf8');
|
|
39
|
+
let bytes = 0;
|
|
40
|
+
let text = '';
|
|
41
|
+
let truncated = false;
|
|
42
|
+
stream.on('data', chunk => {
|
|
43
|
+
const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
44
|
+
const kept = data.subarray(0, Math.max(0, MAX_DIAGNOSTIC_BYTES - bytes));
|
|
45
|
+
bytes += kept.length;
|
|
46
|
+
truncated ||= kept.length !== data.length;
|
|
47
|
+
if (kept.length) text += decoder.write(kept);
|
|
48
|
+
});
|
|
49
|
+
return () => text + decoder.end() + (truncated ? '\n[NATIVE_DIAGNOSTIC_OUTPUT_TRUNCATED]' : '');
|
|
50
|
+
};
|
|
51
|
+
const stdout = collect(child.stdout);
|
|
52
|
+
const stderr = collect(child.stderr);
|
|
53
|
+
const stop = () => {
|
|
54
|
+
cancelled = true;
|
|
55
|
+
stopping ??= stopOwnedProcessTree(child).catch(stopError => {
|
|
56
|
+
error = stopError;
|
|
57
|
+
try { child.kill('SIGKILL'); } catch {}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
signal?.addEventListener('abort', stop, { once: true });
|
|
61
|
+
child.once('error', spawnError => { error = spawnError; });
|
|
62
|
+
child.once('close', async (status, exitSignal) => {
|
|
63
|
+
signal?.removeEventListener('abort', stop);
|
|
64
|
+
await stopping;
|
|
65
|
+
if (cancelled) reject(new DOMException('Native command cancelled', 'AbortError'));
|
|
66
|
+
else resolve({ status, signal: exitSignal, stdout: stdout(), stderr: stderr(), error });
|
|
67
|
+
});
|
|
68
|
+
if (signal?.aborted) stop();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { runNativeCommand };
|