blun-king-cli 9.1.69 → 9.1.70

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 CHANGED
@@ -85,6 +85,16 @@ Ein neuer /loop ersetzt den bisherigen Loop derselben Sitzung. Dabei bleibt
85
85
  genau ein gespeicherter Loop aktiv. /loop stop, /loop pause und /loop status
86
86
  wirken auch dann sofort, wenn der Agent gerade arbeitet.
87
87
 
88
+ Wird ein neuer /loop während eines laufenden Zugs aktiviert, wird sein erster
89
+ Auftrag hinter dem aktuellen Zug eingereiht. Er startet danach automatisch; es
90
+ entsteht weder ein zweiter paralleler Zug noch der Fehler turn.agent_busy.
91
+
92
+ Beispiel:
93
+
94
+ /loop 5min Prüfe den Teststand und arbeite am nächsten offenen Punkt weiter.
95
+
96
+ Der erste Lauf beginnt direkt nach dem aktuellen Zug, danach alle fünf Minuten.
97
+
88
98
  Eine fällige Wiederholung wird als autonomer Aufwecker eingespeist und nicht
89
99
  als neue Nutzernachricht behandelt. Die Statuszeile zeigt den nächsten
90
100
  Aufweckzeitpunkt laufend an. Beim Fortsetzen derselben Sitzung wird der
@@ -154,10 +164,18 @@ Alle Befehle funktionieren identisch mit `king`.
154
164
 
155
165
  Medienerzeugung
156
166
  ---------------
157
- GenerateVideo erzeugt Videos aus einer Textbeschreibung, animiert einen
158
- abgeschlossenen Bildauftrag oder lädt eine lokale PNG- beziehungsweise JPEG-Datei
159
- hoch und animiert genau dieses Bild. Der Auftrag läuft asynchron und wird
160
- anschließend mit GetMedia abgerufen.
167
+ Für Medienaufträge bleiben GenerateImage, GenerateVideo, GenerateSpeech und
168
+ GetMedia auch bei schlanker Werkzeugauswahl unmittelbar verfügbar. Nutzer
169
+ müssen weder Werkzeugnamen noch Modell-Prompts kennen. Eine normale Anweisung
170
+ wie „Erzeuge ein realistisches Produktbild einer schwarzen Armbanduhr auf weißem
171
+ Marmor“ oder „Animiere das letzte Bild als ruhige Kamerafahrt von sechs Sekunden“
172
+ genügt. King wählt den passenden Medienweg und kann das asynchrone Ergebnis mit
173
+ GetMedia abrufen.
174
+
175
+ Angehängte Bilder werden weiterhin mit ReadMediaFile gelesen. Bild-, Video- und
176
+ Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
177
+ nutzbar bleibt. GenerateVideo kann außerdem einen abgeschlossenen Bildauftrag
178
+ oder eine lokale PNG- beziehungsweise JPEG-Datei animieren.
161
179
 
162
180
  Die Medienleiste erscheint nur bei aktiven Medienaufträgen. Sie zeigt
163
181
  Warteschlangenplatz, Produktionsphase und Laufzeit sowie nur tatsächlich vom
package/README.md CHANGED
@@ -108,6 +108,14 @@ genau ein gespeicherter Loop aktiv; der neue Auftrag, das neue Intervall und
108
108
  der neue Startzeitpunkt gelten vollständig. `/loop stop`, `/loop pause` und
109
109
  `/loop status` wirken auch dann sofort, wenn der Agent gerade arbeitet.
110
110
 
111
+ Wird ein neuer `/loop` während eines laufenden Zugs aktiviert, wird sein erster
112
+ Auftrag hinter dem aktuellen Zug eingereiht. Er startet danach automatisch; es
113
+ entsteht weder ein zweiter paralleler Zug noch der Fehler `turn.agent_busy`.
114
+
115
+ Beispiel: `/loop 5min Prüfe den Teststand und arbeite am nächsten offenen Punkt
116
+ weiter.` führt den ersten Lauf direkt nach dem aktuellen Zug aus und danach alle
117
+ fünf Minuten.
118
+
111
119
  Eine fällige Wiederholung wird als autonomer Aufwecker eingespeist und nicht
112
120
  als neue Nutzernachricht behandelt. Die Statuszeile zeigt den nächsten
113
121
  Aufweckzeitpunkt laufend an. Beim Fortsetzen derselben Sitzung wird der
@@ -184,10 +192,18 @@ Sie stehen unter `blun` und `king` identisch zur Verfügung.
184
192
 
185
193
  ## Medienerzeugung
186
194
 
187
- `GenerateVideo` erzeugt Videos aus einer Textbeschreibung, animiert einen
188
- abgeschlossenen Bildauftrag oder lädt eine lokale PNG- beziehungsweise JPEG-Datei
189
- hoch und animiert genau dieses Bild. Der Auftrag läuft asynchron und wird
190
- anschließend mit `GetMedia` abgerufen.
195
+ Für Medienaufträge bleiben `GenerateImage`, `GenerateVideo`, `GenerateSpeech`
196
+ und `GetMedia` auch bei schlanker Werkzeugauswahl unmittelbar verfügbar. Nutzer
197
+ müssen weder Werkzeugnamen noch Modell-Prompts kennen. Eine normale Anweisung
198
+ wie „Erzeuge ein realistisches Produktbild einer schwarzen Armbanduhr auf weißem
199
+ Marmor“ oder „Animiere das letzte Bild als ruhige Kamerafahrt von sechs Sekunden“
200
+ genügt. King wählt den passenden Medienweg und kann das asynchrone Ergebnis mit
201
+ `GetMedia` abrufen.
202
+
203
+ Angehängte Bilder werden weiterhin mit `ReadMediaFile` gelesen. Bild-, Video-
204
+ und Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
205
+ nutzbar bleibt. `GenerateVideo` kann außerdem einen abgeschlossenen Bildauftrag
206
+ oder eine lokale PNG- beziehungsweise JPEG-Datei animieren.
191
207
 
192
208
  Die Medienleiste erscheint nur bei aktiven Medienaufträgen. Sie zeigt
193
209
  Warteschlangenplatz, Produktionsphase und Laufzeit sowie nur tatsächlich vom
@@ -431,6 +431,7 @@ async function runLauncher(options = {}) {
431
431
  packageRoot: PKG,
432
432
  blunDir: privatePaths.profilePaths.home,
433
433
  names: mcpNames,
434
+ forceEnable: true,
434
435
  });
435
436
  }
436
437
  if (selected.includes('telegram')) {
@@ -31,6 +31,9 @@ function readCatalogue(packageRoot) {
31
31
  || !tool.platforms.every((platform) => platform === 'darwin' || platform === 'win32'))) {
32
32
  throw new Error(`Ungueltige Standard-Tool-Plattform: ${name}`);
33
33
  }
34
+ if (tool.autoEnable !== undefined && typeof tool.autoEnable !== 'boolean') {
35
+ throw new Error(`Ungueltiger Standard-Tool-Autostart: ${name}`);
36
+ }
34
37
  if (tool.owner === 'user-mcp'
35
38
  && (!isObject(tool.config) || typeof tool.config.enabled !== 'boolean')) {
36
39
  throw new Error(`Standard-Tool braucht einen Aktivierungszustand: ${name}`);
@@ -98,7 +101,21 @@ function recognizedBundledServer(name, current, expected) {
98
101
  function migrateBundledTimeouts(name, current, expected) {
99
102
  if (!recognizedBundledServer(name, current, expected)) return false;
100
103
  let changed = false;
101
- if (current.enabled === false) {
104
+ if (name === 'context7') {
105
+ if (expected.blunExplicitlyEnabled === true) {
106
+ if (current.enabled !== true) {
107
+ current.enabled = true;
108
+ changed = true;
109
+ }
110
+ if (current.blunExplicitlyEnabled !== true) {
111
+ current.blunExplicitlyEnabled = true;
112
+ changed = true;
113
+ }
114
+ } else if (current.blunExplicitlyEnabled !== true && current.enabled !== false) {
115
+ current.enabled = false;
116
+ changed = true;
117
+ }
118
+ } else if (current.enabled === false) {
102
119
  current.enabled = true;
103
120
  changed = true;
104
121
  }
@@ -134,9 +151,16 @@ function writeMcpDocument(configPath, document) {
134
151
  }
135
152
  }
136
153
 
137
- function bundledToolConfig(name, tool, { packageRoot, blunDir, platform, env }) {
154
+ function bundledToolConfig(name, tool, {
155
+ packageRoot,
156
+ blunDir,
157
+ platform,
158
+ env,
159
+ forceEnable,
160
+ }) {
138
161
  const config = structuredClone(tool.config);
139
- config.enabled = true;
162
+ config.enabled = forceEnable || tool.autoEnable !== false;
163
+ if (forceEnable && tool.autoEnable === false) config.blunExplicitlyEnabled = true;
140
164
  if (name === 'blun-language-guard') {
141
165
  config.command = platform === 'win32' ? 'python' : 'python3';
142
166
  config.args = [
@@ -185,6 +209,7 @@ function seedStandardTools({
185
209
  names,
186
210
  platform = process.platform,
187
211
  env = process.env,
212
+ forceEnable = false,
188
213
  }) {
189
214
  const catalogue = readCatalogue(packageRoot);
190
215
  if (!Array.isArray(names) || names.length === 0) {
@@ -215,6 +240,7 @@ function seedStandardTools({
215
240
  blunDir,
216
241
  platform,
217
242
  env,
243
+ forceEnable,
218
244
  });
219
245
  if (Object.hasOwn(servers, name)) {
220
246
  if (migrateBundledTimeouts(name, servers[name], expected)) updated.push(name);
@@ -18,6 +18,10 @@ const CORE_TOOL_NAMES = Object.freeze([
18
18
  'ReadMediaFile',
19
19
  'TaskOutput',
20
20
  'Skill',
21
+ 'GenerateImage',
22
+ 'GenerateVideo',
23
+ 'GenerateSpeech',
24
+ 'GetMedia',
21
25
  'mcp__plugin-telegram_telegram__reply',
22
26
  'mcp__plugin-telegram_telegram__react',
23
27
  'mcp__plugin-telegram_telegram__edit_message',
package/blun.mjs CHANGED
@@ -421036,7 +421036,8 @@ async function handleLoopCommand(host, args) {
421036
421036
  host.setAppState?.({ loop });
421037
421037
  host.track("loop_start", { interval: loop.interval });
421038
421038
  host.showStatus(`/loop ${loop.status}`);
421039
- host.sendNormalUserInput(loop.prompt);
421039
+ if (isStreaming(host) || host.state.appState.isCompacting) host.enqueueNormalUserInput(loop.prompt);
421040
+ else host.sendNormalUserInput(loop.prompt);
421040
421041
  return;
421041
421042
  }
421042
421043
  case "pause": {
@@ -515014,6 +515015,12 @@ var BlunTUI = class {
515014
515015
  this.updateQueueDisplay();
515015
515016
  this.state.ui.requestRender();
515016
515017
  }
515018
+ enqueueNormalUserInput(text) {
515019
+ this.enqueueMessage(text);
515020
+ this.updateQueueDisplay();
515021
+ this.scheduleQueueDrain();
515022
+ this.state.ui.requestRender();
515023
+ }
515017
515024
  beginSessionRequest() {
515018
515025
  this.streamingUI.setTurnId(void 0);
515019
515026
  this.streamingUI.resetLiveText();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.69",
3
+ "version": "9.1.70",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -34,6 +34,7 @@
34
34
  "context7": {
35
35
  "owner": "user-mcp",
36
36
  "version": "latest",
37
+ "autoEnable": false,
37
38
  "detectTokens": ["context7", "@upstash/context7-mcp"],
38
39
  "config": {
39
40
  "transport": "stdio",
@@ -4372,21 +4372,36 @@ var KingSession = class {
4372
4372
  * orphan means permanent 409 Conflict), pairing-approval confirmations,
4373
4373
  * and the backoff polling loop.
4374
4374
  */
4375
- /** Kill any stale poller holding the token, then claim the pid file. */
4375
+ /** Atomically claim the token poller without interrupting a live owner. */
4376
4376
  function claimPollerPid() {
4377
4377
  mkdirSync(stateDir(), {
4378
4378
  recursive: true,
4379
4379
  mode: 448
4380
4380
  });
4381
- try {
4382
- const stale = parseInt(readFileSync(pidFile(), "utf8"), 10);
4383
- if (stale > 1 && stale !== process.pid) {
4384
- process.kill(stale, 0);
4385
- process.stderr.write(`telegram channel: replacing stale poller pid=${stale}\n`);
4386
- process.kill(stale, "SIGTERM");
4381
+ for (let attempt = 0; attempt < 3; attempt += 1) {
4382
+ try {
4383
+ writeFileSync(pidFile(), String(process.pid), { flag: "wx" });
4384
+ return true;
4385
+ } catch (error) {
4386
+ if (error?.code !== "EEXIST") throw error;
4387
4387
  }
4388
- } catch {}
4389
- writeFileSync(pidFile(), String(process.pid));
4388
+ let ownerPid;
4389
+ try {
4390
+ ownerPid = parseInt(readFileSync(pidFile(), "utf8"), 10);
4391
+ } catch {}
4392
+ if (ownerPid === process.pid) return true;
4393
+ if (ownerPid !== void 0 && ownerPid > 1) try {
4394
+ process.kill(ownerPid, 0);
4395
+ process.stderr.write(`telegram channel: live poller pid=${ownerPid} already owns the bot token\n`);
4396
+ return false;
4397
+ } catch {}
4398
+ try {
4399
+ rmSync(pidFile());
4400
+ } catch {
4401
+ return false;
4402
+ }
4403
+ }
4404
+ return false;
4390
4405
  }
4391
4406
  function releasePollerPid() {
4392
4407
  try {
@@ -4546,7 +4561,10 @@ process.on("uncaughtException", (err) => {
4546
4561
  });
4547
4562
  ensureStateDir();
4548
4563
  rotateStaleTuiQueue();
4549
- claimPollerPid();
4564
+ if (!claimPollerPid()) {
4565
+ process.stderr.write("telegram channel: another live poller owns the bot token; exiting\n");
4566
+ process.exit(0);
4567
+ }
4550
4568
  const bot = new import_out.Bot(TOKEN);
4551
4569
  let botUsername = "";
4552
4570
  const session = new KingSession(KING_ENTRY, (msg) => {