@lzhzzzzwill/cofos 1.3.0 → 1.3.1
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/cofos.js +120 -10
- package/package.json +1 -1
package/bin/cofos.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
-
import { createInterface } from "node:readline";
|
|
3
|
+
import { createInterface, emitKeypressEvents } from "node:readline";
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -134,6 +134,12 @@ function slashHint(line = "/") {
|
|
|
134
134
|
return `\n Commands\n${rows}\n Press Tab to complete.\n`;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
function slashInlineHint(line = "/") {
|
|
138
|
+
const hits = slashMatches(line);
|
|
139
|
+
if (!hits.length) return "";
|
|
140
|
+
return hits.map(({ cmd, desc }) => ` ${C}${cmd}${R} ${desc}`).join("\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
137
143
|
/* ─── helpers ─── */
|
|
138
144
|
|
|
139
145
|
function cmdExist(c) { return spawnSync(c, ["--version"], { encoding: "utf-8", stdio: "pipe" }).status === 0; }
|
|
@@ -352,9 +358,17 @@ async function createTui() {
|
|
|
352
358
|
};
|
|
353
359
|
}
|
|
354
360
|
|
|
355
|
-
function
|
|
361
|
+
function blessedToAnsi(text) {
|
|
356
362
|
return String(text)
|
|
357
|
-
.replace(/\{
|
|
363
|
+
.replace(/\{bold\}/g, B)
|
|
364
|
+
.replace(/\{\/bold\}/g, R)
|
|
365
|
+
.replace(/\{cyan-fg\}/g, C)
|
|
366
|
+
.replace(/\{yellow-fg\}/g, Y)
|
|
367
|
+
.replace(/\{green-fg\}/g, GR)
|
|
368
|
+
.replace(/\{gray-fg\}/g, G)
|
|
369
|
+
.replace(/\{red-fg\}/g, "\x1b[91m")
|
|
370
|
+
.replace(/\{black-fg\}|\{yellow-bg\}|\{\/yellow-bg\}|\{\/black-fg\}/g, "")
|
|
371
|
+
.replace(/\{\/(?:cyan-fg|yellow-fg|green-fg|gray-fg|red-fg)\}/g, R)
|
|
358
372
|
.replace(/[{}]/g, "");
|
|
359
373
|
}
|
|
360
374
|
|
|
@@ -368,6 +382,7 @@ function createPlainUi() {
|
|
|
368
382
|
let onSubmit = () => {};
|
|
369
383
|
let onExit = () => {};
|
|
370
384
|
let prompt = "cofos";
|
|
385
|
+
let slashHintShown = false;
|
|
371
386
|
|
|
372
387
|
function setPrompt(label) {
|
|
373
388
|
prompt = label;
|
|
@@ -375,7 +390,23 @@ function createPlainUi() {
|
|
|
375
390
|
}
|
|
376
391
|
|
|
377
392
|
setPrompt(prompt);
|
|
393
|
+
if (process.stdin.isTTY) {
|
|
394
|
+
emitKeypressEvents(process.stdin, rl);
|
|
395
|
+
process.stdin.on("keypress", () => {
|
|
396
|
+
setTimeout(() => {
|
|
397
|
+
const line = rl.line || "";
|
|
398
|
+
if (line === "/" && !slashHintShown) {
|
|
399
|
+
slashHintShown = true;
|
|
400
|
+
process.stdout.write(`\n${slashInlineHint("/")}\n`);
|
|
401
|
+
rl.prompt(true);
|
|
402
|
+
} else if (!line.startsWith("/")) {
|
|
403
|
+
slashHintShown = false;
|
|
404
|
+
}
|
|
405
|
+
}, 0);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
378
408
|
rl.on("line", (line) => {
|
|
409
|
+
slashHintShown = false;
|
|
379
410
|
onSubmit(line);
|
|
380
411
|
});
|
|
381
412
|
rl.on("SIGINT", () => onExit());
|
|
@@ -383,11 +414,11 @@ function createPlainUi() {
|
|
|
383
414
|
|
|
384
415
|
return {
|
|
385
416
|
supportsReplace: false,
|
|
386
|
-
append: (text = "") => process.stdout.write(
|
|
387
|
-
print: (text = "") => console.log(
|
|
417
|
+
append: (text = "") => process.stdout.write(blessedToAnsi(text)),
|
|
418
|
+
print: (text = "") => console.log(blessedToAnsi(text)),
|
|
388
419
|
clear: () => console.clear(),
|
|
389
420
|
length: () => 0,
|
|
390
|
-
replaceFrom: (_index, text = "") => process.stdout.write(
|
|
421
|
+
replaceFrom: (_index, text = "") => process.stdout.write(blessedToAnsi(text)),
|
|
391
422
|
setSubmitHandler: (fn) => { onSubmit = fn; },
|
|
392
423
|
setExitHandler: (fn) => { onExit = fn; },
|
|
393
424
|
focus: () => rl.prompt(),
|
|
@@ -510,7 +541,8 @@ async function main() {
|
|
|
510
541
|
proc.stderr.setEncoding("utf-8");
|
|
511
542
|
|
|
512
543
|
let buf = "", ready = false, generating = false, responseOpen = false, commandOpen = false, closed = false;
|
|
513
|
-
let currentAnswer = "", assistantStart = -1;
|
|
544
|
+
let currentAnswer = "", assistantStart = -1, plainAssistantMode = "undecided", plainAssistantPending = "";
|
|
545
|
+
let plainReasonBuffer = "", plainReasonPrinted = 0;
|
|
514
546
|
const inputQ = [];
|
|
515
547
|
let mlBuf = [];
|
|
516
548
|
|
|
@@ -566,6 +598,10 @@ async function main() {
|
|
|
566
598
|
responseOpen = false;
|
|
567
599
|
assistantStart = -1;
|
|
568
600
|
currentAnswer = "";
|
|
601
|
+
plainAssistantMode = "undecided";
|
|
602
|
+
plainAssistantPending = "";
|
|
603
|
+
plainReasonBuffer = "";
|
|
604
|
+
plainReasonPrinted = 0;
|
|
569
605
|
commandOpen = true;
|
|
570
606
|
proc.stdin.write(input + "\n");
|
|
571
607
|
generating = true;
|
|
@@ -575,6 +611,10 @@ async function main() {
|
|
|
575
611
|
responseOpen = false;
|
|
576
612
|
assistantStart = -1;
|
|
577
613
|
currentAnswer = "";
|
|
614
|
+
plainAssistantMode = "undecided";
|
|
615
|
+
plainAssistantPending = "";
|
|
616
|
+
plainReasonBuffer = "";
|
|
617
|
+
plainReasonPrinted = 0;
|
|
578
618
|
commandOpen = true;
|
|
579
619
|
proc.stdin.write(input + "\n");
|
|
580
620
|
generating = true;
|
|
@@ -593,6 +633,10 @@ async function main() {
|
|
|
593
633
|
responseOpen = false;
|
|
594
634
|
assistantStart = -1;
|
|
595
635
|
currentAnswer = "";
|
|
636
|
+
plainAssistantMode = "undecided";
|
|
637
|
+
plainAssistantPending = "";
|
|
638
|
+
plainReasonBuffer = "";
|
|
639
|
+
plainReasonPrinted = 0;
|
|
596
640
|
generating = true;
|
|
597
641
|
proc.stdin.write(input + "\n");
|
|
598
642
|
return true;
|
|
@@ -635,11 +679,73 @@ async function main() {
|
|
|
635
679
|
return "{green-fg}{bold}◆{/bold}{/green-fg} " + raw;
|
|
636
680
|
}
|
|
637
681
|
|
|
682
|
+
const reasonRe = /^(The user|Okay[,,]|I need|We need|I should|I'll|Let me|Since the input|In COF nomenclature|Based on the provided information[,,]? I can|用户|让我)/i;
|
|
683
|
+
|
|
638
684
|
function redrawAssistant() {
|
|
639
685
|
if (assistantStart < 0 || !ui.supportsReplace) return;
|
|
640
686
|
ui.replaceFrom(assistantStart, formatAssistant(currentAnswer));
|
|
641
687
|
}
|
|
642
688
|
|
|
689
|
+
function streamPlainAssistant(text, final = false) {
|
|
690
|
+
if (ui.supportsReplace) return;
|
|
691
|
+
plainAssistantPending += text;
|
|
692
|
+
if (plainAssistantMode === "undecided") {
|
|
693
|
+
const start = plainAssistantPending.trimStart();
|
|
694
|
+
if (!start && !final) return;
|
|
695
|
+
if (start.startsWith("<think>") || reasonRe.test(start)) {
|
|
696
|
+
plainAssistantMode = "reason";
|
|
697
|
+
plainReasonBuffer = start.replace(/^<think>\s*/i, "");
|
|
698
|
+
plainReasonPrinted = 0;
|
|
699
|
+
plainAssistantPending = "";
|
|
700
|
+
ui.append(`${G}-${R} ${G}`);
|
|
701
|
+
} else if (final || start.length >= 12 || /\s/.test(start)) {
|
|
702
|
+
plainAssistantMode = "answer";
|
|
703
|
+
plainAssistantPending = start;
|
|
704
|
+
ui.append(`${GR}${B}◆${R} `);
|
|
705
|
+
} else {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (plainAssistantMode === "reason") {
|
|
711
|
+
plainReasonBuffer += plainAssistantPending;
|
|
712
|
+
plainAssistantPending = "";
|
|
713
|
+
const thinkEnd = plainReasonBuffer.indexOf("</think>");
|
|
714
|
+
const paraEnd = plainReasonBuffer.search(/\n\s*\n/);
|
|
715
|
+
const split = thinkEnd !== -1 ? thinkEnd : paraEnd;
|
|
716
|
+
if (split !== -1) {
|
|
717
|
+
const reason = plainReasonBuffer.slice(plainReasonPrinted, split).replace(/<\/think>/gi, "");
|
|
718
|
+
const restStart = thinkEnd !== -1 ? split + "</think>".length : split;
|
|
719
|
+
const rest = plainReasonBuffer.slice(restStart).trimStart();
|
|
720
|
+
if (reason) ui.append(reason);
|
|
721
|
+
ui.append(`${R}\n${GR}${B}◆${R} `);
|
|
722
|
+
plainAssistantMode = "answer";
|
|
723
|
+
plainAssistantPending = rest;
|
|
724
|
+
plainReasonBuffer = "";
|
|
725
|
+
plainReasonPrinted = 0;
|
|
726
|
+
} else {
|
|
727
|
+
const printableUntil = final ? plainReasonBuffer.length : Math.max(0, plainReasonBuffer.length - 2);
|
|
728
|
+
if (printableUntil > plainReasonPrinted) {
|
|
729
|
+
ui.append(plainReasonBuffer.slice(plainReasonPrinted, printableUntil));
|
|
730
|
+
plainReasonPrinted = printableUntil;
|
|
731
|
+
}
|
|
732
|
+
if (!final) return;
|
|
733
|
+
if (plainReasonPrinted < plainReasonBuffer.length) ui.append(plainReasonBuffer.slice(plainReasonPrinted));
|
|
734
|
+
plainReasonBuffer = "";
|
|
735
|
+
plainReasonPrinted = 0;
|
|
736
|
+
ui.append(R);
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (plainAssistantMode === "answer") {
|
|
742
|
+
if (plainAssistantPending) {
|
|
743
|
+
ui.append(plainAssistantPending);
|
|
744
|
+
plainAssistantPending = "";
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
643
749
|
|
|
644
750
|
proc.stdout.on("data", (chunk) => {
|
|
645
751
|
buf += chunk;
|
|
@@ -684,16 +790,20 @@ async function main() {
|
|
|
684
790
|
responseOpen = true;
|
|
685
791
|
assistantStart = ui.supportsReplace ? ui.length() : 0;
|
|
686
792
|
currentAnswer = "";
|
|
793
|
+
plainAssistantMode = "undecided";
|
|
794
|
+
plainAssistantPending = "";
|
|
795
|
+
plainReasonBuffer = "";
|
|
796
|
+
plainReasonPrinted = 0;
|
|
687
797
|
}
|
|
688
798
|
|
|
689
799
|
const di = buf.indexOf(DONE);
|
|
690
800
|
if (di !== -1) {
|
|
691
801
|
const pre = buf.slice(0, di);
|
|
692
|
-
if (pre) { currentAnswer += pre; redrawAssistant(); }
|
|
802
|
+
if (pre) { currentAnswer += pre; if (ui.supportsReplace) redrawAssistant(); else streamPlainAssistant(pre); }
|
|
693
803
|
buf = buf.slice(di + DONE.length);
|
|
694
804
|
if (buf[0] === "\n") buf = buf.slice(1);
|
|
695
805
|
if (ui.supportsReplace) redrawAssistant();
|
|
696
|
-
else
|
|
806
|
+
else streamPlainAssistant("", true);
|
|
697
807
|
generating = false;
|
|
698
808
|
responseOpen = false;
|
|
699
809
|
if (ui.supportsReplace) ui.append("\n");
|
|
@@ -704,7 +814,7 @@ async function main() {
|
|
|
704
814
|
const keep = DONE.length - 1;
|
|
705
815
|
if (buf.length > keep) {
|
|
706
816
|
const out = buf.slice(0, -keep);
|
|
707
|
-
if (out) { currentAnswer += out; redrawAssistant(); }
|
|
817
|
+
if (out) { currentAnswer += out; if (ui.supportsReplace) redrawAssistant(); else streamPlainAssistant(out); }
|
|
708
818
|
buf = buf.slice(-keep);
|
|
709
819
|
}
|
|
710
820
|
}
|