blun-king-cli 9.1.602 → 9.1.603
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/CHANGELOG.md +103 -97
- package/LIESMICH.txt +57 -57
- package/README.md +90 -90
- package/bin/agentspine-king-goal-inbox.mjs +127 -127
- package/bin/agentspine-king-goal-intake.mjs +106 -106
- package/bin/agentspine-king-host-runner.mjs +109 -109
- package/bin/agentspine-king-snapshot-policy.cjs +80 -80
- package/bin/agentspine-king-status-policy.mjs +226 -226
- package/bin/agentspine-king-worker-host.mjs +160 -160
- package/bin/core-bootstrap.js +77 -77
- package/bin/launcher-mode.js +93 -93
- package/bin/launcher-restart-policy.cjs +131 -131
- package/bin/launcher-runtime.js +491 -491
- package/bin/managed-node.js +186 -186
- package/bin/managed-plugin-selection.cjs +115 -115
- package/bin/native-module-repair.js +201 -201
- package/bin/node-runtime.js +34 -34
- package/bin/node-version.js +49 -49
- package/bin/plugin-bootstrap.js +162 -162
- package/bin/private-paths.js +1606 -1606
- package/bin/runtime-exit-ledger.cjs +144 -144
- package/bin/runtime-exit-ledger.d.cts +23 -23
- package/bin/standard-tools-bootstrap.js +272 -272
- package/bin/turn-thinking-policy.cjs +82 -82
- package/bin/update-copy.js +200 -200
- package/bin/update-lease.js +597 -597
- package/bin/update-notice.js +1568 -1568
- package/bin/verify-agent-behavior.cjs +122 -122
- package/bin/verify-agent-components.cjs +104 -104
- package/bin/verify-bundled-agent-sources.cjs +83 -83
- package/bin/windows-node-crash-dump.cjs +289 -289
- package/blun.mjs +115 -7
- package/codebase-index/codebase_index.py +470 -470
- package/dist-web/assets/{CodeBlockNode-CXnTLMNr.js → CodeBlockNode-TKKkJX8L.js} +1 -1
- package/dist-web/assets/{CodeBlockShell.vue_vue_type_style_index_0_lang-CCF8Ntnq.js → CodeBlockShell.vue_vue_type_style_index_0_lang-CUj2Ds5x.js} +1 -1
- package/dist-web/assets/DesignSystemView-DwK1aQMo.js +12 -0
- package/dist-web/assets/DesignSystemView-szjTj7ns.css +1 -0
- package/dist-web/assets/{Tooltip-CFQEzqC-.js → Tooltip-CTWv7WCU.js} +1 -1
- package/dist-web/assets/{abnfDiagram-N423BO3Z-Z1APZG5m.js → abnfDiagram-N423BO3Z-DA8V8Zjh.js} +1 -1
- package/dist-web/assets/{arc-C4VyWloT.js → arc-YtddKbdY.js} +1 -1
- package/dist-web/assets/{architectureDiagram-T3A2C74G-DpddbUcM.js → architectureDiagram-T3A2C74G-CGEiObmo.js} +1 -1
- package/dist-web/assets/{blockDiagram-VBNYF7ZC-BqccOSMr.js → blockDiagram-VBNYF7ZC-Bd5Vt50e.js} +1 -1
- package/dist-web/assets/{c4Diagram-5PPSVZJV-OHAcJrTy.js → c4Diagram-5PPSVZJV-D2ApPM54.js} +1 -1
- package/dist-web/assets/channel-BnluexuN.js +1 -0
- package/dist-web/assets/{chunk-2GRJ4B5K-DqJHs8Fw.js → chunk-2GRJ4B5K-BDcIEZSl.js} +1 -1
- package/dist-web/assets/{chunk-2Q5K7J3B-B1nCd91o.js → chunk-2Q5K7J3B-6aSabCX7.js} +1 -1
- package/dist-web/assets/{chunk-5RXB4S5H-neG70kZx.js → chunk-5RXB4S5H-BFRQvIJS.js} +1 -1
- package/dist-web/assets/{chunk-5VM5RSS4-l7biGqeH.js → chunk-5VM5RSS4-ChAa-DNY.js} +1 -1
- package/dist-web/assets/{chunk-6Q2QTUOP-BO-F11Gs.js → chunk-6Q2QTUOP-BIfrb2Jc.js} +1 -1
- package/dist-web/assets/{chunk-GF5L2VYU-BtC69fw0.js → chunk-GF5L2VYU-BR0gvtWI.js} +1 -1
- package/dist-web/assets/{chunk-JWPE2WC7-DIOubEqr.js → chunk-JWPE2WC7-BgXKsx5E.js} +1 -1
- package/dist-web/assets/{chunk-KBJHAD2P-DDUeH_Ba.js → chunk-KBJHAD2P-I8mrZhpz.js} +1 -1
- package/dist-web/assets/{chunk-RYQCIY6F-B9gFMVbS.js → chunk-RYQCIY6F-BMPaem92.js} +1 -1
- package/dist-web/assets/{chunk-XXDRQBXY-Bn1Xz6TQ.js → chunk-XXDRQBXY-COy0J-0j.js} +1 -1
- package/dist-web/assets/classDiagram-JCYQIIEL-BliFdSD9.js +1 -0
- package/dist-web/assets/classDiagram-v2-OCEON4UE-BliFdSD9.js +1 -0
- package/dist-web/assets/{cose-bilkent-JH36ORCC-DqigO3T4.js → cose-bilkent-JH36ORCC-MosTTRdo.js} +1 -1
- package/dist-web/assets/{cynefin-VYW2F7L2-rpD93tQ-.js → cynefin-VYW2F7L2-5tjPgrkP.js} +1 -1
- package/dist-web/assets/{cynefinDiagram-MW4NZA55-DbTqnkVJ.js → cynefinDiagram-MW4NZA55-CbDcdInR.js} +1 -1
- package/dist-web/assets/{dagre-VZM6K2ZE-DcM8aEVY.js → dagre-VZM6K2ZE-YaBom1ZH.js} +1 -1
- package/dist-web/assets/{diagram-7IWD3JNH-UymgsBNa.js → diagram-7IWD3JNH-Da4m-Hgz.js} +1 -1
- package/dist-web/assets/{diagram-B4RE2ZJO-BiYu5WPF.js → diagram-B4RE2ZJO-CAUUegxg.js} +1 -1
- package/dist-web/assets/{diagram-LBJQPF4R-BD3kkgOO.js → diagram-LBJQPF4R-o21mWKG4.js} +1 -1
- package/dist-web/assets/{diagram-Q27KOJAE-D1mYQ_cI.js → diagram-Q27KOJAE-BA3z3sXf.js} +1 -1
- package/dist-web/assets/{diagram-UB23O5K3-BtX8u6HG.js → diagram-UB23O5K3-CGRdSSDK.js} +1 -1
- package/dist-web/assets/{ebnfDiagram-BXEA7PRR-CBAnFkRF.js → ebnfDiagram-BXEA7PRR-Bw-iU9FP.js} +1 -1
- package/dist-web/assets/{erDiagram-JOGREHBK-3vKcWsxm.js → erDiagram-JOGREHBK-BwM1yiR0.js} +1 -1
- package/dist-web/assets/{flowDiagram-UKHOOZJN-D9wBbHm2.js → flowDiagram-UKHOOZJN-z05eOR1Q.js} +1 -1
- package/dist-web/assets/{ganttDiagram-PKOTCBZU-Cd_dViF-.js → ganttDiagram-PKOTCBZU-BMUubNTl.js} +1 -1
- package/dist-web/assets/{gitGraphDiagram-DS77QQ5N-BiEEtTo7.js → gitGraphDiagram-DS77QQ5N-Dccbi1wU.js} +1 -1
- package/dist-web/assets/{index-DdEakFJM.js → index-BBbC5LRF.js} +1 -1
- package/dist-web/assets/{index-BmKj7CB1.css → index-C9eDH3u-.css} +1 -1
- package/dist-web/assets/{index-Ba1OvLjf.js → index-D48p0M2S.js} +2 -2
- package/dist-web/assets/{index-CiPA8Kex.js → index-h0KEOmkU.js} +14 -14
- package/dist-web/assets/{index10-CNr4U-Nw.js → index10-Qbuw1N6u.js} +1 -1
- package/dist-web/assets/{index11-Dto_yZNz.js → index11-g8v025wN.js} +1 -1
- package/dist-web/assets/{index5-B2PldaO2.js → index5-BYNcV8BY.js} +1 -1
- package/dist-web/assets/{index6-CBdZmmyx.js → index6-BetANtnV.js} +1 -1
- package/dist-web/assets/{index7-Dz9oCsUF.js → index7-BcPbgCKN.js} +1 -1
- package/dist-web/assets/{index8-B6k25jtU.js → index8-B1lp-RvF.js} +2 -2
- package/dist-web/assets/{infoDiagram-6WML65LV-D4ph2sIH.js → infoDiagram-6WML65LV-BF_pbUnN.js} +1 -1
- package/dist-web/assets/{ishikawaDiagram-WSZJBQD7-DjKjbMm9.js → ishikawaDiagram-WSZJBQD7-Dedv4urS.js} +1 -1
- package/dist-web/assets/{journeyDiagram-NVQOT4AX-DttHDfPQ.js → journeyDiagram-NVQOT4AX-B1Ex3b6o.js} +1 -1
- package/dist-web/assets/{kanban-definition-27J2QSJJ-C5gB-W0d.js → kanban-definition-27J2QSJJ-BtCGliBH.js} +1 -1
- package/dist-web/assets/{linear-BRGe4-d6.js → linear-ByItc8Qr.js} +1 -1
- package/dist-web/assets/{mermaid.core-D-9Sx1No.js → mermaid.core-B1M2O7nN.js} +4 -4
- package/dist-web/assets/{mindmap-definition-FAOFIHXS-Cg_hoVIq.js → mindmap-definition-FAOFIHXS-D0UHKUyj.js} +1 -1
- package/dist-web/assets/{pegDiagram-VL7TDLO6-CSX9hitD.js → pegDiagram-VL7TDLO6-D7zYCVmG.js} +1 -1
- package/dist-web/assets/{pieDiagram-7S7Q4E2Y-0L0mxahZ.js → pieDiagram-7S7Q4E2Y-Bd0i9CHa.js} +1 -1
- package/dist-web/assets/{quadrantDiagram-CIZ2JOQS-U483z0er.js → quadrantDiagram-CIZ2JOQS-MG-Ug47d.js} +1 -1
- package/dist-web/assets/{railroadDiagram-AXF67PYL-D80a2_DJ.js → railroadDiagram-AXF67PYL-AZdl90Gj.js} +1 -1
- package/dist-web/assets/{requirementDiagram-LRYGKXZP-CeyHfq8t.js → requirementDiagram-LRYGKXZP-DjplF9kt.js} +1 -1
- package/dist-web/assets/{sankeyDiagram-W5VNT64P-CCISkkrR.js → sankeyDiagram-W5VNT64P-Bi9Zgfjs.js} +1 -1
- package/dist-web/assets/{sequenceDiagram-SI44F4Z6-jQfm2XS7.js → sequenceDiagram-SI44F4Z6-ChEpdCvb.js} +1 -1
- package/dist-web/assets/{sizeCapture-X5ZJPWSS-lRZsmCoS.js → sizeCapture-X5ZJPWSS-B5RFUdaa.js} +1 -1
- package/dist-web/assets/{stateDiagram-OKZ733FA-ClCgTdUm.js → stateDiagram-OKZ733FA-BkRblSKp.js} +1 -1
- package/dist-web/assets/stateDiagram-v2-UEYNNEHI-fVkQcACW.js +1 -0
- package/dist-web/assets/{swimlanes-SLNWSIFB-BCdOUN05.js → swimlanes-SLNWSIFB-DLXH6Zhc.js} +2 -2
- package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-Bg-BiZJQ.js +8 -0
- package/dist-web/assets/{timeline-definition-Z64GVDOM-DpWutSKa.js → timeline-definition-Z64GVDOM-C_N88siO.js} +1 -1
- package/dist-web/assets/{vennDiagram-T6HMQDX7-DVC5JVsc.js → vennDiagram-T6HMQDX7-DMsnvDt0.js} +1 -1
- package/dist-web/assets/{vue.runtime.esm-bundler-BwwRtQwQ.js → vue.runtime.esm-bundler-B-3uIy85.js} +1 -1
- package/dist-web/assets/{wardleyDiagram-T6FBY63Y-BK2mrq7L.js → wardleyDiagram-T6FBY63Y-C9SJIPzq.js} +1 -1
- package/dist-web/assets/{xychartDiagram-ELKLHX3M-DUJu9F7-.js → xychartDiagram-ELKLHX3M-DZLWA6Go.js} +1 -1
- package/dist-web/index.html +32 -32
- package/dist-web/vis/index.html.gz +0 -0
- package/package.json +1 -1
- package/scripts/fix-node-pty-perms.js +53 -53
- package/standard-skills/blun-app-design-system/SKILL.md +30 -30
- package/standard-skills/blun-app-design-system/provenance.json +17 -17
- package/standard-skills/blun-app-design-system/references/tokens.md +48 -48
- package/standard-skills/blun-app-design-system/scripts/check-blun-design.mjs +49 -49
- package/telegram-plugin/blun.plugin.json +20 -20
- package/telegram-plugin/commands/access.md +37 -37
- package/worker-host.mjs +5 -5
- package/dist-web/assets/DesignSystemView-6HgAUEmC.css +0 -1
- package/dist-web/assets/DesignSystemView-BbMOu-XO.js +0 -12
- package/dist-web/assets/channel-B2VOQOI-.js +0 -1
- package/dist-web/assets/classDiagram-JCYQIIEL-BA1TjAxj.js +0 -1
- package/dist-web/assets/classDiagram-v2-OCEON4UE-BA1TjAxj.js +0 -1
- package/dist-web/assets/stateDiagram-v2-UEYNNEHI-fi_lrTAx.js +0 -1
- package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-BUd_uYK3.js +0 -8
package/bin/launcher-runtime.js
CHANGED
|
@@ -1,491 +1,491 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
/*
|
|
3
|
-
* BLUN CLI Launcher (npm) — Erststart richtet alles ein:
|
|
4
|
-
* 1. King richtet das Telegram-Plugin im BLUN_HOME ein
|
|
5
|
-
* Danach: TUI starten. Anmeldung und Modellkatalog kommen ueber BLUN OAuth.
|
|
6
|
-
*/
|
|
7
|
-
const fs = require('node:fs');
|
|
8
|
-
const path = require('node:path');
|
|
9
|
-
const os = require('node:os');
|
|
10
|
-
const { spawn } = require('node:child_process');
|
|
11
|
-
const {
|
|
12
|
-
LAUNCHER_MODES,
|
|
13
|
-
createLauncherEnvironment,
|
|
14
|
-
isNpmPackageUpdateRequest,
|
|
15
|
-
isStandaloneHelpRequest,
|
|
16
|
-
launcherHelpText,
|
|
17
|
-
launcherModeFromArgv,
|
|
18
|
-
} = require('./launcher-mode');
|
|
19
|
-
const { installManagedTelegramPlugin, installManagedAgentSpinePlugin } = require('./plugin-bootstrap');
|
|
20
|
-
const {
|
|
21
|
-
availableStandardToolNames,
|
|
22
|
-
seedStandardDesignSkill,
|
|
23
|
-
seedStandardTools,
|
|
24
|
-
} = require('./standard-tools-bootstrap');
|
|
25
|
-
const { CORE_LOADED_MESSAGE } = require('./core-bootstrap');
|
|
26
|
-
const {
|
|
27
|
-
appendLauncherStatus, claimLauncherRestart, consumeIntentionalStopMarker,
|
|
28
|
-
isAbnormalCoreExit, restartDelayMs,
|
|
29
|
-
} = require('./launcher-restart-policy.cjs');
|
|
30
|
-
const { recordRuntimeExit } = require('./runtime-exit-ledger.cjs');
|
|
31
|
-
const { prepareManagedNodeRuntime } = require('./node-runtime');
|
|
32
|
-
const { unsupportedNodeMessage } = require('./node-version');
|
|
33
|
-
const { repairConfiguredNativeModules } = require('./native-module-repair');
|
|
34
|
-
const { acquireSharedRuntimeLease, tryAcquireUpdateLease } = require('./update-lease');
|
|
35
|
-
const { runExplicitUpdate, runUpdateNotice } = require('./update-notice');
|
|
36
|
-
const {
|
|
37
|
-
ensurePrivateDirectory,
|
|
38
|
-
securePrivateFile,
|
|
39
|
-
writePrivateFile,
|
|
40
|
-
} = require('./private-paths');
|
|
41
|
-
|
|
42
|
-
const PKG = path.resolve(__dirname, '..');
|
|
43
|
-
const ARGS = process.argv.slice(2);
|
|
44
|
-
const CORE_LOAD_TIMEOUT_MS = 30_000;
|
|
45
|
-
|
|
46
|
-
function normalizeWindowsPath(value) {
|
|
47
|
-
return path.win32.normalize(value).replace(/\\+$/u, '').toLowerCase();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function resolveLauncherPrivatePaths(options = {}) {
|
|
51
|
-
const env = options.env || process.env;
|
|
52
|
-
const platform = options.platform || process.platform;
|
|
53
|
-
const configuredHome = String(env.BLUN_HOME || '').trim();
|
|
54
|
-
if (platform !== 'win32') {
|
|
55
|
-
const userHome = (options.homedir || os.homedir)();
|
|
56
|
-
return {
|
|
57
|
-
blunHome: configuredHome ? path.resolve(configuredHome) : path.join(userHome, '.blun'),
|
|
58
|
-
userHome,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const fsImpl = options.fsImpl || fs;
|
|
63
|
-
const userInfo = (options.userInfo || os.userInfo)();
|
|
64
|
-
const reportedHome = String(userInfo?.homedir || '').trim();
|
|
65
|
-
if (!path.win32.isAbsolute(reportedHome) || reportedHome.startsWith('\\\\')) {
|
|
66
|
-
throw new Error('Das lokale Windows-Benutzerprofil konnte nicht sicher bestimmt werden.');
|
|
67
|
-
}
|
|
68
|
-
const resolvedHome = path.win32.resolve(reportedHome);
|
|
69
|
-
const stat = fsImpl.lstatSync(resolvedHome);
|
|
70
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
71
|
-
throw new Error('Das lokale Windows-Benutzerprofil ist kein vertrauenswuerdiges Verzeichnis.');
|
|
72
|
-
}
|
|
73
|
-
const realpath = fsImpl.realpathSync.native || fsImpl.realpathSync;
|
|
74
|
-
const userHome = path.win32.normalize(realpath(resolvedHome));
|
|
75
|
-
const volumeRoot = path.win32.parse(userHome).root;
|
|
76
|
-
if (!/^[A-Za-z]:\\$/u.test(volumeRoot)
|
|
77
|
-
|| normalizeWindowsPath(userHome) !== normalizeWindowsPath(resolvedHome)) {
|
|
78
|
-
throw new Error('Das lokale Windows-Benutzerprofil ist kein kanonischer lokaler Pfad.');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const blunHome = path.win32.join(userHome, '.blun');
|
|
82
|
-
if (configuredHome
|
|
83
|
-
&& normalizeWindowsPath(path.win32.resolve(configuredHome)) !== normalizeWindowsPath(blunHome)) {
|
|
84
|
-
throw new Error(`BLUN_HOME muss unter Windows exakt im Windows-Benutzerprofil liegen: ${blunHome}`);
|
|
85
|
-
}
|
|
86
|
-
return { blunHome, userHome };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function readPackageVersion() {
|
|
90
|
-
const { version } = JSON.parse(fs.readFileSync(path.join(PKG, 'package.json'), 'utf8'));
|
|
91
|
-
if (typeof version !== 'string' || version.length === 0) {
|
|
92
|
-
throw new Error('Ungueltige Paketversion in package.json');
|
|
93
|
-
}
|
|
94
|
-
return version;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function shouldDetachProtectedCore(options = {}) {
|
|
98
|
-
const platform = options.platform || process.platform;
|
|
99
|
-
const stdinIsTTY = Object.hasOwn(options, 'stdinIsTTY')
|
|
100
|
-
? options.stdinIsTTY
|
|
101
|
-
: process.stdin.isTTY;
|
|
102
|
-
return platform !== 'win32' || stdinIsTTY !== true;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function spawnProtectedCore(args, env, cwd) {
|
|
106
|
-
const child = spawn(
|
|
107
|
-
process.execPath,
|
|
108
|
-
[path.join(PKG, 'bin', 'core-bootstrap.js'), ...args],
|
|
109
|
-
{
|
|
110
|
-
cwd,
|
|
111
|
-
detached: shouldDetachProtectedCore(),
|
|
112
|
-
env,
|
|
113
|
-
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
114
|
-
windowsHide: true,
|
|
115
|
-
},
|
|
116
|
-
);
|
|
117
|
-
let loadedSettled = false;
|
|
118
|
-
let completedSettled = false;
|
|
119
|
-
let operatorSignal;
|
|
120
|
-
let resolveLoaded;
|
|
121
|
-
let resolveCompleted;
|
|
122
|
-
const loadTimer = setTimeout(() => {
|
|
123
|
-
try {
|
|
124
|
-
child.kill();
|
|
125
|
-
} catch {}
|
|
126
|
-
}, CORE_LOAD_TIMEOUT_MS);
|
|
127
|
-
const loaded = new Promise((resolve) => { resolveLoaded = resolve; });
|
|
128
|
-
const completed = new Promise((resolve) => { resolveCompleted = resolve; });
|
|
129
|
-
const settleLoaded = (value) => {
|
|
130
|
-
if (loadedSettled) return;
|
|
131
|
-
loadedSettled = true;
|
|
132
|
-
clearTimeout(loadTimer);
|
|
133
|
-
resolveLoaded(value);
|
|
134
|
-
};
|
|
135
|
-
const settleCompleted = (result) => {
|
|
136
|
-
if (completedSettled) return;
|
|
137
|
-
completedSettled = true;
|
|
138
|
-
for (const [signal, handler] of signalHandlers) process.removeListener(signal, handler);
|
|
139
|
-
settleLoaded(false);
|
|
140
|
-
resolveCompleted(operatorSignal === undefined ? result : { ...result, signal: operatorSignal });
|
|
141
|
-
};
|
|
142
|
-
const signalHandlers = new Map();
|
|
143
|
-
for (const signal of ['SIGHUP', 'SIGINT', 'SIGTERM']) {
|
|
144
|
-
const handler = () => {
|
|
145
|
-
operatorSignal = signal;
|
|
146
|
-
try {
|
|
147
|
-
child.kill(signal);
|
|
148
|
-
} catch {}
|
|
149
|
-
};
|
|
150
|
-
signalHandlers.set(signal, handler);
|
|
151
|
-
process.once(signal, handler);
|
|
152
|
-
}
|
|
153
|
-
child.on('message', (message) => {
|
|
154
|
-
if (message?.type !== CORE_LOADED_MESSAGE) return;
|
|
155
|
-
settleLoaded(true);
|
|
156
|
-
try {
|
|
157
|
-
child.disconnect();
|
|
158
|
-
} catch {}
|
|
159
|
-
});
|
|
160
|
-
child.once('error', (error) => settleCompleted({ error }));
|
|
161
|
-
child.once('exit', (code, signal) => settleCompleted({ code, signal }));
|
|
162
|
-
return { child, completed, loaded };
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async function runInformationalCore(args, env, cwd, releaseNotice) {
|
|
166
|
-
const core = spawnProtectedCore(args, env, cwd);
|
|
167
|
-
await core.loaded;
|
|
168
|
-
await releaseNotice();
|
|
169
|
-
const result = await core.completed;
|
|
170
|
-
if (result.error) throw result.error;
|
|
171
|
-
return Number.isInteger(result.code) ? result.code : exitCodeForChild(result, false);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function superviseProtectedCore(args, env, cwd, releaseNotice) {
|
|
175
|
-
let noticeReleased = false;
|
|
176
|
-
for (;;) {
|
|
177
|
-
const core = spawnProtectedCore(args, env, cwd);
|
|
178
|
-
const loaded = await core.loaded;
|
|
179
|
-
if (!noticeReleased) {
|
|
180
|
-
noticeReleased = true;
|
|
181
|
-
await releaseNotice();
|
|
182
|
-
}
|
|
183
|
-
const result = await core.completed;
|
|
184
|
-
const exitCode = result.error ? 1 : exitCodeForChild(result, loaded);
|
|
185
|
-
const intentionalStop = consumeIntentionalStopMarker({
|
|
186
|
-
homeDir: env.BLUN_HOME, childPid: core.child.pid, nowMs: Date.now(),
|
|
187
|
-
});
|
|
188
|
-
recordRuntimeExit({
|
|
189
|
-
homeDir: env.BLUN_HOME, source: 'launcher', kind: result.error ? 'child-error' : 'child-exit',
|
|
190
|
-
exitCode, signal: result.signal, phase: loaded ? 'runtime' : 'startup',
|
|
191
|
-
cliVersion: env.BLUN_PUBLIC_PACKAGE_VERSION, profile: env.BLUN_PROFILE,
|
|
192
|
-
loaded, childPid: core.child.pid, error: result.error,
|
|
193
|
-
});
|
|
194
|
-
if (intentionalStop || !isAbnormalCoreExit(result, loaded)) {
|
|
195
|
-
if (result.error) throw result.error;
|
|
196
|
-
return exitCode;
|
|
197
|
-
}
|
|
198
|
-
const claim = claimLauncherRestart({ homeDir: env.BLUN_HOME, nowMs: Date.now() });
|
|
199
|
-
try {
|
|
200
|
-
appendLauncherStatus({
|
|
201
|
-
homeDir: env.BLUN_HOME,
|
|
202
|
-
stateDir: env.BLUN_TELEGRAM_STATE_DIR
|
|
203
|
-
|| path.join(env.BLUN_SHARED_HOME || env.BLUN_HOME, 'channels', 'telegram'),
|
|
204
|
-
nowMs: Date.now(), attempt: claim.attempt, limit: claim.limit, exitCode, signal: result.signal,
|
|
205
|
-
kind: claim.allowed ? 'launcher-restart' : 'launcher-restart-exhausted',
|
|
206
|
-
});
|
|
207
|
-
} catch {}
|
|
208
|
-
if (!claim.allowed) {
|
|
209
|
-
if (result.error) throw result.error;
|
|
210
|
-
return exitCode;
|
|
211
|
-
}
|
|
212
|
-
await new Promise((resolve) => setTimeout(resolve, restartDelayMs(claim.attempt)));
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function exitCodeForChild(result, loaded) {
|
|
217
|
-
if (!loaded && result.code === 75) return 0;
|
|
218
|
-
if (Number.isInteger(result.code)) return result.code;
|
|
219
|
-
if (result.signal === 'SIGINT') return 130;
|
|
220
|
-
if (result.signal === 'SIGTERM') return 143;
|
|
221
|
-
return 1;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function installTelegramForProfile(packageRoot, blunDir) {
|
|
225
|
-
installManagedTelegramPlugin({ packageRoot, blunDir });
|
|
226
|
-
const channelsDir = path.join(blunDir, 'channels');
|
|
227
|
-
const channelDir = path.join(channelsDir, 'telegram');
|
|
228
|
-
ensurePrivateDirectory(channelsDir);
|
|
229
|
-
ensurePrivateDirectory(channelDir);
|
|
230
|
-
const envFile = path.join(channelDir, '.env');
|
|
231
|
-
if (!fs.existsSync(envFile)) {
|
|
232
|
-
writePrivateFile(envFile, 'BLUN_TELEGRAM_BOT_TOKEN=HIER_DEIN_BOT_TOKEN_EINFUEGEN\n');
|
|
233
|
-
} else {
|
|
234
|
-
securePrivateFile(envFile);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function spawnManagedLauncher(binary, cwd) {
|
|
239
|
-
return new Promise((resolve, reject) => {
|
|
240
|
-
const child = spawn(binary, process.argv.slice(1), {
|
|
241
|
-
cwd,
|
|
242
|
-
env: process.env,
|
|
243
|
-
stdio: 'inherit',
|
|
244
|
-
windowsHide: true,
|
|
245
|
-
});
|
|
246
|
-
child.once('error', reject);
|
|
247
|
-
child.once('exit', (code, signal) => {
|
|
248
|
-
if (Number.isInteger(code)) {
|
|
249
|
-
resolve(code);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (signal === 'SIGINT') {
|
|
253
|
-
resolve(130);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
if (signal === 'SIGTERM') {
|
|
257
|
-
resolve(143);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
resolve(1);
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
async function runLauncher(options = {}) {
|
|
266
|
-
const callerCwd = process.cwd();
|
|
267
|
-
const mode = options.mode || launcherModeFromArgv(process.argv);
|
|
268
|
-
const explicitUpdateRequest = isNpmPackageUpdateRequest(ARGS);
|
|
269
|
-
const explicitToolsRequest = String(ARGS[0] || '').toLowerCase() === 'tools';
|
|
270
|
-
const noticeResult = options.skipUpdateNotice === true
|
|
271
|
-
? { acquired: true, lease: { assertOwned() {}, async release() {} } }
|
|
272
|
-
: options.noticeLease
|
|
273
|
-
? { acquired: true, lease: options.noticeLease }
|
|
274
|
-
: await tryAcquireUpdateLease({ packageRoot: PKG, role: 'notice' });
|
|
275
|
-
if (!noticeResult.acquired) {
|
|
276
|
-
if (explicitUpdateRequest) {
|
|
277
|
-
process.stderr.write('Das Update konnte nicht verifiziert werden.\n');
|
|
278
|
-
process.exitCode = 1;
|
|
279
|
-
}
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const noticeLease = noticeResult.lease;
|
|
283
|
-
let runtimeLease = options.runtimeLease;
|
|
284
|
-
let noticeReleased = false;
|
|
285
|
-
const releaseNotice = async () => {
|
|
286
|
-
if (noticeReleased) return;
|
|
287
|
-
noticeReleased = true;
|
|
288
|
-
await noticeLease.release();
|
|
289
|
-
await runtimeLease?.release();
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
try {
|
|
293
|
-
if (!runtimeLease && !explicitUpdateRequest) {
|
|
294
|
-
const installProbe = await tryAcquireUpdateLease({ packageRoot: PKG, role: 'install' });
|
|
295
|
-
if (!installProbe.acquired) {
|
|
296
|
-
process.stderr.write(explicitUpdateRequest
|
|
297
|
-
? 'Das Update konnte nicht verifiziert werden.\n'
|
|
298
|
-
: 'Der geschuetzte BLUN-Start ist gerade nicht verfuegbar.\n');
|
|
299
|
-
process.exitCode = 1;
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
await installProbe.lease.release();
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (explicitToolsRequest) {
|
|
306
|
-
const action = String(ARGS[1] || '').toLowerCase();
|
|
307
|
-
const available = availableStandardToolNames({ packageRoot: PKG });
|
|
308
|
-
if (action === 'list' && ARGS.length === 2) {
|
|
309
|
-
process.stdout.write(`${available.join('\n')}\n`);
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
if (action === 'enable' && ARGS.length > 2) {
|
|
313
|
-
const privatePaths = resolveLauncherPrivatePaths();
|
|
314
|
-
const selected = [...new Set(ARGS.slice(2))];
|
|
315
|
-
const unknown = selected.find((name) => !available.includes(name));
|
|
316
|
-
if (unknown) {
|
|
317
|
-
process.stderr.write(`Unbekanntes Standard-Tool: ${unknown}\n`);
|
|
318
|
-
process.exitCode = 1;
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
const mcpNames = selected.filter((name) => name !== 'telegram');
|
|
322
|
-
if (mcpNames.length > 0) {
|
|
323
|
-
seedStandardTools({
|
|
324
|
-
packageRoot: PKG,
|
|
325
|
-
blunDir: privatePaths.blunHome,
|
|
326
|
-
names: mcpNames,
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
if (selected.includes('telegram')) {
|
|
330
|
-
installTelegramForProfile(PKG, privatePaths.blunHome);
|
|
331
|
-
}
|
|
332
|
-
process.stdout.write(`${selected.join('\n')}\n`);
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
process.stderr.write('Usage: blun tools list | blun tools enable <name...>\n');
|
|
336
|
-
process.exitCode = 1;
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
if (ARGS.some((arg) => arg === '--version' || arg === '-V')) {
|
|
340
|
-
process.stdout.write(`${readPackageVersion()}\n`);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (explicitUpdateRequest) {
|
|
345
|
-
const privatePaths = resolveLauncherPrivatePaths();
|
|
346
|
-
const result = await runExplicitUpdate({
|
|
347
|
-
currentVersion: readPackageVersion(),
|
|
348
|
-
env: process.env,
|
|
349
|
-
stdin: process.stdin,
|
|
350
|
-
stdout: process.stdout,
|
|
351
|
-
stderr: process.stderr,
|
|
352
|
-
blunDir: privatePaths.blunHome,
|
|
353
|
-
packageRoot: PKG,
|
|
354
|
-
noticeLease,
|
|
355
|
-
request: ARGS[0],
|
|
356
|
-
});
|
|
357
|
-
if (result.kind === 'failed') process.exitCode = result.exitCode || 1;
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if (isStandaloneHelpRequest(ARGS)) {
|
|
362
|
-
const compatibilityError = unsupportedNodeMessage(process.versions.node, process.platform);
|
|
363
|
-
if (compatibilityError !== null) {
|
|
364
|
-
process.stderr.write(`${compatibilityError}\n`);
|
|
365
|
-
process.exitCode = 1;
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
process.stdout.write(launcherHelpText());
|
|
369
|
-
const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
|
|
370
|
-
const informationalEnv = { ...process.env };
|
|
371
|
-
delete informationalEnv.BLUN_HOME;
|
|
372
|
-
env.BLUN_HOME = resolveLauncherPrivatePaths({ env: informationalEnv }).blunHome;
|
|
373
|
-
process.exitCode = await runInformationalCore(ARGS, env, callerCwd, releaseNotice);
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
let privatePaths;
|
|
378
|
-
const getPrivatePaths = () => {
|
|
379
|
-
privatePaths ||= resolveLauncherPrivatePaths();
|
|
380
|
-
return privatePaths;
|
|
381
|
-
};
|
|
382
|
-
const nodeRuntime = await prepareManagedNodeRuntime({
|
|
383
|
-
getBlunDir: () => getPrivatePaths().blunHome,
|
|
384
|
-
});
|
|
385
|
-
if (nodeRuntime.kind === 'failed') {
|
|
386
|
-
process.stderr.write(`${nodeRuntime.message}\n`);
|
|
387
|
-
process.exitCode = 1;
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
if (nodeRuntime.kind === 'managed') {
|
|
391
|
-
process.stdout.write('BLUN hat die passende Node.js-Laufzeit eingerichtet.\n');
|
|
392
|
-
await releaseNotice();
|
|
393
|
-
process.exitCode = await spawnManagedLauncher(nodeRuntime.binary, callerCwd);
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (ARGS.some((arg) => arg === '--help' || arg === '-h')) {
|
|
398
|
-
process.stdout.write(launcherHelpText());
|
|
399
|
-
const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
|
|
400
|
-
const informationalEnv = { ...process.env };
|
|
401
|
-
delete informationalEnv.BLUN_HOME;
|
|
402
|
-
env.BLUN_HOME = resolveLauncherPrivatePaths({ env: informationalEnv }).blunHome;
|
|
403
|
-
process.exitCode = await superviseProtectedCore(ARGS, env, callerCwd, releaseNotice);
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const blunDir = getPrivatePaths().blunHome;
|
|
408
|
-
ensurePrivateDirectory(blunDir);
|
|
409
|
-
|
|
410
|
-
// --- 1. Oeffentlicher npm-Update-Dialog. -------------------------------
|
|
411
|
-
const updateResult = options.skipUpdateNotice === true
|
|
412
|
-
? { kind: 'continue', reason: 'notice_busy' }
|
|
413
|
-
: await runUpdateNotice({
|
|
414
|
-
currentVersion: readPackageVersion(),
|
|
415
|
-
argv: ARGS,
|
|
416
|
-
env: process.env,
|
|
417
|
-
stdin: process.stdin,
|
|
418
|
-
stdout: process.stdout,
|
|
419
|
-
stderr: process.stderr,
|
|
420
|
-
blunDir,
|
|
421
|
-
packageRoot: PKG,
|
|
422
|
-
noticeLease,
|
|
423
|
-
});
|
|
424
|
-
if (updateResult.kind === 'installed' || updateResult.kind === 'update_in_progress') return;
|
|
425
|
-
|
|
426
|
-
if (!runtimeLease) {
|
|
427
|
-
const runtimeResult = await acquireSharedRuntimeLease({ packageRoot: PKG });
|
|
428
|
-
if (!runtimeResult.acquired) {
|
|
429
|
-
process.stderr.write('Der geschuetzte BLUN-Start ist gerade nicht verfuegbar.\n');
|
|
430
|
-
process.exitCode = 1;
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
runtimeLease = runtimeResult.lease;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// --- 2. Bestehende Konfiguration absichern -------------------------------
|
|
437
|
-
// Ein frischer Start bleibt absichtlich ohne statischen Anbieter. /login
|
|
438
|
-
// provisioniert King ueber den BLUN-OAuth-Server.
|
|
439
|
-
const cfgFile = path.join(blunDir, 'config.toml');
|
|
440
|
-
if (fs.existsSync(cfgFile)) securePrivateFile(cfgFile);
|
|
441
|
-
require('./verify-bundled-agent-sources.cjs').verifyBundledAgentSources(PKG);
|
|
442
|
-
|
|
443
|
-
// --- 3. BLUN-Standard-MCPs additiv im Benutzerprofil einrichten ---------
|
|
444
|
-
const standardMcpNames = availableStandardToolNames({ packageRoot: PKG })
|
|
445
|
-
.filter((name) => name !== 'telegram');
|
|
446
|
-
seedStandardTools({
|
|
447
|
-
packageRoot: PKG,
|
|
448
|
-
blunDir,
|
|
449
|
-
names: standardMcpNames,
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
// --- 4. Eigenes BLUN-Grunddesign als einzigen Standard-Skill installieren
|
|
453
|
-
seedStandardDesignSkill({ packageRoot: PKG, blunDir });
|
|
454
|
-
installManagedAgentSpinePlugin({ packageRoot: PKG, blunDir });
|
|
455
|
-
|
|
456
|
-
// --- 5. Telegram-Plugin + Token-Vorlage (nur King) -----------------------
|
|
457
|
-
if (mode === LAUNCHER_MODES.KING) {
|
|
458
|
-
installTelegramForProfile(PKG, blunDir);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const nativeModules = repairConfiguredNativeModules({
|
|
462
|
-
blunDir,
|
|
463
|
-
callerCwd,
|
|
464
|
-
onRepairStart() {
|
|
465
|
-
process.stdout.write('BLUN richtet native Node.js-Module fuer die neue Laufzeit ein.\n');
|
|
466
|
-
},
|
|
467
|
-
});
|
|
468
|
-
if (nativeModules.repaired.length > 0) {
|
|
469
|
-
process.stdout.write('Native Node.js-Module sind wieder einsatzbereit.\n');
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// --- 6. Start -----------------------------------------------------------
|
|
473
|
-
const env = createLauncherEnvironment(process.env, mode, readPackageVersion(), blunDir);
|
|
474
|
-
env.BLUN_HOME = blunDir;
|
|
475
|
-
process.exitCode = await superviseProtectedCore(
|
|
476
|
-
ARGS,
|
|
477
|
-
env,
|
|
478
|
-
callerCwd,
|
|
479
|
-
releaseNotice,
|
|
480
|
-
);
|
|
481
|
-
} finally {
|
|
482
|
-
await releaseNotice();
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
module.exports = {
|
|
487
|
-
resolveLauncherPrivatePaths,
|
|
488
|
-
runLauncher,
|
|
489
|
-
shouldDetachProtectedCore,
|
|
490
|
-
spawnManagedLauncher,
|
|
491
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
/*
|
|
3
|
+
* BLUN CLI Launcher (npm) — Erststart richtet alles ein:
|
|
4
|
+
* 1. King richtet das Telegram-Plugin im BLUN_HOME ein
|
|
5
|
+
* Danach: TUI starten. Anmeldung und Modellkatalog kommen ueber BLUN OAuth.
|
|
6
|
+
*/
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const os = require('node:os');
|
|
10
|
+
const { spawn } = require('node:child_process');
|
|
11
|
+
const {
|
|
12
|
+
LAUNCHER_MODES,
|
|
13
|
+
createLauncherEnvironment,
|
|
14
|
+
isNpmPackageUpdateRequest,
|
|
15
|
+
isStandaloneHelpRequest,
|
|
16
|
+
launcherHelpText,
|
|
17
|
+
launcherModeFromArgv,
|
|
18
|
+
} = require('./launcher-mode');
|
|
19
|
+
const { installManagedTelegramPlugin, installManagedAgentSpinePlugin } = require('./plugin-bootstrap');
|
|
20
|
+
const {
|
|
21
|
+
availableStandardToolNames,
|
|
22
|
+
seedStandardDesignSkill,
|
|
23
|
+
seedStandardTools,
|
|
24
|
+
} = require('./standard-tools-bootstrap');
|
|
25
|
+
const { CORE_LOADED_MESSAGE } = require('./core-bootstrap');
|
|
26
|
+
const {
|
|
27
|
+
appendLauncherStatus, claimLauncherRestart, consumeIntentionalStopMarker,
|
|
28
|
+
isAbnormalCoreExit, restartDelayMs,
|
|
29
|
+
} = require('./launcher-restart-policy.cjs');
|
|
30
|
+
const { recordRuntimeExit } = require('./runtime-exit-ledger.cjs');
|
|
31
|
+
const { prepareManagedNodeRuntime } = require('./node-runtime');
|
|
32
|
+
const { unsupportedNodeMessage } = require('./node-version');
|
|
33
|
+
const { repairConfiguredNativeModules } = require('./native-module-repair');
|
|
34
|
+
const { acquireSharedRuntimeLease, tryAcquireUpdateLease } = require('./update-lease');
|
|
35
|
+
const { runExplicitUpdate, runUpdateNotice } = require('./update-notice');
|
|
36
|
+
const {
|
|
37
|
+
ensurePrivateDirectory,
|
|
38
|
+
securePrivateFile,
|
|
39
|
+
writePrivateFile,
|
|
40
|
+
} = require('./private-paths');
|
|
41
|
+
|
|
42
|
+
const PKG = path.resolve(__dirname, '..');
|
|
43
|
+
const ARGS = process.argv.slice(2);
|
|
44
|
+
const CORE_LOAD_TIMEOUT_MS = 30_000;
|
|
45
|
+
|
|
46
|
+
function normalizeWindowsPath(value) {
|
|
47
|
+
return path.win32.normalize(value).replace(/\\+$/u, '').toLowerCase();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function resolveLauncherPrivatePaths(options = {}) {
|
|
51
|
+
const env = options.env || process.env;
|
|
52
|
+
const platform = options.platform || process.platform;
|
|
53
|
+
const configuredHome = String(env.BLUN_HOME || '').trim();
|
|
54
|
+
if (platform !== 'win32') {
|
|
55
|
+
const userHome = (options.homedir || os.homedir)();
|
|
56
|
+
return {
|
|
57
|
+
blunHome: configuredHome ? path.resolve(configuredHome) : path.join(userHome, '.blun'),
|
|
58
|
+
userHome,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const fsImpl = options.fsImpl || fs;
|
|
63
|
+
const userInfo = (options.userInfo || os.userInfo)();
|
|
64
|
+
const reportedHome = String(userInfo?.homedir || '').trim();
|
|
65
|
+
if (!path.win32.isAbsolute(reportedHome) || reportedHome.startsWith('\\\\')) {
|
|
66
|
+
throw new Error('Das lokale Windows-Benutzerprofil konnte nicht sicher bestimmt werden.');
|
|
67
|
+
}
|
|
68
|
+
const resolvedHome = path.win32.resolve(reportedHome);
|
|
69
|
+
const stat = fsImpl.lstatSync(resolvedHome);
|
|
70
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
71
|
+
throw new Error('Das lokale Windows-Benutzerprofil ist kein vertrauenswuerdiges Verzeichnis.');
|
|
72
|
+
}
|
|
73
|
+
const realpath = fsImpl.realpathSync.native || fsImpl.realpathSync;
|
|
74
|
+
const userHome = path.win32.normalize(realpath(resolvedHome));
|
|
75
|
+
const volumeRoot = path.win32.parse(userHome).root;
|
|
76
|
+
if (!/^[A-Za-z]:\\$/u.test(volumeRoot)
|
|
77
|
+
|| normalizeWindowsPath(userHome) !== normalizeWindowsPath(resolvedHome)) {
|
|
78
|
+
throw new Error('Das lokale Windows-Benutzerprofil ist kein kanonischer lokaler Pfad.');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const blunHome = path.win32.join(userHome, '.blun');
|
|
82
|
+
if (configuredHome
|
|
83
|
+
&& normalizeWindowsPath(path.win32.resolve(configuredHome)) !== normalizeWindowsPath(blunHome)) {
|
|
84
|
+
throw new Error(`BLUN_HOME muss unter Windows exakt im Windows-Benutzerprofil liegen: ${blunHome}`);
|
|
85
|
+
}
|
|
86
|
+
return { blunHome, userHome };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readPackageVersion() {
|
|
90
|
+
const { version } = JSON.parse(fs.readFileSync(path.join(PKG, 'package.json'), 'utf8'));
|
|
91
|
+
if (typeof version !== 'string' || version.length === 0) {
|
|
92
|
+
throw new Error('Ungueltige Paketversion in package.json');
|
|
93
|
+
}
|
|
94
|
+
return version;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function shouldDetachProtectedCore(options = {}) {
|
|
98
|
+
const platform = options.platform || process.platform;
|
|
99
|
+
const stdinIsTTY = Object.hasOwn(options, 'stdinIsTTY')
|
|
100
|
+
? options.stdinIsTTY
|
|
101
|
+
: process.stdin.isTTY;
|
|
102
|
+
return platform !== 'win32' || stdinIsTTY !== true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function spawnProtectedCore(args, env, cwd) {
|
|
106
|
+
const child = spawn(
|
|
107
|
+
process.execPath,
|
|
108
|
+
[path.join(PKG, 'bin', 'core-bootstrap.js'), ...args],
|
|
109
|
+
{
|
|
110
|
+
cwd,
|
|
111
|
+
detached: shouldDetachProtectedCore(),
|
|
112
|
+
env,
|
|
113
|
+
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
114
|
+
windowsHide: true,
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
let loadedSettled = false;
|
|
118
|
+
let completedSettled = false;
|
|
119
|
+
let operatorSignal;
|
|
120
|
+
let resolveLoaded;
|
|
121
|
+
let resolveCompleted;
|
|
122
|
+
const loadTimer = setTimeout(() => {
|
|
123
|
+
try {
|
|
124
|
+
child.kill();
|
|
125
|
+
} catch {}
|
|
126
|
+
}, CORE_LOAD_TIMEOUT_MS);
|
|
127
|
+
const loaded = new Promise((resolve) => { resolveLoaded = resolve; });
|
|
128
|
+
const completed = new Promise((resolve) => { resolveCompleted = resolve; });
|
|
129
|
+
const settleLoaded = (value) => {
|
|
130
|
+
if (loadedSettled) return;
|
|
131
|
+
loadedSettled = true;
|
|
132
|
+
clearTimeout(loadTimer);
|
|
133
|
+
resolveLoaded(value);
|
|
134
|
+
};
|
|
135
|
+
const settleCompleted = (result) => {
|
|
136
|
+
if (completedSettled) return;
|
|
137
|
+
completedSettled = true;
|
|
138
|
+
for (const [signal, handler] of signalHandlers) process.removeListener(signal, handler);
|
|
139
|
+
settleLoaded(false);
|
|
140
|
+
resolveCompleted(operatorSignal === undefined ? result : { ...result, signal: operatorSignal });
|
|
141
|
+
};
|
|
142
|
+
const signalHandlers = new Map();
|
|
143
|
+
for (const signal of ['SIGHUP', 'SIGINT', 'SIGTERM']) {
|
|
144
|
+
const handler = () => {
|
|
145
|
+
operatorSignal = signal;
|
|
146
|
+
try {
|
|
147
|
+
child.kill(signal);
|
|
148
|
+
} catch {}
|
|
149
|
+
};
|
|
150
|
+
signalHandlers.set(signal, handler);
|
|
151
|
+
process.once(signal, handler);
|
|
152
|
+
}
|
|
153
|
+
child.on('message', (message) => {
|
|
154
|
+
if (message?.type !== CORE_LOADED_MESSAGE) return;
|
|
155
|
+
settleLoaded(true);
|
|
156
|
+
try {
|
|
157
|
+
child.disconnect();
|
|
158
|
+
} catch {}
|
|
159
|
+
});
|
|
160
|
+
child.once('error', (error) => settleCompleted({ error }));
|
|
161
|
+
child.once('exit', (code, signal) => settleCompleted({ code, signal }));
|
|
162
|
+
return { child, completed, loaded };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function runInformationalCore(args, env, cwd, releaseNotice) {
|
|
166
|
+
const core = spawnProtectedCore(args, env, cwd);
|
|
167
|
+
await core.loaded;
|
|
168
|
+
await releaseNotice();
|
|
169
|
+
const result = await core.completed;
|
|
170
|
+
if (result.error) throw result.error;
|
|
171
|
+
return Number.isInteger(result.code) ? result.code : exitCodeForChild(result, false);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function superviseProtectedCore(args, env, cwd, releaseNotice) {
|
|
175
|
+
let noticeReleased = false;
|
|
176
|
+
for (;;) {
|
|
177
|
+
const core = spawnProtectedCore(args, env, cwd);
|
|
178
|
+
const loaded = await core.loaded;
|
|
179
|
+
if (!noticeReleased) {
|
|
180
|
+
noticeReleased = true;
|
|
181
|
+
await releaseNotice();
|
|
182
|
+
}
|
|
183
|
+
const result = await core.completed;
|
|
184
|
+
const exitCode = result.error ? 1 : exitCodeForChild(result, loaded);
|
|
185
|
+
const intentionalStop = consumeIntentionalStopMarker({
|
|
186
|
+
homeDir: env.BLUN_HOME, childPid: core.child.pid, nowMs: Date.now(),
|
|
187
|
+
});
|
|
188
|
+
recordRuntimeExit({
|
|
189
|
+
homeDir: env.BLUN_HOME, source: 'launcher', kind: result.error ? 'child-error' : 'child-exit',
|
|
190
|
+
exitCode, signal: result.signal, phase: loaded ? 'runtime' : 'startup',
|
|
191
|
+
cliVersion: env.BLUN_PUBLIC_PACKAGE_VERSION, profile: env.BLUN_PROFILE,
|
|
192
|
+
loaded, childPid: core.child.pid, error: result.error,
|
|
193
|
+
});
|
|
194
|
+
if (intentionalStop || !isAbnormalCoreExit(result, loaded)) {
|
|
195
|
+
if (result.error) throw result.error;
|
|
196
|
+
return exitCode;
|
|
197
|
+
}
|
|
198
|
+
const claim = claimLauncherRestart({ homeDir: env.BLUN_HOME, nowMs: Date.now() });
|
|
199
|
+
try {
|
|
200
|
+
appendLauncherStatus({
|
|
201
|
+
homeDir: env.BLUN_HOME,
|
|
202
|
+
stateDir: env.BLUN_TELEGRAM_STATE_DIR
|
|
203
|
+
|| path.join(env.BLUN_SHARED_HOME || env.BLUN_HOME, 'channels', 'telegram'),
|
|
204
|
+
nowMs: Date.now(), attempt: claim.attempt, limit: claim.limit, exitCode, signal: result.signal,
|
|
205
|
+
kind: claim.allowed ? 'launcher-restart' : 'launcher-restart-exhausted',
|
|
206
|
+
});
|
|
207
|
+
} catch {}
|
|
208
|
+
if (!claim.allowed) {
|
|
209
|
+
if (result.error) throw result.error;
|
|
210
|
+
return exitCode;
|
|
211
|
+
}
|
|
212
|
+
await new Promise((resolve) => setTimeout(resolve, restartDelayMs(claim.attempt)));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function exitCodeForChild(result, loaded) {
|
|
217
|
+
if (!loaded && result.code === 75) return 0;
|
|
218
|
+
if (Number.isInteger(result.code)) return result.code;
|
|
219
|
+
if (result.signal === 'SIGINT') return 130;
|
|
220
|
+
if (result.signal === 'SIGTERM') return 143;
|
|
221
|
+
return 1;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function installTelegramForProfile(packageRoot, blunDir) {
|
|
225
|
+
installManagedTelegramPlugin({ packageRoot, blunDir });
|
|
226
|
+
const channelsDir = path.join(blunDir, 'channels');
|
|
227
|
+
const channelDir = path.join(channelsDir, 'telegram');
|
|
228
|
+
ensurePrivateDirectory(channelsDir);
|
|
229
|
+
ensurePrivateDirectory(channelDir);
|
|
230
|
+
const envFile = path.join(channelDir, '.env');
|
|
231
|
+
if (!fs.existsSync(envFile)) {
|
|
232
|
+
writePrivateFile(envFile, 'BLUN_TELEGRAM_BOT_TOKEN=HIER_DEIN_BOT_TOKEN_EINFUEGEN\n');
|
|
233
|
+
} else {
|
|
234
|
+
securePrivateFile(envFile);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function spawnManagedLauncher(binary, cwd) {
|
|
239
|
+
return new Promise((resolve, reject) => {
|
|
240
|
+
const child = spawn(binary, process.argv.slice(1), {
|
|
241
|
+
cwd,
|
|
242
|
+
env: process.env,
|
|
243
|
+
stdio: 'inherit',
|
|
244
|
+
windowsHide: true,
|
|
245
|
+
});
|
|
246
|
+
child.once('error', reject);
|
|
247
|
+
child.once('exit', (code, signal) => {
|
|
248
|
+
if (Number.isInteger(code)) {
|
|
249
|
+
resolve(code);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (signal === 'SIGINT') {
|
|
253
|
+
resolve(130);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (signal === 'SIGTERM') {
|
|
257
|
+
resolve(143);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
resolve(1);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function runLauncher(options = {}) {
|
|
266
|
+
const callerCwd = process.cwd();
|
|
267
|
+
const mode = options.mode || launcherModeFromArgv(process.argv);
|
|
268
|
+
const explicitUpdateRequest = isNpmPackageUpdateRequest(ARGS);
|
|
269
|
+
const explicitToolsRequest = String(ARGS[0] || '').toLowerCase() === 'tools';
|
|
270
|
+
const noticeResult = options.skipUpdateNotice === true
|
|
271
|
+
? { acquired: true, lease: { assertOwned() {}, async release() {} } }
|
|
272
|
+
: options.noticeLease
|
|
273
|
+
? { acquired: true, lease: options.noticeLease }
|
|
274
|
+
: await tryAcquireUpdateLease({ packageRoot: PKG, role: 'notice' });
|
|
275
|
+
if (!noticeResult.acquired) {
|
|
276
|
+
if (explicitUpdateRequest) {
|
|
277
|
+
process.stderr.write('Das Update konnte nicht verifiziert werden.\n');
|
|
278
|
+
process.exitCode = 1;
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const noticeLease = noticeResult.lease;
|
|
283
|
+
let runtimeLease = options.runtimeLease;
|
|
284
|
+
let noticeReleased = false;
|
|
285
|
+
const releaseNotice = async () => {
|
|
286
|
+
if (noticeReleased) return;
|
|
287
|
+
noticeReleased = true;
|
|
288
|
+
await noticeLease.release();
|
|
289
|
+
await runtimeLease?.release();
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
if (!runtimeLease && !explicitUpdateRequest) {
|
|
294
|
+
const installProbe = await tryAcquireUpdateLease({ packageRoot: PKG, role: 'install' });
|
|
295
|
+
if (!installProbe.acquired) {
|
|
296
|
+
process.stderr.write(explicitUpdateRequest
|
|
297
|
+
? 'Das Update konnte nicht verifiziert werden.\n'
|
|
298
|
+
: 'Der geschuetzte BLUN-Start ist gerade nicht verfuegbar.\n');
|
|
299
|
+
process.exitCode = 1;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
await installProbe.lease.release();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (explicitToolsRequest) {
|
|
306
|
+
const action = String(ARGS[1] || '').toLowerCase();
|
|
307
|
+
const available = availableStandardToolNames({ packageRoot: PKG });
|
|
308
|
+
if (action === 'list' && ARGS.length === 2) {
|
|
309
|
+
process.stdout.write(`${available.join('\n')}\n`);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (action === 'enable' && ARGS.length > 2) {
|
|
313
|
+
const privatePaths = resolveLauncherPrivatePaths();
|
|
314
|
+
const selected = [...new Set(ARGS.slice(2))];
|
|
315
|
+
const unknown = selected.find((name) => !available.includes(name));
|
|
316
|
+
if (unknown) {
|
|
317
|
+
process.stderr.write(`Unbekanntes Standard-Tool: ${unknown}\n`);
|
|
318
|
+
process.exitCode = 1;
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const mcpNames = selected.filter((name) => name !== 'telegram');
|
|
322
|
+
if (mcpNames.length > 0) {
|
|
323
|
+
seedStandardTools({
|
|
324
|
+
packageRoot: PKG,
|
|
325
|
+
blunDir: privatePaths.blunHome,
|
|
326
|
+
names: mcpNames,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
if (selected.includes('telegram')) {
|
|
330
|
+
installTelegramForProfile(PKG, privatePaths.blunHome);
|
|
331
|
+
}
|
|
332
|
+
process.stdout.write(`${selected.join('\n')}\n`);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
process.stderr.write('Usage: blun tools list | blun tools enable <name...>\n');
|
|
336
|
+
process.exitCode = 1;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (ARGS.some((arg) => arg === '--version' || arg === '-V')) {
|
|
340
|
+
process.stdout.write(`${readPackageVersion()}\n`);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (explicitUpdateRequest) {
|
|
345
|
+
const privatePaths = resolveLauncherPrivatePaths();
|
|
346
|
+
const result = await runExplicitUpdate({
|
|
347
|
+
currentVersion: readPackageVersion(),
|
|
348
|
+
env: process.env,
|
|
349
|
+
stdin: process.stdin,
|
|
350
|
+
stdout: process.stdout,
|
|
351
|
+
stderr: process.stderr,
|
|
352
|
+
blunDir: privatePaths.blunHome,
|
|
353
|
+
packageRoot: PKG,
|
|
354
|
+
noticeLease,
|
|
355
|
+
request: ARGS[0],
|
|
356
|
+
});
|
|
357
|
+
if (result.kind === 'failed') process.exitCode = result.exitCode || 1;
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (isStandaloneHelpRequest(ARGS)) {
|
|
362
|
+
const compatibilityError = unsupportedNodeMessage(process.versions.node, process.platform);
|
|
363
|
+
if (compatibilityError !== null) {
|
|
364
|
+
process.stderr.write(`${compatibilityError}\n`);
|
|
365
|
+
process.exitCode = 1;
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
process.stdout.write(launcherHelpText());
|
|
369
|
+
const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
|
|
370
|
+
const informationalEnv = { ...process.env };
|
|
371
|
+
delete informationalEnv.BLUN_HOME;
|
|
372
|
+
env.BLUN_HOME = resolveLauncherPrivatePaths({ env: informationalEnv }).blunHome;
|
|
373
|
+
process.exitCode = await runInformationalCore(ARGS, env, callerCwd, releaseNotice);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
let privatePaths;
|
|
378
|
+
const getPrivatePaths = () => {
|
|
379
|
+
privatePaths ||= resolveLauncherPrivatePaths();
|
|
380
|
+
return privatePaths;
|
|
381
|
+
};
|
|
382
|
+
const nodeRuntime = await prepareManagedNodeRuntime({
|
|
383
|
+
getBlunDir: () => getPrivatePaths().blunHome,
|
|
384
|
+
});
|
|
385
|
+
if (nodeRuntime.kind === 'failed') {
|
|
386
|
+
process.stderr.write(`${nodeRuntime.message}\n`);
|
|
387
|
+
process.exitCode = 1;
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (nodeRuntime.kind === 'managed') {
|
|
391
|
+
process.stdout.write('BLUN hat die passende Node.js-Laufzeit eingerichtet.\n');
|
|
392
|
+
await releaseNotice();
|
|
393
|
+
process.exitCode = await spawnManagedLauncher(nodeRuntime.binary, callerCwd);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (ARGS.some((arg) => arg === '--help' || arg === '-h')) {
|
|
398
|
+
process.stdout.write(launcherHelpText());
|
|
399
|
+
const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
|
|
400
|
+
const informationalEnv = { ...process.env };
|
|
401
|
+
delete informationalEnv.BLUN_HOME;
|
|
402
|
+
env.BLUN_HOME = resolveLauncherPrivatePaths({ env: informationalEnv }).blunHome;
|
|
403
|
+
process.exitCode = await superviseProtectedCore(ARGS, env, callerCwd, releaseNotice);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const blunDir = getPrivatePaths().blunHome;
|
|
408
|
+
ensurePrivateDirectory(blunDir);
|
|
409
|
+
|
|
410
|
+
// --- 1. Oeffentlicher npm-Update-Dialog. -------------------------------
|
|
411
|
+
const updateResult = options.skipUpdateNotice === true
|
|
412
|
+
? { kind: 'continue', reason: 'notice_busy' }
|
|
413
|
+
: await runUpdateNotice({
|
|
414
|
+
currentVersion: readPackageVersion(),
|
|
415
|
+
argv: ARGS,
|
|
416
|
+
env: process.env,
|
|
417
|
+
stdin: process.stdin,
|
|
418
|
+
stdout: process.stdout,
|
|
419
|
+
stderr: process.stderr,
|
|
420
|
+
blunDir,
|
|
421
|
+
packageRoot: PKG,
|
|
422
|
+
noticeLease,
|
|
423
|
+
});
|
|
424
|
+
if (updateResult.kind === 'installed' || updateResult.kind === 'update_in_progress') return;
|
|
425
|
+
|
|
426
|
+
if (!runtimeLease) {
|
|
427
|
+
const runtimeResult = await acquireSharedRuntimeLease({ packageRoot: PKG });
|
|
428
|
+
if (!runtimeResult.acquired) {
|
|
429
|
+
process.stderr.write('Der geschuetzte BLUN-Start ist gerade nicht verfuegbar.\n');
|
|
430
|
+
process.exitCode = 1;
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
runtimeLease = runtimeResult.lease;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// --- 2. Bestehende Konfiguration absichern -------------------------------
|
|
437
|
+
// Ein frischer Start bleibt absichtlich ohne statischen Anbieter. /login
|
|
438
|
+
// provisioniert King ueber den BLUN-OAuth-Server.
|
|
439
|
+
const cfgFile = path.join(blunDir, 'config.toml');
|
|
440
|
+
if (fs.existsSync(cfgFile)) securePrivateFile(cfgFile);
|
|
441
|
+
require('./verify-bundled-agent-sources.cjs').verifyBundledAgentSources(PKG);
|
|
442
|
+
|
|
443
|
+
// --- 3. BLUN-Standard-MCPs additiv im Benutzerprofil einrichten ---------
|
|
444
|
+
const standardMcpNames = availableStandardToolNames({ packageRoot: PKG })
|
|
445
|
+
.filter((name) => name !== 'telegram');
|
|
446
|
+
seedStandardTools({
|
|
447
|
+
packageRoot: PKG,
|
|
448
|
+
blunDir,
|
|
449
|
+
names: standardMcpNames,
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// --- 4. Eigenes BLUN-Grunddesign als einzigen Standard-Skill installieren
|
|
453
|
+
seedStandardDesignSkill({ packageRoot: PKG, blunDir });
|
|
454
|
+
installManagedAgentSpinePlugin({ packageRoot: PKG, blunDir });
|
|
455
|
+
|
|
456
|
+
// --- 5. Telegram-Plugin + Token-Vorlage (nur King) -----------------------
|
|
457
|
+
if (mode === LAUNCHER_MODES.KING) {
|
|
458
|
+
installTelegramForProfile(PKG, blunDir);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const nativeModules = repairConfiguredNativeModules({
|
|
462
|
+
blunDir,
|
|
463
|
+
callerCwd,
|
|
464
|
+
onRepairStart() {
|
|
465
|
+
process.stdout.write('BLUN richtet native Node.js-Module fuer die neue Laufzeit ein.\n');
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
if (nativeModules.repaired.length > 0) {
|
|
469
|
+
process.stdout.write('Native Node.js-Module sind wieder einsatzbereit.\n');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// --- 6. Start -----------------------------------------------------------
|
|
473
|
+
const env = createLauncherEnvironment(process.env, mode, readPackageVersion(), blunDir);
|
|
474
|
+
env.BLUN_HOME = blunDir;
|
|
475
|
+
process.exitCode = await superviseProtectedCore(
|
|
476
|
+
ARGS,
|
|
477
|
+
env,
|
|
478
|
+
callerCwd,
|
|
479
|
+
releaseNotice,
|
|
480
|
+
);
|
|
481
|
+
} finally {
|
|
482
|
+
await releaseNotice();
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
module.exports = {
|
|
487
|
+
resolveLauncherPrivatePaths,
|
|
488
|
+
runLauncher,
|
|
489
|
+
shouldDetachProtectedCore,
|
|
490
|
+
spawnManagedLauncher,
|
|
491
|
+
};
|