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.
Files changed (55) hide show
  1. package/LIESMICH.txt +1 -7
  2. package/README.md +4 -16
  3. package/bin/blun.js +248 -160
  4. package/bin/core-bootstrap.js +47 -0
  5. package/bin/king.js +277 -1
  6. package/bin/launcher-mode.js +2 -1
  7. package/bin/launcher-runtime.js +221 -0
  8. package/bin/plugin-bootstrap.js +0 -0
  9. package/bin/private-paths.js +0 -0
  10. package/bin/update-lease.js +399 -0
  11. package/bin/update-notice.js +1094 -0
  12. package/blun.mjs +4060 -6667
  13. package/package.json +3 -10
  14. package/skills/screenshot-lesen/SKILL.md +0 -1
  15. package/skills/web-lesen/SKILL.md +0 -1
  16. package/telegram-plugin/dist/bridge.mjs +1 -21
  17. package/mnemo/access_routes.js +0 -692
  18. package/mnemo/agent_governance.js +0 -4242
  19. package/mnemo/agent_mail.js +0 -901
  20. package/mnemo/bootstrap_auto.js +0 -137
  21. package/mnemo/brief_coordination.js +0 -226
  22. package/mnemo/code_read_tools.js +0 -375
  23. package/mnemo/context_preview_tools.js +0 -603
  24. package/mnemo/embeddings.js +0 -66
  25. package/mnemo/external_repo_ops.js +0 -575
  26. package/mnemo/facts/example-project-rules.json +0 -90
  27. package/mnemo/facts/example.json +0 -34
  28. package/mnemo/identity_schema.sql +0 -139
  29. package/mnemo/journal_schema.js +0 -561
  30. package/mnemo/loop_doctor_tools.js +0 -661
  31. package/mnemo/mail_secret_refs.js +0 -150
  32. package/mnemo/mcp.js +0 -9309
  33. package/mnemo/memory_consolidation.js +0 -1914
  34. package/mnemo/memory_health_tools.js +0 -165
  35. package/mnemo/package.json +0 -79
  36. package/mnemo/protected_scope_gate.js +0 -627
  37. package/mnemo/resource_access_control.js +0 -684
  38. package/mnemo/runtime_governance.js +0 -1256
  39. package/mnemo/runtime_turn_gate.js +0 -862
  40. package/mnemo/sandbox.js +0 -143
  41. package/mnemo/schema.sql +0 -389
  42. package/mnemo/shared_utils.js +0 -763
  43. package/mnemo/skills/agent-auto-resume/SKILL.md +0 -56
  44. package/mnemo/skills/agent_hand/SKILL.md +0 -43
  45. package/mnemo/skills/agent_hand/run.js +0 -63
  46. package/mnemo/skills/book_flight/SKILL.md +0 -34
  47. package/mnemo/skills/external_repo_review/SKILL.md +0 -43
  48. package/mnemo/skills/external_repo_review/run.js +0 -73
  49. package/mnemo/skills/pay_invoice/SKILL.md +0 -34
  50. package/mnemo/team_quality_ops.js +0 -944
  51. package/mnemo/timeline_report_tools.js +0 -810
  52. package/mnemo/write_gate_risk.js +0 -80
  53. package/mnemo/writer_health.js +0 -152
  54. package/skills/doku-ingestion/SKILL.md +0 -48
  55. 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
- require('./blun');
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
+ });
@@ -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 };
File without changes
File without changes