@botconnector/bccli 0.4.14 → 0.4.15
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/{chunk-RRE6WRU5.js → chunk-2PAVSETH.js} +1 -1
- package/dist/cli.js +5 -5
- package/dist/{serve-KBXP23UW.js → serve-RKV2JCU7.js} +1 -1
- package/dist/ui/liveCap.d.ts +9 -0
- package/dist/ui/thinkingMouse.d.ts +6 -1
- package/dist/ui/tty.d.ts +4 -2
- package/dist/{ui-K5RLE2R6.js → ui-ICUWKBUG.js} +83 -70
- package/dist/{update-CRFQXBNM.js → update-U4FXFWNV.js} +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import "./chunk-7GM6NN5E.js";
|
|
|
13
13
|
import "./chunk-PSO7R5AD.js";
|
|
14
14
|
import {
|
|
15
15
|
VERSION
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-2PAVSETH.js";
|
|
17
17
|
import {
|
|
18
18
|
helpText,
|
|
19
19
|
parseCliArgs
|
|
@@ -109,7 +109,7 @@ async function main() {
|
|
|
109
109
|
return runModelsCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), fetch: globalThis.fetch });
|
|
110
110
|
}
|
|
111
111
|
if (args.command === "update") {
|
|
112
|
-
const { runUpdateCommand } = await import("./update-
|
|
112
|
+
const { runUpdateCommand } = await import("./update-U4FXFWNV.js");
|
|
113
113
|
return runUpdateCommand({ env: process.env });
|
|
114
114
|
}
|
|
115
115
|
if (args.command === "acp") {
|
|
@@ -118,7 +118,7 @@ async function main() {
|
|
|
118
118
|
return 0;
|
|
119
119
|
}
|
|
120
120
|
if (args.command === "serve") {
|
|
121
|
-
const { serveBanner, startServe } = await import("./serve-
|
|
121
|
+
const { serveBanner, startServe } = await import("./serve-RKV2JCU7.js");
|
|
122
122
|
const handle = await startServe({ port: args.port, host: args.host, token: args.token, cwd, env: process.env });
|
|
123
123
|
process.stderr.write(serveBanner(handle, { token: args.token }));
|
|
124
124
|
await new Promise((resolve) => {
|
|
@@ -215,13 +215,13 @@ async function main() {
|
|
|
215
215
|
let updateMessage;
|
|
216
216
|
if (rt.config.updateCheck !== "off" && !process.env.BCCLI_NO_UPDATE_CHECK) {
|
|
217
217
|
try {
|
|
218
|
-
const { checkForUpdate } = await import("./update-
|
|
218
|
+
const { checkForUpdate } = await import("./update-U4FXFWNV.js");
|
|
219
219
|
const update = await checkForUpdate({ home: rt.home, fetch: globalThis.fetch, now: /* @__PURE__ */ new Date() });
|
|
220
220
|
updateMessage = update;
|
|
221
221
|
} catch {
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
-
const { startInteractive } = await import("./ui-
|
|
224
|
+
const { startInteractive } = await import("./ui-ICUWKBUG.js");
|
|
225
225
|
return startInteractive(rt, { initialPrompt: args.prompt, resume: args.resume, version: VERSION, updateMessage });
|
|
226
226
|
}
|
|
227
227
|
main().then(
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live assistant text is drawn inside the Ink frame, so on a real terminal it is capped to the rows left after the
|
|
3
|
+
* prompt and status bar; the hidden part is announced instead of silently dropped. Piped stdout has no rows to
|
|
4
|
+
* respect (`bccli | tee log`, CI harnesses), so the text is kept whole there.
|
|
5
|
+
*/
|
|
6
|
+
export declare function capLiveMarkdown(raw: string, options: {
|
|
7
|
+
isRealTty: boolean;
|
|
8
|
+
maxLines: number;
|
|
9
|
+
}): string;
|
|
@@ -15,8 +15,13 @@ export declare function clickedThinking(click: MousePress, cursorRow: number, ro
|
|
|
15
15
|
width: number;
|
|
16
16
|
height: number;
|
|
17
17
|
}): boolean;
|
|
18
|
-
/** Detect raw SGR
|
|
18
|
+
/** Detect raw SGR wheel packets in a raw stdin data chunk (before ESC is stripped), used to suspend mouse capture. */
|
|
19
19
|
export declare function hasMouseWheelSequence(chunk: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Sequence fragments Ink 8 does not recognise (mouse, cursor reports) are dropped when complete, but an incomplete
|
|
22
|
+
* escape is flushed as literal input after ~20 ms. That fragment must never be typed into a prompt or filter.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isControlReportFragment(input: string): boolean;
|
|
20
25
|
/** Tokenize split/coalesced Windows ConPTY SGR presses and cursor reports. */
|
|
21
26
|
export declare function parseThinkingReports(value: string): {
|
|
22
27
|
events: Array<{
|
package/dist/ui/tty.d.ts
CHANGED
|
@@ -3,7 +3,9 @@ export declare function isTtyStream(stream: unknown): boolean;
|
|
|
3
3
|
/** True only for a real terminal: a TTY that also exposes a file descriptor. */
|
|
4
4
|
export declare function isRealTty(stream: unknown): boolean;
|
|
5
5
|
/**
|
|
6
|
-
* Terminal width at render time. Reading it instead of listening to `resize` keeps
|
|
7
|
-
*
|
|
6
|
+
* Terminal width at render time. Reading it instead of listening to `resize` keeps the app to the single resize
|
|
7
|
+
* listener Ink already owns (it re-renders the tree on resize), instead of one listener per rendered message.
|
|
8
8
|
*/
|
|
9
9
|
export declare function streamColumns(stream: unknown): number;
|
|
10
|
+
/** Terminal height at render time; same reasoning as `streamColumns`. */
|
|
11
|
+
export declare function streamRows(stream: unknown): number;
|
|
@@ -76,7 +76,7 @@ import { execFile as execFile3 } from "child_process";
|
|
|
76
76
|
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
77
77
|
import { homedir, tmpdir } from "os";
|
|
78
78
|
import { dirname, join as join3, relative as relative2, resolve as resolve2 } from "path";
|
|
79
|
-
import { Box as Box13, Static, Text as Text16, measureElement, useApp, useInput as useInput9, useStdin, useStdout as useStdout2
|
|
79
|
+
import { Box as Box13, Static, Text as Text16, measureElement, useApp, useInput as useInput9, useStdin, useStdout as useStdout2 } from "ink";
|
|
80
80
|
import { useCallback, useEffect as useEffect3, useRef as useRef2, useState as useState10 } from "react";
|
|
81
81
|
|
|
82
82
|
// src/commands.ts
|
|
@@ -558,6 +558,10 @@ function streamColumns(stream) {
|
|
|
558
558
|
const columns = typeof stream === "object" && stream !== null ? stream.columns : void 0;
|
|
559
559
|
return typeof columns === "number" && columns > 0 ? columns : 80;
|
|
560
560
|
}
|
|
561
|
+
function streamRows(stream) {
|
|
562
|
+
const rows = typeof stream === "object" && stream !== null ? stream.rows : void 0;
|
|
563
|
+
return typeof rows === "number" && rows > 0 ? rows : 24;
|
|
564
|
+
}
|
|
561
565
|
|
|
562
566
|
// src/ui/Markdown.tsx
|
|
563
567
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -823,11 +827,69 @@ function EditableText({ state, mask = false, showCursor = true }) {
|
|
|
823
827
|
] });
|
|
824
828
|
}
|
|
825
829
|
|
|
830
|
+
// src/ui/thinkingMouse.ts
|
|
831
|
+
function parseMousePress(value) {
|
|
832
|
+
const start = value.indexOf("\x1B[<");
|
|
833
|
+
if (start < 0) return void 0;
|
|
834
|
+
const match = /^<(\d+);(\d+);(\d+)M/.exec(value.slice(start + 2));
|
|
835
|
+
if (!match) return void 0;
|
|
836
|
+
const button = Number(match[1]);
|
|
837
|
+
if (button !== 0) return void 0;
|
|
838
|
+
const x = Number(match[2]);
|
|
839
|
+
const y = Number(match[3]);
|
|
840
|
+
if (x < 1 || y < 1) return void 0;
|
|
841
|
+
return { x, y };
|
|
842
|
+
}
|
|
843
|
+
function parseCursorPosition(value) {
|
|
844
|
+
const start = value.indexOf("\x1B[");
|
|
845
|
+
if (start < 0) return void 0;
|
|
846
|
+
const match = /^(\d+);(\d+)R/.exec(value.slice(start + 2));
|
|
847
|
+
if (!match) return void 0;
|
|
848
|
+
const row = Number(match[1]);
|
|
849
|
+
const col = Number(match[2]);
|
|
850
|
+
if (row < 1 || col < 1) return void 0;
|
|
851
|
+
return { row, col };
|
|
852
|
+
}
|
|
853
|
+
function clickedThinking(click, cursorRow, rootHeight, target) {
|
|
854
|
+
if (!rootHeight || !target.width || !target.height) return false;
|
|
855
|
+
const origin = cursorRow - rootHeight + 1;
|
|
856
|
+
const row = origin + target.y;
|
|
857
|
+
const col = target.x + 1;
|
|
858
|
+
return (click.y === row || click.y === row - 1) && click.x >= col && click.x < col + Math.min(target.width, 72);
|
|
859
|
+
}
|
|
860
|
+
function hasMouseWheelSequence(chunk) {
|
|
861
|
+
return chunk.includes("\x1B[") && /\[<?6[4567];\d+;\d+[mM]/.test(chunk);
|
|
862
|
+
}
|
|
863
|
+
function isControlReportFragment(input) {
|
|
864
|
+
return /^\[<[\d;]*[mM]?$/.test(input) || /^\[(?:\d+;){2}\d+[mM]$/.test(input) || /^\[(?:\d+;)+\d*R?$/.test(input) || /^\[M[\s\S]{0,3}$/.test(input);
|
|
865
|
+
}
|
|
866
|
+
function parseThinkingReports(value) {
|
|
867
|
+
const events = [];
|
|
868
|
+
let offset = 0;
|
|
869
|
+
while (true) {
|
|
870
|
+
const start = value.indexOf("\x1B[", offset);
|
|
871
|
+
if (start < 0) break;
|
|
872
|
+
const finish = /[a-zA-Z~]/.exec(value.slice(start + 2));
|
|
873
|
+
if (!finish) return { events, rest: value.slice(start) };
|
|
874
|
+
const end = start + 2 + finish.index + 1;
|
|
875
|
+
const raw = value.slice(start, end);
|
|
876
|
+
const press = parseMousePress(raw);
|
|
877
|
+
const cursor = parseCursorPosition(raw);
|
|
878
|
+
if (press) events.push({ type: "mouse", press });
|
|
879
|
+
else if (cursor) events.push({ type: "cursor", cursor });
|
|
880
|
+
offset = end;
|
|
881
|
+
}
|
|
882
|
+
const remaining = value.lastIndexOf("\x1B[");
|
|
883
|
+
if (remaining >= offset) return { events, rest: value.slice(remaining) };
|
|
884
|
+
return { events, rest: value.endsWith("\x1B") ? "\x1B" : "" };
|
|
885
|
+
}
|
|
886
|
+
|
|
826
887
|
// src/ui/LinePrompt.tsx
|
|
827
888
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
828
889
|
function LinePrompt({ label, mask, onSubmit, onCancel }) {
|
|
829
890
|
const [line, setLine] = useState({ value: "", cursor: 0 });
|
|
830
891
|
useInput((input, key) => {
|
|
892
|
+
if (isControlReportFragment(input)) return;
|
|
831
893
|
if (key.return) onSubmit(line.value.trim());
|
|
832
894
|
else if (key.escape) onCancel();
|
|
833
895
|
else if (applyEditKey(line, input, key)) setLine((current) => applyEditKey(current, input, key) ?? current);
|
|
@@ -854,6 +916,7 @@ function ListPicker({ title, items, onPick }) {
|
|
|
854
916
|
const shown = items.filter((i) => i.label.toLowerCase().includes(filter.toLowerCase()));
|
|
855
917
|
const cursor = Math.min(index, Math.max(0, shown.length - 1));
|
|
856
918
|
useInput2((input, key) => {
|
|
919
|
+
if (isControlReportFragment(input)) return;
|
|
857
920
|
if (key.upArrow) setIndex(Math.max(0, cursor - 1));
|
|
858
921
|
else if (key.downArrow) setIndex(Math.min(Math.max(0, shown.length - 1), cursor + 1));
|
|
859
922
|
else if (key.pageUp) setIndex(Math.max(0, cursor - VISIBLE));
|
|
@@ -900,6 +963,7 @@ function ModelPicker({ groups, current, onPick }) {
|
|
|
900
963
|
const clampedIndex = Math.min(index, Math.max(0, selectable.length - 1));
|
|
901
964
|
const cursor = selectable[clampedIndex] ?? -1;
|
|
902
965
|
useInput3((input, key) => {
|
|
966
|
+
if (isControlReportFragment(input)) return;
|
|
903
967
|
if (key.upArrow) setIndex(Math.max(0, clampedIndex - 1));
|
|
904
968
|
else if (key.downArrow) setIndex(Math.min(Math.max(0, selectable.length - 1), clampedIndex + 1));
|
|
905
969
|
else if (key.pageUp) setIndex(Math.max(0, clampedIndex - VISIBLE2));
|
|
@@ -1232,6 +1296,7 @@ function PromptInput({ disabled, history, cwd, onSubmit, extraCommands, injected
|
|
|
1232
1296
|
};
|
|
1233
1297
|
useInput6(
|
|
1234
1298
|
(input, key) => {
|
|
1299
|
+
if (isControlReportFragment(input)) return;
|
|
1235
1300
|
if (suggestions.length) {
|
|
1236
1301
|
if (key.escape) {
|
|
1237
1302
|
setDismissed(true);
|
|
@@ -1379,60 +1444,6 @@ function Spinner({ label, startedAt }) {
|
|
|
1379
1444
|
return /* @__PURE__ */ jsx10(Text10, { color: color(ACCENT), children: `${FRAMES[tick % FRAMES.length]} ${t("{label}\u2026 ({seconds}s \xB7 esc to cancel)", { label, seconds })}` });
|
|
1380
1445
|
}
|
|
1381
1446
|
|
|
1382
|
-
// src/ui/thinkingMouse.ts
|
|
1383
|
-
function parseMousePress(value) {
|
|
1384
|
-
const start = value.indexOf("\x1B[<");
|
|
1385
|
-
if (start < 0) return void 0;
|
|
1386
|
-
const match = /^<(\d+);(\d+);(\d+)M/.exec(value.slice(start + 2));
|
|
1387
|
-
if (!match) return void 0;
|
|
1388
|
-
const button = Number(match[1]);
|
|
1389
|
-
if (button !== 0) return void 0;
|
|
1390
|
-
const x = Number(match[2]);
|
|
1391
|
-
const y = Number(match[3]);
|
|
1392
|
-
if (x < 1 || y < 1) return void 0;
|
|
1393
|
-
return { x, y };
|
|
1394
|
-
}
|
|
1395
|
-
function parseCursorPosition(value) {
|
|
1396
|
-
const start = value.indexOf("\x1B[");
|
|
1397
|
-
if (start < 0) return void 0;
|
|
1398
|
-
const match = /^(\d+);(\d+)R/.exec(value.slice(start + 2));
|
|
1399
|
-
if (!match) return void 0;
|
|
1400
|
-
const row = Number(match[1]);
|
|
1401
|
-
const col = Number(match[2]);
|
|
1402
|
-
if (row < 1 || col < 1) return void 0;
|
|
1403
|
-
return { row, col };
|
|
1404
|
-
}
|
|
1405
|
-
function clickedThinking(click, cursorRow, rootHeight, target) {
|
|
1406
|
-
if (!rootHeight || !target.width || !target.height) return false;
|
|
1407
|
-
const origin = cursorRow - rootHeight + 1;
|
|
1408
|
-
const row = origin + target.y;
|
|
1409
|
-
const col = target.x + 1;
|
|
1410
|
-
return (click.y === row || click.y === row - 1) && click.x >= col && click.x < col + Math.min(target.width, 72);
|
|
1411
|
-
}
|
|
1412
|
-
function hasMouseWheelSequence(chunk) {
|
|
1413
|
-
return chunk.includes("\x1B[") && /\[<?6[4567];\d+;\d+[mM]/.test(chunk);
|
|
1414
|
-
}
|
|
1415
|
-
function parseThinkingReports(value) {
|
|
1416
|
-
const events = [];
|
|
1417
|
-
let offset = 0;
|
|
1418
|
-
while (true) {
|
|
1419
|
-
const start = value.indexOf("\x1B[", offset);
|
|
1420
|
-
if (start < 0) break;
|
|
1421
|
-
const finish = /[a-zA-Z~]/.exec(value.slice(start + 2));
|
|
1422
|
-
if (!finish) return { events, rest: value.slice(start) };
|
|
1423
|
-
const end = start + 2 + finish.index + 1;
|
|
1424
|
-
const raw = value.slice(start, end);
|
|
1425
|
-
const press = parseMousePress(raw);
|
|
1426
|
-
const cursor = parseCursorPosition(raw);
|
|
1427
|
-
if (press) events.push({ type: "mouse", press });
|
|
1428
|
-
else if (cursor) events.push({ type: "cursor", cursor });
|
|
1429
|
-
offset = end;
|
|
1430
|
-
}
|
|
1431
|
-
const remaining = value.lastIndexOf("\x1B[");
|
|
1432
|
-
if (remaining >= offset) return { events, rest: value.slice(remaining) };
|
|
1433
|
-
return { events, rest: value.endsWith("\x1B") ? "\x1B" : "" };
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
1447
|
// src/ui/StatusBar.tsx
|
|
1437
1448
|
import { Text as Text11 } from "ink";
|
|
1438
1449
|
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
@@ -1450,6 +1461,20 @@ function StatusBar({ mode, tokens, busy, model, reasoning = "auto" }) {
|
|
|
1450
1461
|
return /* @__PURE__ */ jsx11(Text11, { color: danger ? color("red") : void 0, dimColor: !danger, children: ` ${LABEL[mode]}${model ? ` \xB7 ${model}` : ""} \xB7 reasoning: ${reasoning} \xB7 ${t("shift+tab switch mode")} \xB7 ${t("{n} tokens", { n: formatTokens(tokens) })} \xB7 ${busy ? t("esc to cancel") : t("/ for commands")}` });
|
|
1451
1462
|
}
|
|
1452
1463
|
|
|
1464
|
+
// src/ui/liveCap.ts
|
|
1465
|
+
function capLiveMarkdown(raw, options) {
|
|
1466
|
+
if (!options.isRealTty) return raw;
|
|
1467
|
+
const lines = raw.split("\n");
|
|
1468
|
+
if (lines.length <= options.maxLines) return raw;
|
|
1469
|
+
const hidden = lines.length - options.maxLines;
|
|
1470
|
+
const headFence = lines.slice(0, hidden).find((line) => line.trimStart().startsWith("```"));
|
|
1471
|
+
const prefix = headFence ? `${headFence}
|
|
1472
|
+
\u2026 (${hidden} lines above) \u2026
|
|
1473
|
+
` : `\u2026 (${hidden} lines above) \u2026
|
|
1474
|
+
`;
|
|
1475
|
+
return `${prefix}${lines.slice(-options.maxLines).join("\n")}`;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1453
1478
|
// src/ui/McpMenu.tsx
|
|
1454
1479
|
import { Box as Box9, Text as Text12, useInput as useInput7 } from "ink";
|
|
1455
1480
|
import { useState as useState8 } from "react";
|
|
@@ -1792,8 +1817,7 @@ function App({ runtime, initialPrompt, version, updateMessage }) {
|
|
|
1792
1817
|
const { stdin } = useStdin();
|
|
1793
1818
|
const { stdout } = useStdout2();
|
|
1794
1819
|
const realStdout = isRealTty(stdout);
|
|
1795
|
-
const
|
|
1796
|
-
const maxLiveLines = Math.max(8, windowRows - 10);
|
|
1820
|
+
const maxLiveLines = Math.max(8, streamRows(stdout) - 10);
|
|
1797
1821
|
const maxSplitIdxRef = useRef2(0);
|
|
1798
1822
|
const refreshStatic = useCallback(() => {
|
|
1799
1823
|
if (realStdout) {
|
|
@@ -2649,18 +2673,7 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2649
2673
|
pending?.resolve(a);
|
|
2650
2674
|
setAsks((q) => q.slice(1));
|
|
2651
2675
|
};
|
|
2652
|
-
const
|
|
2653
|
-
if (!isRealTty) return raw;
|
|
2654
|
-
const lines = raw.split("\n");
|
|
2655
|
-
if (lines.length <= maxLiveLines) return raw;
|
|
2656
|
-
const hidden = lines.length - maxLiveLines;
|
|
2657
|
-
const headFence = lines.slice(0, hidden).find((l) => l.trimStart().startsWith("```"));
|
|
2658
|
-
const prefix = headFence ? `${headFence}
|
|
2659
|
-
\u2026 (${hidden} lines above) \u2026
|
|
2660
|
-
` : `\u2026 (${hidden} lines above) \u2026
|
|
2661
|
-
`;
|
|
2662
|
-
return `${prefix}${lines.slice(-maxLiveLines).join("\n")}`;
|
|
2663
|
-
};
|
|
2676
|
+
const capLive = (raw) => capLiveMarkdown(raw, { isRealTty: realStdout, maxLines: maxLiveLines });
|
|
2664
2677
|
const cwd = runtime.cwd.startsWith(homedir()) ? `~${runtime.cwd.slice(homedir().length)}` : runtime.cwd;
|
|
2665
2678
|
const renderEntry = (e) => {
|
|
2666
2679
|
const isLive = transcript.live.some((le) => le.id === e.id);
|
|
@@ -2674,7 +2687,7 @@ ARGUMENTS: ${slash.args}`);
|
|
|
2674
2687
|
case "user":
|
|
2675
2688
|
return /* @__PURE__ */ jsx16(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: `> ${e.text}` }) }, e.id);
|
|
2676
2689
|
case "assistant": {
|
|
2677
|
-
const displayText = isLive ?
|
|
2690
|
+
const displayText = isLive ? capLive(e.text) : e.text;
|
|
2678
2691
|
if (e.cont) {
|
|
2679
2692
|
return displayText ? /* @__PURE__ */ jsx16(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Markdown, { text: displayText }) }, e.id) : null;
|
|
2680
2693
|
}
|