blun-king-cli 5.3.3 → 5.3.5

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/blun-cli.js CHANGED
@@ -3065,8 +3065,8 @@ async function main() {
3065
3065
  runHook("post", input.split(/\s+/)[0].slice(1));
3066
3066
  } else {
3067
3067
  // Browser intent — natural language with URL → local Playwright
3068
- var urlMatch = input.match(/(https?:\/\/\S+)/i);
3069
- var browserWords = /\b(öffne|open|schau|zeig|geh auf|navigate|browse|screenshot|bildschirmfoto|aufrufen|besuch|check|prüf|analysier)\b/i;
3068
+ var urlMatch = input.match(/(https?:\/\/\S+)/i) || input.match(/\b((?:www\.)?[a-zA-Z0-9-]+\.[a-z]{2,}(?:\/\S*)?)\b/i);
3069
+ var browserWords = /\b(öffne|open|schau|zeig|geh auf|gehen|navigate|browse|screenshot|bildschirmfoto|aufrufen|besuch|check|prüf|analysier)\b/i;
3070
3070
  if (urlMatch && browserWords.test(input)) {
3071
3071
  var browserUrl = urlMatch[1];
3072
3072
  try {
@@ -3,6 +3,31 @@ const { chromium } = require("playwright");
3
3
  let browserInstance = null;
4
4
  let pageInstance = null;
5
5
 
6
+ function normalizeUrl(input) {
7
+ const raw = String(input || "").trim();
8
+ if (!raw) {
9
+ throw new Error("No URL provided.");
10
+ }
11
+
12
+ let cleaned = raw
13
+ .replace(/^[<\s"'`]+/, "")
14
+ .replace(/[>\s"'`]+$/, "");
15
+
16
+ if (/^www\./i.test(cleaned)) {
17
+ cleaned = `https://${cleaned}`;
18
+ }
19
+
20
+ if (!/^[a-z][a-z0-9+.-]*:/i.test(cleaned)) {
21
+ cleaned = `https://${cleaned}`;
22
+ }
23
+
24
+ try {
25
+ return new URL(cleaned).toString();
26
+ } catch {
27
+ throw new Error(`Invalid URL: ${raw}`);
28
+ }
29
+ }
30
+
6
31
  async function ensurePage() {
7
32
  if (!browserInstance) {
8
33
  browserInstance = await chromium.launch({ headless: true });
@@ -15,9 +40,7 @@ async function ensurePage() {
15
40
 
16
41
  async function open(url) {
17
42
  const page = await ensurePage();
18
- var cleanUrl = String(url).replace(/^<|>$/g, "").trim();
19
- if (!/^https?:\/\//i.test(cleanUrl)) cleanUrl = "https://" + cleanUrl;
20
- await page.goto(cleanUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
43
+ await page.goto(normalizeUrl(url), { waitUntil: "domcontentloaded", timeout: 30000 });
21
44
  return snapshot();
22
45
  }
23
46
 
@@ -68,6 +91,7 @@ async function close() {
68
91
  }
69
92
 
70
93
  module.exports = {
94
+ normalizeUrl,
71
95
  open,
72
96
  click,
73
97
  type,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "5.3.3",
3
+ "version": "5.3.5",
4
4
  "description": "BLUN King CLI — Your local AI assistant powered by Gemma4",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -4,8 +4,36 @@ const { storeReference } = require("./local-data");
4
4
 
5
5
  let chromiumLoader = null;
6
6
 
7
+ function normalizeDetectedUrl(input) {
8
+ const raw = String(input || "").trim();
9
+ if (!raw) return null;
10
+
11
+ let cleaned = raw
12
+ .replace(/^[<\s"'`(]+/, "")
13
+ .replace(/[>\s"'`),.;:!?]+$/, "");
14
+
15
+ if (!cleaned) return null;
16
+ if (/^www\./i.test(cleaned)) cleaned = `https://${cleaned}`;
17
+ if (/^[a-z0-9.-]+\.[a-z]{2,}(\/|$)/i.test(cleaned) && !/^[a-z][a-z0-9+.-]*:/i.test(cleaned)) {
18
+ cleaned = `https://${cleaned}`;
19
+ }
20
+
21
+ try {
22
+ const parsed = new URL(cleaned);
23
+ if (!/^https?:$/i.test(parsed.protocol) && !/^data:$/i.test(parsed.protocol)) return null;
24
+ return parsed.toString();
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+
7
30
  function extractUrls(text) {
8
- return Array.from(String(text || "").matchAll(/https?:\/\/[^\s)>"']+/gi)).map((match) => match[0]);
31
+ const matches = [
32
+ ...Array.from(String(text || "").matchAll(/https?:\/\/[^\s)>"']+/gi)).map((match) => match[0]),
33
+ ...Array.from(String(text || "").matchAll(/(?:^|[\s<(])((?:www\.)?[a-z0-9.-]+\.[a-z]{2,}(?:\/[^\s)>"']*)?)/gi)).map((match) => match[1])
34
+ ];
35
+
36
+ return [...new Set(matches.map(normalizeDetectedUrl).filter(Boolean))];
9
37
  }
10
38
 
11
39
  function summarizeHtml(source) {
@@ -40,3 +40,8 @@ test("browser controller can save a screenshot file", async () => {
40
40
 
41
41
  await browser.close();
42
42
  });
43
+
44
+ test("browser controller normalizes bracketed hostnames into valid urls", () => {
45
+ assert.equal(browser.normalizeUrl("<www.autofiles.de>"), "https://www.autofiles.de/");
46
+ assert.equal(browser.normalizeUrl("autofiles.de"), "https://autofiles.de/");
47
+ });
@@ -34,3 +34,9 @@ test("reference helper builds a prompt block from inspected references", () => {
34
34
  assert.match(block, /Example/);
35
35
  assert.match(block, /example\.png/);
36
36
  });
37
+
38
+ test("reference helper extracts and normalizes bare domains and bracketed urls", () => {
39
+ const urls = inspector.extractUrls("mach einen screenshot von <www.autofiles.de> und vielleicht auch autofiles.de");
40
+
41
+ assert.deepEqual(urls, ["https://www.autofiles.de/", "https://autofiles.de/"]);
42
+ });