blun-king-cli 5.4.0 → 6.1.0

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.
Files changed (56) hide show
  1. package/bin/blun.js +134 -0
  2. package/lib/auth.js +162 -0
  3. package/lib/chat.js +82 -0
  4. package/lib/client.js +65 -0
  5. package/lib/ui.js +215 -0
  6. package/lib/workspace.js +110 -0
  7. package/package.json +14 -18
  8. package/api.js +0 -965
  9. package/blun-cli.js +0 -3726
  10. package/blun-cli.js.claude +0 -3578
  11. package/blunking-api.js +0 -7
  12. package/bot.js +0 -188
  13. package/browser-controller.js +0 -116
  14. package/chat-memory.js +0 -103
  15. package/file-helper.js +0 -63
  16. package/fuzzy-match.js +0 -78
  17. package/identities.js +0 -106
  18. package/installer.js +0 -160
  19. package/job-manager.js +0 -146
  20. package/local-data.js +0 -71
  21. package/message-builder.js +0 -28
  22. package/noisy-evals.js +0 -38
  23. package/palace-memory.js +0 -246
  24. package/reference-inspector.js +0 -256
  25. package/runtime.js +0 -570
  26. package/task-executor.js +0 -104
  27. package/tests/browser-controller.test.js +0 -47
  28. package/tests/cli.test.js +0 -93
  29. package/tests/file-helper.test.js +0 -18
  30. package/tests/installer.test.js +0 -39
  31. package/tests/job-manager.test.js +0 -99
  32. package/tests/merge-compat.test.js +0 -77
  33. package/tests/messages.test.js +0 -23
  34. package/tests/noisy-evals.test.js +0 -12
  35. package/tests/noisy-intent-corpus.test.js +0 -45
  36. package/tests/reference-inspector.test.js +0 -42
  37. package/tests/runtime.test.js +0 -119
  38. package/tests/task-executor.test.js +0 -40
  39. package/tests/tools.test.js +0 -23
  40. package/tests/user-profile.test.js +0 -66
  41. package/tests/website-builder.test.js +0 -66
  42. package/tmp-build-smoke/nicrazy-landing/index.html +0 -53
  43. package/tmp-build-smoke/nicrazy-landing/style.css +0 -110
  44. package/tmp-shot-smoke/website-shot-1776006760424.png +0 -0
  45. package/tmp-shot-smoke/website-shot-1776007850007.png +0 -0
  46. package/tmp-shot-smoke/website-shot-1776007886209.png +0 -0
  47. package/tmp-shot-smoke/website-shot-1776007903766.png +0 -0
  48. package/tmp-shot-smoke/website-shot-1776008737117.png +0 -0
  49. package/tmp-shot-smoke/website-shot-1776008988859.png +0 -0
  50. package/tmp-smoke/nicrazy-landing/index.html +0 -66
  51. package/tmp-smoke/nicrazy-landing/style.css +0 -104
  52. package/tools.js +0 -177
  53. package/user-profile.js +0 -395
  54. package/website-builder.js +0 -394
  55. package/website-shot-1776010648230.png +0 -0
  56. package/website_builder.txt +0 -38
@@ -1,256 +0,0 @@
1
- const path = require("path");
2
- const { execFile } = require("child_process");
3
- const { storeReference } = require("./local-data");
4
-
5
- let chromiumLoader = null;
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
-
30
- function extractUrls(text) {
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))];
37
- }
38
-
39
- function summarizeHtml(source) {
40
- const title = (source.match(/<title[^>]*>([\s\S]*?)<\/title>/i) || [, ""])[1].trim();
41
- const h1s = [...source.matchAll(/<h1[^>]*>([\s\S]*?)<\/h1>/ig)]
42
- .map((match) => match[1].replace(/<[^>]+>/g, "").trim())
43
- .filter(Boolean);
44
- const links = [...source.matchAll(/<a\b/ig)].length;
45
- const images = [...source.matchAll(/<img\b/ig)].length;
46
- const text = cleanText((source.match(/<body[^>]*>([\s\S]*?)<\/body>/i) || [, source])[1]).slice(0, 3000);
47
-
48
- return {
49
- title,
50
- h1s,
51
- links,
52
- images,
53
- text,
54
- responsive: /<meta[^>]+name=["']viewport["']/i.test(source)
55
- };
56
- }
57
-
58
- function cleanText(source) {
59
- return String(source || "")
60
- .replace(/<script[\s\S]*?<\/script>/gi, " ")
61
- .replace(/<style[\s\S]*?<\/style>/gi, " ")
62
- .replace(/<[^>]+>/g, " ")
63
- .replace(/\s+/g, " ")
64
- .trim();
65
- }
66
-
67
- function shouldAutoReference(input, task = {}) {
68
- const text = String(input || "");
69
- if (!extractUrls(text).length) return false;
70
- if (["installation", "browser_capture"].includes(task?.type)) return false;
71
- if (task?.type === "website_builder" || task?.type === "analysis") return true;
72
- return /\b(referenz|vorlage|inspiriert|angelehnt|wie diese|wie die|schau dir|analysier.*https?:\/\/|bau.*https?:\/\/)\b/i.test(text);
73
- }
74
-
75
- function buildReferencePromptBlock(references = []) {
76
- if (!Array.isArray(references) || references.length === 0) return "";
77
-
78
- const lines = ["[Auto Reference Chain]"];
79
- references.forEach((ref, index) => {
80
- const summary = ref.summary || ref;
81
- lines.push(`Referenz ${index + 1}: ${ref.url || "-"}`);
82
- if (summary.title) lines.push(`Titel: ${summary.title}`);
83
- if (Array.isArray(summary.h1s) && summary.h1s.length) lines.push(`H1: ${summary.h1s.join(" | ")}`);
84
- if (summary.text) lines.push(`Text: ${String(summary.text).slice(0, 500)}`);
85
- if (typeof summary.links === "number") lines.push(`Links: ${summary.links}`);
86
- if (typeof summary.images === "number") lines.push(`Bilder: ${summary.images}`);
87
- if (ref.screenshotPath) lines.push(`Screenshot: ${ref.screenshotPath}`);
88
- if (ref.error) lines.push(`Fehler: ${ref.error}`);
89
- });
90
-
91
- return lines.join("\n");
92
- }
93
-
94
- async function loadChromium() {
95
- if (chromiumLoader !== null) return chromiumLoader;
96
-
97
- try {
98
- const playwright = require("playwright");
99
- chromiumLoader = playwright.chromium;
100
- } catch {
101
- chromiumLoader = false;
102
- }
103
-
104
- return chromiumLoader;
105
- }
106
-
107
- function execFileAsync(command, args) {
108
- return new Promise((resolve, reject) => {
109
- execFile(command, args, { maxBuffer: 20 * 1024 * 1024 }, (error, stdout, stderr) => {
110
- if (error) reject(new Error(stderr || error.message));
111
- else resolve(stdout);
112
- });
113
- });
114
- }
115
-
116
- async function fetchHtml(url, options = {}) {
117
- try {
118
- const resp = await fetch(url, {
119
- headers: {
120
- "User-Agent": options.userAgent || "BLUN-King/5.0 (+reference-inspector)"
121
- }
122
- });
123
- if (!resp.ok) throw new Error(`Reference fetch failed with HTTP ${resp.status}`);
124
- return await resp.text();
125
- } catch {
126
- try {
127
- const curlBinary = process.platform === "win32" ? "curl.exe" : "curl";
128
- return await execFileAsync(curlBinary, [
129
- "-L",
130
- "-A",
131
- options.userAgent || "BLUN-King/5.0 (+reference-inspector)",
132
- url
133
- ]);
134
- } catch {
135
- if (process.platform === "win32") {
136
- const script = [
137
- `$ProgressPreference='SilentlyContinue'`,
138
- `$resp = Invoke-WebRequest -UseBasicParsing -Headers @{'User-Agent'='${options.userAgent || "BLUN-King/5.0 (+reference-inspector)"}'} -Uri '${url}'`,
139
- `[Console]::Out.Write($resp.Content)`
140
- ].join("; ");
141
- return await execFileAsync("powershell.exe", ["-NoProfile", "-Command", script]);
142
- }
143
-
144
- throw new Error("Reference fetch failed");
145
- }
146
- }
147
- }
148
-
149
- async function inspectReference(url, options = {}) {
150
- let html = "";
151
- let summary = {
152
- title: "",
153
- h1s: [],
154
- links: 0,
155
- images: 0,
156
- responsive: false
157
- };
158
- const result = {
159
- url,
160
- fetchedAt: Date.now(),
161
- summary,
162
- html,
163
- screenshotPath: null,
164
- screenshotAvailable: false,
165
- error: null
166
- };
167
-
168
- try {
169
- html = await fetchHtml(url, options);
170
- summary = summarizeHtml(html);
171
- result.html = html;
172
- result.summary = summary;
173
- } catch (error) {
174
- result.error = error.message;
175
- }
176
-
177
- if (!result.error && options.screenshot !== false) {
178
- const chromium = await loadChromium();
179
- if (chromium) {
180
- try {
181
- const browser = await chromium.launch({ headless: true });
182
- try {
183
- const page = await browser.newPage({ viewport: { width: 1440, height: 1024 } });
184
- await page.goto(url, { waitUntil: "networkidle", timeout: options.timeout || 30000 });
185
- const stored = storeReference(url, {
186
- type: "reference",
187
- mode: "html+screenshot",
188
- summary
189
- });
190
- const screenshotPath = path.join(path.dirname(stored.filePath), `${stored.id}.png`);
191
- await page.screenshot({ path: screenshotPath, fullPage: true });
192
- result.screenshotPath = screenshotPath;
193
- result.screenshotAvailable = true;
194
- result.storage = stored.filePath;
195
- } finally {
196
- await browser.close();
197
- }
198
- } catch {
199
- result.screenshotAvailable = false;
200
- }
201
- }
202
- }
203
-
204
- if (!result.storage) {
205
- const stored = storeReference(url, {
206
- type: "reference",
207
- mode: result.error ? "error" : "html",
208
- summary,
209
- error: result.error
210
- });
211
- result.storage = stored.filePath;
212
- }
213
-
214
- return result;
215
- }
216
-
217
- async function screenshotUrl(url, options = {}) {
218
- const result = await inspectReference(url, { ...options, screenshot: true });
219
- return {
220
- url: result.url,
221
- screenshotPath: result.screenshotPath,
222
- screenshotAvailable: result.screenshotAvailable,
223
- summary: result.summary,
224
- storage: result.storage,
225
- error: result.error
226
- };
227
- }
228
-
229
- async function autoReferenceChain(input, options = {}) {
230
- const urls = extractUrls(input).slice(0, options.limit || 2);
231
- const references = [];
232
-
233
- for (const url of urls) {
234
- try {
235
- references.push(await inspectReference(url, { screenshot: options.screenshot !== false }));
236
- } catch (error) {
237
- references.push({
238
- url,
239
- error: error.message,
240
- summary: { title: "", h1s: [], links: 0, images: 0, text: "", responsive: false }
241
- });
242
- }
243
- }
244
-
245
- return references;
246
- }
247
-
248
- module.exports = {
249
- extractUrls,
250
- inspectReference,
251
- summarizeHtml,
252
- shouldAutoReference,
253
- buildReferencePromptBlock,
254
- screenshotUrl,
255
- autoReferenceChain
256
- };