blun-king-cli 4.1.0 → 5.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/api.js +965 -0
- package/blun-cli.js +763 -0
- package/blunking-api.js +7 -0
- package/bot.js +188 -0
- package/browser-controller.js +76 -0
- package/chat-memory.js +103 -0
- package/file-helper.js +63 -0
- package/fuzzy-match.js +78 -0
- package/identities.js +106 -0
- package/installer.js +160 -0
- package/job-manager.js +146 -0
- package/local-data.js +71 -0
- package/message-builder.js +28 -0
- package/noisy-evals.js +38 -0
- package/package.json +17 -4
- package/palace-memory.js +246 -0
- package/reference-inspector.js +228 -0
- package/runtime.js +555 -0
- package/task-executor.js +104 -0
- package/tests/browser-controller.test.js +42 -0
- package/tests/cli.test.js +93 -0
- package/tests/file-helper.test.js +18 -0
- package/tests/installer.test.js +39 -0
- package/tests/job-manager.test.js +99 -0
- package/tests/merge-compat.test.js +77 -0
- package/tests/messages.test.js +23 -0
- package/tests/noisy-evals.test.js +12 -0
- package/tests/noisy-intent-corpus.test.js +45 -0
- package/tests/reference-inspector.test.js +36 -0
- package/tests/runtime.test.js +119 -0
- package/tests/task-executor.test.js +40 -0
- package/tests/tools.test.js +23 -0
- package/tests/user-profile.test.js +66 -0
- package/tests/website-builder.test.js +66 -0
- package/tmp-build-smoke/nicrazy-landing/index.html +53 -0
- package/tmp-build-smoke/nicrazy-landing/style.css +110 -0
- package/tmp-shot-smoke/website-shot-1776006760424.png +0 -0
- package/tmp-shot-smoke/website-shot-1776007850007.png +0 -0
- package/tmp-shot-smoke/website-shot-1776007886209.png +0 -0
- package/tmp-shot-smoke/website-shot-1776007903766.png +0 -0
- package/tmp-shot-smoke/website-shot-1776008737117.png +0 -0
- package/tmp-shot-smoke/website-shot-1776008988859.png +0 -0
- package/tmp-smoke/nicrazy-landing/index.html +66 -0
- package/tmp-smoke/nicrazy-landing/style.css +104 -0
- package/tools.js +177 -0
- package/user-profile.js +395 -0
- package/website-builder.js +394 -0
- package/website-shot-1776010648230.png +0 -0
- package/website_builder.txt +38 -0
- package/bin/blun.js +0 -3156
- package/setup.js +0 -30
package/installer.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const os = require("os");
|
|
4
|
+
const { execFile } = require("child_process");
|
|
5
|
+
const { hasApproxWord, hasApproxPhrase } = require("./fuzzy-match");
|
|
6
|
+
|
|
7
|
+
const BLUN_HOME = process.env.BLUN_HOME || path.join(os.homedir(), ".blun");
|
|
8
|
+
const EXTENSIONS_DIR = path.join(BLUN_HOME, "extensions");
|
|
9
|
+
if (!fs.existsSync(EXTENSIONS_DIR)) fs.mkdirSync(EXTENSIONS_DIR, { recursive: true });
|
|
10
|
+
|
|
11
|
+
function execFileAsync(command, args, options = {}) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
execFile(command, args, { ...options, maxBuffer: 20 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
14
|
+
if (error) reject(new Error(stderr || error.message));
|
|
15
|
+
else resolve({ stdout, stderr });
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function classifySource(source) {
|
|
21
|
+
if (/^https?:\/\/.+\.git$/i.test(source) || /github\.com[:/].+\/.+/i.test(source)) {
|
|
22
|
+
return "git";
|
|
23
|
+
}
|
|
24
|
+
if (/^https?:\/\//i.test(source)) {
|
|
25
|
+
return "url";
|
|
26
|
+
}
|
|
27
|
+
return "npm";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveInstallRequest(text) {
|
|
31
|
+
const raw = String(text || "").trim();
|
|
32
|
+
const urlMatch = raw.match(/https?:\/\/[^\s]+/i);
|
|
33
|
+
if (urlMatch) {
|
|
34
|
+
return {
|
|
35
|
+
source: urlMatch[0],
|
|
36
|
+
kind: classifySource(urlMatch[0]),
|
|
37
|
+
label: urlMatch[0]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const normalized = raw.toLowerCase();
|
|
42
|
+
const aliases = [
|
|
43
|
+
{
|
|
44
|
+
match: (value) => /\btelegramm?\s+bot\b|\btelegramm?-bot\b/.test(value) || hasApproxPhrase(value, "telegram bot"),
|
|
45
|
+
source: "node-telegram-bot-api",
|
|
46
|
+
label: "Telegram Bot API"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
match: (value) => /\bplaywright\b/.test(value) || hasApproxWord(value, ["playwright"]) || hasApproxPhrase(value, "playwright"),
|
|
50
|
+
source: "playwright",
|
|
51
|
+
label: "Playwright"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
match: (value) => /\bdiscord bot\b|\bdiscord\.js\b/.test(value) || hasApproxPhrase(value, "discord bot"),
|
|
55
|
+
source: "discord.js",
|
|
56
|
+
label: "Discord.js"
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
for (const alias of aliases) {
|
|
61
|
+
if (alias.match(normalized)) {
|
|
62
|
+
return {
|
|
63
|
+
source: alias.source,
|
|
64
|
+
kind: classifySource(alias.source),
|
|
65
|
+
label: alias.label
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const packageMatch = normalized.match(/\b(?:installier(?:e)?|install|installiere|hol dir|lade|besorg dir)\b[\s:,-]*([@a-z0-9._/-]+)\b/i);
|
|
71
|
+
const fuzzyInstallVerb = hasApproxWord(normalized, ["installieren", "installier", "install", "insallieren", "instaliier"]);
|
|
72
|
+
if (packageMatch) {
|
|
73
|
+
return {
|
|
74
|
+
source: packageMatch[1],
|
|
75
|
+
kind: classifySource(packageMatch[1]),
|
|
76
|
+
label: packageMatch[1]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (fuzzyInstallVerb) {
|
|
81
|
+
const pkgCandidate = normalized
|
|
82
|
+
.split(/\s+/)
|
|
83
|
+
.find((token) => /^[@a-z0-9._/-]+$/.test(token) && !hasApproxWord(token, ["installieren", "installier", "install", "den", "die", "das", "dir", "bot"]));
|
|
84
|
+
if (pkgCandidate) {
|
|
85
|
+
return {
|
|
86
|
+
source: pkgCandidate,
|
|
87
|
+
kind: classifySource(pkgCandidate),
|
|
88
|
+
label: pkgCandidate
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function npmTargetPath(source) {
|
|
97
|
+
return path.join(EXTENSIONS_DIR, "node_modules", source);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function isInstalled(source) {
|
|
101
|
+
const kind = classifySource(source);
|
|
102
|
+
if (kind === "npm") {
|
|
103
|
+
return fs.existsSync(npmTargetPath(source));
|
|
104
|
+
}
|
|
105
|
+
if (kind === "git") {
|
|
106
|
+
const repoName = source.split("/").pop().replace(/\.git$/i, "") || "";
|
|
107
|
+
return repoName ? fs.existsSync(path.join(EXTENSIONS_DIR, repoName)) : false;
|
|
108
|
+
}
|
|
109
|
+
if (kind === "url") {
|
|
110
|
+
const safeName = source.replace(/[^\w.-]+/g, "_").slice(0, 80);
|
|
111
|
+
return fs.existsSync(path.join(EXTENSIONS_DIR, safeName));
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function installSource(source) {
|
|
117
|
+
const kind = classifySource(source);
|
|
118
|
+
const gitBinary = process.platform === "win32" ? "git.exe" : "git";
|
|
119
|
+
const npmBinary = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
120
|
+
|
|
121
|
+
if (kind === "git") {
|
|
122
|
+
const repoName = source.split("/").pop().replace(/\.git$/i, "") || `repo-${Date.now()}`;
|
|
123
|
+
const target = path.join(EXTENSIONS_DIR, repoName);
|
|
124
|
+
if (!fs.existsSync(target)) {
|
|
125
|
+
await execFileAsync(gitBinary, ["clone", source, target], { cwd: EXTENSIONS_DIR });
|
|
126
|
+
}
|
|
127
|
+
return { kind, source, target };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (kind === "url") {
|
|
131
|
+
const safeName = source.replace(/[^\w.-]+/g, "_").slice(0, 80);
|
|
132
|
+
const target = path.join(EXTENSIONS_DIR, safeName);
|
|
133
|
+
const script = [
|
|
134
|
+
`$ProgressPreference='SilentlyContinue'`,
|
|
135
|
+
`Invoke-WebRequest -UseBasicParsing -Uri '${source}' -OutFile '${target}'`
|
|
136
|
+
].join("; ");
|
|
137
|
+
await execFileAsync("powershell.exe", ["-NoProfile", "-Command", script], { cwd: EXTENSIONS_DIR });
|
|
138
|
+
return { kind, source, target };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (kind === "npm" && isInstalled(source)) {
|
|
142
|
+
return { kind, source, target: npmTargetPath(source), alreadyInstalled: true };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (process.platform === "win32") {
|
|
146
|
+
const command = `${npmBinary} install ${source}`;
|
|
147
|
+
await execFileAsync("cmd.exe", ["/d", "/s", "/c", command], { cwd: EXTENSIONS_DIR });
|
|
148
|
+
} else {
|
|
149
|
+
await execFileAsync(npmBinary, ["install", source], { cwd: EXTENSIONS_DIR });
|
|
150
|
+
}
|
|
151
|
+
return { kind, source, target: path.join(EXTENSIONS_DIR, "node_modules", source) };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = {
|
|
155
|
+
EXTENSIONS_DIR,
|
|
156
|
+
classifySource,
|
|
157
|
+
resolveInstallRequest,
|
|
158
|
+
isInstalled,
|
|
159
|
+
installSource
|
|
160
|
+
};
|
package/job-manager.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const crypto = require("crypto");
|
|
5
|
+
|
|
6
|
+
const BLUN_HOME = process.env.BLUN_HOME || path.join(os.homedir(), ".blun");
|
|
7
|
+
const DATA_DIR = path.join(BLUN_HOME, "data");
|
|
8
|
+
const JOBS_FILE = path.join(DATA_DIR, "jobs.json");
|
|
9
|
+
|
|
10
|
+
function ensureDataDir() {
|
|
11
|
+
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readJobs() {
|
|
15
|
+
ensureDataDir();
|
|
16
|
+
if (!fs.existsSync(JOBS_FILE)) return [];
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(fs.readFileSync(JOBS_FILE, "utf8"));
|
|
19
|
+
} catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function writeJobs(jobs) {
|
|
25
|
+
ensureDataDir();
|
|
26
|
+
fs.writeFileSync(JOBS_FILE, JSON.stringify(jobs, null, 2), "utf8");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function makeId() {
|
|
30
|
+
return `job_${Date.now()}_${crypto.randomBytes(4).toString("hex")}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createJob({ endpoint, sessionId, payload, mode = "agent", resumedFrom = null }) {
|
|
34
|
+
const jobs = readJobs();
|
|
35
|
+
const job = {
|
|
36
|
+
id: makeId(),
|
|
37
|
+
endpoint,
|
|
38
|
+
mode,
|
|
39
|
+
sessionId: String(sessionId || "default"),
|
|
40
|
+
payload: payload || {},
|
|
41
|
+
status: "queued",
|
|
42
|
+
resumedFrom,
|
|
43
|
+
createdAt: Date.now(),
|
|
44
|
+
updatedAt: Date.now(),
|
|
45
|
+
startedAt: null,
|
|
46
|
+
finishedAt: null,
|
|
47
|
+
error: null,
|
|
48
|
+
result: null,
|
|
49
|
+
logs: []
|
|
50
|
+
};
|
|
51
|
+
jobs.unshift(job);
|
|
52
|
+
writeJobs(jobs.slice(0, 200));
|
|
53
|
+
return job;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function summarizeJob(job) {
|
|
57
|
+
const result = job?.result || {};
|
|
58
|
+
return {
|
|
59
|
+
id: job?.id || "",
|
|
60
|
+
status: job?.status || "unknown",
|
|
61
|
+
endpoint: job?.endpoint || "",
|
|
62
|
+
taskType: result.task_type || "",
|
|
63
|
+
durationMs: job?.startedAt && job?.finishedAt ? Math.max(0, job.finishedAt - job.startedAt) : null,
|
|
64
|
+
fileCount: Array.isArray(result.files) ? result.files.length : 0,
|
|
65
|
+
referenceCount: Array.isArray(result.references) ? result.references.length : 0,
|
|
66
|
+
artifact: result.artifact?.filename || null,
|
|
67
|
+
error: job?.error || null
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function updateJob(jobId, patch = {}) {
|
|
72
|
+
const jobs = readJobs();
|
|
73
|
+
const index = jobs.findIndex((job) => job.id === jobId);
|
|
74
|
+
if (index === -1) return null;
|
|
75
|
+
jobs[index] = {
|
|
76
|
+
...jobs[index],
|
|
77
|
+
...patch,
|
|
78
|
+
updatedAt: Date.now()
|
|
79
|
+
};
|
|
80
|
+
writeJobs(jobs);
|
|
81
|
+
return jobs[index];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getJob(jobId) {
|
|
85
|
+
return readJobs().find((job) => job.id === jobId) || null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function listJobs({ sessionId, limit = 20 } = {}) {
|
|
89
|
+
return readJobs()
|
|
90
|
+
.filter((job) => !sessionId || job.sessionId === sessionId)
|
|
91
|
+
.slice(0, limit);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function cloneJobForResume(jobId) {
|
|
95
|
+
const original = getJob(jobId);
|
|
96
|
+
if (!original) return null;
|
|
97
|
+
return createJob({
|
|
98
|
+
endpoint: original.endpoint,
|
|
99
|
+
sessionId: original.sessionId,
|
|
100
|
+
payload: {
|
|
101
|
+
...original.payload,
|
|
102
|
+
resume_context: {
|
|
103
|
+
resumedFrom: original.id,
|
|
104
|
+
previousStatus: original.status,
|
|
105
|
+
previousError: original.error || "",
|
|
106
|
+
previousAnswer: String(original.result?.answer || "").slice(0, 500),
|
|
107
|
+
previousLogs: getJobLogs(original.id).slice(-10)
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
mode: original.mode,
|
|
111
|
+
resumedFrom: original.id
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function appendJobLog(jobId, event, payload = {}) {
|
|
116
|
+
const jobs = readJobs();
|
|
117
|
+
const index = jobs.findIndex((job) => job.id === jobId);
|
|
118
|
+
if (index === -1) return null;
|
|
119
|
+
|
|
120
|
+
jobs[index].logs = Array.isArray(jobs[index].logs) ? jobs[index].logs : [];
|
|
121
|
+
jobs[index].logs.push({
|
|
122
|
+
ts: Date.now(),
|
|
123
|
+
event,
|
|
124
|
+
payload
|
|
125
|
+
});
|
|
126
|
+
jobs[index].logs = jobs[index].logs.slice(-100);
|
|
127
|
+
jobs[index].updatedAt = Date.now();
|
|
128
|
+
writeJobs(jobs);
|
|
129
|
+
return jobs[index].logs;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function getJobLogs(jobId) {
|
|
133
|
+
return getJob(jobId)?.logs || [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = {
|
|
137
|
+
JOBS_FILE,
|
|
138
|
+
createJob,
|
|
139
|
+
summarizeJob,
|
|
140
|
+
updateJob,
|
|
141
|
+
getJob,
|
|
142
|
+
listJobs,
|
|
143
|
+
cloneJobForResume,
|
|
144
|
+
appendJobLog,
|
|
145
|
+
getJobLogs
|
|
146
|
+
};
|
package/local-data.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const os = require("os");
|
|
4
|
+
const crypto = require("crypto");
|
|
5
|
+
|
|
6
|
+
const BLUN_HOME = process.env.BLUN_HOME || path.join(os.homedir(), ".blun");
|
|
7
|
+
const DATA_DIR = path.join(BLUN_HOME, "data");
|
|
8
|
+
const FACTS_FILE = path.join(DATA_DIR, "facts.json");
|
|
9
|
+
const REFERENCES_DIR = path.join(DATA_DIR, "references");
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
12
|
+
if (!fs.existsSync(REFERENCES_DIR)) fs.mkdirSync(REFERENCES_DIR, { recursive: true });
|
|
13
|
+
|
|
14
|
+
function readFacts() {
|
|
15
|
+
if (!fs.existsSync(FACTS_FILE)) return [];
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(fs.readFileSync(FACTS_FILE, "utf8"));
|
|
18
|
+
} catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function writeFacts(facts) {
|
|
24
|
+
fs.writeFileSync(FACTS_FILE, JSON.stringify(facts, null, 2), "utf8");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function storeFact({ scope = "global", key, value, tags = [] }) {
|
|
28
|
+
const facts = readFacts();
|
|
29
|
+
const record = {
|
|
30
|
+
id: crypto.randomBytes(8).toString("hex"),
|
|
31
|
+
scope,
|
|
32
|
+
key,
|
|
33
|
+
value,
|
|
34
|
+
tags,
|
|
35
|
+
updatedAt: Date.now()
|
|
36
|
+
};
|
|
37
|
+
facts.push(record);
|
|
38
|
+
writeFacts(facts);
|
|
39
|
+
return record;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function findFacts(query = "") {
|
|
43
|
+
const needle = String(query || "").toLowerCase();
|
|
44
|
+
return readFacts().filter((fact) => {
|
|
45
|
+
const haystack = JSON.stringify(fact).toLowerCase();
|
|
46
|
+
return !needle || haystack.includes(needle);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function storeReference(url, payload) {
|
|
51
|
+
const id = crypto.randomBytes(8).toString("hex");
|
|
52
|
+
const safeHost = String(url).replace(/[^a-z0-9]+/gi, "_").slice(0, 80);
|
|
53
|
+
const filePath = path.join(REFERENCES_DIR, `${Date.now()}_${safeHost}_${id}.json`);
|
|
54
|
+
const record = {
|
|
55
|
+
id,
|
|
56
|
+
url,
|
|
57
|
+
storedAt: Date.now(),
|
|
58
|
+
...payload
|
|
59
|
+
};
|
|
60
|
+
fs.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf8");
|
|
61
|
+
return { ...record, filePath };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
DATA_DIR,
|
|
66
|
+
REFERENCES_DIR,
|
|
67
|
+
readFacts,
|
|
68
|
+
storeFact,
|
|
69
|
+
findFacts,
|
|
70
|
+
storeReference
|
|
71
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function sanitizeHistory(history, limit = 12) {
|
|
2
|
+
if (!Array.isArray(history)) return [];
|
|
3
|
+
|
|
4
|
+
return history
|
|
5
|
+
.filter((item) => item && item.role && typeof item.content === "string")
|
|
6
|
+
.slice(-limit)
|
|
7
|
+
.map((item) => ({
|
|
8
|
+
role: item.role,
|
|
9
|
+
content: String(item.content)
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function buildConversationMessages({ prompt, session, history, message }) {
|
|
14
|
+
const messages = [{ role: "system", content: String(prompt || "") }];
|
|
15
|
+
const sourceHistory = Array.isArray(history) && history.length ? history : session?.history;
|
|
16
|
+
|
|
17
|
+
for (const item of sanitizeHistory(sourceHistory)) {
|
|
18
|
+
messages.push(item);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
messages.push({ role: "user", content: String(message || "") });
|
|
22
|
+
return messages;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
sanitizeHistory,
|
|
27
|
+
buildConversationMessages
|
|
28
|
+
};
|
package/noisy-evals.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const runtime = require("./runtime");
|
|
2
|
+
|
|
3
|
+
const CORPUS = [
|
|
4
|
+
{ input: "instaliier dir den telegarm bot", expected: "installation" },
|
|
5
|
+
{ input: "insalieren telegramm bot", expected: "installation" },
|
|
6
|
+
{ input: "kanst du playwrigth instalieren", expected: "installation" },
|
|
7
|
+
{ input: "mach ein screenshoot von der websiete https://example.com", expected: "browser_capture" },
|
|
8
|
+
{ input: "brauch ein screnshot von der website https://example.com", expected: "browser_capture" },
|
|
9
|
+
{ input: "baue mir eine websiete fuer mein studio", expected: "website_builder" },
|
|
10
|
+
{ input: "mach landingpgae fuer nicrazy style", expected: "website_builder" }
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function evaluateNoisyIntentCorpus() {
|
|
14
|
+
const results = CORPUS.map((entry) => {
|
|
15
|
+
const actual = runtime.classifyTask(entry.input, null).type;
|
|
16
|
+
return {
|
|
17
|
+
input: entry.input,
|
|
18
|
+
expected: entry.expected,
|
|
19
|
+
actual,
|
|
20
|
+
passed: actual === entry.expected
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const passed = results.filter((entry) => entry.passed).length;
|
|
25
|
+
const total = results.length;
|
|
26
|
+
return {
|
|
27
|
+
total,
|
|
28
|
+
passed,
|
|
29
|
+
failed: total - passed,
|
|
30
|
+
score: total ? Number((passed / total).toFixed(4)) : 0,
|
|
31
|
+
results
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
CORPUS,
|
|
37
|
+
evaluateNoisyIntentCorpus
|
|
38
|
+
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blun-king-cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "BLUN King CLI —
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "BLUN King CLI — Your local AI assistant powered by Gemma4",
|
|
5
|
+
"type": "commonjs",
|
|
5
6
|
"bin": {
|
|
6
|
-
"blun": "./
|
|
7
|
+
"blun": "./blun-cli.js"
|
|
7
8
|
},
|
|
8
|
-
"
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node api.js",
|
|
11
|
+
"cli": "node blun-cli.js",
|
|
12
|
+
"bot": "node bot.js",
|
|
13
|
+
"test": "node --test tests/*.test.js"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"express": "^5.1.0",
|
|
17
|
+
"node-telegram-bot-api": "^0.66.0",
|
|
18
|
+
"playwright": "^1.53.0"
|
|
19
|
+
},
|
|
20
|
+
"keywords": ["ai", "cli", "blun", "gemma", "local-ai"],
|
|
21
|
+
"author": "BLUN AI",
|
|
9
22
|
"license": "MIT"
|
|
10
23
|
}
|