blun-king-cli 1.0.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 +812 -0
- package/package.json +10 -0
- package/setup.js +30 -0
package/bin/blun.js
ADDED
|
@@ -0,0 +1,812 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// BLUN King CLI — Interactive Console
|
|
3
|
+
const readline = require("readline");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { execSync, spawn } = require("child_process");
|
|
7
|
+
const https = require("https");
|
|
8
|
+
const http = require("http");
|
|
9
|
+
|
|
10
|
+
// ── Config ──
|
|
11
|
+
var _rawHome = process.env.BLUN_HOME || process.env.REAL_HOME || require("os").homedir();
|
|
12
|
+
// Strip .claude-telegram-profile suffix if present (Claude Code Telegram session override)
|
|
13
|
+
const HOME = _rawHome.replace(/[\/\\]\.claude-telegram-profile$/, "");
|
|
14
|
+
const CONFIG_DIR = path.join(HOME, ".blun");
|
|
15
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
16
|
+
const MEMORY_DIR = path.join(CONFIG_DIR, "memory");
|
|
17
|
+
const SKILLS_DIR = path.join(CONFIG_DIR, "skills");
|
|
18
|
+
const HISTORY_FILE = path.join(CONFIG_DIR, "history.json");
|
|
19
|
+
const LOG_FILE = path.join(CONFIG_DIR, "cli.log");
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
22
|
+
if (!fs.existsSync(MEMORY_DIR)) fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
23
|
+
if (!fs.existsSync(SKILLS_DIR)) fs.mkdirSync(SKILLS_DIR, { recursive: true });
|
|
24
|
+
|
|
25
|
+
// ── Default Config ──
|
|
26
|
+
function loadConfig() {
|
|
27
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
28
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf8")); } catch(e) {}
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
auth: { type: "api_key", api_key: "", oauth_token: "", oauth_expires: null },
|
|
32
|
+
api: { base_url: "http://176.9.158.30:3200", timeout: 60000 },
|
|
33
|
+
telegram: { enabled: false, bot_token: "", chat_id: "" },
|
|
34
|
+
model: "blun-king-v100",
|
|
35
|
+
workdir: process.cwd(),
|
|
36
|
+
theme: "dark",
|
|
37
|
+
verbose: false,
|
|
38
|
+
watchdog: { enabled: false, interval: 600 }
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function saveConfig(cfg) {
|
|
43
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
var config = loadConfig();
|
|
47
|
+
|
|
48
|
+
// ── Colors ──
|
|
49
|
+
const C = {
|
|
50
|
+
reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
|
|
51
|
+
red: "\x1b[31m", green: "\x1b[32m", yellow: "\x1b[33m",
|
|
52
|
+
blue: "\x1b[34m", magenta: "\x1b[35m", cyan: "\x1b[36m", white: "\x1b[37m",
|
|
53
|
+
bg_blue: "\x1b[44m", bg_green: "\x1b[42m"
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function log(msg) { if (config.verbose) fs.appendFileSync(LOG_FILE, new Date().toISOString() + " " + msg + "\n"); }
|
|
57
|
+
|
|
58
|
+
// ── API Call ──
|
|
59
|
+
function apiCall(method, endpoint, body) {
|
|
60
|
+
return new Promise(function(resolve, reject) {
|
|
61
|
+
var url = new URL(config.api.base_url + endpoint);
|
|
62
|
+
var isHttps = url.protocol === "https:";
|
|
63
|
+
var options = {
|
|
64
|
+
hostname: url.hostname, port: url.port, path: url.pathname,
|
|
65
|
+
method: method,
|
|
66
|
+
headers: { "Content-Type": "application/json" },
|
|
67
|
+
timeout: config.api.timeout
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (config.auth.type === "api_key" && config.auth.api_key) {
|
|
71
|
+
options.headers["Authorization"] = "Bearer " + config.auth.api_key;
|
|
72
|
+
} else if (config.auth.type === "oauth" && config.auth.oauth_token) {
|
|
73
|
+
options.headers["Authorization"] = "Bearer " + config.auth.oauth_token;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var data = body ? JSON.stringify(body) : null;
|
|
77
|
+
if (data) options.headers["Content-Length"] = Buffer.byteLength(data);
|
|
78
|
+
|
|
79
|
+
var mod = isHttps ? https : http;
|
|
80
|
+
var req = mod.request(options, function(res) {
|
|
81
|
+
var chunks = [];
|
|
82
|
+
res.on("data", function(c) { chunks.push(c); });
|
|
83
|
+
res.on("end", function() {
|
|
84
|
+
var raw = Buffer.concat(chunks).toString();
|
|
85
|
+
try { resolve({ status: res.statusCode, data: JSON.parse(raw) }); }
|
|
86
|
+
catch(e) { resolve({ status: res.statusCode, data: raw }); }
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
req.on("error", function(e) { reject(e); });
|
|
90
|
+
req.on("timeout", function() { req.destroy(); reject(new Error("Timeout")); });
|
|
91
|
+
if (data) req.write(data);
|
|
92
|
+
req.end();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── Chat History ──
|
|
97
|
+
var chatHistory = [];
|
|
98
|
+
|
|
99
|
+
// ── Print Helpers ──
|
|
100
|
+
function printHeader() {
|
|
101
|
+
console.log("");
|
|
102
|
+
console.log(C.bold + C.blue + " ╔══════════════════════════════════════╗" + C.reset);
|
|
103
|
+
console.log(C.bold + C.blue + " ║ BLUN KING CLI v1.0 ║" + C.reset);
|
|
104
|
+
console.log(C.bold + C.blue + " ║ Premium KI — Local First ║" + C.reset);
|
|
105
|
+
console.log(C.bold + C.blue + " ╚══════════════════════════════════════╝" + C.reset);
|
|
106
|
+
console.log("");
|
|
107
|
+
console.log(C.dim + " API: " + config.api.base_url + C.reset);
|
|
108
|
+
console.log(C.dim + " Auth: " + config.auth.type + (config.auth.api_key ? " (key set)" : " (no key)") + C.reset);
|
|
109
|
+
console.log(C.dim + " Model: " + config.model + C.reset);
|
|
110
|
+
console.log(C.dim + " Workdir: " + config.workdir + C.reset);
|
|
111
|
+
console.log("");
|
|
112
|
+
console.log(C.dim + " Type /help for commands, or just chat." + C.reset);
|
|
113
|
+
console.log("");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function printAnswer(answer) {
|
|
117
|
+
console.log("");
|
|
118
|
+
console.log(C.green + C.bold + "BLUN King:" + C.reset);
|
|
119
|
+
// Simple markdown rendering
|
|
120
|
+
var lines = answer.split("\n");
|
|
121
|
+
var inCode = false;
|
|
122
|
+
for (var i = 0; i < lines.length; i++) {
|
|
123
|
+
var line = lines[i];
|
|
124
|
+
if (line.startsWith("```")) {
|
|
125
|
+
inCode = !inCode;
|
|
126
|
+
console.log(C.dim + line + C.reset);
|
|
127
|
+
} else if (inCode) {
|
|
128
|
+
console.log(C.cyan + line + C.reset);
|
|
129
|
+
} else if (line.startsWith("# ")) {
|
|
130
|
+
console.log(C.bold + C.yellow + line + C.reset);
|
|
131
|
+
} else if (line.startsWith("## ")) {
|
|
132
|
+
console.log(C.bold + C.magenta + line + C.reset);
|
|
133
|
+
} else if (line.startsWith("### ")) {
|
|
134
|
+
console.log(C.bold + line + C.reset);
|
|
135
|
+
} else if (line.startsWith("- ") || line.startsWith("* ")) {
|
|
136
|
+
console.log(C.white + " " + line + C.reset);
|
|
137
|
+
} else if (line.match(/^\d+\./)) {
|
|
138
|
+
console.log(C.white + " " + line + C.reset);
|
|
139
|
+
} else {
|
|
140
|
+
console.log(line);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
console.log("");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function printError(msg) { console.log(C.red + "ERROR: " + msg + C.reset); }
|
|
147
|
+
function printInfo(msg) { console.log(C.cyan + msg + C.reset); }
|
|
148
|
+
function printSuccess(msg) { console.log(C.green + "✓ " + msg + C.reset); }
|
|
149
|
+
|
|
150
|
+
// ── Commands ──
|
|
151
|
+
async function handleCommand(input) {
|
|
152
|
+
var parts = input.trim().split(/\s+/);
|
|
153
|
+
var cmd = parts[0].toLowerCase();
|
|
154
|
+
var args = parts.slice(1).join(" ");
|
|
155
|
+
|
|
156
|
+
switch(cmd) {
|
|
157
|
+
case "/help":
|
|
158
|
+
console.log("");
|
|
159
|
+
console.log(C.bold + "BLUN King CLI — Commands:" + C.reset);
|
|
160
|
+
console.log("");
|
|
161
|
+
console.log(C.yellow + " CHAT" + C.reset);
|
|
162
|
+
console.log(" (just type) Chat with BLUN King");
|
|
163
|
+
console.log(" /clear Clear chat history");
|
|
164
|
+
console.log("");
|
|
165
|
+
console.log(C.yellow + " SKILLS" + C.reset);
|
|
166
|
+
console.log(" /skills List all skills");
|
|
167
|
+
console.log(" /skill install Install a skill from server");
|
|
168
|
+
console.log(" /skill info <n> Show skill details");
|
|
169
|
+
console.log("");
|
|
170
|
+
console.log(C.yellow + " TOOLS" + C.reset);
|
|
171
|
+
console.log(" /search <query> Web search");
|
|
172
|
+
console.log(" /learn <text> Teach BLUN King");
|
|
173
|
+
console.log(" /learn-url <url> Learn from URL");
|
|
174
|
+
console.log(" /generate <desc> Generate a file");
|
|
175
|
+
console.log(" /analyze <url> Analyze a website");
|
|
176
|
+
console.log(" /score <text> Score a response");
|
|
177
|
+
console.log(" /eval Run eval suite");
|
|
178
|
+
console.log("");
|
|
179
|
+
console.log(C.yellow + " SETTINGS" + C.reset);
|
|
180
|
+
console.log(" /settings Show all settings");
|
|
181
|
+
console.log(" /set auth api Switch to API key auth");
|
|
182
|
+
console.log(" /set auth oauth Switch to OAuth");
|
|
183
|
+
console.log(" /set key <key> Set API key");
|
|
184
|
+
console.log(" /set url <url> Set API base URL");
|
|
185
|
+
console.log(" /set telegram Configure Telegram bridge");
|
|
186
|
+
console.log(" /set verbose Toggle verbose logging");
|
|
187
|
+
console.log("");
|
|
188
|
+
console.log(C.yellow + " SYSTEM" + C.reset);
|
|
189
|
+
console.log(" /status Runtime status");
|
|
190
|
+
console.log(" /versions Prompt versions");
|
|
191
|
+
console.log(" /health Quick health check");
|
|
192
|
+
console.log(" /watchdog Watchdog status");
|
|
193
|
+
console.log(" /watchdog on/off Enable/disable watchdog");
|
|
194
|
+
console.log(" /memory Show local memory");
|
|
195
|
+
console.log(" /memory save <k> Save to memory");
|
|
196
|
+
console.log(" /memory del <k> Delete from memory");
|
|
197
|
+
console.log(" /files List workdir files");
|
|
198
|
+
console.log(" /exit Exit CLI");
|
|
199
|
+
console.log("");
|
|
200
|
+
break;
|
|
201
|
+
|
|
202
|
+
case "/clear":
|
|
203
|
+
chatHistory = [];
|
|
204
|
+
printSuccess("Chat history cleared.");
|
|
205
|
+
break;
|
|
206
|
+
|
|
207
|
+
case "/skills":
|
|
208
|
+
await cmdSkills();
|
|
209
|
+
break;
|
|
210
|
+
|
|
211
|
+
case "/skill":
|
|
212
|
+
await cmdSkillAction(args);
|
|
213
|
+
break;
|
|
214
|
+
|
|
215
|
+
case "/search":
|
|
216
|
+
await cmdSearch(args);
|
|
217
|
+
break;
|
|
218
|
+
|
|
219
|
+
case "/learn":
|
|
220
|
+
await cmdLearn(args, false);
|
|
221
|
+
break;
|
|
222
|
+
|
|
223
|
+
case "/learn-url":
|
|
224
|
+
await cmdLearn(args, true);
|
|
225
|
+
break;
|
|
226
|
+
|
|
227
|
+
case "/generate":
|
|
228
|
+
await cmdGenerate(args);
|
|
229
|
+
break;
|
|
230
|
+
|
|
231
|
+
case "/analyze":
|
|
232
|
+
await cmdAnalyze(args);
|
|
233
|
+
break;
|
|
234
|
+
|
|
235
|
+
case "/score":
|
|
236
|
+
await cmdScore(args);
|
|
237
|
+
break;
|
|
238
|
+
|
|
239
|
+
case "/eval":
|
|
240
|
+
await cmdEval();
|
|
241
|
+
break;
|
|
242
|
+
|
|
243
|
+
case "/settings":
|
|
244
|
+
cmdSettings();
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case "/set":
|
|
248
|
+
cmdSet(args);
|
|
249
|
+
break;
|
|
250
|
+
|
|
251
|
+
case "/status":
|
|
252
|
+
await cmdStatus();
|
|
253
|
+
break;
|
|
254
|
+
|
|
255
|
+
case "/versions":
|
|
256
|
+
await cmdVersions();
|
|
257
|
+
break;
|
|
258
|
+
|
|
259
|
+
case "/health":
|
|
260
|
+
await cmdHealth();
|
|
261
|
+
break;
|
|
262
|
+
|
|
263
|
+
case "/watchdog":
|
|
264
|
+
cmdWatchdog(args);
|
|
265
|
+
break;
|
|
266
|
+
|
|
267
|
+
case "/memory":
|
|
268
|
+
cmdMemory(args);
|
|
269
|
+
break;
|
|
270
|
+
|
|
271
|
+
case "/files":
|
|
272
|
+
cmdFiles();
|
|
273
|
+
break;
|
|
274
|
+
|
|
275
|
+
case "/exit":
|
|
276
|
+
case "/quit":
|
|
277
|
+
case "/q":
|
|
278
|
+
console.log(C.dim + "Bye." + C.reset);
|
|
279
|
+
process.exit(0);
|
|
280
|
+
|
|
281
|
+
default:
|
|
282
|
+
printError("Unknown command: " + cmd + ". Type /help for commands.");
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ── Chat ──
|
|
287
|
+
async function sendChat(message) {
|
|
288
|
+
try {
|
|
289
|
+
process.stdout.write(C.dim + "⏳ thinking..." + C.reset);
|
|
290
|
+
var resp = await apiCall("POST", "/chat", {
|
|
291
|
+
message: message,
|
|
292
|
+
history: chatHistory.slice(-10)
|
|
293
|
+
});
|
|
294
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
295
|
+
|
|
296
|
+
if (resp.status !== 200) {
|
|
297
|
+
printError(resp.data.error || "API Error " + resp.status);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
chatHistory.push({ role: "user", content: message });
|
|
302
|
+
chatHistory.push({ role: "assistant", content: resp.data.answer });
|
|
303
|
+
|
|
304
|
+
printAnswer(resp.data.answer);
|
|
305
|
+
|
|
306
|
+
// Status line
|
|
307
|
+
var q = resp.data.quality || {};
|
|
308
|
+
console.log(C.dim + " [" + resp.data.task_type + "/" + resp.data.role + " | score: " + (q.score || "?") +
|
|
309
|
+
(q.retried ? " | retried" : "") +
|
|
310
|
+
(q.flags && q.flags.length > 0 ? " | flags: " + q.flags.join(", ") : "") + "]" + C.reset);
|
|
311
|
+
console.log("");
|
|
312
|
+
|
|
313
|
+
} catch(e) {
|
|
314
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
315
|
+
printError("Connection failed: " + e.message);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ── Command Implementations ──
|
|
320
|
+
async function cmdSkills() {
|
|
321
|
+
try {
|
|
322
|
+
var resp = await apiCall("POST", "/classify", { message: "test" });
|
|
323
|
+
// List skills from known role files
|
|
324
|
+
var skills = [
|
|
325
|
+
"api", "artifact", "artifact_reviewer", "automation", "business", "creative",
|
|
326
|
+
"critic", "data", "debug", "decomposer", "design", "dev", "devops", "docs",
|
|
327
|
+
"eval_designer", "learn_curator", "librarian", "marketing", "math", "mobile",
|
|
328
|
+
"operator", "prompt_architect", "research", "sales", "security", "seo",
|
|
329
|
+
"support", "text", "tool_brain", "translator", "web_ui", "website_builder"
|
|
330
|
+
];
|
|
331
|
+
console.log("");
|
|
332
|
+
console.log(C.bold + "BLUN King Skills (" + skills.length + "):" + C.reset);
|
|
333
|
+
console.log("");
|
|
334
|
+
for (var i = 0; i < skills.length; i++) {
|
|
335
|
+
var installed = fs.existsSync(path.join(SKILLS_DIR, skills[i] + ".txt"));
|
|
336
|
+
console.log(" " + (installed ? C.green + "●" : C.dim + "○") + C.reset +
|
|
337
|
+
" " + skills[i] + (installed ? C.dim + " (local)" + C.reset : ""));
|
|
338
|
+
}
|
|
339
|
+
console.log("");
|
|
340
|
+
console.log(C.dim + " /skill install <name> — Install skill locally" + C.reset);
|
|
341
|
+
console.log(C.dim + " /skill info <name> — Show skill details" + C.reset);
|
|
342
|
+
console.log("");
|
|
343
|
+
} catch(e) {
|
|
344
|
+
printError(e.message);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function cmdSkillAction(args) {
|
|
349
|
+
var parts = args.split(/\s+/);
|
|
350
|
+
var action = parts[0];
|
|
351
|
+
var name = parts[1];
|
|
352
|
+
|
|
353
|
+
if (action === "install" && name) {
|
|
354
|
+
// Fetch from server
|
|
355
|
+
try {
|
|
356
|
+
var resp = await apiCall("POST", "/chat", {
|
|
357
|
+
message: "Zeige mir den kompletten Inhalt der Rolle " + name + " — nur den Prompt-Text, keine Erklaerung."
|
|
358
|
+
});
|
|
359
|
+
if (resp.status === 200 && resp.data.answer) {
|
|
360
|
+
fs.writeFileSync(path.join(SKILLS_DIR, name + ".txt"), resp.data.answer);
|
|
361
|
+
printSuccess("Skill '" + name + "' installed to " + SKILLS_DIR);
|
|
362
|
+
}
|
|
363
|
+
} catch(e) { printError(e.message); }
|
|
364
|
+
} else if (action === "info" && name) {
|
|
365
|
+
var file = path.join(SKILLS_DIR, name + ".txt");
|
|
366
|
+
if (fs.existsSync(file)) {
|
|
367
|
+
console.log("");
|
|
368
|
+
console.log(C.bold + "Skill: " + name + C.reset);
|
|
369
|
+
console.log(C.dim + "─".repeat(40) + C.reset);
|
|
370
|
+
console.log(fs.readFileSync(file, "utf8"));
|
|
371
|
+
} else {
|
|
372
|
+
printInfo("Skill not installed locally. Use /skill install " + name);
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
printError("Usage: /skill install <name> or /skill info <name>");
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async function cmdSearch(query) {
|
|
380
|
+
if (!query) { printError("Usage: /search <query>"); return; }
|
|
381
|
+
try {
|
|
382
|
+
process.stdout.write(C.dim + "🔍 searching..." + C.reset);
|
|
383
|
+
var resp = await apiCall("POST", "/web-search", { query: query, summarize: true });
|
|
384
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
385
|
+
if (resp.status === 200) {
|
|
386
|
+
console.log("");
|
|
387
|
+
console.log(C.bold + "Search: " + query + C.reset);
|
|
388
|
+
console.log("");
|
|
389
|
+
var results = resp.data.results || [];
|
|
390
|
+
for (var i = 0; i < results.length; i++) {
|
|
391
|
+
console.log(C.yellow + " " + (i+1) + ". " + results[i].title + C.reset);
|
|
392
|
+
if (results[i].snippet) console.log(C.dim + " " + results[i].snippet.slice(0, 100) + C.reset);
|
|
393
|
+
if (results[i].url) console.log(C.blue + " " + results[i].url + C.reset);
|
|
394
|
+
}
|
|
395
|
+
if (resp.data.summary) {
|
|
396
|
+
console.log("");
|
|
397
|
+
console.log(C.green + C.bold + "Summary:" + C.reset);
|
|
398
|
+
console.log(resp.data.summary);
|
|
399
|
+
}
|
|
400
|
+
console.log("");
|
|
401
|
+
} else { printError(resp.data.error || "Search failed"); }
|
|
402
|
+
} catch(e) {
|
|
403
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
404
|
+
printError(e.message);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async function cmdLearn(input, isUrl) {
|
|
409
|
+
if (!input) { printError("Usage: /learn <text> or /learn-url <url>"); return; }
|
|
410
|
+
try {
|
|
411
|
+
process.stdout.write(C.dim + "📚 learning..." + C.reset);
|
|
412
|
+
var body = isUrl ? { url: input } : { content: input };
|
|
413
|
+
var resp = await apiCall("POST", "/learn", body);
|
|
414
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
415
|
+
if (resp.status === 200) {
|
|
416
|
+
printSuccess("Learned! Category: " + resp.data.category + " | File: " + resp.data.file);
|
|
417
|
+
} else { printError(resp.data.error || "Learn failed"); }
|
|
418
|
+
} catch(e) {
|
|
419
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
420
|
+
printError(e.message);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async function cmdGenerate(desc) {
|
|
425
|
+
if (!desc) { printError("Usage: /generate <description>"); return; }
|
|
426
|
+
// Parse format from description
|
|
427
|
+
var format = "txt";
|
|
428
|
+
var fmatch = desc.match(/\.(html|css|js|json|md|py|txt|yaml|xml|sql|csv)\b/i);
|
|
429
|
+
if (fmatch) format = fmatch[1].toLowerCase();
|
|
430
|
+
else if (/html|webseite|landing/i.test(desc)) format = "html";
|
|
431
|
+
else if (/json|config/i.test(desc)) format = "json";
|
|
432
|
+
else if (/javascript|node|function/i.test(desc)) format = "js";
|
|
433
|
+
else if (/python/i.test(desc)) format = "py";
|
|
434
|
+
else if (/css|style/i.test(desc)) format = "css";
|
|
435
|
+
else if (/markdown/i.test(desc)) format = "md";
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
process.stdout.write(C.dim + "⚙️ generating " + format + "..." + C.reset);
|
|
439
|
+
var resp = await apiCall("POST", "/generate-file", { prompt: desc, format: format });
|
|
440
|
+
process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
441
|
+
if (resp.status === 200) {
|
|
442
|
+
// Save locally too
|
|
443
|
+
var localPath = path.join(config.workdir, resp.data.filename);
|
|
444
|
+
fs.writeFileSync(localPath, resp.data.content);
|
|
445
|
+
printSuccess("Generated: " + resp.data.filename + " (" + resp.data.size + " bytes)");
|
|
446
|
+
printInfo("Local: " + localPath);
|
|
447
|
+
printInfo("Remote: " + config.api.base_url + resp.data.download);
|
|
448
|
+
} else { printError(resp.data.error || "Generate failed"); }
|
|
449
|
+
} catch(e) {
|
|
450
|
+
process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
451
|
+
printError(e.message);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
async function cmdAnalyze(url) {
|
|
456
|
+
if (!url) { printError("Usage: /analyze <url>"); return; }
|
|
457
|
+
try {
|
|
458
|
+
process.stdout.write(C.dim + "🔎 analyzing..." + C.reset);
|
|
459
|
+
var resp = await apiCall("POST", "/analyze", { url: url, type: "website" });
|
|
460
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
461
|
+
if (resp.status === 200) {
|
|
462
|
+
var meta = resp.data.meta || {};
|
|
463
|
+
console.log("");
|
|
464
|
+
console.log(C.bold + "Analysis: " + url + C.reset);
|
|
465
|
+
console.log(C.dim + "─".repeat(50) + C.reset);
|
|
466
|
+
if (meta.title) console.log(" Title: " + meta.title);
|
|
467
|
+
if (meta.h1s) console.log(" H1s: " + meta.h1s.join(", "));
|
|
468
|
+
console.log(" Links: " + meta.links + " | Images: " + meta.images);
|
|
469
|
+
console.log(" Responsive: " + (meta.responsive ? "yes" : "no"));
|
|
470
|
+
if (meta.frameworks && meta.frameworks.length) console.log(" Frameworks: " + meta.frameworks.join(", "));
|
|
471
|
+
console.log("");
|
|
472
|
+
printAnswer(resp.data.analysis);
|
|
473
|
+
} else { printError(resp.data.error || "Analyze failed"); }
|
|
474
|
+
} catch(e) {
|
|
475
|
+
process.stdout.write("\r" + " ".repeat(30) + "\r");
|
|
476
|
+
printError(e.message);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async function cmdScore(text) {
|
|
481
|
+
if (!text) { printError("Usage: /score <text>"); return; }
|
|
482
|
+
try {
|
|
483
|
+
var resp = await apiCall("POST", "/score", { answer: text, task_type: "chat" });
|
|
484
|
+
if (resp.status === 200) {
|
|
485
|
+
var s = resp.data.score;
|
|
486
|
+
var color = s >= 80 ? C.green : s >= 50 ? C.yellow : C.red;
|
|
487
|
+
console.log("");
|
|
488
|
+
console.log(C.bold + "Score: " + color + s + "/100" + C.reset);
|
|
489
|
+
if (resp.data.reasons && resp.data.reasons.length > 0) {
|
|
490
|
+
console.log(C.dim + "Flags: " + resp.data.reasons.join(", ") + C.reset);
|
|
491
|
+
}
|
|
492
|
+
console.log("");
|
|
493
|
+
} else { printError(resp.data.error || "Score failed"); }
|
|
494
|
+
} catch(e) { printError(e.message); }
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
async function cmdEval() {
|
|
498
|
+
printInfo("Running eval suite... this takes a few minutes.");
|
|
499
|
+
try {
|
|
500
|
+
var resp = await apiCall("GET", "/eval");
|
|
501
|
+
if (resp.status === 200) {
|
|
502
|
+
var d = resp.data;
|
|
503
|
+
console.log("");
|
|
504
|
+
console.log(C.bold + "EVAL RESULTS" + C.reset);
|
|
505
|
+
console.log(C.dim + "─".repeat(50) + C.reset);
|
|
506
|
+
if (d.results) {
|
|
507
|
+
for (var i = 0; i < d.results.length; i++) {
|
|
508
|
+
var r = d.results[i];
|
|
509
|
+
var icon = r.passed ? C.green + "PASS" : C.red + "FAIL";
|
|
510
|
+
console.log(" " + icon + C.reset + " " + r.name +
|
|
511
|
+
(r.failures && r.failures.length > 0 ? C.dim + " — " + r.failures.join(", ") + C.reset : ""));
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
console.log(C.dim + "─".repeat(50) + C.reset);
|
|
515
|
+
var scoreColor = d.score >= 83 ? C.green : C.red;
|
|
516
|
+
console.log(" " + C.bold + "Score: " + scoreColor + d.score + "% (" + d.passed + "/" + d.total + ")" + C.reset);
|
|
517
|
+
console.log("");
|
|
518
|
+
} else { printError(resp.data.error || "Eval failed"); }
|
|
519
|
+
} catch(e) { printError(e.message); }
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function cmdSettings() {
|
|
523
|
+
console.log("");
|
|
524
|
+
console.log(C.bold + "BLUN King Settings:" + C.reset);
|
|
525
|
+
console.log(C.dim + "─".repeat(40) + C.reset);
|
|
526
|
+
console.log(" Auth Type: " + config.auth.type);
|
|
527
|
+
console.log(" API Key: " + (config.auth.api_key ? config.auth.api_key.slice(0, 10) + "..." : "(not set)"));
|
|
528
|
+
console.log(" OAuth Token: " + (config.auth.oauth_token ? "set (expires: " + config.auth.oauth_expires + ")" : "(not set)"));
|
|
529
|
+
console.log(" API URL: " + config.api.base_url);
|
|
530
|
+
console.log(" Model: " + config.model);
|
|
531
|
+
console.log(" Workdir: " + config.workdir);
|
|
532
|
+
console.log(" Verbose: " + config.verbose);
|
|
533
|
+
console.log(" Telegram: " + (config.telegram.enabled ? "on (chat: " + config.telegram.chat_id + ")" : "off"));
|
|
534
|
+
console.log(" Watchdog: " + (config.watchdog.enabled ? "on (" + config.watchdog.interval + "s)" : "off"));
|
|
535
|
+
console.log("");
|
|
536
|
+
console.log(C.dim + " Config file: " + CONFIG_FILE + C.reset);
|
|
537
|
+
console.log("");
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function cmdSet(args) {
|
|
541
|
+
var parts = args.split(/\s+/);
|
|
542
|
+
var key = parts[0];
|
|
543
|
+
var val = parts.slice(1).join(" ");
|
|
544
|
+
|
|
545
|
+
switch(key) {
|
|
546
|
+
case "auth":
|
|
547
|
+
if (val === "api" || val === "api_key") {
|
|
548
|
+
config.auth.type = "api_key";
|
|
549
|
+
saveConfig(config);
|
|
550
|
+
printSuccess("Auth switched to API key.");
|
|
551
|
+
} else if (val === "oauth") {
|
|
552
|
+
config.auth.type = "oauth";
|
|
553
|
+
printInfo("Enter OAuth token:");
|
|
554
|
+
// Will be set interactively
|
|
555
|
+
var rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
556
|
+
rl2.question(" OAuth Token: ", function(token) {
|
|
557
|
+
config.auth.oauth_token = token.trim();
|
|
558
|
+
rl2.question(" Expires (ISO date or 'never'): ", function(exp) {
|
|
559
|
+
config.auth.oauth_expires = exp.trim() === "never" ? null : exp.trim();
|
|
560
|
+
saveConfig(config);
|
|
561
|
+
printSuccess("OAuth configured.");
|
|
562
|
+
rl2.close();
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
return;
|
|
566
|
+
} else {
|
|
567
|
+
printError("Usage: /set auth api or /set auth oauth");
|
|
568
|
+
}
|
|
569
|
+
break;
|
|
570
|
+
|
|
571
|
+
case "key":
|
|
572
|
+
config.auth.api_key = val;
|
|
573
|
+
config.auth.type = "api_key";
|
|
574
|
+
saveConfig(config);
|
|
575
|
+
printSuccess("API key set: " + val.slice(0, 10) + "...");
|
|
576
|
+
break;
|
|
577
|
+
|
|
578
|
+
case "url":
|
|
579
|
+
config.api.base_url = val;
|
|
580
|
+
saveConfig(config);
|
|
581
|
+
printSuccess("API URL set: " + val);
|
|
582
|
+
break;
|
|
583
|
+
|
|
584
|
+
case "telegram":
|
|
585
|
+
printInfo("Telegram Bridge Setup:");
|
|
586
|
+
var rl3 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
587
|
+
rl3.question(" Bot Token: ", function(token) {
|
|
588
|
+
config.telegram.bot_token = token.trim();
|
|
589
|
+
rl3.question(" Chat ID: ", function(chatId) {
|
|
590
|
+
config.telegram.chat_id = chatId.trim();
|
|
591
|
+
config.telegram.enabled = true;
|
|
592
|
+
saveConfig(config);
|
|
593
|
+
printSuccess("Telegram bridge configured.");
|
|
594
|
+
rl3.close();
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
return;
|
|
598
|
+
|
|
599
|
+
case "verbose":
|
|
600
|
+
config.verbose = !config.verbose;
|
|
601
|
+
saveConfig(config);
|
|
602
|
+
printSuccess("Verbose logging: " + (config.verbose ? "ON" : "OFF"));
|
|
603
|
+
break;
|
|
604
|
+
|
|
605
|
+
case "workdir":
|
|
606
|
+
if (fs.existsSync(val)) {
|
|
607
|
+
config.workdir = path.resolve(val);
|
|
608
|
+
saveConfig(config);
|
|
609
|
+
printSuccess("Workdir: " + config.workdir);
|
|
610
|
+
} else { printError("Directory not found: " + val); }
|
|
611
|
+
break;
|
|
612
|
+
|
|
613
|
+
default:
|
|
614
|
+
printError("Unknown setting. Use /settings to see all options.");
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
async function cmdStatus() {
|
|
619
|
+
try {
|
|
620
|
+
process.stdout.write(C.dim + "checking..." + C.reset);
|
|
621
|
+
var resp = await apiCall("GET", "/runtime/status");
|
|
622
|
+
process.stdout.write("\r" + " ".repeat(20) + "\r");
|
|
623
|
+
if (resp.status === 200) {
|
|
624
|
+
var d = resp.data;
|
|
625
|
+
console.log("");
|
|
626
|
+
console.log(C.bold + "BLUN King Runtime Status:" + C.reset);
|
|
627
|
+
console.log(C.dim + "─".repeat(40) + C.reset);
|
|
628
|
+
console.log(" Status: " + C.green + d.status + C.reset);
|
|
629
|
+
console.log(" Model: " + d.model);
|
|
630
|
+
console.log(" Ollama: " + (d.ollama === "connected" ? C.green : C.red) + d.ollama + C.reset);
|
|
631
|
+
console.log(" Uptime: " + d.uptime_seconds + "s");
|
|
632
|
+
console.log(" Artifacts: " + d.artifacts_count);
|
|
633
|
+
console.log(" Models: " + (d.available_models || []).join(", "));
|
|
634
|
+
if (d.prompt_registry && d.prompt_registry.layers) {
|
|
635
|
+
console.log("");
|
|
636
|
+
console.log(C.bold + " Prompt Registry:" + C.reset);
|
|
637
|
+
d.prompt_registry.layers.forEach(function(l) { console.log(" " + l); });
|
|
638
|
+
}
|
|
639
|
+
console.log("");
|
|
640
|
+
} else { printError(resp.data.error || "Status failed"); }
|
|
641
|
+
} catch(e) {
|
|
642
|
+
process.stdout.write("\r" + " ".repeat(20) + "\r");
|
|
643
|
+
printError(e.message);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async function cmdVersions() {
|
|
648
|
+
try {
|
|
649
|
+
var resp = await apiCall("GET", "/versions");
|
|
650
|
+
if (resp.status === 200) {
|
|
651
|
+
console.log("");
|
|
652
|
+
console.log(C.bold + "Prompt Versions:" + C.reset);
|
|
653
|
+
if (resp.data.versions) {
|
|
654
|
+
resp.data.versions.forEach(function(v) {
|
|
655
|
+
console.log(" " + (v.active ? C.green + "● " : C.dim + "○ ") + v.version + C.reset + " " + v.file);
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
console.log("");
|
|
659
|
+
} else { printError(resp.data.error || "Versions failed"); }
|
|
660
|
+
} catch(e) { printError(e.message); }
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
async function cmdHealth() {
|
|
664
|
+
try {
|
|
665
|
+
var resp = await apiCall("GET", "/health");
|
|
666
|
+
if (resp.status === 200) {
|
|
667
|
+
printSuccess("API healthy — " + resp.data.model + " | uptime: " + Math.round(resp.data.uptime) + "s");
|
|
668
|
+
} else { printError("API unhealthy: " + resp.status); }
|
|
669
|
+
} catch(e) { printError("API unreachable: " + e.message); }
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function cmdWatchdog(args) {
|
|
673
|
+
if (args === "on") {
|
|
674
|
+
config.watchdog.enabled = true;
|
|
675
|
+
saveConfig(config);
|
|
676
|
+
printSuccess("Watchdog enabled (interval: " + config.watchdog.interval + "s)");
|
|
677
|
+
} else if (args === "off") {
|
|
678
|
+
config.watchdog.enabled = false;
|
|
679
|
+
saveConfig(config);
|
|
680
|
+
printSuccess("Watchdog disabled.");
|
|
681
|
+
} else {
|
|
682
|
+
console.log("");
|
|
683
|
+
console.log(C.bold + "Watchdog:" + C.reset);
|
|
684
|
+
console.log(" Status: " + (config.watchdog.enabled ? C.green + "ON" : C.red + "OFF") + C.reset);
|
|
685
|
+
console.log(" Interval: " + config.watchdog.interval + "s");
|
|
686
|
+
console.log("");
|
|
687
|
+
console.log(C.dim + " /watchdog on — Enable" + C.reset);
|
|
688
|
+
console.log(C.dim + " /watchdog off — Disable" + C.reset);
|
|
689
|
+
console.log("");
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function cmdMemory(args) {
|
|
694
|
+
if (!args) {
|
|
695
|
+
// List memory
|
|
696
|
+
var files = fs.existsSync(MEMORY_DIR) ? fs.readdirSync(MEMORY_DIR) : [];
|
|
697
|
+
console.log("");
|
|
698
|
+
console.log(C.bold + "Local Memory (" + files.length + " entries):" + C.reset);
|
|
699
|
+
files.forEach(function(f) {
|
|
700
|
+
var content = fs.readFileSync(path.join(MEMORY_DIR, f), "utf8").trim();
|
|
701
|
+
console.log(" " + C.yellow + f.replace(".txt", "") + C.reset + " — " + content.slice(0, 60));
|
|
702
|
+
});
|
|
703
|
+
if (files.length === 0) console.log(C.dim + " (empty)" + C.reset);
|
|
704
|
+
console.log("");
|
|
705
|
+
} else {
|
|
706
|
+
var parts = args.split(/\s+/);
|
|
707
|
+
if (parts[0] === "save" && parts.length >= 3) {
|
|
708
|
+
var key = parts[1];
|
|
709
|
+
var value = parts.slice(2).join(" ");
|
|
710
|
+
fs.writeFileSync(path.join(MEMORY_DIR, key + ".txt"), value);
|
|
711
|
+
printSuccess("Saved: " + key);
|
|
712
|
+
} else if (parts[0] === "del" && parts[1]) {
|
|
713
|
+
var file = path.join(MEMORY_DIR, parts[1] + ".txt");
|
|
714
|
+
if (fs.existsSync(file)) { fs.unlinkSync(file); printSuccess("Deleted: " + parts[1]); }
|
|
715
|
+
else { printError("Not found: " + parts[1]); }
|
|
716
|
+
} else {
|
|
717
|
+
printError("Usage: /memory save <key> <value> or /memory del <key>");
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function cmdFiles() {
|
|
723
|
+
try {
|
|
724
|
+
var files = fs.readdirSync(config.workdir).slice(0, 30);
|
|
725
|
+
console.log("");
|
|
726
|
+
console.log(C.bold + "Workdir: " + config.workdir + C.reset);
|
|
727
|
+
files.forEach(function(f) {
|
|
728
|
+
var stat = fs.statSync(path.join(config.workdir, f));
|
|
729
|
+
console.log(" " + (stat.isDirectory() ? C.blue + "📁 " : "📄 ") + f + C.reset +
|
|
730
|
+
(stat.isFile() ? C.dim + " (" + stat.size + " bytes)" + C.reset : ""));
|
|
731
|
+
});
|
|
732
|
+
console.log("");
|
|
733
|
+
} catch(e) { printError(e.message); }
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// ── Main Loop ──
|
|
737
|
+
async function main() {
|
|
738
|
+
// Handle CLI args
|
|
739
|
+
var cliArgs = process.argv.slice(2);
|
|
740
|
+
if (cliArgs.length > 0) {
|
|
741
|
+
// Non-interactive mode
|
|
742
|
+
if (cliArgs[0] === "setup") {
|
|
743
|
+
printInfo("BLUN King CLI Setup");
|
|
744
|
+
var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
745
|
+
rl.question("API Base URL [" + config.api.base_url + "]: ", function(url) {
|
|
746
|
+
if (url.trim()) config.api.base_url = url.trim();
|
|
747
|
+
rl.question("API Key: ", function(key) {
|
|
748
|
+
if (key.trim()) { config.auth.api_key = key.trim(); config.auth.type = "api_key"; }
|
|
749
|
+
saveConfig(config);
|
|
750
|
+
printSuccess("Config saved to " + CONFIG_FILE);
|
|
751
|
+
rl.close();
|
|
752
|
+
});
|
|
753
|
+
});
|
|
754
|
+
return;
|
|
755
|
+
} else if (cliArgs[0] === "chat") {
|
|
756
|
+
// One-shot chat
|
|
757
|
+
var msg = cliArgs.slice(1).join(" ");
|
|
758
|
+
if (msg) {
|
|
759
|
+
var resp = await apiCall("POST", "/chat", { message: msg });
|
|
760
|
+
if (resp.status === 200) console.log(resp.data.answer);
|
|
761
|
+
else console.error("Error: " + (resp.data.error || resp.status));
|
|
762
|
+
}
|
|
763
|
+
return;
|
|
764
|
+
} else if (cliArgs[0] === "health") {
|
|
765
|
+
await cmdHealth();
|
|
766
|
+
return;
|
|
767
|
+
} else if (cliArgs[0] === "search") {
|
|
768
|
+
await cmdSearch(cliArgs.slice(1).join(" "));
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// Interactive mode
|
|
774
|
+
printHeader();
|
|
775
|
+
|
|
776
|
+
// Health check
|
|
777
|
+
try {
|
|
778
|
+
var h = await apiCall("GET", "/health");
|
|
779
|
+
if (h.status === 200) printSuccess("Connected to BLUN King API");
|
|
780
|
+
else printError("API returned " + h.status);
|
|
781
|
+
} catch(e) {
|
|
782
|
+
printError("Cannot reach API at " + config.api.base_url);
|
|
783
|
+
printInfo("Run: blun setup — to configure");
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
var rl = readline.createInterface({
|
|
787
|
+
input: process.stdin,
|
|
788
|
+
output: process.stdout,
|
|
789
|
+
prompt: C.blue + C.bold + "blun > " + C.reset
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
rl.prompt();
|
|
793
|
+
|
|
794
|
+
rl.on("line", async function(line) {
|
|
795
|
+
var input = line.trim();
|
|
796
|
+
if (!input) { rl.prompt(); return; }
|
|
797
|
+
|
|
798
|
+
if (input.startsWith("/")) {
|
|
799
|
+
await handleCommand(input);
|
|
800
|
+
} else {
|
|
801
|
+
await sendChat(input);
|
|
802
|
+
}
|
|
803
|
+
rl.prompt();
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
rl.on("close", function() {
|
|
807
|
+
console.log(C.dim + "\nBye." + C.reset);
|
|
808
|
+
process.exit(0);
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
main().catch(console.error);
|
package/package.json
ADDED
package/setup.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Quick setup script for BLUN King CLI
|
|
2
|
+
var fs = require("fs");
|
|
3
|
+
var path = require("path");
|
|
4
|
+
var os = require("os");
|
|
5
|
+
|
|
6
|
+
var home = os.homedir().replace(/[\/\\]\.claude-telegram-profile$/, "");
|
|
7
|
+
var dir = path.join(home, ".blun");
|
|
8
|
+
var configFile = path.join(dir, "config.json");
|
|
9
|
+
|
|
10
|
+
console.log("Home:", home);
|
|
11
|
+
console.log("Config:", configFile);
|
|
12
|
+
|
|
13
|
+
// Create dirs
|
|
14
|
+
[dir, path.join(dir, "memory"), path.join(dir, "skills")].forEach(function(d) {
|
|
15
|
+
if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
var config = {
|
|
19
|
+
auth: { type: "api_key", api_key: "blun_cdab41afb0913b367bdf05e4271864aed964b67862a7802d", oauth_token: "", oauth_expires: null },
|
|
20
|
+
api: { base_url: "http://176.9.158.30:3200", timeout: 60000 },
|
|
21
|
+
telegram: { enabled: false, bot_token: "", chat_id: "" },
|
|
22
|
+
model: "blun-king-v100",
|
|
23
|
+
workdir: home,
|
|
24
|
+
theme: "dark",
|
|
25
|
+
verbose: false,
|
|
26
|
+
watchdog: { enabled: false, interval: 600 }
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
30
|
+
console.log("Config saved!");
|