blun-king-cli 9.1.602 → 9.1.604

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.
Files changed (124) hide show
  1. package/CHANGELOG.md +118 -99
  2. package/LIESMICH.txt +74 -57
  3. package/README.md +119 -76
  4. package/bin/agentspine-king-goal-inbox.mjs +127 -127
  5. package/bin/agentspine-king-goal-intake.mjs +106 -106
  6. package/bin/agentspine-king-host-runner.mjs +109 -109
  7. package/bin/agentspine-king-snapshot-policy.cjs +80 -80
  8. package/bin/agentspine-king-status-policy.mjs +226 -226
  9. package/bin/agentspine-king-worker-host.mjs +160 -160
  10. package/bin/core-bootstrap.js +77 -77
  11. package/bin/launcher-mode.js +93 -93
  12. package/bin/launcher-restart-policy.cjs +131 -131
  13. package/bin/launcher-runtime.js +491 -491
  14. package/bin/managed-node.js +186 -186
  15. package/bin/managed-plugin-selection.cjs +115 -115
  16. package/bin/native-module-repair.js +201 -201
  17. package/bin/node-runtime.js +34 -34
  18. package/bin/node-version.js +49 -49
  19. package/bin/plugin-bootstrap.js +162 -162
  20. package/bin/private-paths.js +1606 -1606
  21. package/bin/runtime-exit-ledger.cjs +144 -144
  22. package/bin/runtime-exit-ledger.d.cts +23 -23
  23. package/bin/standard-tools-bootstrap.js +272 -272
  24. package/bin/turn-thinking-policy.cjs +82 -82
  25. package/bin/update-copy.js +200 -200
  26. package/bin/update-lease.js +597 -597
  27. package/bin/update-notice.js +1568 -1568
  28. package/bin/verify-agent-behavior.cjs +122 -122
  29. package/bin/verify-agent-components.cjs +104 -104
  30. package/bin/verify-bundled-agent-sources.cjs +98 -76
  31. package/bin/windows-node-crash-dump.cjs +289 -289
  32. package/blun.mjs +8041 -6407
  33. package/codebase-index/codebase_index.py +470 -470
  34. package/dist-web/assets/{CodeBlockNode-CXnTLMNr.js → CodeBlockNode-TKKkJX8L.js} +1 -1
  35. 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
  36. package/dist-web/assets/DesignSystemView-DwK1aQMo.js +12 -0
  37. package/dist-web/assets/DesignSystemView-szjTj7ns.css +1 -0
  38. package/dist-web/assets/{Tooltip-CFQEzqC-.js → Tooltip-CTWv7WCU.js} +1 -1
  39. package/dist-web/assets/{abnfDiagram-N423BO3Z-Z1APZG5m.js → abnfDiagram-N423BO3Z-DA8V8Zjh.js} +1 -1
  40. package/dist-web/assets/{arc-C4VyWloT.js → arc-YtddKbdY.js} +1 -1
  41. package/dist-web/assets/{architectureDiagram-T3A2C74G-DpddbUcM.js → architectureDiagram-T3A2C74G-CGEiObmo.js} +1 -1
  42. package/dist-web/assets/{blockDiagram-VBNYF7ZC-BqccOSMr.js → blockDiagram-VBNYF7ZC-Bd5Vt50e.js} +1 -1
  43. package/dist-web/assets/{c4Diagram-5PPSVZJV-OHAcJrTy.js → c4Diagram-5PPSVZJV-D2ApPM54.js} +1 -1
  44. package/dist-web/assets/channel-BnluexuN.js +1 -0
  45. package/dist-web/assets/{chunk-2GRJ4B5K-DqJHs8Fw.js → chunk-2GRJ4B5K-BDcIEZSl.js} +1 -1
  46. package/dist-web/assets/{chunk-2Q5K7J3B-B1nCd91o.js → chunk-2Q5K7J3B-6aSabCX7.js} +1 -1
  47. package/dist-web/assets/{chunk-5RXB4S5H-neG70kZx.js → chunk-5RXB4S5H-BFRQvIJS.js} +1 -1
  48. package/dist-web/assets/{chunk-5VM5RSS4-l7biGqeH.js → chunk-5VM5RSS4-ChAa-DNY.js} +1 -1
  49. package/dist-web/assets/{chunk-6Q2QTUOP-BO-F11Gs.js → chunk-6Q2QTUOP-BIfrb2Jc.js} +1 -1
  50. package/dist-web/assets/{chunk-GF5L2VYU-BtC69fw0.js → chunk-GF5L2VYU-BR0gvtWI.js} +1 -1
  51. package/dist-web/assets/{chunk-JWPE2WC7-DIOubEqr.js → chunk-JWPE2WC7-BgXKsx5E.js} +1 -1
  52. package/dist-web/assets/{chunk-KBJHAD2P-DDUeH_Ba.js → chunk-KBJHAD2P-I8mrZhpz.js} +1 -1
  53. package/dist-web/assets/{chunk-RYQCIY6F-B9gFMVbS.js → chunk-RYQCIY6F-BMPaem92.js} +1 -1
  54. package/dist-web/assets/{chunk-XXDRQBXY-Bn1Xz6TQ.js → chunk-XXDRQBXY-COy0J-0j.js} +1 -1
  55. package/dist-web/assets/classDiagram-JCYQIIEL-BliFdSD9.js +1 -0
  56. package/dist-web/assets/classDiagram-v2-OCEON4UE-BliFdSD9.js +1 -0
  57. package/dist-web/assets/{cose-bilkent-JH36ORCC-DqigO3T4.js → cose-bilkent-JH36ORCC-MosTTRdo.js} +1 -1
  58. package/dist-web/assets/{cynefin-VYW2F7L2-rpD93tQ-.js → cynefin-VYW2F7L2-5tjPgrkP.js} +1 -1
  59. package/dist-web/assets/{cynefinDiagram-MW4NZA55-DbTqnkVJ.js → cynefinDiagram-MW4NZA55-CbDcdInR.js} +1 -1
  60. package/dist-web/assets/{dagre-VZM6K2ZE-DcM8aEVY.js → dagre-VZM6K2ZE-YaBom1ZH.js} +1 -1
  61. package/dist-web/assets/{diagram-7IWD3JNH-UymgsBNa.js → diagram-7IWD3JNH-Da4m-Hgz.js} +1 -1
  62. package/dist-web/assets/{diagram-B4RE2ZJO-BiYu5WPF.js → diagram-B4RE2ZJO-CAUUegxg.js} +1 -1
  63. package/dist-web/assets/{diagram-LBJQPF4R-BD3kkgOO.js → diagram-LBJQPF4R-o21mWKG4.js} +1 -1
  64. package/dist-web/assets/{diagram-Q27KOJAE-D1mYQ_cI.js → diagram-Q27KOJAE-BA3z3sXf.js} +1 -1
  65. package/dist-web/assets/{diagram-UB23O5K3-BtX8u6HG.js → diagram-UB23O5K3-CGRdSSDK.js} +1 -1
  66. package/dist-web/assets/{ebnfDiagram-BXEA7PRR-CBAnFkRF.js → ebnfDiagram-BXEA7PRR-Bw-iU9FP.js} +1 -1
  67. package/dist-web/assets/{erDiagram-JOGREHBK-3vKcWsxm.js → erDiagram-JOGREHBK-BwM1yiR0.js} +1 -1
  68. package/dist-web/assets/{flowDiagram-UKHOOZJN-D9wBbHm2.js → flowDiagram-UKHOOZJN-z05eOR1Q.js} +1 -1
  69. package/dist-web/assets/{ganttDiagram-PKOTCBZU-Cd_dViF-.js → ganttDiagram-PKOTCBZU-BMUubNTl.js} +1 -1
  70. package/dist-web/assets/{gitGraphDiagram-DS77QQ5N-BiEEtTo7.js → gitGraphDiagram-DS77QQ5N-Dccbi1wU.js} +1 -1
  71. package/dist-web/assets/{index-DdEakFJM.js → index-BBbC5LRF.js} +1 -1
  72. package/dist-web/assets/{index-BmKj7CB1.css → index-C9eDH3u-.css} +1 -1
  73. package/dist-web/assets/{index-Ba1OvLjf.js → index-D48p0M2S.js} +2 -2
  74. package/dist-web/assets/{index-CiPA8Kex.js → index-h0KEOmkU.js} +14 -14
  75. package/dist-web/assets/{index10-CNr4U-Nw.js → index10-Qbuw1N6u.js} +1 -1
  76. package/dist-web/assets/{index11-Dto_yZNz.js → index11-g8v025wN.js} +1 -1
  77. package/dist-web/assets/{index5-B2PldaO2.js → index5-BYNcV8BY.js} +1 -1
  78. package/dist-web/assets/{index6-CBdZmmyx.js → index6-BetANtnV.js} +1 -1
  79. package/dist-web/assets/{index7-Dz9oCsUF.js → index7-BcPbgCKN.js} +1 -1
  80. package/dist-web/assets/{index8-B6k25jtU.js → index8-B1lp-RvF.js} +2 -2
  81. package/dist-web/assets/{infoDiagram-6WML65LV-D4ph2sIH.js → infoDiagram-6WML65LV-BF_pbUnN.js} +1 -1
  82. package/dist-web/assets/{ishikawaDiagram-WSZJBQD7-DjKjbMm9.js → ishikawaDiagram-WSZJBQD7-Dedv4urS.js} +1 -1
  83. package/dist-web/assets/{journeyDiagram-NVQOT4AX-DttHDfPQ.js → journeyDiagram-NVQOT4AX-B1Ex3b6o.js} +1 -1
  84. package/dist-web/assets/{kanban-definition-27J2QSJJ-C5gB-W0d.js → kanban-definition-27J2QSJJ-BtCGliBH.js} +1 -1
  85. package/dist-web/assets/{linear-BRGe4-d6.js → linear-ByItc8Qr.js} +1 -1
  86. package/dist-web/assets/{mermaid.core-D-9Sx1No.js → mermaid.core-B1M2O7nN.js} +4 -4
  87. package/dist-web/assets/{mindmap-definition-FAOFIHXS-Cg_hoVIq.js → mindmap-definition-FAOFIHXS-D0UHKUyj.js} +1 -1
  88. package/dist-web/assets/{pegDiagram-VL7TDLO6-CSX9hitD.js → pegDiagram-VL7TDLO6-D7zYCVmG.js} +1 -1
  89. package/dist-web/assets/{pieDiagram-7S7Q4E2Y-0L0mxahZ.js → pieDiagram-7S7Q4E2Y-Bd0i9CHa.js} +1 -1
  90. package/dist-web/assets/{quadrantDiagram-CIZ2JOQS-U483z0er.js → quadrantDiagram-CIZ2JOQS-MG-Ug47d.js} +1 -1
  91. package/dist-web/assets/{railroadDiagram-AXF67PYL-D80a2_DJ.js → railroadDiagram-AXF67PYL-AZdl90Gj.js} +1 -1
  92. package/dist-web/assets/{requirementDiagram-LRYGKXZP-CeyHfq8t.js → requirementDiagram-LRYGKXZP-DjplF9kt.js} +1 -1
  93. package/dist-web/assets/{sankeyDiagram-W5VNT64P-CCISkkrR.js → sankeyDiagram-W5VNT64P-Bi9Zgfjs.js} +1 -1
  94. package/dist-web/assets/{sequenceDiagram-SI44F4Z6-jQfm2XS7.js → sequenceDiagram-SI44F4Z6-ChEpdCvb.js} +1 -1
  95. package/dist-web/assets/{sizeCapture-X5ZJPWSS-lRZsmCoS.js → sizeCapture-X5ZJPWSS-B5RFUdaa.js} +1 -1
  96. package/dist-web/assets/{stateDiagram-OKZ733FA-ClCgTdUm.js → stateDiagram-OKZ733FA-BkRblSKp.js} +1 -1
  97. package/dist-web/assets/stateDiagram-v2-UEYNNEHI-fVkQcACW.js +1 -0
  98. package/dist-web/assets/{swimlanes-SLNWSIFB-BCdOUN05.js → swimlanes-SLNWSIFB-DLXH6Zhc.js} +2 -2
  99. package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-Bg-BiZJQ.js +8 -0
  100. package/dist-web/assets/{timeline-definition-Z64GVDOM-DpWutSKa.js → timeline-definition-Z64GVDOM-C_N88siO.js} +1 -1
  101. package/dist-web/assets/{vennDiagram-T6HMQDX7-DVC5JVsc.js → vennDiagram-T6HMQDX7-DMsnvDt0.js} +1 -1
  102. package/dist-web/assets/{vue.runtime.esm-bundler-BwwRtQwQ.js → vue.runtime.esm-bundler-B-3uIy85.js} +1 -1
  103. package/dist-web/assets/{wardleyDiagram-T6FBY63Y-BK2mrq7L.js → wardleyDiagram-T6FBY63Y-C9SJIPzq.js} +1 -1
  104. package/dist-web/assets/{xychartDiagram-ELKLHX3M-DUJu9F7-.js → xychartDiagram-ELKLHX3M-DZLWA6Go.js} +1 -1
  105. package/dist-web/index.html +32 -32
  106. package/dist-web/vis/index.html.gz +0 -0
  107. package/package.json +1 -1
  108. package/scripts/fix-node-pty-perms.js +53 -53
  109. package/standard-skills/blun-app-design-system/SKILL.md +30 -30
  110. package/standard-skills/blun-app-design-system/provenance.json +17 -17
  111. package/standard-skills/blun-app-design-system/references/tokens.md +48 -48
  112. package/standard-skills/blun-app-design-system/scripts/check-blun-design.mjs +49 -49
  113. package/telegram-plugin/blun.plugin.json +20 -20
  114. package/telegram-plugin/commands/access.md +37 -37
  115. package/telegram-plugin/dist/bridge.mjs +117 -100
  116. package/telegram-plugin/dist/mcp-server.mjs +185 -161
  117. package/worker-host.mjs +1458 -1022
  118. package/dist-web/assets/DesignSystemView-6HgAUEmC.css +0 -1
  119. package/dist-web/assets/DesignSystemView-BbMOu-XO.js +0 -12
  120. package/dist-web/assets/channel-B2VOQOI-.js +0 -1
  121. package/dist-web/assets/classDiagram-JCYQIIEL-BA1TjAxj.js +0 -1
  122. package/dist-web/assets/classDiagram-v2-OCEON4UE-BA1TjAxj.js +0 -1
  123. package/dist-web/assets/stateDiagram-v2-UEYNNEHI-fi_lrTAx.js +0 -1
  124. package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-BUd_uYK3.js +0 -8
@@ -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
+ };