blun-king-cli 1.0.0 → 1.2.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 +388 -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,68 @@ 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
|
-
function printAnswer(answer) {
|
|
124
|
+
function printAnswer(answer, meta) {
|
|
117
125
|
console.log("");
|
|
118
|
-
console.log(C.green + C.bold + "BLUN King
|
|
119
|
-
|
|
126
|
+
console.log(C.green + C.bold + " " + BOX.bot + " BLUN King" + C.reset + (meta ? C.gray + " " + BOX.dot + " " + meta + C.reset : ""));
|
|
127
|
+
console.log(C.green + " " + BOX.h.repeat(40) + C.reset);
|
|
128
|
+
// Markdown rendering
|
|
120
129
|
var lines = answer.split("\n");
|
|
121
130
|
var inCode = false;
|
|
122
131
|
for (var i = 0; i < lines.length; i++) {
|
|
123
132
|
var line = lines[i];
|
|
124
133
|
if (line.startsWith("```")) {
|
|
125
134
|
inCode = !inCode;
|
|
126
|
-
|
|
135
|
+
if (inCode) {
|
|
136
|
+
var lang = line.slice(3).trim();
|
|
137
|
+
console.log(C.dim + " " + BOX.tl + BOX.h.repeat(38) + (lang ? " " + lang + " " : "") + C.reset);
|
|
138
|
+
} else {
|
|
139
|
+
console.log(C.dim + " " + BOX.bl + BOX.h.repeat(38) + C.reset);
|
|
140
|
+
}
|
|
127
141
|
} else if (inCode) {
|
|
128
|
-
console.log(C.cyan + line + C.reset);
|
|
142
|
+
console.log(C.cyan + " " + BOX.v + " " + line + C.reset);
|
|
129
143
|
} else if (line.startsWith("# ")) {
|
|
130
|
-
console.log(C.bold + C.yellow + line + C.reset);
|
|
144
|
+
console.log(C.bold + C.yellow + " " + line + C.reset);
|
|
131
145
|
} else if (line.startsWith("## ")) {
|
|
132
|
-
console.log(C.bold + C.magenta + line + C.reset);
|
|
146
|
+
console.log(C.bold + C.magenta + " " + line + C.reset);
|
|
133
147
|
} else if (line.startsWith("### ")) {
|
|
134
|
-
console.log(C.bold + line + C.reset);
|
|
148
|
+
console.log(C.bold + " " + line + C.reset);
|
|
135
149
|
} else if (line.startsWith("- ") || line.startsWith("* ")) {
|
|
136
|
-
console.log(C.white + "
|
|
150
|
+
console.log(C.white + " " + line + C.reset);
|
|
137
151
|
} else if (line.match(/^\d+\./)) {
|
|
138
|
-
console.log(C.white + "
|
|
152
|
+
console.log(C.white + " " + line + C.reset);
|
|
153
|
+
} else if (line.startsWith("**") && line.endsWith("**")) {
|
|
154
|
+
console.log(C.bold + C.brightWhite + " " + line + C.reset);
|
|
139
155
|
} else {
|
|
140
|
-
console.log(line);
|
|
156
|
+
console.log(" " + line);
|
|
141
157
|
}
|
|
142
158
|
}
|
|
143
159
|
console.log("");
|
|
144
160
|
}
|
|
145
161
|
|
|
162
|
+
function printUserMessage(msg) {
|
|
163
|
+
console.log("");
|
|
164
|
+
console.log(C.brightBlue + C.bold + " " + BOX.user + " Du" + C.reset);
|
|
165
|
+
console.log(C.brightBlue + " " + BOX.h.repeat(40) + C.reset);
|
|
166
|
+
console.log(C.brightWhite + " " + msg + C.reset);
|
|
167
|
+
}
|
|
168
|
+
|
|
146
169
|
function printError(msg) { console.log(C.red + "ERROR: " + msg + C.reset); }
|
|
147
170
|
function printInfo(msg) { console.log(C.cyan + msg + C.reset); }
|
|
148
171
|
function printSuccess(msg) { console.log(C.green + "✓ " + msg + C.reset); }
|
|
@@ -185,6 +208,16 @@ async function handleCommand(input) {
|
|
|
185
208
|
console.log(" /set telegram Configure Telegram bridge");
|
|
186
209
|
console.log(" /set verbose Toggle verbose logging");
|
|
187
210
|
console.log("");
|
|
211
|
+
console.log(C.yellow + " DEV" + C.reset);
|
|
212
|
+
console.log(" /sh <cmd> Run shell command");
|
|
213
|
+
console.log(" /git <cmd> Git commands (status, log, diff, add, commit, push, pull, branch, checkout, clone, init)");
|
|
214
|
+
console.log(" /ssh add/del/list Connect & manage SSH hosts");
|
|
215
|
+
console.log(" /ssh <host> <cmd> Run command on remote host");
|
|
216
|
+
console.log(" /deploy <method> Deploy: git, ssh, sync, pm2");
|
|
217
|
+
console.log(" /read <file> Read a file");
|
|
218
|
+
console.log(" /write <file> Write/create a file");
|
|
219
|
+
console.log(" /init Init project (AGENT.md, .gitignore, git init)");
|
|
220
|
+
console.log("");
|
|
188
221
|
console.log(C.yellow + " SYSTEM" + C.reset);
|
|
189
222
|
console.log(" /status Runtime status");
|
|
190
223
|
console.log(" /versions Prompt versions");
|
|
@@ -272,6 +305,34 @@ async function handleCommand(input) {
|
|
|
272
305
|
cmdFiles();
|
|
273
306
|
break;
|
|
274
307
|
|
|
308
|
+
case "/sh":
|
|
309
|
+
cmdShell(args);
|
|
310
|
+
break;
|
|
311
|
+
|
|
312
|
+
case "/git":
|
|
313
|
+
cmdGit(args);
|
|
314
|
+
break;
|
|
315
|
+
|
|
316
|
+
case "/ssh":
|
|
317
|
+
cmdSsh(args);
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case "/deploy":
|
|
321
|
+
cmdDeploy(args);
|
|
322
|
+
break;
|
|
323
|
+
|
|
324
|
+
case "/read":
|
|
325
|
+
cmdRead(args);
|
|
326
|
+
break;
|
|
327
|
+
|
|
328
|
+
case "/write":
|
|
329
|
+
cmdWrite(args);
|
|
330
|
+
break;
|
|
331
|
+
|
|
332
|
+
case "/init":
|
|
333
|
+
cmdInit();
|
|
334
|
+
break;
|
|
335
|
+
|
|
275
336
|
case "/exit":
|
|
276
337
|
case "/quit":
|
|
277
338
|
case "/q":
|
|
@@ -286,12 +347,22 @@ async function handleCommand(input) {
|
|
|
286
347
|
// ── Chat ──
|
|
287
348
|
async function sendChat(message) {
|
|
288
349
|
try {
|
|
289
|
-
|
|
350
|
+
printUserMessage(message);
|
|
351
|
+
// Animated thinking
|
|
352
|
+
var dots = 0;
|
|
353
|
+
var thinkFrames = ["thinking", "thinking.", "thinking..", "thinking..."];
|
|
354
|
+
var thinkTimer = setInterval(function() {
|
|
355
|
+
process.stdout.write("\r" + C.dim + " " + BOX.bot + " " + thinkFrames[dots % 4] + " " + C.reset);
|
|
356
|
+
dots++;
|
|
357
|
+
}, 300);
|
|
358
|
+
|
|
290
359
|
var resp = await apiCall("POST", "/chat", {
|
|
291
360
|
message: message,
|
|
292
361
|
history: chatHistory.slice(-10)
|
|
293
362
|
});
|
|
294
|
-
|
|
363
|
+
|
|
364
|
+
clearInterval(thinkTimer);
|
|
365
|
+
process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
295
366
|
|
|
296
367
|
if (resp.status !== 200) {
|
|
297
368
|
printError(resp.data.error || "API Error " + resp.status);
|
|
@@ -301,17 +372,14 @@ async function sendChat(message) {
|
|
|
301
372
|
chatHistory.push({ role: "user", content: message });
|
|
302
373
|
chatHistory.push({ role: "assistant", content: resp.data.answer });
|
|
303
374
|
|
|
304
|
-
printAnswer(resp.data.answer);
|
|
305
|
-
|
|
306
|
-
// Status line
|
|
307
375
|
var q = resp.data.quality || {};
|
|
308
|
-
|
|
309
|
-
(q.retried ? "
|
|
310
|
-
|
|
311
|
-
console.log("");
|
|
376
|
+
var meta = resp.data.task_type + "/" + resp.data.role + " " + BOX.dot + " score: " + (q.score || "?") +
|
|
377
|
+
(q.retried ? " " + BOX.dot + " retried" : "");
|
|
378
|
+
printAnswer(resp.data.answer, meta);
|
|
312
379
|
|
|
313
380
|
} catch(e) {
|
|
314
|
-
|
|
381
|
+
if (typeof thinkTimer !== "undefined") clearInterval(thinkTimer);
|
|
382
|
+
process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
315
383
|
printError("Connection failed: " + e.message);
|
|
316
384
|
}
|
|
317
385
|
}
|
|
@@ -733,6 +801,277 @@ function cmdFiles() {
|
|
|
733
801
|
} catch(e) { printError(e.message); }
|
|
734
802
|
}
|
|
735
803
|
|
|
804
|
+
// ── Shell Execution ──
|
|
805
|
+
function cmdShell(command) {
|
|
806
|
+
if (!command) { printError("Usage: /sh <command>"); return; }
|
|
807
|
+
try {
|
|
808
|
+
console.log(C.dim + "$ " + command + C.reset);
|
|
809
|
+
var output = execSync(command, { cwd: config.workdir, encoding: "utf8", timeout: 30000, stdio: ["pipe", "pipe", "pipe"] });
|
|
810
|
+
console.log(output);
|
|
811
|
+
} catch(e) {
|
|
812
|
+
if (e.stdout) console.log(e.stdout);
|
|
813
|
+
if (e.stderr) console.log(C.red + e.stderr + C.reset);
|
|
814
|
+
printError("Exit code: " + (e.status || "unknown"));
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// ── Git Commands ──
|
|
819
|
+
function cmdGit(args) {
|
|
820
|
+
if (!args) {
|
|
821
|
+
// Show git status
|
|
822
|
+
cmdShell("git status --short");
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
var sub = args.split(/\s+/)[0];
|
|
826
|
+
var rest = args.slice(sub.length).trim();
|
|
827
|
+
|
|
828
|
+
switch(sub) {
|
|
829
|
+
case "status": cmdShell("git status"); break;
|
|
830
|
+
case "log": cmdShell("git log --oneline -10"); break;
|
|
831
|
+
case "diff": cmdShell("git diff --stat"); break;
|
|
832
|
+
case "add":
|
|
833
|
+
cmdShell("git add " + (rest || "."));
|
|
834
|
+
printSuccess("Files staged.");
|
|
835
|
+
break;
|
|
836
|
+
case "commit":
|
|
837
|
+
if (!rest) { printError("Usage: /git commit <message>"); return; }
|
|
838
|
+
cmdShell('git commit -m "' + rest.replace(/"/g, '\\"') + '"');
|
|
839
|
+
break;
|
|
840
|
+
case "push":
|
|
841
|
+
var remote = rest || "origin";
|
|
842
|
+
try {
|
|
843
|
+
var branch = execSync("git branch --show-current", { cwd: config.workdir, encoding: "utf8" }).trim();
|
|
844
|
+
printInfo("Pushing " + branch + " to " + remote + "...");
|
|
845
|
+
cmdShell("git push " + remote + " " + branch);
|
|
846
|
+
printSuccess("Pushed!");
|
|
847
|
+
} catch(e) { printError("Push failed: " + e.message); }
|
|
848
|
+
break;
|
|
849
|
+
case "pull":
|
|
850
|
+
cmdShell("git pull " + (rest || ""));
|
|
851
|
+
break;
|
|
852
|
+
case "branch":
|
|
853
|
+
cmdShell("git branch " + (rest || "-a"));
|
|
854
|
+
break;
|
|
855
|
+
case "checkout":
|
|
856
|
+
if (!rest) { printError("Usage: /git checkout <branch>"); return; }
|
|
857
|
+
cmdShell("git checkout " + rest);
|
|
858
|
+
break;
|
|
859
|
+
case "clone":
|
|
860
|
+
if (!rest) { printError("Usage: /git clone <url>"); return; }
|
|
861
|
+
cmdShell("git clone " + rest);
|
|
862
|
+
break;
|
|
863
|
+
case "init":
|
|
864
|
+
cmdShell("git init");
|
|
865
|
+
break;
|
|
866
|
+
case "remote":
|
|
867
|
+
cmdShell("git remote -v");
|
|
868
|
+
break;
|
|
869
|
+
default:
|
|
870
|
+
cmdShell("git " + args);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// ── SSH Commands ──
|
|
875
|
+
function cmdSsh(args) {
|
|
876
|
+
if (!args) {
|
|
877
|
+
// Show saved connections
|
|
878
|
+
var sshFile = path.join(CONFIG_DIR, "ssh.json");
|
|
879
|
+
if (fs.existsSync(sshFile)) {
|
|
880
|
+
var connections = JSON.parse(fs.readFileSync(sshFile, "utf8"));
|
|
881
|
+
console.log("");
|
|
882
|
+
console.log(C.bold + "SSH Connections:" + C.reset);
|
|
883
|
+
for (var name in connections) {
|
|
884
|
+
var c = connections[name];
|
|
885
|
+
console.log(" " + C.yellow + name + C.reset + " — " + c.user + "@" + c.host + (c.port !== 22 ? ":" + c.port : ""));
|
|
886
|
+
}
|
|
887
|
+
console.log("");
|
|
888
|
+
console.log(C.dim + " /ssh <name> <command> — Run command" + C.reset);
|
|
889
|
+
console.log(C.dim + " /ssh add <name> <user@host> — Add connection" + C.reset);
|
|
890
|
+
console.log(C.dim + " /ssh del <name> — Remove connection" + C.reset);
|
|
891
|
+
} else {
|
|
892
|
+
printInfo("No SSH connections saved. Use /ssh add <name> <user@host>");
|
|
893
|
+
}
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
var parts = args.split(/\s+/);
|
|
898
|
+
var sshFile = path.join(CONFIG_DIR, "ssh.json");
|
|
899
|
+
var connections = {};
|
|
900
|
+
if (fs.existsSync(sshFile)) {
|
|
901
|
+
try { connections = JSON.parse(fs.readFileSync(sshFile, "utf8")); } catch(e) {}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if (parts[0] === "add" && parts[1] && parts[2]) {
|
|
905
|
+
var match = parts[2].match(/^([^@]+)@([^:]+)(?::(\d+))?$/);
|
|
906
|
+
if (!match) { printError("Format: user@host or user@host:port"); return; }
|
|
907
|
+
connections[parts[1]] = { user: match[1], host: match[2], port: parseInt(match[3] || "22"), key: parts[3] || null };
|
|
908
|
+
fs.writeFileSync(sshFile, JSON.stringify(connections, null, 2));
|
|
909
|
+
printSuccess("SSH connection '" + parts[1] + "' saved.");
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (parts[0] === "del" && parts[1]) {
|
|
914
|
+
delete connections[parts[1]];
|
|
915
|
+
fs.writeFileSync(sshFile, JSON.stringify(connections, null, 2));
|
|
916
|
+
printSuccess("SSH connection '" + parts[1] + "' removed.");
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Execute command on saved connection
|
|
921
|
+
var connName = parts[0];
|
|
922
|
+
var remoteCmd = parts.slice(1).join(" ");
|
|
923
|
+
if (!connections[connName]) { printError("Unknown connection: " + connName + ". Use /ssh to list."); return; }
|
|
924
|
+
if (!remoteCmd) { printError("Usage: /ssh " + connName + " <command>"); return; }
|
|
925
|
+
|
|
926
|
+
var conn = connections[connName];
|
|
927
|
+
var sshCmd = "ssh";
|
|
928
|
+
if (conn.key) sshCmd += " -i " + conn.key;
|
|
929
|
+
sshCmd += " -o ConnectTimeout=10 -o StrictHostKeyChecking=no";
|
|
930
|
+
sshCmd += " -p " + conn.port;
|
|
931
|
+
sshCmd += " " + conn.user + "@" + conn.host;
|
|
932
|
+
sshCmd += ' "' + remoteCmd.replace(/"/g, '\\"') + '"';
|
|
933
|
+
|
|
934
|
+
printInfo("SSH " + connName + ": " + remoteCmd);
|
|
935
|
+
cmdShell(sshCmd);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// ── Deploy Command ──
|
|
939
|
+
async function cmdDeploy(args) {
|
|
940
|
+
if (!args) {
|
|
941
|
+
console.log("");
|
|
942
|
+
console.log(C.bold + "Deploy Options:" + C.reset);
|
|
943
|
+
console.log(" /deploy git — git add + commit + push");
|
|
944
|
+
console.log(" /deploy ssh <name> <cmd> — Run deploy command via SSH");
|
|
945
|
+
console.log(" /deploy sync <name> — rsync workdir to server");
|
|
946
|
+
console.log(" /deploy pm2 <name> <app> — pm2 restart on server");
|
|
947
|
+
console.log("");
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
var parts = args.split(/\s+/);
|
|
952
|
+
var action = parts[0];
|
|
953
|
+
|
|
954
|
+
if (action === "git") {
|
|
955
|
+
printInfo("Deploying via git...");
|
|
956
|
+
try {
|
|
957
|
+
var status = execSync("git status --porcelain", { cwd: config.workdir, encoding: "utf8" }).trim();
|
|
958
|
+
if (status) {
|
|
959
|
+
cmdShell("git add .");
|
|
960
|
+
var msg = parts.slice(1).join(" ") || "deploy: " + new Date().toISOString().slice(0, 16);
|
|
961
|
+
cmdShell('git commit -m "' + msg + '"');
|
|
962
|
+
}
|
|
963
|
+
var branch = execSync("git branch --show-current", { cwd: config.workdir, encoding: "utf8" }).trim();
|
|
964
|
+
cmdShell("git push origin " + branch);
|
|
965
|
+
printSuccess("Deployed via git push!");
|
|
966
|
+
} catch(e) { printError("Deploy failed: " + e.message); }
|
|
967
|
+
|
|
968
|
+
} else if (action === "ssh" && parts[1]) {
|
|
969
|
+
cmdSsh(parts.slice(1).join(" "));
|
|
970
|
+
|
|
971
|
+
} else if (action === "sync" && parts[1]) {
|
|
972
|
+
var sshFile = path.join(CONFIG_DIR, "ssh.json");
|
|
973
|
+
if (!fs.existsSync(sshFile)) { printError("No SSH connections. Use /ssh add first."); return; }
|
|
974
|
+
var connections = JSON.parse(fs.readFileSync(sshFile, "utf8"));
|
|
975
|
+
var conn = connections[parts[1]];
|
|
976
|
+
if (!conn) { printError("Unknown connection: " + parts[1]); return; }
|
|
977
|
+
var dest = parts[2] || "/root/" + path.basename(config.workdir);
|
|
978
|
+
var rsyncCmd = "rsync -avz --exclude node_modules --exclude .git";
|
|
979
|
+
if (conn.key) rsyncCmd += " -e 'ssh -i " + conn.key + " -p " + conn.port + "'";
|
|
980
|
+
rsyncCmd += " " + config.workdir + "/ " + conn.user + "@" + conn.host + ":" + dest + "/";
|
|
981
|
+
printInfo("Syncing to " + parts[1] + ":" + dest);
|
|
982
|
+
cmdShell(rsyncCmd);
|
|
983
|
+
|
|
984
|
+
} else if (action === "pm2" && parts[1] && parts[2]) {
|
|
985
|
+
cmdSsh(parts[1] + " pm2 restart " + parts[2]);
|
|
986
|
+
|
|
987
|
+
} else {
|
|
988
|
+
printError("Unknown deploy action. Use /deploy for help.");
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// ── Read/Write Files ──
|
|
993
|
+
function cmdRead(filePath) {
|
|
994
|
+
if (!filePath) { printError("Usage: /read <file>"); return; }
|
|
995
|
+
var full = path.resolve(config.workdir, filePath);
|
|
996
|
+
if (!fs.existsSync(full)) { printError("File not found: " + full); return; }
|
|
997
|
+
var content = fs.readFileSync(full, "utf8");
|
|
998
|
+
console.log("");
|
|
999
|
+
console.log(C.bold + "File: " + full + C.reset);
|
|
1000
|
+
console.log(C.dim + "─".repeat(50) + C.reset);
|
|
1001
|
+
var lines = content.split("\n");
|
|
1002
|
+
for (var i = 0; i < lines.length; i++) {
|
|
1003
|
+
console.log(C.dim + String(i + 1).padStart(4) + " │ " + C.reset + lines[i]);
|
|
1004
|
+
}
|
|
1005
|
+
console.log("");
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
function cmdWrite(args) {
|
|
1009
|
+
if (!args) { printError("Usage: /write <file> <content> or /write <file> (then type content, end with EOF)"); return; }
|
|
1010
|
+
var parts = args.split(/\s+/);
|
|
1011
|
+
var filePath = parts[0];
|
|
1012
|
+
var content = parts.slice(1).join(" ");
|
|
1013
|
+
var full = path.resolve(config.workdir, filePath);
|
|
1014
|
+
|
|
1015
|
+
if (content) {
|
|
1016
|
+
fs.writeFileSync(full, content);
|
|
1017
|
+
printSuccess("Written: " + full + " (" + content.length + " bytes)");
|
|
1018
|
+
} else {
|
|
1019
|
+
printInfo("Type content (end with line 'EOF'):");
|
|
1020
|
+
// This will be handled differently in interactive mode
|
|
1021
|
+
printError("For multi-line write, use: /generate instead");
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// ── Project Init ──
|
|
1026
|
+
function cmdInit(args) {
|
|
1027
|
+
var name = args || path.basename(config.workdir);
|
|
1028
|
+
var projectDir = args ? path.join(config.workdir, args) : config.workdir;
|
|
1029
|
+
|
|
1030
|
+
if (args && !fs.existsSync(projectDir)) {
|
|
1031
|
+
fs.mkdirSync(projectDir, { recursive: true });
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// Create project memory file
|
|
1035
|
+
var memFile = path.join(projectDir, "AGENT.md");
|
|
1036
|
+
if (!fs.existsSync(memFile)) {
|
|
1037
|
+
var agentContent = "# " + name + "\n\n" +
|
|
1038
|
+
"## Projekt\n" +
|
|
1039
|
+
"Beschreibung: \n\n" +
|
|
1040
|
+
"## Stack\n" +
|
|
1041
|
+
"- \n\n" +
|
|
1042
|
+
"## Regeln\n" +
|
|
1043
|
+
"- \n\n" +
|
|
1044
|
+
"## Status\n" +
|
|
1045
|
+
"Erstellt: " + new Date().toISOString().split("T")[0] + "\n";
|
|
1046
|
+
fs.writeFileSync(memFile, agentContent);
|
|
1047
|
+
printSuccess("AGENT.md erstellt in " + projectDir);
|
|
1048
|
+
} else {
|
|
1049
|
+
printInfo("AGENT.md existiert bereits.");
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// Git init if not already
|
|
1053
|
+
try {
|
|
1054
|
+
execSync("git rev-parse --git-dir", { cwd: projectDir, stdio: "pipe" });
|
|
1055
|
+
printInfo("Git repo already initialized.");
|
|
1056
|
+
} catch(e) {
|
|
1057
|
+
execSync("git init", { cwd: projectDir, stdio: "pipe" });
|
|
1058
|
+
printSuccess("Git repo initialized.");
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// Create .gitignore if missing
|
|
1062
|
+
var gitignore = path.join(projectDir, ".gitignore");
|
|
1063
|
+
if (!fs.existsSync(gitignore)) {
|
|
1064
|
+
fs.writeFileSync(gitignore, "node_modules/\n.env\n*.log\n.blun/\n");
|
|
1065
|
+
printSuccess(".gitignore erstellt.");
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
if (args) {
|
|
1069
|
+
config.workdir = projectDir;
|
|
1070
|
+
saveConfig(config);
|
|
1071
|
+
printSuccess("Workdir set to: " + projectDir);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
736
1075
|
// ── Main Loop ──
|
|
737
1076
|
async function main() {
|
|
738
1077
|
// Handle CLI args
|
|
@@ -783,10 +1122,25 @@ async function main() {
|
|
|
783
1122
|
printInfo("Run: blun setup — to configure");
|
|
784
1123
|
}
|
|
785
1124
|
|
|
1125
|
+
var ALL_COMMANDS = [
|
|
1126
|
+
"/help", "/clear", "/skills", "/skill", "/search", "/learn", "/learn-url",
|
|
1127
|
+
"/generate", "/analyze", "/score", "/eval", "/settings", "/set",
|
|
1128
|
+
"/status", "/versions", "/health", "/watchdog", "/memory", "/files",
|
|
1129
|
+
"/sh", "/git", "/ssh", "/deploy", "/read", "/write", "/init",
|
|
1130
|
+
"/screenshot", "/render", "/agent", "/exit"
|
|
1131
|
+
];
|
|
1132
|
+
|
|
786
1133
|
var rl = readline.createInterface({
|
|
787
1134
|
input: process.stdin,
|
|
788
1135
|
output: process.stdout,
|
|
789
|
-
prompt: C.
|
|
1136
|
+
prompt: "\n" + C.brightBlue + C.bold + " " + BOX.arrow + " " + C.reset,
|
|
1137
|
+
completer: function(line) {
|
|
1138
|
+
if (line.startsWith("/")) {
|
|
1139
|
+
var hits = ALL_COMMANDS.filter(function(c) { return c.startsWith(line); });
|
|
1140
|
+
return [hits.length ? hits : ALL_COMMANDS, line];
|
|
1141
|
+
}
|
|
1142
|
+
return [[], line];
|
|
1143
|
+
}
|
|
790
1144
|
});
|
|
791
1145
|
|
|
792
1146
|
rl.prompt();
|