blun-king-cli 9.1.41 → 9.1.43
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 +12 -1
- package/README.md +11 -1
- package/bin/update-notice.js +103 -8
- package/blun.mjs +498 -215
- package/package.json +1 -1
package/LIESMICH.txt
CHANGED
|
@@ -9,7 +9,7 @@ Installation
|
|
|
9
9
|
------------
|
|
10
10
|
Die geprüfte Version exakt global installieren:
|
|
11
11
|
|
|
12
|
-
npm install -g blun-king-cli@9.1.
|
|
12
|
+
npm install -g blun-king-cli@9.1.43
|
|
13
13
|
|
|
14
14
|
Start
|
|
15
15
|
-----
|
|
@@ -20,6 +20,17 @@ Start
|
|
|
20
20
|
Beide Startbefehle verwenden dieselbe Version, dasselbe Konto, dasselbe Modell
|
|
21
21
|
und dieselben Befehle.
|
|
22
22
|
|
|
23
|
+
Zug-Wächter
|
|
24
|
+
-----------
|
|
25
|
+
Läuft ein Zug 20 Minuten ohne neues Werkzeugergebnis, meldet die Konsole den
|
|
26
|
+
Stand auch im verbundenen Telegram-Kanal. Fünf aufeinanderfolgende identische
|
|
27
|
+
fehlgeschlagene Werkzeugaufrufe beenden den Zug mit einer Fehlermeldung.
|
|
28
|
+
|
|
29
|
+
Die Grenzwerte lassen sich vor dem Start über diese Umgebungsvariablen ändern:
|
|
30
|
+
|
|
31
|
+
BLUN_TURN_WATCHDOG_IDLE_MINUTES
|
|
32
|
+
BLUN_TURN_WATCHDOG_MAX_FAILED_REPETITIONS
|
|
33
|
+
|
|
23
34
|
Nachweisbare Arbeitsabläufe
|
|
24
35
|
---------------------------
|
|
25
36
|
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Voraussetzung ist Node.js 24.15 oder neuer. Die geprüfte Version wird exakt
|
|
|
9
9
|
installiert:
|
|
10
10
|
|
|
11
11
|
```powershell
|
|
12
|
-
npm install -g blun-king-cli@9.1.
|
|
12
|
+
npm install -g blun-king-cli@9.1.43
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Reproduzierbares Staging und Packen
|
|
@@ -35,6 +35,16 @@ an. Version, Konto, Modell und Befehle sind ansonsten identisch. Der Unterschied
|
|
|
35
35
|
gilt nur für den laufenden Prozess; die gespeicherte
|
|
36
36
|
Plugin-Konfiguration wird nicht umgeschrieben.
|
|
37
37
|
|
|
38
|
+
## Zug-Wächter
|
|
39
|
+
|
|
40
|
+
Läuft ein Zug 20 Minuten ohne neues Werkzeugergebnis, meldet die Konsole den
|
|
41
|
+
Stand auch im verbundenen Telegram-Kanal. Fünf aufeinanderfolgende identische
|
|
42
|
+
fehlgeschlagene Werkzeugaufrufe beenden den Zug mit einer Fehlermeldung.
|
|
43
|
+
|
|
44
|
+
Die Grenzwerte lassen sich vor dem Start mit
|
|
45
|
+
`BLUN_TURN_WATCHDOG_IDLE_MINUTES` und
|
|
46
|
+
`BLUN_TURN_WATCHDOG_MAX_FAILED_REPETITIONS` ändern.
|
|
47
|
+
|
|
38
48
|
Beim ersten Start werden das Telegram-Plugin und die mitgelieferten Skills
|
|
39
49
|
eingerichtet. Die Anmeldung erfolgt anschließend in der
|
|
40
50
|
Konsole mit `/login` über den BLUN-OAuth-Server. Das Paket erzeugt keine
|
package/bin/update-notice.js
CHANGED
|
@@ -26,6 +26,7 @@ const STATE_FILE = 'update-notice.json';
|
|
|
26
26
|
const UPDATE_SKIP_FILE = 'update-skip-version.json';
|
|
27
27
|
const PENDING_UPDATE_FILE = 'update-pending.json';
|
|
28
28
|
const UPDATE_DIRECTORY = 'updates';
|
|
29
|
+
const CORE_INSTALL_STATE_FILE = 'install.json';
|
|
29
30
|
const UPDATE_KEY_ESCAPE_TIMEOUT_MS = 500;
|
|
30
31
|
const UPDATE_SIGNAL_EXIT_CODES = Object.freeze({
|
|
31
32
|
SIGHUP: 129,
|
|
@@ -45,14 +46,25 @@ const SEMVER_PATTERN = new RegExp(
|
|
|
45
46
|
+ `(?:-(${SEMVER_IDENTIFIER}(?:\\.${SEMVER_IDENTIFIER})*))?`
|
|
46
47
|
+ '(?:\\+([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?$',
|
|
47
48
|
);
|
|
49
|
+
const LEGACY_MANIFEST_KEYS = Object.freeze([
|
|
50
|
+
'install',
|
|
51
|
+
'latest',
|
|
52
|
+
'minSupported',
|
|
53
|
+
'name',
|
|
54
|
+
'notesUrl',
|
|
55
|
+
'releasedAt',
|
|
56
|
+
]);
|
|
48
57
|
const MANIFEST_KEYS = Object.freeze([
|
|
49
58
|
'install',
|
|
50
59
|
'latest',
|
|
51
60
|
'minSupported',
|
|
52
61
|
'name',
|
|
53
62
|
'notesUrl',
|
|
63
|
+
'releaseNotes',
|
|
54
64
|
'releasedAt',
|
|
55
65
|
]);
|
|
66
|
+
const RELEASE_NOTES_MAX_COUNT = 8;
|
|
67
|
+
const RELEASE_NOTE_MAX_BYTES = 240;
|
|
56
68
|
const ENDPOINT_LIMITS = new Map([
|
|
57
69
|
[REGISTRY_URL, 1024 * 1024],
|
|
58
70
|
[FALLBACK_MANIFEST_URL, 64 * 1024],
|
|
@@ -192,9 +204,23 @@ function isIsoTimestamp(value) {
|
|
|
192
204
|
&& date.getUTCSeconds() === Number(match[6]);
|
|
193
205
|
}
|
|
194
206
|
|
|
207
|
+
function parseReleaseNotes(value) {
|
|
208
|
+
if (!Array.isArray(value) || value.length > RELEASE_NOTES_MAX_COUNT) return undefined;
|
|
209
|
+
const notes = [];
|
|
210
|
+
for (const note of value) {
|
|
211
|
+
if (!isBoundedText(note, RELEASE_NOTE_MAX_BYTES)) return undefined;
|
|
212
|
+
notes.push(note);
|
|
213
|
+
}
|
|
214
|
+
return Object.freeze(notes);
|
|
215
|
+
}
|
|
216
|
+
|
|
195
217
|
function parseFallbackManifest(value) {
|
|
196
|
-
const record = exactDataRecord(value, MANIFEST_KEYS)
|
|
218
|
+
const record = exactDataRecord(value, MANIFEST_KEYS)
|
|
219
|
+
|| exactDataRecord(value, LEGACY_MANIFEST_KEYS);
|
|
197
220
|
const notesUrl = record ? canonicalOfficialNotesUrl(record.notesUrl) : undefined;
|
|
221
|
+
const releaseNotes = record && Object.hasOwn(record, 'releaseNotes')
|
|
222
|
+
? parseReleaseNotes(record.releaseNotes)
|
|
223
|
+
: undefined;
|
|
198
224
|
if (!record
|
|
199
225
|
|| record.name !== PACKAGE_NAME
|
|
200
226
|
|| !parseSemver(record.latest)
|
|
@@ -202,6 +228,7 @@ function parseFallbackManifest(value) {
|
|
|
202
228
|
|| compareSemver(record.minSupported, record.latest) === 1
|
|
203
229
|
|| !isIsoTimestamp(record.releasedAt)
|
|
204
230
|
|| !notesUrl
|
|
231
|
+
|| (Object.hasOwn(record, 'releaseNotes') && releaseNotes === undefined)
|
|
205
232
|
|| !isBoundedText(record.install, 256)) {
|
|
206
233
|
return undefined;
|
|
207
234
|
}
|
|
@@ -211,6 +238,7 @@ function parseFallbackManifest(value) {
|
|
|
211
238
|
minSupported: record.minSupported,
|
|
212
239
|
releasedAt: record.releasedAt,
|
|
213
240
|
notesUrl,
|
|
241
|
+
...(releaseNotes === undefined ? {} : { releaseNotes }),
|
|
214
242
|
install: record.install,
|
|
215
243
|
});
|
|
216
244
|
}
|
|
@@ -243,7 +271,14 @@ function resolveEffectiveRelease(currentVersion, registryTags, fallbackManifest)
|
|
|
243
271
|
if (compareSemver(version, currentVersion) <= 0) return undefined;
|
|
244
272
|
|
|
245
273
|
if (validFallback && compareSemver(validFallback.latest, version) === 0) {
|
|
246
|
-
return {
|
|
274
|
+
return {
|
|
275
|
+
channel: 'stable',
|
|
276
|
+
version,
|
|
277
|
+
notesUrl: validFallback.notesUrl,
|
|
278
|
+
...(validFallback.releaseNotes === undefined
|
|
279
|
+
? {}
|
|
280
|
+
: { releaseNotes: validFallback.releaseNotes }),
|
|
281
|
+
};
|
|
247
282
|
}
|
|
248
283
|
return { channel: 'stable', version };
|
|
249
284
|
}
|
|
@@ -495,6 +530,50 @@ function clearPendingUpdate(blunDir) {
|
|
|
495
530
|
fs.rmSync(pendingUpdatePath(blunDir), { force: true });
|
|
496
531
|
}
|
|
497
532
|
|
|
533
|
+
function writeCoreInstallSuccess(
|
|
534
|
+
blunDir,
|
|
535
|
+
fromVersion,
|
|
536
|
+
version,
|
|
537
|
+
releaseNotes,
|
|
538
|
+
now = Date.now(),
|
|
539
|
+
) {
|
|
540
|
+
if (!parseSemver(fromVersion) || !parseSemver(version)) {
|
|
541
|
+
throw new Error('INVALID_UPDATE_VERSION');
|
|
542
|
+
}
|
|
543
|
+
const updateDirectory = path.join(blunDir, UPDATE_DIRECTORY);
|
|
544
|
+
ensurePrivateDirectory(blunDir);
|
|
545
|
+
ensurePrivateDirectory(updateDirectory);
|
|
546
|
+
const filePath = path.join(updateDirectory, CORE_INSTALL_STATE_FILE);
|
|
547
|
+
const temporaryPath = path.join(
|
|
548
|
+
updateDirectory,
|
|
549
|
+
`.${CORE_INSTALL_STATE_FILE}.${process.pid}.${randomUUID()}.tmp`,
|
|
550
|
+
);
|
|
551
|
+
try {
|
|
552
|
+
writePrivateFile(temporaryPath, `${JSON.stringify({
|
|
553
|
+
active: null,
|
|
554
|
+
lastFailure: null,
|
|
555
|
+
lastSuccess: {
|
|
556
|
+
version,
|
|
557
|
+
fromVersion,
|
|
558
|
+
...(releaseNotes === undefined ? {} : { releaseNotes }),
|
|
559
|
+
installedAt: new Date(now).toISOString(),
|
|
560
|
+
notifiedAt: null,
|
|
561
|
+
},
|
|
562
|
+
})}\n`);
|
|
563
|
+
const handle = fs.openSync(temporaryPath, 'r+');
|
|
564
|
+
try {
|
|
565
|
+
fs.fsyncSync(handle);
|
|
566
|
+
} finally {
|
|
567
|
+
fs.closeSync(handle);
|
|
568
|
+
}
|
|
569
|
+
fs.renameSync(temporaryPath, filePath);
|
|
570
|
+
securePrivateFile(filePath);
|
|
571
|
+
} catch (error) {
|
|
572
|
+
fs.rmSync(temporaryPath, { force: true });
|
|
573
|
+
throw error;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
498
577
|
function isInteractiveUpdateStart({ argv, env, stdin, stdout }) {
|
|
499
578
|
const isOrdinaryStartup = Array.isArray(argv)
|
|
500
579
|
&& (argv.length === 0
|
|
@@ -1178,10 +1257,10 @@ function noReleaseReason(currentVersion, registryTags, fallbackManifest) {
|
|
|
1178
1257
|
return parseSemver(latest) || fallbackManifest ? 'current' : 'unavailable';
|
|
1179
1258
|
}
|
|
1180
1259
|
|
|
1181
|
-
function explicitNoTarget(stdout, reason) {
|
|
1260
|
+
function explicitNoTarget(stdout, reason, currentVersion) {
|
|
1182
1261
|
stdout.write(reason === 'current'
|
|
1183
|
-
?
|
|
1184
|
-
:
|
|
1262
|
+
? `Kein Update verfügbar. Installiert: ${currentVersion} · Aktuell: ${currentVersion}.\n`
|
|
1263
|
+
: `Kein sicherer Update-Zielstand verfügbar. Installiert: ${currentVersion}.\n`);
|
|
1185
1264
|
return { kind: 'current', reason };
|
|
1186
1265
|
}
|
|
1187
1266
|
|
|
@@ -1280,7 +1359,7 @@ async function runUpdateFlow(options, explicitUpdate) {
|
|
|
1280
1359
|
sources = await (options.loadReleaseSources || loadReleaseSources)();
|
|
1281
1360
|
} catch {
|
|
1282
1361
|
return explicitUpdate
|
|
1283
|
-
? explicitNoTarget(stdout, 'unavailable')
|
|
1362
|
+
? explicitNoTarget(stdout, 'unavailable', currentVersion)
|
|
1284
1363
|
: { kind: 'continue', reason: 'unavailable' };
|
|
1285
1364
|
}
|
|
1286
1365
|
const registryTags = parseRegistryDocument(sources?.registryDocument);
|
|
@@ -1289,7 +1368,7 @@ async function runUpdateFlow(options, explicitUpdate) {
|
|
|
1289
1368
|
if (!release) {
|
|
1290
1369
|
const reason = noReleaseReason(currentVersion, registryTags, fallbackManifest);
|
|
1291
1370
|
return explicitUpdate
|
|
1292
|
-
? explicitNoTarget(stdout, reason)
|
|
1371
|
+
? explicitNoTarget(stdout, reason, currentVersion)
|
|
1293
1372
|
: { kind: 'continue', reason };
|
|
1294
1373
|
}
|
|
1295
1374
|
const queuedForRelease = pendingVersion === release.version;
|
|
@@ -1413,7 +1492,23 @@ async function runUpdateFlow(options, explicitUpdate) {
|
|
|
1413
1492
|
if (installResult?.code === 0 && installResult?.verified === true) {
|
|
1414
1493
|
updateProgress(4, 'Installation geprüft');
|
|
1415
1494
|
clearPendingUpdate(blunDir);
|
|
1416
|
-
|
|
1495
|
+
try {
|
|
1496
|
+
writeCoreInstallSuccess(
|
|
1497
|
+
blunDir,
|
|
1498
|
+
currentVersion,
|
|
1499
|
+
release.version,
|
|
1500
|
+
release.releaseNotes,
|
|
1501
|
+
(options.now || Date.now)(),
|
|
1502
|
+
);
|
|
1503
|
+
} catch {
|
|
1504
|
+
stderr.write(
|
|
1505
|
+
`Update ${release.version} wurde installiert, aber die Startmeldung konnte nicht gespeichert werden.\n`,
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
stdout.write(
|
|
1509
|
+
`Update ${currentVersion} -> ${release.version} erfolgreich installiert. Starte BLUN Code neu, um die neue Version zu verwenden.\n`,
|
|
1510
|
+
);
|
|
1511
|
+
if (release.notesUrl) stdout.write(`Versionshinweise: ${release.notesUrl}\n`);
|
|
1417
1512
|
return { kind: 'installed', version: release.version };
|
|
1418
1513
|
}
|
|
1419
1514
|
|