@oxecli/oxe 1.0.104 → 1.0.106
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/README.md +4 -0
- package/dist/engine.js +3 -3
- package/dist/tools.js +21 -1
- package/dist/ui.js +24 -55
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/engine.js
CHANGED
|
@@ -82,7 +82,7 @@ export class InferenceEngine {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
async runTool(name, argumentsJson) {
|
|
85
|
+
async runTool(name, argumentsJson, signal) {
|
|
86
86
|
let args = {};
|
|
87
87
|
try {
|
|
88
88
|
args = argumentsJson ? JSON.parse(argumentsJson) : {};
|
|
@@ -103,7 +103,7 @@ export class InferenceEngine {
|
|
|
103
103
|
result = toolEditFile(args.path, args.old_string, args.new_string, args.replace_all);
|
|
104
104
|
break;
|
|
105
105
|
case "bash":
|
|
106
|
-
result = await toolBash(args.command, args.timeout, args.cwd);
|
|
106
|
+
result = await toolBash(args.command, args.timeout, args.cwd, signal);
|
|
107
107
|
break;
|
|
108
108
|
case "glob":
|
|
109
109
|
result = toolGlob(args.pattern, args.path, args.limit, args.depth);
|
|
@@ -591,7 +591,7 @@ export class InferenceEngine {
|
|
|
591
591
|
dockTransientStart("flush");
|
|
592
592
|
const toolSpinner = new Spinner();
|
|
593
593
|
toolSpinner.startDots("\x1b[90m╰─\x1b[0m Working");
|
|
594
|
-
const rawResult = await this.runTool(c.name, c.arguments);
|
|
594
|
+
const rawResult = await this.runTool(c.name, c.arguments, this.activeAbort?.signal);
|
|
595
595
|
// A tool call was made, which interrupts any current working phase,
|
|
596
596
|
// so the next agent iteration may commit a fresh "Worked for …" block.
|
|
597
597
|
this.workedCommitted = false;
|
package/dist/tools.js
CHANGED
|
@@ -586,7 +586,7 @@ function terminateProcessTree(proc) {
|
|
|
586
586
|
}
|
|
587
587
|
}
|
|
588
588
|
}
|
|
589
|
-
export function toolBash(command, timeout = 60, cwd) {
|
|
589
|
+
export function toolBash(command, timeout = 60, cwd, signal) {
|
|
590
590
|
try {
|
|
591
591
|
let t = typeof timeout === "number" ? timeout : parseInt(String(timeout), 10);
|
|
592
592
|
if (Number.isNaN(t))
|
|
@@ -597,6 +597,10 @@ export function toolBash(command, timeout = 60, cwd) {
|
|
|
597
597
|
return Promise.resolve(`Error: working directory not found: ${cwd}`);
|
|
598
598
|
}
|
|
599
599
|
return new Promise((resolve) => {
|
|
600
|
+
if (signal && signal.aborted) {
|
|
601
|
+
resolve("Error: command aborted");
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
600
604
|
let child;
|
|
601
605
|
try {
|
|
602
606
|
if (process.platform === "win32") {
|
|
@@ -621,6 +625,18 @@ export function toolBash(command, timeout = 60, cwd) {
|
|
|
621
625
|
let stdout = "";
|
|
622
626
|
let stderr = "";
|
|
623
627
|
let finished = false;
|
|
628
|
+
const onAbort = () => {
|
|
629
|
+
if (finished)
|
|
630
|
+
return;
|
|
631
|
+
terminateProcessTree({
|
|
632
|
+
pid: child.pid,
|
|
633
|
+
kill: (sig) => child.kill(sig),
|
|
634
|
+
});
|
|
635
|
+
finished = true;
|
|
636
|
+
clearTimeout(timer);
|
|
637
|
+
resolve("Error: command aborted");
|
|
638
|
+
};
|
|
639
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
624
640
|
const timer = setTimeout(() => {
|
|
625
641
|
if (finished)
|
|
626
642
|
return;
|
|
@@ -633,6 +649,8 @@ export function toolBash(command, timeout = 60, cwd) {
|
|
|
633
649
|
if (output)
|
|
634
650
|
msg += `\n${output}`;
|
|
635
651
|
finished = true;
|
|
652
|
+
clearTimeout(timer);
|
|
653
|
+
signal?.removeEventListener("abort", onAbort);
|
|
636
654
|
resolve(msg);
|
|
637
655
|
}, t * 1000);
|
|
638
656
|
child.stdout?.on("data", (d) => (stdout += d.toString("utf-8")));
|
|
@@ -642,6 +660,7 @@ export function toolBash(command, timeout = 60, cwd) {
|
|
|
642
660
|
return;
|
|
643
661
|
finished = true;
|
|
644
662
|
clearTimeout(timer);
|
|
663
|
+
signal?.removeEventListener("abort", onAbort);
|
|
645
664
|
resolve(`Error: ${err}`);
|
|
646
665
|
});
|
|
647
666
|
child.on("close", (code) => {
|
|
@@ -649,6 +668,7 @@ export function toolBash(command, timeout = 60, cwd) {
|
|
|
649
668
|
return;
|
|
650
669
|
finished = true;
|
|
651
670
|
clearTimeout(timer);
|
|
671
|
+
signal?.removeEventListener("abort", onAbort);
|
|
652
672
|
const output = (stdout + stderr).trim() || "(no output)";
|
|
653
673
|
resolve(`exit code: ${code}\n${output}`);
|
|
654
674
|
});
|
package/dist/ui.js
CHANGED
|
@@ -1394,7 +1394,6 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1394
1394
|
let draft = null;
|
|
1395
1395
|
let bracketedPaste = false;
|
|
1396
1396
|
let bracketedPasteBuffer = "";
|
|
1397
|
-
let bracketedPasteSince = 0;
|
|
1398
1397
|
let pasteBurst = false;
|
|
1399
1398
|
let pasteBurstTimer = null;
|
|
1400
1399
|
readline.emitKeypressEvents(process.stdin);
|
|
@@ -1719,19 +1718,17 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1719
1718
|
const end = "\x1b[201~";
|
|
1720
1719
|
const sequence = key?.sequence || "";
|
|
1721
1720
|
const value = str || "";
|
|
1722
|
-
|
|
1721
|
+
// A new paste-start while already inside a paste means the previous paste
|
|
1722
|
+
// was aborted (its end marker never arrived). Reset and start over so we
|
|
1723
|
+
// never swallow subsequent typing.
|
|
1724
|
+
if ((key?.name === "paste-start" || sequence === start || value === start)) {
|
|
1723
1725
|
bracketedPaste = true;
|
|
1724
|
-
bracketedPasteSince = Date.now();
|
|
1725
1726
|
bracketedPasteBuffer = "";
|
|
1726
1727
|
const inline = value === start ? "" : value.startsWith(start) ? value.slice(start.length) : "";
|
|
1727
1728
|
if (inline)
|
|
1728
1729
|
bracketedPasteBuffer = inline;
|
|
1729
1730
|
if (inline.includes(end)) {
|
|
1730
|
-
const
|
|
1731
|
-
const pasted = inline
|
|
1732
|
-
.slice(0, endAt)
|
|
1733
|
-
.replace(/\r\n/g, "\n")
|
|
1734
|
-
.replace(/\r/g, "\n");
|
|
1731
|
+
const pasted = inline.slice(0, inline.indexOf(end)).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1735
1732
|
bracketedPaste = false;
|
|
1736
1733
|
bracketedPasteBuffer = "";
|
|
1737
1734
|
if (pasted)
|
|
@@ -1742,30 +1739,12 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1742
1739
|
}
|
|
1743
1740
|
if (!bracketedPaste)
|
|
1744
1741
|
return false;
|
|
1745
|
-
//
|
|
1746
|
-
// paste,
|
|
1747
|
-
//
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
// literal input and reset so typing works again.
|
|
1752
|
-
if (Date.now() - bracketedPasteSince > 3000 &&
|
|
1753
|
-
str &&
|
|
1754
|
-
str.length === 1 &&
|
|
1755
|
-
!key?.ctrl &&
|
|
1756
|
-
!key?.meta) {
|
|
1757
|
-
const pasted = bracketedPasteBuffer
|
|
1758
|
-
.replace(/\r\n/g, "\n")
|
|
1759
|
-
.replace(/\r/g, "\n");
|
|
1760
|
-
bracketedPaste = false;
|
|
1761
|
-
bracketedPasteBuffer = "";
|
|
1762
|
-
if (pasted) {
|
|
1763
|
-
insert(pasted, true);
|
|
1764
|
-
repaint();
|
|
1765
|
-
}
|
|
1766
|
-
return false; // let this keystroke be handled normally below
|
|
1767
|
-
}
|
|
1768
|
-
if (sequence === end || value === end) {
|
|
1742
|
+
// Close on the end marker. readline emits it as a dedicated keypress
|
|
1743
|
+
// (key.name === "paste-end", sequence === \x1b[201~); also scan the raw
|
|
1744
|
+
// value in case a terminal delivers it embedded in a larger chunk.
|
|
1745
|
+
const endAt = value.indexOf(end);
|
|
1746
|
+
if (key?.name === "paste-end" || sequence === end || value === end || endAt >= 0) {
|
|
1747
|
+
bracketedPasteBuffer += endAt >= 0 ? value.slice(0, endAt) : "";
|
|
1769
1748
|
const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1770
1749
|
bracketedPaste = false;
|
|
1771
1750
|
bracketedPasteBuffer = "";
|
|
@@ -1775,21 +1754,8 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1775
1754
|
return true;
|
|
1776
1755
|
}
|
|
1777
1756
|
const chunk = value || (sequence && !sequence.startsWith("\x1b[") ? sequence : "");
|
|
1778
|
-
if (chunk)
|
|
1779
|
-
|
|
1780
|
-
if (endAt >= 0) {
|
|
1781
|
-
bracketedPasteBuffer += chunk.slice(0, endAt);
|
|
1782
|
-
const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1783
|
-
bracketedPaste = false;
|
|
1784
|
-
bracketedPasteBuffer = "";
|
|
1785
|
-
if (pasted)
|
|
1786
|
-
insert(pasted, true);
|
|
1787
|
-
repaint();
|
|
1788
|
-
}
|
|
1789
|
-
else {
|
|
1790
|
-
bracketedPasteBuffer += chunk;
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1757
|
+
if (chunk)
|
|
1758
|
+
bracketedPasteBuffer += chunk;
|
|
1793
1759
|
return true;
|
|
1794
1760
|
};
|
|
1795
1761
|
const markPasteBurst = () => {
|
|
@@ -1802,14 +1768,9 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1802
1768
|
}, 500);
|
|
1803
1769
|
};
|
|
1804
1770
|
const onKeypress = (str, key) => {
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
pasteBurst = false;
|
|
1809
|
-
if (pasteBurstTimer)
|
|
1810
|
-
clearTimeout(pasteBurstTimer);
|
|
1811
|
-
pasteBurstTimer = null;
|
|
1812
|
-
}
|
|
1771
|
+
// Interrupts (Ctrl+C/D/Z) must be handled BEFORE bracketed-paste capture so
|
|
1772
|
+
// that even a stuck/interrupted paste can always be escaped — otherwise the
|
|
1773
|
+
// paste handler swallows them and the field appears locked with no way out.
|
|
1813
1774
|
if (key && key.ctrl && key.name === "c") {
|
|
1814
1775
|
settle(new Error("interrupt"), true);
|
|
1815
1776
|
return;
|
|
@@ -1818,6 +1779,14 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1818
1779
|
settle(new Error("eof"), true);
|
|
1819
1780
|
return;
|
|
1820
1781
|
}
|
|
1782
|
+
if (consumeBracketedPaste(str, key))
|
|
1783
|
+
return;
|
|
1784
|
+
if (pasteBurst && str && str.length === 1 && !key?.ctrl && !key?.meta) {
|
|
1785
|
+
pasteBurst = false;
|
|
1786
|
+
if (pasteBurstTimer)
|
|
1787
|
+
clearTimeout(pasteBurstTimer);
|
|
1788
|
+
pasteBurstTimer = null;
|
|
1789
|
+
}
|
|
1821
1790
|
if (key && key.ctrl && key.name === "a") {
|
|
1822
1791
|
cursor = 0;
|
|
1823
1792
|
repaint();
|