blun-king-cli 9.1.614 → 9.1.615

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.
@@ -7,7 +7,7 @@ Auflagen Dieter 43102:
7
7
  (Run-3-Befund: Liste trieb RAM-Spitze auf 10,5 GB).
8
8
  - Inkrementell: Content-Hash je Datei im Manifest, Delta statt Vollindex.
9
9
  - Query mit Frische-Auskunft + Selbstauskunft in JEDER Antwort.
10
- - Query-Fehlschlag-Log lokal (query-log.jsonl) — der Moat.
10
+ - Lokales Query-Metadatenlog (query-log.jsonl), ohne Fragetext.
11
11
 
12
12
  Aufruf:
13
13
  python codebase_index.py build <repo>
@@ -351,13 +351,29 @@ def cmd_update(repo: str) -> None:
351
351
  )
352
352
 
353
353
 
354
- def freshness(repo: str, man: dict) -> str:
355
- current = git_head(repo)
356
- same = "== HEAD" if current == man["head"] else f"Index {man['head']} != HEAD {current} — 'update' faellig"
357
- return f"Index-Stand: {man['head']} ({man['built_at']}), {man['chunks']} Chunks | {same}"
358
-
359
-
360
- def cmd_query(repo: str, question: str, top: int, selector: str = "auto") -> int:
354
+ def freshness(repo: str, man: dict) -> str:
355
+ current = git_head(repo)
356
+ same = "== HEAD" if current == man["head"] else f"Index {man['head']} != HEAD {current} — 'update' faellig"
357
+ return f"Index-Stand: {man['head']} ({man['built_at']}), {man['chunks']} Chunks | {same}"
358
+
359
+
360
+ def append_query_metadata(dirpath: str, question: str, hits: list[tuple[str, float]],
361
+ requested_top: int, latency_ms: float, man: dict) -> None:
362
+ record = {
363
+ "ts": datetime.now(timezone.utc).isoformat(),
364
+ "query_bytes": len(question.encode("utf-8")),
365
+ "requested_top": requested_top,
366
+ "result_count": len(hits),
367
+ "result_paths": list(dict.fromkeys(meta.rsplit("#", 1)[0] for meta, _ in hits)),
368
+ "query_ms": round(latency_ms, 1),
369
+ "index_head": man["head"],
370
+ "model": man["model"],
371
+ }
372
+ with open(os.path.join(dirpath, "query-log.jsonl"), "a", encoding="utf-8") as fh:
373
+ fh.write(json.dumps(record) + "\n")
374
+
375
+
376
+ def cmd_query(repo: str, question: str, top: int, selector: str = "auto") -> int:
361
377
  dirpath, man = resolve_existing_index(repo, selector)
362
378
  activate_model(man["model"], man["dim"])
363
379
  mat = np.load(os.path.join(dirpath, "vectors.npy"))
@@ -380,15 +396,7 @@ def cmd_query(repo: str, question: str, top: int, selector: str = "auto") -> int
380
396
  print(f" {s:.4f} {m}")
381
397
  print(f"query_ms={latency_ms:.1f}")
382
398
 
383
- log_path = os.path.join(dirpath, "query-log.jsonl")
384
- with open(log_path, "a", encoding="utf-8") as fh:
385
- fh.write(json.dumps({
386
- "ts": datetime.now(timezone.utc).isoformat(),
387
- "query": question,
388
- "top": hits,
389
- "query_ms": round(latency_ms, 1),
390
- "index_head": man["head"],
391
- }) + "\n")
399
+ append_query_metadata(dirpath, question, hits, top, latency_ms, man)
392
400
  return 0
393
401
 
394
402
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.614",
3
+ "version": "9.1.615",
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": {
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { appendFileSync, chmodSync, closeSync, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join, resolve } from "node:path";
4
- import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
4
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
5
5
  import { homedir } from "node:os";
6
6
  import { spawn } from "node:child_process";
7
7
  import { Readable, Writable } from "node:stream";
@@ -67686,204 +67686,6 @@ var RequestError = class RequestError extends Error {
67686
67686
  }
67687
67687
  };
67688
67688
  //#endregion
67689
- //#region src/language-release.ts
67690
- const EXACT_LANGUAGE_TAG = /^(?:[A-Za-z]{2,8}|x)(?:-[A-Za-z0-9]{1,8})*$/;
67691
- const LETTER = /^\p{Letter}$/u;
67692
- const LATIN = /^\p{Script=Latin}$/u;
67693
- const CYRILLIC = /^\p{Script=Cyrillic}$/u;
67694
- const GREEK = /^\p{Script=Greek}$/u;
67695
- const ARABIC = /^\p{Script=Arabic}$/u;
67696
- const HEBREW = /^\p{Script=Hebrew}$/u;
67697
- const HAN = /^\p{Script=Han}$/u;
67698
- const HIRAGANA = /^\p{Script=Hiragana}$/u;
67699
- const KATAKANA = /^\p{Script=Katakana}$/u;
67700
- const HANGUL = /^\p{Script=Hangul}$/u;
67701
- const LATIN_LANGUAGES = new Set([
67702
- "af",
67703
- "bs",
67704
- "ca",
67705
- "cs",
67706
- "cy",
67707
- "da",
67708
- "de",
67709
- "en",
67710
- "es",
67711
- "et",
67712
- "eu",
67713
- "fi",
67714
- "fr",
67715
- "ga",
67716
- "gd",
67717
- "gl",
67718
- "hr",
67719
- "hu",
67720
- "id",
67721
- "is",
67722
- "it",
67723
- "la",
67724
- "lb",
67725
- "lt",
67726
- "lv",
67727
- "ms",
67728
- "mt",
67729
- "nl",
67730
- "no",
67731
- "pl",
67732
- "pt",
67733
- "ro",
67734
- "sk",
67735
- "sl",
67736
- "sq",
67737
- "sv",
67738
- "sw",
67739
- "tr",
67740
- "vi"
67741
- ]);
67742
- function canonicalText(text) {
67743
- return text.replace(/^\uFEFF/, "").replace(/\r\n?/g, "\n").normalize("NFC");
67744
- }
67745
- function canonicalHash(text) {
67746
- return createHash("sha256").update(canonicalText(text), "utf8").digest("hex");
67747
- }
67748
- function maskTechnicalText(text) {
67749
- const mask = (value) => " ".repeat(value.length);
67750
- return text.replace(/```[\s\S]*?```|~~~[\s\S]*?~~~/g, mask).replace(/`[^`\r\n]*`/g, mask);
67751
- }
67752
- function allowedScriptMatchers(locale) {
67753
- if (!EXACT_LANGUAGE_TAG.test(locale) || ["auto", "all"].includes(locale.toLowerCase())) throw new Error("language release blocked: exact output locale required");
67754
- const parts = locale.replaceAll("_", "-").split("-");
67755
- const base = parts[0]?.toLowerCase() ?? "";
67756
- const explicitScript = parts.find((part) => part.length === 4)?.toLowerCase();
67757
- if (explicitScript !== void 0) {
67758
- const explicit = {
67759
- latn: [LATIN],
67760
- cyrl: [CYRILLIC],
67761
- grek: [GREEK],
67762
- arab: [ARABIC],
67763
- hebr: [HEBREW],
67764
- hani: [HAN],
67765
- jpan: [
67766
- HAN,
67767
- HIRAGANA,
67768
- KATAKANA
67769
- ],
67770
- hang: [HANGUL, HAN]
67771
- }[explicitScript];
67772
- if (explicit === void 0) throw new Error("language release blocked: unsupported output script");
67773
- return explicit;
67774
- }
67775
- if (LATIN_LANGUAGES.has(base)) return [LATIN];
67776
- if ([
67777
- "ru",
67778
- "uk",
67779
- "bg",
67780
- "mk"
67781
- ].includes(base)) return [CYRILLIC];
67782
- if (base === "el") return [GREEK];
67783
- if ([
67784
- "ar",
67785
- "fa",
67786
- "ur"
67787
- ].includes(base)) return [ARABIC];
67788
- if (base === "he") return [HEBREW];
67789
- if (base === "zh") return [HAN];
67790
- if (base === "ja") return [
67791
- HAN,
67792
- HIRAGANA,
67793
- KATAKANA
67794
- ];
67795
- if (base === "ko") return [HANGUL, HAN];
67796
- throw new Error("language release blocked: output locale has no deterministic script policy");
67797
- }
67798
- function assertExpectedScript(text, locale) {
67799
- const allowed = allowedScriptMatchers(locale);
67800
- for (const character of maskTechnicalText(text)) {
67801
- if (!LETTER.test(character)) continue;
67802
- if (allowed.some((matcher) => matcher.test(character))) continue;
67803
- const point = character.codePointAt(0)?.toString(16).toUpperCase().padStart(4, "0") ?? "UNKNOWN";
67804
- throw new Error(`language release blocked: unexpected-script-contamination (U+${point})`);
67805
- }
67806
- }
67807
- function parseAndVerifySignature(token, key) {
67808
- if (key.length < 32) throw new Error("language release blocked: invalid verification key");
67809
- if (token.length > 16384) throw new Error("language release blocked: release token too large");
67810
- const parts = token.split(".");
67811
- if (parts.length !== 3 || parts[0] !== "blg6" || parts[1] === "" || parts[2] === "") throw new Error("language release blocked: malformed release token");
67812
- const encoded = parts[1];
67813
- let supplied;
67814
- let payload;
67815
- try {
67816
- supplied = Buffer.from(parts[2], "base64url");
67817
- payload = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
67818
- } catch {
67819
- throw new Error("language release blocked: malformed release token");
67820
- }
67821
- const expected = createHmac("sha256", key).update(encoded).digest();
67822
- if (supplied.length !== expected.length || !timingSafeEqual(supplied, expected)) throw new Error("language release blocked: invalid release token signature");
67823
- if (payload === null || typeof payload !== "object" || Array.isArray(payload)) throw new Error("language release blocked: malformed release payload");
67824
- return payload;
67825
- }
67826
- function assertEqual(actual, expected, field) {
67827
- if (actual !== expected) throw new Error(`language release blocked: ${field} mismatch`);
67828
- }
67829
- function verifyTelegramLanguageRelease(text, releaseToken, options) {
67830
- if (typeof releaseToken !== "string" || releaseToken.length === 0) throw new Error("language release blocked: release token required");
67831
- if (options.expectedVersion.trim().length === 0) throw new Error("language release blocked: expected guard version required");
67832
- assertExpectedScript(text, options.locale);
67833
- const payload = parseAndVerifySignature(releaseToken, options.key);
67834
- const now = Math.floor(options.now ?? Date.now() / 1e3);
67835
- if (!Number.isSafeInteger(payload.iat) || !Number.isSafeInteger(payload.exp)) throw new Error("language release blocked: invalid receipt lifetime");
67836
- const issuedAt = payload.iat;
67837
- const expiresAt = payload.exp;
67838
- if (expiresAt < now) throw new Error("language release blocked: release token expired");
67839
- if (issuedAt > now + 60 || expiresAt < issuedAt || expiresAt - issuedAt > 604800) throw new Error("language release blocked: invalid receipt lifetime");
67840
- if (typeof payload.nonce !== "string" || payload.nonce.length === 0) throw new Error("language release blocked: invalid receipt nonce");
67841
- assertEqual(payload.source_sha256, canonicalHash(""), "source");
67842
- assertEqual(payload.target_sha256, canonicalHash(text), "target");
67843
- if (payload.language !== options.locale) throw new Error(`language release blocked: language mismatch (expected ${options.locale})`);
67844
- assertEqual(payload.purpose, "response", "purpose");
67845
- assertEqual(payload.content_type, "prose", "content type");
67846
- assertEqual(payload.short_text_reviewed, false, "short text review");
67847
- assertEqual(payload.v, options.expectedVersion, "version");
67848
- }
67849
- function isLanguageReleaseRequired() {
67850
- return process.env["BLUN_TELEGRAM_LANGUAGE_GATE"]?.trim().toLowerCase() === "required";
67851
- }
67852
- function requiredSetting(name) {
67853
- const value = process.env[name]?.trim();
67854
- if (value === void 0 || value.length === 0) throw new Error(`language release blocked: host setting ${name} required`);
67855
- return value;
67856
- }
67857
- function telegramLanguageReleaseLocale() {
67858
- if (!isLanguageReleaseRequired()) return void 0;
67859
- const locale = requiredSetting("BLUN_TELEGRAM_OUTPUT_LOCALE");
67860
- allowedScriptMatchers(locale);
67861
- return locale;
67862
- }
67863
- function defaultKeyFile() {
67864
- const configuredHome = process.env["BLUN_HOME"]?.trim();
67865
- return join(configuredHome !== void 0 && configuredHome.length > 0 ? configuredHome : join(homedir(), ".blun"), "secrets", "blun-language-guard-signing.key");
67866
- }
67867
- function assertTelegramLanguageReleased(text, releaseToken, now) {
67868
- if (!isLanguageReleaseRequired()) return;
67869
- if (typeof releaseToken !== "string" || releaseToken.length === 0) throw new Error("language release blocked: release token required");
67870
- const locale = telegramLanguageReleaseLocale();
67871
- if (locale === void 0) throw new Error("language release blocked: exact output locale required");
67872
- const keyFile = process.env["BLUN_LANGUAGE_GUARD_KEY_FILE"]?.trim() || defaultKeyFile();
67873
- let key;
67874
- try {
67875
- key = readFileSync(keyFile);
67876
- } catch {
67877
- throw new Error("language release blocked: verification key unavailable");
67878
- }
67879
- verifyTelegramLanguageRelease(text, releaseToken, {
67880
- locale,
67881
- expectedVersion: requiredSetting("BLUN_LANGUAGE_GUARD_VERSION"),
67882
- key,
67883
- now
67884
- });
67885
- }
67886
- //#endregion
67887
67689
  //#region src/noise.ts
67888
67690
  /**
67889
67691
  * Conservative outbound guard for group acknowledgements and narrated silence.
@@ -68071,7 +67873,6 @@ async function sendReplyFallback(chatId, rawText, threadId) {
68071
67873
  const text = scrubSecrets(rawText);
68072
67874
  const body = text.length > TELEGRAM_TEXT_LIMIT ? `${text.slice(0, TELEGRAM_TEXT_LIMIT - 1)}…` : text;
68073
67875
  try {
68074
- assertTelegramLanguageReleased(body, void 0);
68075
67876
  const payload = await (await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
68076
67877
  method: "POST",
68077
67878
  headers: { "Content-Type": "application/json" },
@@ -73449,6 +73449,10 @@ function assertTelegramLanguageReleased(text, releaseToken, now) {
73449
73449
  now
73450
73450
  });
73451
73451
  }
73452
+ function assertOptionalTelegramLanguageRelease(text, releaseToken, now) {
73453
+ if (releaseToken === void 0) return;
73454
+ assertTelegramLanguageReleased(text, releaseToken, now);
73455
+ }
73452
73456
  //#endregion
73453
73457
  //#region src/noise.ts
73454
73458
  /**
@@ -73528,7 +73532,7 @@ const TOOL_DEFINITIONS = [
73528
73532
  text: { type: "string" },
73529
73533
  release_token: {
73530
73534
  type: "string",
73531
- description: "Language Guard receipt for this exact text. Required when the host enables the language release gate."
73535
+ description: "Optional Language Guard receipt for released text. When provided, it must match this exact text."
73532
73536
  },
73533
73537
  reply_to: {
73534
73538
  type: "string",
@@ -73592,7 +73596,7 @@ const TOOL_DEFINITIONS = [
73592
73596
  text: { type: "string" },
73593
73597
  release_token: {
73594
73598
  type: "string",
73595
- description: "Language Guard receipt for this exact text. Required when the host enables the language release gate."
73599
+ description: "Optional Language Guard receipt for released text. When provided, it must match this exact text."
73596
73600
  },
73597
73601
  format: {
73598
73602
  type: "string",
@@ -73622,7 +73626,7 @@ async function runTool(api, token, name, args) {
73622
73626
  case "edit_message": {
73623
73627
  const chatId = resolveAllowedChatId(args["chat_id"]);
73624
73628
  if (isSilentTurnMarker(args["text"])) throw new Error("silent marker cannot be used to edit a Telegram message");
73625
- assertTelegramLanguageReleased(args["text"], args["release_token"]);
73629
+ assertOptionalTelegramLanguageRelease(args["text"], args["release_token"]);
73626
73630
  const parseMode = args["format"] === "markdownv2" ? "MarkdownV2" : void 0;
73627
73631
  const edited = await api.editMessageText(chatId, Number(args["message_id"]), args["text"], parseMode !== void 0 ? { parse_mode: parseMode } : void 0);
73628
73632
  const id = typeof edited === "object" ? edited.message_id : args["message_id"];
@@ -73701,7 +73705,7 @@ async function runReply(api, args) {
73701
73705
  });
73702
73706
  return "already delivered: an identical message was sent to this chat moments ago — do NOT send it again";
73703
73707
  }
73704
- assertTelegramLanguageReleased(text, args["release_token"]);
73708
+ assertOptionalTelegramLanguageRelease(text, args["release_token"]);
73705
73709
  for (const file of files) {
73706
73710
  assertSendable(file);
73707
73711
  const info = statSync(file);