parallel-codex-tui 0.1.0 → 0.1.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/.parallel-codex/config.example.toml +90 -3
- package/README.md +269 -12
- package/dist/bootstrap.js +50 -18
- package/dist/cli-args.js +96 -14
- package/dist/cli-help.js +20 -0
- package/dist/cli-startup-recovery.js +70 -0
- package/dist/cli-workspace-picker.js +330 -0
- package/dist/cli-workspace-transition.js +33 -0
- package/dist/cli-workspace.js +40 -0
- package/dist/cli.js +291 -35
- package/dist/core/app-root.js +8 -0
- package/dist/core/collaboration-timeline.js +261 -0
- package/dist/core/config-errors.js +14 -0
- package/dist/core/config.js +191 -23
- package/dist/core/file-store.js +130 -6
- package/dist/core/lease-finalization.js +22 -0
- package/dist/core/paths.js +10 -0
- package/dist/core/process-identity.js +48 -0
- package/dist/core/process-mutation-turn.js +128 -0
- package/dist/core/process-ownership.js +276 -0
- package/dist/core/process-tree.js +90 -0
- package/dist/core/router-audit.js +155 -0
- package/dist/core/router-redaction.js +31 -0
- package/dist/core/router.js +473 -42
- package/dist/core/session-index.js +225 -30
- package/dist/core/session-manager.js +1182 -44
- package/dist/core/task-state-machine.js +17 -0
- package/dist/core/workspace-commit-recovery.js +118 -0
- package/dist/core/workspace.js +126 -0
- package/dist/doctor.js +384 -30
- package/dist/domain/schemas.js +127 -6
- package/dist/orchestrator/collaboration-channel.js +255 -4
- package/dist/orchestrator/feature-plan.js +70 -0
- package/dist/orchestrator/judge-artifacts.js +236 -0
- package/dist/orchestrator/orchestrator.js +1777 -212
- package/dist/orchestrator/prompts.js +126 -2
- package/dist/orchestrator/supervisor-summary.js +56 -2
- package/dist/orchestrator/workspace-sandbox.js +911 -0
- package/dist/tui/App.js +2838 -159
- package/dist/tui/AppShell.js +188 -23
- package/dist/tui/CollaborationTimelineView.js +327 -0
- package/dist/tui/FeatureBoardView.js +227 -0
- package/dist/tui/InputBar.js +514 -57
- package/dist/tui/RouterDiagnosticsView.js +469 -0
- package/dist/tui/StatusBar.js +610 -57
- package/dist/tui/TaskSessionsView.js +207 -0
- package/dist/tui/TerminalOutput.js +53 -9
- package/dist/tui/WorkerOutputView.js +1403 -161
- package/dist/tui/WorkerOverviewView.js +250 -0
- package/dist/tui/chat-history.js +25 -0
- package/dist/tui/chat-input.js +67 -19
- package/dist/tui/chat-paste.js +76 -0
- package/dist/tui/display-width.js +41 -3
- package/dist/tui/incremental-text-file.js +101 -0
- package/dist/tui/keyboard.js +46 -0
- package/dist/tui/markdown-text.js +14 -0
- package/dist/tui/raw-input-decoder.js +3 -0
- package/dist/tui/scrolling.js +2 -1
- package/dist/tui/status-line.js +318 -11
- package/dist/tui/task-memory.js +15 -0
- package/dist/tui/task-result.js +105 -0
- package/dist/tui/terminal-screen.js +13 -1
- package/dist/tui/theme-contrast.js +144 -0
- package/dist/tui/theme-preview.js +109 -0
- package/dist/tui/theme.js +158 -0
- package/dist/version.js +1 -1
- package/dist/workers/capabilities.js +212 -0
- package/dist/workers/live-probe.js +176 -0
- package/dist/workers/mock-adapter.js +39 -6
- package/dist/workers/native-attach.js +147 -8
- package/dist/workers/native-session-detection.js +17 -0
- package/dist/workers/process-adapter.js +580 -81
- package/dist/workers/registry.js +4 -2
- package/package.json +17 -2
package/dist/tui/StatusBar.js
CHANGED
|
@@ -1,32 +1,70 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { compactEndByDisplayWidth, displayWidth } from "./display-width.js";
|
|
4
|
-
|
|
5
|
-
export function StatusBar({ text, terminalWidth
|
|
4
|
+
import { TUI_THEME } from "./theme.js";
|
|
5
|
+
export function StatusBar({ text, terminalWidth: providedTerminalWidth, showTask = false, fillRail: providedFillRail }) {
|
|
6
|
+
const terminalWidth = providedTerminalWidth ?? process.stdout.columns ?? 120;
|
|
7
|
+
const fillRail = providedFillRail ?? (providedTerminalWidth !== undefined || typeof process.stdout.columns === "number");
|
|
8
|
+
const { segments: fittedSegments, compact } = resolveStatusBar(text, terminalWidth, showTask);
|
|
9
|
+
if (isIdleStatus(fittedSegments)) {
|
|
10
|
+
return _jsx(IdleStatusRail, { terminalWidth: terminalWidth, fill: fillRail });
|
|
11
|
+
}
|
|
12
|
+
const { leadingWidth, trailingWidth } = statusRailLayout(terminalWidth, statusSegmentsWidth(fittedSegments, compact), { fill: fillRail });
|
|
13
|
+
return (_jsxs(Box, { children: [leadingWidth > 0 ? _jsx(Text, { backgroundColor: TUI_THEME.rail, children: " ".repeat(leadingWidth) }) : null, fittedSegments.map((segment, index) => (_jsx(StatusSegment, { segment: segment, compact: compact, isLast: index === fittedSegments.length - 1 }, `${segment.label}-${index}`))), trailingWidth > 0 ? _jsx(Text, { backgroundColor: TUI_THEME.rail, children: " ".repeat(trailingWidth) }) : null] }));
|
|
14
|
+
}
|
|
15
|
+
export function statusBarDisplayText(text, terminalWidth, showTask = false) {
|
|
16
|
+
const resolved = resolveStatusBar(text, terminalWidth, showTask);
|
|
17
|
+
if (isIdleStatus(resolved.segments)) {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
return resolved.segments.map((segment, index) => {
|
|
21
|
+
const display = statusSegmentDisplay(segment, resolved.compact);
|
|
22
|
+
return `${display.label}${display.separator}${display.value}${index < resolved.segments.length - 1 ? statusSegmentSeparator(resolved.compact) : ""}`;
|
|
23
|
+
}).join("");
|
|
24
|
+
}
|
|
25
|
+
function resolveStatusBar(text, terminalWidth, showTask) {
|
|
6
26
|
const parsedSegments = parseStatusText(text, { hideTask: !showTask || terminalWidth < 40 });
|
|
7
27
|
if (isIdleStatus(parsedSegments)) {
|
|
8
|
-
return
|
|
28
|
+
return { segments: parsedSegments, compact: false };
|
|
9
29
|
}
|
|
10
|
-
const
|
|
30
|
+
const baseSegments = omitTinyCurrentSegment(readableCompletedSegments(parsedSegments, terminalWidth), terminalWidth);
|
|
31
|
+
const segments = fitCompletedMainIdentityBesideRoute(baseSegments, terminalWidth);
|
|
11
32
|
const compact = shouldUseCompactStatus(segments, terminalWidth);
|
|
12
33
|
const fittedSegments = fitStatusSegments(segments, terminalWidth, compact);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
34
|
+
return { segments: fittedSegments, compact };
|
|
35
|
+
}
|
|
36
|
+
function IdleStatusRail({ terminalWidth, fill }) {
|
|
37
|
+
const { leadingWidth, trailingWidth } = statusRailLayout(terminalWidth, 0, { fill });
|
|
38
|
+
return (_jsx(Box, { children: _jsx(Text, { backgroundColor: TUI_THEME.rail, children: " ".repeat(leadingWidth + trailingWidth) }) }));
|
|
39
|
+
}
|
|
40
|
+
export function statusRailLayout(terminalWidth, contentWidth, options = {}) {
|
|
16
41
|
const leadingWidth = terminalWidth > 1 ? 1 : 0;
|
|
42
|
+
if (options.fill === false) {
|
|
43
|
+
return { leadingWidth, trailingWidth: 0 };
|
|
44
|
+
}
|
|
17
45
|
const renderWidth = typeof process.stdout.columns === "number"
|
|
18
46
|
? Math.max(1, Math.min(terminalWidth, process.stdout.columns))
|
|
19
|
-
:
|
|
20
|
-
const barWidth =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
: Math.max(0, barWidth - leadingWidth -
|
|
24
|
-
|
|
47
|
+
: Math.max(1, terminalWidth);
|
|
48
|
+
const barWidth = Math.max(1, renderWidth - 1);
|
|
49
|
+
return {
|
|
50
|
+
leadingWidth,
|
|
51
|
+
trailingWidth: Math.max(0, barWidth - leadingWidth - Math.max(0, contentWidth))
|
|
52
|
+
};
|
|
25
53
|
}
|
|
26
54
|
function omitTinyCurrentSegment(segments, terminalWidth) {
|
|
27
55
|
if (terminalWidth >= 24 || segments.length <= 1) {
|
|
28
56
|
return segments;
|
|
29
57
|
}
|
|
58
|
+
const current = segments.find((segment) => segment.label.toLowerCase() === "current");
|
|
59
|
+
const currentRole = current ? workerIdentityStatus(current.value)?.role : undefined;
|
|
60
|
+
if (current
|
|
61
|
+
&& (currentRole === "main"
|
|
62
|
+
|| current.tone === "run"
|
|
63
|
+
|| current.tone === "wait"
|
|
64
|
+
|| current.tone === "fail"
|
|
65
|
+
|| current.tone === "stop")) {
|
|
66
|
+
return segments;
|
|
67
|
+
}
|
|
30
68
|
const compacted = segments.filter((segment) => segment.label.toLowerCase() !== "current");
|
|
31
69
|
return compacted.length > 0 ? compacted : segments;
|
|
32
70
|
}
|
|
@@ -37,36 +75,243 @@ function shouldUseCompactStatus(segments, terminalWidth) {
|
|
|
37
75
|
const roomyDisplays = segments.map((segment) => statusSegmentDisplay(segment, false));
|
|
38
76
|
return statusSegmentsDisplayWidth(roomyDisplays, false) > Math.max(1, terminalWidth - 2);
|
|
39
77
|
}
|
|
78
|
+
function readableCompletedSegments(segments, terminalWidth) {
|
|
79
|
+
const workersIndex = segments.findIndex((segment) => segment.label.toLowerCase() === "workers");
|
|
80
|
+
const doneIndex = segments.findIndex((segment) => (segment.label.toLowerCase() === "done" && segment.tone === "done"));
|
|
81
|
+
const routeIndex = segments.findIndex((segment) => (segment.label.toLowerCase() === "route"
|
|
82
|
+
&& !segment.tone
|
|
83
|
+
&& /^(?:simple|complex)(?:\s+·\s+|$)/i.test(segment.value)));
|
|
84
|
+
const completed = workersIndex >= 0
|
|
85
|
+
&& doneIndex >= 0
|
|
86
|
+
&& segments[workersIndex]?.value === segments[doneIndex]?.value;
|
|
87
|
+
if (!completed) {
|
|
88
|
+
return segments;
|
|
89
|
+
}
|
|
90
|
+
const readable = segments.map((segment, index) => (index === doneIndex
|
|
91
|
+
? { ...segment, value: "done", hideLabel: true }
|
|
92
|
+
: index === routeIndex && terminalWidth < 56
|
|
93
|
+
? { ...segment, hideLabel: true }
|
|
94
|
+
: segment));
|
|
95
|
+
return readable;
|
|
96
|
+
}
|
|
97
|
+
function fitCompletedMainIdentityBesideRoute(segments, terminalWidth) {
|
|
98
|
+
if (terminalWidth < 56 || segments.length !== 2) {
|
|
99
|
+
return segments;
|
|
100
|
+
}
|
|
101
|
+
const currentIndex = segments.findIndex((segment) => segment.label.toLowerCase() === "current");
|
|
102
|
+
const routeIndex = segments.findIndex((segment) => segment.label.toLowerCase() === "route");
|
|
103
|
+
if (currentIndex < 0 || routeIndex < 0 || statusSegmentsWidth(segments, false) <= terminalWidth - 2) {
|
|
104
|
+
return segments;
|
|
105
|
+
}
|
|
106
|
+
const current = segments[currentIndex];
|
|
107
|
+
const identity = current ? workerIdentityStatus(current.value) : null;
|
|
108
|
+
if (!current
|
|
109
|
+
|| identity?.role !== "main"
|
|
110
|
+
|| current.tone === "run"
|
|
111
|
+
|| current.tone === "wait"
|
|
112
|
+
|| current.tone === "fail") {
|
|
113
|
+
return segments;
|
|
114
|
+
}
|
|
115
|
+
const route = segments[routeIndex];
|
|
116
|
+
if (!route) {
|
|
117
|
+
return segments;
|
|
118
|
+
}
|
|
119
|
+
const contentWidth = Math.max(1, terminalWidth - 2);
|
|
120
|
+
const currentWidth = Math.max(1, contentWidth
|
|
121
|
+
- statusSegmentsWidth([route], false)
|
|
122
|
+
- displayWidth(statusSegmentSeparator(false)));
|
|
123
|
+
const fittedCurrent = fitCurrentStatusSegment(current, currentWidth, false);
|
|
124
|
+
if (!fittedCurrent) {
|
|
125
|
+
return segments;
|
|
126
|
+
}
|
|
127
|
+
const fitted = segments.map((segment, index) => (index === currentIndex ? fittedCurrent : segment));
|
|
128
|
+
return statusSegmentsWidth(fitted, false) <= contentWidth ? fitted : segments;
|
|
129
|
+
}
|
|
40
130
|
function isIdleStatus(segments) {
|
|
41
131
|
return segments.length === 1 && segments[0]?.tone === "idle" && segments[0]?.value === "idle";
|
|
42
132
|
}
|
|
43
133
|
function StatusSegment({ segment, compact, isLast }) {
|
|
44
134
|
const display = statusSegmentDisplay(segment, compact);
|
|
45
|
-
return (_jsxs(Box, { flexShrink: 0, children: [display.label ? (_jsxs(_Fragment, { children: [_jsx(Text, {
|
|
135
|
+
return (_jsxs(Box, { flexShrink: 0, children: [display.label ? (_jsxs(_Fragment, { children: [_jsx(Text, { ...statusSegmentLabelTheme(segment.tone), children: display.label }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: display.separator })] })) : null, _jsx(Text, { ...statusSegmentValueTheme(segment.tone), wrap: "truncate-end", children: display.value }), !isLast ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: statusSegmentSeparator(compact) }) : null] }));
|
|
46
136
|
}
|
|
47
137
|
function fitStatusSegments(segments, terminalWidth, compact) {
|
|
48
138
|
const contentWidth = Math.max(1, terminalWidth - 2);
|
|
49
|
-
|
|
50
|
-
|
|
139
|
+
const semanticSegments = segments.map((segment) => fitAtomicStatusSegment(segment, contentWidth, compact));
|
|
140
|
+
if (statusSegmentsWidth(semanticSegments, compact) <= contentWidth) {
|
|
141
|
+
return semanticSegments;
|
|
51
142
|
}
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
143
|
+
const activeMainPair = fitActiveMainWithCompletedRoute(semanticSegments, contentWidth, compact);
|
|
144
|
+
if (activeMainPair) {
|
|
145
|
+
return activeMainPair;
|
|
146
|
+
}
|
|
147
|
+
const currentIndex = semanticSegments.map((segment) => segment.label.toLowerCase()).lastIndexOf("current");
|
|
55
148
|
if (currentIndex >= 0) {
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
|
|
149
|
+
const current = semanticSegments[currentIndex];
|
|
150
|
+
if (current) {
|
|
151
|
+
const others = semanticSegments.filter((_, index) => index !== currentIndex);
|
|
152
|
+
const besideWidth = contentWidth
|
|
153
|
+
- statusSegmentsWidth(others, compact)
|
|
154
|
+
- (others.length > 0 ? displayWidth(statusSegmentSeparator(compact)) : 0);
|
|
155
|
+
const fittedBeside = fitCurrentStatusSegment(current, besideWidth, compact, true);
|
|
156
|
+
if (fittedBeside) {
|
|
157
|
+
const fitted = semanticSegments.map((segment, index) => (index === currentIndex ? fittedBeside : segment));
|
|
158
|
+
if (statusSegmentsWidth(fitted, compact) <= contentWidth) {
|
|
159
|
+
return fitted;
|
|
160
|
+
}
|
|
65
161
|
}
|
|
162
|
+
const fittedCurrent = fitCurrentStatusSegment(current, contentWidth, compact);
|
|
163
|
+
const fitted = fittedCurrent
|
|
164
|
+
? semanticSegments.map((segment, index) => index === currentIndex ? fittedCurrent : segment)
|
|
165
|
+
: semanticSegments.filter((_, index) => index !== currentIndex);
|
|
66
166
|
return selectStatusSegmentsThatFit(fitted, contentWidth, compact);
|
|
67
167
|
}
|
|
68
168
|
}
|
|
69
|
-
return selectStatusSegmentsThatFit(
|
|
169
|
+
return selectStatusSegmentsThatFit(semanticSegments, contentWidth, compact);
|
|
170
|
+
}
|
|
171
|
+
function fitActiveMainWithCompletedRoute(segments, contentWidth, compact) {
|
|
172
|
+
if (segments.length !== 2) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
const currentIndex = segments.findIndex((segment) => segment.label.toLowerCase() === "current");
|
|
176
|
+
const routeIndex = segments.findIndex((segment) => segment.label.toLowerCase() === "route");
|
|
177
|
+
const current = segments[currentIndex];
|
|
178
|
+
const route = segments[routeIndex];
|
|
179
|
+
if (currentIndex < 0
|
|
180
|
+
|| routeIndex < 0
|
|
181
|
+
|| !current
|
|
182
|
+
|| !route
|
|
183
|
+
|| (current.tone !== "run" && current.tone !== "wait" && current.tone !== "fail")
|
|
184
|
+
|| !/^(?:simple|complex)(?:\s+·\s+|$)/i.test(route.value)
|
|
185
|
+
|| workerIdentityStatus(current.value)?.role !== "main") {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
for (const value of currentStatusValueCandidates(current.value)) {
|
|
189
|
+
const fittedCurrent = { ...current, value };
|
|
190
|
+
if (!currentStatusDisplay(value, compact).value) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const remaining = contentWidth
|
|
194
|
+
- statusSegmentsWidth([fittedCurrent], compact)
|
|
195
|
+
- displayWidth(statusSegmentSeparator(compact));
|
|
196
|
+
const fittedRoute = fitCompletedRouteSegment(route, remaining, compact);
|
|
197
|
+
if (!fittedRoute) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const pair = segments.map((segment, index) => (index === currentIndex ? fittedCurrent : fittedRoute));
|
|
201
|
+
if (statusSegmentsWidth(pair, compact) <= contentWidth) {
|
|
202
|
+
return pair;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const fittedCurrent = fitCurrentStatusSegment(current, contentWidth, compact);
|
|
206
|
+
return fittedCurrent ? [fittedCurrent] : null;
|
|
207
|
+
}
|
|
208
|
+
function fitCompletedRouteSegment(segment, maxWidth, compact) {
|
|
209
|
+
if (maxWidth <= 0) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
const display = statusSegmentDisplay(segment, compact);
|
|
213
|
+
const valueWidth = maxWidth - displayWidth(`${display.label}${display.separator}`);
|
|
214
|
+
if (valueWidth < 1) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
const value = compactCompletedRouteStatusValue(segment.value, valueWidth);
|
|
218
|
+
return value ? { ...segment, value } : null;
|
|
219
|
+
}
|
|
220
|
+
function compactCompletedRouteStatusValue(value, maxWidth) {
|
|
221
|
+
const parts = value.split(/\s+·\s+/).map((part) => part.trim()).filter(Boolean);
|
|
222
|
+
const mode = /^(?:simple|complex)$/i.test(parts[0] ?? "") ? parts[0] : null;
|
|
223
|
+
const duration = parts.find((part) => /^\d+(?:\.\d+)?(?:ms|s|m)(?:\s+total)?$/i.test(part));
|
|
224
|
+
const compactFailure = compactRouteStatusValue(value, true);
|
|
225
|
+
const exceptional = /(?:^|\s·\s)(?:fallback|forced)(?:\s·\s|$)/i.test(value);
|
|
226
|
+
const candidates = [
|
|
227
|
+
value,
|
|
228
|
+
...(mode && exceptional && compactFailure !== value
|
|
229
|
+
? [`${mode} · ${compactFailure}${duration ? ` · ${duration}` : ""}`]
|
|
230
|
+
: []),
|
|
231
|
+
...(mode && duration ? [`${mode} · ${duration}`] : []),
|
|
232
|
+
...(mode ? [mode] : []),
|
|
233
|
+
...(compactFailure !== value ? [compactFailure] : []),
|
|
234
|
+
...(duration ? [duration] : [])
|
|
235
|
+
];
|
|
236
|
+
return candidates.find((candidate) => displayWidth(candidate) <= maxWidth) ?? null;
|
|
237
|
+
}
|
|
238
|
+
function fitCurrentStatusSegment(segment, maxWidth, compact, requireDetail = false) {
|
|
239
|
+
if (maxWidth <= 0) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
for (const value of currentStatusValueCandidates(segment.value)) {
|
|
243
|
+
const candidate = { ...segment, value };
|
|
244
|
+
if (requireDetail && !currentStatusDisplay(value, compact).value) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (statusSegmentsWidth([candidate], compact) <= maxWidth) {
|
|
248
|
+
return candidate;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
function currentStatusValueCandidates(text) {
|
|
254
|
+
const identity = workerIdentityStatus(text);
|
|
255
|
+
const roleMatch = identity ? null : text.match(/^(main|judge|actor|critic)(?:\s+(.+))?$/i);
|
|
256
|
+
const role = identity?.role ?? roleMatch?.[1]?.toLowerCase();
|
|
257
|
+
if (!role) {
|
|
258
|
+
return [text];
|
|
259
|
+
}
|
|
260
|
+
const fullIdentity = identity ? `${identity.role}/${identity.engine}` : role;
|
|
261
|
+
const detail = identity
|
|
262
|
+
? text.slice(fullIdentity.length).trim()
|
|
263
|
+
: roleMatch?.[2]?.trim() ?? "";
|
|
264
|
+
const compactDetail = compactCurrentStatusDetail(detail);
|
|
265
|
+
const compactState = compactDetail.split(/\s+/, 1)[0] ?? "";
|
|
266
|
+
return Array.from(new Set([
|
|
267
|
+
text,
|
|
268
|
+
...(compactDetail ? [`${fullIdentity} ${compactDetail}`] : []),
|
|
269
|
+
...(compactDetail ? [`${role} ${compactDetail}`] : []),
|
|
270
|
+
...(compactState ? [`${fullIdentity} ${compactState}`] : []),
|
|
271
|
+
...(compactState ? [`${role} ${compactState}`] : []),
|
|
272
|
+
fullIdentity,
|
|
273
|
+
role,
|
|
274
|
+
compactRoleStatusLabel(role)
|
|
275
|
+
].filter(Boolean)));
|
|
276
|
+
}
|
|
277
|
+
function compactCurrentStatusDetail(detail) {
|
|
278
|
+
const progress = detail.match(/^(waiting output|responding)\s+·\s+(\d+(?:\.\d+)?s)\s*\/\s*(\d+(?:\.\d+)?(?:ms|s|m))\s+(?:first|idle)$/i);
|
|
279
|
+
if (progress) {
|
|
280
|
+
const state = progress[1]?.toLowerCase() === "responding" ? "reply" : "wait";
|
|
281
|
+
return `${state} ${progress[2]}/${progress[3]}`;
|
|
282
|
+
}
|
|
283
|
+
const state = detail.match(/^(starting|running|run|done|failed|fail|waiting|wait|queued|stopping|stop|idle|cancelled|canceled)\b/i)?.[1]?.toLowerCase();
|
|
284
|
+
if (state === "starting") {
|
|
285
|
+
return "start";
|
|
286
|
+
}
|
|
287
|
+
if (state === "running") {
|
|
288
|
+
return "run";
|
|
289
|
+
}
|
|
290
|
+
if (state === "failed") {
|
|
291
|
+
return "fail";
|
|
292
|
+
}
|
|
293
|
+
if (state === "waiting" || state === "queued") {
|
|
294
|
+
return "wait";
|
|
295
|
+
}
|
|
296
|
+
if (state === "stopping" || state === "cancelled" || state === "canceled") {
|
|
297
|
+
return "stop";
|
|
298
|
+
}
|
|
299
|
+
return state ?? "";
|
|
300
|
+
}
|
|
301
|
+
function fitAtomicStatusSegment(segment, contentWidth, compact) {
|
|
302
|
+
if (segment.label.toLowerCase() !== "route"
|
|
303
|
+
|| (segment.tone !== "wait" && segment.tone !== "fail")) {
|
|
304
|
+
return segment;
|
|
305
|
+
}
|
|
306
|
+
const display = statusSegmentDisplay(segment, compact);
|
|
307
|
+
const valueWidth = Math.max(1, contentWidth - displayWidth(`${display.label}${display.separator}`));
|
|
308
|
+
if (displayWidth(display.value) <= valueWidth) {
|
|
309
|
+
return segment;
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
...segment,
|
|
313
|
+
value: compactRouteStatusValueToWidth(segment.value, valueWidth)
|
|
314
|
+
};
|
|
70
315
|
}
|
|
71
316
|
function statusSegmentsDisplayWidth(displays, compact) {
|
|
72
317
|
const segmentWidths = displays.map((display) => displayWidth(`${display.label}${display.separator}${display.value}`));
|
|
@@ -93,32 +338,51 @@ function selectStatusSegmentsThatFit(segments, contentWidth, compact) {
|
|
|
93
338
|
}
|
|
94
339
|
function statusSegmentKeepPriority(segment) {
|
|
95
340
|
const label = segment.label.toLowerCase();
|
|
341
|
+
if (label === "route" && segment.tone === "fail") {
|
|
342
|
+
return -1;
|
|
343
|
+
}
|
|
96
344
|
if (segment.tone === "fail" || label === "fail") {
|
|
97
345
|
return 0;
|
|
98
346
|
}
|
|
99
|
-
if (segment.tone === "
|
|
347
|
+
if (segment.tone === "stop" || label === "stop") {
|
|
100
348
|
return 1;
|
|
101
349
|
}
|
|
102
|
-
if (segment.tone === "
|
|
350
|
+
if (segment.tone === "run" || label === "run") {
|
|
103
351
|
return 2;
|
|
104
352
|
}
|
|
105
|
-
if (label === "
|
|
353
|
+
if (segment.tone === "wait" || label === "wait") {
|
|
106
354
|
return 3;
|
|
107
355
|
}
|
|
108
|
-
if (
|
|
356
|
+
if (label === "workers") {
|
|
109
357
|
return 4;
|
|
110
358
|
}
|
|
111
|
-
if (label === "
|
|
359
|
+
if (segment.tone === "done" || label === "done") {
|
|
112
360
|
return 5;
|
|
113
361
|
}
|
|
114
|
-
|
|
362
|
+
if (label === "current") {
|
|
363
|
+
return 6;
|
|
364
|
+
}
|
|
365
|
+
if (label === "route") {
|
|
366
|
+
return 8;
|
|
367
|
+
}
|
|
368
|
+
return 7;
|
|
115
369
|
}
|
|
116
370
|
function compactSingleStatusSegment(segment, contentWidth, compact) {
|
|
117
371
|
const display = statusSegmentDisplay(segment, compact);
|
|
118
372
|
const labelWidth = displayWidth(`${display.label}${display.separator}`);
|
|
373
|
+
const valueWidth = Math.max(1, contentWidth - labelWidth);
|
|
374
|
+
if (segment.label.toLowerCase() === "route") {
|
|
375
|
+
return {
|
|
376
|
+
...segment,
|
|
377
|
+
value: compactRouteStatusValueToWidth(segment.value, valueWidth)
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
if (segment.label.toLowerCase() === "current") {
|
|
381
|
+
return fitCurrentStatusSegment(segment, contentWidth, compact) ?? segment;
|
|
382
|
+
}
|
|
119
383
|
return {
|
|
120
384
|
...segment,
|
|
121
|
-
value: compactStatusTextEnd(display.value,
|
|
385
|
+
value: compactStatusTextEnd(display.value, valueWidth)
|
|
122
386
|
};
|
|
123
387
|
}
|
|
124
388
|
function statusSegmentSeparator(compact) {
|
|
@@ -127,7 +391,20 @@ function statusSegmentSeparator(compact) {
|
|
|
127
391
|
function compactStatusTextEnd(text, maxLength) {
|
|
128
392
|
return compactEndByDisplayWidth(text, maxLength);
|
|
129
393
|
}
|
|
394
|
+
function workerIdentityStatus(text) {
|
|
395
|
+
const match = text.trim().match(/^(main|judge|actor|critic)\/([^\s]+)(?:\s+.*)?$/i);
|
|
396
|
+
if (!match) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
role: (match[1] ?? "").toLowerCase(),
|
|
401
|
+
engine: match[2] ?? ""
|
|
402
|
+
};
|
|
403
|
+
}
|
|
130
404
|
function statusSegmentDisplay(segment, compact) {
|
|
405
|
+
if (segment.hideLabel) {
|
|
406
|
+
return { label: "", separator: "", value: segment.value };
|
|
407
|
+
}
|
|
131
408
|
const label = segment.label.toLowerCase();
|
|
132
409
|
if (label === "task") {
|
|
133
410
|
return { label: "", separator: "", value: segment.value };
|
|
@@ -135,13 +412,33 @@ function statusSegmentDisplay(segment, compact) {
|
|
|
135
412
|
if (isRoleStatusLabel(label)) {
|
|
136
413
|
return {
|
|
137
414
|
label: compact ? compactRoleStatusLabel(label) : displayRoleStatusLabel(label),
|
|
138
|
-
separator: compact ? ":" : " ",
|
|
415
|
+
separator: compact ? ":" : " · ",
|
|
139
416
|
value: segment.value
|
|
140
417
|
};
|
|
141
418
|
}
|
|
419
|
+
if (label === "route") {
|
|
420
|
+
return {
|
|
421
|
+
label: compact ? "r" : "route",
|
|
422
|
+
separator: compact ? ":" : " ",
|
|
423
|
+
value: compactRouteStatusValue(segment.value, compact)
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (label === "wave") {
|
|
427
|
+
return {
|
|
428
|
+
label: "wave",
|
|
429
|
+
separator: " ",
|
|
430
|
+
value: compact ? compactWaveStatusValue(segment.value) : segment.value
|
|
431
|
+
};
|
|
432
|
+
}
|
|
142
433
|
if (!compact) {
|
|
143
434
|
if (label === "current") {
|
|
144
|
-
return
|
|
435
|
+
return currentStatusDisplay(segment.value, false);
|
|
436
|
+
}
|
|
437
|
+
if (label === "workers") {
|
|
438
|
+
return { label: "workers", separator: " ", value: segment.value };
|
|
439
|
+
}
|
|
440
|
+
if (segment.tone && segment.tone !== "idle" && label === segment.tone) {
|
|
441
|
+
return { label: segment.tone, separator: " ", value: segment.value };
|
|
145
442
|
}
|
|
146
443
|
if (segment.tone && segment.tone !== "idle") {
|
|
147
444
|
return { label: segment.tone, separator: " ", value: segment.value };
|
|
@@ -149,16 +446,177 @@ function statusSegmentDisplay(segment, compact) {
|
|
|
149
446
|
return { label, separator: " ", value: segment.value };
|
|
150
447
|
}
|
|
151
448
|
if (label === "workers") {
|
|
152
|
-
return { label: "
|
|
449
|
+
return { label: "wk", separator: "", value: segment.value };
|
|
153
450
|
}
|
|
154
451
|
if (label === "current") {
|
|
155
|
-
return
|
|
452
|
+
return currentStatusDisplay(segment.value, true);
|
|
156
453
|
}
|
|
157
454
|
if (segment.tone && segment.tone !== "idle") {
|
|
158
455
|
return { label: compactToneLabel(segment.tone), separator: "", value: segment.value };
|
|
159
456
|
}
|
|
160
457
|
return { label, separator: " ", value: segment.value };
|
|
161
458
|
}
|
|
459
|
+
function currentStatusDisplay(text, compact) {
|
|
460
|
+
const identity = workerIdentityStatus(text);
|
|
461
|
+
if (identity) {
|
|
462
|
+
const fullIdentity = `${identity.role}/${identity.engine}`;
|
|
463
|
+
const detail = text.slice(fullIdentity.length).trim();
|
|
464
|
+
return {
|
|
465
|
+
label: fullIdentity,
|
|
466
|
+
separator: detail ? (compact ? ":" : " · ") : "",
|
|
467
|
+
value: detail
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const role = text.match(/^(main|judge|actor|critic)(?:\s+(.+))?$/i);
|
|
471
|
+
if (role) {
|
|
472
|
+
const detail = role[2]?.trim() ?? "";
|
|
473
|
+
return {
|
|
474
|
+
label: displayRoleStatusLabel(role[1] ?? ""),
|
|
475
|
+
separator: detail ? (compact ? ":" : " · ") : "",
|
|
476
|
+
value: detail
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
return { label: text, separator: "", value: "" };
|
|
480
|
+
}
|
|
481
|
+
function compactRouteStatusValue(value, compact) {
|
|
482
|
+
if (compact && /(?:^|\s·\s)fallback(?:\s·\s|$)/i.test(value)) {
|
|
483
|
+
const cause = value
|
|
484
|
+
.split(/\s+·\s+/)
|
|
485
|
+
.find((part) => /^(?:(?:idle|total) timeout(?: waiting output| after (?:stdout|stderr|output))?|first output timeout|timeout(?: via proxy| waiting output| after (?:stdout|stderr|output))?|proxy|auth|rate limit|network|unavailable|invalid output|exit)$/i.test(part));
|
|
486
|
+
if (cause) {
|
|
487
|
+
return cause.toLowerCase() === "invalid output" ? "invalid" : cause.toLowerCase();
|
|
488
|
+
}
|
|
489
|
+
return "fallback";
|
|
490
|
+
}
|
|
491
|
+
return value;
|
|
492
|
+
}
|
|
493
|
+
function compactRouteStatusValueToWidth(value, maxWidth) {
|
|
494
|
+
const compact = compactRouteStatusValue(value, true);
|
|
495
|
+
const parts = value.split(/\s+·\s+/).map((part) => part.trim()).filter(Boolean);
|
|
496
|
+
const duration = parts.find((part) => /^\d+(?:\.\d+)?(?:ms|s|m)(?:\s+(?:max|total|first|idle))?$/i.test(part))
|
|
497
|
+
?.replace(/\s+(?:max|total|first|idle)$/i, "");
|
|
498
|
+
const progress = parts
|
|
499
|
+
.map((part) => part.match(/^(\d+(?:\.\d+)?s)\s*\/\s*(\d+(?:\.\d+)?(?:ms|s|m))(?:\s+(first|total))?$/i))
|
|
500
|
+
.find((match) => match !== null);
|
|
501
|
+
const elapsed = progress?.[1];
|
|
502
|
+
const compactProgress = progress
|
|
503
|
+
? `${elapsed?.replace(/s$/i, "")}/${progress[2]}`
|
|
504
|
+
: undefined;
|
|
505
|
+
const progressCandidates = compactProgress
|
|
506
|
+
? [compactProgress, elapsed].filter((item) => Boolean(item))
|
|
507
|
+
: duration ? [duration] : [];
|
|
508
|
+
const first = parts[0]?.toLowerCase() ?? "";
|
|
509
|
+
const retry = first.match(/^retry\s+(\d+\/\d+)$/);
|
|
510
|
+
let candidates;
|
|
511
|
+
if (retry) {
|
|
512
|
+
candidates = [compact, `retry ${retry[1]}`, retry[1] ?? "retry", "retry"];
|
|
513
|
+
}
|
|
514
|
+
else if (first === "checking") {
|
|
515
|
+
candidates = [compact, progressCandidates[0] ? `check ${progressCandidates[0]}` : "check", ...progressCandidates, "wait"];
|
|
516
|
+
}
|
|
517
|
+
else if (first === "follow-up") {
|
|
518
|
+
candidates = [compact, progressCandidates[0] ? `follow ${progressCandidates[0]}` : "follow", ...progressCandidates, "wait"];
|
|
519
|
+
}
|
|
520
|
+
else if (routeProgressStatusAlias(first)) {
|
|
521
|
+
const alias = routeProgressStatusAlias(first);
|
|
522
|
+
const firstOutputProgress = progress?.[3]?.toLowerCase() === "first";
|
|
523
|
+
candidates = [
|
|
524
|
+
compact,
|
|
525
|
+
...(compactProgress ? [`${alias} ${compactProgress}`] : []),
|
|
526
|
+
...(firstOutputProgress && compactProgress ? [compactProgress] : []),
|
|
527
|
+
...(elapsed ? [`${alias} ${elapsed}`] : []),
|
|
528
|
+
alias,
|
|
529
|
+
...progressCandidates,
|
|
530
|
+
"wait"
|
|
531
|
+
];
|
|
532
|
+
}
|
|
533
|
+
else if (/(?:^|\s·\s)fallback(?:\s·\s|$)/i.test(value)) {
|
|
534
|
+
candidates = [compact, ...compactRouteFailureAliases(compact), "wait"];
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
candidates = [compact, first, "route"];
|
|
538
|
+
}
|
|
539
|
+
return candidates.find((candidate) => displayWidth(candidate) <= maxWidth)
|
|
540
|
+
?? candidates.at(-1)
|
|
541
|
+
?? "route";
|
|
542
|
+
}
|
|
543
|
+
function compactRouteFailureAliases(value) {
|
|
544
|
+
if (value === "timeout via proxy") {
|
|
545
|
+
return ["proxy", "p:to"];
|
|
546
|
+
}
|
|
547
|
+
if (value === "proxy") {
|
|
548
|
+
return ["pxy"];
|
|
549
|
+
}
|
|
550
|
+
if (value === "rate limit") {
|
|
551
|
+
return ["rate"];
|
|
552
|
+
}
|
|
553
|
+
if (value === "timeout") {
|
|
554
|
+
return ["time"];
|
|
555
|
+
}
|
|
556
|
+
if (value === "timeout waiting output") {
|
|
557
|
+
return ["wait:to", "w:to", "time"];
|
|
558
|
+
}
|
|
559
|
+
if (value === "timeout after stderr") {
|
|
560
|
+
return ["err:to", "e:to", "time"];
|
|
561
|
+
}
|
|
562
|
+
if (value === "timeout after stdout") {
|
|
563
|
+
return ["out:to", "o:to", "time"];
|
|
564
|
+
}
|
|
565
|
+
if (value === "timeout after output") {
|
|
566
|
+
return ["out:to", "o:to", "time"];
|
|
567
|
+
}
|
|
568
|
+
if (value === "first output timeout") {
|
|
569
|
+
return ["first:to", "f:to", "time"];
|
|
570
|
+
}
|
|
571
|
+
if (value.startsWith("idle timeout")) {
|
|
572
|
+
return ["idle:to", "i:to", "time"];
|
|
573
|
+
}
|
|
574
|
+
if (value.startsWith("total timeout")) {
|
|
575
|
+
return ["total:to", "t:to", "time"];
|
|
576
|
+
}
|
|
577
|
+
if (value === "network") {
|
|
578
|
+
return ["net"];
|
|
579
|
+
}
|
|
580
|
+
if (value === "unavailable") {
|
|
581
|
+
return ["down"];
|
|
582
|
+
}
|
|
583
|
+
if (value === "invalid") {
|
|
584
|
+
return ["bad"];
|
|
585
|
+
}
|
|
586
|
+
if (value === "fallback") {
|
|
587
|
+
return ["fall"];
|
|
588
|
+
}
|
|
589
|
+
return [value];
|
|
590
|
+
}
|
|
591
|
+
function routeProgressStatusAlias(value) {
|
|
592
|
+
if (value === "starting") {
|
|
593
|
+
return "start";
|
|
594
|
+
}
|
|
595
|
+
if (value === "waiting output") {
|
|
596
|
+
return "wait";
|
|
597
|
+
}
|
|
598
|
+
if (value === "diagnostics") {
|
|
599
|
+
return "diag";
|
|
600
|
+
}
|
|
601
|
+
if (value === "receiving") {
|
|
602
|
+
return "recv";
|
|
603
|
+
}
|
|
604
|
+
if (value === "parsing") {
|
|
605
|
+
return "parse";
|
|
606
|
+
}
|
|
607
|
+
if (value === "stopping") {
|
|
608
|
+
return "stop";
|
|
609
|
+
}
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
function compactWaveStatusValue(value) {
|
|
613
|
+
return value
|
|
614
|
+
.replace(/\s+·\s+actor\s+/i, " a")
|
|
615
|
+
.replace(/\s+·\s+critic\s+/i, " c")
|
|
616
|
+
.replace(/\s+·\s+revision\s+/i, " r")
|
|
617
|
+
.replace(/\s+·\s+integration\s+/i, " i")
|
|
618
|
+
.replace(/\s+·\s+verification\s+/i, " v");
|
|
619
|
+
}
|
|
162
620
|
function compactToneLabel(tone) {
|
|
163
621
|
if (tone === "run") {
|
|
164
622
|
return "r";
|
|
@@ -169,18 +627,18 @@ function compactToneLabel(tone) {
|
|
|
169
627
|
if (tone === "fail") {
|
|
170
628
|
return "f";
|
|
171
629
|
}
|
|
630
|
+
if (tone === "stop") {
|
|
631
|
+
return "s";
|
|
632
|
+
}
|
|
172
633
|
return "w";
|
|
173
634
|
}
|
|
174
635
|
function isRoleStatusLabel(label) {
|
|
175
636
|
return label === "main" || label === "judge" || label === "actor" || label === "critic";
|
|
176
637
|
}
|
|
177
638
|
function displayRoleStatusLabel(label) {
|
|
178
|
-
return label
|
|
639
|
+
return label;
|
|
179
640
|
}
|
|
180
641
|
function compactRoleStatusLabel(label) {
|
|
181
|
-
if (label === "main") {
|
|
182
|
-
return "chat";
|
|
183
|
-
}
|
|
184
642
|
return label.slice(0, 1);
|
|
185
643
|
}
|
|
186
644
|
function parseStatusText(text, options = {}) {
|
|
@@ -201,11 +659,30 @@ function parseStatusText(text, options = {}) {
|
|
|
201
659
|
segments.push({ label: "TASK", value: first, tone: first === "idle" ? "idle" : undefined });
|
|
202
660
|
}
|
|
203
661
|
for (const part of statusParts) {
|
|
662
|
+
const wave = parseWaveProgress(part);
|
|
663
|
+
if (wave) {
|
|
664
|
+
segments.push(wave);
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
204
667
|
const workerMatch = part.match(/^workers\s+(\d+)$/i);
|
|
205
668
|
if (workerMatch) {
|
|
206
669
|
segments.push({ label: "WORKERS", value: workerMatch[1] ?? "0" });
|
|
207
670
|
continue;
|
|
208
671
|
}
|
|
672
|
+
const routeMatch = part.match(/^route\s+(.+)$/i);
|
|
673
|
+
if (routeMatch) {
|
|
674
|
+
const value = routeMatch[1]?.trim() || "unknown";
|
|
675
|
+
segments.push({
|
|
676
|
+
label: "ROUTE",
|
|
677
|
+
value,
|
|
678
|
+
tone: /(?:^|\s·\s)fallback(?:\s·\s|$)/i.test(value)
|
|
679
|
+
? "fail"
|
|
680
|
+
: /^(?:checking|follow-up|starting|retry|waiting output|diagnostics|receiving|parsing|stopping)\b/i.test(value)
|
|
681
|
+
? "wait"
|
|
682
|
+
: undefined
|
|
683
|
+
});
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
209
686
|
const counts = parseStateCounts(part);
|
|
210
687
|
if (counts.length > 0) {
|
|
211
688
|
segments.push(...counts);
|
|
@@ -216,10 +693,26 @@ function parseStatusText(text, options = {}) {
|
|
|
216
693
|
return segments.length > 0 ? segments : [{ label: "STATUS", value: "idle", tone: "idle" }];
|
|
217
694
|
}
|
|
218
695
|
function isStatusPart(part) {
|
|
219
|
-
return
|
|
696
|
+
return parseWaveProgress(part) !== null
|
|
697
|
+
|| /^workers\s+\d+$/i.test(part)
|
|
698
|
+
|| /^route\s+\S+/i.test(part)
|
|
699
|
+
|| parseStateCounts(part).length > 0
|
|
700
|
+
|| parseRoleStatus(part) !== null
|
|
701
|
+
|| workerIdentityStatus(part) !== null;
|
|
702
|
+
}
|
|
703
|
+
function parseWaveProgress(part) {
|
|
704
|
+
const match = part.match(/^wave\s+(\d+\/\d+)\s+·\s+(actor|critic|revision|integration|verification)\s+(\d+\/\d+)$/i);
|
|
705
|
+
if (!match) {
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
label: "WAVE",
|
|
710
|
+
value: `${match[1]} · ${(match[2] ?? "actor").toLowerCase()} ${match[3]}`,
|
|
711
|
+
tone: "run"
|
|
712
|
+
};
|
|
220
713
|
}
|
|
221
714
|
function parseStateCounts(part) {
|
|
222
|
-
const matches = Array.from(part.matchAll(/\b(run|done|fail|wait|idle)\s+(\d+)\b/gi));
|
|
715
|
+
const matches = Array.from(part.matchAll(/\b(run|done|fail|wait|stop|idle)\s+(\d+)\b/gi));
|
|
223
716
|
if (matches.length === 0) {
|
|
224
717
|
return [];
|
|
225
718
|
}
|
|
@@ -233,12 +726,23 @@ function parseStateCounts(part) {
|
|
|
233
726
|
});
|
|
234
727
|
}
|
|
235
728
|
function parseCurrentStatus(part) {
|
|
729
|
+
const mainEngineStatus = parseMainEngineStatus(part);
|
|
730
|
+
if (mainEngineStatus) {
|
|
731
|
+
return mainEngineStatus;
|
|
732
|
+
}
|
|
236
733
|
const roleStatus = parseRoleStatus(part);
|
|
237
734
|
if (roleStatus) {
|
|
238
735
|
return roleStatus;
|
|
239
736
|
}
|
|
240
|
-
const toneMatch = part.match(/\b(run|done|fail|wait|idle)\b/i);
|
|
737
|
+
const toneMatch = part.match(/\b(run|done|fail|wait|stop|idle)\b/i);
|
|
241
738
|
const tone = toneMatch ? normalizeTone(toneMatch[1] ?? "idle") : undefined;
|
|
739
|
+
if (workerIdentityStatus(part)) {
|
|
740
|
+
return {
|
|
741
|
+
label: "CURRENT",
|
|
742
|
+
value: part,
|
|
743
|
+
tone
|
|
744
|
+
};
|
|
745
|
+
}
|
|
242
746
|
const value = toneMatch ? part.replace(toneMatch[0], "").trim() || part : part;
|
|
243
747
|
return {
|
|
244
748
|
label: "CURRENT",
|
|
@@ -246,8 +750,41 @@ function parseCurrentStatus(part) {
|
|
|
246
750
|
tone
|
|
247
751
|
};
|
|
248
752
|
}
|
|
753
|
+
function parseMainEngineStatus(part) {
|
|
754
|
+
const match = part.match(/^main\/([^\s]+)\s+([^\s]+)\b/i);
|
|
755
|
+
if (!match) {
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
const status = (match[2] ?? "idle").toLowerCase();
|
|
759
|
+
return {
|
|
760
|
+
label: "CURRENT",
|
|
761
|
+
value: part,
|
|
762
|
+
tone: runtimeStatusTone(status)
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
function runtimeStatusTone(status) {
|
|
766
|
+
if (status === "run" || status === "running" || status === "starting" || status === "responding") {
|
|
767
|
+
return "run";
|
|
768
|
+
}
|
|
769
|
+
if (status === "done") {
|
|
770
|
+
return "done";
|
|
771
|
+
}
|
|
772
|
+
if (status === "fail" || status === "failed" || status === "error") {
|
|
773
|
+
return "fail";
|
|
774
|
+
}
|
|
775
|
+
if (status === "wait" || status === "waiting" || status === "queued" || status === "stopping") {
|
|
776
|
+
return "wait";
|
|
777
|
+
}
|
|
778
|
+
if (status === "stop" || status === "cancelled" || status === "canceled") {
|
|
779
|
+
return "stop";
|
|
780
|
+
}
|
|
781
|
+
if (status === "idle") {
|
|
782
|
+
return "idle";
|
|
783
|
+
}
|
|
784
|
+
return undefined;
|
|
785
|
+
}
|
|
249
786
|
function parseRoleStatus(part) {
|
|
250
|
-
const match = part.match(/^(main|judge|actor|critic)\s+(run|done|fail|wait|idle)\b/i);
|
|
787
|
+
const match = part.match(/^(main|judge|actor|critic)\s+(run|done|fail|wait|stop|idle)\b/i);
|
|
251
788
|
if (!match) {
|
|
252
789
|
return null;
|
|
253
790
|
}
|
|
@@ -260,29 +797,45 @@ function parseRoleStatus(part) {
|
|
|
260
797
|
}
|
|
261
798
|
function normalizeTone(value) {
|
|
262
799
|
const normalized = value.toLowerCase();
|
|
263
|
-
if (normalized === "run" || normalized === "done" || normalized === "fail" || normalized === "wait") {
|
|
800
|
+
if (normalized === "run" || normalized === "done" || normalized === "fail" || normalized === "wait" || normalized === "stop") {
|
|
264
801
|
return normalized;
|
|
265
802
|
}
|
|
266
803
|
return "idle";
|
|
267
804
|
}
|
|
805
|
+
export function statusSegmentLabelTheme(_tone) {
|
|
806
|
+
return {
|
|
807
|
+
backgroundColor: TUI_THEME.rail,
|
|
808
|
+
color: TUI_THEME.muted
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
export function statusSegmentValueTheme(tone) {
|
|
812
|
+
return {
|
|
813
|
+
backgroundColor: TUI_THEME.rail,
|
|
814
|
+
color: valueColorForTone(tone),
|
|
815
|
+
...(tone === "run" || tone === "fail" ? { bold: true } : {})
|
|
816
|
+
};
|
|
817
|
+
}
|
|
268
818
|
function colorForTone(tone) {
|
|
269
819
|
if (tone === "run") {
|
|
270
|
-
return
|
|
820
|
+
return TUI_THEME.accent;
|
|
271
821
|
}
|
|
272
822
|
if (tone === "done") {
|
|
273
|
-
return
|
|
823
|
+
return TUI_THEME.success;
|
|
274
824
|
}
|
|
275
825
|
if (tone === "fail") {
|
|
276
|
-
return
|
|
826
|
+
return TUI_THEME.danger;
|
|
277
827
|
}
|
|
278
828
|
if (tone === "wait") {
|
|
279
|
-
return
|
|
829
|
+
return TUI_THEME.warning;
|
|
830
|
+
}
|
|
831
|
+
if (tone === "stop") {
|
|
832
|
+
return TUI_THEME.warning;
|
|
280
833
|
}
|
|
281
|
-
return
|
|
834
|
+
return TUI_THEME.muted;
|
|
282
835
|
}
|
|
283
836
|
function valueColorForTone(tone) {
|
|
284
|
-
if (tone === "fail") {
|
|
285
|
-
return
|
|
837
|
+
if (tone === "run" || tone === "done" || tone === "fail" || tone === "wait" || tone === "stop") {
|
|
838
|
+
return colorForTone(tone);
|
|
286
839
|
}
|
|
287
|
-
return
|
|
840
|
+
return TUI_THEME.text;
|
|
288
841
|
}
|