@lzhzzzzwill/cofos 1.1.15 → 1.1.19

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.
Files changed (2) hide show
  1. package/bin/cofos.js +80 -24
  2. package/package.json +1 -1
package/bin/cofos.js CHANGED
@@ -16,6 +16,7 @@ const DEFAULT_CONFIG = process.env.COFOS_CONFIG || "config/config.yaml";
16
16
  /* ─── ANSI ─── */
17
17
  const R = "\x1b[0m", B = "\x1b[1m", D = "\x1b[2m";
18
18
  const C = "\x1b[96m", W = "\x1b[97m", G = "\x1b[90m", Y = "\x1b[93m";
19
+ const M = "\x1b[95m", BL = "\x1b[94m", GR = "\x1b[92m", A = "\x1b[38;5;208m";
19
20
 
20
21
  /* ─── Protocol markers ─── */
21
22
  const READY = "===READY===", RESPONSE = "===RESPONSE===", DONE = "===DONE===";
@@ -30,9 +31,16 @@ const ART_W = ART[0].length;
30
31
 
31
32
  function vis(t) { return t.replace(/\x1b\[[0-9;]*m/g, "").length; }
32
33
  function fw() { return Math.min(Math.max((process.stdout.columns || 110) - 2, 82), 150); }
33
- function top(w) { return "╭" + "─".repeat(w) + ""; }
34
- function bot(w) { return "╰" + "".repeat(w) + "╯"; }
35
- function sep(w) { return "├" + "─".repeat(w) + "┤"; }
34
+ function plain(t) { return t.replace(/\x1b\[[0-9;]*m/g, ""); }
35
+ function topTitle(w, left, right = "") {
36
+ const l = ` ${C}${B}${left}${R} `;
37
+ const r = right ? ` ${G}${right}${R} ` : "";
38
+ const room = Math.max(0, w - vis(l) - vis(r));
39
+ return C + "╭" + R + l + C + "─".repeat(room) + R + r + C + "╮" + R;
40
+ }
41
+ function top(w) { return C + "╭" + "─".repeat(w) + "╮" + R; }
42
+ function bot(w) { return C + "╰" + "─".repeat(w) + "╯" + R; }
43
+ function sep(w) { return C + "├" + "─".repeat(w) + "┤" + R; }
36
44
  function vpad(t, w) { return t + " ".repeat(Math.max(0, w - vis(t))); }
37
45
  function clip(t, w) {
38
46
  const plain = t.replace(/\x1b\[[0-9;]*m/g, "");
@@ -41,12 +49,12 @@ function clip(t, w) {
41
49
  return plain.slice(0, w - 1) + "…";
42
50
  }
43
51
  function cell(t, w) { return vpad(clip(t, w), w); }
44
- function row(left, right, lw, rw) { return "│ " + cell(left, lw) + " │ " + cell(right, rw) + " │"; }
45
- function full(t, w) { return "│ " + cell(t, w - 2) + " │"; }
52
+ function row(left, right, lw, rw) { return C + "│ " + R + cell(left, lw) + C + " │ " + R + cell(right, rw) + C + " │" + R; }
53
+ function full(t, w) { return C + "│ " + R + cell(t, w - 2) + C + " │" + R; }
46
54
  function empty(w) { return full("", w); }
47
55
 
48
56
  function logoLines(width) {
49
- if (width >= ART_W) return ART.map((line) => C + B + line + R);
57
+ if (width >= ART_W) return ART.map((line, i) => (i < 2 ? C : i < 4 ? BL : M) + B + line + R);
50
58
  return [C + B + "COFOS" + R];
51
59
  }
52
60
 
@@ -60,25 +68,26 @@ function render({ modelPath, cacheDir, pdfDir }) {
60
68
  ...logoLines(Math.floor((w - 5) * 0.48)),
61
69
  "",
62
70
  G + "Scientific RAG CLI" + R,
63
- W + "Model" + R + " " + modelPath,
64
- W + "Params" + R + " 9B",
65
- W + "Path" + R + " " + cwd,
66
- W + "Cache" + R + " " + cacheDir,
71
+ Y + "Model" + R + " " + W + modelPath + R,
72
+ Y + "Params" + R + " " + W + "9B" + R,
73
+ Y + "Path" + R + " " + W + cwd + R,
74
+ Y + "Cache" + R + " " + W + cacheDir + R,
67
75
  ];
68
76
  const right = [
69
- W + B + "What COFOS Does" + R,
77
+ GR + B + "What COFOS Does" + R,
70
78
  "COFOS answers scientific questions about covalent organic frameworks,",
71
79
  "photocatalysis, reactive oxygen species, and oxygen-reduction products.",
72
80
  rag,
73
81
  "",
74
- W + B + "Suggested Commands" + R,
75
- "/help show commands and PDF-RAG usage",
76
- "/topk 5 change retrieval depth",
77
- "/rag off answer without retrieval",
78
- "cofos --pdf-dir ./new_pdfs add local papers",
82
+ A + B + "Suggested Commands" + R,
83
+ C + "/help" + R + " show commands and PDF-RAG usage",
84
+ C + "/topk 5" + R + " change retrieval depth",
85
+ C + "/rag off" + R + " answer without retrieval",
86
+ M + "cofos --pdf-dir ./new_pdfs" + R + " add local papers",
79
87
  ];
80
88
 
81
- const lines = [top(w)];
89
+ const titleLeft = `COFOS CLI v${pkg.version}`;
90
+ const lines = [topTitle(w, titleLeft)];
82
91
  if (w >= minSplit) {
83
92
  const inner = w - 5;
84
93
  const lw = Math.max(46, Math.floor(inner * 0.48));
@@ -247,6 +256,11 @@ function main() {
247
256
  const inputQ = [];
248
257
  const mainPrompt = C + " cofos " + R;
249
258
  const contPrompt = G + " ... " + R;
259
+ const docked = Boolean(process.stdout.isTTY && process.stdin.isTTY);
260
+ let dockRows = process.stdout.rows || 24;
261
+ let dockCols = process.stdout.columns || 80;
262
+ let historyRow = Math.max(0, dockRows - 2);
263
+ let historyCol = 0;
250
264
  const rl = createInterface({
251
265
  input: process.stdin,
252
266
  output: process.stdout,
@@ -259,21 +273,60 @@ function main() {
259
273
  else if (!handle(l)) ask();
260
274
  });
261
275
 
276
+ function resetDock() {
277
+ if (docked) process.stdout.write("\x1b[r");
278
+ }
279
+
280
+ function setupDock() {
281
+ if (!docked) return;
282
+ dockRows = process.stdout.rows || dockRows;
283
+ dockCols = process.stdout.columns || dockCols;
284
+ historyRow = Math.max(0, dockRows - 2);
285
+ historyCol = Math.min(historyCol, Math.max(0, dockCols - 1));
286
+ process.stdout.write(`\x1b[1;${Math.max(1, dockRows - 1)}r`);
287
+ cursorTo(process.stdout, 0, dockRows - 1);
288
+ clearLine(process.stdout, 0);
289
+ }
290
+
291
+ function redrawInput() {
292
+ if (!docked || closed) return;
293
+ cursorTo(rl.output, 0, dockRows - 1);
294
+ clearLine(rl.output, 0);
295
+ rl.prompt(true);
296
+ }
297
+
298
+ function writeHistory(data) {
299
+ if (!docked) {
300
+ process.stdout.write(data);
301
+ return;
302
+ }
303
+ cursorTo(process.stdout, historyCol, historyRow);
304
+ for (const ch of data) {
305
+ process.stdout.write(ch);
306
+ if (ch === "\n") {
307
+ historyCol = 0;
308
+ } else if (ch === "\r") {
309
+ historyCol = 0;
310
+ } else {
311
+ historyCol += 1;
312
+ if (historyCol >= dockCols) historyCol = 0;
313
+ }
314
+ }
315
+ redrawInput();
316
+ }
317
+
262
318
  function preserveInputWrite(stream, chunk) {
263
319
  const data = String(chunk);
264
320
  if (!rl.terminal || closed) {
265
321
  stream.write(data);
266
322
  return;
267
323
  }
268
- const activeInput = !generating;
269
- if (activeInput) {
270
- clearLine(rl.output, 0);
271
- cursorTo(rl.output, 0);
272
- }
273
- stream.write(data);
274
- if (activeInput) rl.prompt(true);
324
+ writeHistory(data);
275
325
  }
276
326
 
327
+ setupDock();
328
+ process.stdout.on("resize", () => { setupDock(); redrawInput(); });
329
+
277
330
  // Let users type while the Python backend is still loading. Submitted lines
278
331
  // are queued until READY, while status logs are written above and the input
279
332
  // line is restored afterward.
@@ -336,6 +389,7 @@ function main() {
336
389
  proc.on("error", (e) => { console.error("[cofos] Python error:", e.message); process.exit(1); });
337
390
  proc.on("exit", (c) => {
338
391
  closed = true;
392
+ resetDock();
339
393
  if (c && c !== 0) console.error("[cofos] Python exited with code", c);
340
394
  try { rl.close(); } catch (_) {}
341
395
  // Let queued stdout writes flush before tearing the process down.
@@ -345,6 +399,7 @@ function main() {
345
399
  function shutdown() {
346
400
  if (closed) return;
347
401
  closed = true;
402
+ resetDock();
348
403
  try { proc.stdin.end(); } catch (_) {}
349
404
  try { rl.close(); } catch (_) {}
350
405
  proc.kill("SIGTERM");
@@ -354,6 +409,7 @@ function main() {
354
409
  rl.on("SIGINT", () => { console.log("\n[cofos] Interrupted."); shutdown(); });
355
410
  rl.on("close", () => { if (!closed) shutdown(); });
356
411
  process.on("SIGTERM", shutdown);
412
+ process.on("exit", resetDock);
357
413
 
358
414
  function drain() {
359
415
  while (inputQ.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lzhzzzzwill/cofos",
3
- "version": "1.1.15",
3
+ "version": "1.1.19",
4
4
  "description": "COFOS 9B + RAG chat CLI with optional local PDF ingestion.",
5
5
  "type": "module",
6
6
  "bin": {