blun-king-cli 9.1.403 → 9.1.405
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 +18 -5
- package/README.md +17 -5
- package/bin/config-write-dedup-policy.cjs +27 -0
- package/bin/telegram-console-status-policy.cjs +102 -0
- package/bin/telegram-remote-status-policy.cjs +47 -4
- package/blun.mjs +31 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge.mjs +30 -5
package/LIESMICH.txt
CHANGED
|
@@ -307,11 +307,15 @@ Rechner aus Telegram prüfen
|
|
|
307
307
|
----------------------------
|
|
308
308
|
Der Telegram-Befehl /status prüft die lokale Verbindung ohne Modellaufruf. Er
|
|
309
309
|
zeigt den Rechnernamen, die installierte BLUN-Version, die Laufzeit der
|
|
310
|
-
Telegram-Brücke, die Verbindung
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
310
|
+
Telegram-Brücke, die Verbindung und Laufzeit der Konsole, den aktuellen
|
|
311
|
+
Zustellweg, den Konsolen-Herzschlag, den letzten Warteschlangen-Fortschritt sowie
|
|
312
|
+
den aktuellen Arbeitsschritt. Die Warteschlange wird als tatsächlich ungelesener
|
|
313
|
+
Anteil und Gesamtgröße gemessen; ein Prüfpunkt wird nur für exakt dieselbe
|
|
314
|
+
Warteschlangendatei akzeptiert. Damit bleibt der Zustand auch dann abfragbar,
|
|
315
|
+
wenn die Konsole hängt oder der Modellanbieter ausgelastet ist. Die Ausgabe
|
|
316
|
+
enthält keine Chat-IDs, Dateipfade, Zugangsdaten, Nachrichteninhalte oder
|
|
317
|
+
vollständigen Aufgabenlisten. Nicht verbundene Absender erhalten weiterhin nur
|
|
318
|
+
die Kopplungsanweisung.
|
|
315
319
|
|
|
316
320
|
Nachweisbare Arbeitsabläufe
|
|
317
321
|
---------------------------
|
|
@@ -391,6 +395,15 @@ Anfrage höchstens 1.000 Zeichen. Eingebettete Inhalte aus
|
|
|
391
395
|
große Anhänge weder die Auswahl unnötig verteuern noch anhand ihres Inhalts ein
|
|
392
396
|
fachfremdes Werkzeug vorladen.
|
|
393
397
|
|
|
398
|
+
Ruhige Konfigurationsaktualisierung
|
|
399
|
+
-----------------------------------
|
|
400
|
+
|
|
401
|
+
Ab BLUN King 9.1.404 ersetzt eine Konfigurationsaktualisierung die private
|
|
402
|
+
config.toml nur noch, wenn sich ihre serialisierten Bytes tatsächlich ändern.
|
|
403
|
+
Bei identischem Inhalt bleiben Dateidentität und Änderungszeit erhalten; die
|
|
404
|
+
Prüfung und Härtung der privaten Dateirechte läuft trotzdem weiter. Echte
|
|
405
|
+
Änderungen verwenden unverändert den crash-sicheren atomaren Schreibweg.
|
|
406
|
+
|
|
394
407
|
Der eigentliche Anhang und die vollständige Nutzernachricht bleiben für den Zug
|
|
395
408
|
unverändert verfügbar. Die Begrenzung betrifft ausschließlich die kleine
|
|
396
409
|
lexikalische Vorauswahl von höchstens zwei Werkzeugschemas; ToolSearch und alle
|
package/README.md
CHANGED
|
@@ -313,11 +313,15 @@ Sicherheitsgründen als normale Chatnachrichten behandelt.
|
|
|
313
313
|
|
|
314
314
|
Der Telegram-Befehl `/status` prüft die lokale Verbindung ohne Modellaufruf.
|
|
315
315
|
Er zeigt den Rechnernamen, die installierte BLUN-Version, die Laufzeit der
|
|
316
|
-
Telegram-Brücke, die Verbindung
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
316
|
+
Telegram-Brücke, die Verbindung und Laufzeit der Konsole, den aktuellen
|
|
317
|
+
Zustellweg, den Konsolen-Herzschlag, den letzten Warteschlangen-Fortschritt sowie
|
|
318
|
+
den aktuellen Arbeitsschritt. Die Warteschlange wird als tatsächlich ungelesener
|
|
319
|
+
Anteil und Gesamtgröße gemessen; ein Prüfpunkt wird nur für exakt dieselbe
|
|
320
|
+
Warteschlangendatei akzeptiert. Damit bleibt der Zustand auch dann abfragbar,
|
|
321
|
+
wenn die Konsole hängt oder der Modellanbieter ausgelastet ist. Die Ausgabe
|
|
322
|
+
enthält keine Chat-IDs, Dateipfade, Zugangsdaten, Nachrichteninhalte oder
|
|
323
|
+
vollständigen Aufgabenlisten. Nicht verbundene Absender erhalten weiterhin nur
|
|
324
|
+
die Kopplungsanweisung.
|
|
321
325
|
|
|
322
326
|
Beim ersten Start werden das Telegram-Plugin und die mitgelieferten Skills
|
|
323
327
|
eingerichtet. Die Anmeldung erfolgt anschließend in der
|
|
@@ -400,6 +404,14 @@ Anfrage höchstens 1.000 Zeichen. Eingebettete Inhalte aus
|
|
|
400
404
|
große Anhänge weder die Auswahl unnötig verteuern noch anhand ihres Inhalts ein
|
|
401
405
|
fachfremdes Werkzeug vorladen.
|
|
402
406
|
|
|
407
|
+
## Ruhige Konfigurationsaktualisierung
|
|
408
|
+
|
|
409
|
+
Ab BLUN King 9.1.404 ersetzt eine Konfigurationsaktualisierung die private
|
|
410
|
+
`config.toml` nur noch, wenn sich ihre serialisierten Bytes tatsächlich ändern.
|
|
411
|
+
Bei identischem Inhalt bleiben Dateidentität und Änderungszeit erhalten; die
|
|
412
|
+
Prüfung und Härtung der privaten Dateirechte läuft trotzdem weiter. Echte
|
|
413
|
+
Änderungen verwenden unverändert den crash-sicheren atomaren Schreibweg.
|
|
414
|
+
|
|
403
415
|
Der eigentliche Anhang und die vollständige Nutzernachricht bleiben für den Zug
|
|
404
416
|
unverändert verfügbar. Die Begrenzung betrifft ausschließlich die kleine
|
|
405
417
|
lexikalische Vorauswahl von höchstens zwei Werkzeugschemas; ToolSearch und alle
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
|
|
5
|
+
async function configFileMatches(filePath, nextContent, fsImpl = fs) {
|
|
6
|
+
const content = Buffer.isBuffer(nextContent)
|
|
7
|
+
? nextContent
|
|
8
|
+
: Buffer.from(String(nextContent), 'utf8');
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const stat = await fsImpl.lstat(filePath);
|
|
12
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size !== content.byteLength) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const current = await fsImpl.readFile(filePath);
|
|
17
|
+
return Buffer.isBuffer(current)
|
|
18
|
+
? current.equals(content)
|
|
19
|
+
: Buffer.from(current).equals(content);
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
configFileMatches,
|
|
27
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } = require('node:fs');
|
|
4
|
+
const { join } = require('node:path');
|
|
5
|
+
|
|
6
|
+
const STATUS_FILE = 'telegram-console-status.json';
|
|
7
|
+
const MAX_STATUS_BYTES = 16_384;
|
|
8
|
+
const MAX_TASK_CHARS = 240;
|
|
9
|
+
|
|
10
|
+
function validInstant(value) {
|
|
11
|
+
if (typeof value !== 'string' || value.length === 0) return undefined;
|
|
12
|
+
return Number.isFinite(Date.parse(value)) ? value : undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function boundedTask(value) {
|
|
16
|
+
if (typeof value !== 'string') return undefined;
|
|
17
|
+
const title = value.replace(/\s+/gu, ' ').trim();
|
|
18
|
+
return title.length > 0 ? title.slice(0, MAX_TASK_CHARS) : undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildTelegramConsoleStatus(input = {}) {
|
|
22
|
+
const capturedAt = validInstant(input.capturedAt);
|
|
23
|
+
const processStartedAt = validInstant(input.processStartedAt);
|
|
24
|
+
const pid = Number.isSafeInteger(input.pid) && input.pid > 1 ? input.pid : undefined;
|
|
25
|
+
const step = Number.isSafeInteger(input.step) && input.step >= 0 ? input.step : 0;
|
|
26
|
+
if (capturedAt === undefined || processStartedAt === undefined || pid === undefined
|
|
27
|
+
|| typeof input.turnActive !== 'boolean') {
|
|
28
|
+
throw new TypeError('invalid Telegram console status');
|
|
29
|
+
}
|
|
30
|
+
const activeTask = input.turnActive
|
|
31
|
+
? boundedTask(input.todos?.find((todo) => todo?.status === 'in_progress')?.title)
|
|
32
|
+
: undefined;
|
|
33
|
+
return {
|
|
34
|
+
version: 1,
|
|
35
|
+
pid,
|
|
36
|
+
capturedAt,
|
|
37
|
+
processStartedAt,
|
|
38
|
+
turnActive: input.turnActive,
|
|
39
|
+
step,
|
|
40
|
+
...(activeTask === undefined ? {} : { activeTask }),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseTelegramConsoleStatus(value, options = {}) {
|
|
45
|
+
try {
|
|
46
|
+
const text = typeof value === 'string' ? value : JSON.stringify(value);
|
|
47
|
+
if (text.length === 0 || Buffer.byteLength(text, 'utf8') > MAX_STATUS_BYTES) return undefined;
|
|
48
|
+
const parsed = typeof value === 'string' ? JSON.parse(value) : value;
|
|
49
|
+
if (parsed?.version !== 1 || !Number.isSafeInteger(parsed.pid) || parsed.pid <= 1
|
|
50
|
+
|| (options.expectedPid !== undefined && parsed.pid !== options.expectedPid)
|
|
51
|
+
|| validInstant(parsed.capturedAt) === undefined
|
|
52
|
+
|| validInstant(parsed.processStartedAt) === undefined
|
|
53
|
+
|| typeof parsed.turnActive !== 'boolean'
|
|
54
|
+
|| !Number.isSafeInteger(parsed.step) || parsed.step < 0) return undefined;
|
|
55
|
+
const activeTask = boundedTask(parsed.activeTask);
|
|
56
|
+
return {
|
|
57
|
+
version: 1,
|
|
58
|
+
pid: parsed.pid,
|
|
59
|
+
capturedAt: parsed.capturedAt,
|
|
60
|
+
processStartedAt: parsed.processStartedAt,
|
|
61
|
+
turnActive: parsed.turnActive,
|
|
62
|
+
step: parsed.step,
|
|
63
|
+
...(parsed.turnActive && activeTask !== undefined ? { activeTask } : {}),
|
|
64
|
+
};
|
|
65
|
+
} catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function writeTelegramConsoleStatus(input = {}) {
|
|
71
|
+
const status = buildTelegramConsoleStatus(input);
|
|
72
|
+
const directory = input.directory;
|
|
73
|
+
if (typeof directory !== 'string' || directory.length === 0) {
|
|
74
|
+
throw new TypeError('invalid Telegram console status directory');
|
|
75
|
+
}
|
|
76
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
77
|
+
const target = join(directory, STATUS_FILE);
|
|
78
|
+
const temporary = join(directory, `.telegram-console-status.${status.pid}.${Date.now()}.tmp`);
|
|
79
|
+
try {
|
|
80
|
+
writeFileSync(temporary, `${JSON.stringify(status)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
81
|
+
renameSync(temporary, target);
|
|
82
|
+
} finally {
|
|
83
|
+
rmSync(temporary, { force: true });
|
|
84
|
+
}
|
|
85
|
+
return status;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function readTelegramConsoleStatus(directory, options = {}) {
|
|
89
|
+
try {
|
|
90
|
+
return parseTelegramConsoleStatus(readFileSync(join(directory, STATUS_FILE), 'utf8'), options);
|
|
91
|
+
} catch {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
STATUS_FILE,
|
|
98
|
+
buildTelegramConsoleStatus,
|
|
99
|
+
parseTelegramConsoleStatus,
|
|
100
|
+
readTelegramConsoleStatus,
|
|
101
|
+
writeTelegramConsoleStatus,
|
|
102
|
+
};
|
|
@@ -23,8 +23,35 @@ function formatDuration(seconds) {
|
|
|
23
23
|
function formatBytes(bytes) {
|
|
24
24
|
const value = Math.max(0, Number(bytes) || 0);
|
|
25
25
|
if (value < 1024) return `${Math.round(value)} B`;
|
|
26
|
-
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`;
|
|
27
|
-
return `${(value / (1024 * 1024)).toFixed(1)} MB`;
|
|
26
|
+
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1).replace('.', ',')} KB`;
|
|
27
|
+
return `${(value / (1024 * 1024)).toFixed(1).replace('.', ',')} MB`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function formatAge(value, capturedAt) {
|
|
31
|
+
const then = Date.parse(value);
|
|
32
|
+
const now = Date.parse(capturedAt);
|
|
33
|
+
if (!Number.isFinite(then) || !Number.isFinite(now) || then > now) return undefined;
|
|
34
|
+
const seconds = Math.floor((now - then) / 1000);
|
|
35
|
+
return seconds < 60 ? `vor ${seconds} s` : `vor ${formatDuration(seconds)}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveTelegramQueueSnapshot(input = {}) {
|
|
39
|
+
const totalBytes = Number.isSafeInteger(input.queueBytes) && input.queueBytes >= 0
|
|
40
|
+
? input.queueBytes
|
|
41
|
+
: 0;
|
|
42
|
+
const checkpoint = input.checkpoint;
|
|
43
|
+
const checkpointValid = checkpoint?.version === 1
|
|
44
|
+
&& typeof input.queueFileId === 'string'
|
|
45
|
+
&& input.queueFileId.length > 0
|
|
46
|
+
&& checkpoint.fileId === input.queueFileId
|
|
47
|
+
&& Number.isSafeInteger(checkpoint.offset)
|
|
48
|
+
&& checkpoint.offset >= 0
|
|
49
|
+
&& checkpoint.offset <= totalBytes;
|
|
50
|
+
return {
|
|
51
|
+
totalBytes,
|
|
52
|
+
unreadBytes: checkpointValid ? totalBytes - checkpoint.offset : undefined,
|
|
53
|
+
checkpointValid,
|
|
54
|
+
};
|
|
28
55
|
}
|
|
29
56
|
|
|
30
57
|
function buildTelegramRemoteStatus(input) {
|
|
@@ -34,11 +61,23 @@ function buildTelegramRemoteStatus(input) {
|
|
|
34
61
|
|
|
35
62
|
const username = input.username ? `@${String(input.username).replace(/^@/u, '')}` : 'verbundener Nutzer';
|
|
36
63
|
const tui = input.tuiFresh
|
|
37
|
-
? `verbunden${Number.isInteger(input.tuiPid) ? ` (PID ${input.tuiPid}
|
|
64
|
+
? `verbunden${Number.isInteger(input.tuiPid) ? ` (PID ${input.tuiPid}${input.consoleStatus?.processStartedAt
|
|
65
|
+
? `, ${formatDuration((Date.parse(input.capturedAt) - Date.parse(input.consoleStatus.processStartedAt)) / 1000)}`
|
|
66
|
+
: ''})` : ''}`
|
|
38
67
|
: 'nicht verbunden';
|
|
39
68
|
const delivery = input.deliveryTarget === 'tui'
|
|
40
69
|
? 'Konsolen-Warteschlange'
|
|
41
70
|
: 'Brücke ohne Konsole';
|
|
71
|
+
const heartbeatAge = formatAge(input.tuiHeartbeatAt, input.capturedAt);
|
|
72
|
+
const checkpointAge = formatAge(input.queueCheckpointAt, input.capturedAt);
|
|
73
|
+
const workLines = input.consoleStatus === undefined
|
|
74
|
+
? ['Arbeitsstatus: nicht verfügbar']
|
|
75
|
+
: input.consoleStatus.turnActive
|
|
76
|
+
? [
|
|
77
|
+
`Arbeitsstatus: aktiv, Schritt ${input.consoleStatus.step}`,
|
|
78
|
+
...(input.consoleStatus.activeTask === undefined ? [] : [`Aktive Aufgabe: ${input.consoleStatus.activeTask}`]),
|
|
79
|
+
]
|
|
80
|
+
: ['Arbeitsstatus: Leerlauf'];
|
|
42
81
|
return [
|
|
43
82
|
`BLUN-Fernstatus für ${username}`,
|
|
44
83
|
`Rechner: ${input.machineName || 'unbekannt'}`,
|
|
@@ -46,7 +85,10 @@ function buildTelegramRemoteStatus(input) {
|
|
|
46
85
|
`Brücke: aktiv (PID ${input.bridgePid}, ${formatDuration(input.bridgeUptimeSeconds)})`,
|
|
47
86
|
`Konsole: ${tui}`,
|
|
48
87
|
`Zustellung: ${delivery}`,
|
|
49
|
-
`Warteschlange: ${formatBytes(input.queueBytes)}`,
|
|
88
|
+
`Warteschlange: ${input.queueUnreadBytes === undefined ? 'unbekannt' : `${formatBytes(input.queueUnreadBytes)} ungelesen`} (${formatBytes(input.queueBytes)} gesamt)`,
|
|
89
|
+
...(heartbeatAge === undefined ? [] : [`Konsolen-Herzschlag: ${heartbeatAge}`]),
|
|
90
|
+
...(checkpointAge === undefined ? [] : [`Warteschlangen-Fortschritt: ${checkpointAge}`]),
|
|
91
|
+
...workLines,
|
|
50
92
|
`Gemessen: ${input.capturedAt}`,
|
|
51
93
|
].join('\n');
|
|
52
94
|
}
|
|
@@ -55,5 +97,6 @@ module.exports = {
|
|
|
55
97
|
buildTelegramRemoteStatus,
|
|
56
98
|
formatBytes,
|
|
57
99
|
formatDuration,
|
|
100
|
+
resolveTelegramQueueSnapshot,
|
|
58
101
|
resolveTelegramRemoteVersion,
|
|
59
102
|
};
|
package/blun.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { dirname as __cjsShimDirname } from 'node:path';
|
|
|
5
5
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = __cjsShimDirname(__filename);
|
|
7
7
|
import { createRequire } from "node:module";
|
|
8
|
+
import telegramConsoleStatusPolicy from "./bin/telegram-console-status-policy.cjs";
|
|
8
9
|
import crypto$1, { createHash, randomBytes, randomInt, randomUUID, timingSafeEqual } from "node:crypto";
|
|
9
10
|
import * as fs$16 from "node:fs";
|
|
10
11
|
import Kt, { accessSync, appendFileSync, chmodSync, closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, promises, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
@@ -34,6 +35,7 @@ import * as posixPath from "node:path/posix";
|
|
|
34
35
|
import * as win32Path from "node:path/win32";
|
|
35
36
|
import { createServer } from "node:http";
|
|
36
37
|
import { pipeline as pipeline$1 } from "node:stream/promises";
|
|
38
|
+
const { writeTelegramConsoleStatus } = telegramConsoleStatusPolicy;
|
|
37
39
|
import { EventEmitter as EventEmitter$1 } from "node:events";
|
|
38
40
|
import { StringDecoder } from "node:string_decoder";
|
|
39
41
|
import co from "node:assert";
|
|
@@ -16051,6 +16053,7 @@ function transformLoopControlData(data) {
|
|
|
16051
16053
|
delete out["maxStepsPerRun"];
|
|
16052
16054
|
return out;
|
|
16053
16055
|
}
|
|
16056
|
+
const { configFileMatches } = createRequire(import.meta.url)("./bin/config-write-dedup-policy.cjs");
|
|
16054
16057
|
async function writeConfigFile(filePath, config) {
|
|
16055
16058
|
const validated = validateConfig(stripEnvModelConfig(config));
|
|
16056
16059
|
const configDirectory = dirname$3(filePath);
|
|
@@ -16058,7 +16061,12 @@ async function writeConfigFile(filePath, config) {
|
|
|
16058
16061
|
recursive: true,
|
|
16059
16062
|
mode: 448
|
|
16060
16063
|
}) !== void 0) await hardenPrivatePath(configDirectory, "directory");
|
|
16061
|
-
|
|
16064
|
+
const serialized = `${stringify$1(configToTomlData(validated))}\n`;
|
|
16065
|
+
if (await configFileMatches(filePath, serialized)) {
|
|
16066
|
+
await hardenPrivatePath(filePath, "file");
|
|
16067
|
+
return;
|
|
16068
|
+
}
|
|
16069
|
+
await atomicWritePrivate$1(filePath, serialized);
|
|
16062
16070
|
}
|
|
16063
16071
|
function configToTomlData(config) {
|
|
16064
16072
|
const out = cloneRecord$2(config.raw);
|
|
@@ -511023,6 +511031,8 @@ var StreamingUIController = class {
|
|
|
511023
511031
|
pendingToolCallFlushIds = /* @__PURE__ */ new Set();
|
|
511024
511032
|
_currentTurnId = void 0;
|
|
511025
511033
|
_currentStep = 0;
|
|
511034
|
+
_remoteTodos = [];
|
|
511035
|
+
_remoteProcessStartedAt = new Date(Date.now() - process.uptime() * 1e3).toISOString();
|
|
511026
511036
|
_liveTurnStartedAtMs = void 0;
|
|
511027
511037
|
_liveOutputTokens = new LiveOutputTokenCounter();
|
|
511028
511038
|
_countedToolCallIds = /* @__PURE__ */ new Set();
|
|
@@ -511048,9 +511058,25 @@ var StreamingUIController = class {
|
|
|
511048
511058
|
}
|
|
511049
511059
|
setTurnId(turnId) {
|
|
511050
511060
|
this._currentTurnId = turnId;
|
|
511061
|
+
this.publishTelegramConsoleStatus();
|
|
511051
511062
|
}
|
|
511052
511063
|
setStep(step) {
|
|
511053
511064
|
this._currentStep = step;
|
|
511065
|
+
this.publishTelegramConsoleStatus();
|
|
511066
|
+
}
|
|
511067
|
+
publishTelegramConsoleStatus() {
|
|
511068
|
+
if (!telegramChannelAttachEnabled()) return;
|
|
511069
|
+
try {
|
|
511070
|
+
writeTelegramConsoleStatus({
|
|
511071
|
+
directory: telegramStateDir(),
|
|
511072
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
511073
|
+
pid: process.pid,
|
|
511074
|
+
processStartedAt: this._remoteProcessStartedAt,
|
|
511075
|
+
step: this._currentStep,
|
|
511076
|
+
todos: this._remoteTodos,
|
|
511077
|
+
turnActive: this._currentTurnId !== void 0
|
|
511078
|
+
});
|
|
511079
|
+
} catch {}
|
|
511054
511080
|
}
|
|
511055
511081
|
hasActiveTurn() {
|
|
511056
511082
|
return this._currentTurnId !== void 0;
|
|
@@ -511358,8 +511384,10 @@ var StreamingUIController = class {
|
|
|
511358
511384
|
this._pendingReadGroup = null;
|
|
511359
511385
|
this._currentTurnId = void 0;
|
|
511360
511386
|
this._currentStep = 0;
|
|
511387
|
+
this._remoteTodos = [];
|
|
511361
511388
|
this._streamingToolCallArguments.clear();
|
|
511362
511389
|
this.pendingToolCallFlushIds.clear();
|
|
511390
|
+
this.publishTelegramConsoleStatus();
|
|
511363
511391
|
this.host.state.ui.requestRender();
|
|
511364
511392
|
}
|
|
511365
511393
|
disposeActiveThinkingComponent() {
|
|
@@ -511621,9 +511649,11 @@ var StreamingUIController = class {
|
|
|
511621
511649
|
}
|
|
511622
511650
|
setTodoList(todos) {
|
|
511623
511651
|
const { state } = this.host;
|
|
511652
|
+
this._remoteTodos = todos;
|
|
511624
511653
|
state.todoPanel.setTodos(todos);
|
|
511625
511654
|
state.todoPanelContainer.clear();
|
|
511626
511655
|
if (!state.todoPanel.isEmpty()) state.todoPanelContainer.addChild(state.todoPanel);
|
|
511656
|
+
this.publishTelegramConsoleStatus();
|
|
511627
511657
|
state.ui.requestRender();
|
|
511628
511658
|
}
|
|
511629
511659
|
beginCompaction(instruction) {
|
package/package.json
CHANGED
|
@@ -7,9 +7,11 @@ import { Readable, Writable } from "node:stream";
|
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { hostname } from "node:os";
|
|
9
9
|
import remoteStatusPolicy from "../../bin/telegram-remote-status-policy.cjs";
|
|
10
|
+
import consoleStatusPolicy from "../../bin/telegram-console-status-policy.cjs";
|
|
10
11
|
import telegramApprovalRelay from "../../bin/telegram-approval-relay.cjs";
|
|
11
12
|
import privateConversationPolicy from "../bin/telegram-private-conversation-policy.cjs";
|
|
12
|
-
const { buildTelegramRemoteStatus, resolveTelegramRemoteVersion } = remoteStatusPolicy;
|
|
13
|
+
const { buildTelegramRemoteStatus, resolveTelegramQueueSnapshot, resolveTelegramRemoteVersion } = remoteStatusPolicy;
|
|
14
|
+
const { parseTelegramConsoleStatus } = consoleStatusPolicy;
|
|
13
15
|
const { buildTelegramApprovalCard, isAuthorizedTelegramApprovalCallback, listPendingTelegramApprovals, markTelegramApprovalSent, parseTelegramApprovalCallback, resolveTelegramApprovalTarget, wasTelegramApprovalSent, writeTelegramApprovalResponse } = telegramApprovalRelay;
|
|
14
16
|
const { findDuplicateReplyWithoutNewInbound, isPrivateInternalStatusReply, sanitizePrivateConversationReply } = privateConversationPolicy;
|
|
15
17
|
//#region ../../node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs
|
|
@@ -4759,26 +4761,49 @@ bot.command("status", async (ctx) => {
|
|
|
4759
4761
|
if (access.allowFrom.includes(senderId)) {
|
|
4760
4762
|
let version = "unbekannt";
|
|
4761
4763
|
let tuiPid;
|
|
4762
|
-
let
|
|
4764
|
+
let tuiHeartbeatAt;
|
|
4765
|
+
let queueStats;
|
|
4766
|
+
let checkpoint;
|
|
4767
|
+
let queueCheckpointAt;
|
|
4768
|
+
let consoleStatus;
|
|
4769
|
+
const capturedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4763
4770
|
try {
|
|
4764
4771
|
version = JSON.parse(readFileSync(join(dirname(KING_ENTRY), "package.json"), "utf8")).version ?? version;
|
|
4765
4772
|
} catch {}
|
|
4766
4773
|
try {
|
|
4767
4774
|
tuiPid = parseInt(readFileSync(tuiPidFile(), "utf8"), 10);
|
|
4775
|
+
tuiHeartbeatAt = statSync(tuiPidFile()).mtime.toISOString();
|
|
4768
4776
|
} catch {}
|
|
4769
4777
|
try {
|
|
4770
|
-
|
|
4778
|
+
queueStats = statSync(tuiInboundQueueFile());
|
|
4771
4779
|
} catch {}
|
|
4780
|
+
try {
|
|
4781
|
+
const checkpointFile = join(stateDir(), "inbound-queue.checkpoint.json");
|
|
4782
|
+
checkpoint = JSON.parse(readFileSync(checkpointFile, "utf8"));
|
|
4783
|
+
queueCheckpointAt = statSync(checkpointFile).mtime.toISOString();
|
|
4784
|
+
} catch {}
|
|
4785
|
+
if (Number.isInteger(tuiPid)) try {
|
|
4786
|
+
consoleStatus = parseTelegramConsoleStatus(readFileSync(join(stateDir(), "telegram-console-status.json"), "utf8"), { expectedPid: tuiPid });
|
|
4787
|
+
} catch {}
|
|
4788
|
+
const queueSnapshot = resolveTelegramQueueSnapshot({
|
|
4789
|
+
checkpoint,
|
|
4790
|
+
queueBytes: queueStats?.size,
|
|
4791
|
+
queueFileId: queueStats === void 0 ? void 0 : `${queueStats.dev}:${queueStats.ino}:${queueStats.birthtimeMs}`
|
|
4792
|
+
});
|
|
4772
4793
|
await ctx.reply(buildTelegramRemoteStatus({
|
|
4773
4794
|
access,
|
|
4774
4795
|
bridgePid: process.pid,
|
|
4775
4796
|
bridgeUptimeSeconds: process.uptime(),
|
|
4776
|
-
capturedAt
|
|
4797
|
+
capturedAt,
|
|
4798
|
+
consoleStatus,
|
|
4777
4799
|
deliveryTarget: deliveryTarget(),
|
|
4778
4800
|
machineName: hostname(),
|
|
4779
|
-
queueBytes,
|
|
4801
|
+
queueBytes: queueSnapshot.totalBytes,
|
|
4802
|
+
queueCheckpointAt,
|
|
4803
|
+
queueUnreadBytes: queueSnapshot.unreadBytes,
|
|
4780
4804
|
senderId,
|
|
4781
4805
|
tuiFresh: isTuiLeaseFresh(),
|
|
4806
|
+
tuiHeartbeatAt,
|
|
4782
4807
|
tuiPid: Number.isInteger(tuiPid) ? tuiPid : void 0,
|
|
4783
4808
|
username: ctx.from?.username,
|
|
4784
4809
|
version: resolveTelegramRemoteVersion({
|