blun-king-cli 5.3.7 → 5.4.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.
- package/blun-cli.js +310 -118
- package/blun-cli.js.claude +3578 -0
- package/browser-controller.js +16 -2
- package/package.json +1 -1
- package/runtime.js +9 -8
package/blun-cli.js
CHANGED
|
@@ -7,6 +7,12 @@ const { execSync, spawn } = require("child_process");
|
|
|
7
7
|
const https = require("https");
|
|
8
8
|
const http = require("http");
|
|
9
9
|
|
|
10
|
+
if (process.platform === "win32") {
|
|
11
|
+
try {
|
|
12
|
+
execSync("chcp 65001>nul", { stdio: "ignore", shell: process.env.ComSpec || "cmd.exe" });
|
|
13
|
+
} catch (e) {}
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
// ── Config ──
|
|
11
17
|
var _rawHome = process.env.BLUN_HOME || process.env.REAL_HOME || require("os").homedir();
|
|
12
18
|
// Strip .claude-telegram-profile suffix if present (Claude Code Telegram session override)
|
|
@@ -112,16 +118,15 @@ function apiCall(method, endpoint, body) {
|
|
|
112
118
|
});
|
|
113
119
|
}
|
|
114
120
|
|
|
115
|
-
function
|
|
121
|
+
function downloadBinary(urlOrPath) {
|
|
116
122
|
return new Promise(function(resolve, reject) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
var url = new URL(config.api.base_url + downloadPath);
|
|
123
|
+
var absolute = new URL(String(urlOrPath || ""), config.api.base_url).toString();
|
|
124
|
+
var url = new URL(absolute);
|
|
120
125
|
var isHttps = url.protocol === "https:";
|
|
121
126
|
var options = {
|
|
122
127
|
hostname: url.hostname,
|
|
123
128
|
port: url.port,
|
|
124
|
-
path: url.pathname +
|
|
129
|
+
path: url.pathname + url.search,
|
|
125
130
|
method: "GET",
|
|
126
131
|
headers: {},
|
|
127
132
|
timeout: config.api.timeout
|
|
@@ -136,84 +141,158 @@ function downloadArtifactToWorkdir(downloadPath, filename) {
|
|
|
136
141
|
|
|
137
142
|
var mod = isHttps ? https : http;
|
|
138
143
|
var req = mod.request(options, function(res) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
var localPath = path.join(config.workdir, path.basename(filename));
|
|
149
|
-
var localDir = path.dirname(localPath);
|
|
150
|
-
if (!fs.existsSync(localDir)) fs.mkdirSync(localDir, { recursive: true });
|
|
151
|
-
var out = fs.createWriteStream(localPath);
|
|
152
|
-
|
|
153
|
-
res.pipe(out);
|
|
154
|
-
out.on("finish", function() {
|
|
155
|
-
out.close(function() {
|
|
156
|
-
try {
|
|
157
|
-
var stat = fs.statSync(localPath);
|
|
158
|
-
resolve({
|
|
159
|
-
name: path.basename(localPath),
|
|
160
|
-
path: localPath,
|
|
161
|
-
size: stat.size
|
|
162
|
-
});
|
|
163
|
-
} catch (statErr) {
|
|
164
|
-
reject(statErr);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
out.on("error", function(err) {
|
|
169
|
-
out.destroy();
|
|
170
|
-
reject(err);
|
|
144
|
+
var chunks = [];
|
|
145
|
+
res.on("data", function(c) { chunks.push(c); });
|
|
146
|
+
res.on("end", function() {
|
|
147
|
+
var buffer = Buffer.concat(chunks);
|
|
148
|
+
if ((res.statusCode || 500) >= 400) {
|
|
149
|
+
return reject(new Error("HTTP " + res.statusCode + " " + buffer.toString("utf8").slice(0, 400)));
|
|
150
|
+
}
|
|
151
|
+
resolve({ buffer: buffer, contentType: res.headers["content-type"] || "", url: absolute });
|
|
171
152
|
});
|
|
172
153
|
});
|
|
173
|
-
|
|
174
154
|
req.on("error", function(e) { reject(e); });
|
|
175
|
-
req.on("timeout", function() { req.destroy(); reject(new Error("
|
|
155
|
+
req.on("timeout", function() { req.destroy(); reject(new Error("Timeout")); });
|
|
176
156
|
req.end();
|
|
177
157
|
});
|
|
178
158
|
}
|
|
179
159
|
|
|
180
|
-
|
|
181
|
-
var
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
160
|
+
function ensureLocalDir(filePath) {
|
|
161
|
+
var dir = path.dirname(filePath);
|
|
162
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function saveTextFileToWorkdir(fileName, content) {
|
|
166
|
+
var localPath = path.join(config.workdir, fileName);
|
|
167
|
+
ensureLocalDir(localPath);
|
|
168
|
+
fs.writeFileSync(localPath, content, "utf8");
|
|
169
|
+
return localPath;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function printSavedFiles(savedFiles) {
|
|
173
|
+
if (!savedFiles.length) return;
|
|
174
|
+
console.log(C.green + " Dateien gespeichert:" + C.reset);
|
|
175
|
+
savedFiles.forEach(function(file) {
|
|
176
|
+
var sizeLabel = typeof file.size === "number" ? C.dim + " (" + file.size + " bytes)" + C.reset : "";
|
|
177
|
+
console.log(" " + C.brightCyan + file.path + C.reset + sizeLabel);
|
|
178
|
+
});
|
|
179
|
+
console.log("");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function rememberBrowserContext(snapshot, extra) {
|
|
183
|
+
var snap = snapshot || {};
|
|
184
|
+
lastBrowserContext = {
|
|
185
|
+
url: snap.url || (lastBrowserContext && lastBrowserContext.url) || "",
|
|
186
|
+
title: snap.title || (lastBrowserContext && lastBrowserContext.title) || "",
|
|
187
|
+
text: String(snap.text || (lastBrowserContext && lastBrowserContext.text) || "").slice(0, 2000),
|
|
188
|
+
updatedAt: new Date().toISOString()
|
|
189
|
+
};
|
|
190
|
+
if (extra && typeof extra === "object") {
|
|
191
|
+
Object.keys(extra).forEach(function(key) {
|
|
192
|
+
lastBrowserContext[key] = extra[key];
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return lastBrowserContext;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function browserContextPrompt(input) {
|
|
199
|
+
if (!lastBrowserContext || !lastBrowserContext.url) return input;
|
|
200
|
+
return input + "\n\n[Browser-Kontext: " + (lastBrowserContext.title || lastBrowserContext.url) + " - " + String(lastBrowserContext.text || "").slice(0, 1000) + "\nURL: " + lastBrowserContext.url + "]";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function rememberBrowserAction(type, payload) {
|
|
204
|
+
lastBrowserAction = {
|
|
205
|
+
type: type,
|
|
206
|
+
payload: payload || {},
|
|
207
|
+
updatedAt: new Date().toISOString()
|
|
208
|
+
};
|
|
209
|
+
return lastBrowserAction;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function saveArtifactToWorkdir(artifact, references) {
|
|
213
|
+
if (!artifact) return [];
|
|
214
|
+
|
|
215
|
+
var referenceList = Array.isArray(references) ? references : [];
|
|
216
|
+
var downloadUrl = artifact.download || artifact.url || "";
|
|
217
|
+
var fileName = artifact.filename || artifact.name || "";
|
|
218
|
+
|
|
219
|
+
if (!fileName && downloadUrl) {
|
|
220
|
+
try {
|
|
221
|
+
fileName = path.basename(new URL(downloadUrl, config.api.base_url).pathname) || "";
|
|
222
|
+
} catch (e) {}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!fileName) {
|
|
226
|
+
fileName = "artifact-" + Date.now();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
var localPath = path.join(config.workdir, fileName);
|
|
230
|
+
ensureLocalDir(localPath);
|
|
231
|
+
|
|
232
|
+
if (artifact.content && !artifact.encoding) {
|
|
233
|
+
fs.writeFileSync(localPath, artifact.content, "utf8");
|
|
234
|
+
return [{ name: fileName, path: localPath, size: Buffer.byteLength(artifact.content, "utf8") }];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
if (!downloadUrl) throw new Error("No artifact download URL provided.");
|
|
239
|
+
var result = await downloadBinary(downloadUrl);
|
|
240
|
+
fs.writeFileSync(localPath, result.buffer);
|
|
241
|
+
return [{ name: fileName, path: localPath, size: result.buffer.length }];
|
|
242
|
+
} catch (downloadErr) {
|
|
243
|
+
var screenshotRef = referenceList.find(function(ref) { return ref && ref.url; });
|
|
244
|
+
if (/\.(png|jpg|jpeg|webp)$/i.test(fileName) && screenshotRef) {
|
|
245
|
+
var bc = require(path.join(__dirname, "browser-controller"));
|
|
246
|
+
try {
|
|
247
|
+
await bc.open(screenshotRef.url);
|
|
248
|
+
await bc.screenshot(localPath);
|
|
249
|
+
return [{ name: fileName, path: localPath, size: fs.statSync(localPath).size }];
|
|
250
|
+
} finally {
|
|
251
|
+
await bc.close().catch(function() {});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
throw new Error("Artifact download failed: " + downloadErr.message);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function saveApiOutputs(receivedFiles, artifact, references) {
|
|
259
|
+
var savedFiles = [];
|
|
260
|
+
var files = Array.isArray(receivedFiles) ? receivedFiles : [];
|
|
261
|
+
|
|
262
|
+
for (var fi = 0; fi < files.length; fi++) {
|
|
263
|
+
var f = files[fi];
|
|
264
|
+
if (f && f.content && f.name) {
|
|
265
|
+
var localPath = saveTextFileToWorkdir(f.name, f.content);
|
|
266
|
+
savedFiles.push({ name: f.name, path: localPath, size: f.size || Buffer.byteLength(f.content, "utf8") });
|
|
199
267
|
}
|
|
200
268
|
}
|
|
201
269
|
|
|
202
|
-
if (artifact
|
|
270
|
+
if (artifact) {
|
|
203
271
|
try {
|
|
204
|
-
var
|
|
205
|
-
|
|
272
|
+
var artifactFiles = await saveArtifactToWorkdir(artifact, references);
|
|
273
|
+
artifactFiles.forEach(function(file) {
|
|
274
|
+
if (!savedFiles.some(function(existing) { return existing.path === file.path; })) {
|
|
275
|
+
savedFiles.push(file);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
206
278
|
} catch (artifactErr) {
|
|
207
279
|
printError("Artefakt konnte nicht lokal gespeichert werden: " + artifactErr.message);
|
|
208
280
|
}
|
|
209
281
|
}
|
|
210
282
|
|
|
211
|
-
|
|
283
|
+
if (savedFiles.length) {
|
|
284
|
+
lastCreatedFiles = savedFiles.slice();
|
|
285
|
+
printSavedFiles(savedFiles);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return savedFiles;
|
|
212
289
|
}
|
|
213
290
|
|
|
214
291
|
// ── Chat History ──
|
|
215
292
|
var chatHistory = [];
|
|
216
293
|
var lastCreatedFiles = []; // Track files from last response for follow-up context
|
|
294
|
+
var lastBrowserContext = null; // Persist browser follow-up context even after local browser closes
|
|
295
|
+
var lastBrowserAction = null; // Repeatable local browser action state
|
|
217
296
|
|
|
218
297
|
// ── Print Helpers ──
|
|
219
298
|
function printHeader() {
|
|
@@ -739,7 +818,6 @@ async function sendChat(message) {
|
|
|
739
818
|
|
|
740
819
|
var fullAnswer = "";
|
|
741
820
|
var receivedFiles = [];
|
|
742
|
-
var receivedArtifact = null;
|
|
743
821
|
var meta = "";
|
|
744
822
|
|
|
745
823
|
try {
|
|
@@ -760,7 +838,6 @@ async function sendChat(message) {
|
|
|
760
838
|
|
|
761
839
|
fullAnswer = resp.data.answer || "";
|
|
762
840
|
receivedFiles = resp.data.files || [];
|
|
763
|
-
receivedArtifact = resp.data.artifact || null;
|
|
764
841
|
var q = resp.data.quality || {};
|
|
765
842
|
meta = (resp.data.task_type || "") + "/" + (resp.data.role || "") + " " + BOX.dot + " score: " + (q.score || "?") +
|
|
766
843
|
(q.retried ? " " + BOX.dot + " retried" : "") +
|
|
@@ -784,18 +861,7 @@ async function sendChat(message) {
|
|
|
784
861
|
sessionCost.inputTokensEst += inTok;
|
|
785
862
|
sessionCost.outputTokensEst += outTok;
|
|
786
863
|
|
|
787
|
-
|
|
788
|
-
var savedOutputs = await saveApiOutputs(receivedFiles, receivedArtifact);
|
|
789
|
-
if (savedOutputs.length > 0) {
|
|
790
|
-
console.log(C.green + " \uD83D\uDCC1 Dateien gespeichert:" + C.reset);
|
|
791
|
-
lastCreatedFiles = [];
|
|
792
|
-
for (var fi = 0; fi < savedOutputs.length; fi++) {
|
|
793
|
-
var f = savedOutputs[fi];
|
|
794
|
-
console.log(" " + C.brightCyan + f.path + C.reset + C.dim + " (" + f.size + " bytes)" + C.reset);
|
|
795
|
-
lastCreatedFiles.push({ name: f.name, path: f.path, size: f.size });
|
|
796
|
-
}
|
|
797
|
-
console.log("");
|
|
798
|
-
}
|
|
864
|
+
await saveApiOutputs(receivedFiles, resp.data.artifact || null, resp.data.references || []);
|
|
799
865
|
|
|
800
866
|
// Auto Memory Dream Mode
|
|
801
867
|
dreamCounter++;
|
|
@@ -1105,7 +1171,6 @@ function cmdSet(args) {
|
|
|
1105
1171
|
break;
|
|
1106
1172
|
|
|
1107
1173
|
default:
|
|
1108
|
-
// Handle plugin.X.Y settings
|
|
1109
1174
|
if (key && key.startsWith("plugin.")) {
|
|
1110
1175
|
var pluginParts = key.split(".");
|
|
1111
1176
|
if (pluginParts.length === 3) {
|
|
@@ -1658,7 +1723,6 @@ function cmdPlugin(args) {
|
|
|
1658
1723
|
if (!plugins[parts[1]]) { printError("Plugin not found: " + parts[1]); return; }
|
|
1659
1724
|
plugins[parts[1]].running = true;
|
|
1660
1725
|
savePlugins(plugins);
|
|
1661
|
-
// Actually start built-in plugins
|
|
1662
1726
|
if (parts[1] === "telegram") { ensureTgBot(); }
|
|
1663
1727
|
printSuccess("Plugin '" + parts[1] + "' started.");
|
|
1664
1728
|
|
|
@@ -1864,17 +1928,10 @@ async function cmdAgent(args) {
|
|
|
1864
1928
|
var d = resp.data;
|
|
1865
1929
|
lastAnswer = d.answer || "";
|
|
1866
1930
|
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
totalFiles.push({
|
|
1872
|
-
name: savedStepOutputs[fi].name,
|
|
1873
|
-
localPath: savedStepOutputs[fi].path,
|
|
1874
|
-
size: savedStepOutputs[fi].size
|
|
1875
|
-
});
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1931
|
+
var savedApiFiles = await saveApiOutputs(d.files || [], d.artifact || null, d.references || []);
|
|
1932
|
+
savedApiFiles.forEach(function(file) {
|
|
1933
|
+
totalFiles.push({ name: file.name, localPath: file.path, size: file.size });
|
|
1934
|
+
});
|
|
1878
1935
|
|
|
1879
1936
|
// If answer contains code blocks with filenames, extract and save them
|
|
1880
1937
|
var codeBlockRe = /```(?:\w+)?\s*\n\/\/\s*(.+\.(?:html|css|js|json|md|txt|py))\n([\s\S]*?)```/g;
|
|
@@ -3307,23 +3364,157 @@ async function main() {
|
|
|
3307
3364
|
await handleCommand(input);
|
|
3308
3365
|
runHook("post", input.split(/\s+/)[0].slice(1));
|
|
3309
3366
|
} else {
|
|
3310
|
-
|
|
3367
|
+
var lowerInput = String(input || "").toLowerCase();
|
|
3368
|
+
var normalizedInput = String(input || "")
|
|
3369
|
+
.normalize("NFD")
|
|
3370
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
3371
|
+
.toLowerCase();
|
|
3372
|
+
var trimmedInput = String(input || "").trim();
|
|
3311
3373
|
var urlMatch = input.match(/(https?:\/\/\S+)/i) || input.match(/\b((?:www\.)?[a-zA-Z0-9-]+\.[a-z]{2,}(?:\/\S*)?)\b/i);
|
|
3312
|
-
var
|
|
3313
|
-
|
|
3374
|
+
var screenshotish = /\b(screenshot|screen ?shot|scre+ns?h[o0]t|screesnhot|screnshot|bildschirmfoto|bild)\b/i;
|
|
3375
|
+
var browserWords = /\b(offne|oeffne|open|schau|zeig|geh auf|gehen|navigate|browse|screenshot|bildschirmfoto|aufrufen|besuch|check|checken|pruef|analysier)\b/i;
|
|
3376
|
+
var localBrowserRequest = /\b(browser|bowser|brwoser|broser|browserfenster|lokalen browser|local browser|auf meinem pc im browser|im browser|browser lokal|lokal im browser)\b/i.test(normalizedInput);
|
|
3377
|
+
var looksLikeSearch = !urlMatch &&
|
|
3378
|
+
trimmedInput.length > 1 &&
|
|
3379
|
+
trimmedInput.length <= 80 &&
|
|
3380
|
+
trimmedInput.split(/\s+/).length <= 5 &&
|
|
3381
|
+
!/^(hi|hallo|hey|moin|servus|yo|sup|ok|ja|nein|danke|thanks|bye|tschuess|ciao)$/i.test(normalizedInput) &&
|
|
3382
|
+
!/^(wo|was|wie|wer|wann|warum)\b/i.test(normalizedInput) &&
|
|
3383
|
+
!/(screenshot|bildschirmfoto|png|datei|file|ordner|link|pfad|screesnhot|screnshot)\b/i.test(normalizedInput);
|
|
3384
|
+
var browserFollowupRequest = !urlMatch &&
|
|
3385
|
+
!!(lastBrowserContext && lastBrowserContext.url) &&
|
|
3386
|
+
/^(und|auch|noch|dann|danach|davon|darunter|weiter|weiterhin)\b/i.test(normalizedInput);
|
|
3387
|
+
var browserController = null;
|
|
3388
|
+
var browserOpen = false;
|
|
3389
|
+
try {
|
|
3390
|
+
browserController = require(path.join(__dirname, "browser-controller"));
|
|
3391
|
+
browserOpen = typeof browserController.isOpen === "function" && browserController.isOpen();
|
|
3392
|
+
} catch (browserStateErr) {}
|
|
3393
|
+
|
|
3394
|
+
if (lastCreatedFiles.length > 0 &&
|
|
3395
|
+
/(wo|welcher|welche|link|pfad|ordner|liegt|finde|zeig|anzeigen)/i.test(lowerInput) &&
|
|
3396
|
+
/(screenshot|bild|png|datei|file)/i.test(lowerInput)) {
|
|
3397
|
+
printInfo("Letzte gespeicherte Datei(en):");
|
|
3398
|
+
printSavedFiles(lastCreatedFiles);
|
|
3399
|
+
}
|
|
3400
|
+
else if (!urlMatch &&
|
|
3401
|
+
/\b(browser|browserfenster|lokalen browser|local browser|lokalen browsr|lokalen brwoser)\b/i.test(normalizedInput) &&
|
|
3402
|
+
!/(screenshot|bildschirmfoto|png|bild)\b/i.test(normalizedInput)) {
|
|
3403
|
+
try {
|
|
3404
|
+
printInfo("Oeffne lokalen Browser...");
|
|
3405
|
+
await browserController.open("about:blank");
|
|
3406
|
+
rememberBrowserAction("open", { url: "about:blank" });
|
|
3407
|
+
printSuccess("Lokaler Browser geoeffnet.");
|
|
3408
|
+
} catch (bcOpenErr) {
|
|
3409
|
+
printError("Lokaler Browser konnte nicht geoeffnet werden: " + bcOpenErr.message);
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
else if (!urlMatch && lastBrowserAction && /\b(nochmal|noch mal|erneut|wieder|same again|again)\b/i.test(normalizedInput)) {
|
|
3413
|
+
try {
|
|
3414
|
+
if (lastBrowserAction.type === "search" && lastBrowserAction.payload && lastBrowserAction.payload.query) {
|
|
3415
|
+
printInfo("Suche im lokalen Browser: " + lastBrowserAction.payload.query);
|
|
3416
|
+
var repeatSearchSnap = await browserController.search(lastBrowserAction.payload.query);
|
|
3417
|
+
rememberBrowserContext(repeatSearchSnap, { query: lastBrowserAction.payload.query, source: "search_repeat" });
|
|
3418
|
+
printSuccess(repeatSearchSnap.title || "Google-Suche geoeffnet.");
|
|
3419
|
+
console.log(" " + C.gray + "URL: " + C.reset + repeatSearchSnap.url);
|
|
3420
|
+
} else if (lastBrowserAction.type === "screenshot" && (browserOpen || (lastBrowserContext && lastBrowserContext.url))) {
|
|
3421
|
+
var repeatSnap = browserOpen ? await browserController.snapshot() : await browserController.open(lastBrowserContext.url);
|
|
3422
|
+
rememberBrowserContext(repeatSnap, { source: "snapshot_repeat" });
|
|
3423
|
+
var repeatShot = path.join(config.workdir, "screenshot-" + Date.now() + ".png");
|
|
3424
|
+
await browserController.screenshot(repeatShot);
|
|
3425
|
+
lastCreatedFiles = [{ name: path.basename(repeatShot), path: repeatShot, size: fs.statSync(repeatShot).size }];
|
|
3426
|
+
rememberBrowserAction("screenshot", { url: repeatSnap.url, path: repeatShot });
|
|
3427
|
+
printSuccess("Screenshot gespeichert: " + repeatShot);
|
|
3428
|
+
console.log(" " + C.gray + "URL: " + C.reset + repeatSnap.url);
|
|
3429
|
+
} else if (lastBrowserAction.type === "open" && lastBrowserAction.payload && lastBrowserAction.payload.url) {
|
|
3430
|
+
printInfo("Oeffne " + lastBrowserAction.payload.url + "...");
|
|
3431
|
+
var repeatOpenSnap = await browserController.open(lastBrowserAction.payload.url);
|
|
3432
|
+
rememberBrowserContext(repeatOpenSnap, { source: "open_repeat", requestedUrl: lastBrowserAction.payload.url });
|
|
3433
|
+
printSuccess(repeatOpenSnap.title || lastBrowserAction.payload.url);
|
|
3434
|
+
console.log(" " + C.gray + "URL: " + C.reset + repeatOpenSnap.url);
|
|
3435
|
+
} else {
|
|
3436
|
+
await sendChat(browserContextPrompt(input));
|
|
3437
|
+
}
|
|
3438
|
+
} catch (repeatErr) {
|
|
3439
|
+
await sendChat(browserContextPrompt(input));
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
else if (!urlMatch && (browserOpen || (lastBrowserContext && lastBrowserContext.url)) && screenshotish.test(normalizedInput)) {
|
|
3443
|
+
try {
|
|
3444
|
+
var currentSnap = browserOpen ? await browserController.snapshot() : await browserController.open(lastBrowserContext.url);
|
|
3445
|
+
rememberBrowserContext(currentSnap, { source: "snapshot_followup" });
|
|
3446
|
+
var currentShot = path.join(config.workdir, "screenshot-" + Date.now() + ".png");
|
|
3447
|
+
await browserController.screenshot(currentShot);
|
|
3448
|
+
lastCreatedFiles = [{ name: path.basename(currentShot), path: currentShot, size: fs.statSync(currentShot).size }];
|
|
3449
|
+
rememberBrowserAction("screenshot", { url: currentSnap.url, path: currentShot });
|
|
3450
|
+
printSuccess("Screenshot der offenen Seite gespeichert: " + currentShot);
|
|
3451
|
+
console.log(" " + C.gray + "URL: " + C.reset + currentSnap.url);
|
|
3452
|
+
} catch (followErr) {
|
|
3453
|
+
printError("Screenshot der offenen Seite fehlgeschlagen: " + followErr.message);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
else if (!urlMatch && (browserOpen || (lastBrowserContext && lastBrowserContext.url)) && /(welche seite|wo bist du|welche url|url|was ist offen|wo gerade)/i.test(normalizedInput)) {
|
|
3457
|
+
try {
|
|
3458
|
+
var statusSnap = browserOpen ? await browserController.snapshot() : lastBrowserContext;
|
|
3459
|
+
rememberBrowserContext(statusSnap, { source: "status_followup" });
|
|
3460
|
+
printSuccess(statusSnap.title || "Browserseite aktiv.");
|
|
3461
|
+
console.log(" " + C.gray + "URL: " + C.reset + statusSnap.url);
|
|
3462
|
+
} catch (statusErr) {
|
|
3463
|
+
printError("Browserstatus konnte nicht gelesen werden: " + statusErr.message);
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
else if (!urlMatch && (browserOpen || (lastBrowserContext && lastBrowserContext.url)) && /(analysier|analyse|schau|check|pruef|was siehst du|was ist auf der seite)/i.test(normalizedInput)) {
|
|
3467
|
+
try {
|
|
3468
|
+
var ctxSnap = browserOpen ? await browserController.snapshot() : lastBrowserContext;
|
|
3469
|
+
rememberBrowserContext(ctxSnap, { source: "analysis_followup" });
|
|
3470
|
+
await sendChat(browserContextPrompt(input));
|
|
3471
|
+
} catch (ctxErr) {
|
|
3472
|
+
await sendChat(input);
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
else if (browserFollowupRequest) {
|
|
3476
|
+
await sendChat(browserContextPrompt(input));
|
|
3477
|
+
}
|
|
3478
|
+
else if (looksLikeSearch) {
|
|
3479
|
+
try {
|
|
3480
|
+
if (browserOpen || (lastBrowserContext && lastBrowserContext.url)) {
|
|
3481
|
+
printInfo("Suche im lokalen Browser: " + trimmedInput);
|
|
3482
|
+
var searchSnap = await browserController.search(trimmedInput);
|
|
3483
|
+
rememberBrowserContext(searchSnap, { query: trimmedInput, source: "search" });
|
|
3484
|
+
rememberBrowserAction("search", { query: trimmedInput });
|
|
3485
|
+
printSuccess(searchSnap.title || "Google-Suche geoeffnet.");
|
|
3486
|
+
console.log(" " + C.gray + "URL: " + C.reset + searchSnap.url);
|
|
3487
|
+
if (searchSnap.text) {
|
|
3488
|
+
console.log(C.dim + " " + BOX.h.repeat(40) + C.reset);
|
|
3489
|
+
console.log(" " + searchSnap.text.slice(0, 500).replace(/\n{3,}/g, "\n\n"));
|
|
3490
|
+
}
|
|
3491
|
+
console.log("");
|
|
3492
|
+
} else {
|
|
3493
|
+
await sendChat(input);
|
|
3494
|
+
}
|
|
3495
|
+
} catch (searchErr) {
|
|
3496
|
+
await sendChat(input);
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
else if (urlMatch && (localBrowserRequest || browserWords.test(normalizedInput) || screenshotish.test(normalizedInput) || /\blokal|mein pc|pc\b/i.test(normalizedInput) || normalizedInput.trim() === String(urlMatch[0]).toLowerCase() || normalizedInput.trim() === String(urlMatch[1]).toLowerCase())) {
|
|
3314
3500
|
var browserUrl = urlMatch[1];
|
|
3315
3501
|
try {
|
|
3316
3502
|
var bc = require(path.join(__dirname, "browser-controller"));
|
|
3317
|
-
if (
|
|
3503
|
+
if (screenshotish.test(normalizedInput)) {
|
|
3318
3504
|
printInfo("Screenshot: " + browserUrl);
|
|
3319
3505
|
var snap = await bc.open(browserUrl);
|
|
3506
|
+
rememberBrowserContext(snap, { source: "open", requestedUrl: browserUrl });
|
|
3320
3507
|
var shotFile = path.join(config.workdir, "screenshot-" + Date.now() + ".png");
|
|
3321
3508
|
await bc.screenshot(shotFile);
|
|
3509
|
+
lastCreatedFiles = [{ name: path.basename(shotFile), path: shotFile, size: fs.statSync(shotFile).size }];
|
|
3510
|
+
rememberBrowserAction("screenshot", { url: snap.url, path: shotFile });
|
|
3322
3511
|
printSuccess("Screenshot gespeichert: " + shotFile);
|
|
3323
3512
|
console.log(" " + C.gray + "Title: " + C.reset + snap.title);
|
|
3324
3513
|
} else {
|
|
3325
|
-
printInfo("
|
|
3514
|
+
printInfo("Oeffne " + browserUrl + "...");
|
|
3326
3515
|
var snap = await bc.open(browserUrl);
|
|
3516
|
+
rememberBrowserContext(snap, { source: "open", requestedUrl: browserUrl });
|
|
3517
|
+
rememberBrowserAction("open", { url: snap.url });
|
|
3327
3518
|
printSuccess(snap.title);
|
|
3328
3519
|
console.log(" " + C.gray + "URL: " + C.reset + snap.url);
|
|
3329
3520
|
if (snap.text) {
|
|
@@ -3332,37 +3523,38 @@ async function main() {
|
|
|
3332
3523
|
console.log(" " + preview);
|
|
3333
3524
|
}
|
|
3334
3525
|
console.log("");
|
|
3335
|
-
|
|
3336
|
-
|
|
3526
|
+
if (!localBrowserRequest) {
|
|
3527
|
+
await sendChat(browserContextPrompt(input));
|
|
3528
|
+
}
|
|
3337
3529
|
}
|
|
3338
|
-
} catch(bcErr) {
|
|
3339
|
-
// Playwright not available, send to chat normally
|
|
3530
|
+
} catch (bcErr) {
|
|
3340
3531
|
await sendChat(input);
|
|
3341
3532
|
}
|
|
3342
3533
|
}
|
|
3343
|
-
// Intent detection — natural language → command
|
|
3344
3534
|
else {
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3535
|
+
var detected = detectIntent(input);
|
|
3536
|
+
if (detected) {
|
|
3537
|
+
printInfo("-> " + detected);
|
|
3538
|
+
runHook("pre", detected.split(/\s+/)[0].slice(1));
|
|
3539
|
+
await handleCommand(detected);
|
|
3540
|
+
runHook("post", detected.split(/\s+/)[0].slice(1));
|
|
3541
|
+
} else if (lastBrowserContext && lastBrowserContext.url && !/^(hi|hallo|hey|moin|servus|yo|sup|ok|ja|nein|danke|thanks|bye|tschuess|ciao)$/i.test(normalizedInput)) {
|
|
3542
|
+
await sendChat(browserContextPrompt(input));
|
|
3543
|
+
} else if (sessionMode === "agent") {
|
|
3544
|
+
// Agent mode: detect if question or task
|
|
3545
|
+
var trimmed = input.trim();
|
|
3546
|
+
var isChat = trimmed.length < 15 || // short messages = chat
|
|
3547
|
+
/^(hi|hallo|hey|moin|servus|yo|sup|ok|ja|nein|danke|thanks|bye|tschuess|ciao)/i.test(trimmed) ||
|
|
3548
|
+
/^(wo |was |wie |wer |wann |warum |kannst|hast|ist |sind |where|what|how|who|when|why|can you|do you|is |are )/i.test(trimmed) ||
|
|
3549
|
+
/\?$/.test(trimmed);
|
|
3550
|
+
if (isChat) {
|
|
3551
|
+
await sendChat(input);
|
|
3552
|
+
} else {
|
|
3553
|
+
await cmdAgent(input);
|
|
3554
|
+
}
|
|
3360
3555
|
} else {
|
|
3361
|
-
await
|
|
3556
|
+
await sendChat(input);
|
|
3362
3557
|
}
|
|
3363
|
-
} else {
|
|
3364
|
-
await sendChat(input);
|
|
3365
|
-
}
|
|
3366
3558
|
}
|
|
3367
3559
|
}
|
|
3368
3560
|
|