@clutor/cli 0.1.1 → 0.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/c/Makefile +34 -2
- package/c/src/forward.c +354 -0
- package/c/src/forward.h +69 -0
- package/c/src/main.c +337 -6
- package/c/src/meta.c +118 -0
- package/c/src/meta.h +22 -0
- package/c/src/quant.c +360 -0
- package/c/src/quant.h +43 -0
- package/c/src/stream.c +6 -3
- package/c/src/stream.h +2 -0
- package/c/src/tokenizer.c +287 -0
- package/c/src/tokenizer.h +25 -0
- package/c/src/xs.c +215 -0
- package/c/src/xs.h +39 -0
- package/dist/bin.js +2 -1
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-YL63VENN.js → chunk-2FBPYNHP.js} +238 -33
- package/dist/chunk-2FBPYNHP.js.map +1 -0
- package/dist/{chunk-RBSFKJ6L.js → chunk-BZZUO7XX.js} +35 -6
- package/dist/chunk-BZZUO7XX.js.map +1 -0
- package/dist/index.d.ts +26 -7
- package/dist/index.js +8 -2
- package/dist/{repl-OVODHQI5.js → repl-DOWMDXFR.js} +2 -2
- package/package.json +2 -2
- package/dist/chunk-RBSFKJ6L.js.map +0 -1
- package/dist/chunk-YL63VENN.js.map +0 -1
- /package/dist/{repl-OVODHQI5.js.map → repl-DOWMDXFR.js.map} +0 -0
|
@@ -6,6 +6,38 @@ import {
|
|
|
6
6
|
|
|
7
7
|
// src/repl.ts
|
|
8
8
|
import { TOOLS } from "@clutor/core";
|
|
9
|
+
|
|
10
|
+
// src/banner.ts
|
|
11
|
+
var GLYPHS = {
|
|
12
|
+
C: [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2551 ", "\u2588\u2588\u2551 ", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D"],
|
|
13
|
+
L: ["\u2588\u2588\u2557 ", "\u2588\u2588\u2551 ", "\u2588\u2588\u2551 ", "\u2588\u2588\u2551 ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],
|
|
14
|
+
U: ["\u2588\u2588\u2557 \u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
15
|
+
T: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D", " \u2588\u2588\u2551 ", " \u2588\u2588\u2551 ", " \u2588\u2588\u2551 ", " \u255A\u2550\u255D "],
|
|
16
|
+
O: [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
17
|
+
R: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2550\u255D \u255A\u2550\u255D"]
|
|
18
|
+
};
|
|
19
|
+
var WORDMARK = Array.from({ length: 6 }, (_, row) => [..."CLUTOR"].map((ch) => GLYPHS[ch]?.[row] ?? "").join(""));
|
|
20
|
+
var WORDMARK_WIDTH = Math.max(...WORDMARK.map((r) => r.length));
|
|
21
|
+
var GRADIENT = [51, 45, 39, 33, 63, 99];
|
|
22
|
+
function useColor(env = process.env, isTTY = !!process.stdout.isTTY) {
|
|
23
|
+
return isTTY && !("NO_COLOR" in env) && env["TERM"] !== "dumb";
|
|
24
|
+
}
|
|
25
|
+
function banner(o) {
|
|
26
|
+
const paint = (code, s) => o.color ? `\x1B[${typeof code === "number" ? `38;5;${code}` : code}m${s}\x1B[0m` : s;
|
|
27
|
+
const dim = (s) => paint("2", s);
|
|
28
|
+
const wide = (o.columns ?? 80) >= WORDMARK_WIDTH + 2;
|
|
29
|
+
const lines = [""];
|
|
30
|
+
if (wide) lines.push(...WORDMARK.map((r, i) => paint(GRADIENT[i] ?? 99, r)));
|
|
31
|
+
else lines.push(paint(45, "C L U T O R"));
|
|
32
|
+
lines.push("", `${paint("1", "clutor interactive session")} ${dim(`v${o.version}`)}`, dim("big models on small machines, with honest numbers"), "");
|
|
33
|
+
lines.push(` ${dim("folder")} ${o.folder}`, ` ${dim("model")} ${o.model}`, "");
|
|
34
|
+
lines.push(` ${dim("tips")} type a task in plain words and the agent works on this folder`);
|
|
35
|
+
lines.push(` its changes are held aside; nothing is written without your approval`);
|
|
36
|
+
lines.push(` /help lists commands \xB7 /plan <model> \xB7 /cache \xB7 /exit`, "");
|
|
37
|
+
return lines.join("\n");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/repl.ts
|
|
9
41
|
var FORWARD = {
|
|
10
42
|
status: { usage: "/status", help: "machine, model backend, memory plan, receipts" },
|
|
11
43
|
doctor: { usage: "/doctor", help: "check this machine and the configuration" },
|
|
@@ -60,7 +92,7 @@ function helpText() {
|
|
|
60
92
|
|
|
61
93
|
${table(rows)}`;
|
|
62
94
|
}
|
|
63
|
-
async function runRepl(io, run, engine) {
|
|
95
|
+
async function runRepl(io, run, engine, version = "dev") {
|
|
64
96
|
const ask = io.prompt;
|
|
65
97
|
const session = { auto: false, notes: [], usage: { promptTokens: 0, completionTokens: 0 } };
|
|
66
98
|
const load = () => {
|
|
@@ -73,10 +105,7 @@ async function runRepl(io, run, engine) {
|
|
|
73
105
|
};
|
|
74
106
|
const cfg = load();
|
|
75
107
|
const model = (c) => !c ? "not configured" : c.backend.kind === "openai" ? `${c.backend.model ?? "default"} at ${c.backend.baseUrl}` : c.backend.llamaServer.model ?? "not configured (set backend.llamaServer.model)";
|
|
76
|
-
io.out(
|
|
77
|
-
folder ${io.cwd}
|
|
78
|
-
model ${model(cfg)}
|
|
79
|
-
Type a task, or /help for commands. Nothing is written to your files without your approval.`);
|
|
108
|
+
io.out(banner({ version, folder: io.cwd, model: model(cfg), columns: process.stdout.columns ?? 80, color: useColor() }));
|
|
80
109
|
try {
|
|
81
110
|
for (; ; ) {
|
|
82
111
|
const line = (await ask("clt> ")).trim();
|
|
@@ -175,4 +204,4 @@ export {
|
|
|
175
204
|
splitWords,
|
|
176
205
|
runRepl
|
|
177
206
|
};
|
|
178
|
-
//# sourceMappingURL=chunk-
|
|
207
|
+
//# sourceMappingURL=chunk-BZZUO7XX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/repl.ts","../src/banner.ts"],"sourcesContent":["import { TOOLS } from \"@clutor/core\";\nimport { loadConfig, ConfigError, type ClutorConfig } from \"./config\";\nimport { banner, useColor } from \"./banner\";\nimport { table } from \"./format\";\nimport type { Io, SessionState } from \"./cli\";\n\n/**\n * The interactive session that `clutor` opens when it is started with no command in a terminal. Text you type is a task for the coding\n * agent (its changes are held aside until you approve them); a line starting with `/` is a command. Most commands are the ordinary\n * clutor commands, so they behave exactly as they do outside the session.\n */\n\ntype Run = (argv: string[], session: SessionState) => Promise<number>;\ntype EngineView = (config: ClutorConfig, what: \"layers\" | \"cache\") => Promise<void>;\n\ninterface Slash { usage: string; help: string }\n\n/** Commands that are forwarded unchanged to the normal CLI. */\nconst FORWARD: Record<string, Slash> = {\n status: { usage: \"/status\", help: \"machine, model backend, memory plan, receipts\" },\n doctor: { usage: \"/doctor\", help: \"check this machine and the configuration\" },\n models: { usage: \"/models\", help: \"model catalog and the .gguf files in your models folder\" },\n plan: { usage: \"/plan <model> [--ram GiB]\", help: \"will it fit, where the memory goes, how fast it could possibly run\" },\n simulate: { usage: \"/simulate <model> [--cache GiB]\", help: \"replay a routing trace through the expert cache (synthetic)\" },\n inspect: { usage: \"/inspect <file.gguf>\", help: \"read a model file's header\" },\n bench: { usage: \"/bench [--file model.gguf]\", help: \"measure real disk reads; with the engine, compare cache strategies\" },\n stream: { usage: \"/stream <model.gguf>\", help: \"stream a model's experts through the native engine (synthetic routing)\" },\n pull: { usage: \"/pull [id]\", help: \"list or download a small test model\" },\n init: { usage: \"/init\", help: \"create clutor.config.json and a models folder here\" },\n backend: { usage: \"/backend\", help: \"show how llama-server would be started\" },\n ledger: { usage: \"/ledger\", help: \"verify the receipt log and list recent tasks and checkpoints\" },\n decide: { usage: '/decide --prompt \"<text>\" --choices \"a,b\"', help: \"one forward pass: probability of each choice\" },\n};\n\nconst OWN: Record<string, Slash> = {\n help: { usage: \"/help\", help: \"this list\" },\n model: { usage: \"/model\", help: \"which model this session talks to\" },\n verify: { usage: \"/verify [command|off]\", help: \"run this check after every task and only offer changes that pass it\" },\n yes: { usage: \"/yes\", help: \"toggle: run project commands without asking (only for projects you trust)\" },\n undo: { usage: \"/undo\", help: \"restore the files changed by the last applied task\" },\n tools: { usage: \"/tools\", help: \"what the agent can do\" },\n cost: { usage: \"/cost\", help: \"tokens used this session, as reported by the model server\" },\n history: { usage: \"/history\", help: \"tasks done in this session\" },\n clear: { usage: \"/clear\", help: \"forget this session's task history\" },\n layers: { usage: \"/layers\", help: \"where the model file's bytes are, per layer (native engine)\" },\n cache: { usage: \"/cache\", help: \"a streaming pass over the model file: cache, prefetch, disk waits\" },\n exit: { usage: \"/exit\", help: \"leave (also /quit, Ctrl-D)\" },\n};\n\n/** Splits a line into words, honouring single and double quotes. */\nexport function splitWords(line: string): string[] {\n const out: string[] = [];\n let cur = \"\", quote: string | undefined, has = false;\n for (const ch of line) {\n if (quote) { if (ch === quote) quote = undefined; else cur += ch; }\n else if (ch === '\"' || ch === \"'\") { quote = ch; has = true; }\n else if (/\\s/.test(ch)) { if (cur || has) out.push(cur); cur = \"\"; has = false; }\n else cur += ch;\n }\n if (cur || has) out.push(cur);\n return out;\n}\n\nfunction helpText(): string {\n const rows = [...Object.values(OWN), ...Object.values(FORWARD)].map((c) => [c.usage, c.help]);\n return `Type a task in plain words and the agent works on this folder. Its changes are held aside; you see the diff and approve before anything is written.\\n\\n${table(rows)}`;\n}\n\nexport async function runRepl(io: Io, run: Run, engine: EngineView, version = \"dev\"): Promise<number> {\n const ask = io.prompt as NonNullable<Io[\"prompt\"]>;\n const session: SessionState = { auto: false, notes: [], usage: { promptTokens: 0, completionTokens: 0 } };\n const load = (): ClutorConfig | undefined => {\n try { return loadConfig(io.cwd).config; } catch (e) { io.err(e instanceof ConfigError || e instanceof Error ? e.message : String(e)); return undefined; }\n };\n const cfg = load();\n const model = (c: ClutorConfig | undefined): string => !c ? \"not configured\" : c.backend.kind === \"openai\" ? `${c.backend.model ?? \"default\"} at ${c.backend.baseUrl}` : c.backend.llamaServer.model ?? \"not configured (set backend.llamaServer.model)\";\n io.out(banner({ version, folder: io.cwd, model: model(cfg), columns: process.stdout.columns ?? 80, color: useColor() }));\n\n try {\n for (;;) {\n const line = (await ask(\"clt> \")).trim();\n if (!line) continue;\n if (!line.startsWith(\"/\")) { await guarded(io, () => run([\"code\", line, \"--dir\", io.cwd], session)); continue; }\n\n const [word = \"\", ...rest] = splitWords(line.slice(1));\n const name = word.toLowerCase();\n if (name === \"exit\" || name === \"quit\") return 0;\n if (name === \"help\" || name === \"?\") { io.out(helpText()); continue; }\n if (name === \"model\") { io.out(`model: ${model(load())}${session.backend ? \" (running)\" : \"\"}\\nverify: ${session.verify ?? \"none\"} auto-approve: ${session.auto ? \"on\" : \"off\"}`); continue; }\n if (name === \"verify\") {\n const v = rest.join(\" \").trim();\n if (!v) io.out(session.verify ? `verify: ${session.verify}` : \"verify: none. Set one with /verify npm test\");\n else if (v === \"off\") { delete session.verify; io.out(\"verify: off\"); }\n else { session.verify = v; io.out(`verify: ${v}. It runs in a scratch copy after each task; changes are only offered when it passes.`); }\n continue;\n }\n if (name === \"yes\") { session.auto = !session.auto; io.out(session.auto ? \"auto-approve ON: commands from the agent run without asking.\" : \"auto-approve off.\"); continue; }\n if (name === \"undo\") {\n if (!session.lastCheckpoint) { io.out(\"Nothing to undo in this session.\"); continue; }\n const { id, dir } = session.lastCheckpoint;\n if (await guarded(io, () => run([\"rollback\", id, \"--dir\", dir], session)) === 0) delete session.lastCheckpoint;\n continue;\n }\n if (name === \"tools\") { io.out(table(TOOLS.map((t) => [t.name, t.description.split(\". \")[0] ?? \"\"]))); continue; }\n if (name === \"cost\") {\n const u = session.usage;\n io.out(`${u.promptTokens} prompt + ${u.completionTokens} generated tokens this session (reported by the model server; local models cost no money).`);\n continue;\n }\n if (name === \"history\") { io.out(session.notes.length ? session.notes.map((n, i) => `${i + 1}. ${n}`).join(\"\\n\") : \"No tasks yet.\"); continue; }\n if (name === \"clear\") { session.notes.length = 0; io.out(\"(session history cleared)\"); continue; }\n if (name === \"layers\" || name === \"cache\") {\n const c = load();\n if (c) await guarded(io, async () => { await engine(c, name); return 0; });\n continue;\n }\n if (FORWARD[name]) { await guarded(io, () => run([name, ...rest], session)); continue; }\n io.err(`unknown command /${word}. /help lists them.`);\n }\n } finally {\n try { await session.backend?.stop(); } catch { /* the server may already be gone */ }\n }\n}\n\nasync function guarded(io: Io, f: () => Promise<number>): Promise<number | undefined> {\n try { return await f(); } catch (e) { io.err(e instanceof Error ? e.message : String(e)); return undefined; }\n}\n","/**\n * The welcome screen of the interactive session: the name in large block letters, then where you are and what to type.\n * Colour is used only on a terminal that asked for it (not when piped, not with NO_COLOR); the text is the same either way.\n */\n\nconst GLYPHS: Record<string, string[]> = {\n C: [\" ██████╗\", \"██╔════╝\", \"██║ \", \"██║ \", \"╚██████╗\", \" ╚═════╝\"],\n L: [\"██╗ \", \"██║ \", \"██║ \", \"██║ \", \"███████╗\", \"╚══════╝\"],\n U: [\"██╗ ██╗\", \"██║ ██║\", \"██║ ██║\", \"██║ ██║\", \"╚██████╔╝\", \" ╚═════╝ \"],\n T: [\"████████╗\", \"╚══██╔══╝\", \" ██║ \", \" ██║ \", \" ██║ \", \" ╚═╝ \"],\n O: [\" ██████╗ \", \"██╔═══██╗\", \"██║ ██║\", \"██║ ██║\", \"╚██████╔╝\", \" ╚═════╝ \"],\n R: [\"██████╗ \", \"██╔══██╗\", \"██████╔╝\", \"██╔══██╗\", \"██║ ██║\", \"╚═╝ ╚═╝\"],\n};\n\n/** The six rows of \"CLUTOR\" in block letters. */\nexport const WORDMARK: string[] = Array.from({ length: 6 }, (_, row) => [...\"CLUTOR\"].map((ch) => GLYPHS[ch]?.[row] ?? \"\").join(\"\"));\nexport const WORDMARK_WIDTH = Math.max(...WORDMARK.map((r) => r.length));\n\n/** Top to bottom: cyan, through blue, to violet (xterm 256-colour codes). */\nconst GRADIENT = [51, 45, 39, 33, 63, 99];\n\nexport interface BannerOptions {\n version: string;\n folder: string;\n model: string;\n /** Width of the terminal in columns; a narrower terminal than the block letters gets a one-line title. */\n columns?: number;\n color?: boolean;\n}\n\nexport function useColor(env: NodeJS.ProcessEnv = process.env, isTTY: boolean = !!process.stdout.isTTY): boolean {\n return isTTY && !(\"NO_COLOR\" in env) && env[\"TERM\"] !== \"dumb\";\n}\n\nexport function banner(o: BannerOptions): string {\n const paint = (code: number | string, s: string): string => (o.color ? `\\x1b[${typeof code === \"number\" ? `38;5;${code}` : code}m${s}\\x1b[0m` : s);\n const dim = (s: string): string => paint(\"2\", s);\n const wide = (o.columns ?? 80) >= WORDMARK_WIDTH + 2;\n const lines: string[] = [\"\"];\n if (wide) lines.push(...WORDMARK.map((r, i) => paint(GRADIENT[i] ?? 99, r)));\n else lines.push(paint(45, \"C L U T O R\"));\n lines.push(\"\", `${paint(\"1\", \"clutor interactive session\")} ${dim(`v${o.version}`)}`, dim(\"big models on small machines, with honest numbers\"), \"\");\n lines.push(` ${dim(\"folder\")} ${o.folder}`, ` ${dim(\"model\")} ${o.model}`, \"\");\n lines.push(` ${dim(\"tips\")} type a task in plain words and the agent works on this folder`);\n lines.push(` its changes are held aside; nothing is written without your approval`);\n lines.push(` /help lists commands · /plan <model> · /cache · /exit`, \"\");\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -211,16 +211,13 @@ interface LocalModel {
|
|
|
211
211
|
bytes: number;
|
|
212
212
|
}
|
|
213
213
|
declare function listLocalModels(dir: string): Promise<LocalModel[]>;
|
|
214
|
-
/**
|
|
215
|
-
* Real models small enough to download and test with. Sizes are the file sizes Hugging Face reported on 2026-10-30; the download
|
|
216
|
-
* is checked against the size the server announces, and nothing is fetched unless you run `clutor pull`.
|
|
217
|
-
*/
|
|
218
214
|
interface Download {
|
|
219
215
|
id: string;
|
|
220
216
|
catalogId: string;
|
|
221
217
|
repo: string;
|
|
222
218
|
file: string;
|
|
223
219
|
bytes: number;
|
|
220
|
+
sha256: string;
|
|
224
221
|
note: string;
|
|
225
222
|
}
|
|
226
223
|
declare const DOWNLOADS: Download[];
|
|
@@ -228,10 +225,15 @@ declare const downloadUrl: (d: Download) => string;
|
|
|
228
225
|
declare class DownloadError extends Error {
|
|
229
226
|
constructor(message: string);
|
|
230
227
|
}
|
|
231
|
-
/**
|
|
228
|
+
/**
|
|
229
|
+
* Downloads `url` to `dest` through a `.part` file. An interrupted download keeps its `.part` and the next call resumes it with a Range request. It refuses
|
|
230
|
+
* to start without room for the rest, refuses a size the server did not announce, and (with `expectedSha256`) deletes the file and fails unless the whole
|
|
231
|
+
* file hashes to the published value.
|
|
232
|
+
*/
|
|
232
233
|
declare function downloadFile(url: string, dest: string, opts?: {
|
|
233
234
|
fetch?: typeof fetch;
|
|
234
235
|
expectedBytes?: number;
|
|
236
|
+
expectedSha256?: string;
|
|
235
237
|
onProgress?: (done: number, total: number) => void;
|
|
236
238
|
}): Promise<number>;
|
|
237
239
|
|
|
@@ -389,6 +391,7 @@ interface Rpc {
|
|
|
389
391
|
}
|
|
390
392
|
declare function handleMcp(req: Rpc, ctx: {
|
|
391
393
|
isRoot(dir: string): Promise<string | null>;
|
|
394
|
+
version?: string;
|
|
392
395
|
}): Promise<{
|
|
393
396
|
status: number;
|
|
394
397
|
body: unknown;
|
|
@@ -396,6 +399,8 @@ declare function handleMcp(req: Rpc, ctx: {
|
|
|
396
399
|
|
|
397
400
|
interface Io {
|
|
398
401
|
out(line: string): void;
|
|
402
|
+
/** Writes text with no newline added (a model's reply as it is generated). Falls back to `out` when absent. */
|
|
403
|
+
write?(text: string): void;
|
|
399
404
|
err(line: string): void;
|
|
400
405
|
cwd: string;
|
|
401
406
|
/** Asks the user a question and returns the answer. Absent when there is no terminal. */
|
|
@@ -436,7 +441,7 @@ type Run = (argv: string[], session: SessionState) => Promise<number>;
|
|
|
436
441
|
type EngineView = (config: ClutorConfig, what: "layers" | "cache") => Promise<void>;
|
|
437
442
|
/** Splits a line into words, honouring single and double quotes. */
|
|
438
443
|
declare function splitWords(line: string): string[];
|
|
439
|
-
declare function runRepl(io: Io, run: Run, engine: EngineView): Promise<number>;
|
|
444
|
+
declare function runRepl(io: Io, run: Run, engine: EngineView, version?: string): Promise<number>;
|
|
440
445
|
|
|
441
446
|
/** Where llama-server saves KV-cache snapshots. They contain the project map the model read, so they stay in the user's own home folder. */
|
|
442
447
|
declare const slotDir: () => string;
|
|
@@ -508,6 +513,8 @@ interface StreamOptions {
|
|
|
508
513
|
computeUs?: number;
|
|
509
514
|
windowPct?: number;
|
|
510
515
|
noOsCache?: boolean;
|
|
516
|
+
/** A route trace recorded by `generate --trace`: the model's real router scores, replayed instead of the synthetic router. */
|
|
517
|
+
trace?: string;
|
|
511
518
|
/** auto: keep the dense weights in RAM when they fit in a quarter of it; ring: re-read two layers at a time every token. */
|
|
512
519
|
dense?: "auto" | "resident" | "ring";
|
|
513
520
|
/** kernel: a Q4_0 x Q8_0 dot-product kernel over every loaded byte (default); checksum: a cheap hash, so the run measures I/O alone. */
|
|
@@ -610,6 +617,18 @@ interface StreamReport {
|
|
|
610
617
|
}
|
|
611
618
|
declare function engineInfo(bin: string, file: string): Promise<EngineInfo>;
|
|
612
619
|
declare function engineStream(bin: string, file: string, o?: StreamOptions, timeoutMs?: number): Promise<StreamReport>;
|
|
620
|
+
/** Runs an engine command whose output should reach the user as it is produced (`generate` writes each token as it is chosen). */
|
|
621
|
+
declare function engineRun(bin: string, args: string[], onOut: (chunk: string) => void, timeoutMs?: number): Promise<{
|
|
622
|
+
code: number;
|
|
623
|
+
stderr: string;
|
|
624
|
+
}>;
|
|
625
|
+
/** The platform name used in release asset names, or undefined where no prebuilt binary is published. */
|
|
626
|
+
declare function enginePlatform(): string | undefined;
|
|
627
|
+
/**
|
|
628
|
+
* Installs the prebuilt engine for this platform from the GitHub release of `version`: downloads SHA256SUMS and the tarball, refuses it unless the
|
|
629
|
+
* SHA-256 matches, unpacks it and puts the binary in `dest` (default ~/.clutor/bin). Returns the path of the installed binary.
|
|
630
|
+
*/
|
|
631
|
+
declare function installPrebuilt(version: string, dest?: string): Promise<string>;
|
|
613
632
|
|
|
614
|
-
export { AgentSession, BackendManager, CONFIG_FILE, ConfigError, ConfigSchema, DEFAULT_PORT, DOWNLOADS, DecideError, DownloadError, EngineError, ModelError, SlotSnapshots, UsageError, agentLlm, buildEngine, buildLlamaArgs, createClutorServer, downloadFile, downloadUrl, engineInfo, engineSourceDir, engineStream, ensureSlotDir, findEngine, findLlamaServer, flagBool, flagInt, flagString, formatGates, formatPlan, formatSimulation, fromOpenAiReply, handleMcp, isInside, listLocalModels, loadConfig, main, normaliseRoot, parseArgs, parseSse, pct, probeLlamaServer, resolveModel, run, runDecide, runRepl, sealDecision, slotDir, splitWords, startBackendIfConfigured, streamToOllama, table, toOpenAiBody, tps, version, withPrefixSnapshots };
|
|
633
|
+
export { AgentSession, BackendManager, CONFIG_FILE, ConfigError, ConfigSchema, DEFAULT_PORT, DOWNLOADS, DecideError, DownloadError, EngineError, ModelError, SlotSnapshots, UsageError, agentLlm, buildEngine, buildLlamaArgs, createClutorServer, downloadFile, downloadUrl, engineInfo, enginePlatform, engineRun, engineSourceDir, engineStream, ensureSlotDir, findEngine, findLlamaServer, flagBool, flagInt, flagString, formatGates, formatPlan, formatSimulation, fromOpenAiReply, handleMcp, installPrebuilt, isInside, listLocalModels, loadConfig, main, normaliseRoot, parseArgs, parseSse, pct, probeLlamaServer, resolveModel, run, runDecide, runRepl, sealDecision, slotDir, splitWords, startBackendIfConfigured, streamToOllama, table, toOpenAiBody, tps, version, withPrefixSnapshots };
|
|
615
634
|
export type { BackendState, ClutorConfig, ClutorServer, DecideEndpoint, DecideResult, Download, EngineInfo, Io, LlamaArgsInput, LlamaCapabilities, LocalModel, OllamaChatRequest, OllamaMessage, ParsedArgs, ResolvedModel, ServerOptions, SessionEvent, SessionInfo, SessionState, StreamOptions, StreamReport };
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
downloadFile,
|
|
17
17
|
downloadUrl,
|
|
18
18
|
engineInfo,
|
|
19
|
+
enginePlatform,
|
|
20
|
+
engineRun,
|
|
19
21
|
engineSourceDir,
|
|
20
22
|
engineStream,
|
|
21
23
|
ensureSlotDir,
|
|
@@ -26,6 +28,7 @@ import {
|
|
|
26
28
|
flagString,
|
|
27
29
|
fromOpenAiReply,
|
|
28
30
|
handleMcp,
|
|
31
|
+
installPrebuilt,
|
|
29
32
|
isInside,
|
|
30
33
|
listLocalModels,
|
|
31
34
|
main,
|
|
@@ -43,11 +46,11 @@ import {
|
|
|
43
46
|
toOpenAiBody,
|
|
44
47
|
version,
|
|
45
48
|
withPrefixSnapshots
|
|
46
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-2FBPYNHP.js";
|
|
47
50
|
import {
|
|
48
51
|
runRepl,
|
|
49
52
|
splitWords
|
|
50
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-BZZUO7XX.js";
|
|
51
54
|
import {
|
|
52
55
|
CONFIG_FILE,
|
|
53
56
|
ConfigError,
|
|
@@ -83,6 +86,8 @@ export {
|
|
|
83
86
|
downloadFile,
|
|
84
87
|
downloadUrl,
|
|
85
88
|
engineInfo,
|
|
89
|
+
enginePlatform,
|
|
90
|
+
engineRun,
|
|
86
91
|
engineSourceDir,
|
|
87
92
|
engineStream,
|
|
88
93
|
ensureSlotDir,
|
|
@@ -96,6 +101,7 @@ export {
|
|
|
96
101
|
formatSimulation,
|
|
97
102
|
fromOpenAiReply,
|
|
98
103
|
handleMcp,
|
|
104
|
+
installPrebuilt,
|
|
99
105
|
isInside,
|
|
100
106
|
listLocalModels,
|
|
101
107
|
loadConfig,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runRepl,
|
|
3
3
|
splitWords
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BZZUO7XX.js";
|
|
5
5
|
import "./chunk-U5GHVJJO.js";
|
|
6
6
|
export {
|
|
7
7
|
runRepl,
|
|
8
8
|
splitWords
|
|
9
9
|
};
|
|
10
|
-
//# sourceMappingURL=repl-
|
|
10
|
+
//# sourceMappingURL=repl-DOWMDXFR.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clutor/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The clutor command and local server: plan whether a model fits your machine, inspect GGUF files, simulate expert streaming, run a sandboxed coding agent, and serve an OpenAI- and Ollama-compatible API on localhost in front of llama.cpp.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
|
|
14
14
|
"files": ["dist", "c/src", "c/Makefile", "README.md", "LICENSE"],
|
|
15
15
|
"engines": { "node": ">=20" },
|
|
16
|
-
"dependencies": { "@clutor/core": "^0.
|
|
16
|
+
"dependencies": { "@clutor/core": "^0.2.0", "zod": "^4.0.0" },
|
|
17
17
|
"scripts": { "prepack": "node ../../scripts/pack-engine.mjs", "postpack": "node ../../scripts/pack-engine.mjs clean", "build": "tsup", "typecheck": "tsc --noEmit", "test": "vitest run" }
|
|
18
18
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/repl.ts"],"sourcesContent":["import { TOOLS } from \"@clutor/core\";\nimport { loadConfig, ConfigError, type ClutorConfig } from \"./config\";\nimport { table } from \"./format\";\nimport type { Io, SessionState } from \"./cli\";\n\n/**\n * The interactive session that `clutor` opens when it is started with no command in a terminal. Text you type is a task for the coding\n * agent (its changes are held aside until you approve them); a line starting with `/` is a command. Most commands are the ordinary\n * clutor commands, so they behave exactly as they do outside the session.\n */\n\ntype Run = (argv: string[], session: SessionState) => Promise<number>;\ntype EngineView = (config: ClutorConfig, what: \"layers\" | \"cache\") => Promise<void>;\n\ninterface Slash { usage: string; help: string }\n\n/** Commands that are forwarded unchanged to the normal CLI. */\nconst FORWARD: Record<string, Slash> = {\n status: { usage: \"/status\", help: \"machine, model backend, memory plan, receipts\" },\n doctor: { usage: \"/doctor\", help: \"check this machine and the configuration\" },\n models: { usage: \"/models\", help: \"model catalog and the .gguf files in your models folder\" },\n plan: { usage: \"/plan <model> [--ram GiB]\", help: \"will it fit, where the memory goes, how fast it could possibly run\" },\n simulate: { usage: \"/simulate <model> [--cache GiB]\", help: \"replay a routing trace through the expert cache (synthetic)\" },\n inspect: { usage: \"/inspect <file.gguf>\", help: \"read a model file's header\" },\n bench: { usage: \"/bench [--file model.gguf]\", help: \"measure real disk reads; with the engine, compare cache strategies\" },\n stream: { usage: \"/stream <model.gguf>\", help: \"stream a model's experts through the native engine (synthetic routing)\" },\n pull: { usage: \"/pull [id]\", help: \"list or download a small test model\" },\n init: { usage: \"/init\", help: \"create clutor.config.json and a models folder here\" },\n backend: { usage: \"/backend\", help: \"show how llama-server would be started\" },\n ledger: { usage: \"/ledger\", help: \"verify the receipt log and list recent tasks and checkpoints\" },\n decide: { usage: '/decide --prompt \"<text>\" --choices \"a,b\"', help: \"one forward pass: probability of each choice\" },\n};\n\nconst OWN: Record<string, Slash> = {\n help: { usage: \"/help\", help: \"this list\" },\n model: { usage: \"/model\", help: \"which model this session talks to\" },\n verify: { usage: \"/verify [command|off]\", help: \"run this check after every task and only offer changes that pass it\" },\n yes: { usage: \"/yes\", help: \"toggle: run project commands without asking (only for projects you trust)\" },\n undo: { usage: \"/undo\", help: \"restore the files changed by the last applied task\" },\n tools: { usage: \"/tools\", help: \"what the agent can do\" },\n cost: { usage: \"/cost\", help: \"tokens used this session, as reported by the model server\" },\n history: { usage: \"/history\", help: \"tasks done in this session\" },\n clear: { usage: \"/clear\", help: \"forget this session's task history\" },\n layers: { usage: \"/layers\", help: \"where the model file's bytes are, per layer (native engine)\" },\n cache: { usage: \"/cache\", help: \"a streaming pass over the model file: cache, prefetch, disk waits\" },\n exit: { usage: \"/exit\", help: \"leave (also /quit, Ctrl-D)\" },\n};\n\n/** Splits a line into words, honouring single and double quotes. */\nexport function splitWords(line: string): string[] {\n const out: string[] = [];\n let cur = \"\", quote: string | undefined, has = false;\n for (const ch of line) {\n if (quote) { if (ch === quote) quote = undefined; else cur += ch; }\n else if (ch === '\"' || ch === \"'\") { quote = ch; has = true; }\n else if (/\\s/.test(ch)) { if (cur || has) out.push(cur); cur = \"\"; has = false; }\n else cur += ch;\n }\n if (cur || has) out.push(cur);\n return out;\n}\n\nfunction helpText(): string {\n const rows = [...Object.values(OWN), ...Object.values(FORWARD)].map((c) => [c.usage, c.help]);\n return `Type a task in plain words and the agent works on this folder. Its changes are held aside; you see the diff and approve before anything is written.\\n\\n${table(rows)}`;\n}\n\nexport async function runRepl(io: Io, run: Run, engine: EngineView): Promise<number> {\n const ask = io.prompt as NonNullable<Io[\"prompt\"]>;\n const session: SessionState = { auto: false, notes: [], usage: { promptTokens: 0, completionTokens: 0 } };\n const load = (): ClutorConfig | undefined => {\n try { return loadConfig(io.cwd).config; } catch (e) { io.err(e instanceof ConfigError || e instanceof Error ? e.message : String(e)); return undefined; }\n };\n const cfg = load();\n const model = (c: ClutorConfig | undefined): string => !c ? \"not configured\" : c.backend.kind === \"openai\" ? `${c.backend.model ?? \"default\"} at ${c.backend.baseUrl}` : c.backend.llamaServer.model ?? \"not configured (set backend.llamaServer.model)\";\n io.out(`clutor interactive session\\n folder ${io.cwd}\\n model ${model(cfg)}\\n Type a task, or /help for commands. Nothing is written to your files without your approval.`);\n\n try {\n for (;;) {\n const line = (await ask(\"clt> \")).trim();\n if (!line) continue;\n if (!line.startsWith(\"/\")) { await guarded(io, () => run([\"code\", line, \"--dir\", io.cwd], session)); continue; }\n\n const [word = \"\", ...rest] = splitWords(line.slice(1));\n const name = word.toLowerCase();\n if (name === \"exit\" || name === \"quit\") return 0;\n if (name === \"help\" || name === \"?\") { io.out(helpText()); continue; }\n if (name === \"model\") { io.out(`model: ${model(load())}${session.backend ? \" (running)\" : \"\"}\\nverify: ${session.verify ?? \"none\"} auto-approve: ${session.auto ? \"on\" : \"off\"}`); continue; }\n if (name === \"verify\") {\n const v = rest.join(\" \").trim();\n if (!v) io.out(session.verify ? `verify: ${session.verify}` : \"verify: none. Set one with /verify npm test\");\n else if (v === \"off\") { delete session.verify; io.out(\"verify: off\"); }\n else { session.verify = v; io.out(`verify: ${v}. It runs in a scratch copy after each task; changes are only offered when it passes.`); }\n continue;\n }\n if (name === \"yes\") { session.auto = !session.auto; io.out(session.auto ? \"auto-approve ON: commands from the agent run without asking.\" : \"auto-approve off.\"); continue; }\n if (name === \"undo\") {\n if (!session.lastCheckpoint) { io.out(\"Nothing to undo in this session.\"); continue; }\n const { id, dir } = session.lastCheckpoint;\n if (await guarded(io, () => run([\"rollback\", id, \"--dir\", dir], session)) === 0) delete session.lastCheckpoint;\n continue;\n }\n if (name === \"tools\") { io.out(table(TOOLS.map((t) => [t.name, t.description.split(\". \")[0] ?? \"\"]))); continue; }\n if (name === \"cost\") {\n const u = session.usage;\n io.out(`${u.promptTokens} prompt + ${u.completionTokens} generated tokens this session (reported by the model server; local models cost no money).`);\n continue;\n }\n if (name === \"history\") { io.out(session.notes.length ? session.notes.map((n, i) => `${i + 1}. ${n}`).join(\"\\n\") : \"No tasks yet.\"); continue; }\n if (name === \"clear\") { session.notes.length = 0; io.out(\"(session history cleared)\"); continue; }\n if (name === \"layers\" || name === \"cache\") {\n const c = load();\n if (c) await guarded(io, async () => { await engine(c, name); return 0; });\n continue;\n }\n if (FORWARD[name]) { await guarded(io, () => run([name, ...rest], session)); continue; }\n io.err(`unknown command /${word}. /help lists them.`);\n }\n } finally {\n try { await session.backend?.stop(); } catch { /* the server may already be gone */ }\n }\n}\n\nasync function guarded(io: Io, f: () => Promise<number>): Promise<number | undefined> {\n try { return await f(); } catch (e) { io.err(e instanceof Error ? e.message : String(e)); return undefined; }\n}\n"],"mappings":"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","names":[]}
|