blun-king-cli 1.1.0 → 1.3.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/bin/blun.js +349 -34
- package/package.json +1 -1
package/bin/blun.js
CHANGED
|
@@ -47,10 +47,16 @@ var config = loadConfig();
|
|
|
47
47
|
|
|
48
48
|
// ── Colors ──
|
|
49
49
|
const C = {
|
|
50
|
-
reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
|
|
50
|
+
reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m", italic: "\x1b[3m",
|
|
51
51
|
red: "\x1b[31m", green: "\x1b[32m", yellow: "\x1b[33m",
|
|
52
52
|
blue: "\x1b[34m", magenta: "\x1b[35m", cyan: "\x1b[36m", white: "\x1b[37m",
|
|
53
|
-
|
|
53
|
+
gray: "\x1b[90m", brightBlue: "\x1b[94m", brightCyan: "\x1b[96m", brightWhite: "\x1b[97m",
|
|
54
|
+
bg_blue: "\x1b[44m", bg_green: "\x1b[42m", bg_gray: "\x1b[100m", bg_darkBlue: "\x1b[48;5;17m"
|
|
55
|
+
};
|
|
56
|
+
// Box drawing helpers
|
|
57
|
+
const BOX = {
|
|
58
|
+
tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│",
|
|
59
|
+
sep: "├", sepR: "┤", dot: "●", arrow: "❯", chat: "💬", bot: "🤖", user: "👤"
|
|
54
60
|
};
|
|
55
61
|
|
|
56
62
|
function log(msg) { if (config.verbose) fs.appendFileSync(LOG_FILE, new Date().toISOString() + " " + msg + "\n"); }
|
|
@@ -98,51 +104,81 @@ var chatHistory = [];
|
|
|
98
104
|
|
|
99
105
|
// ── Print Helpers ──
|
|
100
106
|
function printHeader() {
|
|
107
|
+
var w = Math.min(process.stdout.columns || 60, 60);
|
|
108
|
+
var line = BOX.h.repeat(w - 4);
|
|
101
109
|
console.log("");
|
|
102
|
-
console.log(C.
|
|
103
|
-
console.log(C.bold + C.
|
|
104
|
-
console.log(C.
|
|
105
|
-
console.log(C.
|
|
106
|
-
console.log("");
|
|
107
|
-
console.log(C.
|
|
108
|
-
|
|
109
|
-
console.log(C.
|
|
110
|
-
console.log(C.
|
|
110
|
+
console.log(C.brightBlue + " " + BOX.tl + line + BOX.tr + C.reset);
|
|
111
|
+
console.log(C.brightBlue + " " + BOX.v + C.reset + C.bold + C.brightWhite + " " + BOX.bot + " BLUN KING CLI" + C.reset + C.dim + " v1.1" + C.reset + " ".repeat(Math.max(0, w - 24)) + C.brightBlue + BOX.v + C.reset);
|
|
112
|
+
console.log(C.brightBlue + " " + BOX.v + C.reset + C.gray + " Premium KI — Local First — Autonom" + C.reset + " ".repeat(Math.max(0, w - 41)) + C.brightBlue + BOX.v + C.reset);
|
|
113
|
+
console.log(C.brightBlue + " " + BOX.v + line + BOX.v + C.reset);
|
|
114
|
+
console.log(C.brightBlue + " " + BOX.v + C.reset + C.gray + " API " + C.brightCyan + config.api.base_url + C.reset + " ".repeat(Math.max(0, w - 14 - config.api.base_url.length)) + C.brightBlue + BOX.v + C.reset);
|
|
115
|
+
console.log(C.brightBlue + " " + BOX.v + C.reset + C.gray + " Model " + C.brightCyan + config.model + C.reset + " ".repeat(Math.max(0, w - 14 - config.model.length)) + C.brightBlue + BOX.v + C.reset);
|
|
116
|
+
var wdShort = config.workdir.length > w - 16 ? "..." + config.workdir.slice(-(w - 19)) : config.workdir;
|
|
117
|
+
console.log(C.brightBlue + " " + BOX.v + C.reset + C.gray + " Dir " + C.brightCyan + wdShort + C.reset + " ".repeat(Math.max(0, w - 14 - wdShort.length)) + C.brightBlue + BOX.v + C.reset);
|
|
118
|
+
console.log(C.brightBlue + " " + BOX.bl + line + BOX.br + C.reset);
|
|
111
119
|
console.log("");
|
|
112
|
-
console.log(C.
|
|
120
|
+
console.log(C.gray + " /help " + C.dim + "for commands " + C.gray + BOX.dot + " just type to chat" + C.reset);
|
|
113
121
|
console.log("");
|
|
114
122
|
}
|
|
115
123
|
|
|
116
|
-
|
|
124
|
+
// ── Update Checker ──
|
|
125
|
+
function checkForUpdates() {
|
|
126
|
+
try {
|
|
127
|
+
var currentVersion = "1.3.0";
|
|
128
|
+
var latest = execSync("npm view blun-king-cli version 2>/dev/null", { encoding: "utf8", timeout: 5000 }).trim();
|
|
129
|
+
if (latest && latest !== currentVersion) {
|
|
130
|
+
console.log(C.yellow + C.bold + " " + BOX.arrow + " Update available: " + currentVersion + " → " + latest + C.reset);
|
|
131
|
+
console.log(C.yellow + " npm update -g blun-king-cli" + C.reset);
|
|
132
|
+
console.log("");
|
|
133
|
+
}
|
|
134
|
+
} catch(e) { /* silent */ }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function printAnswer(answer, meta) {
|
|
117
138
|
console.log("");
|
|
118
|
-
console.log(C.green + C.bold + "BLUN King
|
|
119
|
-
|
|
139
|
+
console.log(C.green + C.bold + " " + BOX.bot + " BLUN King" + C.reset + (meta ? C.gray + " " + BOX.dot + " " + meta + C.reset : ""));
|
|
140
|
+
console.log(C.green + " " + BOX.h.repeat(40) + C.reset);
|
|
141
|
+
// Markdown rendering
|
|
120
142
|
var lines = answer.split("\n");
|
|
121
143
|
var inCode = false;
|
|
122
144
|
for (var i = 0; i < lines.length; i++) {
|
|
123
145
|
var line = lines[i];
|
|
124
146
|
if (line.startsWith("```")) {
|
|
125
147
|
inCode = !inCode;
|
|
126
|
-
|
|
148
|
+
if (inCode) {
|
|
149
|
+
var lang = line.slice(3).trim();
|
|
150
|
+
console.log(C.dim + " " + BOX.tl + BOX.h.repeat(38) + (lang ? " " + lang + " " : "") + C.reset);
|
|
151
|
+
} else {
|
|
152
|
+
console.log(C.dim + " " + BOX.bl + BOX.h.repeat(38) + C.reset);
|
|
153
|
+
}
|
|
127
154
|
} else if (inCode) {
|
|
128
|
-
console.log(C.cyan + line + C.reset);
|
|
155
|
+
console.log(C.cyan + " " + BOX.v + " " + line + C.reset);
|
|
129
156
|
} else if (line.startsWith("# ")) {
|
|
130
|
-
console.log(C.bold + C.yellow + line + C.reset);
|
|
157
|
+
console.log(C.bold + C.yellow + " " + line + C.reset);
|
|
131
158
|
} else if (line.startsWith("## ")) {
|
|
132
|
-
console.log(C.bold + C.magenta + line + C.reset);
|
|
159
|
+
console.log(C.bold + C.magenta + " " + line + C.reset);
|
|
133
160
|
} else if (line.startsWith("### ")) {
|
|
134
|
-
console.log(C.bold + line + C.reset);
|
|
161
|
+
console.log(C.bold + " " + line + C.reset);
|
|
135
162
|
} else if (line.startsWith("- ") || line.startsWith("* ")) {
|
|
136
|
-
console.log(C.white + "
|
|
163
|
+
console.log(C.white + " " + line + C.reset);
|
|
137
164
|
} else if (line.match(/^\d+\./)) {
|
|
138
|
-
console.log(C.white + "
|
|
165
|
+
console.log(C.white + " " + line + C.reset);
|
|
166
|
+
} else if (line.startsWith("**") && line.endsWith("**")) {
|
|
167
|
+
console.log(C.bold + C.brightWhite + " " + line + C.reset);
|
|
139
168
|
} else {
|
|
140
|
-
console.log(line);
|
|
169
|
+
console.log(" " + line);
|
|
141
170
|
}
|
|
142
171
|
}
|
|
143
172
|
console.log("");
|
|
144
173
|
}
|
|
145
174
|
|
|
175
|
+
function printUserMessage(msg) {
|
|
176
|
+
console.log("");
|
|
177
|
+
console.log(C.brightBlue + C.bold + " " + BOX.user + " Du" + C.reset);
|
|
178
|
+
console.log(C.brightBlue + " " + BOX.h.repeat(40) + C.reset);
|
|
179
|
+
console.log(C.brightWhite + " " + msg + C.reset);
|
|
180
|
+
}
|
|
181
|
+
|
|
146
182
|
function printError(msg) { console.log(C.red + "ERROR: " + msg + C.reset); }
|
|
147
183
|
function printInfo(msg) { console.log(C.cyan + msg + C.reset); }
|
|
148
184
|
function printSuccess(msg) { console.log(C.green + "✓ " + msg + C.reset); }
|
|
@@ -176,6 +212,16 @@ async function handleCommand(input) {
|
|
|
176
212
|
console.log(" /score <text> Score a response");
|
|
177
213
|
console.log(" /eval Run eval suite");
|
|
178
214
|
console.log("");
|
|
215
|
+
console.log(C.yellow + " PLUGINS (MCP)" + C.reset);
|
|
216
|
+
console.log(" /plugin list List installed plugins");
|
|
217
|
+
console.log(" /plugin add <cmd> Add MCP server (npm pkg or local path)");
|
|
218
|
+
console.log(" /plugin remove <n>Remove a plugin");
|
|
219
|
+
console.log(" /plugin run <n> Start a plugin server");
|
|
220
|
+
console.log(" /plugin stop <n> Stop a plugin server");
|
|
221
|
+
console.log(" /permissions Show permission settings");
|
|
222
|
+
console.log(" /permissions allow-all Allow all tool calls");
|
|
223
|
+
console.log(" /permissions ask Ask before each tool call");
|
|
224
|
+
console.log("");
|
|
179
225
|
console.log(C.yellow + " SETTINGS" + C.reset);
|
|
180
226
|
console.log(" /settings Show all settings");
|
|
181
227
|
console.log(" /set auth api Switch to API key auth");
|
|
@@ -310,6 +356,26 @@ async function handleCommand(input) {
|
|
|
310
356
|
cmdInit();
|
|
311
357
|
break;
|
|
312
358
|
|
|
359
|
+
case "/plugin":
|
|
360
|
+
cmdPlugin(args);
|
|
361
|
+
break;
|
|
362
|
+
|
|
363
|
+
case "/permissions":
|
|
364
|
+
cmdPermissions(args);
|
|
365
|
+
break;
|
|
366
|
+
|
|
367
|
+
case "/agent":
|
|
368
|
+
await cmdAgent(args);
|
|
369
|
+
break;
|
|
370
|
+
|
|
371
|
+
case "/screenshot":
|
|
372
|
+
await cmdScreenshot(args);
|
|
373
|
+
break;
|
|
374
|
+
|
|
375
|
+
case "/render":
|
|
376
|
+
await cmdRender(args);
|
|
377
|
+
break;
|
|
378
|
+
|
|
313
379
|
case "/exit":
|
|
314
380
|
case "/quit":
|
|
315
381
|
case "/q":
|
|
@@ -324,12 +390,22 @@ async function handleCommand(input) {
|
|
|
324
390
|
// ── Chat ──
|
|
325
391
|
async function sendChat(message) {
|
|
326
392
|
try {
|
|
327
|
-
|
|
393
|
+
printUserMessage(message);
|
|
394
|
+
// Animated thinking
|
|
395
|
+
var dots = 0;
|
|
396
|
+
var thinkFrames = ["thinking", "thinking.", "thinking..", "thinking..."];
|
|
397
|
+
var thinkTimer = setInterval(function() {
|
|
398
|
+
process.stdout.write("\r" + C.dim + " " + BOX.bot + " " + thinkFrames[dots % 4] + " " + C.reset);
|
|
399
|
+
dots++;
|
|
400
|
+
}, 300);
|
|
401
|
+
|
|
328
402
|
var resp = await apiCall("POST", "/chat", {
|
|
329
403
|
message: message,
|
|
330
404
|
history: chatHistory.slice(-10)
|
|
331
405
|
});
|
|
332
|
-
|
|
406
|
+
|
|
407
|
+
clearInterval(thinkTimer);
|
|
408
|
+
process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
333
409
|
|
|
334
410
|
if (resp.status !== 200) {
|
|
335
411
|
printError(resp.data.error || "API Error " + resp.status);
|
|
@@ -339,17 +415,14 @@ async function sendChat(message) {
|
|
|
339
415
|
chatHistory.push({ role: "user", content: message });
|
|
340
416
|
chatHistory.push({ role: "assistant", content: resp.data.answer });
|
|
341
417
|
|
|
342
|
-
printAnswer(resp.data.answer);
|
|
343
|
-
|
|
344
|
-
// Status line
|
|
345
418
|
var q = resp.data.quality || {};
|
|
346
|
-
|
|
347
|
-
(q.retried ? "
|
|
348
|
-
|
|
349
|
-
console.log("");
|
|
419
|
+
var meta = resp.data.task_type + "/" + resp.data.role + " " + BOX.dot + " score: " + (q.score || "?") +
|
|
420
|
+
(q.retried ? " " + BOX.dot + " retried" : "");
|
|
421
|
+
printAnswer(resp.data.answer, meta);
|
|
350
422
|
|
|
351
423
|
} catch(e) {
|
|
352
|
-
|
|
424
|
+
if (typeof thinkTimer !== "undefined") clearInterval(thinkTimer);
|
|
425
|
+
process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
353
426
|
printError("Connection failed: " + e.message);
|
|
354
427
|
}
|
|
355
428
|
}
|
|
@@ -1042,6 +1115,232 @@ function cmdInit(args) {
|
|
|
1042
1115
|
}
|
|
1043
1116
|
}
|
|
1044
1117
|
|
|
1118
|
+
// ── Plugins (MCP-style) ──
|
|
1119
|
+
const PLUGINS_FILE = path.join(CONFIG_DIR, "plugins.json");
|
|
1120
|
+
const PERMISSIONS_FILE = path.join(CONFIG_DIR, "permissions.json");
|
|
1121
|
+
|
|
1122
|
+
function loadPlugins() {
|
|
1123
|
+
if (fs.existsSync(PLUGINS_FILE)) {
|
|
1124
|
+
try { return JSON.parse(fs.readFileSync(PLUGINS_FILE, "utf8")); } catch(e) {}
|
|
1125
|
+
}
|
|
1126
|
+
return {};
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function savePlugins(p) { fs.writeFileSync(PLUGINS_FILE, JSON.stringify(p, null, 2)); }
|
|
1130
|
+
|
|
1131
|
+
function loadPermissions() {
|
|
1132
|
+
if (fs.existsSync(PERMISSIONS_FILE)) {
|
|
1133
|
+
try { return JSON.parse(fs.readFileSync(PERMISSIONS_FILE, "utf8")); } catch(e) {}
|
|
1134
|
+
}
|
|
1135
|
+
return { mode: "ask", allowed: [], denied: [] };
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
function savePermissions(p) { fs.writeFileSync(PERMISSIONS_FILE, JSON.stringify(p, null, 2)); }
|
|
1139
|
+
|
|
1140
|
+
// Built-in plugin templates
|
|
1141
|
+
var PLUGIN_TEMPLATES = {
|
|
1142
|
+
telegram: {
|
|
1143
|
+
name: "telegram",
|
|
1144
|
+
description: "Telegram Bot Bridge — send/receive messages",
|
|
1145
|
+
type: "builtin",
|
|
1146
|
+
config: { bot_token: "", chat_id: "" },
|
|
1147
|
+
commands: ["/tg send <msg>", "/tg status"],
|
|
1148
|
+
setup: ["bot_token", "chat_id"]
|
|
1149
|
+
},
|
|
1150
|
+
github: {
|
|
1151
|
+
name: "github",
|
|
1152
|
+
description: "GitHub Integration — repos, issues, PRs",
|
|
1153
|
+
type: "builtin",
|
|
1154
|
+
config: { token: "", default_repo: "" },
|
|
1155
|
+
commands: ["/gh repos", "/gh issues", "/gh pr"],
|
|
1156
|
+
setup: ["token"]
|
|
1157
|
+
},
|
|
1158
|
+
browser: {
|
|
1159
|
+
name: "browser",
|
|
1160
|
+
description: "Playwright Browser — screenshots, rendering, scraping",
|
|
1161
|
+
type: "builtin",
|
|
1162
|
+
config: {},
|
|
1163
|
+
commands: ["/screenshot <url>", "/render <url>"],
|
|
1164
|
+
setup: []
|
|
1165
|
+
},
|
|
1166
|
+
slack: {
|
|
1167
|
+
name: "slack",
|
|
1168
|
+
description: "Slack Webhook — send messages to channels",
|
|
1169
|
+
type: "builtin",
|
|
1170
|
+
config: { webhook_url: "" },
|
|
1171
|
+
commands: ["/slack send <msg>"],
|
|
1172
|
+
setup: ["webhook_url"]
|
|
1173
|
+
},
|
|
1174
|
+
docker: {
|
|
1175
|
+
name: "docker",
|
|
1176
|
+
description: "Docker Management — containers, images, logs",
|
|
1177
|
+
type: "builtin",
|
|
1178
|
+
config: { host: "localhost" },
|
|
1179
|
+
commands: ["/docker ps", "/docker logs <container>"],
|
|
1180
|
+
setup: []
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
function cmdPlugin(args) {
|
|
1185
|
+
var plugins = loadPlugins();
|
|
1186
|
+
var parts = (args || "").split(/\s+/);
|
|
1187
|
+
var action = parts[0] || "list";
|
|
1188
|
+
|
|
1189
|
+
if (action === "list") {
|
|
1190
|
+
console.log("");
|
|
1191
|
+
console.log(C.bold + " " + BOX.bot + " Installed Plugins:" + C.reset);
|
|
1192
|
+
console.log(C.brightBlue + " " + BOX.h.repeat(40) + C.reset);
|
|
1193
|
+
var names = Object.keys(plugins);
|
|
1194
|
+
if (names.length === 0) {
|
|
1195
|
+
console.log(C.gray + " (none installed)" + C.reset);
|
|
1196
|
+
} else {
|
|
1197
|
+
names.forEach(function(name) {
|
|
1198
|
+
var p = plugins[name];
|
|
1199
|
+
var status = p.running ? C.green + BOX.dot + " running" : C.gray + BOX.dot + " stopped";
|
|
1200
|
+
console.log(" " + C.brightCyan + name + C.reset + " — " + (p.description || "") + " " + status + C.reset);
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
console.log("");
|
|
1204
|
+
console.log(C.gray + " Available: " + Object.keys(PLUGIN_TEMPLATES).join(", ") + C.reset);
|
|
1205
|
+
console.log(C.gray + " Or add custom: /plugin add <npm-package> or /plugin add <path>" + C.reset);
|
|
1206
|
+
console.log("");
|
|
1207
|
+
|
|
1208
|
+
} else if (action === "add" && parts[1]) {
|
|
1209
|
+
var pluginName = parts[1];
|
|
1210
|
+
if (PLUGIN_TEMPLATES[pluginName]) {
|
|
1211
|
+
// Built-in plugin
|
|
1212
|
+
var tmpl = PLUGIN_TEMPLATES[pluginName];
|
|
1213
|
+
plugins[pluginName] = {
|
|
1214
|
+
name: tmpl.name,
|
|
1215
|
+
description: tmpl.description,
|
|
1216
|
+
type: tmpl.type,
|
|
1217
|
+
config: Object.assign({}, tmpl.config),
|
|
1218
|
+
commands: tmpl.commands,
|
|
1219
|
+
installed: new Date().toISOString(),
|
|
1220
|
+
running: false
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
// If setup fields needed, prompt
|
|
1224
|
+
if (tmpl.setup && tmpl.setup.length > 0) {
|
|
1225
|
+
printInfo("Plugin '" + pluginName + "' needs configuration:");
|
|
1226
|
+
tmpl.setup.forEach(function(field) {
|
|
1227
|
+
printInfo(" Set with: /set plugin." + pluginName + "." + field + " <value>");
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
savePlugins(plugins);
|
|
1232
|
+
printSuccess("Plugin '" + pluginName + "' installed!");
|
|
1233
|
+
} else {
|
|
1234
|
+
// Custom: npm package or local path
|
|
1235
|
+
printInfo("Installing custom plugin: " + pluginName);
|
|
1236
|
+
try {
|
|
1237
|
+
execSync("npm install " + pluginName, { cwd: CONFIG_DIR, encoding: "utf8", stdio: "pipe" });
|
|
1238
|
+
plugins[pluginName] = {
|
|
1239
|
+
name: pluginName,
|
|
1240
|
+
description: "Custom MCP server",
|
|
1241
|
+
type: "npm",
|
|
1242
|
+
config: {},
|
|
1243
|
+
commands: [],
|
|
1244
|
+
installed: new Date().toISOString(),
|
|
1245
|
+
running: false
|
|
1246
|
+
};
|
|
1247
|
+
savePlugins(plugins);
|
|
1248
|
+
printSuccess("Plugin '" + pluginName + "' installed via npm!");
|
|
1249
|
+
} catch(e) {
|
|
1250
|
+
printError("Install failed: " + e.message.slice(0, 200));
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
} else if (action === "remove" && parts[1]) {
|
|
1255
|
+
delete plugins[parts[1]];
|
|
1256
|
+
savePlugins(plugins);
|
|
1257
|
+
printSuccess("Plugin '" + parts[1] + "' removed.");
|
|
1258
|
+
|
|
1259
|
+
} else if (action === "run" && parts[1]) {
|
|
1260
|
+
if (!plugins[parts[1]]) { printError("Plugin not found: " + parts[1]); return; }
|
|
1261
|
+
plugins[parts[1]].running = true;
|
|
1262
|
+
savePlugins(plugins);
|
|
1263
|
+
printSuccess("Plugin '" + parts[1] + "' started.");
|
|
1264
|
+
|
|
1265
|
+
} else if (action === "stop" && parts[1]) {
|
|
1266
|
+
if (!plugins[parts[1]]) { printError("Plugin not found: " + parts[1]); return; }
|
|
1267
|
+
plugins[parts[1]].running = false;
|
|
1268
|
+
savePlugins(plugins);
|
|
1269
|
+
printSuccess("Plugin '" + parts[1] + "' stopped.");
|
|
1270
|
+
|
|
1271
|
+
} else {
|
|
1272
|
+
printError("Usage: /plugin list|add|remove|run|stop <name>");
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function cmdPermissions(args) {
|
|
1277
|
+
var perms = loadPermissions();
|
|
1278
|
+
if (!args) {
|
|
1279
|
+
console.log("");
|
|
1280
|
+
console.log(C.bold + " Permissions:" + C.reset);
|
|
1281
|
+
console.log(" Mode: " + C.brightCyan + perms.mode + C.reset);
|
|
1282
|
+
console.log(" Allowed: " + (perms.allowed.length > 0 ? perms.allowed.join(", ") : C.gray + "(none)" + C.reset));
|
|
1283
|
+
console.log(" Denied: " + (perms.denied.length > 0 ? perms.denied.join(", ") : C.gray + "(none)" + C.reset));
|
|
1284
|
+
console.log("");
|
|
1285
|
+
console.log(C.gray + " /permissions allow-all — Skip all permission prompts" + C.reset);
|
|
1286
|
+
console.log(C.gray + " /permissions ask — Ask before each tool call" + C.reset);
|
|
1287
|
+
console.log("");
|
|
1288
|
+
} else if (args === "allow-all") {
|
|
1289
|
+
perms.mode = "allow-all";
|
|
1290
|
+
savePermissions(perms);
|
|
1291
|
+
printSuccess("Permission mode: allow-all (no prompts)");
|
|
1292
|
+
} else if (args === "ask") {
|
|
1293
|
+
perms.mode = "ask";
|
|
1294
|
+
savePermissions(perms);
|
|
1295
|
+
printSuccess("Permission mode: ask (prompt before each tool call)");
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// ── Agent Loop (CLI wrapper) ──
|
|
1300
|
+
async function cmdAgent(args) {
|
|
1301
|
+
if (!args) { printError("Usage: /agent <goal>"); return; }
|
|
1302
|
+
try {
|
|
1303
|
+
printUserMessage(args);
|
|
1304
|
+
process.stdout.write(C.dim + " " + BOX.bot + " working autonomously..." + C.reset);
|
|
1305
|
+
var resp = await apiCall("POST", "/agent", { goal: args, verbose: true });
|
|
1306
|
+
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
1307
|
+
if (resp.status !== 200) { printError(resp.data.error || "Error"); return; }
|
|
1308
|
+
var d = resp.data;
|
|
1309
|
+
var meta = d.steps_executed + " steps" + (d.files.length > 0 ? " " + BOX.dot + " " + d.files.length + " files" : "");
|
|
1310
|
+
printAnswer(d.answer, meta);
|
|
1311
|
+
if (d.files.length > 0) {
|
|
1312
|
+
console.log(C.green + " Files:" + C.reset);
|
|
1313
|
+
d.files.forEach(function(f) { console.log(" " + C.brightCyan + f.name + C.reset + " → " + config.api.base_url + f.download); });
|
|
1314
|
+
console.log("");
|
|
1315
|
+
}
|
|
1316
|
+
} catch(e) { printError(e.message); }
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// ── Screenshot/Render (CLI wrapper) ──
|
|
1320
|
+
async function cmdScreenshot(args) {
|
|
1321
|
+
if (!args) { printError("Usage: /screenshot <url>"); return; }
|
|
1322
|
+
try {
|
|
1323
|
+
printInfo("Taking screenshot of " + args + "...");
|
|
1324
|
+
var resp = await apiCall("POST", "/screenshot", { url: args });
|
|
1325
|
+
if (resp.status !== 200) { printError(resp.data.error || "Error"); return; }
|
|
1326
|
+
printSuccess("Screenshot: " + resp.data.title);
|
|
1327
|
+
console.log(" Download: " + C.brightCyan + config.api.base_url + resp.data.screenshot + C.reset);
|
|
1328
|
+
console.log("");
|
|
1329
|
+
} catch(e) { printError(e.message); }
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
async function cmdRender(args) {
|
|
1333
|
+
if (!args) { printError("Usage: /render <url>"); return; }
|
|
1334
|
+
try {
|
|
1335
|
+
printInfo("Rendering " + args + " with Playwright...");
|
|
1336
|
+
var resp = await apiCall("POST", "/render", { url: args });
|
|
1337
|
+
if (resp.status !== 200) { printError(resp.data.error || "Error"); return; }
|
|
1338
|
+
printSuccess("Rendered: " + resp.data.title + " (" + resp.data.html_length + " chars)");
|
|
1339
|
+
if (resp.data.screenshot) console.log(" Screenshot: " + C.brightCyan + resp.data.screenshot + C.reset);
|
|
1340
|
+
console.log("");
|
|
1341
|
+
} catch(e) { printError(e.message); }
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1045
1344
|
// ── Main Loop ──
|
|
1046
1345
|
async function main() {
|
|
1047
1346
|
// Handle CLI args
|
|
@@ -1081,6 +1380,7 @@ async function main() {
|
|
|
1081
1380
|
|
|
1082
1381
|
// Interactive mode
|
|
1083
1382
|
printHeader();
|
|
1383
|
+
checkForUpdates();
|
|
1084
1384
|
|
|
1085
1385
|
// Health check
|
|
1086
1386
|
try {
|
|
@@ -1092,10 +1392,25 @@ async function main() {
|
|
|
1092
1392
|
printInfo("Run: blun setup — to configure");
|
|
1093
1393
|
}
|
|
1094
1394
|
|
|
1395
|
+
var ALL_COMMANDS = [
|
|
1396
|
+
"/help", "/clear", "/skills", "/skill", "/search", "/learn", "/learn-url",
|
|
1397
|
+
"/generate", "/analyze", "/score", "/eval", "/settings", "/set",
|
|
1398
|
+
"/status", "/versions", "/health", "/watchdog", "/memory", "/files",
|
|
1399
|
+
"/sh", "/git", "/ssh", "/deploy", "/read", "/write", "/init",
|
|
1400
|
+
"/screenshot", "/render", "/agent", "/plugin", "/permissions", "/exit"
|
|
1401
|
+
];
|
|
1402
|
+
|
|
1095
1403
|
var rl = readline.createInterface({
|
|
1096
1404
|
input: process.stdin,
|
|
1097
1405
|
output: process.stdout,
|
|
1098
|
-
prompt: C.
|
|
1406
|
+
prompt: "\n" + C.brightBlue + C.bold + " " + BOX.arrow + " " + C.reset,
|
|
1407
|
+
completer: function(line) {
|
|
1408
|
+
if (line.startsWith("/")) {
|
|
1409
|
+
var hits = ALL_COMMANDS.filter(function(c) { return c.startsWith(line); });
|
|
1410
|
+
return [hits.length ? hits : ALL_COMMANDS, line];
|
|
1411
|
+
}
|
|
1412
|
+
return [[], line];
|
|
1413
|
+
}
|
|
1099
1414
|
});
|
|
1100
1415
|
|
|
1101
1416
|
rl.prompt();
|