@triflux/remote 10.50.2 → 10.52.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/cto/brief.mjs +0 -20
- package/cto/collect.mjs +1 -200
- package/cto/current.schema.json +2 -23
- package/cto/hygiene.mjs +2 -61
- package/cto/lake-root.mjs +0 -56
- package/cto/status.mjs +5 -187
- package/hub/index.mjs +0 -17
- package/hub/team/agent-map.json +0 -1
- package/hub/team/agents-row-attach.py +120 -0
- package/hub/team/agents-row.mjs +200 -0
- package/hub/team/ansi.mjs +1 -35
- package/hub/team/backend.mjs +9 -145
- package/hub/team/claude-daemon-control.mjs +1 -2
- package/hub/team/claude-session-projection.mjs +5 -73
- package/hub/team/cli/commands/attach.mjs +1 -17
- package/hub/team/cli/commands/control.mjs +12 -38
- package/hub/team/cli/commands/debug.mjs +2 -39
- package/hub/team/cli/commands/focus.mjs +5 -31
- package/hub/team/cli/commands/interrupt.mjs +6 -25
- package/hub/team/cli/commands/kill.mjs +1 -30
- package/hub/team/cli/commands/list.mjs +0 -16
- package/hub/team/cli/commands/send.mjs +6 -26
- package/hub/team/cli/commands/start/index.mjs +42 -133
- package/hub/team/cli/commands/start/parse-args.mjs +15 -40
- package/hub/team/cli/commands/start/start-headless.mjs +138 -105
- package/hub/team/cli/commands/start/start-mux.mjs +2 -7
- package/hub/team/cli/commands/status.mjs +2 -57
- package/hub/team/cli/commands/stop.mjs +14 -19
- package/hub/team/cli/help.mjs +4 -5
- package/hub/team/cli/manifest.mjs +5 -1
- package/hub/team/cli/render.mjs +0 -12
- package/hub/team/cli/services/runtime-mode.mjs +27 -98
- package/hub/team/cli/services/state-store.mjs +5 -20
- package/hub/team/handoff.mjs +3 -34
- package/hub/team/headless.mjs +108 -1296
- package/hub/team/orchestrator.mjs +4 -30
- package/hub/team/pane.mjs +6 -28
- package/hub/team/psmux.mjs +3 -34
- package/hub/team/remote-session.mjs +1 -2
- package/hub/team/session-context.mjs +1 -1
- package/hub/team/session.mjs +7 -91
- package/hub/team/tui-core.mjs +1 -1
- package/hub/team/tui-viewer.mjs +11 -58
- package/hub/team/tui.mjs +8 -306
- package/hub/team/uds-orchestrator.mjs +2 -580
- package/hub/team/wt-manager.mjs +2 -2
- package/hub/workers/factory.mjs +6 -84
- package/hub/workers/interface.mjs +1 -7
- package/hub/workers/lib/jsonrpc-core.mjs +11 -0
- package/hub/workers/lib/jsonrpc-ws-uds.mjs +5 -8
- package/package.json +3 -8
- package/scripts/lib/agent-route-policy.mjs +143 -62
- package/scripts/lib/cli-claude.mjs +0 -14
- package/scripts/lib/env-probe.mjs +2 -131
- package/scripts/lib/gemini-profiles.mjs +2 -69
- package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
- package/scripts/lib/machine-profile.mjs +3 -17
- package/scripts/lib/mcp-filter.mjs +109 -53
- package/scripts/lib/mcp-guard-engine.mjs +40 -98
- package/scripts/lib/mcp-health.mjs +2 -2
- package/scripts/lib/mcp-server-catalog.mjs +2 -2
- package/scripts/lib/psmux-info.mjs +11 -59
- package/scripts/lib/skill-template.mjs +0 -245
- package/hub/hub-lifecycle.mjs +0 -248
- package/hub/pipe.mjs +0 -825
- package/hub/promote-penalties.mjs +0 -138
- package/hub/public/dashboard.html +0 -416
- package/hub/schema.sql +0 -209
- package/hub/server.mjs +0 -3048
- package/hub/store-adapter.mjs +0 -182
- package/hub/store.mjs +0 -1736
- package/hub/team/build-worker-prompt.mjs +0 -92
- package/hub/team/check-mcp-hub.mjs +0 -49
- package/hub/team/claude-native-bridge.mjs +0 -987
- package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
- package/hub/team/cli/commands/start/start-wt.mjs +0 -86
- package/hub/team/cli/services/hub-client.mjs +0 -264
- package/hub/team/cli/services/native-control.mjs +0 -145
- package/hub/team/codex-review.mjs +0 -520
- package/hub/team/conductor-mesh-bridge.mjs +0 -122
- package/hub/team/conductor-registry.mjs +0 -121
- package/hub/team/conductor.mjs +0 -1529
- package/hub/team/consensus-meta.mjs +0 -176
- package/hub/team/cto-auto-collect.mjs +0 -115
- package/hub/team/cto-pull.mjs +0 -31
- package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
- package/hub/team/dashboard.mjs +0 -322
- package/hub/team/event-log.mjs +0 -84
- package/hub/team/execution-mode.mjs +0 -324
- package/hub/team/extract-completion-payload.mjs +0 -110
- package/hub/team/headless-bridge-session.mjs +0 -17
- package/hub/team/health-probe.mjs +0 -435
- package/hub/team/interactive-native-launcher.mjs +0 -119
- package/hub/team/interactive-native-worker.mjs +0 -80
- package/hub/team/interactive-tui-transport.mjs +0 -479
- package/hub/team/launcher-template.mjs +0 -144
- package/hub/team/lead-control.mjs +0 -191
- package/hub/team/mcp-selector.mjs +0 -145
- package/hub/team/native-supervisor.mjs +0 -460
- package/hub/team/native.mjs +0 -727
- package/hub/team/recovery-store.mjs +0 -149
- package/hub/team/remote-probe.mjs +0 -292
- package/hub/team/remote-watcher.mjs +0 -506
- package/hub/team/runtime-strategy.mjs +0 -200
- package/hub/team/schemas/worker-completion.json +0 -39
- package/hub/team/sentinel-capture.mjs +0 -127
- package/hub/team/session-sync.mjs +0 -316
- package/hub/team/swarm-cli.mjs +0 -390
- package/hub/team/swarm-hypervisor.mjs +0 -2206
- package/hub/team/swarm-intent.mjs +0 -216
- package/hub/team/swarm-locks.mjs +0 -335
- package/hub/team/swarm-planner.mjs +0 -371
- package/hub/team/swarm-preflight.mjs +0 -366
- package/hub/team/swarm-reconciler.mjs +0 -242
- package/hub/team/synapse-cli.mjs +0 -458
- package/hub/team/synapse-http.mjs +0 -311
- package/hub/team/synapse-registry.mjs +0 -618
- package/hub/team/tui-remote-adapter.mjs +0 -377
- package/hub/team/tui-synapse.mjs +0 -223
- package/hub/team/worker-completion-validator.mjs +0 -79
- package/hub/team/worker-sandbox.mjs +0 -125
- package/hub/team/worktree-lifecycle.mjs +0 -639
- package/hub/team/wt-templates.json +0 -43
- package/hub/tools.mjs +0 -900
- package/hub/workers/antigravity-route-worker.mjs +0 -179
- package/hub/workers/codex-app-server-worker.mjs +0 -1067
- package/hub/workers/delegator-mcp.mjs +0 -1098
- package/hub/workers/lib/jsonrpc-stdio.mjs +0 -204
- package/scripts/lib/claudemd-scanner.mjs +0 -223
- package/scripts/lib/context.mjs +0 -67
- package/scripts/lib/dynamic-route-cli.mjs +0 -107
- package/scripts/lib/handoff.mjs +0 -187
- package/scripts/lib/logger.mjs +0 -122
- package/scripts/lib/stealth-fetch.mjs +0 -176
package/hub/team/tui.mjs
CHANGED
|
@@ -32,10 +32,10 @@ import {
|
|
|
32
32
|
cliColor,
|
|
33
33
|
normalizeWorkerState as coreNormalizeWorkerState,
|
|
34
34
|
countStatuses,
|
|
35
|
-
FALLBACK_COLUMNS,
|
|
36
|
-
FALLBACK_ROWS,
|
|
37
35
|
formatTokens,
|
|
38
36
|
loadVersion,
|
|
37
|
+
resolveViewportColumns,
|
|
38
|
+
resolveViewportRows,
|
|
39
39
|
runtimeStatus,
|
|
40
40
|
sanitizeFiles,
|
|
41
41
|
sanitizeOneLine,
|
|
@@ -44,22 +44,15 @@ import {
|
|
|
44
44
|
wrapLine,
|
|
45
45
|
wrapText as wrapTextAll,
|
|
46
46
|
} from "./tui-core.mjs";
|
|
47
|
-
import {
|
|
48
|
-
createMetricsCollector,
|
|
49
|
-
createSynapseEventStream,
|
|
50
|
-
renderMetricsTier1,
|
|
51
|
-
} from "./tui-synapse.mjs";
|
|
52
47
|
import {
|
|
53
48
|
createPanelResizer,
|
|
54
49
|
createSearchState,
|
|
55
|
-
createTokenTracker,
|
|
56
50
|
createVimMotion,
|
|
57
51
|
} from "./tui-widgets.mjs";
|
|
58
52
|
import { createWtManager } from "./wt-manager.mjs";
|
|
59
53
|
|
|
60
54
|
const VERSION = await loadVersion();
|
|
61
55
|
|
|
62
|
-
// FALLBACK_COLUMNS, FALLBACK_ROWS → tui-core.mjs에서 import
|
|
63
56
|
const MIN_CARD_WIDTH = 28;
|
|
64
57
|
const ATTACH_SESSION_NAME_PATTERN = /^[a-zA-Z0-9_.-]+$/u;
|
|
65
58
|
const DEFAULT_ATTACH_TAB_TTL_MS = 30_000;
|
|
@@ -284,11 +277,6 @@ function activityWave(tick, count = 4) {
|
|
|
284
277
|
return `${MOCHA.executing}${wave}${RESET}`;
|
|
285
278
|
}
|
|
286
279
|
|
|
287
|
-
const GRID_GAP = 2;
|
|
288
|
-
const DEFAULT_DETAIL_LINES = 10;
|
|
289
|
-
// Tier1 상단 고정 행 수
|
|
290
|
-
const _TIER1_ROWS = 2;
|
|
291
|
-
|
|
292
280
|
const SUMMARY_KEYS = [
|
|
293
281
|
"status",
|
|
294
282
|
"lead_action",
|
|
@@ -302,30 +290,6 @@ const SUMMARY_KEYS = [
|
|
|
302
290
|
"partial_output",
|
|
303
291
|
];
|
|
304
292
|
|
|
305
|
-
// ── 레이아웃 브레이크포인트 ──────────────────────────────────────────────
|
|
306
|
-
// 80-119: 28col rail, 120-159: 36col rail, 160+: 균등
|
|
307
|
-
function _resolveRailWidth(totalCols, columnCount) {
|
|
308
|
-
if (columnCount <= 1) return totalCols;
|
|
309
|
-
if (totalCols >= 160)
|
|
310
|
-
return Math.floor((totalCols - GRID_GAP * (columnCount - 1)) / columnCount);
|
|
311
|
-
if (totalCols >= 120)
|
|
312
|
-
return Math.min(
|
|
313
|
-
36,
|
|
314
|
-
Math.floor((totalCols - GRID_GAP * (columnCount - 1)) / columnCount),
|
|
315
|
-
);
|
|
316
|
-
return Math.min(
|
|
317
|
-
28,
|
|
318
|
-
Math.floor((totalCols - GRID_GAP * (columnCount - 1)) / columnCount),
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function _autoColumnCount(totalCols, workerCount) {
|
|
323
|
-
if (workerCount <= 1) return 1;
|
|
324
|
-
if (totalCols >= 160) return Math.min(workerCount, 3);
|
|
325
|
-
if (totalCols >= 120) return Math.min(workerCount, 2);
|
|
326
|
-
return 1;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
293
|
// 텍스트/상태/색상 유틸은 tui-core.mjs에서 import (위 참조)
|
|
330
294
|
|
|
331
295
|
// ── MOCHA RGB (gradual fade 보간용) ──
|
|
@@ -354,8 +318,6 @@ function statusToRgb(status) {
|
|
|
354
318
|
return MOCHA_RGB.muted;
|
|
355
319
|
}
|
|
356
320
|
|
|
357
|
-
const FADE_DURATION_MS = 1500;
|
|
358
|
-
const FLASH_PHASE_MS = 250;
|
|
359
321
|
const CARD_GLOW_MS = 3000;
|
|
360
322
|
|
|
361
323
|
// Effect 1: Pulse border — running 워커 보더가 heartbeat 동기 breathing
|
|
@@ -374,22 +336,6 @@ function gradientBorderFn(topRgb, bottomRgb) {
|
|
|
374
336
|
};
|
|
375
337
|
}
|
|
376
338
|
|
|
377
|
-
// Effect 3: Flash-fade border — 상태 변경 시 백색 플래시 → 페이드아웃
|
|
378
|
-
function _flashFadeBorderColor(currentStatus, prevStatus, changedAt) {
|
|
379
|
-
const elapsed = Date.now() - (changedAt || 0);
|
|
380
|
-
if (elapsed >= FADE_DURATION_MS || !prevStatus) return null;
|
|
381
|
-
const statusRgb = statusToRgb(currentStatus);
|
|
382
|
-
if (elapsed < FLASH_PHASE_MS) {
|
|
383
|
-
const t = elapsed / FLASH_PHASE_MS;
|
|
384
|
-
const bright = { r: 255, g: 255, b: 255 };
|
|
385
|
-
const c = lerpRgb(bright, statusRgb, t);
|
|
386
|
-
return `\x1b[38;2;${c.r};${c.g};${c.b}m`;
|
|
387
|
-
}
|
|
388
|
-
const t = (elapsed - FLASH_PHASE_MS) / (FADE_DURATION_MS - FLASH_PHASE_MS);
|
|
389
|
-
const c = lerpRgb(statusRgb, MOCHA_RGB.border, t);
|
|
390
|
-
return `\x1b[38;2;${c.r};${c.g};${c.b}m`;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
339
|
function easeOutCubic(t) {
|
|
394
340
|
return 1 - (1 - t) ** 3;
|
|
395
341
|
}
|
|
@@ -436,26 +382,6 @@ function dedupeRole(role, name, cli) {
|
|
|
436
382
|
|
|
437
383
|
// wrapLine, wrapTextAll → tui-core.mjs에서 import
|
|
438
384
|
|
|
439
|
-
function _wrapText(
|
|
440
|
-
text,
|
|
441
|
-
width,
|
|
442
|
-
maxLines = DEFAULT_DETAIL_LINES,
|
|
443
|
-
rawMode = false,
|
|
444
|
-
) {
|
|
445
|
-
if (maxLines <= 0) return [];
|
|
446
|
-
const input = sanitizeTextBlock(text, rawMode);
|
|
447
|
-
if (!input) return [];
|
|
448
|
-
const wrapped = input
|
|
449
|
-
.split("\n")
|
|
450
|
-
.flatMap((line) => wrapLine(line, width))
|
|
451
|
-
.filter(Boolean);
|
|
452
|
-
if (wrapped.length <= maxLines) return wrapped;
|
|
453
|
-
return [
|
|
454
|
-
...wrapped.slice(0, maxLines - 1),
|
|
455
|
-
truncate(wrapped[wrapped.length - 1], width),
|
|
456
|
-
];
|
|
457
|
-
}
|
|
458
|
-
|
|
459
385
|
// ── virtual row buffer ────────────────────────────────────────────────────
|
|
460
386
|
class RowBuffer {
|
|
461
387
|
constructor() {
|
|
@@ -927,19 +853,6 @@ function buildHelpOverlay(width, height) {
|
|
|
927
853
|
return result;
|
|
928
854
|
}
|
|
929
855
|
|
|
930
|
-
// ── joinColumns ───────────────────────────────────────────────────────────
|
|
931
|
-
function _joinColumns(blocks, gap = GRID_GAP) {
|
|
932
|
-
const maxHeight = Math.max(...blocks.map((b) => b.length));
|
|
933
|
-
return Array.from({ length: maxHeight }, (_, rowIdx) =>
|
|
934
|
-
blocks
|
|
935
|
-
.map(
|
|
936
|
-
(block) =>
|
|
937
|
-
block[rowIdx] || " ".repeat(wcswidth(stripAnsi(block[0] || ""))),
|
|
938
|
-
)
|
|
939
|
-
.join(" ".repeat(gap)),
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
|
|
943
856
|
// ── normalizeWorkerState ──────────────────────────────────────────────────
|
|
944
857
|
function normalizeWorkerState(existing, state) {
|
|
945
858
|
return coreNormalizeWorkerState(
|
|
@@ -1010,26 +923,12 @@ export function createLogDashboard(opts = {}) {
|
|
|
1010
923
|
let rawModeEnabled = false;
|
|
1011
924
|
|
|
1012
925
|
// UX 위젯 (ISSUE-14)
|
|
1013
|
-
const tokenTracker = createTokenTracker();
|
|
1014
926
|
const searchState = createSearchState();
|
|
1015
927
|
const vimMotion = createVimMotion();
|
|
1016
928
|
const panelResizer = createPanelResizer({
|
|
1017
929
|
initialRatio: focus === "detail" ? 0.2 : 0.3,
|
|
1018
930
|
});
|
|
1019
931
|
|
|
1020
|
-
// Synapse 실시간 관제 (Phase 3)
|
|
1021
|
-
const synapseMetrics = deps.synapseMetrics || createMetricsCollector();
|
|
1022
|
-
const synapseStream =
|
|
1023
|
-
deps.synapseStream ||
|
|
1024
|
-
createSynapseEventStream({
|
|
1025
|
-
onEvent(event) {
|
|
1026
|
-
synapseMetrics.ingest(event);
|
|
1027
|
-
},
|
|
1028
|
-
fetchImpl: deps.synapseFetch,
|
|
1029
|
-
});
|
|
1030
|
-
// Synapse 자동 시작 (옵트인: deps.enableSynapse)
|
|
1031
|
-
if (deps.enableSynapse) synapseStream.start();
|
|
1032
|
-
|
|
1033
932
|
// virtual row buffer (altScreen 전용)
|
|
1034
933
|
const rowBuf = new RowBuffer();
|
|
1035
934
|
|
|
@@ -1046,26 +945,6 @@ export function createLogDashboard(opts = {}) {
|
|
|
1046
945
|
return Math.max(0, Math.round((now() - startedAt) / 1000));
|
|
1047
946
|
}
|
|
1048
947
|
|
|
1049
|
-
function getViewportColumns() {
|
|
1050
|
-
const v = Number.isFinite(columns)
|
|
1051
|
-
? columns
|
|
1052
|
-
: Number.isFinite(stream?.columns)
|
|
1053
|
-
? stream.columns
|
|
1054
|
-
: Number.isFinite(process.stdout?.columns)
|
|
1055
|
-
? process.stdout.columns
|
|
1056
|
-
: FALLBACK_COLUMNS;
|
|
1057
|
-
return Math.max(48, v || FALLBACK_COLUMNS);
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
function getViewportRows() {
|
|
1061
|
-
const v = Number.isFinite(stream?.rows)
|
|
1062
|
-
? stream.rows
|
|
1063
|
-
: Number.isFinite(process.stdout?.rows)
|
|
1064
|
-
? process.stdout.rows
|
|
1065
|
-
: FALLBACK_ROWS;
|
|
1066
|
-
return Math.max(10, v || FALLBACK_ROWS);
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
948
|
function visibleWorkerNames() {
|
|
1070
949
|
return [...workers.keys()].sort();
|
|
1071
950
|
}
|
|
@@ -1120,7 +999,6 @@ export function createLogDashboard(opts = {}) {
|
|
|
1120
999
|
function doClose() {
|
|
1121
1000
|
if (closed) return;
|
|
1122
1001
|
if (timer) clearInterval(timer);
|
|
1123
|
-
synapseStream.stop();
|
|
1124
1002
|
if (inputAttached && typeof input?.off === "function")
|
|
1125
1003
|
input.off("data", handleInput);
|
|
1126
1004
|
if (rawModeEnabled && typeof input?.setRawMode === "function")
|
|
@@ -1244,7 +1122,10 @@ export function createLogDashboard(opts = {}) {
|
|
|
1244
1122
|
if (key === "g") return;
|
|
1245
1123
|
|
|
1246
1124
|
// PgUp/PgDn: 페이지 단위 스크롤
|
|
1247
|
-
const pageSize = Math.max(
|
|
1125
|
+
const pageSize = Math.max(
|
|
1126
|
+
1,
|
|
1127
|
+
Math.floor(resolveViewportRows({ stream }) / 2),
|
|
1128
|
+
);
|
|
1248
1129
|
if (key === "\x1b[5~") {
|
|
1249
1130
|
scrollDetail(-pageSize);
|
|
1250
1131
|
return;
|
|
@@ -1442,8 +1323,8 @@ export function createLogDashboard(opts = {}) {
|
|
|
1442
1323
|
ensureSelectedWorker(names);
|
|
1443
1324
|
attachInput();
|
|
1444
1325
|
|
|
1445
|
-
const totalCols =
|
|
1446
|
-
const totalRows =
|
|
1326
|
+
const totalCols = resolveViewportColumns({ columns, stream });
|
|
1327
|
+
const totalRows = resolveViewportRows({ stream });
|
|
1447
1328
|
|
|
1448
1329
|
// Help overlay: 전체 화면 오버레이
|
|
1449
1330
|
if (helpOverlay) {
|
|
@@ -1469,13 +1350,6 @@ export function createLogDashboard(opts = {}) {
|
|
|
1469
1350
|
truncate(` ${color("▸", MOCHA.green)} ${flashMessage}`, totalCols),
|
|
1470
1351
|
);
|
|
1471
1352
|
}
|
|
1472
|
-
// Synapse 실시간 메트릭 (활성 시)
|
|
1473
|
-
if (synapseStream.isRunning) {
|
|
1474
|
-
const metricsSnap = synapseMetrics.snapshot();
|
|
1475
|
-
tier1.push(
|
|
1476
|
-
truncate(renderMetricsTier1(metricsSnap, totalCols), totalCols),
|
|
1477
|
-
);
|
|
1478
|
-
}
|
|
1479
1353
|
// 검색 프롬프트 (/ 모드)
|
|
1480
1354
|
const searchPrompt = searchState.renderPrompt(totalCols);
|
|
1481
1355
|
if (searchPrompt) {
|
|
@@ -1681,9 +1555,6 @@ export function createLogDashboard(opts = {}) {
|
|
|
1681
1555
|
? existing._logSec
|
|
1682
1556
|
: (explicitElapsed ?? nowElapsedSec());
|
|
1683
1557
|
workers.set(paneName, merged);
|
|
1684
|
-
// 토큰 히스토리 추적 (스파크라인용)
|
|
1685
|
-
if (merged.tokens !== undefined)
|
|
1686
|
-
tokenTracker.record(paneName, merged.tokens);
|
|
1687
1558
|
ensureSelectedWorker(visibleWorkerNames());
|
|
1688
1559
|
// follow-tail: 새 데이터 → 자동 scroll 재계산
|
|
1689
1560
|
if (followTail) detailScrollOffset = 0;
|
|
@@ -1762,177 +1633,8 @@ export function createLogDashboard(opts = {}) {
|
|
|
1762
1633
|
return attachToSession(w);
|
|
1763
1634
|
},
|
|
1764
1635
|
|
|
1765
|
-
// Synapse 관제 API
|
|
1766
|
-
startSynapse() {
|
|
1767
|
-
synapseStream.start();
|
|
1768
|
-
},
|
|
1769
|
-
stopSynapse() {
|
|
1770
|
-
synapseStream.stop();
|
|
1771
|
-
},
|
|
1772
|
-
getSynapseMetrics() {
|
|
1773
|
-
return synapseMetrics.snapshot();
|
|
1774
|
-
},
|
|
1775
|
-
|
|
1776
1636
|
close() {
|
|
1777
1637
|
doClose();
|
|
1778
1638
|
},
|
|
1779
1639
|
};
|
|
1780
1640
|
}
|
|
1781
|
-
|
|
1782
|
-
// ── Conductor Tier: 세션 테이블 렌더러 ─────────────────────────────────
|
|
1783
|
-
//
|
|
1784
|
-
// renderConductorTier(snapshot, cols)
|
|
1785
|
-
// snapshot: conductor.getSnapshot() 반환 배열
|
|
1786
|
-
// cols: 터미널 폭 (기본 100)
|
|
1787
|
-
//
|
|
1788
|
-
// 레이아웃:
|
|
1789
|
-
// ┌─ CONDUCTOR ──────────────────────────────────────────┐
|
|
1790
|
-
// │ ID Agent Host Health Last Out Restarts Why │
|
|
1791
|
-
// │ abc123 codex local ■ OK 2s ago 0 │
|
|
1792
|
-
// └──────────────────────────────────────────────────────┘
|
|
1793
|
-
//
|
|
1794
|
-
// Health 색상: healthy=green, stalled=yellow, input_wait=cyan,
|
|
1795
|
-
// failed=red, dead/init/starting=dim
|
|
1796
|
-
|
|
1797
|
-
const CONDUCTOR_STATE_LABEL = {
|
|
1798
|
-
init: { label: "INIT", seq: MOCHA.subtext },
|
|
1799
|
-
starting: { label: "START", seq: MOCHA.executing },
|
|
1800
|
-
healthy: { label: "OK", seq: MOCHA.ok },
|
|
1801
|
-
stalled: { label: "STALL", seq: MOCHA.yellow },
|
|
1802
|
-
input_wait: { label: "INPUT_WAIT", seq: FG.cyan },
|
|
1803
|
-
failed: { label: "FAIL", seq: MOCHA.fail },
|
|
1804
|
-
restarting: { label: "RESTART", seq: MOCHA.partial },
|
|
1805
|
-
dead: { label: "DEAD", seq: FG.gray },
|
|
1806
|
-
completed: { label: "DONE", seq: MOCHA.ok },
|
|
1807
|
-
};
|
|
1808
|
-
|
|
1809
|
-
function conductorHealthCell(state) {
|
|
1810
|
-
const entry = CONDUCTOR_STATE_LABEL[state] || {
|
|
1811
|
-
label: state.toUpperCase(),
|
|
1812
|
-
seq: FG.gray,
|
|
1813
|
-
};
|
|
1814
|
-
return `${entry.seq}■ ${entry.label}${RESET}`;
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
function conductorRelTime(ms) {
|
|
1818
|
-
if (!ms) return "—";
|
|
1819
|
-
const sec = Math.round((Date.now() - ms) / 1000);
|
|
1820
|
-
if (sec < 0) return "—";
|
|
1821
|
-
if (sec < 60) return `${sec}s ago`;
|
|
1822
|
-
if (sec < 3600) return `${Math.floor(sec / 60)}m ago`;
|
|
1823
|
-
return `${Math.floor(sec / 3600)}h ago`;
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
/**
|
|
1827
|
-
* Conductor 세션 테이블을 문자열 배열(행 목록)로 렌더링.
|
|
1828
|
-
*
|
|
1829
|
-
* @param {object[]} snapshot — conductor.getSnapshot() 결과
|
|
1830
|
-
* @param {number} [cols=100] — 터미널 폭
|
|
1831
|
-
* @returns {string[]} 렌더링된 행 목록 (altScreen rowBuf.set()에 바로 삽입 가능)
|
|
1832
|
-
*/
|
|
1833
|
-
export function renderConductorTier(snapshot, cols = 100) {
|
|
1834
|
-
const width = Math.max(48, cols);
|
|
1835
|
-
const inner = width - 4; // border: '│ ' + content + ' │'
|
|
1836
|
-
|
|
1837
|
-
// ── 열 너비 계산 ────────────────────────────────────────
|
|
1838
|
-
// ID(8) Agent(7) Host(6) Health(dyn) LastOut(dyn) Restarts(8) Why(rest)
|
|
1839
|
-
const COL_ID = 8;
|
|
1840
|
-
const COL_AGENT = 7;
|
|
1841
|
-
const COL_HOST = 6;
|
|
1842
|
-
const COL_RESTARTS = 4;
|
|
1843
|
-
const COL_HEALTH = 12; // '■ INPUT_WAIT' = 12 chars
|
|
1844
|
-
const COL_LASTOUT = 9; // '999m ago' = 8 + space
|
|
1845
|
-
// Why gets the remainder
|
|
1846
|
-
const fixedCols =
|
|
1847
|
-
COL_ID + COL_AGENT + COL_HOST + COL_HEALTH + COL_LASTOUT + COL_RESTARTS + 6; // 6 spaces between cols
|
|
1848
|
-
const COL_WHY = Math.max(4, inner - fixedCols);
|
|
1849
|
-
|
|
1850
|
-
function cell(text, width_) {
|
|
1851
|
-
return clip(String(text ?? ""), width_);
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
function buildRow(id, agent, host, healthCell, lastOut, restarts, why) {
|
|
1855
|
-
const idC = cell(id, COL_ID);
|
|
1856
|
-
const agentC = cell(agent, COL_AGENT);
|
|
1857
|
-
const hostC = cell(host, COL_HOST);
|
|
1858
|
-
const restartsC = cell(String(restarts ?? 0), COL_RESTARTS);
|
|
1859
|
-
const lastOutC = clip(lastOut, COL_LASTOUT);
|
|
1860
|
-
const whyC = cell(why, COL_WHY);
|
|
1861
|
-
// healthCell already has ANSI codes; pad its visible width manually
|
|
1862
|
-
const healthVis = wcswidth(stripAnsi(healthCell));
|
|
1863
|
-
const healthPad = Math.max(0, COL_HEALTH - healthVis);
|
|
1864
|
-
const healthC = healthCell + " ".repeat(healthPad);
|
|
1865
|
-
|
|
1866
|
-
return `${idC} ${agentC} ${hostC} ${healthC} ${lastOutC} ${restartsC} ${whyC}`;
|
|
1867
|
-
}
|
|
1868
|
-
|
|
1869
|
-
const boxWidth = inner;
|
|
1870
|
-
|
|
1871
|
-
// ── 타이틀 행 ───────────────────────────────────────────
|
|
1872
|
-
const titleText = ` CONDUCTOR `;
|
|
1873
|
-
const titleColored = bold(color(titleText, FG.accent));
|
|
1874
|
-
// Border top with title embedded: ┌─ CONDUCTOR ──...─┐
|
|
1875
|
-
const dashLen = Math.max(0, boxWidth - titleText.length);
|
|
1876
|
-
const dashLeft = 1;
|
|
1877
|
-
const dashRight = Math.max(0, dashLen - dashLeft);
|
|
1878
|
-
const borderSeq = MOCHA.border;
|
|
1879
|
-
const topBorder = `${borderSeq}┌${"─".repeat(dashLeft)}${RESET}${titleColored}${borderSeq}${"─".repeat(dashRight)}┐${RESET}`;
|
|
1880
|
-
|
|
1881
|
-
// ── ヘッダー行 ───────────────────────────────────────────
|
|
1882
|
-
const headerRow = buildRow(
|
|
1883
|
-
"ID",
|
|
1884
|
-
"Agent",
|
|
1885
|
-
"Host",
|
|
1886
|
-
clip("Health", COL_HEALTH),
|
|
1887
|
-
"Last Out",
|
|
1888
|
-
"Rst",
|
|
1889
|
-
"Why",
|
|
1890
|
-
);
|
|
1891
|
-
const headerLine = `${borderSeq}│${RESET} ${dim(headerRow)} ${borderSeq}│${RESET}`;
|
|
1892
|
-
|
|
1893
|
-
// ── データ行 ────────────────────────────────────────────
|
|
1894
|
-
const dataLines = [];
|
|
1895
|
-
if (!snapshot || snapshot.length === 0) {
|
|
1896
|
-
const emptyMsg = color("(no sessions)", FG.muted);
|
|
1897
|
-
const _emptyPad = clip(
|
|
1898
|
-
stripAnsi(emptyMsg) === "(no sessions)" ? emptyMsg : emptyMsg,
|
|
1899
|
-
inner,
|
|
1900
|
-
);
|
|
1901
|
-
dataLines.push(
|
|
1902
|
-
`${borderSeq}│${RESET} ${padRight(emptyMsg, inner - 2)} ${borderSeq}│${RESET}`,
|
|
1903
|
-
);
|
|
1904
|
-
} else {
|
|
1905
|
-
for (const s of snapshot) {
|
|
1906
|
-
const id = String(s.id ?? "").slice(0, COL_ID);
|
|
1907
|
-
const agent = String(s.agent ?? "unknown").slice(0, COL_AGENT);
|
|
1908
|
-
const host = "local";
|
|
1909
|
-
const state = s.state ?? "init";
|
|
1910
|
-
const healthCell = conductorHealthCell(state);
|
|
1911
|
-
const lastOut = conductorRelTime(s.health?.lastProbeAt ?? null);
|
|
1912
|
-
const restarts = s.restarts ?? 0;
|
|
1913
|
-
// derive "why" from last state transition context
|
|
1914
|
-
const why = s.health?.inputWaitPattern
|
|
1915
|
-
? String(s.health.inputWaitPattern).slice(0, COL_WHY)
|
|
1916
|
-
: "";
|
|
1917
|
-
|
|
1918
|
-
const rowText = buildRow(
|
|
1919
|
-
id,
|
|
1920
|
-
agent,
|
|
1921
|
-
host,
|
|
1922
|
-
healthCell,
|
|
1923
|
-
lastOut,
|
|
1924
|
-
restarts,
|
|
1925
|
-
why,
|
|
1926
|
-
);
|
|
1927
|
-
dataLines.push(`${borderSeq}│${RESET} ${rowText} ${borderSeq}│${RESET}`);
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
|
|
1931
|
-
// ── Bottom border ─────────────────────────────────────
|
|
1932
|
-
const botBorder = `${borderSeq}└${"─".repeat(boxWidth)}┘${RESET}`;
|
|
1933
|
-
|
|
1934
|
-
return [topBorder, headerLine, ...dataLines, botBorder];
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
// 하위 호환
|
|
1938
|
-
export { createLogDashboard as createTui };
|