blun-king-cli 9.0.0 → 9.0.1
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/LIESMICH.txt +1 -7
- package/README.md +4 -16
- package/bin/blun.js +248 -160
- package/bin/core-bootstrap.js +47 -0
- package/bin/king.js +277 -1
- package/bin/launcher-mode.js +2 -1
- package/bin/launcher-runtime.js +221 -0
- package/bin/plugin-bootstrap.js +0 -0
- package/bin/private-paths.js +0 -0
- package/bin/update-lease.js +399 -0
- package/bin/update-notice.js +1094 -0
- package/blun.mjs +4060 -6667
- package/package.json +3 -10
- package/skills/screenshot-lesen/SKILL.md +0 -1
- package/skills/web-lesen/SKILL.md +0 -1
- package/telegram-plugin/dist/bridge.mjs +1 -21
- package/mnemo/access_routes.js +0 -692
- package/mnemo/agent_governance.js +0 -4242
- package/mnemo/agent_mail.js +0 -901
- package/mnemo/bootstrap_auto.js +0 -137
- package/mnemo/brief_coordination.js +0 -226
- package/mnemo/code_read_tools.js +0 -375
- package/mnemo/context_preview_tools.js +0 -603
- package/mnemo/embeddings.js +0 -66
- package/mnemo/external_repo_ops.js +0 -575
- package/mnemo/facts/example-project-rules.json +0 -90
- package/mnemo/facts/example.json +0 -34
- package/mnemo/identity_schema.sql +0 -139
- package/mnemo/journal_schema.js +0 -561
- package/mnemo/loop_doctor_tools.js +0 -661
- package/mnemo/mail_secret_refs.js +0 -150
- package/mnemo/mcp.js +0 -9309
- package/mnemo/memory_consolidation.js +0 -1914
- package/mnemo/memory_health_tools.js +0 -165
- package/mnemo/package.json +0 -79
- package/mnemo/protected_scope_gate.js +0 -627
- package/mnemo/resource_access_control.js +0 -684
- package/mnemo/runtime_governance.js +0 -1256
- package/mnemo/runtime_turn_gate.js +0 -862
- package/mnemo/sandbox.js +0 -143
- package/mnemo/schema.sql +0 -389
- package/mnemo/shared_utils.js +0 -763
- package/mnemo/skills/agent-auto-resume/SKILL.md +0 -56
- package/mnemo/skills/agent_hand/SKILL.md +0 -43
- package/mnemo/skills/agent_hand/run.js +0 -63
- package/mnemo/skills/book_flight/SKILL.md +0 -34
- package/mnemo/skills/external_repo_review/SKILL.md +0 -43
- package/mnemo/skills/external_repo_review/run.js +0 -73
- package/mnemo/skills/pay_invoice/SKILL.md +0 -34
- package/mnemo/team_quality_ops.js +0 -944
- package/mnemo/timeline_report_tools.js +0 -810
- package/mnemo/write_gate_risk.js +0 -80
- package/mnemo/writer_health.js +0 -152
- package/skills/doku-ingestion/SKILL.md +0 -48
- package/skills/doku-ingestion/ingest_docs.py +0 -133
package/bin/king.js
CHANGED
|
@@ -1,4 +1,280 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// This public entry must stay self-contained. Package-local modules are loaded
|
|
5
|
+
// only after the update owner proves that the package root is not being replaced.
|
|
6
|
+
const { createHash } = require('node:crypto');
|
|
7
|
+
const fs = require('node:fs');
|
|
8
|
+
const net = require('node:net');
|
|
9
|
+
const os = require('node:os');
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
|
|
12
|
+
const LEASE_PROTOCOL_VERSION = 'BLUN_UPDATE_LEASE_V3';
|
|
13
|
+
const LEASE_PROBE_TIMEOUT_MS = 500;
|
|
14
|
+
const MAX_POSIX_SOCKET_PATH_BYTES = 96;
|
|
15
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
16
|
+
|
|
17
|
+
function deriveLeaseEndpoint(role) {
|
|
18
|
+
const realpath = fs.realpathSync.native || fs.realpathSync;
|
|
19
|
+
let identity = realpath(PACKAGE_ROOT);
|
|
20
|
+
if (!fs.statSync(identity).isDirectory()) throw new Error('INVALID_PACKAGE_ROOT');
|
|
21
|
+
identity = identity.replaceAll('\\', '/');
|
|
22
|
+
if (process.platform === 'win32') identity = identity.toLowerCase();
|
|
23
|
+
const digest = createHash('sha256')
|
|
24
|
+
.update(`blun-update-v3\0${role}\0${identity}`)
|
|
25
|
+
.digest('hex');
|
|
26
|
+
const magic = `${LEASE_PROTOCOL_VERSION} ${digest}\n`;
|
|
27
|
+
if (process.platform === 'win32') {
|
|
28
|
+
return {
|
|
29
|
+
magic,
|
|
30
|
+
path: `\\\\.\\pipe\\blun-update-v3-${digest.slice(0, 32)}`,
|
|
31
|
+
platform: 'win32',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const uid = typeof process.getuid === 'function' ? process.getuid() : undefined;
|
|
36
|
+
const directoryName = `blun-update-${uid ?? digest.slice(0, 12)}`;
|
|
37
|
+
let directory = path.posix.join(os.tmpdir().replaceAll('\\', '/'), directoryName);
|
|
38
|
+
let socketPath = path.posix.join(directory, `lease-${digest.slice(0, 24)}.sock`);
|
|
39
|
+
if (Buffer.byteLength(socketPath, 'utf8') > MAX_POSIX_SOCKET_PATH_BYTES) {
|
|
40
|
+
directory = path.posix.join('/tmp', directoryName);
|
|
41
|
+
socketPath = path.posix.join(directory, `lease-${digest.slice(0, 24)}.sock`);
|
|
42
|
+
}
|
|
43
|
+
if (Buffer.byteLength(socketPath, 'utf8') > MAX_POSIX_SOCKET_PATH_BYTES) {
|
|
44
|
+
throw new Error('UPDATE_LEASE_SOCKET_PATH_TOO_LONG');
|
|
45
|
+
}
|
|
46
|
+
return { directory, magic, path: socketPath, platform: process.platform };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ensurePrivateSocketDirectory(endpoint) {
|
|
50
|
+
if (endpoint.platform === 'win32') return;
|
|
51
|
+
fs.mkdirSync(endpoint.directory, { recursive: true, mode: 0o700 });
|
|
52
|
+
const stat = fs.lstatSync(endpoint.directory);
|
|
53
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
54
|
+
throw new Error('INVALID_UPDATE_LEASE_DIRECTORY');
|
|
55
|
+
}
|
|
56
|
+
if (typeof process.getuid === 'function'
|
|
57
|
+
&& Number.isInteger(stat.uid)
|
|
58
|
+
&& stat.uid !== process.getuid()) {
|
|
59
|
+
throw new Error('INVALID_UPDATE_LEASE_DIRECTORY_OWNER');
|
|
60
|
+
}
|
|
61
|
+
fs.chmodSync(endpoint.directory, 0o700);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function closeServer(server) {
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
if (!server.listening) {
|
|
67
|
+
resolve();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
server.close(() => resolve());
|
|
72
|
+
} catch {
|
|
73
|
+
resolve();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function listenForLease(endpoint) {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const server = net.createServer((socket) => {
|
|
81
|
+
socket.on('error', () => {});
|
|
82
|
+
socket.end(endpoint.magic);
|
|
83
|
+
});
|
|
84
|
+
const onError = (error) => {
|
|
85
|
+
server.removeListener('error', onError);
|
|
86
|
+
reject(error);
|
|
87
|
+
};
|
|
88
|
+
server.once('error', onError);
|
|
89
|
+
server.listen({ path: endpoint.path, exclusive: true }, () => {
|
|
90
|
+
server.removeListener('error', onError);
|
|
91
|
+
resolve(server);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function probeLease(endpoint) {
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
let received = '';
|
|
99
|
+
let settled = false;
|
|
100
|
+
let socket;
|
|
101
|
+
const finish = (state) => {
|
|
102
|
+
if (settled) return;
|
|
103
|
+
settled = true;
|
|
104
|
+
clearTimeout(timer);
|
|
105
|
+
try {
|
|
106
|
+
socket?.destroy();
|
|
107
|
+
} catch {}
|
|
108
|
+
// oxlint-disable-next-line promise/no-multiple-resolved -- settled is the idempotence guard.
|
|
109
|
+
resolve(state);
|
|
110
|
+
};
|
|
111
|
+
const timer = setTimeout(() => finish('foreign'), LEASE_PROBE_TIMEOUT_MS);
|
|
112
|
+
timer.unref?.();
|
|
113
|
+
try {
|
|
114
|
+
socket = net.createConnection({ path: endpoint.path });
|
|
115
|
+
socket.setEncoding('utf8');
|
|
116
|
+
socket.on('data', (chunk) => {
|
|
117
|
+
received += String(chunk);
|
|
118
|
+
if (Buffer.byteLength(received, 'utf8') > Buffer.byteLength(endpoint.magic, 'utf8')) {
|
|
119
|
+
finish('foreign');
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
socket.once('end', () => finish(received === endpoint.magic ? 'owner' : 'foreign'));
|
|
123
|
+
socket.once('error', (error) => {
|
|
124
|
+
finish(error?.code === 'ECONNREFUSED' || error?.code === 'ENOENT'
|
|
125
|
+
? 'stale'
|
|
126
|
+
: 'foreign');
|
|
127
|
+
});
|
|
128
|
+
socket.once('close', () => {
|
|
129
|
+
if (!settled) finish(received === endpoint.magic ? 'owner' : 'foreign');
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
finish(error?.code === 'ECONNREFUSED' || error?.code === 'ENOENT'
|
|
133
|
+
? 'stale'
|
|
134
|
+
: 'foreign');
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function observeSocket(endpoint) {
|
|
140
|
+
if (endpoint.platform === 'win32') return { state: 'not_applicable' };
|
|
141
|
+
try {
|
|
142
|
+
const stat = fs.lstatSync(endpoint.path);
|
|
143
|
+
if (!stat.isSocket() || stat.isSymbolicLink()) return { state: 'unsafe' };
|
|
144
|
+
return { dev: stat.dev, ino: stat.ino, state: 'socket' };
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return error?.code === 'ENOENT' ? { state: 'missing' } : { state: 'unsafe' };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function removeObservedSocket(endpoint, observed) {
|
|
151
|
+
if (endpoint.platform === 'win32') return false;
|
|
152
|
+
try {
|
|
153
|
+
const stat = fs.lstatSync(endpoint.path);
|
|
154
|
+
if (observed.state !== 'socket'
|
|
155
|
+
|| !stat.isSocket()
|
|
156
|
+
|| stat.isSymbolicLink()
|
|
157
|
+
|| stat.dev !== observed.dev
|
|
158
|
+
|| stat.ino !== observed.ino) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
fs.unlinkSync(endpoint.path);
|
|
162
|
+
return true;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
return error?.code === 'ENOENT' && observed.state === 'missing';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function recordSocket(endpoint) {
|
|
169
|
+
if (endpoint.platform === 'win32') return undefined;
|
|
170
|
+
const stat = fs.lstatSync(endpoint.path);
|
|
171
|
+
if (!stat.isSocket() || stat.isSymbolicLink()) throw new Error('INVALID_UPDATE_LEASE_SOCKET');
|
|
172
|
+
fs.chmodSync(endpoint.path, 0o600);
|
|
173
|
+
return { dev: stat.dev, ino: stat.ino };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function removeOwnedSocket(endpoint, identity) {
|
|
177
|
+
if (endpoint.platform === 'win32' || !identity) return;
|
|
178
|
+
try {
|
|
179
|
+
const stat = fs.lstatSync(endpoint.path);
|
|
180
|
+
if (stat.isSocket()
|
|
181
|
+
&& !stat.isSymbolicLink()
|
|
182
|
+
&& stat.dev === identity.dev
|
|
183
|
+
&& stat.ino === identity.ino) {
|
|
184
|
+
fs.unlinkSync(endpoint.path);
|
|
185
|
+
}
|
|
186
|
+
} catch {}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function acquireNoticeLease() {
|
|
190
|
+
let endpoint;
|
|
191
|
+
try {
|
|
192
|
+
endpoint = deriveLeaseEndpoint('notice');
|
|
193
|
+
ensurePrivateSocketDirectory(endpoint);
|
|
194
|
+
} catch {
|
|
195
|
+
return { acquired: false, reason: 'unavailable' };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let server;
|
|
199
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
200
|
+
try {
|
|
201
|
+
server = await listenForLease(endpoint);
|
|
202
|
+
break;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (error?.code !== 'EADDRINUSE') return { acquired: false, reason: 'unavailable' };
|
|
205
|
+
const observed = observeSocket(endpoint);
|
|
206
|
+
const state = await probeLease(endpoint);
|
|
207
|
+
if (state === 'owner') return { acquired: false, reason: 'busy' };
|
|
208
|
+
if (state !== 'stale' || attempt > 0) return { acquired: false, reason: 'unavailable' };
|
|
209
|
+
if (endpoint.platform === 'win32') continue;
|
|
210
|
+
if (!removeObservedSocket(endpoint, observed)) {
|
|
211
|
+
return { acquired: false, reason: 'unavailable' };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (!server) return { acquired: false, reason: 'unavailable' };
|
|
216
|
+
|
|
217
|
+
let identity;
|
|
218
|
+
try {
|
|
219
|
+
identity = recordSocket(endpoint);
|
|
220
|
+
} catch {
|
|
221
|
+
await closeServer(server);
|
|
222
|
+
return { acquired: false, reason: 'unavailable' };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
let owned = true;
|
|
226
|
+
let released = false;
|
|
227
|
+
server.on('error', () => { owned = false; });
|
|
228
|
+
server.on('close', () => { owned = false; });
|
|
229
|
+
return {
|
|
230
|
+
acquired: true,
|
|
231
|
+
lease: Object.freeze({
|
|
232
|
+
assertOwned() {
|
|
233
|
+
if (!owned || !server.listening) throw new Error('UPDATE_LEASE_LOST');
|
|
234
|
+
},
|
|
235
|
+
async release() {
|
|
236
|
+
if (released) return;
|
|
237
|
+
released = true;
|
|
238
|
+
owned = false;
|
|
239
|
+
await closeServer(server);
|
|
240
|
+
removeOwnedSocket(endpoint, identity);
|
|
241
|
+
},
|
|
242
|
+
}),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function installLeaseIsClear() {
|
|
247
|
+
try {
|
|
248
|
+
const endpoint = deriveLeaseEndpoint('install');
|
|
249
|
+
ensurePrivateSocketDirectory(endpoint);
|
|
250
|
+
return await probeLease(endpoint) === 'stale';
|
|
251
|
+
} catch {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function main() {
|
|
257
|
+
const result = await acquireNoticeLease();
|
|
258
|
+
if (!result.acquired) return;
|
|
259
|
+
try {
|
|
260
|
+
if (!await installLeaseIsClear()) return;
|
|
261
|
+
result.lease.assertOwned();
|
|
262
|
+
const { runLauncher } = require('./launcher-runtime');
|
|
263
|
+
result.lease.assertOwned();
|
|
264
|
+
const filename = path.basename(__filename).toLowerCase();
|
|
265
|
+
await runLauncher({
|
|
266
|
+
mode: filename.startsWith('king') ? 'king' : 'blun',
|
|
267
|
+
noticeLease: result.lease,
|
|
268
|
+
});
|
|
269
|
+
} finally {
|
|
270
|
+
await result.lease.release();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// oxlint-disable-next-line unicorn/prefer-top-level-await -- Public npm bins are CommonJS.
|
|
275
|
+
void main().catch((error) => {
|
|
276
|
+
// oxlint-disable-next-line no-console -- Fatal launcher errors belong on stderr.
|
|
277
|
+
console.error('BLUN Start fehlgeschlagen: '
|
|
278
|
+
+ (error && error.message ? error.message : String(error)));
|
|
279
|
+
process.exitCode = 1;
|
|
280
|
+
});
|
package/bin/launcher-mode.js
CHANGED
|
@@ -14,10 +14,11 @@ function launcherModeFromArgv(argv) {
|
|
|
14
14
|
return command === LAUNCHER_MODES.KING ? LAUNCHER_MODES.KING : LAUNCHER_MODES.BLUN;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function createLauncherEnvironment(sourceEnv, mode) {
|
|
17
|
+
function createLauncherEnvironment(sourceEnv, mode, publicPackageVersion) {
|
|
18
18
|
return {
|
|
19
19
|
...sourceEnv,
|
|
20
20
|
BLUN_NO_AUTO_UPDATE: '1',
|
|
21
|
+
BLUN_PUBLIC_PACKAGE_VERSION: publicPackageVersion,
|
|
21
22
|
BLUN_TELEGRAM_ATTACH: mode === LAUNCHER_MODES.KING ? 'on' : 'off',
|
|
22
23
|
BLUN_TELEGRAM_HEADLESS: 'off',
|
|
23
24
|
};
|
|
@@ -0,0 +1,221 @@
|
|
|
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
|
+
NPM_PACKAGE_UPDATE_COMMAND,
|
|
14
|
+
createLauncherEnvironment,
|
|
15
|
+
isNpmPackageUpdateRequest,
|
|
16
|
+
launcherModeFromArgv,
|
|
17
|
+
} = require('./launcher-mode');
|
|
18
|
+
const { installManagedTelegramPlugin } = require('./plugin-bootstrap');
|
|
19
|
+
const { CORE_LOADED_MESSAGE } = require('./core-bootstrap');
|
|
20
|
+
const { tryAcquireUpdateLease } = require('./update-lease');
|
|
21
|
+
const { runUpdateNotice } = require('./update-notice');
|
|
22
|
+
const {
|
|
23
|
+
ensurePrivateDirectory,
|
|
24
|
+
securePrivateFile,
|
|
25
|
+
writePrivateFile,
|
|
26
|
+
} = require('./private-paths');
|
|
27
|
+
|
|
28
|
+
const PKG = path.resolve(__dirname, '..');
|
|
29
|
+
const HOME = os.homedir();
|
|
30
|
+
const configuredBlunHome = String(process.env.BLUN_HOME || '').trim();
|
|
31
|
+
const BLUN = configuredBlunHome ? path.resolve(configuredBlunHome) : path.join(HOME, '.blun');
|
|
32
|
+
const ARGS = process.argv.slice(2);
|
|
33
|
+
const CORE_LOAD_TIMEOUT_MS = 30_000;
|
|
34
|
+
|
|
35
|
+
function readPackageVersion() {
|
|
36
|
+
const { version } = JSON.parse(fs.readFileSync(path.join(PKG, 'package.json'), 'utf8'));
|
|
37
|
+
if (typeof version !== 'string' || version.length === 0) {
|
|
38
|
+
throw new Error('Ungueltige Paketversion in package.json');
|
|
39
|
+
}
|
|
40
|
+
return version;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function spawnProtectedCore(args, env, cwd) {
|
|
44
|
+
const child = spawn(
|
|
45
|
+
process.execPath,
|
|
46
|
+
[path.join(PKG, 'bin', 'core-bootstrap.js'), ...args],
|
|
47
|
+
{
|
|
48
|
+
cwd,
|
|
49
|
+
detached: true,
|
|
50
|
+
env,
|
|
51
|
+
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
52
|
+
windowsHide: true,
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
let loadedSettled = false;
|
|
56
|
+
let completedSettled = false;
|
|
57
|
+
let resolveLoaded;
|
|
58
|
+
let resolveCompleted;
|
|
59
|
+
const loadTimer = setTimeout(() => {
|
|
60
|
+
try {
|
|
61
|
+
child.kill();
|
|
62
|
+
} catch {}
|
|
63
|
+
}, CORE_LOAD_TIMEOUT_MS);
|
|
64
|
+
const loaded = new Promise((resolve) => { resolveLoaded = resolve; });
|
|
65
|
+
const completed = new Promise((resolve) => { resolveCompleted = resolve; });
|
|
66
|
+
const settleLoaded = (value) => {
|
|
67
|
+
if (loadedSettled) return;
|
|
68
|
+
loadedSettled = true;
|
|
69
|
+
clearTimeout(loadTimer);
|
|
70
|
+
resolveLoaded(value);
|
|
71
|
+
};
|
|
72
|
+
const settleCompleted = (result) => {
|
|
73
|
+
if (completedSettled) return;
|
|
74
|
+
completedSettled = true;
|
|
75
|
+
for (const [signal, handler] of signalHandlers) process.removeListener(signal, handler);
|
|
76
|
+
settleLoaded(false);
|
|
77
|
+
resolveCompleted(result);
|
|
78
|
+
};
|
|
79
|
+
const signalHandlers = new Map();
|
|
80
|
+
for (const signal of ['SIGHUP', 'SIGINT', 'SIGTERM']) {
|
|
81
|
+
const handler = () => {
|
|
82
|
+
try {
|
|
83
|
+
child.kill(signal);
|
|
84
|
+
} catch {}
|
|
85
|
+
};
|
|
86
|
+
signalHandlers.set(signal, handler);
|
|
87
|
+
process.once(signal, handler);
|
|
88
|
+
}
|
|
89
|
+
child.on('message', (message) => {
|
|
90
|
+
if (message?.type !== CORE_LOADED_MESSAGE) return;
|
|
91
|
+
settleLoaded(true);
|
|
92
|
+
try {
|
|
93
|
+
child.disconnect();
|
|
94
|
+
} catch {}
|
|
95
|
+
});
|
|
96
|
+
child.once('error', (error) => settleCompleted({ error }));
|
|
97
|
+
child.once('exit', (code, signal) => settleCompleted({ code, signal }));
|
|
98
|
+
return { child, completed, loaded };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function superviseProtectedCore(args, env, cwd, releaseNotice) {
|
|
102
|
+
const core = spawnProtectedCore(args, env, cwd);
|
|
103
|
+
const loaded = await core.loaded;
|
|
104
|
+
await releaseNotice();
|
|
105
|
+
const result = await core.completed;
|
|
106
|
+
if (result.error) throw result.error;
|
|
107
|
+
return exitCodeForChild(result, loaded);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function exitCodeForChild(result, loaded) {
|
|
111
|
+
if (!loaded && result.code === 75) return 0;
|
|
112
|
+
if (Number.isInteger(result.code)) return result.code;
|
|
113
|
+
if (result.signal === 'SIGINT') return 130;
|
|
114
|
+
if (result.signal === 'SIGTERM') return 143;
|
|
115
|
+
return 1;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function runLauncher(options = {}) {
|
|
119
|
+
const mode = options.mode || launcherModeFromArgv(process.argv);
|
|
120
|
+
const noticeResult = options.noticeLease
|
|
121
|
+
? { acquired: true, lease: options.noticeLease }
|
|
122
|
+
: await tryAcquireUpdateLease({ packageRoot: PKG, role: 'notice' });
|
|
123
|
+
if (!noticeResult.acquired) return;
|
|
124
|
+
const noticeLease = noticeResult.lease;
|
|
125
|
+
let noticeReleased = false;
|
|
126
|
+
const releaseNotice = async () => {
|
|
127
|
+
if (noticeReleased) return;
|
|
128
|
+
noticeReleased = true;
|
|
129
|
+
await noticeLease.release();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const installProbe = await tryAcquireUpdateLease({ packageRoot: PKG, role: 'install' });
|
|
134
|
+
if (!installProbe.acquired) return;
|
|
135
|
+
await installProbe.lease.release();
|
|
136
|
+
|
|
137
|
+
if (ARGS.some((arg) => arg === '--version' || arg === '-V')) {
|
|
138
|
+
process.stdout.write(`${readPackageVersion()}\n`);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (isNpmPackageUpdateRequest(ARGS)) {
|
|
143
|
+
process.stdout.write(`${NPM_PACKAGE_UPDATE_COMMAND}\n`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// --- 1. Oeffentlicher npm-Update-Dialog. -------------------------------
|
|
148
|
+
const updateResult = await runUpdateNotice({
|
|
149
|
+
currentVersion: readPackageVersion(),
|
|
150
|
+
argv: ARGS,
|
|
151
|
+
env: process.env,
|
|
152
|
+
stdin: process.stdin,
|
|
153
|
+
stdout: process.stdout,
|
|
154
|
+
stderr: process.stderr,
|
|
155
|
+
blunDir: BLUN,
|
|
156
|
+
packageRoot: PKG,
|
|
157
|
+
noticeLease,
|
|
158
|
+
});
|
|
159
|
+
if (updateResult.kind === 'installed' || updateResult.kind === 'update_in_progress') return;
|
|
160
|
+
|
|
161
|
+
if (ARGS.some((arg) => arg === '--help' || arg === '-h')) {
|
|
162
|
+
const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
|
|
163
|
+
env.BLUN_HOME = BLUN;
|
|
164
|
+
process.exitCode = await superviseProtectedCore(ARGS, env, process.cwd(), releaseNotice);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
ensurePrivateDirectory(BLUN);
|
|
169
|
+
|
|
170
|
+
// --- 2. Bestehende Konfiguration absichern -------------------------------
|
|
171
|
+
// Ein frischer Start bleibt absichtlich ohne statischen Anbieter. /login
|
|
172
|
+
// provisioniert King ueber den BLUN-OAuth-Server.
|
|
173
|
+
const cfgFile = path.join(BLUN, 'config.toml');
|
|
174
|
+
if (fs.existsSync(cfgFile)) securePrivateFile(cfgFile);
|
|
175
|
+
|
|
176
|
+
// --- 3. Telegram-Plugin + Token-Vorlage (nur King) -----------------------
|
|
177
|
+
if (mode === LAUNCHER_MODES.KING) {
|
|
178
|
+
installManagedTelegramPlugin({ packageRoot: PKG, blunDir: BLUN });
|
|
179
|
+
const channelDir = path.join(BLUN, 'channels', 'telegram');
|
|
180
|
+
fs.mkdirSync(channelDir, { recursive: true });
|
|
181
|
+
const envFile = path.join(channelDir, '.env');
|
|
182
|
+
if (!fs.existsSync(envFile)) {
|
|
183
|
+
writePrivateFile(envFile, 'BLUN_TELEGRAM_BOT_TOKEN=HIER_DEIN_BOT_TOKEN_EINFUEGEN\n');
|
|
184
|
+
} else {
|
|
185
|
+
securePrivateFile(envFile);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// --- 3b. Built-in-Skills installieren (fehlende ergaenzen, nie
|
|
190
|
+
// vorhandene ueberschreiben — User-Anpassungen bleiben) -----------
|
|
191
|
+
const skillsSrc = path.join(PKG, 'skills');
|
|
192
|
+
const skillsDst = path.join(BLUN, 'skills');
|
|
193
|
+
if (fs.existsSync(skillsSrc)) {
|
|
194
|
+
fs.mkdirSync(skillsDst, { recursive: true });
|
|
195
|
+
for (const entry of fs.readdirSync(skillsSrc)) {
|
|
196
|
+
const dst = path.join(skillsDst, entry);
|
|
197
|
+
if (!fs.existsSync(dst)) {
|
|
198
|
+
fs.cpSync(path.join(skillsSrc, entry), dst, { recursive: true });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// --- 4. Start -----------------------------------------------------------
|
|
204
|
+
const env = createLauncherEnvironment(process.env, mode, readPackageVersion());
|
|
205
|
+
env.BLUN_HOME = BLUN;
|
|
206
|
+
env.BLUN_MODEL_MAX_COMPLETION_TOKENS = env.BLUN_MODEL_MAX_COMPLETION_TOKENS || '32768';
|
|
207
|
+
const workspace = path.join(HOME, 'BLUN-Workspace');
|
|
208
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
209
|
+
|
|
210
|
+
process.exitCode = await superviseProtectedCore(
|
|
211
|
+
['--yolo', ...ARGS],
|
|
212
|
+
env,
|
|
213
|
+
workspace,
|
|
214
|
+
releaseNotice,
|
|
215
|
+
);
|
|
216
|
+
} finally {
|
|
217
|
+
await releaseNotice();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = { runLauncher };
|
package/bin/plugin-bootstrap.js
CHANGED
|
File without changes
|
package/bin/private-paths.js
CHANGED
|
File without changes
|