aemeathcli 1.0.12 → 1.1.0
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/{App-JQ622M66.js → App-NT6MRKQJ.js} +468 -119
- package/dist/App-NT6MRKQJ.js.map +1 -0
- package/dist/cli.js +1 -1
- package/package.json +1 -1
- package/dist/App-JQ622M66.js.map +0 -1
|
@@ -11,39 +11,18 @@ import { DEFAULT_CONFIG, PACKAGE_VERSION } from './chunk-2Y7TR6BS.js';
|
|
|
11
11
|
import './chunk-IR5HLBMH.js';
|
|
12
12
|
import './chunk-D275MCIH.js';
|
|
13
13
|
import React11, { useRef, useState, useCallback, useEffect, useMemo, startTransition } from 'react';
|
|
14
|
-
import { Box, Text, render, useInput } from 'ink';
|
|
14
|
+
import { Box, Text, render, useInput, useStdout } from 'ink';
|
|
15
15
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
16
16
|
import { randomUUID } from 'crypto';
|
|
17
17
|
|
|
18
18
|
// src/ui/theme.ts
|
|
19
19
|
var BRAND_COLOR = "#F0C5DA";
|
|
20
20
|
var colors = {
|
|
21
|
-
text: {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
response: "#F9F5F5"
|
|
27
|
-
},
|
|
28
|
-
border: {
|
|
29
|
-
dim: "#6b5459",
|
|
30
|
-
active: "#d3acb3"},
|
|
31
|
-
syntax: {
|
|
32
|
-
keyword: "#F0C5DA",
|
|
33
|
-
string: "#EDD6DC"},
|
|
34
|
-
status: {
|
|
35
|
-
success: "#F0C5DA",
|
|
36
|
-
error: "#f87171",
|
|
37
|
-
warning: "#EDD6DC",
|
|
38
|
-
info: "#F0C5DA",
|
|
39
|
-
active: "#F0C5DA"
|
|
40
|
-
},
|
|
41
|
-
role: {
|
|
42
|
-
user: "#F0C5DA",
|
|
43
|
-
assistant: "#F9F5F5",
|
|
44
|
-
system: "#EDD6DC",
|
|
45
|
-
tool: "#d3acb3"
|
|
46
|
-
}
|
|
21
|
+
text: { primary: "#F9F5F5", secondary: "#d3acb3", muted: "#9e8085", accent: "#F0C5DA", response: "#F9F5F5" },
|
|
22
|
+
border: { dim: "#6b5459", active: "#d3acb3"},
|
|
23
|
+
syntax: { keyword: "#F0C5DA", string: "#EDD6DC"},
|
|
24
|
+
status: { success: "#F0C5DA", error: "#f87171", warning: "#EDD6DC", info: "#F0C5DA", active: "#F0C5DA" },
|
|
25
|
+
role: { user: "#F0C5DA", assistant: "#F9F5F5", system: "#EDD6DC", tool: "#d3acb3" }
|
|
47
26
|
};
|
|
48
27
|
function parseBlocks(raw) {
|
|
49
28
|
const blocks = [];
|
|
@@ -180,12 +159,24 @@ function InlineMarkdown({
|
|
|
180
159
|
}
|
|
181
160
|
const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)]+)\)/);
|
|
182
161
|
if (linkMatch) {
|
|
162
|
+
const linkLabel = linkMatch[1] ?? "";
|
|
163
|
+
const linkUrl = linkMatch[2] ?? "";
|
|
164
|
+
const isLocalPath = linkUrl.startsWith("/") || linkUrl.startsWith("./") || linkUrl.startsWith("../");
|
|
165
|
+
const displayText = isLocalPath ? linkUrl : linkLabel;
|
|
183
166
|
segments.push(
|
|
184
|
-
/* @__PURE__ */ jsx(Text, { color: colors.status.info, underline: true, children:
|
|
167
|
+
/* @__PURE__ */ jsx(Text, { color: colors.status.info, underline: true, children: displayText }, key++)
|
|
185
168
|
);
|
|
186
169
|
remaining = remaining.slice(linkMatch[0].length);
|
|
187
170
|
continue;
|
|
188
171
|
}
|
|
172
|
+
const bareUrlMatch = remaining.match(/^(https?:\/\/[^\s)>\]]+)/);
|
|
173
|
+
if (bareUrlMatch) {
|
|
174
|
+
segments.push(
|
|
175
|
+
/* @__PURE__ */ jsx(Text, { color: colors.status.info, underline: true, children: bareUrlMatch[1] }, key++)
|
|
176
|
+
);
|
|
177
|
+
remaining = remaining.slice(bareUrlMatch[0].length);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
189
180
|
const nextSpecial = remaining.search(/[[*`~]/);
|
|
190
181
|
if (nextSpecial === -1) {
|
|
191
182
|
segments.push(/* @__PURE__ */ jsx(Text, { children: remaining }, key++));
|
|
@@ -367,6 +358,96 @@ function useAnimationTick(intervalMs, enabled = true) {
|
|
|
367
358
|
}, [enabled, intervalMs]);
|
|
368
359
|
return tick;
|
|
369
360
|
}
|
|
361
|
+
|
|
362
|
+
// src/ui/shimmer.ts
|
|
363
|
+
var DEFAULT_PERIOD_MS = 2e3;
|
|
364
|
+
var BAND_WIDTH = 10;
|
|
365
|
+
var MAX_INTENSITY = 0.9;
|
|
366
|
+
var trueColorCached;
|
|
367
|
+
function supportsTrueColor() {
|
|
368
|
+
if (trueColorCached === void 0) {
|
|
369
|
+
const ct = process.env["COLORTERM"] ?? "";
|
|
370
|
+
trueColorCached = ct === "truecolor" || ct === "24bit";
|
|
371
|
+
}
|
|
372
|
+
return trueColorCached;
|
|
373
|
+
}
|
|
374
|
+
function parseHex(hex) {
|
|
375
|
+
let raw = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
376
|
+
if (raw.length === 3) {
|
|
377
|
+
const c0 = raw[0] ?? "f";
|
|
378
|
+
const c1 = raw[1] ?? "f";
|
|
379
|
+
const c2 = raw[2] ?? "f";
|
|
380
|
+
raw = c0 + c0 + c1 + c1 + c2 + c2;
|
|
381
|
+
}
|
|
382
|
+
if (raw.length !== 6) return [255, 255, 255];
|
|
383
|
+
const n = parseInt(raw, 16);
|
|
384
|
+
if (Number.isNaN(n)) return [255, 255, 255];
|
|
385
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
386
|
+
}
|
|
387
|
+
function toHex(r, g, b) {
|
|
388
|
+
const c = (v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, "0");
|
|
389
|
+
return "#" + c(r) + c(g) + c(b);
|
|
390
|
+
}
|
|
391
|
+
function blendChannel(base, intensity) {
|
|
392
|
+
return base + (255 - base) * intensity;
|
|
393
|
+
}
|
|
394
|
+
function shimmerIntensity(charIndex, totalLength, periodMs = DEFAULT_PERIOD_MS) {
|
|
395
|
+
const totalWidth = totalLength + BAND_WIDTH;
|
|
396
|
+
const progress = Date.now() % periodMs / periodMs;
|
|
397
|
+
const bandCenter = progress * totalWidth;
|
|
398
|
+
const distance = Math.abs(charIndex - bandCenter) / (totalWidth / 2);
|
|
399
|
+
return Math.max(0, Math.cos(distance * (Math.PI / 2))) * MAX_INTENSITY;
|
|
400
|
+
}
|
|
401
|
+
function shimmerText(text, baseColor) {
|
|
402
|
+
const len = text.length;
|
|
403
|
+
const trueColor = supportsTrueColor();
|
|
404
|
+
const [br, bg, bb] = parseHex(baseColor);
|
|
405
|
+
const result = [];
|
|
406
|
+
for (let i = 0; i < len; i++) {
|
|
407
|
+
const intensity = shimmerIntensity(i, len);
|
|
408
|
+
if (trueColor) {
|
|
409
|
+
const color = toHex(
|
|
410
|
+
blendChannel(br, intensity),
|
|
411
|
+
blendChannel(bg, intensity),
|
|
412
|
+
blendChannel(bb, intensity)
|
|
413
|
+
);
|
|
414
|
+
const ch = text[i] ?? " ";
|
|
415
|
+
result.push({ char: ch, intensity, color, bold: false, dim: false });
|
|
416
|
+
} else {
|
|
417
|
+
const bold = intensity > 0.6;
|
|
418
|
+
const dim = intensity <= 0.1;
|
|
419
|
+
const ch = text[i] ?? " ";
|
|
420
|
+
result.push({ char: ch, intensity, color: baseColor, bold, dim });
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
function shimmerToInkSpans(text, baseColor) {
|
|
426
|
+
const chars = shimmerText(text, baseColor);
|
|
427
|
+
if (chars.length === 0) return [];
|
|
428
|
+
const spans = [];
|
|
429
|
+
const first = chars[0];
|
|
430
|
+
if (first === void 0) return [];
|
|
431
|
+
let curText = first.char;
|
|
432
|
+
let curColor = first.color;
|
|
433
|
+
let curBold = first.bold;
|
|
434
|
+
let curDim = first.dim;
|
|
435
|
+
for (let i = 1; i < chars.length; i++) {
|
|
436
|
+
const ch = chars[i];
|
|
437
|
+
if (ch === void 0) continue;
|
|
438
|
+
if (ch.color === curColor && ch.bold === curBold && ch.dim === curDim) {
|
|
439
|
+
curText += ch.char;
|
|
440
|
+
} else {
|
|
441
|
+
spans.push({ text: curText, color: curColor, bold: curBold, dim: curDim });
|
|
442
|
+
curText = ch.char;
|
|
443
|
+
curColor = ch.color;
|
|
444
|
+
curBold = ch.bold;
|
|
445
|
+
curDim = ch.dim;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
spans.push({ text: curText, color: curColor, bold: curBold, dim: curDim });
|
|
449
|
+
return spans;
|
|
450
|
+
}
|
|
370
451
|
var SPINNER_VARIANTS = {
|
|
371
452
|
dots: {
|
|
372
453
|
frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"],
|
|
@@ -389,11 +470,13 @@ var SPINNER_VARIANTS = {
|
|
|
389
470
|
interval: 120
|
|
390
471
|
}
|
|
391
472
|
};
|
|
473
|
+
var SHIMMER_INTERVAL_MS = 32;
|
|
392
474
|
function GradientSpinner({
|
|
393
475
|
label,
|
|
394
476
|
labelColor = "#888888",
|
|
395
477
|
variant = "dots",
|
|
396
|
-
speed
|
|
478
|
+
speed,
|
|
479
|
+
shimmer = false
|
|
397
480
|
}) {
|
|
398
481
|
const spinnerDef = SPINNER_VARIANTS[variant] ?? SPINNER_VARIANTS["dots"];
|
|
399
482
|
if (!spinnerDef) {
|
|
@@ -401,10 +484,27 @@ function GradientSpinner({
|
|
|
401
484
|
}
|
|
402
485
|
const interval = speed ?? spinnerDef.interval;
|
|
403
486
|
const tick = useAnimationTick(interval);
|
|
487
|
+
useAnimationTick(SHIMMER_INTERVAL_MS, shimmer && label !== void 0 && label.length > 0);
|
|
404
488
|
const frame = tick % spinnerDef.frames.length;
|
|
489
|
+
const shimmerSpans = useMemo(() => {
|
|
490
|
+
if (!shimmer || !label) return null;
|
|
491
|
+
return shimmerToInkSpans(label, labelColor);
|
|
492
|
+
}, [shimmer, label, labelColor, tick]);
|
|
405
493
|
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
406
494
|
/* @__PURE__ */ jsx(Text, { color: BRAND_COLOR, children: spinnerDef.frames[frame] }),
|
|
407
|
-
label ? /* @__PURE__ */ jsxs(Text, {
|
|
495
|
+
label ? shimmerSpans ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
496
|
+
" ",
|
|
497
|
+
shimmerSpans.map((span, i) => /* @__PURE__ */ jsx(
|
|
498
|
+
Text,
|
|
499
|
+
{
|
|
500
|
+
color: span.color,
|
|
501
|
+
bold: span.bold,
|
|
502
|
+
dimColor: span.dim,
|
|
503
|
+
children: span.text
|
|
504
|
+
},
|
|
505
|
+
i
|
|
506
|
+
))
|
|
507
|
+
] }) : /* @__PURE__ */ jsxs(Text, { color: labelColor, children: [
|
|
408
508
|
" ",
|
|
409
509
|
label
|
|
410
510
|
] }) : null
|
|
@@ -428,31 +528,62 @@ function StatusIcon({
|
|
|
428
528
|
}
|
|
429
529
|
function formatDuration(ms) {
|
|
430
530
|
if (ms < 1e3) return `${ms}ms`;
|
|
431
|
-
|
|
531
|
+
const totalSec = Math.floor(ms / 1e3);
|
|
532
|
+
if (totalSec < 60) {
|
|
533
|
+
const frac = ms % 1e3;
|
|
534
|
+
return frac >= 100 ? `${(ms / 1e3).toFixed(1)}s` : `${totalSec}s`;
|
|
535
|
+
}
|
|
536
|
+
const minutes = Math.floor(totalSec / 60);
|
|
537
|
+
const seconds = totalSec % 60;
|
|
538
|
+
if (minutes < 60) {
|
|
539
|
+
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
540
|
+
}
|
|
541
|
+
const hours = Math.floor(minutes / 60);
|
|
542
|
+
const remainMin = minutes % 60;
|
|
543
|
+
return seconds > 0 ? `${hours}h ${remainMin}m ${seconds}s` : `${hours}h ${remainMin}m`;
|
|
432
544
|
}
|
|
545
|
+
var TOOL_ICONS = {
|
|
546
|
+
read: "\u{1F4C4}",
|
|
547
|
+
write: "\u270F\uFE0F",
|
|
548
|
+
edit: "\u{1F4DD}",
|
|
549
|
+
glob: "\u{1F50D}",
|
|
550
|
+
grep: "\u{1F50E}",
|
|
551
|
+
bash: "\u26A1",
|
|
552
|
+
web_search: "\u{1F310}",
|
|
553
|
+
webSearch: "\u{1F310}",
|
|
554
|
+
web_fetch: "\u{1F4E1}",
|
|
555
|
+
webFetch: "\u{1F4E1}"
|
|
556
|
+
};
|
|
433
557
|
function getToolIcon(name) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
return "\u{1F310}";
|
|
450
|
-
case "web_fetch":
|
|
451
|
-
case "webFetch":
|
|
452
|
-
return "\u{1F4E1}";
|
|
453
|
-
default:
|
|
454
|
-
return "\u2699";
|
|
558
|
+
return TOOL_ICONS[name] ?? "\u2699";
|
|
559
|
+
}
|
|
560
|
+
var HEAD_LINES = 5;
|
|
561
|
+
var TAIL_LINES = 2;
|
|
562
|
+
function formatOutputLines(raw) {
|
|
563
|
+
const allLines = raw.split("\n");
|
|
564
|
+
if (allLines.length > 0 && allLines[allLines.length - 1] === "") {
|
|
565
|
+
allLines.pop();
|
|
566
|
+
}
|
|
567
|
+
const total = allLines.length;
|
|
568
|
+
if (total <= HEAD_LINES + TAIL_LINES) {
|
|
569
|
+
return allLines.map((line, i) => {
|
|
570
|
+
const prefix = i === 0 ? " \u2514 " : " ";
|
|
571
|
+
return `${prefix}${line}`;
|
|
572
|
+
});
|
|
455
573
|
}
|
|
574
|
+
const head = allLines.slice(0, HEAD_LINES);
|
|
575
|
+
const tail = allLines.slice(total - TAIL_LINES);
|
|
576
|
+
const omitted = total - HEAD_LINES - TAIL_LINES;
|
|
577
|
+
const result = [];
|
|
578
|
+
head.forEach((line, i) => {
|
|
579
|
+
const prefix = i === 0 ? " \u2514 " : " ";
|
|
580
|
+
result.push(`${prefix}${line}`);
|
|
581
|
+
});
|
|
582
|
+
result.push(` \u2026 +${omitted} lines`);
|
|
583
|
+
tail.forEach((line) => {
|
|
584
|
+
result.push(` ${line}`);
|
|
585
|
+
});
|
|
586
|
+
return result;
|
|
456
587
|
}
|
|
457
588
|
function ToolCallDisplay({
|
|
458
589
|
toolName,
|
|
@@ -463,7 +594,6 @@ function ToolCallDisplay({
|
|
|
463
594
|
duration,
|
|
464
595
|
isCollapsed = true
|
|
465
596
|
}) {
|
|
466
|
-
const borderColor = status === "error" ? colors.status.error : status === "executing" ? colors.status.active : colors.border.dim;
|
|
467
597
|
const icon = getToolIcon(toolName);
|
|
468
598
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 0, children: [
|
|
469
599
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
@@ -485,28 +615,16 @@ function ToolCallDisplay({
|
|
|
485
615
|
")"
|
|
486
616
|
] }) : null
|
|
487
617
|
] }),
|
|
488
|
-
!isCollapsed && output ? /* @__PURE__ */ jsx(
|
|
489
|
-
|
|
618
|
+
!isCollapsed && output ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: formatOutputLines(output).map((line, i) => /* @__PURE__ */ jsx(
|
|
619
|
+
Text,
|
|
490
620
|
{
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
borderColor,
|
|
499
|
-
paddingLeft: 1,
|
|
500
|
-
children: /* @__PURE__ */ jsx(
|
|
501
|
-
Text,
|
|
502
|
-
{
|
|
503
|
-
wrap: "wrap",
|
|
504
|
-
color: isError ? colors.status.error : colors.text.secondary,
|
|
505
|
-
children: output.length > 2e3 ? output.slice(0, 2e3) + "\n\u2026 (truncated)" : output
|
|
506
|
-
}
|
|
507
|
-
)
|
|
508
|
-
}
|
|
509
|
-
) : null
|
|
621
|
+
wrap: "truncate",
|
|
622
|
+
dimColor: true,
|
|
623
|
+
color: isError ? colors.status.error : colors.text.secondary,
|
|
624
|
+
children: line
|
|
625
|
+
},
|
|
626
|
+
i
|
|
627
|
+
)) }) : null
|
|
510
628
|
] });
|
|
511
629
|
}
|
|
512
630
|
function getRoleColor(role) {
|
|
@@ -966,6 +1084,10 @@ function InputBar({
|
|
|
966
1084
|
const isAutocompleteActiveRef = useRef(false);
|
|
967
1085
|
const previousHistoryIndexRef = useRef(void 0);
|
|
968
1086
|
const historyCacheRef = useRef({});
|
|
1087
|
+
const lastInputTimeRef = useRef(0);
|
|
1088
|
+
const pasteBufferRef = useRef("");
|
|
1089
|
+
const pasteTimeoutRef = useRef(null);
|
|
1090
|
+
const isPastingRef = useRef(false);
|
|
969
1091
|
const setInputWithCursor = useCallback(
|
|
970
1092
|
(nextInput, cursorPosition = "end") => {
|
|
971
1093
|
const nextCursor = cursorPosition === "start" ? 0 : cursorPosition === "end" ? codePointLength(nextInput) : clampCursorOffset(nextInput, cursorPosition);
|
|
@@ -1032,6 +1154,17 @@ function InputBar({
|
|
|
1032
1154
|
});
|
|
1033
1155
|
}
|
|
1034
1156
|
}, [initialHistory]);
|
|
1157
|
+
useEffect(() => {
|
|
1158
|
+
process.stdout.write("\x1B[?2004h");
|
|
1159
|
+
return () => {
|
|
1160
|
+
process.stdout.write("\x1B[?2004l");
|
|
1161
|
+
};
|
|
1162
|
+
}, []);
|
|
1163
|
+
useEffect(() => {
|
|
1164
|
+
return () => {
|
|
1165
|
+
if (pasteTimeoutRef.current) clearTimeout(pasteTimeoutRef.current);
|
|
1166
|
+
};
|
|
1167
|
+
}, []);
|
|
1035
1168
|
useEffect(() => {
|
|
1036
1169
|
inputRef.current = input;
|
|
1037
1170
|
cursorOffsetRef.current = cursorOffset;
|
|
@@ -1178,6 +1311,24 @@ function InputBar({
|
|
|
1178
1311
|
}
|
|
1179
1312
|
if (key.ctrl && inputChar === "l") return;
|
|
1180
1313
|
if (!key.ctrl && !key.meta && inputChar) {
|
|
1314
|
+
const now = Date.now();
|
|
1315
|
+
const timeSinceLastInput = now - lastInputTimeRef.current;
|
|
1316
|
+
lastInputTimeRef.current = now;
|
|
1317
|
+
if (timeSinceLastInput < 5) {
|
|
1318
|
+
isPastingRef.current = true;
|
|
1319
|
+
pasteBufferRef.current += inputChar;
|
|
1320
|
+
if (pasteTimeoutRef.current) clearTimeout(pasteTimeoutRef.current);
|
|
1321
|
+
pasteTimeoutRef.current = setTimeout(() => {
|
|
1322
|
+
const pasteText = pasteBufferRef.current;
|
|
1323
|
+
pasteBufferRef.current = "";
|
|
1324
|
+
isPastingRef.current = false;
|
|
1325
|
+
if (pasteText) {
|
|
1326
|
+
const result2 = insertTextAtCursor(inputRef.current, cursorOffsetRef.current, pasteText);
|
|
1327
|
+
setInputWithCursor(result2.text, result2.cursorOffset);
|
|
1328
|
+
}
|
|
1329
|
+
}, 10);
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1181
1332
|
const result = insertTextAtCursor(currentInput, currentCursorOffset, inputChar);
|
|
1182
1333
|
setInputWithCursor(result.text, result.cursorOffset);
|
|
1183
1334
|
}
|
|
@@ -1238,6 +1389,12 @@ function StatusBar({
|
|
|
1238
1389
|
gitBranch,
|
|
1239
1390
|
gitChanges
|
|
1240
1391
|
}) {
|
|
1392
|
+
const { stdout } = useStdout();
|
|
1393
|
+
const width = stdout.columns ?? 120;
|
|
1394
|
+
const showRole = width > 100 && !!role;
|
|
1395
|
+
const showGit = width > 60 && !!gitBranch;
|
|
1396
|
+
const showTokens = width > 45;
|
|
1397
|
+
const showCost = width > 45;
|
|
1241
1398
|
return /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: colors.border.dim, paddingX: 1, children: [
|
|
1242
1399
|
/* @__PURE__ */ jsxs(Text, { color: BRAND_COLOR, bold: true, children: [
|
|
1243
1400
|
"\u25C6",
|
|
@@ -1246,18 +1403,22 @@ function StatusBar({
|
|
|
1246
1403
|
/* @__PURE__ */ jsx(Text, { color: colors.status.active, bold: true, children: "Aemeath Agent Swarm" }),
|
|
1247
1404
|
/* @__PURE__ */ jsx(Text, { color: colors.text.muted, children: SEP }),
|
|
1248
1405
|
/* @__PURE__ */ jsx(Text, { color: colors.status.warning, bold: true, children: shortModelLabel(model) }),
|
|
1249
|
-
|
|
1406
|
+
showRole ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1250
1407
|
/* @__PURE__ */ jsx(Text, { color: colors.text.muted, children: SEP }),
|
|
1251
1408
|
/* @__PURE__ */ jsx(Text, { color: colors.role.tool, children: role })
|
|
1252
1409
|
] }) : null,
|
|
1253
|
-
/* @__PURE__ */
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1410
|
+
showTokens ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1411
|
+
/* @__PURE__ */ jsx(Text, { color: colors.text.muted, children: SEP }),
|
|
1412
|
+
/* @__PURE__ */ jsxs(Text, { color: colors.text.secondary, children: [
|
|
1413
|
+
tokenCount,
|
|
1414
|
+
" tok"
|
|
1415
|
+
] })
|
|
1416
|
+
] }) : null,
|
|
1417
|
+
showCost ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1418
|
+
/* @__PURE__ */ jsx(Text, { color: colors.text.muted, children: SEP }),
|
|
1419
|
+
/* @__PURE__ */ jsx(Text, { color: colors.status.success, children: cost })
|
|
1420
|
+
] }) : null,
|
|
1421
|
+
showGit ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1261
1422
|
/* @__PURE__ */ jsx(Text, { color: colors.text.muted, children: SEP }),
|
|
1262
1423
|
/* @__PURE__ */ jsxs(Text, { color: colors.status.info, children: [
|
|
1263
1424
|
"\u2387",
|
|
@@ -1278,26 +1439,49 @@ var THINKING_PHRASES = [
|
|
|
1278
1439
|
"Evaluating"
|
|
1279
1440
|
];
|
|
1280
1441
|
var PHRASE_CYCLE_MS = 2500;
|
|
1442
|
+
function formatElapsed(ms) {
|
|
1443
|
+
const totalSecs = Math.floor(ms / 1e3);
|
|
1444
|
+
if (totalSecs < 1) return "0s";
|
|
1445
|
+
if (totalSecs < 60) return `${totalSecs}s`;
|
|
1446
|
+
const mins = Math.floor(totalSecs / 60);
|
|
1447
|
+
const secs = totalSecs % 60;
|
|
1448
|
+
if (mins < 60) return `${mins}m ${String(secs).padStart(2, "0")}s`;
|
|
1449
|
+
const hrs = Math.floor(mins / 60);
|
|
1450
|
+
const remMins = mins % 60;
|
|
1451
|
+
return `${hrs}h ${String(remMins).padStart(2, "0")}m ${String(secs).padStart(2, "0")}s`;
|
|
1452
|
+
}
|
|
1281
1453
|
function ThinkingIndicator({
|
|
1282
1454
|
activity,
|
|
1283
1455
|
isStreaming,
|
|
1284
1456
|
modelName,
|
|
1285
|
-
startTime
|
|
1457
|
+
startTime,
|
|
1458
|
+
isPaused = false,
|
|
1459
|
+
details,
|
|
1460
|
+
maxDetailLines = 3
|
|
1286
1461
|
}) {
|
|
1287
1462
|
const tick = useAnimationTick(1e3);
|
|
1288
|
-
const
|
|
1463
|
+
const pausedAccumRef = useRef(0);
|
|
1464
|
+
const pauseStartRef = useRef(void 0);
|
|
1465
|
+
if (isPaused && pauseStartRef.current === void 0) {
|
|
1466
|
+
pauseStartRef.current = Date.now();
|
|
1467
|
+
} else if (!isPaused && pauseStartRef.current !== void 0) {
|
|
1468
|
+
pausedAccumRef.current += Date.now() - pauseStartRef.current;
|
|
1469
|
+
pauseStartRef.current = void 0;
|
|
1470
|
+
}
|
|
1471
|
+
const now = isPaused ? pauseStartRef.current ?? Date.now() : Date.now();
|
|
1472
|
+
const rawElapsed = startTime === void 0 ? 0 : Math.max(0, now - startTime);
|
|
1473
|
+
const elapsed = Math.max(0, rawElapsed - pausedAccumRef.current);
|
|
1289
1474
|
const phraseIndex = Math.floor(elapsed / PHRASE_CYCLE_MS) % THINKING_PHRASES.length;
|
|
1290
1475
|
const elapsedStr = useMemo(() => {
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
if (secs < 60) return `${secs}s`;
|
|
1294
|
-
const mins = Math.floor(secs / 60);
|
|
1295
|
-
return `${mins}m${secs % 60}s`;
|
|
1476
|
+
if (elapsed < 1e3) return "";
|
|
1477
|
+
return formatElapsed(elapsed);
|
|
1296
1478
|
}, [elapsed]);
|
|
1297
1479
|
const dotCount = tick % 4;
|
|
1298
1480
|
const dots = ".".repeat(dotCount);
|
|
1299
1481
|
const phrase = THINKING_PHRASES[phraseIndex] ?? "Thinking";
|
|
1300
|
-
const displayText = activity ? activity : isStreaming ? "Streaming response" : `${phrase}${dots}`;
|
|
1482
|
+
const displayText = isPaused ? "Rate limited \u2014 waiting" : activity ? activity : isStreaming ? "Streaming response" : `${phrase}${dots}`;
|
|
1483
|
+
const visibleDetails = details ? details.slice(0, maxDetailLines) : void 0;
|
|
1484
|
+
const hiddenDetailCount = details ? Math.max(0, details.length - maxDetailLines) : 0;
|
|
1301
1485
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1302
1486
|
modelName ? /* @__PURE__ */ jsxs(Box, { children: [
|
|
1303
1487
|
/* @__PURE__ */ jsxs(Text, { color: colors.role.assistant, bold: true, children: [
|
|
@@ -1310,21 +1494,35 @@ function ThinkingIndicator({
|
|
|
1310
1494
|
/* @__PURE__ */ jsx(
|
|
1311
1495
|
GradientSpinner,
|
|
1312
1496
|
{
|
|
1313
|
-
variant: activity ? "braille" : "dots",
|
|
1497
|
+
variant: isPaused ? "pulse" : activity ? "braille" : "dots",
|
|
1314
1498
|
label: displayText,
|
|
1315
|
-
labelColor: activity ? colors.text.secondary : colors.text.muted
|
|
1499
|
+
labelColor: isPaused ? colors.status.warning : activity ? colors.text.secondary : colors.text.muted
|
|
1316
1500
|
}
|
|
1317
1501
|
),
|
|
1318
1502
|
elapsedStr ? /* @__PURE__ */ jsxs(Text, { color: colors.text.muted, children: [
|
|
1319
|
-
"
|
|
1503
|
+
" ",
|
|
1504
|
+
"(",
|
|
1320
1505
|
elapsedStr,
|
|
1321
|
-
"
|
|
1322
|
-
|
|
1506
|
+
" ",
|
|
1507
|
+
"\u2022",
|
|
1508
|
+
" esc to interrupt)"
|
|
1509
|
+
] }) : /* @__PURE__ */ jsxs(Text, { color: colors.text.muted, children: [
|
|
1510
|
+
" ",
|
|
1511
|
+
"(esc to interrupt)"
|
|
1512
|
+
] })
|
|
1323
1513
|
] }),
|
|
1324
|
-
|
|
1325
|
-
"
|
|
1326
|
-
|
|
1327
|
-
|
|
1514
|
+
visibleDetails && visibleDetails.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
|
|
1515
|
+
visibleDetails.map((detail, i) => /* @__PURE__ */ jsxs(Text, { color: colors.text.muted, dimColor: true, wrap: "truncate", children: [
|
|
1516
|
+
" \u2514 ",
|
|
1517
|
+
detail
|
|
1518
|
+
] }, i)),
|
|
1519
|
+
hiddenDetailCount > 0 ? /* @__PURE__ */ jsxs(Text, { color: colors.text.muted, dimColor: true, children: [
|
|
1520
|
+
" ",
|
|
1521
|
+
"(+",
|
|
1522
|
+
hiddenDetailCount,
|
|
1523
|
+
" more)"
|
|
1524
|
+
] }) : null
|
|
1525
|
+
] }) : null
|
|
1328
1526
|
] });
|
|
1329
1527
|
}
|
|
1330
1528
|
var MASCOT_LINES = [
|
|
@@ -1764,6 +1962,111 @@ function useModel(config, initialModel, initialRole) {
|
|
|
1764
1962
|
router
|
|
1765
1963
|
};
|
|
1766
1964
|
}
|
|
1965
|
+
|
|
1966
|
+
// src/ui/streaming-controller.ts
|
|
1967
|
+
var StreamingController = class {
|
|
1968
|
+
buffer;
|
|
1969
|
+
committed;
|
|
1970
|
+
lineCounter;
|
|
1971
|
+
headerEmitted;
|
|
1972
|
+
/** Index into `committed` marking where the last drain ended. */
|
|
1973
|
+
drainCursor;
|
|
1974
|
+
constructor() {
|
|
1975
|
+
this.buffer = "";
|
|
1976
|
+
this.committed = [];
|
|
1977
|
+
this.lineCounter = 0;
|
|
1978
|
+
this.headerEmitted = false;
|
|
1979
|
+
this.drainCursor = 0;
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Feed a text chunk into the controller.
|
|
1983
|
+
*
|
|
1984
|
+
* Lines terminated by `\n` are committed immediately.
|
|
1985
|
+
* Any trailing text without a newline stays in the buffer.
|
|
1986
|
+
*
|
|
1987
|
+
* @returns Newly committed lines from this chunk.
|
|
1988
|
+
*/
|
|
1989
|
+
push(chunk) {
|
|
1990
|
+
this.buffer += chunk;
|
|
1991
|
+
const newLines = [];
|
|
1992
|
+
let newlineIdx = this.buffer.indexOf("\n");
|
|
1993
|
+
while (newlineIdx !== -1) {
|
|
1994
|
+
const lineText = this.buffer.slice(0, newlineIdx);
|
|
1995
|
+
this.buffer = this.buffer.slice(newlineIdx + 1);
|
|
1996
|
+
this.lineCounter += 1;
|
|
1997
|
+
const committed = {
|
|
1998
|
+
text: lineText,
|
|
1999
|
+
lineNumber: this.lineCounter
|
|
2000
|
+
};
|
|
2001
|
+
this.committed.push(committed);
|
|
2002
|
+
newLines.push(committed);
|
|
2003
|
+
newlineIdx = this.buffer.indexOf("\n");
|
|
2004
|
+
}
|
|
2005
|
+
if (newLines.length > 0) {
|
|
2006
|
+
this.headerEmitted = true;
|
|
2007
|
+
}
|
|
2008
|
+
return newLines;
|
|
2009
|
+
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Flush the remaining buffer as a final committed line.
|
|
2012
|
+
*
|
|
2013
|
+
* Call this when the stream ends to commit any trailing
|
|
2014
|
+
* partial line that never received a terminating newline.
|
|
2015
|
+
*
|
|
2016
|
+
* @returns The flushed line, or `null` if the buffer was empty.
|
|
2017
|
+
*/
|
|
2018
|
+
flush() {
|
|
2019
|
+
if (this.buffer.length === 0) {
|
|
2020
|
+
return null;
|
|
2021
|
+
}
|
|
2022
|
+
this.lineCounter += 1;
|
|
2023
|
+
const committed = {
|
|
2024
|
+
text: this.buffer,
|
|
2025
|
+
lineNumber: this.lineCounter
|
|
2026
|
+
};
|
|
2027
|
+
this.committed.push(committed);
|
|
2028
|
+
this.buffer = "";
|
|
2029
|
+
this.headerEmitted = true;
|
|
2030
|
+
return committed;
|
|
2031
|
+
}
|
|
2032
|
+
/** Get an immutable snapshot of the current controller state. */
|
|
2033
|
+
getState() {
|
|
2034
|
+
return {
|
|
2035
|
+
committedLines: [...this.committed],
|
|
2036
|
+
pendingLine: this.buffer,
|
|
2037
|
+
hasEmittedHeader: this.headerEmitted,
|
|
2038
|
+
totalLines: this.lineCounter
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
/** Get only the pending (incomplete) line text. */
|
|
2042
|
+
getPendingLine() {
|
|
2043
|
+
return this.buffer;
|
|
2044
|
+
}
|
|
2045
|
+
/** Reset the controller to its initial state. */
|
|
2046
|
+
reset() {
|
|
2047
|
+
this.buffer = "";
|
|
2048
|
+
this.committed = [];
|
|
2049
|
+
this.lineCounter = 0;
|
|
2050
|
+
this.headerEmitted = false;
|
|
2051
|
+
this.drainCursor = 0;
|
|
2052
|
+
}
|
|
2053
|
+
/**
|
|
2054
|
+
* Get committed lines since the last call to this method.
|
|
2055
|
+
*
|
|
2056
|
+
* Useful for incremental rendering — each call returns only
|
|
2057
|
+
* the lines that were committed after the previous drain.
|
|
2058
|
+
*/
|
|
2059
|
+
drainNewLines() {
|
|
2060
|
+
const newLines = this.committed.slice(this.drainCursor);
|
|
2061
|
+
this.drainCursor = this.committed.length;
|
|
2062
|
+
return newLines;
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
function createStreamingController() {
|
|
2066
|
+
return new StreamingController();
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
// src/ui/hooks/useStream.ts
|
|
1767
2070
|
function formatToolActivity(toolCall) {
|
|
1768
2071
|
const args = toolCall.arguments;
|
|
1769
2072
|
switch (toolCall.name) {
|
|
@@ -1809,25 +2112,32 @@ function useStream() {
|
|
|
1809
2112
|
const [state, setState] = useState({
|
|
1810
2113
|
isStreaming: false,
|
|
1811
2114
|
content: "",
|
|
2115
|
+
pendingLine: "",
|
|
1812
2116
|
usage: void 0,
|
|
1813
2117
|
error: void 0,
|
|
1814
2118
|
activity: void 0,
|
|
1815
2119
|
toolCalls: [],
|
|
1816
|
-
startTime: void 0
|
|
2120
|
+
startTime: void 0,
|
|
2121
|
+
isPaused: false
|
|
1817
2122
|
});
|
|
1818
2123
|
const cancelRef = useRef(false);
|
|
2124
|
+
const streamingCtrlRef = useRef(null);
|
|
1819
2125
|
const isCancelled = () => cancelRef.current;
|
|
1820
2126
|
const startStream = useCallback(
|
|
1821
2127
|
async (stream) => {
|
|
1822
2128
|
cancelRef.current = false;
|
|
2129
|
+
const ctrl = createStreamingController();
|
|
2130
|
+
streamingCtrlRef.current = ctrl;
|
|
1823
2131
|
setState({
|
|
1824
2132
|
isStreaming: true,
|
|
1825
2133
|
content: "",
|
|
2134
|
+
pendingLine: "",
|
|
1826
2135
|
usage: void 0,
|
|
1827
2136
|
error: void 0,
|
|
1828
2137
|
activity: void 0,
|
|
1829
2138
|
toolCalls: [],
|
|
1830
|
-
startTime: Date.now()
|
|
2139
|
+
startTime: Date.now(),
|
|
2140
|
+
isPaused: false
|
|
1831
2141
|
});
|
|
1832
2142
|
try {
|
|
1833
2143
|
for await (const chunk of stream) {
|
|
@@ -1836,9 +2146,12 @@ function useStream() {
|
|
|
1836
2146
|
case "text":
|
|
1837
2147
|
if (chunk.content !== void 0) {
|
|
1838
2148
|
const text = chunk.content;
|
|
2149
|
+
ctrl.push(text);
|
|
2150
|
+
const ctrlState = ctrl.getState();
|
|
1839
2151
|
setState((prev) => ({
|
|
1840
2152
|
...prev,
|
|
1841
|
-
content:
|
|
2153
|
+
content: ctrlState.committedLines.map((l) => l.text).join("\n"),
|
|
2154
|
+
pendingLine: ctrlState.pendingLine,
|
|
1842
2155
|
activity: void 0
|
|
1843
2156
|
}));
|
|
1844
2157
|
}
|
|
@@ -1894,12 +2207,17 @@ function useStream() {
|
|
|
1894
2207
|
)
|
|
1895
2208
|
}));
|
|
1896
2209
|
return;
|
|
1897
|
-
case "done":
|
|
2210
|
+
case "done": {
|
|
2211
|
+
ctrl.flush();
|
|
2212
|
+
const finalState = ctrl.getState();
|
|
1898
2213
|
setState((prev) => ({
|
|
1899
2214
|
...prev,
|
|
1900
2215
|
isStreaming: false,
|
|
2216
|
+
content: finalState.committedLines.map((l) => l.text).join("\n"),
|
|
2217
|
+
pendingLine: "",
|
|
1901
2218
|
usage: chunk.usage ?? prev.usage,
|
|
1902
2219
|
activity: void 0,
|
|
2220
|
+
isPaused: false,
|
|
1903
2221
|
toolCalls: prev.toolCalls.map(
|
|
1904
2222
|
(tc) => tc.status === "executing" ? {
|
|
1905
2223
|
...tc,
|
|
@@ -1909,12 +2227,18 @@ function useStream() {
|
|
|
1909
2227
|
)
|
|
1910
2228
|
}));
|
|
1911
2229
|
return;
|
|
2230
|
+
}
|
|
1912
2231
|
}
|
|
1913
2232
|
}
|
|
2233
|
+
ctrl.flush();
|
|
2234
|
+
const endState = ctrl.getState();
|
|
1914
2235
|
setState((prev) => ({
|
|
1915
2236
|
...prev,
|
|
1916
2237
|
isStreaming: false,
|
|
2238
|
+
content: endState.committedLines.map((l) => l.text).join("\n"),
|
|
2239
|
+
pendingLine: "",
|
|
1917
2240
|
activity: void 0,
|
|
2241
|
+
isPaused: false,
|
|
1918
2242
|
toolCalls: prev.toolCalls.map(
|
|
1919
2243
|
(tc) => tc.status === "executing" ? {
|
|
1920
2244
|
...tc,
|
|
@@ -1928,7 +2252,8 @@ function useStream() {
|
|
|
1928
2252
|
...prev,
|
|
1929
2253
|
isStreaming: false,
|
|
1930
2254
|
error: error instanceof Error ? error.message : String(error),
|
|
1931
|
-
activity: void 0
|
|
2255
|
+
activity: void 0,
|
|
2256
|
+
isPaused: false
|
|
1932
2257
|
}));
|
|
1933
2258
|
}
|
|
1934
2259
|
},
|
|
@@ -1936,28 +2261,52 @@ function useStream() {
|
|
|
1936
2261
|
);
|
|
1937
2262
|
const cancelStream = useCallback(() => {
|
|
1938
2263
|
cancelRef.current = true;
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
2264
|
+
const ctrl = streamingCtrlRef.current;
|
|
2265
|
+
if (ctrl) {
|
|
2266
|
+
ctrl.flush();
|
|
2267
|
+
const finalState = ctrl.getState();
|
|
2268
|
+
setState((prev) => ({
|
|
2269
|
+
...prev,
|
|
2270
|
+
isStreaming: false,
|
|
2271
|
+
content: finalState.committedLines.map((l) => l.text).join("\n"),
|
|
2272
|
+
pendingLine: "",
|
|
2273
|
+
isPaused: false,
|
|
2274
|
+
toolCalls: prev.toolCalls.map(
|
|
2275
|
+
(tc) => tc.status === "executing" ? {
|
|
2276
|
+
...tc,
|
|
2277
|
+
status: "cancelled",
|
|
2278
|
+
endTime: Date.now()
|
|
2279
|
+
} : tc
|
|
2280
|
+
)
|
|
2281
|
+
}));
|
|
2282
|
+
} else {
|
|
2283
|
+
setState((prev) => ({
|
|
2284
|
+
...prev,
|
|
2285
|
+
isStreaming: false,
|
|
2286
|
+
isPaused: false,
|
|
2287
|
+
toolCalls: prev.toolCalls.map(
|
|
2288
|
+
(tc) => tc.status === "executing" ? {
|
|
2289
|
+
...tc,
|
|
2290
|
+
status: "cancelled",
|
|
2291
|
+
endTime: Date.now()
|
|
2292
|
+
} : tc
|
|
2293
|
+
)
|
|
2294
|
+
}));
|
|
2295
|
+
}
|
|
1950
2296
|
}, []);
|
|
1951
2297
|
const reset = useCallback(() => {
|
|
1952
2298
|
cancelRef.current = true;
|
|
2299
|
+
streamingCtrlRef.current = null;
|
|
1953
2300
|
setState({
|
|
1954
2301
|
isStreaming: false,
|
|
1955
2302
|
content: "",
|
|
2303
|
+
pendingLine: "",
|
|
1956
2304
|
usage: void 0,
|
|
1957
2305
|
error: void 0,
|
|
1958
2306
|
activity: void 0,
|
|
1959
2307
|
toolCalls: [],
|
|
1960
|
-
startTime: void 0
|
|
2308
|
+
startTime: void 0,
|
|
2309
|
+
isPaused: false
|
|
1961
2310
|
});
|
|
1962
2311
|
}, []);
|
|
1963
2312
|
return { state, startStream, cancelStream, reset };
|
|
@@ -4427,5 +4776,5 @@ async function runFirstRunSetup() {
|
|
|
4427
4776
|
}
|
|
4428
4777
|
|
|
4429
4778
|
export { runFirstRunSetup, startChatSession };
|
|
4430
|
-
//# sourceMappingURL=App-
|
|
4431
|
-
//# sourceMappingURL=App-
|
|
4779
|
+
//# sourceMappingURL=App-NT6MRKQJ.js.map
|
|
4780
|
+
//# sourceMappingURL=App-NT6MRKQJ.js.map
|