blun-king-cli 9.1.208 → 9.1.210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/launcher-runtime.js +140 -18
- package/bin/running-update.cjs +103 -7
- package/blun.mjs +16 -18
- package/package.json +1 -1
package/bin/launcher-runtime.js
CHANGED
|
@@ -33,12 +33,19 @@ const {
|
|
|
33
33
|
RUNNING_UPDATE_HANDOFF_MESSAGE,
|
|
34
34
|
RUNNING_UPDATE_MODE_MESSAGE,
|
|
35
35
|
RUNNING_UPDATE_PREPARED_MESSAGE,
|
|
36
|
+
RUNTIME_EXIT_INTENT_MESSAGE,
|
|
36
37
|
activateRuntime,
|
|
38
|
+
clearActiveRuntime,
|
|
37
39
|
handoffArgsForMode,
|
|
38
40
|
handoffRuntime,
|
|
39
41
|
prepareRunningUpdate,
|
|
40
42
|
pruneRunningUpdateReleases,
|
|
43
|
+
recordRunningUpdateEvent,
|
|
41
44
|
readActiveRuntime,
|
|
45
|
+
readPendingRuntime,
|
|
46
|
+
restoreActiveRuntime,
|
|
47
|
+
clearPendingRuntime,
|
|
48
|
+
stageRuntime,
|
|
42
49
|
} = require('./running-update.cjs');
|
|
43
50
|
const {
|
|
44
51
|
RUNNING_UPDATE_MODES,
|
|
@@ -62,9 +69,9 @@ const PROFILE = parseProfileLaunchArgs(RAW_ARGS, launcherModeFromArgv(process.ar
|
|
|
62
69
|
const ARGS = PROFILE.args;
|
|
63
70
|
const CORE_LOAD_TIMEOUT_MS = 30_000;
|
|
64
71
|
const RUNTIME_READY_TIMEOUT_MS = 60_000;
|
|
65
|
-
const RUNNING_UPDATE_RECHECK_MS = 60_000;
|
|
66
|
-
const RUNNING_UPDATE_RECHECK_JITTER_MS =
|
|
67
|
-
const
|
|
72
|
+
const RUNNING_UPDATE_RECHECK_MS = 5 * 60_000;
|
|
73
|
+
const RUNNING_UPDATE_RECHECK_JITTER_MS = 2 * 60_000;
|
|
74
|
+
const RUNNING_UPDATE_STABILIZATION_MS = 2 * 60 * 1000;
|
|
68
75
|
|
|
69
76
|
function runningUpdateRecheckDelay(randomValue = Math.random()) {
|
|
70
77
|
const normalized = Number.isFinite(randomValue)
|
|
@@ -83,10 +90,6 @@ function scheduleRunningUpdateRecheck(callback, options = {}) {
|
|
|
83
90
|
return timer;
|
|
84
91
|
}
|
|
85
92
|
|
|
86
|
-
function writeRunningUpdateHandoffNotice(output = process.stdout) {
|
|
87
|
-
output.write(`\n${RUNNING_UPDATE_HANDOFF_NOTICE}\n`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
93
|
function normalizeWindowsPath(value) {
|
|
91
94
|
return path.win32.normalize(value).replace(/\\+$/u, '').toLowerCase();
|
|
92
95
|
}
|
|
@@ -268,6 +271,14 @@ function resolveHandoffCwd(candidate, fallback) {
|
|
|
268
271
|
}
|
|
269
272
|
}
|
|
270
273
|
|
|
274
|
+
function shouldRecoverActivatedRuntime(result, recovery, options = {}) {
|
|
275
|
+
if (recovery === undefined || options.runtimeExitIntent === true) return false;
|
|
276
|
+
const signal = result?.signal;
|
|
277
|
+
if (signal === 'SIGINT' || signal === 'SIGTERM' || signal === 'SIGHUP') return false;
|
|
278
|
+
const now = options.now ?? Date.now();
|
|
279
|
+
return now - recovery.activatedAt <= RUNNING_UPDATE_STABILIZATION_MS;
|
|
280
|
+
}
|
|
281
|
+
|
|
271
282
|
async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {}) {
|
|
272
283
|
const packageRoot = path.resolve(options.packageRoot || PKG);
|
|
273
284
|
let preparedTarget;
|
|
@@ -276,6 +287,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
276
287
|
let handoffCwd = cwd;
|
|
277
288
|
let updateStarted = false;
|
|
278
289
|
let runtimeReady = false;
|
|
290
|
+
let runtimeExitIntent = false;
|
|
279
291
|
let runningUpdatePollTimer;
|
|
280
292
|
let runningUpdateMode = readRunningUpdateMode(env.BLUN_HOME);
|
|
281
293
|
const automaticMode = () => runningUpdateMode === RUNNING_UPDATE_MODES.RESUME
|
|
@@ -306,11 +318,17 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
306
318
|
};
|
|
307
319
|
const announcePrepared = (child) => {
|
|
308
320
|
if (preparedTarget === undefined || !automaticMode() || child.connected !== true) return;
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
321
|
+
try {
|
|
322
|
+
child.send({
|
|
323
|
+
type: RUNNING_UPDATE_PREPARED_MESSAGE,
|
|
324
|
+
version: preparedTarget.version,
|
|
325
|
+
mode: runningUpdateMode,
|
|
326
|
+
}, (error) => {
|
|
327
|
+
if (error) options.onRunningUpdateError?.(error);
|
|
328
|
+
});
|
|
329
|
+
} catch (error) {
|
|
330
|
+
options.onRunningUpdateError?.(error);
|
|
331
|
+
}
|
|
314
332
|
};
|
|
315
333
|
const startPreparation = (child) => {
|
|
316
334
|
if (updateStarted || options.runningUpdate === false || !automaticMode()) return;
|
|
@@ -332,6 +350,15 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
332
350
|
return;
|
|
333
351
|
}
|
|
334
352
|
preparedTarget = target;
|
|
353
|
+
(options.stageRuntime || stageRuntime)(sharedHome, target, { mode: runningUpdateMode });
|
|
354
|
+
(options.recordRunningUpdateEvent || recordRunningUpdateEvent)(sharedHome, {
|
|
355
|
+
event: 'prepared-for-next-start',
|
|
356
|
+
fromVersion: readPackageVersionAt(packageRoot),
|
|
357
|
+
toVersion: target.version,
|
|
358
|
+
}, { now: options.nowImpl });
|
|
359
|
+
Promise.resolve((options.pruneRunningUpdateReleases || pruneRunningUpdateReleases)(sharedHome, {
|
|
360
|
+
activePackageRoot: packageRoot,
|
|
361
|
+
})).catch((error) => options.onRunningUpdateError?.(error));
|
|
335
362
|
clearRunningUpdatePoll();
|
|
336
363
|
announcePrepared(child);
|
|
337
364
|
}).catch((error) => {
|
|
@@ -341,6 +368,7 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
341
368
|
});
|
|
342
369
|
};
|
|
343
370
|
const handleCoreMessage = (message, child) => {
|
|
371
|
+
if (message?.type === RUNTIME_EXIT_INTENT_MESSAGE) runtimeExitIntent = true;
|
|
344
372
|
if (message?.type === RUNTIME_READY_MESSAGE) {
|
|
345
373
|
runtimeReady = true;
|
|
346
374
|
refreshRunningUpdateMode();
|
|
@@ -369,6 +397,13 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
369
397
|
handoffSessionId = message.sessionId;
|
|
370
398
|
handoffMode = runningUpdateMode;
|
|
371
399
|
handoffCwd = resolveHandoffCwd(message.cwd, cwd);
|
|
400
|
+
if (preparedTarget !== undefined) {
|
|
401
|
+
(options.stageRuntime || stageRuntime)(env.BLUN_SHARED_HOME, preparedTarget, {
|
|
402
|
+
mode: handoffMode,
|
|
403
|
+
sessionId: handoffSessionId,
|
|
404
|
+
cwd: handoffCwd,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
372
407
|
}
|
|
373
408
|
};
|
|
374
409
|
const spawnCore = options.spawnProtectedCore || spawnProtectedCore;
|
|
@@ -382,6 +417,21 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
382
417
|
core.child.on('message', existingMessageHandler);
|
|
383
418
|
}
|
|
384
419
|
const loaded = await core.loaded;
|
|
420
|
+
if (loaded && options.startupPendingRuntime !== undefined) {
|
|
421
|
+
await (options.activateRuntime || activateRuntime)(env.BLUN_SHARED_HOME, options.startupPendingRuntime);
|
|
422
|
+
(options.clearPendingRuntime || clearPendingRuntime)(env.BLUN_SHARED_HOME, options.startupPendingRuntime);
|
|
423
|
+
(options.recordRunningUpdateEvent || recordRunningUpdateEvent)(env.BLUN_SHARED_HOME, {
|
|
424
|
+
event: 'activated-on-start',
|
|
425
|
+
version: options.startupPendingRuntime.version,
|
|
426
|
+
}, { now: options.nowImpl });
|
|
427
|
+
} else if (loaded && options.staleActiveRuntime !== undefined) {
|
|
428
|
+
(options.clearActiveRuntime || clearActiveRuntime)(env.BLUN_SHARED_HOME, options.staleActiveRuntime);
|
|
429
|
+
(options.recordRunningUpdateEvent || recordRunningUpdateEvent)(env.BLUN_SHARED_HOME, {
|
|
430
|
+
event: 'stale-active-cleared',
|
|
431
|
+
staleVersion: options.staleActiveRuntime.version,
|
|
432
|
+
runningVersion: readPackageVersionAt(packageRoot),
|
|
433
|
+
}, { now: options.nowImpl });
|
|
434
|
+
}
|
|
385
435
|
if (loaded && !runtimeReady) {
|
|
386
436
|
// Core load is an independent readiness signal. Start the download even
|
|
387
437
|
// when the later TUI-ready IPC message is lost; handoff still waits for
|
|
@@ -395,12 +445,12 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
395
445
|
const result = await core.completed;
|
|
396
446
|
clearRunningUpdatePoll();
|
|
397
447
|
if (existingMessageHandler) core.child.off('message', existingMessageHandler);
|
|
398
|
-
if (result.error) throw result.error;
|
|
399
448
|
if (result.code === RUNNING_UPDATE_HANDOFF_EXIT_CODE
|
|
400
449
|
&& preparedTarget !== undefined
|
|
401
450
|
&& handoffSessionId !== undefined
|
|
402
451
|
&& handoffMode !== undefined) {
|
|
403
|
-
|
|
452
|
+
const previousActiveRuntime = (options.readActiveRuntime || readActiveRuntime)(env.BLUN_SHARED_HOME);
|
|
453
|
+
const activatedAt = (options.nowImpl || Date.now)();
|
|
404
454
|
const handoff = await handoffRuntime({
|
|
405
455
|
target: preparedTarget,
|
|
406
456
|
previous: { packageRoot, version: readPackageVersionAt(packageRoot) },
|
|
@@ -421,6 +471,11 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
421
471
|
},
|
|
422
472
|
activateTarget: async (target) => {
|
|
423
473
|
await (options.activateRuntime || activateRuntime)(env.BLUN_SHARED_HOME, target);
|
|
474
|
+
(options.recordRunningUpdateEvent || recordRunningUpdateEvent)(env.BLUN_SHARED_HOME, {
|
|
475
|
+
event: 'activated',
|
|
476
|
+
fromVersion: readPackageVersionAt(packageRoot),
|
|
477
|
+
toVersion: target.version,
|
|
478
|
+
}, { now: options.nowImpl });
|
|
424
479
|
try {
|
|
425
480
|
await (options.pruneRunningUpdateReleases || pruneRunningUpdateReleases)(env.BLUN_SHARED_HOME, {
|
|
426
481
|
activePackageRoot: target.packageRoot,
|
|
@@ -437,8 +492,49 @@ async function superviseProtectedCore(args, env, cwd, releaseNotice, options = {
|
|
|
437
492
|
...options,
|
|
438
493
|
existingCore: resumed,
|
|
439
494
|
packageRoot: handoff.runtime.packageRoot,
|
|
495
|
+
startupPendingRuntime: undefined,
|
|
496
|
+
staleActiveRuntime: undefined,
|
|
497
|
+
recoveryRuntime: handoff.kind === 'activated' ? {
|
|
498
|
+
activatedAt,
|
|
499
|
+
activeRuntime: previousActiveRuntime,
|
|
500
|
+
packageRoot,
|
|
501
|
+
version: readPackageVersionAt(packageRoot),
|
|
502
|
+
} : undefined,
|
|
440
503
|
});
|
|
441
504
|
}
|
|
505
|
+
const recovery = options.recoveryRuntime;
|
|
506
|
+
if (shouldRecoverActivatedRuntime(result, recovery, {
|
|
507
|
+
now: (options.nowImpl || Date.now)(),
|
|
508
|
+
runtimeExitIntent,
|
|
509
|
+
})) {
|
|
510
|
+
(options.recordRunningUpdateEvent || recordRunningUpdateEvent)(env.BLUN_SHARED_HOME, {
|
|
511
|
+
event: 'post-activation-exit',
|
|
512
|
+
activeVersion: readPackageVersionAt(packageRoot),
|
|
513
|
+
code: Number.isInteger(result.code) ? result.code : null,
|
|
514
|
+
error: result.error?.message || null,
|
|
515
|
+
signal: result.signal || null,
|
|
516
|
+
recoveryVersion: recovery.version,
|
|
517
|
+
}, { now: options.nowImpl });
|
|
518
|
+
await (options.restoreActiveRuntime || restoreActiveRuntime)(env.BLUN_SHARED_HOME, recovery.activeRuntime);
|
|
519
|
+
const fallback = spawnCore(args, env, cwd, { packageRoot: recovery.packageRoot });
|
|
520
|
+
const fallbackLoaded = await fallback.loaded;
|
|
521
|
+
const fallbackReady = fallbackLoaded && await waitForRuntimeReady(fallback, options.readyTimeoutMs);
|
|
522
|
+
(options.recordRunningUpdateEvent || recordRunningUpdateEvent)(env.BLUN_SHARED_HOME, {
|
|
523
|
+
event: fallbackReady ? 'recovery-ready' : 'recovery-failed',
|
|
524
|
+
version: recovery.version,
|
|
525
|
+
}, { now: options.nowImpl });
|
|
526
|
+
if (fallbackReady) {
|
|
527
|
+
return superviseProtectedCore(args, env, cwd, async () => {}, {
|
|
528
|
+
...options,
|
|
529
|
+
existingCore: fallback,
|
|
530
|
+
packageRoot: recovery.packageRoot,
|
|
531
|
+
startupPendingRuntime: undefined,
|
|
532
|
+
staleActiveRuntime: undefined,
|
|
533
|
+
recoveryRuntime: undefined,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (result.error) throw result.error;
|
|
442
538
|
return exitCodeForChild(result, loaded);
|
|
443
539
|
}
|
|
444
540
|
|
|
@@ -512,12 +608,12 @@ function spawnManagedLauncher(binary, cwd) {
|
|
|
512
608
|
});
|
|
513
609
|
}
|
|
514
610
|
|
|
515
|
-
function spawnActiveLauncher(packageRoot, cwd) {
|
|
611
|
+
function spawnActiveLauncher(packageRoot, cwd, args = RAW_ARGS) {
|
|
516
612
|
const entryName = path.basename(process.argv[1] || 'king.js').toLowerCase() === 'blun.js'
|
|
517
613
|
? 'blun.js'
|
|
518
614
|
: 'king.js';
|
|
519
615
|
return new Promise((resolve, reject) => {
|
|
520
|
-
const child = spawn(process.execPath, [path.join(packageRoot, 'bin', entryName), ...
|
|
616
|
+
const child = spawn(process.execPath, [path.join(packageRoot, 'bin', entryName), ...args], {
|
|
521
617
|
cwd,
|
|
522
618
|
env: { ...process.env, BLUN_ACTIVE_RUNTIME_ROOT: packageRoot },
|
|
523
619
|
stdio: 'inherit',
|
|
@@ -535,16 +631,40 @@ function spawnActiveLauncher(packageRoot, cwd) {
|
|
|
535
631
|
|
|
536
632
|
async function runLauncher(options = {}) {
|
|
537
633
|
const callerCwd = process.cwd();
|
|
634
|
+
let startupPendingRuntime;
|
|
635
|
+
let staleActiveRuntime;
|
|
538
636
|
if (options.skipActiveRuntime !== true) {
|
|
539
637
|
const activeHome = resolveLauncherPrivatePaths().sharedHome;
|
|
638
|
+
const pendingRuntime = readPendingRuntime(activeHome);
|
|
540
639
|
const activeRuntime = readActiveRuntime(activeHome);
|
|
541
640
|
const currentVersion = readPackageVersion();
|
|
542
|
-
if (
|
|
641
|
+
if (pendingRuntime !== null
|
|
642
|
+
&& path.resolve(pendingRuntime.packageRoot) !== PKG
|
|
643
|
+
&& compareSemver(currentVersion, pendingRuntime.version) === -1) {
|
|
644
|
+
const pendingArgs = pendingRuntime.mode !== undefined && pendingRuntime.sessionId !== undefined
|
|
645
|
+
? handoffArgsForMode(RAW_ARGS, pendingRuntime.mode, pendingRuntime.sessionId)
|
|
646
|
+
: RAW_ARGS;
|
|
647
|
+
process.exitCode = await spawnActiveLauncher(
|
|
648
|
+
pendingRuntime.packageRoot,
|
|
649
|
+
pendingRuntime.cwd || callerCwd,
|
|
650
|
+
pendingArgs,
|
|
651
|
+
);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (pendingRuntime !== null && path.resolve(pendingRuntime.packageRoot) === PKG) {
|
|
655
|
+
startupPendingRuntime = pendingRuntime;
|
|
656
|
+
}
|
|
657
|
+
if (startupPendingRuntime === undefined && activeRuntime !== null
|
|
543
658
|
&& path.resolve(activeRuntime.packageRoot) !== PKG
|
|
544
659
|
&& compareSemver(currentVersion, activeRuntime.version) === -1) {
|
|
545
660
|
process.exitCode = await spawnActiveLauncher(activeRuntime.packageRoot, callerCwd);
|
|
546
661
|
return;
|
|
547
662
|
}
|
|
663
|
+
if (activeRuntime !== null
|
|
664
|
+
&& path.resolve(activeRuntime.packageRoot) !== PKG
|
|
665
|
+
&& compareSemver(currentVersion, activeRuntime.version) >= 0) {
|
|
666
|
+
staleActiveRuntime = activeRuntime;
|
|
667
|
+
}
|
|
548
668
|
}
|
|
549
669
|
const mode = options.mode || launcherModeFromArgv(process.argv);
|
|
550
670
|
const explicitUpdateRequest = isNpmPackageUpdateRequest(ARGS);
|
|
@@ -759,6 +879,7 @@ async function runLauncher(options = {}) {
|
|
|
759
879
|
env,
|
|
760
880
|
callerCwd,
|
|
761
881
|
releaseNotice,
|
|
882
|
+
{ startupPendingRuntime, staleActiveRuntime },
|
|
762
883
|
);
|
|
763
884
|
} finally {
|
|
764
885
|
await releaseNotice();
|
|
@@ -768,14 +889,15 @@ async function runLauncher(options = {}) {
|
|
|
768
889
|
module.exports = {
|
|
769
890
|
RUNNING_UPDATE_RECHECK_JITTER_MS,
|
|
770
891
|
RUNNING_UPDATE_RECHECK_MS,
|
|
892
|
+
RUNNING_UPDATE_STABILIZATION_MS,
|
|
771
893
|
createManagedNodeEnvironment,
|
|
772
894
|
resolveLauncherPrivatePaths,
|
|
773
895
|
resolveHandoffCwd,
|
|
774
896
|
runningUpdateRecheckDelay,
|
|
775
897
|
runLauncher,
|
|
776
898
|
scheduleRunningUpdateRecheck,
|
|
899
|
+
shouldRecoverActivatedRuntime,
|
|
777
900
|
shouldDetachProtectedCore,
|
|
778
901
|
spawnManagedLauncher,
|
|
779
902
|
superviseProtectedCore,
|
|
780
|
-
writeRunningUpdateHandoffNotice,
|
|
781
903
|
};
|
package/bin/running-update.cjs
CHANGED
|
@@ -14,11 +14,13 @@ const PACKAGE_NAME = 'blun-king-cli';
|
|
|
14
14
|
const MANIFEST_URL = 'https://chat.blun.ai/blun-code-version.json';
|
|
15
15
|
const REGISTRY_URL = 'https://registry.npmjs.org/blun-king-cli';
|
|
16
16
|
const ACTIVE_RUNTIME_FILE = 'active-runtime.json';
|
|
17
|
+
const PENDING_RUNTIME_FILE = 'pending-runtime.json';
|
|
17
18
|
const RUNNING_UPDATE_HANDOFF_EXIT_CODE = 76;
|
|
18
19
|
const RUNNING_UPDATE_PREPARED_MESSAGE = 'blun-running-update-prepared';
|
|
19
20
|
const RUNNING_UPDATE_HANDOFF_MESSAGE = 'blun-running-update-handoff';
|
|
20
21
|
const RUNNING_UPDATE_MODE_MESSAGE = 'blun-running-update-mode';
|
|
21
22
|
const RUNTIME_READY_MESSAGE = 'blun-runtime-session-ready';
|
|
23
|
+
const RUNTIME_EXIT_INTENT_MESSAGE = 'blun-runtime-exit-intent';
|
|
22
24
|
const MAX_TARBALL_BYTES = 128 * 1024 * 1024;
|
|
23
25
|
const AUTO_UPDATE_SETTLE_MS = 30 * 60 * 1000;
|
|
24
26
|
const DEFAULT_RETAINED_RELEASE_VERSIONS = 3;
|
|
@@ -203,6 +205,10 @@ function activeRuntimePath(sharedHome) {
|
|
|
203
205
|
return path.join(path.resolve(sharedHome), 'updates', ACTIVE_RUNTIME_FILE);
|
|
204
206
|
}
|
|
205
207
|
|
|
208
|
+
function pendingRuntimePath(sharedHome) {
|
|
209
|
+
return path.join(path.resolve(sharedHome), 'updates', PENDING_RUNTIME_FILE);
|
|
210
|
+
}
|
|
211
|
+
|
|
206
212
|
function pathWithin(root, candidate) {
|
|
207
213
|
const relative = path.relative(root, candidate);
|
|
208
214
|
return relative === '' || (relative !== '..'
|
|
@@ -229,9 +235,8 @@ function verifyRuntimePackage(packageRoot, version, options = {}) {
|
|
|
229
235
|
}
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
function
|
|
238
|
+
function readRuntimeRecord(sharedHome, filePath) {
|
|
233
239
|
try {
|
|
234
|
-
const filePath = activeRuntimePath(sharedHome);
|
|
235
240
|
const stat = fs.lstatSync(filePath);
|
|
236
241
|
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 16 * 1024) return null;
|
|
237
242
|
const record = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
@@ -245,13 +250,31 @@ function readActiveRuntime(sharedHome) {
|
|
|
245
250
|
|| !verifyRuntimePackage(packageRoot, version, { sharedHome })) {
|
|
246
251
|
return null;
|
|
247
252
|
}
|
|
248
|
-
|
|
253
|
+
const mode = ownData(record, 'mode');
|
|
254
|
+
const sessionId = ownData(record, 'sessionId');
|
|
255
|
+
const cwd = ownData(record, 'cwd');
|
|
256
|
+
return Object.freeze({
|
|
257
|
+
version,
|
|
258
|
+
packageRoot: path.resolve(packageRoot),
|
|
259
|
+
integrity,
|
|
260
|
+
...(mode === 'resume' || mode === 'new' ? { mode } : {}),
|
|
261
|
+
...(typeof sessionId === 'string' && sessionId.length > 0 ? { sessionId } : {}),
|
|
262
|
+
...(typeof cwd === 'string' && path.isAbsolute(cwd) ? { cwd: path.resolve(cwd) } : {}),
|
|
263
|
+
});
|
|
249
264
|
} catch {
|
|
250
265
|
return null;
|
|
251
266
|
}
|
|
252
267
|
}
|
|
253
268
|
|
|
254
|
-
function
|
|
269
|
+
function readActiveRuntime(sharedHome) {
|
|
270
|
+
return readRuntimeRecord(sharedHome, activeRuntimePath(sharedHome));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function readPendingRuntime(sharedHome) {
|
|
274
|
+
return readRuntimeRecord(sharedHome, pendingRuntimePath(sharedHome));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function writeRuntimeRecord(sharedHome, fileName, target, extra = {}) {
|
|
255
278
|
if (!verifyRuntimePackage(target.packageRoot, target.version, { sharedHome })
|
|
256
279
|
|| !validIntegrity(target.integrity)) {
|
|
257
280
|
throw new Error('RUNNING_UPDATE_TARGET_INVALID');
|
|
@@ -259,14 +282,14 @@ function activateRuntime(sharedHome, target) {
|
|
|
259
282
|
const updateRoot = path.join(path.resolve(sharedHome), 'updates');
|
|
260
283
|
ensurePrivateDirectory(path.resolve(sharedHome));
|
|
261
284
|
ensurePrivateDirectory(updateRoot);
|
|
262
|
-
const filePath =
|
|
263
|
-
const temporaryPath = path.join(updateRoot, `.${
|
|
285
|
+
const filePath = path.join(updateRoot, fileName);
|
|
286
|
+
const temporaryPath = path.join(updateRoot, `.${fileName}.${process.pid}.${randomUUID()}.tmp`);
|
|
264
287
|
try {
|
|
265
288
|
writePrivateFile(temporaryPath, `${JSON.stringify({
|
|
266
289
|
version: target.version,
|
|
267
290
|
packageRoot: path.resolve(target.packageRoot),
|
|
268
291
|
integrity: target.integrity,
|
|
269
|
-
|
|
292
|
+
...extra,
|
|
270
293
|
})}\n`);
|
|
271
294
|
fs.renameSync(temporaryPath, filePath);
|
|
272
295
|
securePrivateFile(filePath);
|
|
@@ -276,6 +299,70 @@ function activateRuntime(sharedHome, target) {
|
|
|
276
299
|
}
|
|
277
300
|
}
|
|
278
301
|
|
|
302
|
+
function activateRuntime(sharedHome, target) {
|
|
303
|
+
writeRuntimeRecord(sharedHome, ACTIVE_RUNTIME_FILE, target, {
|
|
304
|
+
activatedAt: new Date().toISOString(),
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function stageRuntime(sharedHome, target, options = {}) {
|
|
309
|
+
const mode = options.mode === 'resume' || options.mode === 'new' ? options.mode : undefined;
|
|
310
|
+
const sessionId = typeof options.sessionId === 'string' && options.sessionId.length > 0
|
|
311
|
+
? options.sessionId
|
|
312
|
+
: undefined;
|
|
313
|
+
const cwd = typeof options.cwd === 'string' && path.isAbsolute(options.cwd)
|
|
314
|
+
? path.resolve(options.cwd)
|
|
315
|
+
: undefined;
|
|
316
|
+
writeRuntimeRecord(sharedHome, PENDING_RUNTIME_FILE, target, {
|
|
317
|
+
preparedAt: new Date((options.now || Date.now)()).toISOString(),
|
|
318
|
+
...(mode === undefined ? {} : { mode }),
|
|
319
|
+
...(sessionId === undefined ? {} : { sessionId }),
|
|
320
|
+
...(cwd === undefined ? {} : { cwd }),
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function clearRuntimeRecord(sharedHome, expected, readRecord, filePath) {
|
|
325
|
+
const runtime = readRecord(sharedHome);
|
|
326
|
+
if (runtime === null) return false;
|
|
327
|
+
if (expected !== undefined
|
|
328
|
+
&& (runtime.version !== expected.version
|
|
329
|
+
|| runtime.packageRoot !== path.resolve(expected.packageRoot))) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
fs.rmSync(filePath(sharedHome), { force: true });
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function clearActiveRuntime(sharedHome, expected) {
|
|
337
|
+
return clearRuntimeRecord(sharedHome, expected, readActiveRuntime, activeRuntimePath);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function clearPendingRuntime(sharedHome, expected) {
|
|
341
|
+
return clearRuntimeRecord(sharedHome, expected, readPendingRuntime, pendingRuntimePath);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function restoreActiveRuntime(sharedHome, runtime) {
|
|
345
|
+
if (runtime === null || runtime === undefined) {
|
|
346
|
+
fs.rmSync(activeRuntimePath(sharedHome), { force: true });
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
activateRuntime(sharedHome, runtime);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function recordRunningUpdateEvent(sharedHome, event, options = {}) {
|
|
353
|
+
if (typeof sharedHome !== 'string' || !path.isAbsolute(sharedHome)) return false;
|
|
354
|
+
const updateRoot = path.join(path.resolve(sharedHome), 'updates');
|
|
355
|
+
ensurePrivateDirectory(path.resolve(sharedHome));
|
|
356
|
+
ensurePrivateDirectory(updateRoot);
|
|
357
|
+
const filePath = path.join(updateRoot, 'handoff.jsonl');
|
|
358
|
+
fs.appendFileSync(filePath, `${JSON.stringify({
|
|
359
|
+
at: new Date((options.now || Date.now)()).toISOString(),
|
|
360
|
+
...event,
|
|
361
|
+
})}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
362
|
+
securePrivateFile(filePath);
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
279
366
|
function registryRelease(registry, version) {
|
|
280
367
|
const versions = ownData(registry, 'versions');
|
|
281
368
|
const entry = ownData(versions, version);
|
|
@@ -469,22 +556,31 @@ async function prepareRunningUpdate(options) {
|
|
|
469
556
|
|
|
470
557
|
module.exports = {
|
|
471
558
|
ACTIVE_RUNTIME_FILE,
|
|
559
|
+
PENDING_RUNTIME_FILE,
|
|
472
560
|
AUTO_UPDATE_SETTLE_MS,
|
|
473
561
|
RUNNING_UPDATE_HANDOFF_EXIT_CODE,
|
|
474
562
|
RUNNING_UPDATE_HANDOFF_MESSAGE,
|
|
475
563
|
RUNNING_UPDATE_MODE_MESSAGE,
|
|
476
564
|
RUNNING_UPDATE_PREPARED_MESSAGE,
|
|
565
|
+
RUNTIME_EXIT_INTENT_MESSAGE,
|
|
477
566
|
RUNTIME_READY_MESSAGE,
|
|
478
567
|
activateRuntime,
|
|
479
568
|
activeRuntimePath,
|
|
569
|
+
clearActiveRuntime,
|
|
570
|
+
clearPendingRuntime,
|
|
480
571
|
defaultProbeRuntime,
|
|
481
572
|
handoffArgsForMode,
|
|
482
573
|
handoffRuntime,
|
|
483
574
|
isSafeRuntimeBoundary,
|
|
484
575
|
prepareRunningUpdate,
|
|
485
576
|
pruneRunningUpdateReleases,
|
|
577
|
+
recordRunningUpdateEvent,
|
|
486
578
|
readActiveRuntime,
|
|
579
|
+
readPendingRuntime,
|
|
580
|
+
restoreActiveRuntime,
|
|
487
581
|
resumeArgsForHandoff,
|
|
488
582
|
selectRunningUpdateTarget,
|
|
583
|
+
stageRuntime,
|
|
584
|
+
pendingRuntimePath,
|
|
489
585
|
verifyRuntimePackage,
|
|
490
586
|
};
|
package/blun.mjs
CHANGED
|
@@ -78012,7 +78012,10 @@ function parseLoopInterval(rawInterval) {
|
|
|
78012
78012
|
cron: hours === 1 ? "0 * * * *" : `0 */${String(hours)} * * *`
|
|
78013
78013
|
};
|
|
78014
78014
|
}
|
|
78015
|
-
|
|
78015
|
+
return {
|
|
78016
|
+
interval: `${String(amount)}${unit}`,
|
|
78017
|
+
cron: "* * * * *"
|
|
78018
|
+
};
|
|
78016
78019
|
}
|
|
78017
78020
|
function invalidLoopInterval(rawInterval) {
|
|
78018
78021
|
return new BlunError(ErrorCodes.LOOP_INTERVAL_INVALID, `Invalid loop interval ${JSON.stringify(rawInterval)}`);
|
|
@@ -422400,9 +422403,14 @@ function parseLoopCommand(rawArgs) {
|
|
|
422400
422403
|
}
|
|
422401
422404
|
if (LOOP_CREATE_FILLERS.has(tokens[startIndex]?.toLowerCase() ?? "")) startIndex += 1;
|
|
422402
422405
|
const intervalToken = tokens[startIndex];
|
|
422403
|
-
|
|
422406
|
+
let interval = intervalToken === void 0 ? null : normalizeLoopIntervalToken(intervalToken);
|
|
422407
|
+
let intervalTokenCount = 1;
|
|
422408
|
+
if (interval === null && /^\d+$/u.test(intervalToken ?? "") && tokens[startIndex + 1] !== void 0) {
|
|
422409
|
+
interval = normalizeLoopIntervalToken(`${intervalToken}${tokens[startIndex + 1]}`);
|
|
422410
|
+
if (interval !== null) intervalTokenCount = 2;
|
|
422411
|
+
}
|
|
422404
422412
|
if (interval !== null) {
|
|
422405
|
-
const prompt = tokens.slice(startIndex +
|
|
422413
|
+
const prompt = tokens.slice(startIndex + intervalTokenCount).join(" ").trim();
|
|
422406
422414
|
if (prompt.length === 0) return { kind: "error" };
|
|
422407
422415
|
return {
|
|
422408
422416
|
kind: "create",
|
|
@@ -515041,6 +515049,7 @@ const {
|
|
|
515041
515049
|
RUNNING_UPDATE_HANDOFF_MESSAGE,
|
|
515042
515050
|
RUNNING_UPDATE_MODE_MESSAGE,
|
|
515043
515051
|
RUNNING_UPDATE_PREPARED_MESSAGE,
|
|
515052
|
+
RUNTIME_EXIT_INTENT_MESSAGE,
|
|
515044
515053
|
RUNTIME_READY_MESSAGE,
|
|
515045
515054
|
isSafeRuntimeBoundary,
|
|
515046
515055
|
pruneRunningUpdateReleases
|
|
@@ -515066,17 +515075,6 @@ function notifyRunningRuntimeReady(tui) {
|
|
|
515066
515075
|
}
|
|
515067
515076
|
function requestRunningUpdateAtSafeBoundary(tui) {
|
|
515068
515077
|
if (tui.runningUpdatePreparedVersion === void 0 || tui.runningUpdatePreparedMode === void 0 || tui.runningUpdateHandoffStarted || !process.connected) return false;
|
|
515069
|
-
const waitMs = tui.runningUpdateNotBeforeMs - Date.now();
|
|
515070
|
-
if (Number.isFinite(waitMs) && waitMs > 0) {
|
|
515071
|
-
if (tui.runningUpdateDelayTimer === void 0) {
|
|
515072
|
-
tui.runningUpdateDelayTimer = setTimeout(() => {
|
|
515073
|
-
tui.runningUpdateDelayTimer = void 0;
|
|
515074
|
-
requestRunningUpdateAtSafeBoundary(tui);
|
|
515075
|
-
}, waitMs);
|
|
515076
|
-
tui.runningUpdateDelayTimer.unref?.();
|
|
515077
|
-
}
|
|
515078
|
-
return false;
|
|
515079
|
-
}
|
|
515080
515078
|
const boundaryState = () => ({
|
|
515081
515079
|
isShuttingDown: tui.isShuttingDown,
|
|
515082
515080
|
streamingPhase: tui.state.appState.streamingPhase,
|
|
@@ -515100,9 +515098,7 @@ function requestRunningUpdateAtSafeBoundary(tui) {
|
|
|
515100
515098
|
}, (error) => {
|
|
515101
515099
|
if (error || !isSafeRuntimeBoundary(boundaryState())) {
|
|
515102
515100
|
tui.runningUpdateHandoffStarted = false;
|
|
515103
|
-
return;
|
|
515104
515101
|
}
|
|
515105
|
-
void tui.stop(RUNNING_UPDATE_HANDOFF_EXIT_CODE);
|
|
515106
515102
|
});
|
|
515107
515103
|
} catch {
|
|
515108
515104
|
tui.runningUpdateHandoffStarted = false;
|
|
@@ -515115,6 +515111,7 @@ function installRunningUpdateListener(tui) {
|
|
|
515115
515111
|
if (message?.type !== RUNNING_UPDATE_PREPARED_MESSAGE || typeof message.version !== "string" || message.mode !== RUNNING_UPDATE_MODES.RESUME && message.mode !== RUNNING_UPDATE_MODES.NEW) return;
|
|
515116
515112
|
tui.runningUpdatePreparedVersion = message.version;
|
|
515117
515113
|
tui.runningUpdatePreparedMode = message.mode;
|
|
515114
|
+
tui.showStatus(`Update ${message.version} ist bereit und wird beim nächsten Start aktiviert.`, "success");
|
|
515118
515115
|
requestRunningUpdateAtSafeBoundary(tui);
|
|
515119
515116
|
};
|
|
515120
515117
|
process.on("message", handler);
|
|
@@ -515207,8 +515204,6 @@ var BlunTUI = class {
|
|
|
515207
515204
|
runningUpdatePreparedVersion;
|
|
515208
515205
|
runningUpdatePreparedMode;
|
|
515209
515206
|
runningUpdateHandoffStarted = false;
|
|
515210
|
-
runningUpdateNotBeforeMs = Date.now() + AUTO_UPDATE_SETTLE_MS;
|
|
515211
|
-
runningUpdateDelayTimer;
|
|
515212
515207
|
runningUpdateMessageDispose;
|
|
515213
515208
|
startupPhaseMs = {};
|
|
515214
515209
|
lastActivityMode;
|
|
@@ -515734,6 +515729,9 @@ var BlunTUI = class {
|
|
|
515734
515729
|
async stop(exitCode) {
|
|
515735
515730
|
if (this.isShuttingDown) return;
|
|
515736
515731
|
this.isShuttingDown = true;
|
|
515732
|
+
if (exitCode === 0 && process.connected) try {
|
|
515733
|
+
process.send({ type: RUNTIME_EXIT_INTENT_MESSAGE });
|
|
515734
|
+
} catch {}
|
|
515737
515735
|
this.unregisterSignalHandlers();
|
|
515738
515736
|
this.aborted = true;
|
|
515739
515737
|
this.channelQueueDeadline?.dispose();
|