blun-king-cli 9.1.206 → 9.1.207

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.
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ const TELEMETRY_RETRY_MAX_FILES = 16;
4
+ const TELEMETRY_SPOOL_MAX_FILES = 512;
5
+ const TELEMETRY_SPOOL_MAX_BYTES = 16 * 1024 * 1024;
6
+ const TELEMETRY_SPOOL_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
7
+
8
+ function isFailedTelemetryEntry(entry) {
9
+ return entry !== null
10
+ && typeof entry === 'object'
11
+ && /^failed_[^.]+\.jsonl$/u.test(entry.name ?? '')
12
+ && Number.isFinite(entry.mtimeMs)
13
+ && Number.isFinite(entry.size)
14
+ && entry.size >= 0;
15
+ }
16
+
17
+ function oldestFirst(left, right) {
18
+ return left.mtimeMs - right.mtimeMs || left.name.localeCompare(right.name);
19
+ }
20
+
21
+ function newestFirst(left, right) {
22
+ return right.mtimeMs - left.mtimeMs || right.name.localeCompare(left.name);
23
+ }
24
+
25
+ function selectTelemetryRetryEntries(entries, maxFiles = TELEMETRY_RETRY_MAX_FILES) {
26
+ if (!Number.isSafeInteger(maxFiles) || maxFiles < 0) return [];
27
+ return entries.filter(isFailedTelemetryEntry).sort(oldestFirst).slice(0, maxFiles);
28
+ }
29
+
30
+ function selectTelemetrySpoolPruneEntries(entries, now = Date.now()) {
31
+ const failed = entries.filter(isFailedTelemetryEntry);
32
+ const expired = failed.filter((entry) => now - entry.mtimeMs > TELEMETRY_SPOOL_MAX_AGE_MS);
33
+ const expiredNames = new Set(expired.map((entry) => entry.name));
34
+ const live = failed.filter((entry) => !expiredNames.has(entry.name)).sort(newestFirst);
35
+ const keptNames = new Set();
36
+ let keptBytes = 0;
37
+
38
+ for (const entry of live) {
39
+ if (keptNames.size >= TELEMETRY_SPOOL_MAX_FILES) continue;
40
+ if (keptBytes + entry.size > TELEMETRY_SPOOL_MAX_BYTES) continue;
41
+ keptNames.add(entry.name);
42
+ keptBytes += entry.size;
43
+ }
44
+
45
+ return failed
46
+ .filter((entry) => expiredNames.has(entry.name) || !keptNames.has(entry.name))
47
+ .sort(oldestFirst);
48
+ }
49
+
50
+ module.exports = {
51
+ TELEMETRY_RETRY_MAX_FILES,
52
+ TELEMETRY_SPOOL_MAX_AGE_MS,
53
+ TELEMETRY_SPOOL_MAX_BYTES,
54
+ TELEMETRY_SPOOL_MAX_FILES,
55
+ selectTelemetryRetryEntries,
56
+ selectTelemetrySpoolPruneEntries,
57
+ };
package/blun.mjs CHANGED
@@ -328163,6 +328163,10 @@ const RETRY_BACKOFFS_MS = [
328163
328163
  16e3
328164
328164
  ];
328165
328165
  const DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
328166
+ const {
328167
+ selectTelemetryRetryEntries,
328168
+ selectTelemetrySpoolPruneEntries
328169
+ } = createRequire(import.meta.url)("./bin/telemetry-spool-policy.cjs");
328166
328170
  var AsyncTransport = class {
328167
328171
  homeDir;
328168
328172
  deviceId;
@@ -328173,6 +328177,7 @@ var AsyncTransport = class {
328173
328177
  requestTimeoutMs;
328174
328178
  sleepImpl;
328175
328179
  now;
328180
+ spoolWritesSincePrune = 0;
328176
328181
  constructor(options) {
328177
328182
  this.homeDir = options.homeDir;
328178
328183
  this.deviceId = options.deviceId;
@@ -328235,26 +328240,44 @@ var AsyncTransport = class {
328235
328240
  try {
328236
328241
  chmodSync(path, 384);
328237
328242
  } catch {}
328243
+ this.spoolWritesSincePrune += 1;
328244
+ if (this.spoolWritesSincePrune === 1 || this.spoolWritesSincePrune >= 64) {
328245
+ this.pruneDiskSpool();
328246
+ this.spoolWritesSincePrune = 0;
328247
+ }
328238
328248
  }
328239
- async retryDiskEvents() {
328240
- let entries;
328249
+ readSpoolEntries() {
328250
+ let names;
328241
328251
  try {
328242
- entries = readdirSync(this.telemetryDir());
328252
+ names = readdirSync(this.telemetryDir());
328243
328253
  } catch {
328244
- return;
328254
+ return [];
328245
328255
  }
328246
- const now = this.now();
328247
- for (const entry of entries) {
328248
- if (!entry.startsWith("failed_") || !entry.endsWith(".jsonl")) continue;
328249
- const path = join(this.telemetryDir(), entry);
328256
+ const entries = [];
328257
+ for (const name of names) {
328258
+ if (!name.startsWith("failed_") || !name.endsWith(".jsonl")) continue;
328259
+ const path = join(this.telemetryDir(), name);
328250
328260
  try {
328251
- if (now - statSync(path).mtimeMs > 6048e5) {
328252
- unlinkSync(path);
328253
- continue;
328254
- }
328255
- } catch {
328256
- continue;
328257
- }
328261
+ const stats = statSync(path);
328262
+ entries.push({ name, mtimeMs: stats.mtimeMs, size: stats.size });
328263
+ } catch {}
328264
+ }
328265
+ return entries;
328266
+ }
328267
+ pruneDiskSpool(spoolEntries = this.readSpoolEntries()) {
328268
+ const pruned = selectTelemetrySpoolPruneEntries(spoolEntries, this.now());
328269
+ for (const entry of pruned) try {
328270
+ unlinkSync(join(this.telemetryDir(), entry.name));
328271
+ } catch {}
328272
+ return new Set(pruned.map((entry) => entry.name));
328273
+ }
328274
+ async retryDiskEvents() {
328275
+ const spoolEntries = this.readSpoolEntries();
328276
+ const prunedNames = this.pruneDiskSpool(spoolEntries);
328277
+ const retryEntries = selectTelemetryRetryEntries(spoolEntries
328278
+ .filter((entry) => !prunedNames.has(entry.name)));
328279
+ for (const entry of retryEntries) {
328280
+ const path = join(this.telemetryDir(), entry.name);
328258
328281
  let events;
328259
328282
  let payload;
328260
328283
  try {
@@ -328270,7 +328293,7 @@ var AsyncTransport = class {
328270
328293
  await this.sendHttp(payload);
328271
328294
  unlinkSync(path);
328272
328295
  } catch (error) {
328273
- if (error instanceof TransientTelemetryError) continue;
328296
+ if (error instanceof TransientTelemetryError) break;
328274
328297
  }
328275
328298
  }
328276
328299
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.206",
3
+ "version": "9.1.207",
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": {