blun-king-cli 9.1.625 → 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 -77
- package/bin/launcher-mode.js +126 -93
- package/bin/launcher-runtime.js +74 -8
- 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,77 +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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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 {
|
|
@@ -102,13 +131,14 @@ function shouldDetachProtectedCore(options = {}) {
|
|
|
102
131
|
}
|
|
103
132
|
|
|
104
133
|
function spawnProtectedCore(args, env, cwd) {
|
|
134
|
+
const terminalOwner = process.platform === 'win32' && process.stdin.isTTY === true;
|
|
105
135
|
const child = spawn(
|
|
106
136
|
process.execPath,
|
|
107
137
|
[path.join(PKG, 'bin', 'core-bootstrap.js'), ...args],
|
|
108
138
|
{
|
|
109
139
|
cwd,
|
|
110
140
|
detached: shouldDetachProtectedCore(),
|
|
111
|
-
env,
|
|
141
|
+
env: { ...env, BLUN_TERMINAL_OWNER: terminalOwner ? '1' : '0' },
|
|
112
142
|
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
113
143
|
windowsHide: true,
|
|
114
144
|
},
|
|
@@ -146,6 +176,7 @@ function spawnProtectedCore(args, env, cwd) {
|
|
|
146
176
|
child.on('message', (message) => {
|
|
147
177
|
if (message?.type !== CORE_LOADED_MESSAGE) return;
|
|
148
178
|
settleLoaded(true);
|
|
179
|
+
if (terminalOwner) return;
|
|
149
180
|
try {
|
|
150
181
|
child.disconnect();
|
|
151
182
|
} catch {}
|
|
@@ -367,6 +398,22 @@ async function runLauncher(options = {}) {
|
|
|
367
398
|
return;
|
|
368
399
|
}
|
|
369
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
|
+
|
|
370
417
|
let privatePaths;
|
|
371
418
|
const getPrivatePaths = () => {
|
|
372
419
|
privatePaths ||= resolveLauncherPrivatePaths();
|
|
@@ -451,13 +498,31 @@ async function runLauncher(options = {}) {
|
|
|
451
498
|
installTelegramForProfile(PKG, blunDir);
|
|
452
499
|
}
|
|
453
500
|
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
+
}
|
|
461
526
|
if (nativeModules.repaired.length > 0) {
|
|
462
527
|
process.stdout.write('Native Node.js-Module sind wieder einsatzbereit.\n');
|
|
463
528
|
}
|
|
@@ -465,6 +530,7 @@ async function runLauncher(options = {}) {
|
|
|
465
530
|
// --- 6. Start -----------------------------------------------------------
|
|
466
531
|
const env = createLauncherEnvironment(process.env, mode, readPackageVersion(), blunDir);
|
|
467
532
|
env.BLUN_HOME = blunDir;
|
|
533
|
+
if (String(process.env.BLUN_STATE_DIR || '').trim()) env.BLUN_SHARED_HOME = blunDir;
|
|
468
534
|
process.exitCode = await superviseProtectedCore(
|
|
469
535
|
ARGS,
|
|
470
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 };
|