blun-king-cli 5.3.7 → 6.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 +100 -0
- package/lib/auth.js +162 -0
- package/lib/chat.js +82 -0
- package/lib/client.js +65 -0
- package/lib/ui.js +215 -0
- package/package.json +14 -18
- package/api.js +0 -965
- package/blun-cli.js +0 -3534
- package/blunking-api.js +0 -7
- package/bot.js +0 -188
- package/browser-controller.js +0 -102
- package/chat-memory.js +0 -103
- package/file-helper.js +0 -63
- package/fuzzy-match.js +0 -78
- package/identities.js +0 -106
- package/installer.js +0 -160
- package/job-manager.js +0 -146
- package/local-data.js +0 -71
- package/message-builder.js +0 -28
- package/noisy-evals.js +0 -38
- package/palace-memory.js +0 -246
- package/reference-inspector.js +0 -256
- package/runtime.js +0 -569
- package/task-executor.js +0 -104
- package/tests/browser-controller.test.js +0 -47
- package/tests/cli.test.js +0 -93
- package/tests/file-helper.test.js +0 -18
- package/tests/installer.test.js +0 -39
- package/tests/job-manager.test.js +0 -99
- package/tests/merge-compat.test.js +0 -77
- package/tests/messages.test.js +0 -23
- package/tests/noisy-evals.test.js +0 -12
- package/tests/noisy-intent-corpus.test.js +0 -45
- package/tests/reference-inspector.test.js +0 -42
- package/tests/runtime.test.js +0 -119
- package/tests/task-executor.test.js +0 -40
- package/tests/tools.test.js +0 -23
- package/tests/user-profile.test.js +0 -66
- package/tests/website-builder.test.js +0 -66
- package/tmp-build-smoke/nicrazy-landing/index.html +0 -53
- package/tmp-build-smoke/nicrazy-landing/style.css +0 -110
- 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 +0 -66
- package/tmp-smoke/nicrazy-landing/style.css +0 -104
- package/tools.js +0 -177
- package/user-profile.js +0 -395
- package/website-builder.js +0 -394
- package/website-shot-1776010648230.png +0 -0
- package/website_builder.txt +0 -38
package/installer.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
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
|
-
};
|
package/message-builder.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
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/palace-memory.js
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const os = require("os");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const { execFileSync } = require("child_process");
|
|
5
|
-
|
|
6
|
-
const BLUN_HOME = process.env.BLUN_HOME || path.join(os.homedir(), ".blun");
|
|
7
|
-
const PALACE_BASE = path.join(BLUN_HOME, "palaces");
|
|
8
|
-
const GLOBAL_PALACE = path.join(PALACE_BASE, "_global");
|
|
9
|
-
|
|
10
|
-
let mempalaceSupport;
|
|
11
|
-
|
|
12
|
-
ensureDir(PALACE_BASE);
|
|
13
|
-
ensurePalaceSkeleton(GLOBAL_PALACE);
|
|
14
|
-
|
|
15
|
-
function ensureDir(dir) {
|
|
16
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function ensurePalaceSkeleton(dir) {
|
|
20
|
-
ensureDir(dir);
|
|
21
|
-
ensureDir(path.join(dir, "memory"));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function safeId(value) {
|
|
25
|
-
return String(value || "global").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getUserPalace(userId) {
|
|
29
|
-
const dir = userId ? path.join(PALACE_BASE, safeId(userId)) : GLOBAL_PALACE;
|
|
30
|
-
ensurePalaceSkeleton(dir);
|
|
31
|
-
maybeInitMempalace(dir);
|
|
32
|
-
return dir;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function getPythonBinary() {
|
|
36
|
-
if (process.env.BLUN_PYTHON) return process.env.BLUN_PYTHON;
|
|
37
|
-
return process.platform === "win32" ? "python" : "python3";
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function detectMempalace() {
|
|
41
|
-
if (mempalaceSupport !== undefined) return mempalaceSupport;
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
execFileSync(getPythonBinary(), ["-m", "mempalace", "--help"], {
|
|
45
|
-
encoding: "utf8",
|
|
46
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
47
|
-
timeout: 4000
|
|
48
|
-
});
|
|
49
|
-
mempalaceSupport = true;
|
|
50
|
-
} catch {
|
|
51
|
-
mempalaceSupport = false;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return mempalaceSupport;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function maybeInitMempalace(dir) {
|
|
58
|
-
if (!detectMempalace()) return false;
|
|
59
|
-
|
|
60
|
-
const marker = path.join(dir, ".mempalace-init");
|
|
61
|
-
if (fs.existsSync(marker)) return true;
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
execFileSync(getPythonBinary(), ["-m", "mempalace", "init", "--yes", "."], {
|
|
65
|
-
cwd: dir,
|
|
66
|
-
encoding: "utf8",
|
|
67
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
68
|
-
timeout: 15000
|
|
69
|
-
});
|
|
70
|
-
fs.writeFileSync(marker, "ok", "utf8");
|
|
71
|
-
return true;
|
|
72
|
-
} catch {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function runMempalace(dir, args, timeout = 10000) {
|
|
78
|
-
return execFileSync(getPythonBinary(), ["-m", "mempalace", ...args], {
|
|
79
|
-
cwd: dir,
|
|
80
|
-
encoding: "utf8",
|
|
81
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
82
|
-
timeout
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function memoryDir(dir) {
|
|
87
|
-
const target = path.join(dir, "memory");
|
|
88
|
-
ensureDir(target);
|
|
89
|
-
return target;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function listMemoryFiles(dir) {
|
|
93
|
-
const target = memoryDir(dir);
|
|
94
|
-
return fs.readdirSync(target)
|
|
95
|
-
.filter((entry) => entry.endsWith(".md"))
|
|
96
|
-
.map((entry) => path.join(target, entry));
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function scoreText(text, query) {
|
|
100
|
-
const haystack = String(text || "").toLowerCase();
|
|
101
|
-
const needles = String(query || "").toLowerCase().split(/\s+/).filter(Boolean);
|
|
102
|
-
if (needles.length === 0) return haystack.length ? 1 : 0;
|
|
103
|
-
return needles.reduce((score, needle) => score + (haystack.includes(needle) ? 1 : 0), 0);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function fallbackSearch(query, limit, userId) {
|
|
107
|
-
const dir = getUserPalace(userId);
|
|
108
|
-
const scored = listMemoryFiles(dir)
|
|
109
|
-
.map((file) => {
|
|
110
|
-
const content = fs.readFileSync(file, "utf8");
|
|
111
|
-
return {
|
|
112
|
-
file,
|
|
113
|
-
content,
|
|
114
|
-
score: scoreText(content, query)
|
|
115
|
-
};
|
|
116
|
-
})
|
|
117
|
-
.filter((entry) => entry.score > 0)
|
|
118
|
-
.sort((a, b) => b.score - a.score || b.file.localeCompare(a.file))
|
|
119
|
-
.slice(0, limit);
|
|
120
|
-
|
|
121
|
-
if (scored.length === 0) return "Keine Ergebnisse.";
|
|
122
|
-
|
|
123
|
-
return scored.map((entry) => {
|
|
124
|
-
const snippet = entry.content.replace(/\s+/g, " ").trim().slice(0, 300);
|
|
125
|
-
return `${path.basename(entry.file)}\n${snippet}`;
|
|
126
|
-
}).join("\n\n");
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function fallbackWakeUp(userId) {
|
|
130
|
-
const dir = getUserPalace(userId);
|
|
131
|
-
const entries = listMemoryFiles(dir)
|
|
132
|
-
.map((file) => ({ file, stat: fs.statSync(file) }))
|
|
133
|
-
.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
|
|
134
|
-
.slice(0, 5)
|
|
135
|
-
.map(({ file }) => {
|
|
136
|
-
const content = fs.readFileSync(file, "utf8").replace(/\s+/g, " ").trim();
|
|
137
|
-
return `- ${path.basename(file)}: ${content.slice(0, 220)}`;
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
return entries.length ? entries.join("\n") : "";
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function fallbackStats(userId) {
|
|
144
|
-
const dir = getUserPalace(userId);
|
|
145
|
-
const files = listMemoryFiles(dir);
|
|
146
|
-
return `Palace local fallback active. Files: ${files.length}`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function appendEntry(dir, filename, text) {
|
|
150
|
-
const file = path.join(memoryDir(dir), filename);
|
|
151
|
-
fs.appendFileSync(file, text, "utf8");
|
|
152
|
-
return file;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function palaceSearch(query, limit = 5, userId) {
|
|
156
|
-
const dir = getUserPalace(userId);
|
|
157
|
-
|
|
158
|
-
if (detectMempalace()) {
|
|
159
|
-
try {
|
|
160
|
-
const out = runMempalace(dir, ["search", String(query || ""), "--results", String(limit)], 10000);
|
|
161
|
-
return out.trim() || "Keine Ergebnisse.";
|
|
162
|
-
} catch {
|
|
163
|
-
return fallbackSearch(query, limit, userId);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return fallbackSearch(query, limit, userId);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function palaceStore(text, source = "misc", userId) {
|
|
171
|
-
const dir = getUserPalace(userId);
|
|
172
|
-
const stamp = new Date();
|
|
173
|
-
const filename = `${stamp.toISOString().slice(0, 10)}_${safeId(source).slice(0, 30) || "misc"}.md`;
|
|
174
|
-
const entry = `\n## ${stamp.toISOString()}\n${String(text || "").trim()}\n`;
|
|
175
|
-
|
|
176
|
-
try {
|
|
177
|
-
appendEntry(dir, filename, entry);
|
|
178
|
-
|
|
179
|
-
if (detectMempalace()) {
|
|
180
|
-
try {
|
|
181
|
-
runMempalace(dir, ["mine", "."], 30000);
|
|
182
|
-
} catch {
|
|
183
|
-
// Local markdown store already succeeded.
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return true;
|
|
188
|
-
} catch {
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function palaceWakeUp(userId) {
|
|
194
|
-
const dir = getUserPalace(userId);
|
|
195
|
-
|
|
196
|
-
if (detectMempalace()) {
|
|
197
|
-
try {
|
|
198
|
-
const out = runMempalace(dir, ["wake-up"], 10000);
|
|
199
|
-
return out.trim();
|
|
200
|
-
} catch {
|
|
201
|
-
return fallbackWakeUp(userId);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return fallbackWakeUp(userId);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function palaceLearn(knowledge, source = "learn", userId) {
|
|
209
|
-
return palaceStore(knowledge, `learn_${source}`, userId);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function palaceLogChat(userId, role, content) {
|
|
213
|
-
const dir = getUserPalace(userId);
|
|
214
|
-
const stamp = new Date();
|
|
215
|
-
const filename = `chat_${stamp.toISOString().slice(0, 10)}.md`;
|
|
216
|
-
const snippet = String(content || "").replace(/\s+/g, " ").trim().slice(0, 1200);
|
|
217
|
-
appendEntry(dir, filename, `\n**${stamp.toISOString().slice(11, 16)} ${role}:** ${snippet}\n`);
|
|
218
|
-
return true;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function palaceStats(userId) {
|
|
222
|
-
const dir = getUserPalace(userId);
|
|
223
|
-
|
|
224
|
-
if (detectMempalace()) {
|
|
225
|
-
try {
|
|
226
|
-
const out = runMempalace(dir, ["status"], 10000);
|
|
227
|
-
return out.trim();
|
|
228
|
-
} catch {
|
|
229
|
-
return fallbackStats(userId);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return fallbackStats(userId);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
module.exports = {
|
|
237
|
-
PALACE_BASE,
|
|
238
|
-
GLOBAL_PALACE,
|
|
239
|
-
getUserPalace,
|
|
240
|
-
palaceSearch,
|
|
241
|
-
palaceStore,
|
|
242
|
-
palaceWakeUp,
|
|
243
|
-
palaceLearn,
|
|
244
|
-
palaceLogChat,
|
|
245
|
-
palaceStats
|
|
246
|
-
};
|