@oxecli/oxe 1.0.7 → 1.0.9
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/dist/cli.js +33 -3
- package/dist/config.js +0 -2
- package/dist/engine.js +1 -1
- package/dist/ui.js +43 -25
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ export class CLI {
|
|
|
15
15
|
story = [];
|
|
16
16
|
sessionId = null;
|
|
17
17
|
promptHistory = [];
|
|
18
|
+
interruptPending = false;
|
|
18
19
|
constructor() {
|
|
19
20
|
clearScreen();
|
|
20
21
|
renderPanel("AI CODING AGENT", "", "Engine: Ready", false);
|
|
@@ -107,7 +108,8 @@ export class CLI {
|
|
|
107
108
|
if (footer) {
|
|
108
109
|
if (!footer.startsWith("("))
|
|
109
110
|
footer = `(${footer})`;
|
|
110
|
-
|
|
111
|
+
// Footer: labels white, only the backtick values grey (not the whole line).
|
|
112
|
+
process.stdout.write("\n" + aiMarkdown(footer) + "\n");
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
115
|
else {
|
|
@@ -138,6 +140,10 @@ export class CLI {
|
|
|
138
140
|
else if (typ === "tool") {
|
|
139
141
|
this.printToolEntry(e["started"], text, e["status"] ?? "");
|
|
140
142
|
}
|
|
143
|
+
else if (typ === "footer") {
|
|
144
|
+
// Footer: labels white, only the backtick values grey (not the whole line).
|
|
145
|
+
process.stdout.write(aiMarkdown(text) + "\n");
|
|
146
|
+
}
|
|
141
147
|
else {
|
|
142
148
|
process.stdout.write(mutedMarkdown(text) + "\n");
|
|
143
149
|
}
|
|
@@ -210,6 +216,7 @@ export class CLI {
|
|
|
210
216
|
const [input, spans] = await askBottomPrompt("You", "❯", this.promptHistory);
|
|
211
217
|
if (!input.trim())
|
|
212
218
|
continue;
|
|
219
|
+
this.interruptPending = false;
|
|
213
220
|
this.promptHistory.push(input);
|
|
214
221
|
const lower = input.trim().toLowerCase();
|
|
215
222
|
if (lower === "/exit" || lower === "/quit") {
|
|
@@ -260,12 +267,35 @@ export class CLI {
|
|
|
260
267
|
this.saveSession(engine);
|
|
261
268
|
}
|
|
262
269
|
catch (err) {
|
|
263
|
-
if (err?.message === "
|
|
270
|
+
if (err?.message === "eof") {
|
|
264
271
|
this.saveSession(engine);
|
|
265
272
|
await engine.cleanupStoredResponses();
|
|
266
|
-
process.stdout.write("\
|
|
273
|
+
process.stdout.write("\nSession closing via exit interrupt hook.\n");
|
|
267
274
|
break;
|
|
268
275
|
}
|
|
276
|
+
if (err?.message === "interrupt") {
|
|
277
|
+
const wasActive = engine.inQuery;
|
|
278
|
+
engine.inQuery = false;
|
|
279
|
+
// Drop the pending user message/story entry that was never run.
|
|
280
|
+
if (this.inputItems.length && this.inputItems[this.inputItems.length - 1]["role"] === "user") {
|
|
281
|
+
this.inputItems.pop();
|
|
282
|
+
}
|
|
283
|
+
if (this.story.length && this.story[this.story.length - 1]["type"] === "user") {
|
|
284
|
+
this.story.pop();
|
|
285
|
+
}
|
|
286
|
+
if (this.interruptPending) {
|
|
287
|
+
this.saveSession(engine);
|
|
288
|
+
await engine.cleanupStoredResponses();
|
|
289
|
+
process.stdout.write("\nClosing terminal session. Goodbye!\n");
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
this.interruptPending = true;
|
|
293
|
+
process.stdout.write("\n");
|
|
294
|
+
process.stdout.write(wasActive
|
|
295
|
+
? "\x1b[2mInterrupted. Press Ctrl+C again to exit, or type a new prompt.\x1b[0m\n"
|
|
296
|
+
: "\x1b[2mNo active response. Press Ctrl+C again to exit.\x1b[0m\n");
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
269
299
|
throw err;
|
|
270
300
|
}
|
|
271
301
|
}
|
package/dist/config.js
CHANGED
|
@@ -169,13 +169,11 @@ export async function loadOrPrompt() {
|
|
|
169
169
|
authSpinner.stop();
|
|
170
170
|
if (validation.valid) {
|
|
171
171
|
key_data = validation.key_data || {};
|
|
172
|
-
process.stdout.write("\n");
|
|
173
172
|
process.stdout.write(markupToAnsi(`[green]✓ Oxe API Key verified[/green] [dim](${String(key_data["name"] ?? "Desktop")})[/dim]`) + "\n");
|
|
174
173
|
process.env.OXE_API_KEY = api_key;
|
|
175
174
|
break;
|
|
176
175
|
}
|
|
177
176
|
else {
|
|
178
|
-
process.stdout.write("\n");
|
|
179
177
|
process.stdout.write(`\x1b[31mAuthentication Error:\x1b[0m ${validation.error}\n`);
|
|
180
178
|
process.stdout.write("\n");
|
|
181
179
|
api_key = "";
|
package/dist/engine.js
CHANGED
|
@@ -400,7 +400,7 @@ export class InferenceEngine {
|
|
|
400
400
|
const stats = { thinking: 0, tokens: 0, reasoning: 0, input: 0 };
|
|
401
401
|
const queryStart = Date.now();
|
|
402
402
|
const footerText = () => `(Thought for ${tickDuration(stats["thinking"])} · Worked for ${tickDuration((Date.now() - queryStart) / 1000)} · Used \`${stats["tokens"].toLocaleString()}\` tokens)`;
|
|
403
|
-
const summary = () =>
|
|
403
|
+
const summary = () => aiMarkdown(footerText());
|
|
404
404
|
const attachFooter = (items) => {
|
|
405
405
|
const footer = footerText();
|
|
406
406
|
for (let i = items.length - 1; i >= 0; i--) {
|
package/dist/ui.js
CHANGED
|
@@ -306,16 +306,25 @@ export class Spinner {
|
|
|
306
306
|
const rendered = mutedMarkdown(`${SPINNER_FRAMES[this.frame % SPINNER_FRAMES.length]} ${this.text}`);
|
|
307
307
|
const newRows = Math.max(1, displayRows(rendered));
|
|
308
308
|
const clearRows = Math.max(this.rows, newRows, 1);
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
// Cursor is always at the home position (col 0) after each draw. Clear
|
|
310
|
+
// `clearRows` lines downward from home, rewrite the content, then return
|
|
311
|
+
// the cursor to home (col 0) so the next frame overwrites in place.
|
|
311
312
|
for (let i = 0; i < clearRows; i++) {
|
|
312
313
|
process.stdout.write("\r\x1b[2K");
|
|
313
314
|
if (i < clearRows - 1)
|
|
314
315
|
process.stdout.write("\n");
|
|
315
316
|
}
|
|
316
|
-
|
|
317
|
+
// Cursor is now `clearRows-1` lines below home; move back to home.
|
|
318
|
+
if (clearRows > 1)
|
|
317
319
|
process.stdout.write(`\x1b[${clearRows - 1}A\r`);
|
|
320
|
+
else
|
|
321
|
+
process.stdout.write("\r");
|
|
318
322
|
process.stdout.write(rendered);
|
|
323
|
+
// Return cursor to home (col 0) for the next draw.
|
|
324
|
+
if (newRows > 1)
|
|
325
|
+
process.stdout.write(`\x1b[${newRows - 1}A\r`);
|
|
326
|
+
else
|
|
327
|
+
process.stdout.write("\r");
|
|
319
328
|
this.rows = newRows;
|
|
320
329
|
}
|
|
321
330
|
stop() {
|
|
@@ -324,14 +333,16 @@ export class Spinner {
|
|
|
324
333
|
this.timer = null;
|
|
325
334
|
}
|
|
326
335
|
if (this.enabled && this.rows > 0) {
|
|
327
|
-
|
|
336
|
+
// Cursor is at home (col 0); clear the rendered rows downward.
|
|
328
337
|
for (let i = 0; i < this.rows; i++) {
|
|
329
338
|
process.stdout.write("\r\x1b[2K");
|
|
330
339
|
if (i < this.rows - 1)
|
|
331
340
|
process.stdout.write("\n");
|
|
332
341
|
}
|
|
333
|
-
if (this.rows >
|
|
334
|
-
process.stdout.write(`\x1b[${this.rows - 1}A`);
|
|
342
|
+
if (this.rows > 1)
|
|
343
|
+
process.stdout.write(`\x1b[${this.rows - 1}A\r`);
|
|
344
|
+
else
|
|
345
|
+
process.stdout.write("\r");
|
|
335
346
|
}
|
|
336
347
|
this.rows = 0;
|
|
337
348
|
}
|
|
@@ -577,17 +588,12 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
577
588
|
let done = false;
|
|
578
589
|
let lastCursorRow = 0;
|
|
579
590
|
let lastTotalRows = 0;
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
// Erase the prompt region: a leading blank line + the box. Cursor sits at
|
|
587
|
-
// lastCursorRow inside the box; move up to the box's top border, erase the
|
|
588
|
-
// box rows, then erase the leading blank line, leaving the cursor on that
|
|
589
|
-
// blank row. The caller's single leading newline then yields exactly ONE
|
|
590
|
-
// blank row before the echoed user prompt.
|
|
591
|
+
// Erase the prompt region: a leading blank line + the box. Cursor sits at
|
|
592
|
+
// lastCursorRow inside the box; move up to the box's top border, erase the
|
|
593
|
+
// box rows, then erase the leading blank line, leaving the cursor on that
|
|
594
|
+
// blank row. The caller's single leading newline then yields exactly ONE
|
|
595
|
+
// blank row before the echoed user prompt / message.
|
|
596
|
+
const clearBox = () => {
|
|
591
597
|
process.stdout.write(`\x1b[${lastCursorRow}A`);
|
|
592
598
|
for (let i = 0; i < lastTotalRows; i++) {
|
|
593
599
|
process.stdout.write("\r\x1b[K");
|
|
@@ -596,9 +602,19 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
596
602
|
}
|
|
597
603
|
if (lastTotalRows > 1)
|
|
598
604
|
process.stdout.write(`\x1b[${lastTotalRows - 1}A`);
|
|
599
|
-
// Clear the leading blank line above the box.
|
|
600
605
|
process.stdout.write("\x1b[1A\r\x1b[K");
|
|
601
|
-
|
|
606
|
+
};
|
|
607
|
+
const settle = (value, isErr) => {
|
|
608
|
+
if (done)
|
|
609
|
+
return;
|
|
610
|
+
done = true;
|
|
611
|
+
process.stdin.setRawMode(false);
|
|
612
|
+
showCursor();
|
|
613
|
+
clearBox();
|
|
614
|
+
if (isErr)
|
|
615
|
+
reject(value);
|
|
616
|
+
else
|
|
617
|
+
resolve(value);
|
|
602
618
|
};
|
|
603
619
|
const repaint = (isFirst = false) => {
|
|
604
620
|
const { frame, cursorRow, cursorCol, totalRows } = renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor);
|
|
@@ -606,7 +622,11 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
606
622
|
lastTotalRows = totalRows;
|
|
607
623
|
const frameLines = frame.split("\n");
|
|
608
624
|
if (isFirst) {
|
|
609
|
-
//
|
|
625
|
+
// The caller's content always ends with "\n", so the cursor sits at the
|
|
626
|
+
// start of a fresh (blank) line — that line is the leading blank
|
|
627
|
+
// separator (mirrors the original's Group(Text(""), panel)). Move down
|
|
628
|
+
// one line so the box starts below that blank; do NOT emit a second
|
|
629
|
+
// blank here (it caused a double gap above the box / on cancel).
|
|
610
630
|
process.stdout.write("\n");
|
|
611
631
|
}
|
|
612
632
|
else {
|
|
@@ -686,18 +706,16 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
686
706
|
};
|
|
687
707
|
const onKeypress = (str, key) => {
|
|
688
708
|
if (key && key.ctrl && key.name === "c") {
|
|
689
|
-
|
|
690
|
-
reject(new Error("interrupt"));
|
|
709
|
+
settle(new Error("interrupt"), true);
|
|
691
710
|
return;
|
|
692
711
|
}
|
|
693
712
|
if (key && key.ctrl && (key.name === "d" || key.name === "z")) {
|
|
694
|
-
|
|
695
|
-
reject(new Error("eof"));
|
|
713
|
+
settle(new Error("eof"), true);
|
|
696
714
|
return;
|
|
697
715
|
}
|
|
698
716
|
if (key && key.name === "return") {
|
|
699
717
|
if (buffer.trim()) {
|
|
700
|
-
|
|
718
|
+
settle([buffer, pasteSpans], false);
|
|
701
719
|
}
|
|
702
720
|
return;
|
|
703
721
|
}
|