blun-king-cli 9.1.584 → 9.1.586
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/CHANGELOG.md +12 -0
- package/blun.mjs +116 -21
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 9.1.586 - 2026-09-09
|
|
2
|
+
|
|
3
|
+
- Fix Telegram receiver startup on Windows when an old bridge PID has been reused by an unrelated process. Preserve uncertain process ownership and never stop that unrelated process.
|
|
4
|
+
- Keep context compaction, model settings, conversation history, and existing Telegram delivery behavior unchanged.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## 9.1.585 - 2026-09-08
|
|
8
|
+
|
|
9
|
+
- Managed WebSearch validates result envelopes and source rows even when optional search status metadata is absent.
|
|
10
|
+
- Malformed successful HTTP responses no longer appear as empty searches; invalid source rows fail the whole response before results or partial-source notices are returned.
|
|
11
|
+
- Valid legacy responses, empty result arrays and empty string excerpts remain supported. Cancellation, request authentication, FetchURL and Telegram behavior are unchanged.
|
|
12
|
+
|
|
1
13
|
## 9.1.584 - 2026-09-08
|
|
2
14
|
|
|
3
15
|
- WebSearch forwards caller cancellation through the tool and managed HTTP search request, including response-body reads.
|
package/blun.mjs
CHANGED
|
@@ -315303,10 +315303,25 @@ var init_blun_web_search = __esmMin((() => {
|
|
|
315303
315303
|
}
|
|
315304
315304
|
const json = (await response.json());
|
|
315305
315305
|
signal?.throwIfAborted();
|
|
315306
|
-
|
|
315306
|
+
if (!json || typeof json !== 'object' || Array.isArray(json) || !Array.isArray(json.search_results))
|
|
315307
|
+
throw new Error('Invalid search results response.');
|
|
315308
|
+
const raw = json.search_results;
|
|
315309
|
+
if (!raw.every(row => {
|
|
315310
|
+
if (!row || typeof row.title !== 'string' || !row.title.trim()
|
|
315311
|
+
|| typeof row.snippet !== 'string' || typeof row.url !== 'string')
|
|
315312
|
+
return false;
|
|
315313
|
+
try {
|
|
315314
|
+
const url = new URL(row.url);
|
|
315315
|
+
return (url.protocol === 'https:' || url.protocol === 'http:') && !url.username && !url.password;
|
|
315316
|
+
}
|
|
315317
|
+
catch {
|
|
315318
|
+
return false;
|
|
315319
|
+
}
|
|
315320
|
+
}))
|
|
315321
|
+
throw new Error('Invalid search results response.');
|
|
315307
315322
|
const status = json.search_status;
|
|
315308
315323
|
if (status !== undefined) {
|
|
315309
|
-
if (!status || typeof status !== 'object'
|
|
315324
|
+
if (!status || typeof status !== 'object'
|
|
315310
315325
|
|| !Number.isInteger(status.failed_sources) || typeof status.failed_sources !== 'number'
|
|
315311
315326
|
|| status.failed_sources < 0 || status.failed_sources > 128
|
|
315312
315327
|
|| (status.state !== 'complete' && status.state !== 'partial')
|
|
@@ -315314,19 +315329,6 @@ var init_blun_web_search = __esmMin((() => {
|
|
|
315314
315329
|
|| (status.state === 'partial' && (status.failed_sources === 0 || raw.length === 0))) {
|
|
315315
315330
|
throw new Error('Invalid search status response.');
|
|
315316
315331
|
}
|
|
315317
|
-
if (!raw.every(row => {
|
|
315318
|
-
if (!row || typeof row.title !== 'string' || !row.title.trim()
|
|
315319
|
-
|| typeof row.snippet !== 'string' || typeof row.url !== 'string')
|
|
315320
|
-
return false;
|
|
315321
|
-
try {
|
|
315322
|
-
const url = new URL(row.url);
|
|
315323
|
-
return (url.protocol === 'https:' || url.protocol === 'http:') && !url.username && !url.password;
|
|
315324
|
-
}
|
|
315325
|
-
catch {
|
|
315326
|
-
return false;
|
|
315327
|
-
}
|
|
315328
|
-
}))
|
|
315329
|
-
throw new Error('Invalid search results response.');
|
|
315330
315332
|
if (status.state === 'partial') {
|
|
315331
315333
|
if (!supportsNotices)
|
|
315332
315334
|
throw new Error('Partial search results require a notice consumer.');
|
|
@@ -422275,7 +422277,41 @@ function resolveTelegramMcpEntry() {
|
|
|
422275
422277
|
];
|
|
422276
422278
|
for (const candidate of candidates) if (candidate !== void 0 && existsSync(candidate)) return resolveKnownTelegramMcp(candidate, { explicitOverride: candidate === process.env["BLUN_TELEGRAM_MCP_SERVER"] });
|
|
422277
422279
|
}
|
|
422280
|
+
import { fstatSync as __blunTelegramPidFstatSync } from "node:fs";
|
|
422281
|
+
"use strict";
|
|
422282
|
+
const PROCESS_START_LOOKUP_TIMEOUT_MS = 1500;
|
|
422283
|
+
const PID_CLAIM_TIME_TOLERANCE_MS = 1000;
|
|
422284
|
+
function readNonBridgeProcessStartedAt(pid) {
|
|
422285
|
+
if (process.platform !== 'win32' || !Number.isSafeInteger(pid) || pid <= 1)
|
|
422286
|
+
return undefined;
|
|
422287
|
+
try {
|
|
422288
|
+
const output = execFileSync('powershell.exe', [
|
|
422289
|
+
'-NoProfile', '-NonInteractive', '-Command',
|
|
422290
|
+
`$ErrorActionPreference='Stop'; $p=Get-CimInstance Win32_Process -Filter 'ProcessId=${pid}'; ` +
|
|
422291
|
+
// A recognizable bridge is preserved even after a wall-clock correction.
|
|
422292
|
+
`if($null -ne $p -and -not [string]::IsNullOrWhiteSpace($p.CommandLine) -and $p.CommandLine -notmatch 'bridge\\.mjs'){` +
|
|
422293
|
+
`([DateTimeOffset]$p.CreationDate.ToUniversalTime()).ToUnixTimeMilliseconds()}`,
|
|
422294
|
+
], {
|
|
422295
|
+
encoding: 'utf8',
|
|
422296
|
+
windowsHide: true,
|
|
422297
|
+
timeout: PROCESS_START_LOOKUP_TIMEOUT_MS,
|
|
422298
|
+
maxBuffer: 4096,
|
|
422299
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
422300
|
+
}).trim();
|
|
422301
|
+
if (!/^\d+$/.test(output))
|
|
422302
|
+
return undefined;
|
|
422303
|
+
const startedAt = Number(output);
|
|
422304
|
+
return Number.isSafeInteger(startedAt) && startedAt > 0 ? startedAt : undefined;
|
|
422305
|
+
}
|
|
422306
|
+
catch {
|
|
422307
|
+
return undefined;
|
|
422308
|
+
}
|
|
422309
|
+
}
|
|
422310
|
+
|
|
422278
422311
|
var TelegramChannelController = class {
|
|
422312
|
+
bridgeClaimUncertain = false;
|
|
422313
|
+
readProcessStartedAt;
|
|
422314
|
+
|
|
422279
422315
|
host;
|
|
422280
422316
|
dir;
|
|
422281
422317
|
heartbeatTimer;
|
|
@@ -422341,7 +422377,9 @@ var TelegramChannelController = class {
|
|
|
422341
422377
|
return false;
|
|
422342
422378
|
}
|
|
422343
422379
|
});
|
|
422344
|
-
|
|
422380
|
+
|
|
422381
|
+
this.readProcessStartedAt = options.readProcessStartedAt ?? readNonBridgeProcessStartedAt;
|
|
422382
|
+
}
|
|
422345
422383
|
get leaseFile() {
|
|
422346
422384
|
return join(this.dir, "tui.pid");
|
|
422347
422385
|
}
|
|
@@ -422464,11 +422502,68 @@ var TelegramChannelController = class {
|
|
|
422464
422502
|
this.handoffTimer.unref();
|
|
422465
422503
|
}
|
|
422466
422504
|
liveBridgePid() {
|
|
422467
|
-
|
|
422468
|
-
|
|
422469
|
-
|
|
422470
|
-
|
|
422471
|
-
|
|
422505
|
+
if (this.bridgeClaimUncertain)
|
|
422506
|
+
return null;
|
|
422507
|
+
let pid;
|
|
422508
|
+
let claim;
|
|
422509
|
+
let stats;
|
|
422510
|
+
try {
|
|
422511
|
+
const fd = openSync(this.botPidFile, 'r');
|
|
422512
|
+
try {
|
|
422513
|
+
claim = readFileSync(fd, 'utf8');
|
|
422514
|
+
stats = __blunTelegramPidFstatSync(fd);
|
|
422515
|
+
}
|
|
422516
|
+
finally {
|
|
422517
|
+
closeSync(fd);
|
|
422518
|
+
}
|
|
422519
|
+
pid = parseInt(claim, 10);
|
|
422520
|
+
if (!Number.isInteger(pid) || pid <= 1)
|
|
422521
|
+
return null;
|
|
422522
|
+
if (!this.isProcessAlive(pid))
|
|
422523
|
+
return undefined;
|
|
422524
|
+
}
|
|
422525
|
+
catch (error) {
|
|
422526
|
+
// Only an absent claim proves that reading it did not hide an owner.
|
|
422527
|
+
return error.code === 'ENOENT' ? undefined : null;
|
|
422528
|
+
}
|
|
422529
|
+
try {
|
|
422530
|
+
const startedAt = this.readProcessStartedAt(pid);
|
|
422531
|
+
if (startedAt !== undefined && Number.isFinite(startedAt)
|
|
422532
|
+
&& startedAt > stats.mtimeMs + PID_CLAIM_TIME_TOLERANCE_MS
|
|
422533
|
+
&& this.ownsChannel() && this.retireReusedBridgePid(claim, stats))
|
|
422534
|
+
return undefined;
|
|
422535
|
+
}
|
|
422536
|
+
catch {
|
|
422537
|
+
// A failed identity or archive lookup must not launch a second poller.
|
|
422538
|
+
}
|
|
422539
|
+
return pid;
|
|
422540
|
+
}
|
|
422541
|
+
retireReusedBridgePid(claim, original) {
|
|
422542
|
+
const matches = (file) => {
|
|
422543
|
+
const current = lstatSync(file);
|
|
422544
|
+
return current.isFile() && current.dev === original.dev && current.ino === original.ino
|
|
422545
|
+
&& current.size === original.size && current.mtimeMs === original.mtimeMs
|
|
422546
|
+
&& readFileSync(file, 'utf8') === claim;
|
|
422547
|
+
};
|
|
422548
|
+
if (!matches(this.botPidFile))
|
|
422549
|
+
return false;
|
|
422550
|
+
const retired = join(this.dir, `bot.pid.stale-${randomUUID()}`);
|
|
422551
|
+
renameSync(this.botPidFile, retired);
|
|
422552
|
+
try {
|
|
422553
|
+
if (matches(retired))
|
|
422554
|
+
return true;
|
|
422555
|
+
}
|
|
422556
|
+
catch {
|
|
422557
|
+
// Restore the name if the moved claim cannot be verified.
|
|
422558
|
+
}
|
|
422559
|
+
this.bridgeClaimUncertain = true;
|
|
422560
|
+
// Do not overwrite a newer claim if another writer won the rename window.
|
|
422561
|
+
try {
|
|
422562
|
+
linkSync(retired, this.botPidFile);
|
|
422563
|
+
}
|
|
422564
|
+
catch { /* A current claim wins. */ }
|
|
422565
|
+
return false;
|
|
422566
|
+
}
|
|
422472
422567
|
releaseOwnership() {
|
|
422473
422568
|
try {
|
|
422474
422569
|
if (this.readOwner()?.ownerId === this.ownerId) rmSync(this.ownerFile);
|