blun-king-cli 5.3.5 → 5.3.6
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 +115 -22
- package/package.json +1 -1
- package/runtime.js +15 -1
package/blun-cli.js
CHANGED
|
@@ -112,6 +112,105 @@ function apiCall(method, endpoint, body) {
|
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
function downloadArtifactToWorkdir(downloadPath, filename) {
|
|
116
|
+
return new Promise(function(resolve, reject) {
|
|
117
|
+
if (!downloadPath || !filename) return resolve(null);
|
|
118
|
+
|
|
119
|
+
var url = new URL(config.api.base_url + downloadPath);
|
|
120
|
+
var isHttps = url.protocol === "https:";
|
|
121
|
+
var options = {
|
|
122
|
+
hostname: url.hostname,
|
|
123
|
+
port: url.port,
|
|
124
|
+
path: url.pathname + (url.search || ""),
|
|
125
|
+
method: "GET",
|
|
126
|
+
headers: {},
|
|
127
|
+
timeout: config.api.timeout
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
var authToken = config.api.key || config.auth.api_key;
|
|
131
|
+
if (authToken) {
|
|
132
|
+
options.headers["Authorization"] = "Bearer " + authToken;
|
|
133
|
+
} else if (config.auth.type === "oauth" && config.auth.oauth_token) {
|
|
134
|
+
options.headers["Authorization"] = "Bearer " + config.auth.oauth_token;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
var mod = isHttps ? https : http;
|
|
138
|
+
var req = mod.request(options, function(res) {
|
|
139
|
+
if (res.statusCode !== 200) {
|
|
140
|
+
var errChunks = [];
|
|
141
|
+
res.on("data", function(c) { errChunks.push(c); });
|
|
142
|
+
res.on("end", function() {
|
|
143
|
+
reject(new Error("Artifact download failed: HTTP " + res.statusCode + (errChunks.length ? " " + Buffer.concat(errChunks).toString() : "")));
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
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);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
req.on("error", function(e) { reject(e); });
|
|
175
|
+
req.on("timeout", function() { req.destroy(); reject(new Error("Artifact download timeout")); });
|
|
176
|
+
req.end();
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function saveApiOutputs(receivedFiles, artifact) {
|
|
181
|
+
var saved = [];
|
|
182
|
+
var seen = new Set();
|
|
183
|
+
|
|
184
|
+
for (var fi = 0; fi < (receivedFiles || []).length; fi++) {
|
|
185
|
+
var f = receivedFiles[fi];
|
|
186
|
+
var relativeName = f && (f.name || f.path);
|
|
187
|
+
if (f && f.content && relativeName) {
|
|
188
|
+
var localPath = path.join(config.workdir, relativeName);
|
|
189
|
+
var localDir = path.dirname(localPath);
|
|
190
|
+
if (!fs.existsSync(localDir)) fs.mkdirSync(localDir, { recursive: true });
|
|
191
|
+
fs.writeFileSync(localPath, f.content, "utf8");
|
|
192
|
+
var fileInfo = {
|
|
193
|
+
name: relativeName,
|
|
194
|
+
path: localPath,
|
|
195
|
+
size: typeof f.size === "number" ? f.size : Buffer.byteLength(String(f.content), "utf8")
|
|
196
|
+
};
|
|
197
|
+
saved.push(fileInfo);
|
|
198
|
+
seen.add(path.basename(relativeName));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (artifact && artifact.download && artifact.filename && !seen.has(path.basename(artifact.filename))) {
|
|
203
|
+
try {
|
|
204
|
+
var downloaded = await downloadArtifactToWorkdir(artifact.download, artifact.filename);
|
|
205
|
+
if (downloaded) saved.push(downloaded);
|
|
206
|
+
} catch (artifactErr) {
|
|
207
|
+
printError("Artefakt konnte nicht lokal gespeichert werden: " + artifactErr.message);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return saved;
|
|
212
|
+
}
|
|
213
|
+
|
|
115
214
|
// ── Chat History ──
|
|
116
215
|
var chatHistory = [];
|
|
117
216
|
var lastCreatedFiles = []; // Track files from last response for follow-up context
|
|
@@ -635,6 +734,7 @@ async function sendChat(message) {
|
|
|
635
734
|
|
|
636
735
|
var fullAnswer = "";
|
|
637
736
|
var receivedFiles = [];
|
|
737
|
+
var receivedArtifact = null;
|
|
638
738
|
var meta = "";
|
|
639
739
|
|
|
640
740
|
try {
|
|
@@ -655,6 +755,7 @@ async function sendChat(message) {
|
|
|
655
755
|
|
|
656
756
|
fullAnswer = resp.data.answer || "";
|
|
657
757
|
receivedFiles = resp.data.files || [];
|
|
758
|
+
receivedArtifact = resp.data.artifact || null;
|
|
658
759
|
var q = resp.data.quality || {};
|
|
659
760
|
meta = (resp.data.task_type || "") + "/" + (resp.data.role || "") + " " + BOX.dot + " score: " + (q.score || "?") +
|
|
660
761
|
(q.retried ? " " + BOX.dot + " retried" : "") +
|
|
@@ -679,19 +780,14 @@ async function sendChat(message) {
|
|
|
679
780
|
sessionCost.outputTokensEst += outTok;
|
|
680
781
|
|
|
681
782
|
// ── Save received files to local workdir ──
|
|
682
|
-
|
|
783
|
+
var savedOutputs = await saveApiOutputs(receivedFiles, receivedArtifact);
|
|
784
|
+
if (savedOutputs.length > 0) {
|
|
683
785
|
console.log(C.green + " \uD83D\uDCC1 Dateien gespeichert:" + C.reset);
|
|
684
786
|
lastCreatedFiles = [];
|
|
685
|
-
for (var fi = 0; fi <
|
|
686
|
-
var f =
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
var localDir = path.dirname(localPath);
|
|
690
|
-
if (!fs.existsSync(localDir)) fs.mkdirSync(localDir, { recursive: true });
|
|
691
|
-
fs.writeFileSync(localPath, f.content, "utf8");
|
|
692
|
-
console.log(" " + C.brightCyan + localPath + C.reset + C.dim + " (" + f.size + " bytes)" + C.reset);
|
|
693
|
-
lastCreatedFiles.push({ name: f.name, path: localPath, size: f.size });
|
|
694
|
-
}
|
|
787
|
+
for (var fi = 0; fi < savedOutputs.length; fi++) {
|
|
788
|
+
var f = savedOutputs[fi];
|
|
789
|
+
console.log(" " + C.brightCyan + f.path + C.reset + C.dim + " (" + f.size + " bytes)" + C.reset);
|
|
790
|
+
lastCreatedFiles.push({ name: f.name, path: f.path, size: f.size });
|
|
695
791
|
}
|
|
696
792
|
console.log("");
|
|
697
793
|
}
|
|
@@ -1619,17 +1715,14 @@ async function cmdAgent(args) {
|
|
|
1619
1715
|
lastAnswer = d.answer || "";
|
|
1620
1716
|
|
|
1621
1717
|
// Save files locally to workspace — API now returns file contents
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
f.localPath = localPath;
|
|
1631
|
-
}
|
|
1632
|
-
totalFiles.push(f);
|
|
1718
|
+
var savedStepOutputs = await saveApiOutputs(d.files || [], d.artifact || null);
|
|
1719
|
+
if (savedStepOutputs.length > 0) {
|
|
1720
|
+
for (var fi = 0; fi < savedStepOutputs.length; fi++) {
|
|
1721
|
+
totalFiles.push({
|
|
1722
|
+
name: savedStepOutputs[fi].name,
|
|
1723
|
+
localPath: savedStepOutputs[fi].path,
|
|
1724
|
+
size: savedStepOutputs[fi].size
|
|
1725
|
+
});
|
|
1633
1726
|
}
|
|
1634
1727
|
}
|
|
1635
1728
|
|
package/package.json
CHANGED
package/runtime.js
CHANGED
|
@@ -56,6 +56,10 @@ function clampConfidence(score) {
|
|
|
56
56
|
return Math.max(0, Math.min(1, score / 8));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function hasResolvableUrl(raw) {
|
|
60
|
+
return /(https?:\/\/\S+)|(?:^|[\s<(])(?:www\.)?[a-z0-9.-]+\.[a-z]{2,}(?:\/[^\s)>"']*)?/i.test(String(raw || ""));
|
|
61
|
+
}
|
|
62
|
+
|
|
59
63
|
function detectIntent(raw, session) {
|
|
60
64
|
const text = normalize(raw);
|
|
61
65
|
const activeTask = session?.activeTask || null;
|
|
@@ -226,6 +230,12 @@ function detectIntent(raw, session) {
|
|
|
226
230
|
const wantsDebug = hasAny(text, debugTerms);
|
|
227
231
|
const wantsOperator = hasAny(text, operatorTerms);
|
|
228
232
|
const wantsCoding = hasAny(text, codingTerms);
|
|
233
|
+
const hasUrl = hasResolvableUrl(raw);
|
|
234
|
+
const screenshotStorageComplaint =
|
|
235
|
+
activeTask?.type === "browser_capture" &&
|
|
236
|
+
wantsScreenshot &&
|
|
237
|
+
!hasUrl &&
|
|
238
|
+
/\b(ordner|datei|gespeichert|download|liegt|finde|fehlt|nicht im ordner|nicht da)\b/i.test(text);
|
|
229
239
|
|
|
230
240
|
const scores = {
|
|
231
241
|
installation:
|
|
@@ -233,7 +243,7 @@ function detectIntent(raw, session) {
|
|
|
233
243
|
scoreApproxWords(text, ["installier", "installieren", "telegram", "telegramm", "plugin", "skill", "playwright"], 1.5) +
|
|
234
244
|
scoreApproxPhrases(text, ["telegram bot", "playwright"], 2),
|
|
235
245
|
browser_capture:
|
|
236
|
-
(wantsWebsite ? 2 : scoreApproxWords(text, ["website", "webseite"], 1.5)) +
|
|
246
|
+
((wantsWebsite || hasUrl) ? 2 : scoreApproxWords(text, ["website", "webseite"], 1.5)) +
|
|
237
247
|
(wantsScreenshot ? 3 : scoreApproxWords(text, ["screenshot", "screen"], 1.5)),
|
|
238
248
|
website_builder:
|
|
239
249
|
(wantsWebsite ? 2 : scoreApproxWords(text, ["website", "webseite", "landingpage", "homepage"], 1.5)) +
|
|
@@ -256,6 +266,10 @@ function detectIntent(raw, session) {
|
|
|
256
266
|
return { kind: "followup", activeTask, scores, confidence: 1 };
|
|
257
267
|
}
|
|
258
268
|
|
|
269
|
+
if (screenshotStorageComplaint) {
|
|
270
|
+
return { kind: "followup", activeTask, scores, confidence: 1 };
|
|
271
|
+
}
|
|
272
|
+
|
|
259
273
|
if (/^(hi|hey|hallo|moin|servus|yo|na|guten morgen|guten abend)\b/.test(text) && text.split(" ").length <= 5) {
|
|
260
274
|
return { kind: "chat", scores, confidence: 1 };
|
|
261
275
|
}
|