blun-king-cli 6.4.1 → 7.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/lib/auth.js +378 -176
- package/lib/chat.js +347 -143
- package/lib/client.js +101 -39
- package/lib/ui.js +150 -34
- package/lib/workspace.js +79 -55
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1,65 +1,127 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* BLUN King CLI v7.0.0 - API Client
|
|
3
|
+
* CommonJS - uses native https + fetch
|
|
4
|
+
*/
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
var https = require("https");
|
|
7
|
+
var auth = require("./auth");
|
|
8
|
+
|
|
9
|
+
var BASE_HOST = "blun.ai";
|
|
5
10
|
|
|
6
11
|
function request(method, urlPath, body) {
|
|
7
|
-
return new Promise((resolve, reject)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
res.on('data', (c) => data += c);
|
|
13
|
-
res.on('end', () => {
|
|
14
|
-
try { resolve({ status: res.statusCode, data: JSON.parse(data) }); }
|
|
15
|
-
catch { resolve({ status: res.statusCode, data }); }
|
|
16
|
-
});
|
|
12
|
+
return new Promise(function (resolve, reject) {
|
|
13
|
+
var authHeader = auth.getAuthHeader() || {};
|
|
14
|
+
var headers = { "Content-Type": "application/json" };
|
|
15
|
+
Object.keys(authHeader).forEach(function (k) {
|
|
16
|
+
headers[k] = authHeader[k];
|
|
17
17
|
});
|
|
18
|
-
req.
|
|
18
|
+
var req = https.request(
|
|
19
|
+
{ hostname: BASE_HOST, path: urlPath, method: method, headers: headers },
|
|
20
|
+
function (res) {
|
|
21
|
+
var data = "";
|
|
22
|
+
res.on("data", function (c) {
|
|
23
|
+
data += c;
|
|
24
|
+
});
|
|
25
|
+
res.on("end", function () {
|
|
26
|
+
try {
|
|
27
|
+
resolve({ status: res.statusCode, data: JSON.parse(data) });
|
|
28
|
+
} catch (e) {
|
|
29
|
+
resolve({ status: res.statusCode, data: data });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
req.on("error", reject);
|
|
19
35
|
if (body) req.write(JSON.stringify(body));
|
|
20
36
|
req.end();
|
|
21
37
|
});
|
|
22
38
|
}
|
|
23
39
|
|
|
24
|
-
|
|
40
|
+
function sendMessage(message, options) {
|
|
25
41
|
options = options || {};
|
|
26
|
-
|
|
27
|
-
return request(
|
|
42
|
+
var body = { message: message, model: options.model || "auto", stream: false };
|
|
43
|
+
return request("POST", "/api/king/chat", body);
|
|
28
44
|
}
|
|
29
45
|
|
|
30
|
-
|
|
46
|
+
function sendMessageStream(message, options) {
|
|
31
47
|
options = options || {};
|
|
32
|
-
return new Promise((resolve, reject)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
return new Promise(function (resolve, reject) {
|
|
49
|
+
var body = JSON.stringify({
|
|
50
|
+
message: message,
|
|
51
|
+
model: options.model || "auto",
|
|
52
|
+
stream: true,
|
|
53
|
+
});
|
|
54
|
+
var authHeader = auth.getAuthHeader() || {};
|
|
55
|
+
var headers = { "Content-Type": "application/json" };
|
|
56
|
+
Object.keys(authHeader).forEach(function (k) {
|
|
57
|
+
headers[k] = authHeader[k];
|
|
58
|
+
});
|
|
59
|
+
var req = https.request(
|
|
60
|
+
{
|
|
61
|
+
hostname: BASE_HOST,
|
|
62
|
+
path: "/api/king/chat",
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: headers,
|
|
65
|
+
},
|
|
66
|
+
function (res) {
|
|
67
|
+
var fullText = "";
|
|
68
|
+
var lastUsage = null;
|
|
69
|
+
var buffer = "";
|
|
70
|
+
res.on("data", function (chunk) {
|
|
71
|
+
buffer += chunk.toString();
|
|
72
|
+
var lines = buffer.split("\n");
|
|
73
|
+
// Keep last incomplete line in buffer
|
|
74
|
+
buffer = lines.pop() || "";
|
|
75
|
+
for (var i = 0; i < lines.length; i++) {
|
|
76
|
+
var line = lines[i].trim();
|
|
77
|
+
if (line.startsWith("data: ")) {
|
|
78
|
+
var payload = line.slice(6).trim();
|
|
79
|
+
if (payload === "[DONE]") continue;
|
|
80
|
+
try {
|
|
81
|
+
var parsed = JSON.parse(payload);
|
|
82
|
+
if (parsed.text) {
|
|
83
|
+
fullText += parsed.text;
|
|
84
|
+
if (options.onChunk) options.onChunk(parsed.text);
|
|
85
|
+
}
|
|
86
|
+
if (parsed.usage) {
|
|
87
|
+
lastUsage = parsed.usage;
|
|
88
|
+
if (options.onUsage) options.onUsage(parsed.usage);
|
|
89
|
+
}
|
|
90
|
+
} catch (e) {
|
|
91
|
+
// ignore parse errors on partial lines
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
res.on("end", function () {
|
|
97
|
+
// Process remaining buffer
|
|
98
|
+
if (buffer.trim().startsWith("data: ")) {
|
|
42
99
|
try {
|
|
43
|
-
|
|
100
|
+
var parsed = JSON.parse(buffer.trim().slice(6));
|
|
44
101
|
if (parsed.text) {
|
|
45
102
|
fullText += parsed.text;
|
|
46
103
|
if (options.onChunk) options.onChunk(parsed.text);
|
|
47
104
|
}
|
|
48
|
-
if (parsed.usage
|
|
49
|
-
} catch {}
|
|
105
|
+
if (parsed.usage) lastUsage = parsed.usage;
|
|
106
|
+
} catch (e) {}
|
|
50
107
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
108
|
+
resolve({ text: fullText, usage: lastUsage });
|
|
109
|
+
});
|
|
110
|
+
res.on("error", reject);
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
req.on("error", reject);
|
|
56
114
|
req.write(body);
|
|
57
115
|
req.end();
|
|
58
116
|
});
|
|
59
117
|
}
|
|
60
118
|
|
|
61
|
-
|
|
62
|
-
return request(
|
|
119
|
+
function getHealth() {
|
|
120
|
+
return request("GET", "/api/monitoring/health");
|
|
63
121
|
}
|
|
64
122
|
|
|
65
|
-
module.exports = {
|
|
123
|
+
module.exports = {
|
|
124
|
+
sendMessage: sendMessage,
|
|
125
|
+
sendMessageStream: sendMessageStream,
|
|
126
|
+
getHealth: getHealth,
|
|
127
|
+
};
|
package/lib/ui.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* BLUN King CLI - Rich Terminal UI Module
|
|
2
|
+
* BLUN King CLI v7.0.0 - Rich Terminal UI Module
|
|
3
3
|
* Claude Code-style terminal experience
|
|
4
4
|
* CommonJS - chalk@4, ora@5, boxen@5
|
|
5
5
|
*/
|
|
@@ -27,6 +27,14 @@ var colors = {
|
|
|
27
27
|
code: chalk.bgGray.white,
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
// Permission modes
|
|
31
|
+
var PERMISSION_MODES = ["safe", "normal", "god"];
|
|
32
|
+
var permissionLabels = {
|
|
33
|
+
safe: chalk.green("safe"),
|
|
34
|
+
normal: chalk.yellow("normal"),
|
|
35
|
+
god: chalk.red("god mode"),
|
|
36
|
+
};
|
|
37
|
+
|
|
30
38
|
function renderBanner() {
|
|
31
39
|
var art;
|
|
32
40
|
try {
|
|
@@ -51,16 +59,22 @@ function getInputBoxWidth() {
|
|
|
51
59
|
return Math.min((process.stdout.columns || 80) - 2, 76);
|
|
52
60
|
}
|
|
53
61
|
|
|
54
|
-
function renderInputBox(username, model) {
|
|
62
|
+
function renderInputBox(username, model, permMode) {
|
|
55
63
|
var w = getInputBoxWidth();
|
|
56
64
|
var u = username || "user";
|
|
57
65
|
var m = model || "auto";
|
|
66
|
+
var pm = permMode || "normal";
|
|
58
67
|
var hints = "/help \u2502 /settings \u2502 /model \u2502 /exit";
|
|
59
68
|
var labelText = " " + u + " [" + m + "] ";
|
|
60
69
|
var hintsText = " " + hints + " ";
|
|
61
70
|
var topFill = w - labelText.length - hintsText.length;
|
|
62
71
|
if (topFill < 2) topFill = 2;
|
|
63
|
-
var topLine =
|
|
72
|
+
var topLine =
|
|
73
|
+
chalk.dim("\u256d") +
|
|
74
|
+
chalk.cyan(labelText) +
|
|
75
|
+
chalk.dim("\u2500".repeat(topFill)) +
|
|
76
|
+
chalk.dim(hintsText) +
|
|
77
|
+
chalk.dim("\u256e");
|
|
64
78
|
console.log(topLine);
|
|
65
79
|
}
|
|
66
80
|
|
|
@@ -69,16 +83,27 @@ function renderInputBoxClose() {
|
|
|
69
83
|
console.log(chalk.dim("\u2570" + "\u2500".repeat(w) + "\u256f"));
|
|
70
84
|
}
|
|
71
85
|
|
|
72
|
-
function renderPrompt(
|
|
73
|
-
// Called by readline as the prompt string
|
|
74
|
-
// Show left border + cursor arrow
|
|
86
|
+
function renderPrompt() {
|
|
75
87
|
return chalk.dim("\u2502") + " " + chalk.green.bold("\u276f") + " ";
|
|
76
88
|
}
|
|
77
89
|
|
|
90
|
+
function renderPermissionLine(permMode) {
|
|
91
|
+
var pm = permMode || "normal";
|
|
92
|
+
var label = permissionLabels[pm] || permissionLabels.normal;
|
|
93
|
+
console.log(
|
|
94
|
+
chalk.dim(" bypass permissions ") +
|
|
95
|
+
label +
|
|
96
|
+
chalk.dim(" (shift+tab to cycle)")
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
78
100
|
function renderWelcomeInfo() {
|
|
79
101
|
var w = getInputBoxWidth();
|
|
80
102
|
console.log(chalk.dim("\u2502"));
|
|
81
|
-
console.log(
|
|
103
|
+
console.log(
|
|
104
|
+
chalk.dim("\u2502") +
|
|
105
|
+
chalk.dim(" Tip: Type a message and press Enter. Use /help for all commands.")
|
|
106
|
+
);
|
|
82
107
|
console.log(chalk.dim("\u2570" + "\u2500".repeat(w) + "\u256f"));
|
|
83
108
|
}
|
|
84
109
|
|
|
@@ -89,7 +114,6 @@ function renderResponse(text, type) {
|
|
|
89
114
|
var rendered = renderMarkdown(text);
|
|
90
115
|
|
|
91
116
|
if (type === "agent") {
|
|
92
|
-
// Agent responses in a box
|
|
93
117
|
console.log(
|
|
94
118
|
boxen(rendered, {
|
|
95
119
|
title: chalk.green.bold(" \u2728 BLUN "),
|
|
@@ -111,6 +135,9 @@ function renderResponse(text, type) {
|
|
|
111
135
|
borderColor: "red",
|
|
112
136
|
})
|
|
113
137
|
);
|
|
138
|
+
} else if (type === "system") {
|
|
139
|
+
console.log(chalk.yellow.bold(" [SYS] ") + chalk.yellow(rendered));
|
|
140
|
+
console.log();
|
|
114
141
|
} else {
|
|
115
142
|
var prefix = colorFn.bold(" [" + label + "] ");
|
|
116
143
|
var lines = rendered.split("\n");
|
|
@@ -125,17 +152,79 @@ function renderResponse(text, type) {
|
|
|
125
152
|
}
|
|
126
153
|
}
|
|
127
154
|
|
|
155
|
+
function renderStreamStart() {
|
|
156
|
+
// Print box top for streaming response
|
|
157
|
+
var w = getInputBoxWidth();
|
|
158
|
+
console.log(
|
|
159
|
+
chalk.dim(" ") +
|
|
160
|
+
chalk.green("\u256d") +
|
|
161
|
+
chalk.green.bold(" \u2728 BLUN ") +
|
|
162
|
+
chalk.green("\u2500".repeat(Math.max(2, w - 12))) +
|
|
163
|
+
chalk.green("\u256e")
|
|
164
|
+
);
|
|
165
|
+
process.stdout.write(chalk.green(" \u2502 "));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function renderStreamChunk(text) {
|
|
169
|
+
process.stdout.write(text);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function renderStreamEnd() {
|
|
173
|
+
var w = getInputBoxWidth();
|
|
174
|
+
console.log();
|
|
175
|
+
console.log(
|
|
176
|
+
chalk.dim(" ") +
|
|
177
|
+
chalk.green("\u2570") +
|
|
178
|
+
chalk.green("\u2500".repeat(w - 2)) +
|
|
179
|
+
chalk.green("\u256f")
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
128
183
|
function renderMarkdown(text) {
|
|
129
184
|
var out = text;
|
|
130
|
-
|
|
131
|
-
out = out.replace(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
185
|
+
// Code blocks
|
|
186
|
+
out = out.replace(/```(\w*)\n([\s\S]*?)```/g, function (_, lang, code) {
|
|
187
|
+
var header = lang ? chalk.dim.italic(" " + lang + " ") : "";
|
|
188
|
+
var lines = code.split("\n").map(function (l) {
|
|
189
|
+
return chalk.dim(" \u2502 ") + chalk.white(l);
|
|
190
|
+
});
|
|
191
|
+
return (
|
|
192
|
+
chalk.dim(" \u250c\u2500\u2500") +
|
|
193
|
+
header +
|
|
194
|
+
chalk.dim("\u2500".repeat(Math.max(1, 48 - (lang || "").length))) +
|
|
195
|
+
"\n" +
|
|
196
|
+
lines.join("\n") +
|
|
197
|
+
"\n" +
|
|
198
|
+
chalk.dim(" \u2514" + "\u2500".repeat(52))
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
out = out.replace(/^### (.+)$/gm, function (_, h) {
|
|
202
|
+
return chalk.bold.underline(h);
|
|
203
|
+
});
|
|
204
|
+
out = out.replace(/^## (.+)$/gm, function (_, h) {
|
|
205
|
+
return chalk.bold.yellow(h);
|
|
206
|
+
});
|
|
207
|
+
out = out.replace(/^# (.+)$/gm, function (_, h) {
|
|
208
|
+
return chalk.bold.magenta.underline(h);
|
|
209
|
+
});
|
|
210
|
+
out = out.replace(/\*\*\*(.+?)\*\*\*/g, function (_, t) {
|
|
211
|
+
return chalk.bold.italic(t);
|
|
212
|
+
});
|
|
213
|
+
out = out.replace(/\*\*(.+?)\*\*/g, function (_, t) {
|
|
214
|
+
return chalk.bold(t);
|
|
215
|
+
});
|
|
216
|
+
out = out.replace(/\*(.+?)\*/g, function (_, t) {
|
|
217
|
+
return chalk.italic(t);
|
|
218
|
+
});
|
|
219
|
+
out = out.replace(/`([^`]+)`/g, function (_, c) {
|
|
220
|
+
return chalk.bgGray.white(" " + c + " ");
|
|
221
|
+
});
|
|
222
|
+
out = out.replace(/^(\s*)[-*] (.+)$/gm, function (_, sp, t) {
|
|
223
|
+
return sp + chalk.green("\u25cf") + " " + t;
|
|
224
|
+
});
|
|
225
|
+
out = out.replace(/^(\s*)\d+\. (.+)$/gm, function (_, sp, t) {
|
|
226
|
+
return sp + chalk.yellow("\u25b8") + " " + t;
|
|
227
|
+
});
|
|
139
228
|
return out;
|
|
140
229
|
}
|
|
141
230
|
|
|
@@ -161,16 +250,20 @@ function stopSpinner(success, message) {
|
|
|
161
250
|
|
|
162
251
|
function renderTable(headers, rows) {
|
|
163
252
|
var table = new Table({
|
|
164
|
-
head: headers.map(function (h) {
|
|
253
|
+
head: headers.map(function (h) {
|
|
254
|
+
return chalk.cyan.bold(h);
|
|
255
|
+
}),
|
|
165
256
|
chars: {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
257
|
+
top: "\u2500", "top-mid": "\u252c", "top-left": "\u250c", "top-right": "\u2510",
|
|
258
|
+
bottom: "\u2500", "bottom-mid": "\u2534", "bottom-left": "\u2514", "bottom-right": "\u2518",
|
|
259
|
+
left: "\u2502", "left-mid": "\u251c", mid: "\u2500", "mid-mid": "\u253c",
|
|
260
|
+
right: "\u2502", "right-mid": "\u2524", middle: "\u2502",
|
|
170
261
|
},
|
|
171
262
|
style: { "padding-left": 1, "padding-right": 1 },
|
|
172
263
|
});
|
|
173
|
-
rows.forEach(function (r) {
|
|
264
|
+
rows.forEach(function (r) {
|
|
265
|
+
table.push(r);
|
|
266
|
+
});
|
|
174
267
|
console.log(table.toString());
|
|
175
268
|
console.log();
|
|
176
269
|
}
|
|
@@ -196,13 +289,27 @@ function renderCodeBlock(code, language) {
|
|
|
196
289
|
console.log(chalk.dim(" \u250c\u2500\u2500") + lang + chalk.dim("\u2500".repeat(lineLen)));
|
|
197
290
|
code.split("\n").forEach(function (line) {
|
|
198
291
|
var highlighted = line;
|
|
199
|
-
highlighted = highlighted.replace(/"[^"]*"/g, function (m) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
292
|
+
highlighted = highlighted.replace(/"[^"]*"/g, function (m) {
|
|
293
|
+
return chalk.yellow(m);
|
|
294
|
+
});
|
|
295
|
+
highlighted = highlighted.replace(/'[^']*'/g, function (m) {
|
|
296
|
+
return chalk.yellow(m);
|
|
297
|
+
});
|
|
298
|
+
var kws = [
|
|
299
|
+
"const", "let", "var", "function", "return", "if", "else", "for",
|
|
300
|
+
"while", "class", "import", "export", "from", "require", "async",
|
|
301
|
+
"await", "try", "catch", "throw", "new",
|
|
302
|
+
];
|
|
303
|
+
var kwRe = new RegExp("\\b(" + kws.join("|") + ")\\b", "g");
|
|
304
|
+
highlighted = highlighted.replace(kwRe, function (m) {
|
|
305
|
+
return chalk.magenta(m);
|
|
306
|
+
});
|
|
307
|
+
highlighted = highlighted.replace(/\b(\d+\.?\d*)\b/g, function (m) {
|
|
308
|
+
return chalk.cyan(m);
|
|
309
|
+
});
|
|
310
|
+
highlighted = highlighted.replace(/(\/\/.*)$/g, function (m) {
|
|
311
|
+
return chalk.dim(m);
|
|
312
|
+
});
|
|
206
313
|
console.log(chalk.dim(" \u2502 ") + highlighted);
|
|
207
314
|
});
|
|
208
315
|
console.log(chalk.dim(" \u2514" + "\u2500".repeat(54)));
|
|
@@ -213,11 +320,13 @@ function renderMiniStatus(model, tokens) {
|
|
|
213
320
|
var elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
214
321
|
var mins = Math.floor(elapsed / 60);
|
|
215
322
|
var secs = elapsed % 60;
|
|
323
|
+
var costEst = totalCost > 0 ? "$" + totalCost.toFixed(4) : "~$" + (totalTokens * 0.000003).toFixed(4);
|
|
216
324
|
var parts = [];
|
|
217
|
-
parts.push(chalk.bgCyan.black(" " + (model || "
|
|
325
|
+
parts.push(chalk.bgCyan.black(" " + (model || "chat") + " "));
|
|
218
326
|
if (tokens) parts.push(chalk.dim("Tokens: " + tokens.toLocaleString()));
|
|
219
327
|
parts.push(chalk.dim("Total: " + totalTokens.toLocaleString()));
|
|
220
|
-
parts.push(chalk.dim(mins + "m " + secs + "s"));
|
|
328
|
+
parts.push(chalk.dim(mins + "m " + (secs < 10 ? "0" : "") + secs + "s"));
|
|
329
|
+
parts.push(chalk.dim(costEst));
|
|
221
330
|
console.log(" " + parts.join(chalk.dim(" \u2502 ")));
|
|
222
331
|
console.log();
|
|
223
332
|
}
|
|
@@ -252,13 +361,15 @@ function renderProgress(current, total, label) {
|
|
|
252
361
|
var bar = chalk.green("\u2588".repeat(filled)) + chalk.dim("\u2591".repeat(empty));
|
|
253
362
|
var pctStr = chalk.bold(Math.round(pct * 100) + "%");
|
|
254
363
|
var lbl = label ? chalk.dim(" " + label) : "";
|
|
255
|
-
process.stdout.write(
|
|
364
|
+
process.stdout.write(
|
|
365
|
+
"\r " + bar + " " + pctStr + lbl + " (" + current + "/" + total + ")"
|
|
366
|
+
);
|
|
256
367
|
if (current >= total) console.log();
|
|
257
368
|
}
|
|
258
369
|
|
|
259
370
|
function addTokens(count, costPerToken) {
|
|
260
371
|
totalTokens += count;
|
|
261
|
-
totalCost += count * (costPerToken || 0);
|
|
372
|
+
totalCost += count * (costPerToken || 0.000003);
|
|
262
373
|
}
|
|
263
374
|
|
|
264
375
|
function getStats() {
|
|
@@ -271,8 +382,12 @@ module.exports = {
|
|
|
271
382
|
renderInputBox: renderInputBox,
|
|
272
383
|
renderInputBoxClose: renderInputBoxClose,
|
|
273
384
|
renderWelcomeInfo: renderWelcomeInfo,
|
|
385
|
+
renderPermissionLine: renderPermissionLine,
|
|
274
386
|
renderMiniStatus: renderMiniStatus,
|
|
275
387
|
renderResponse: renderResponse,
|
|
388
|
+
renderStreamStart: renderStreamStart,
|
|
389
|
+
renderStreamChunk: renderStreamChunk,
|
|
390
|
+
renderStreamEnd: renderStreamEnd,
|
|
276
391
|
renderSpinner: renderSpinner,
|
|
277
392
|
stopSpinner: stopSpinner,
|
|
278
393
|
renderTable: renderTable,
|
|
@@ -284,4 +399,5 @@ module.exports = {
|
|
|
284
399
|
addTokens: addTokens,
|
|
285
400
|
getStats: getStats,
|
|
286
401
|
colors: colors,
|
|
402
|
+
PERMISSION_MODES: PERMISSION_MODES,
|
|
287
403
|
};
|
package/lib/workspace.js
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const readline = require("readline");
|
|
6
|
-
const chalk = require("chalk");
|
|
1
|
+
/**
|
|
2
|
+
* BLUN King CLI v7.0.0 - Workspace Trust Module
|
|
3
|
+
* CommonJS - inquirer@8
|
|
4
|
+
*/
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
var inquirer = require("inquirer");
|
|
7
|
+
var fs = require("fs");
|
|
8
|
+
var path = require("path");
|
|
9
|
+
var os = require("os");
|
|
10
|
+
var chalk = require("chalk");
|
|
11
|
+
|
|
12
|
+
var TRUST_FILE = path.join(os.homedir(), ".blun", "trusted-folders.json");
|
|
9
13
|
|
|
10
14
|
function loadTrusted() {
|
|
11
15
|
try {
|
|
12
16
|
return JSON.parse(fs.readFileSync(TRUST_FILE, "utf8"));
|
|
13
|
-
} catch {
|
|
17
|
+
} catch (e) {
|
|
14
18
|
return [];
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
function saveTrusted(folders) {
|
|
19
|
-
|
|
23
|
+
var dir = path.dirname(TRUST_FILE);
|
|
20
24
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
21
25
|
fs.writeFileSync(TRUST_FILE, JSON.stringify(folders, null, 2));
|
|
22
26
|
}
|
|
@@ -26,8 +30,8 @@ function isTrusted(folder) {
|
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
function trustFolder(folder) {
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
var abs = path.resolve(folder);
|
|
34
|
+
var trusted = loadTrusted();
|
|
31
35
|
if (!trusted.includes(abs)) {
|
|
32
36
|
trusted.push(abs);
|
|
33
37
|
saveTrusted(trusted);
|
|
@@ -35,50 +39,61 @@ function trustFolder(folder) {
|
|
|
35
39
|
return abs;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
+
function selectFolder(initialFolder) {
|
|
43
|
+
var folder = initialFolder || process.cwd();
|
|
44
|
+
var alreadyTrusted = isTrusted(folder);
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (isTrusted(folder)) {
|
|
49
|
-
console.log(chalk.green(" Working in " + folder + " (trusted)"));
|
|
50
|
-
return folder;
|
|
51
|
-
}
|
|
46
|
+
console.log();
|
|
47
|
+
console.log(chalk.bold.white(" Do you trust the files in this folder?"));
|
|
48
|
+
console.log(chalk.dim(" " + folder));
|
|
49
|
+
console.log();
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
51
|
+
return inquirer
|
|
52
|
+
.prompt([
|
|
53
|
+
{
|
|
54
|
+
type: "list",
|
|
55
|
+
name: "trust",
|
|
56
|
+
message: chalk.yellow("Trust decision"),
|
|
57
|
+
choices: [
|
|
58
|
+
{
|
|
59
|
+
name:
|
|
60
|
+
chalk.green("Yes, trust this folder") +
|
|
61
|
+
(alreadyTrusted ? chalk.dim(" (previously trusted)") : ""),
|
|
62
|
+
value: "yes",
|
|
63
|
+
},
|
|
64
|
+
{ name: "Choose a different folder", value: "choose" },
|
|
65
|
+
{ name: chalk.red("No, exit"), value: "no" },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
])
|
|
69
|
+
.then(function (res) {
|
|
70
|
+
if (res.trust === "no") {
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
if (res.trust === "choose") {
|
|
74
|
+
return inquirer
|
|
75
|
+
.prompt([
|
|
76
|
+
{ type: "input", name: "newFolder", message: "Folder path:" },
|
|
77
|
+
])
|
|
78
|
+
.then(function (res2) {
|
|
79
|
+
var resolved = path.resolve(res2.newFolder);
|
|
80
|
+
if (!fs.existsSync(resolved)) {
|
|
81
|
+
console.log(chalk.red(" Folder not found."));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
return selectFolder(resolved);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
trustFolder(folder);
|
|
88
|
+
console.log(chalk.green(" \u2713 Folder trusted.\n"));
|
|
89
|
+
return folder;
|
|
90
|
+
});
|
|
76
91
|
}
|
|
77
92
|
|
|
78
93
|
function loadProjectConfig(folder) {
|
|
79
|
-
|
|
94
|
+
var mdPath = path.join(folder, "BLUN.md");
|
|
80
95
|
if (fs.existsSync(mdPath)) {
|
|
81
|
-
|
|
96
|
+
var content = fs.readFileSync(mdPath, "utf8");
|
|
82
97
|
console.log(chalk.green(" Found project config: BLUN.md"));
|
|
83
98
|
return content;
|
|
84
99
|
}
|
|
@@ -86,17 +101,26 @@ function loadProjectConfig(folder) {
|
|
|
86
101
|
}
|
|
87
102
|
|
|
88
103
|
function initProject(folder) {
|
|
89
|
-
|
|
90
|
-
|
|
104
|
+
var dir = path.resolve(folder);
|
|
105
|
+
var blunDir = path.join(dir, ".blun");
|
|
91
106
|
if (!fs.existsSync(blunDir)) fs.mkdirSync(blunDir, { recursive: true });
|
|
92
107
|
|
|
93
|
-
|
|
108
|
+
var mdPath = path.join(dir, "BLUN.md");
|
|
94
109
|
if (!fs.existsSync(mdPath)) {
|
|
95
|
-
fs.writeFileSync(
|
|
110
|
+
fs.writeFileSync(
|
|
111
|
+
mdPath,
|
|
112
|
+
"# Project Name\n\nYour project name here.\n\n## Description\n\nBrief description of the project.\n\n## Rules\n\n- Add project-specific rules here\n\n## Context\n\n- Add relevant context for the AI assistant\n"
|
|
113
|
+
);
|
|
96
114
|
}
|
|
97
|
-
console.log(chalk.green("
|
|
115
|
+
console.log(chalk.green(" \u2713 Initialized BLUN project in " + dir));
|
|
98
116
|
console.log(chalk.dim(" Created: .blun/ directory"));
|
|
99
117
|
console.log(chalk.dim(" Created: BLUN.md template"));
|
|
100
118
|
}
|
|
101
119
|
|
|
102
|
-
module.exports = {
|
|
120
|
+
module.exports = {
|
|
121
|
+
trustFolder: trustFolder,
|
|
122
|
+
isTrusted: isTrusted,
|
|
123
|
+
selectFolder: selectFolder,
|
|
124
|
+
initProject: initProject,
|
|
125
|
+
loadProjectConfig: loadProjectConfig,
|
|
126
|
+
};
|