@oxecli/oxe 1.0.3 → 1.0.4
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 +3 -3
- package/dist/config.js +1 -1
- package/dist/ui.js +74 -66
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17,7 +17,7 @@ export class CLI {
|
|
|
17
17
|
promptHistory = [];
|
|
18
18
|
constructor() {
|
|
19
19
|
clearScreen();
|
|
20
|
-
renderPanel("AI CODING AGENT", "", "Engine: Ready");
|
|
20
|
+
renderPanel("AI CODING AGENT", "", "Engine: Ready", false);
|
|
21
21
|
process.stdout.write("\n");
|
|
22
22
|
this.config = null;
|
|
23
23
|
}
|
|
@@ -189,7 +189,7 @@ export class CLI {
|
|
|
189
189
|
this.sessionId = sid;
|
|
190
190
|
engine.reasoningEffort = String(rec["reasoning_effort"] ?? default_reasoning_effort);
|
|
191
191
|
clearScreen();
|
|
192
|
-
renderPanel(`AI CODING AGENT (resumed ${sid})`, "", "Engine: Ready");
|
|
192
|
+
renderPanel(`AI CODING AGENT (resumed ${sid})`, "", "Engine: Ready", false);
|
|
193
193
|
process.stdout.write(`\x1b[2mResumed:\x1b[0m ${truncateLabel(rec["label"])}\n`);
|
|
194
194
|
process.stdout.write(`\x1b[2m(${this.inputItems.length} items · ${estimateTokens(this.inputItems).toLocaleString()} tokens · Effort: \x1b[1;36m${engine.reasoningEffort}\x1b[0m\x1b[2m)\x1b[0m\n\n`);
|
|
195
195
|
renderPanel("Conversation history", "Restored");
|
|
@@ -226,7 +226,7 @@ export class CLI {
|
|
|
226
226
|
this.story = [];
|
|
227
227
|
this.sessionId = null;
|
|
228
228
|
clearScreen();
|
|
229
|
-
renderPanel("AI CODING AGENT", "", "Engine: Ready");
|
|
229
|
+
renderPanel("AI CODING AGENT", "", "Engine: Ready", false);
|
|
230
230
|
process.stdout.write("\n");
|
|
231
231
|
continue;
|
|
232
232
|
}
|
package/dist/config.js
CHANGED
|
@@ -153,7 +153,7 @@ export async function loadOrPrompt() {
|
|
|
153
153
|
renderPanel("[bold white]Oxe Desktop Authentication[/bold white]\n\n" +
|
|
154
154
|
`[dim]Only valid Oxe API keys (starting with [bold]${OXE_KEY_PREFIX}[/bold]) generated on your\n` +
|
|
155
155
|
"Oxe web dashboard are authorized to run this agent.\n\n" +
|
|
156
|
-
"Get your key at: [bold underline]http://localhost:5173/dashboard/api-keys[/bold underline][/dim]", "Oxe Cloud Access");
|
|
156
|
+
"Get your key at: [bold underline]http://localhost:5173/dashboard/api-keys[/bold underline][/dim]", "Oxe Cloud Access", "", false);
|
|
157
157
|
process.stdout.write("\n");
|
|
158
158
|
api_key = await promptApiKey("Enter Oxe API Key: ");
|
|
159
159
|
if (!api_key) {
|
package/dist/ui.js
CHANGED
|
@@ -135,46 +135,54 @@ export function mutedMarkdown(text) {
|
|
|
135
135
|
// Panel
|
|
136
136
|
// ---------------------------------------------------------------------------
|
|
137
137
|
/**
|
|
138
|
-
* Render a rich-style rounded panel
|
|
139
|
-
*
|
|
138
|
+
* Render a rich-style rounded panel.
|
|
139
|
+
*
|
|
140
|
+
* Matches rich's Panel behavior:
|
|
141
|
+
* - `expand=false` shrinks the frame to fit the content (banner/auth).
|
|
142
|
+
* - title/subtitle are embedded in the top/bottom borders, centered by
|
|
143
|
+
* default (rich default) or left-aligned.
|
|
140
144
|
*/
|
|
141
|
-
export function renderPanel(content, title = "", subtitle = "", borderStyle = "90" // 90 = bright black (grey50)
|
|
142
|
-
) {
|
|
143
|
-
const w = terminalWidth();
|
|
144
|
-
const innerW = Math.max(w - 4, 10);
|
|
145
|
+
export function renderPanel(content, title = "", subtitle = "", expand = true, borderStyle = "90", // 90 = bright black (grey50)
|
|
146
|
+
titleAlign = "center") {
|
|
145
147
|
const styled = markupToAnsi(content);
|
|
146
148
|
const styledLines = styled.split("\n");
|
|
147
149
|
const plainLines = styled.replace(/\x1b\[[0-9;]*m/g, "").split("\n");
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
150
|
+
// Inner width = widest content line + 1 space padding each side.
|
|
151
|
+
let contentW = 0;
|
|
152
|
+
for (const p of plainLines)
|
|
153
|
+
contentW = Math.max(contentW, p.length);
|
|
154
|
+
contentW = Math.max(contentW, 1);
|
|
155
|
+
let innerW = contentW + 2;
|
|
156
|
+
if (title)
|
|
157
|
+
innerW = Math.max(innerW, title.length + 2);
|
|
158
|
+
if (subtitle)
|
|
159
|
+
innerW = Math.max(innerW, subtitle.length + 2);
|
|
160
|
+
const w = terminalWidth();
|
|
161
|
+
innerW = expand ? Math.max(w - 4, 10) : Math.min(innerW, w - 4);
|
|
162
|
+
// Build a border run of `innerW` chars with `text` embedded.
|
|
163
|
+
const embed = (text, align) => {
|
|
164
|
+
if (!text)
|
|
165
|
+
return "─".repeat(innerW);
|
|
166
|
+
const inner = ` ${text} `;
|
|
167
|
+
const fill = Math.max(innerW - inner.length, 0);
|
|
168
|
+
if (align === "left") {
|
|
169
|
+
return `${inner}${"─".repeat(fill)}`;
|
|
170
|
+
}
|
|
171
|
+
const left = Math.floor(fill / 2);
|
|
172
|
+
const right = fill - left;
|
|
173
|
+
return `${"─".repeat(left)}${inner}${"─".repeat(right)}`;
|
|
174
|
+
};
|
|
175
|
+
const top = `╭${embed(title, titleAlign)}╮`;
|
|
176
|
+
process.stdout.write(`\x1b[${borderStyle}m${top}\x1b[0m\n`);
|
|
161
177
|
for (let i = 0; i < styledLines.length; i++) {
|
|
162
178
|
const line = styledLines[i];
|
|
163
179
|
const plain = plainLines[i] ?? "";
|
|
164
|
-
|
|
180
|
+
// Keep exactly one space padding each side; only pad the right to fill.
|
|
181
|
+
const pad = Math.max(innerW - plain.length - 2, 0);
|
|
165
182
|
process.stdout.write(`\x1b[${borderStyle}m│\x1b[0m ${line}${" ".repeat(pad)} \x1b[${borderStyle}m│\x1b[0m\n`);
|
|
166
183
|
}
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
if (subtitle) {
|
|
170
|
-
const s = ` ${subtitle} `;
|
|
171
|
-
bottomSegs.push(`─${s}${"─".repeat(Math.max(borderW - s.length - 2, 0))}`);
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
bottomSegs.push("─".repeat(borderW));
|
|
175
|
-
}
|
|
176
|
-
bottomSegs.push("╯");
|
|
177
|
-
process.stdout.write(`\x1b[${borderStyle}m${bottomSegs.join("")}\x1b[0m\n`);
|
|
184
|
+
const bottom = `╰${embed(subtitle, "center")}╯`;
|
|
185
|
+
process.stdout.write(`\x1b[${borderStyle}m${bottom}\x1b[0m\n`);
|
|
178
186
|
}
|
|
179
187
|
// ---------------------------------------------------------------------------
|
|
180
188
|
// Durations
|
|
@@ -345,51 +353,51 @@ export function splitBlocks(buffer, pasteSpans) {
|
|
|
345
353
|
}
|
|
346
354
|
return segs;
|
|
347
355
|
}
|
|
348
|
-
function renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor) {
|
|
356
|
+
export function renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor) {
|
|
349
357
|
const w = terminalWidth();
|
|
350
|
-
const
|
|
351
|
-
const border = "─".repeat(innerW);
|
|
358
|
+
const borderW = Math.max(w - 4, 10);
|
|
352
359
|
const [row, col] = cursorLineCol(buffer, cursor);
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
display += " ";
|
|
359
|
-
display += kind === "collapsed" ? `\x1b[1m\x1b[36m${disp}\x1b[0m` : disp;
|
|
360
|
+
// Build the inner content line(s) — the whole prefix+text (or placeholder)
|
|
361
|
+
// lives on a single row when the buffer is empty, matching the original.
|
|
362
|
+
let content;
|
|
363
|
+
if (!buffer) {
|
|
364
|
+
content = `\x1b[1m${prefix}\x1b[0m \x1b[1m▏\x1b[0m\x1b[2m${PROMPT_PLACEHOLDER}\x1b[0m`;
|
|
360
365
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
366
|
+
else {
|
|
367
|
+
const segs = splitBlocks(buffer, pasteSpans);
|
|
368
|
+
let display = "";
|
|
369
|
+
for (let i = 0; i < segs.length; i++) {
|
|
370
|
+
const [, kind, disp] = segs[i];
|
|
371
|
+
if (i && !endsWithWs(segs[i - 1][2]))
|
|
372
|
+
display += " ";
|
|
373
|
+
display += kind === "collapsed" ? `\x1b[1m\x1b[36m${disp}\x1b[0m` : disp;
|
|
374
|
+
}
|
|
375
|
+
content = `\x1b[1m${prefix}\x1b[0m ${display}`;
|
|
369
376
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
377
|
+
const lines = content.split("\n");
|
|
378
|
+
// Top border with the label embedded on the left.
|
|
379
|
+
const topPad = Math.max(borderW - label.length - 2, 0);
|
|
380
|
+
const top = `\x1b[90m╭─ ${label} ${"─".repeat(topPad)}╮\x1b[0m`;
|
|
381
|
+
// Body rows.
|
|
382
|
+
const body = [];
|
|
383
|
+
for (const l of lines) {
|
|
384
|
+
const plain = l.replace(/\x1b\[[0-9;]*m/g, "");
|
|
385
|
+
const pad = Math.max(borderW - 2 - plain.length, 0);
|
|
386
|
+
body.push(`\x1b[90m│\x1b[0m ${l}${" ".repeat(pad)} \x1b[90m│\x1b[0m`);
|
|
375
387
|
}
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
let cursorCol = col + (row === 0 ? prefix.length + 1 : 0);
|
|
384
|
-
// +1 row for the top border.
|
|
385
|
-
cursorRow += 1;
|
|
386
|
-
const totalRows = frame.split("\n").length;
|
|
388
|
+
const bottom = `\x1b[90m╰${"─".repeat(borderW)}╯\x1b[0m`;
|
|
389
|
+
const frame = [top, ...body, bottom].join("\n");
|
|
390
|
+
// Cursor placement: one row below the top border. Column includes the
|
|
391
|
+
// "│ " left border (2) plus "prefix " (prefix.length + 1).
|
|
392
|
+
const cursorRow = row + 1;
|
|
393
|
+
const cursorCol = col + prefix.length + 3;
|
|
394
|
+
const totalRows = body.length + 2;
|
|
387
395
|
let cmd = "";
|
|
388
396
|
if (totalRows - 1 > cursorRow)
|
|
389
397
|
cmd += `\x1b[${totalRows - 1 - cursorRow}A`;
|
|
390
398
|
else if (cursorRow > totalRows - 1)
|
|
391
399
|
cmd += `\x1b[${cursorRow - (totalRows - 1)}B`;
|
|
392
|
-
cmd += `\r\x1b[${cursorCol
|
|
400
|
+
cmd += `\r\x1b[${cursorCol}C`;
|
|
393
401
|
return frame + cmd;
|
|
394
402
|
}
|
|
395
403
|
export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|