u-foo 2.3.32 → 2.4.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/README.md +157 -213
- package/README.zh-CN.md +151 -197
- package/SKILLS/ufoo/SKILL.md +8 -8
- package/bin/uagy.js +69 -0
- package/bin/uclaude.js +2 -2
- package/bin/ucode.js +4 -4
- package/bin/ucodex.js +2 -2
- package/bin/ufoo.js +5 -23
- package/modules/AGENTS.template.md +1 -1
- package/modules/bus/SKILLS/ubus/SKILL.md +35 -10
- package/package.json +5 -5
- package/scripts/chat-app-smoke.js +1 -1
- package/scripts/global-chat-switch-benchmark.js +5 -5
- package/scripts/ink-demo.js +1 -1
- package/scripts/ink-smoke.js +1 -1
- package/scripts/ucode-app-smoke.js +1 -1
- package/src/{agent → agents/activity}/activityDetector.js +39 -2
- package/src/{agent → agents/activity}/activityStatePublisher.js +1 -1
- package/src/{agent → agents/activity}/activityStateWriter.js +2 -2
- package/src/{agent → agents/activity}/activityTracker.js +1 -1
- package/src/agents/activity/index.js +8 -0
- package/src/{agent → agents/controller}/controllerToolExecutor.js +4 -4
- package/src/agents/controller/index.js +8 -0
- package/src/{agent → agents/controller}/loopObservability.js +2 -2
- package/src/{agent → agents/controller}/loopRuntime.js +1 -1
- package/src/{agent → agents/controller}/ufooAgent.js +9 -9
- package/src/agents/index.js +10 -0
- package/src/agents/internal/index.js +3 -0
- package/src/{agent → agents/internal}/internalRunner.js +45 -22
- package/src/agents/launch/agyConversation.js +159 -0
- package/src/agents/launch/index.js +12 -0
- package/src/{agent → agents/launch}/launchEnvironment.js +2 -3
- package/src/{agent → agents/launch}/launcher.js +64 -21
- package/src/{agent → agents/launch}/notifier.js +23 -12
- package/src/{agent → agents/launch}/ptyRunner.js +44 -12
- package/src/{agent → agents/launch}/ptyWrapper.js +2 -2
- package/src/{agent → agents/launch}/publisherRouting.js +1 -1
- package/src/{agent → agents/launch}/readyDetector.js +23 -0
- package/src/{agent → agents/prompts}/defaultBootstrap.js +63 -4
- package/src/{group/bootstrap.js → agents/prompts/groupBootstrap.js} +41 -6
- package/src/agents/prompts/index.js +8 -0
- package/src/{code/prompts → agents/prompts/native}/index.js +1 -1
- package/src/{agent → agents/providers}/claudeThreadProvider.js +1 -1
- package/src/{agent → agents/providers}/codexThreadProvider.js +1 -1
- package/src/{agent → agents/providers}/directAuthStatus.js +184 -1
- package/src/agents/providers/index.js +13 -0
- package/src/{agent → agents/providers}/upstreamTransport.js +2 -2
- package/src/{chat → app/chat}/agentSockets.js +1 -1
- package/src/{chat → app/chat}/commandExecutor.js +50 -26
- package/src/{chat → app/chat}/commands.js +119 -5
- package/src/{chat → app/chat}/daemonConnection.js +1 -1
- package/src/{chat → app/chat}/daemonMessageRouter.js +45 -3
- package/src/{chat → app/chat}/dashboardView.js +2 -1
- package/src/app/chat/index.js +6 -0
- package/src/{chat → app/chat}/inputSubmitHandler.js +4 -13
- package/src/{chat → app/chat}/internalAgentLogHistory.js +1 -1
- package/src/app/chat/multiWindow/index.js +268 -0
- package/src/app/chat/multiWindow/paneLayout.js +84 -0
- package/src/app/chat/multiWindow/paneManager.js +299 -0
- package/src/app/chat/multiWindow/renderer.js +384 -0
- package/src/app/chat/multiWindow/virtualTerminal.js +327 -0
- package/src/{chat → app/chat}/transport.js +1 -1
- package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
- package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
- package/src/{init/index.js → app/cli/features/init.js} +14 -32
- package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
- package/src/app/cli/index.js +9 -0
- package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
- package/src/{cli.js → app/cli/run.js} +59 -57
- package/src/app/index.js +6 -0
- package/src/code/agent.js +10 -9
- package/src/code/index.js +2 -0
- package/src/code/launcher/index.js +9 -0
- package/src/{agent → code/launcher}/ucode.js +7 -8
- package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
- package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
- package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
- package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
- package/src/code/nativeRunner.js +4 -4
- package/src/code/tui.js +3 -1454
- package/src/config.js +15 -2
- package/src/{bus → coordination/bus}/activate.js +2 -2
- package/src/{bus → coordination/bus}/daemon.js +15 -5
- package/src/coordination/bus/envelope.js +173 -0
- package/src/{bus → coordination/bus}/index.js +7 -3
- package/src/{bus → coordination/bus}/inject.js +11 -3
- package/src/{bus → coordination/bus}/message.js +1 -1
- package/src/coordination/bus/messageMeta.js +130 -0
- package/src/coordination/bus/promptEnvelope.js +65 -0
- package/src/{bus → coordination/bus}/shake.js +1 -1
- package/src/{bus → coordination/bus}/store.js +3 -3
- package/src/{bus → coordination/bus}/subscriber.js +2 -2
- package/src/{bus → coordination/bus}/utils.js +2 -2
- package/src/{history → coordination/history}/inputTimeline.js +5 -5
- package/src/coordination/index.js +10 -0
- package/src/{memory → coordination/memory}/historySearch.js +1 -1
- package/src/{memory → coordination/memory}/index.js +3 -3
- package/src/{report → coordination/report}/store.js +2 -2
- package/src/{status → coordination/status}/index.js +3 -3
- package/src/online/bridge.js +2 -2
- package/src/{controller → orchestration/controller}/flags.js +1 -1
- package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
- package/src/orchestration/controller/index.js +10 -0
- package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
- package/src/orchestration/groups/bootstrap.js +3 -0
- package/src/orchestration/groups/index.js +10 -0
- package/src/orchestration/groups/promptProfiles.js +3 -0
- package/src/{group → orchestration/groups}/templates.js +1 -1
- package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
- package/src/orchestration/index.js +7 -0
- package/src/orchestration/solo/index.js +3 -0
- package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
- package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
- package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
- package/src/{daemon → runtime/daemon}/index.js +105 -53
- package/src/{daemon → runtime/daemon}/ipcServer.js +1 -1
- package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
- package/src/{daemon → runtime/daemon}/ops.js +48 -61
- package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
- package/src/{daemon → runtime/daemon}/promptRequest.js +7 -7
- package/src/runtime/daemon/providerSessions.js +230 -0
- package/src/{daemon → runtime/daemon}/reporting.js +4 -4
- package/src/{daemon → runtime/daemon}/run.js +4 -4
- package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
- package/src/{daemon → runtime/daemon}/status.js +5 -5
- package/src/runtime/index.js +10 -0
- package/src/{projects → runtime/projects}/registry.js +1 -1
- package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
- package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
- package/src/tools/handlers/common.js +1 -1
- package/src/tools/handlers/listAgents.js +1 -1
- package/src/tools/handlers/memory.js +3 -3
- package/src/tools/handlers/readBusSummary.js +1 -1
- package/src/tools/handlers/readOpenDecisions.js +1 -1
- package/src/tools/handlers/readProjectRegistry.js +1 -1
- package/src/tools/handlers/readPromptHistory.js +2 -2
- package/src/tools/schemaFixtures.js +1 -1
- package/src/ui/MIGRATION.md +42 -88
- package/src/ui/format/index.js +5 -28
- package/src/ui/index.js +1 -1
- package/src/ui/{components → ink}/ChatApp.js +812 -88
- package/src/ui/ink/DashboardBar.js +685 -0
- package/src/ui/{components → ink}/MultilineInput.js +230 -5
- package/src/ui/{components → ink}/UcodeApp.js +16 -7
- package/src/ui/{components → ink}/agentMirror.js +24 -19
- package/src/ui/{components → ink}/chatReducer.js +29 -7
- package/src/bus/messageMeta.js +0 -52
- package/src/chat/agentViewController.js +0 -1072
- package/src/chat/chatLogController.js +0 -138
- package/src/chat/completionController.js +0 -533
- package/src/chat/dashboardKeyController.js +0 -533
- package/src/chat/index.js +0 -2222
- package/src/chat/inputHistoryController.js +0 -135
- package/src/chat/inputListenerController.js +0 -470
- package/src/chat/layout.js +0 -186
- package/src/chat/pasteController.js +0 -81
- package/src/chat/statusLineController.js +0 -223
- package/src/chat/streamTracker.js +0 -156
- package/src/code/config +0 -0
- package/src/daemon/providerSessions.js +0 -488
- package/src/terminal/adapters/internalPtyAdapter.js +0 -42
- package/src/ui/components/DashboardBar.js +0 -417
- /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
- /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
- /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
- /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
- /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
- /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
- /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
- /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
- /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
- /package/src/{agent → agents/providers}/credentials/index.js +0 -0
- /package/src/{chat → app/chat}/agentBar.js +0 -0
- /package/src/{chat → app/chat}/agentDirectory.js +0 -0
- /package/src/{chat → app/chat}/cronScheduler.js +0 -0
- /package/src/{chat → app/chat}/daemonCoordinator.js +0 -0
- /package/src/{chat → app/chat}/daemonReconnect.js +0 -0
- /package/src/{chat → app/chat}/daemonTransport.js +0 -0
- /package/src/{chat → app/chat}/daemonTransportDefaults.js +0 -0
- /package/src/{chat → app/chat}/inputMath.js +0 -0
- /package/src/{chat → app/chat}/projectCloseController.js +0 -0
- /package/src/{chat → app/chat}/rawKeyMap.js +0 -0
- /package/src/{chat → app/chat}/settingsController.js +0 -0
- /package/src/{chat → app/chat}/shellCommand.js +0 -0
- /package/src/{chat → app/chat}/text.js +0 -0
- /package/src/{chat → app/chat}/transientAgentState.js +0 -0
- /package/src/{cli → app/cli}/busCoreCommands.js +0 -0
- /package/src/{skills/index.js → app/cli/features/skills.js} +0 -0
- /package/src/{bus → coordination/bus}/nickname.js +0 -0
- /package/src/{bus → coordination/bus}/queue.js +0 -0
- /package/src/{context → coordination/context}/decisions.js +0 -0
- /package/src/{context → coordination/context}/doctor.js +0 -0
- /package/src/{context → coordination/context}/index.js +0 -0
- /package/src/{context → coordination/context}/sync.js +0 -0
- /package/src/{ufoo → coordination/state}/agentRegistryDiagnostics.js +0 -0
- /package/src/{ufoo → coordination/state}/agentsStore.js +0 -0
- /package/src/{ufoo → coordination/state}/paths.js +0 -0
- /package/src/{controller → orchestration/controller}/launchRouting.js +0 -0
- /package/src/{controller → orchestration/controller}/routerFastPath.js +0 -0
- /package/src/{controller → orchestration/controller}/routerFinalize.js +0 -0
- /package/src/{group → orchestration/groups}/diagram.js +0 -0
- /package/src/{group → orchestration/groups}/templateValidation.js +0 -0
- /package/src/{solo → orchestration/solo}/commands.js +0 -0
- /package/src/{shared → runtime/contracts}/eventContract.js +0 -0
- /package/src/{shared → runtime/contracts}/ptySocketContract.js +0 -0
- /package/src/{providerapi → runtime/privacy}/redactor.js +0 -0
- /package/src/{providerapi → runtime/privacy}/shadowDiff.js +0 -0
- /package/src/{utils → runtime/process}/nodeExecutable.js +0 -0
- /package/src/{projects → runtime/projects}/identity.js +0 -0
- /package/src/{projects → runtime/projects}/index.js +0 -0
- /package/src/{projects → runtime/projects}/projectId.js +0 -0
- /package/src/{projects → runtime/projects}/runtimes.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapterContract.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/externalAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/hostAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/internalQueueAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/terminalAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/adapters/tmuxAdapter.js +0 -0
- /package/src/{terminal → runtime/terminal}/detect.js +0 -0
- /package/src/{terminal → runtime/terminal}/index.js +0 -0
- /package/src/{terminal → runtime/terminal}/iterm2.js +0 -0
- /package/src/{utils → ui/format}/banner.js +0 -0
- /package/src/{shared → ui/format}/markdownRenderer.js +0 -0
- /package/src/ui/{components → ink}/InkDemo.js +0 -0
|
@@ -1,1072 +0,0 @@
|
|
|
1
|
-
const os = require("os");
|
|
2
|
-
const { version: packageVersion } = require("../../package.json");
|
|
3
|
-
|
|
4
|
-
const ANSI_RESET = "\x1b[0m";
|
|
5
|
-
const CLAUDE_ORANGE = "\x1b[38;2;217;119;87m";
|
|
6
|
-
const BUS_STATUS_INDICATORS = {
|
|
7
|
-
working: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
8
|
-
starting: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
9
|
-
waiting_input: ["∙", "∙∙", "∙∙∙", "∙∙", "∙"],
|
|
10
|
-
blocked: ["!"],
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
function createAgentViewController(options = {}) {
|
|
14
|
-
const {
|
|
15
|
-
screen,
|
|
16
|
-
input,
|
|
17
|
-
processStdout = process.stdout,
|
|
18
|
-
now = () => Date.now(),
|
|
19
|
-
setTimeoutFn = setTimeout,
|
|
20
|
-
setIntervalFn = setInterval,
|
|
21
|
-
clearIntervalFn = clearInterval,
|
|
22
|
-
computeAgentBar = () => ({ bar: "", windowStart: 0 }),
|
|
23
|
-
agentBarHints = { normal: "", dashboard: "" },
|
|
24
|
-
maxAgentWindow = 4,
|
|
25
|
-
getFocusMode = () => "input",
|
|
26
|
-
setFocusMode = () => {},
|
|
27
|
-
getSelectedAgentIndex = () => -1,
|
|
28
|
-
setSelectedAgentIndex = () => {},
|
|
29
|
-
getActiveAgents = () => [],
|
|
30
|
-
getAgentListWindowStart = () => 0,
|
|
31
|
-
setAgentListWindowStart = () => {},
|
|
32
|
-
getAgentLabel = (id) => id,
|
|
33
|
-
getAgentStates = () => ({}),
|
|
34
|
-
getAgentActivityMeta = () => ({}),
|
|
35
|
-
getProjectRoot = () => process.cwd(),
|
|
36
|
-
setDashboardView = () => {},
|
|
37
|
-
setScreenGrabKeys = (value) => {
|
|
38
|
-
if (screen) screen.grabKeys = Boolean(value);
|
|
39
|
-
},
|
|
40
|
-
clearTargetAgent = () => {},
|
|
41
|
-
renderDashboard = () => {},
|
|
42
|
-
focusInput = () => {},
|
|
43
|
-
resizeInput = () => {},
|
|
44
|
-
renderScreen = () => {},
|
|
45
|
-
getInjectSockPath = () => "",
|
|
46
|
-
connectAgentOutput = () => {},
|
|
47
|
-
disconnectAgentOutput = () => {},
|
|
48
|
-
connectAgentInput = () => {},
|
|
49
|
-
disconnectAgentInput = () => {},
|
|
50
|
-
sendRaw = () => {},
|
|
51
|
-
sendBusMessage = () => {},
|
|
52
|
-
sendResize = () => {},
|
|
53
|
-
requestScreenSnapshot = () => {},
|
|
54
|
-
sendBusWatch = () => {},
|
|
55
|
-
getBusLogHistory = () => [],
|
|
56
|
-
} = options;
|
|
57
|
-
|
|
58
|
-
if (!screen || typeof screen.render !== "function") {
|
|
59
|
-
throw new Error("createAgentViewController requires screen.render");
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
let currentView = "main";
|
|
63
|
-
let viewingAgent = null;
|
|
64
|
-
let agentViewUsesBus = false;
|
|
65
|
-
let agentOutputSuppressed = false;
|
|
66
|
-
let agentBarVisible = false;
|
|
67
|
-
let detachedChildren = null;
|
|
68
|
-
let agentInputSuppressUntil = 0;
|
|
69
|
-
let busInputValue = "";
|
|
70
|
-
let busInputCursor = 0;
|
|
71
|
-
let busLogLines = [];
|
|
72
|
-
let busStartupAgentId = "";
|
|
73
|
-
let busStartupLineCount = 0;
|
|
74
|
-
let busAgentReplyActive = false;
|
|
75
|
-
let busStatusInterval = null;
|
|
76
|
-
let busStatusIndex = 0;
|
|
77
|
-
let busStatusKey = "";
|
|
78
|
-
let busStatusLocalStartedAt = 0;
|
|
79
|
-
const originalRender = screen.render.bind(screen);
|
|
80
|
-
let renderFrozen = false;
|
|
81
|
-
|
|
82
|
-
screen.render = function wrappedRender() {
|
|
83
|
-
if (renderFrozen) return;
|
|
84
|
-
return originalRender();
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
function getRows() {
|
|
88
|
-
if (Number.isFinite(screen.height) && screen.height > 0) return screen.height;
|
|
89
|
-
if (Number.isFinite(screen.rows) && screen.rows > 0) return screen.rows;
|
|
90
|
-
return processStdout.rows || 24;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function getCols() {
|
|
94
|
-
if (Number.isFinite(screen.width) && screen.width > 0) return screen.width;
|
|
95
|
-
if (Number.isFinite(screen.cols) && screen.cols > 0) return screen.cols;
|
|
96
|
-
return processStdout.columns || 80;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function stripAnsi(text = "") {
|
|
100
|
-
return String(text || "").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
|
|
101
|
-
.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function hasAnsi(text = "") {
|
|
105
|
-
return /\x1b(?:\][^\x07\x1b]*(?:\x07|\x1b\\)|\[[0-9;?]*[ -/]*[@-~])/.test(String(text || ""));
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function clamp(value, min, max) {
|
|
109
|
-
const normalized = Number.isFinite(value) ? Math.floor(value) : min;
|
|
110
|
-
return Math.max(min, Math.min(max, normalized));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function charDisplayWidth(char = "") {
|
|
114
|
-
if (!char) return 0;
|
|
115
|
-
const code = char.codePointAt(0) || 0;
|
|
116
|
-
if (code === 0) return 0;
|
|
117
|
-
if (code < 32 || (code >= 0x7f && code < 0xa0)) return 0;
|
|
118
|
-
if ((code >= 0x0300 && code <= 0x036f) ||
|
|
119
|
-
(code >= 0x1ab0 && code <= 0x1aff) ||
|
|
120
|
-
(code >= 0x1dc0 && code <= 0x1dff) ||
|
|
121
|
-
(code >= 0x20d0 && code <= 0x20ff) ||
|
|
122
|
-
(code >= 0xfe20 && code <= 0xfe2f)) {
|
|
123
|
-
return 0;
|
|
124
|
-
}
|
|
125
|
-
if ((code >= 0x1100 && code <= 0x115f) ||
|
|
126
|
-
code === 0x2329 ||
|
|
127
|
-
code === 0x232a ||
|
|
128
|
-
(code >= 0x2e80 && code <= 0xa4cf) ||
|
|
129
|
-
(code >= 0xac00 && code <= 0xd7a3) ||
|
|
130
|
-
(code >= 0xf900 && code <= 0xfaff) ||
|
|
131
|
-
(code >= 0xfe10 && code <= 0xfe19) ||
|
|
132
|
-
(code >= 0xfe30 && code <= 0xfe6f) ||
|
|
133
|
-
(code >= 0xff00 && code <= 0xff60) ||
|
|
134
|
-
(code >= 0xffe0 && code <= 0xffe6) ||
|
|
135
|
-
(code >= 0x1f300 && code <= 0x1faff)) {
|
|
136
|
-
return 2;
|
|
137
|
-
}
|
|
138
|
-
return 1;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function displayWidth(text = "") {
|
|
142
|
-
return Array.from(stripAnsi(String(text || ""))).reduce((sum, char) => sum + charDisplayWidth(char), 0);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function padToWidth(text = "", width = 1) {
|
|
146
|
-
const cells = displayWidth(text);
|
|
147
|
-
return String(text || "") + " ".repeat(Math.max(0, width - cells));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function truncateToWidth(text = "", width = 1) {
|
|
151
|
-
const target = Math.max(1, width);
|
|
152
|
-
let out = "";
|
|
153
|
-
let cells = 0;
|
|
154
|
-
for (const char of Array.from(stripAnsi(String(text || "")))) {
|
|
155
|
-
const charWidth = charDisplayWidth(char);
|
|
156
|
-
if (cells + charWidth > target) break;
|
|
157
|
-
out += char;
|
|
158
|
-
cells += charWidth;
|
|
159
|
-
}
|
|
160
|
-
return padToWidth(out, target);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function fitText(text = "", width = 1) {
|
|
164
|
-
const normalizedWidth = Math.max(1, width);
|
|
165
|
-
const clean = stripAnsi(String(text || "")).replace(/\r/g, "");
|
|
166
|
-
if (displayWidth(clean) <= normalizedWidth) {
|
|
167
|
-
return padToWidth(clean, normalizedWidth);
|
|
168
|
-
}
|
|
169
|
-
if (normalizedWidth <= 1) return truncateToWidth(clean, normalizedWidth);
|
|
170
|
-
return `${truncateToWidth(clean, normalizedWidth - 1).trimEnd()}…`;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function fitAnsiText(text = "", width = 1) {
|
|
174
|
-
const normalizedWidth = Math.max(1, width);
|
|
175
|
-
const raw = String(text || "").replace(/\r/g, "");
|
|
176
|
-
if (!hasAnsi(raw)) return fitText(raw, normalizedWidth);
|
|
177
|
-
if (displayWidth(raw) <= normalizedWidth) {
|
|
178
|
-
return padToWidth(raw, normalizedWidth);
|
|
179
|
-
}
|
|
180
|
-
if (normalizedWidth <= 1) return "…";
|
|
181
|
-
|
|
182
|
-
const ansiPattern = /\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
183
|
-
let out = "";
|
|
184
|
-
let cells = 0;
|
|
185
|
-
let index = 0;
|
|
186
|
-
while (index < raw.length && cells < normalizedWidth - 1) {
|
|
187
|
-
ansiPattern.lastIndex = index;
|
|
188
|
-
const match = ansiPattern.exec(raw);
|
|
189
|
-
if (match && match.index === index) {
|
|
190
|
-
out += match[0];
|
|
191
|
-
index += match[0].length;
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
const char = Array.from(raw.slice(index))[0] || "";
|
|
195
|
-
if (!char) break;
|
|
196
|
-
const charWidth = charDisplayWidth(char);
|
|
197
|
-
if (cells + charWidth > normalizedWidth - 1) break;
|
|
198
|
-
out += char;
|
|
199
|
-
cells += charWidth;
|
|
200
|
-
index += char.length;
|
|
201
|
-
}
|
|
202
|
-
const suffix = `${ANSI_RESET}…`;
|
|
203
|
-
return padToWidth(`${out}${suffix}`, normalizedWidth);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function horizontalLine(width = 80) {
|
|
207
|
-
return "─".repeat(Math.max(1, width));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function plainLine(text = "", width = 80) {
|
|
211
|
-
return fitText(text, Math.max(1, width));
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function logLine(text = "", width = 80) {
|
|
215
|
-
const normalizedWidth = Math.max(1, width);
|
|
216
|
-
return hasAnsi(text) ? fitAnsiText(text, normalizedWidth) : plainLine(text, normalizedWidth);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function parseTimeMs(value) {
|
|
220
|
-
if (Number.isFinite(value)) return Number(value);
|
|
221
|
-
const text = String(value || "").trim();
|
|
222
|
-
if (!text) return NaN;
|
|
223
|
-
const parsed = Date.parse(text);
|
|
224
|
-
return Number.isFinite(parsed) ? parsed : NaN;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function formatElapsed(ms = 0) {
|
|
228
|
-
const totalSeconds = Math.max(0, Math.floor(Number(ms) / 1000));
|
|
229
|
-
return `${totalSeconds} s`;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function normalizeActivityState(value = "") {
|
|
233
|
-
const state = String(value || "").trim().toLowerCase();
|
|
234
|
-
if (state === "waiting") return "waiting_input";
|
|
235
|
-
if (state === "busy" || state === "processing") return "working";
|
|
236
|
-
return state;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function getActivityLabel(state = "") {
|
|
240
|
-
if (state === "working") return "working";
|
|
241
|
-
if (state === "waiting_input") return "waiting";
|
|
242
|
-
if (state === "blocked") return "blocked";
|
|
243
|
-
if (state === "starting") return "starting";
|
|
244
|
-
if (state === "idle" || state === "ready") return "ready";
|
|
245
|
-
return state || "ready";
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function isTimedActivityState(state = "") {
|
|
249
|
-
return state === "working"
|
|
250
|
-
|| state === "waiting_input"
|
|
251
|
-
|| state === "blocked"
|
|
252
|
-
|| state === "starting";
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function asActivityObject(value) {
|
|
256
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function pickActivityDetail(meta = {}) {
|
|
260
|
-
const candidates = [
|
|
261
|
-
meta.activity_detail,
|
|
262
|
-
meta.detail,
|
|
263
|
-
meta.status_text,
|
|
264
|
-
meta.command,
|
|
265
|
-
meta.tool_name,
|
|
266
|
-
meta.tool,
|
|
267
|
-
];
|
|
268
|
-
return String(candidates.find((item) => String(item || "").trim()) || "").trim();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function getViewingAgentActivity() {
|
|
272
|
-
const states = getAgentStates() || {};
|
|
273
|
-
const stateEntry = viewingAgent && states ? states[viewingAgent] : "";
|
|
274
|
-
const stateObject = asActivityObject(stateEntry);
|
|
275
|
-
const meta = {
|
|
276
|
-
...(stateObject || {}),
|
|
277
|
-
...(asActivityObject(getAgentActivityMeta(viewingAgent)) || {}),
|
|
278
|
-
};
|
|
279
|
-
const state = normalizeActivityState(meta.activity_state || meta.state || (stateObject ? "" : stateEntry) || "");
|
|
280
|
-
const detail = pickActivityDetail(meta);
|
|
281
|
-
const sinceMs = parseTimeMs(meta.activity_since || meta.since || meta.updated_at || meta.updatedAt);
|
|
282
|
-
return { state: state || "ready", detail, sinceMs };
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function resolveBusStatus() {
|
|
286
|
-
const activity = getViewingAgentActivity();
|
|
287
|
-
const state = activity.state || "ready";
|
|
288
|
-
const timed = isTimedActivityState(state);
|
|
289
|
-
const key = `${viewingAgent || ""}:${state}:${activity.detail || ""}`;
|
|
290
|
-
if (key !== busStatusKey) {
|
|
291
|
-
busStatusKey = key;
|
|
292
|
-
busStatusIndex = 0;
|
|
293
|
-
busStatusLocalStartedAt = now();
|
|
294
|
-
}
|
|
295
|
-
const startedAt = timed && Number.isFinite(activity.sinceMs)
|
|
296
|
-
? activity.sinceMs
|
|
297
|
-
: busStatusLocalStartedAt;
|
|
298
|
-
return {
|
|
299
|
-
...activity,
|
|
300
|
-
state,
|
|
301
|
-
label: getActivityLabel(state),
|
|
302
|
-
timed,
|
|
303
|
-
startedAt,
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function buildBusStatusLine(width = 80, status = resolveBusStatus()) {
|
|
308
|
-
const normalizedWidth = Math.max(1, width);
|
|
309
|
-
const detail = status.detail ? ` · ${status.detail}` : "";
|
|
310
|
-
if (status.timed) {
|
|
311
|
-
const indicators = BUS_STATUS_INDICATORS[status.state] || BUS_STATUS_INDICATORS.working;
|
|
312
|
-
const indicator = indicators[busStatusIndex % indicators.length] || "";
|
|
313
|
-
const elapsed = formatElapsed(now() - status.startedAt);
|
|
314
|
-
return fitText(`${indicator} ${status.label} · ${elapsed}${detail}`, normalizedWidth);
|
|
315
|
-
}
|
|
316
|
-
if (normalizedWidth < 32) return fitText(`ufoo · ${status.label}`, normalizedWidth);
|
|
317
|
-
if (normalizedWidth < 48) return fitText(`ufoo · ${status.label} · Enter send`, normalizedWidth);
|
|
318
|
-
return fitText(`ufoo · ${status.label} · Enter send · Esc back${detail}`, normalizedWidth);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function stopBusStatusTimer() {
|
|
322
|
-
if (!busStatusInterval) return;
|
|
323
|
-
clearIntervalFn(busStatusInterval);
|
|
324
|
-
busStatusInterval = null;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function syncBusStatusTimer(status) {
|
|
328
|
-
const shouldTick = currentView === "agent" && agentViewUsesBus && status && status.timed;
|
|
329
|
-
if (!shouldTick) {
|
|
330
|
-
stopBusStatusTimer();
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
if (busStatusInterval) return;
|
|
334
|
-
busStatusInterval = setIntervalFn(() => {
|
|
335
|
-
busStatusIndex += 1;
|
|
336
|
-
renderBusView();
|
|
337
|
-
}, 1000);
|
|
338
|
-
if (busStatusInterval && typeof busStatusInterval.unref === "function") {
|
|
339
|
-
busStatusInterval.unref();
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
function sliceDisplayCells(text = "", startCell = 0, maxCells = 1) {
|
|
344
|
-
const targetStart = Math.max(0, startCell);
|
|
345
|
-
const targetWidth = Math.max(1, maxCells);
|
|
346
|
-
let out = "";
|
|
347
|
-
let cells = 0;
|
|
348
|
-
let started = false;
|
|
349
|
-
for (const char of Array.from(String(text || ""))) {
|
|
350
|
-
const charWidth = charDisplayWidth(char);
|
|
351
|
-
const nextCells = cells + charWidth;
|
|
352
|
-
if (!started) {
|
|
353
|
-
if (nextCells <= targetStart) {
|
|
354
|
-
cells = nextCells;
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
started = true;
|
|
358
|
-
}
|
|
359
|
-
if (displayWidth(out) + charWidth > targetWidth) break;
|
|
360
|
-
out += char;
|
|
361
|
-
cells = nextCells;
|
|
362
|
-
}
|
|
363
|
-
return out;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function wrapTextLine(text = "", width = 80) {
|
|
367
|
-
const inner = Math.max(1, width);
|
|
368
|
-
if (hasAnsi(text)) return [String(text || "")];
|
|
369
|
-
const clean = stripAnsi(String(text || ""));
|
|
370
|
-
if (!clean) return [""];
|
|
371
|
-
const lines = [];
|
|
372
|
-
let current = "";
|
|
373
|
-
let cells = 0;
|
|
374
|
-
for (const char of Array.from(clean)) {
|
|
375
|
-
const charWidth = charDisplayWidth(char);
|
|
376
|
-
if (cells > 0 && cells + charWidth > inner) {
|
|
377
|
-
lines.push(current);
|
|
378
|
-
current = "";
|
|
379
|
-
cells = 0;
|
|
380
|
-
}
|
|
381
|
-
current += char;
|
|
382
|
-
cells += charWidth;
|
|
383
|
-
}
|
|
384
|
-
lines.push(current);
|
|
385
|
-
return lines;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function getWrappedBusLogLines(width = 80) {
|
|
389
|
-
const inner = Math.max(1, width);
|
|
390
|
-
const wrapped = [];
|
|
391
|
-
for (const line of busLogLines) {
|
|
392
|
-
wrapped.push(...wrapTextLine(line, inner));
|
|
393
|
-
}
|
|
394
|
-
return wrapped;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function writeAt(row, content = "") {
|
|
398
|
-
processStdout.write(`\x1b[${row};1H\x1b[2K${content}`);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function forceScreenRepaint() {
|
|
402
|
-
if (typeof screen.realloc === "function") {
|
|
403
|
-
screen.realloc();
|
|
404
|
-
} else if (typeof screen.alloc === "function") {
|
|
405
|
-
screen.alloc(true);
|
|
406
|
-
}
|
|
407
|
-
try {
|
|
408
|
-
originalRender();
|
|
409
|
-
} catch {
|
|
410
|
-
// Ignore repaint failures while restoring from raw agent view.
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function compactProjectPath(projectRoot = "") {
|
|
415
|
-
const raw = String(projectRoot || process.cwd() || "").trim();
|
|
416
|
-
const home = os.homedir();
|
|
417
|
-
if (home && (raw === home || raw.startsWith(`${home}/`))) {
|
|
418
|
-
return `~${raw.slice(home.length)}`;
|
|
419
|
-
}
|
|
420
|
-
return raw || ".";
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function borderedLines(lines = [], innerWidth = 56) {
|
|
424
|
-
const contentWidth = Math.max(1, innerWidth);
|
|
425
|
-
const out = [`╭${"─".repeat(contentWidth + 2)}╮`];
|
|
426
|
-
for (const line of lines) {
|
|
427
|
-
out.push(`│ ${fitText(line, contentWidth)} │`);
|
|
428
|
-
}
|
|
429
|
-
out.push(`╰${"─".repeat(contentWidth + 2)}╯`);
|
|
430
|
-
return out;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
function normalizeAgentKind(agentId = "") {
|
|
434
|
-
const text = String(agentId || "").trim().toLowerCase();
|
|
435
|
-
if (text.startsWith("codex:") || text === "codex") return "codex";
|
|
436
|
-
if (text.startsWith("claude:") || text.startsWith("claude-code:") || text === "claude" || text === "claude-code") {
|
|
437
|
-
return "claude";
|
|
438
|
-
}
|
|
439
|
-
return "internal";
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
function buildClaudeStartupLines(agentLabel = "", width = 80) {
|
|
443
|
-
const label = String(agentLabel || "").trim();
|
|
444
|
-
const projectPath = compactProjectPath(getProjectRoot());
|
|
445
|
-
const product = "ClaudeCode";
|
|
446
|
-
const detail = label ? `${label} · managed headless` : "managed headless";
|
|
447
|
-
const iconWidth = 9;
|
|
448
|
-
const iconGap = " ";
|
|
449
|
-
const iconLine = (icon = "", text = "") => {
|
|
450
|
-
const pad = " ".repeat(Math.max(0, iconWidth - displayWidth(icon)));
|
|
451
|
-
return `${CLAUDE_ORANGE}${icon}${ANSI_RESET}${pad}${iconGap}${text}`;
|
|
452
|
-
};
|
|
453
|
-
const lines = [
|
|
454
|
-
iconLine(" ▐▛███▜▌", `${product}v${packageVersion}`),
|
|
455
|
-
iconLine("▝▜█████▛▘", detail),
|
|
456
|
-
iconLine(" ▘▘ ▝▝ ", projectPath),
|
|
457
|
-
"",
|
|
458
|
-
];
|
|
459
|
-
if (width < 44) return lines;
|
|
460
|
-
return lines.map((line) => fitAnsiText(line, Math.min(58, Math.max(1, width))));
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
function buildCodexStartupLines(agentLabel = "", width = 80) {
|
|
464
|
-
const label = String(agentLabel || "").trim();
|
|
465
|
-
const projectPath = compactProjectPath(getProjectRoot());
|
|
466
|
-
if (width < 36) {
|
|
467
|
-
return [
|
|
468
|
-
`>_ OpenAI Codex`,
|
|
469
|
-
label ? `model: ${label}` : "model: managed headless",
|
|
470
|
-
`directory: ${projectPath}`,
|
|
471
|
-
"",
|
|
472
|
-
];
|
|
473
|
-
}
|
|
474
|
-
const innerWidth = Math.min(56, Math.max(24, width - 4));
|
|
475
|
-
return [
|
|
476
|
-
...borderedLines([
|
|
477
|
-
`>_ OpenAI Codex (ufoo v${packageVersion})`,
|
|
478
|
-
"",
|
|
479
|
-
`model: ${label ? `${label} · managed headless` : "managed headless"}`,
|
|
480
|
-
`directory: ${projectPath}`,
|
|
481
|
-
], innerWidth),
|
|
482
|
-
"",
|
|
483
|
-
];
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function buildInternalStartupLines(agentId = "", agentLabel = "", width = 80) {
|
|
487
|
-
const kind = normalizeAgentKind(agentId);
|
|
488
|
-
if (kind === "codex") return buildCodexStartupLines(agentLabel || agentId, width);
|
|
489
|
-
return buildClaudeStartupLines(agentLabel || agentId, width);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function staticStartupLines(agentId = "", agentLabel = "", width = 80) {
|
|
493
|
-
const lines = buildInternalStartupLines(agentId, agentLabel, width);
|
|
494
|
-
if (lines.length > 0 && String(lines[lines.length - 1] || "") === "") {
|
|
495
|
-
return lines.slice(0, -1);
|
|
496
|
-
}
|
|
497
|
-
return lines;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function resetBusView(agentId) {
|
|
501
|
-
busInputValue = "";
|
|
502
|
-
busInputCursor = 0;
|
|
503
|
-
busAgentReplyActive = false;
|
|
504
|
-
busStartupAgentId = agentId || "";
|
|
505
|
-
const label = getAgentLabel(agentId);
|
|
506
|
-
const startupLines = staticStartupLines(agentId, label, getCols());
|
|
507
|
-
let historyLines = [];
|
|
508
|
-
try {
|
|
509
|
-
const loaded = getBusLogHistory(agentId);
|
|
510
|
-
historyLines = Array.isArray(loaded) ? loaded : [];
|
|
511
|
-
} catch {
|
|
512
|
-
historyLines = [];
|
|
513
|
-
}
|
|
514
|
-
busLogLines = startupLines.concat("", historyLines);
|
|
515
|
-
busStartupLineCount = startupLines.length;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function refreshBusStartupLines(width = getCols()) {
|
|
519
|
-
if (!busStartupAgentId || busStartupLineCount <= 0) return;
|
|
520
|
-
const label = getAgentLabel(busStartupAgentId);
|
|
521
|
-
const startupLines = staticStartupLines(busStartupAgentId, label, width);
|
|
522
|
-
const tailLines = busLogLines.slice(busStartupLineCount);
|
|
523
|
-
busLogLines = startupLines.concat(tailLines.length > 0 ? tailLines : [""]);
|
|
524
|
-
busStartupLineCount = startupLines.length;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function trimBusLogLines() {
|
|
528
|
-
if (busLogLines.length <= 1000) return;
|
|
529
|
-
const removed = busLogLines.length - 1000;
|
|
530
|
-
busLogLines = busLogLines.slice(-1000);
|
|
531
|
-
busStartupLineCount = Math.max(0, busStartupLineCount - removed);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
function appendBusLog(text = "") {
|
|
535
|
-
const clean = stripAnsi(String(text || "")).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
536
|
-
if (busLogLines.length === 0) busLogLines.push("");
|
|
537
|
-
for (const char of clean) {
|
|
538
|
-
if (char === "\n") {
|
|
539
|
-
busLogLines.push("");
|
|
540
|
-
} else {
|
|
541
|
-
busLogLines[busLogLines.length - 1] += char;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
trimBusLogLines();
|
|
545
|
-
if (clean.endsWith("\n")) {
|
|
546
|
-
busAgentReplyActive = false;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
function ensureBusLinePrefix(prefix = "") {
|
|
551
|
-
if (busLogLines.length === 0) {
|
|
552
|
-
busLogLines.push(prefix);
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
if (busLogLines[busLogLines.length - 1] === "") {
|
|
556
|
-
busLogLines[busLogLines.length - 1] = prefix;
|
|
557
|
-
return;
|
|
558
|
-
}
|
|
559
|
-
busLogLines.push(prefix);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
function appendBusAgentReply(text = "") {
|
|
563
|
-
const clean = stripAnsi(String(text || "")).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
564
|
-
if (!clean) return;
|
|
565
|
-
for (const char of clean) {
|
|
566
|
-
if (char === "\n") {
|
|
567
|
-
busLogLines.push("");
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
if (!busAgentReplyActive) {
|
|
571
|
-
ensureBusLinePrefix("• ");
|
|
572
|
-
busAgentReplyActive = true;
|
|
573
|
-
} else if (busLogLines.length === 0 || busLogLines[busLogLines.length - 1] === "") {
|
|
574
|
-
ensureBusLinePrefix(" ");
|
|
575
|
-
}
|
|
576
|
-
busLogLines[busLogLines.length - 1] += char;
|
|
577
|
-
}
|
|
578
|
-
trimBusLogLines();
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
function getBusInputViewport(width) {
|
|
582
|
-
const inner = Math.max(1, width - 2);
|
|
583
|
-
const value = String(busInputValue || "").replace(/\n/g, "⏎");
|
|
584
|
-
const beforeCursor = String(busInputValue || "").slice(0, busInputCursor).replace(/\n/g, "⏎");
|
|
585
|
-
const cursorCells = displayWidth(beforeCursor);
|
|
586
|
-
let startCell = 0;
|
|
587
|
-
if (cursorCells >= inner) {
|
|
588
|
-
startCell = cursorCells - inner + 1;
|
|
589
|
-
}
|
|
590
|
-
const text = sliceDisplayCells(value, startCell, inner);
|
|
591
|
-
return {
|
|
592
|
-
text,
|
|
593
|
-
cursorCol: Math.max(0, cursorCells - startCell),
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
function renderBusView() {
|
|
598
|
-
if (currentView !== "agent" || !agentViewUsesBus) return;
|
|
599
|
-
const rows = getRows();
|
|
600
|
-
const cols = getCols();
|
|
601
|
-
const width = Math.max(20, cols);
|
|
602
|
-
refreshBusStartupLines(width);
|
|
603
|
-
const inputTop = Math.max(4, rows - 3);
|
|
604
|
-
const logContentTop = 1;
|
|
605
|
-
const logContentBottom = Math.max(logContentTop, inputTop - 1);
|
|
606
|
-
const logContentHeight = Math.max(1, logContentBottom - logContentTop + 1);
|
|
607
|
-
const status = resolveBusStatus();
|
|
608
|
-
const logRows = Math.max(0, logContentHeight - 1);
|
|
609
|
-
const statusRow = logContentTop + logRows;
|
|
610
|
-
|
|
611
|
-
processStdout.write("\x1b[?25l");
|
|
612
|
-
const visibleLines = getWrappedBusLogLines(width).slice(-logRows);
|
|
613
|
-
for (let i = 0; i < logRows; i += 1) {
|
|
614
|
-
writeAt(logContentTop + i, logLine(visibleLines[i] || "", width));
|
|
615
|
-
}
|
|
616
|
-
writeAt(statusRow, logLine(buildBusStatusLine(width, status), width));
|
|
617
|
-
|
|
618
|
-
writeAt(inputTop, horizontalLine(width));
|
|
619
|
-
const viewport = getBusInputViewport(width);
|
|
620
|
-
writeAt(inputTop + 1, plainLine(`> ${viewport.text}`, width));
|
|
621
|
-
writeAt(inputTop + 2, horizontalLine(width));
|
|
622
|
-
|
|
623
|
-
renderAgentDashboard();
|
|
624
|
-
const cursorCol = clamp(3 + viewport.cursorCol, 1, width);
|
|
625
|
-
processStdout.write(`\x1b[${inputTop + 1};${cursorCol}H\x1b[?25h`);
|
|
626
|
-
syncBusStatusTimer(status);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
function renderAgentDashboard() {
|
|
630
|
-
if (!agentBarVisible && getFocusMode() !== "dashboard") return;
|
|
631
|
-
const rows = getRows();
|
|
632
|
-
const cols = getCols();
|
|
633
|
-
const hintText = getFocusMode() === "dashboard"
|
|
634
|
-
? agentBarHints.dashboard
|
|
635
|
-
: agentBarHints.normal;
|
|
636
|
-
const computed = computeAgentBar({
|
|
637
|
-
cols,
|
|
638
|
-
hintText,
|
|
639
|
-
focusMode: getFocusMode(),
|
|
640
|
-
selectedAgentIndex: getSelectedAgentIndex(),
|
|
641
|
-
activeAgents: getActiveAgents(),
|
|
642
|
-
viewingAgent,
|
|
643
|
-
agentListWindowStart: getAgentListWindowStart(),
|
|
644
|
-
maxAgentWindow,
|
|
645
|
-
getAgentLabel,
|
|
646
|
-
agentStates: getAgentStates(),
|
|
647
|
-
});
|
|
648
|
-
setAgentListWindowStart(computed.windowStart);
|
|
649
|
-
processStdout.write(`\x1b7\x1b[${rows};1H${computed.bar}\x1b8`);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
function setAgentBarVisible(visible) {
|
|
653
|
-
const next = Boolean(visible);
|
|
654
|
-
if (agentBarVisible === next) return;
|
|
655
|
-
agentBarVisible = next;
|
|
656
|
-
const rows = getRows();
|
|
657
|
-
if (agentBarVisible) {
|
|
658
|
-
processStdout.write(`\x1b[1;${rows - 1}r`);
|
|
659
|
-
renderAgentDashboard();
|
|
660
|
-
} else {
|
|
661
|
-
processStdout.write(`\x1b[1;${rows}r`);
|
|
662
|
-
processStdout.write(`\x1b7\x1b[${rows};1H\x1b[2K\x1b8`);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
function enterAgentView(agentId, options = {}) {
|
|
667
|
-
if (currentView === "agent" && viewingAgent === agentId) return;
|
|
668
|
-
const wasInAgentView = currentView === "agent";
|
|
669
|
-
if (currentView === "agent") {
|
|
670
|
-
if (agentViewUsesBus && viewingAgent) sendBusWatch(viewingAgent, false);
|
|
671
|
-
disconnectAgentOutput();
|
|
672
|
-
disconnectAgentInput();
|
|
673
|
-
stopBusStatusTimer();
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
currentView = "agent";
|
|
677
|
-
viewingAgent = agentId;
|
|
678
|
-
setFocusMode("input");
|
|
679
|
-
|
|
680
|
-
if (!wasInAgentView) {
|
|
681
|
-
detachedChildren = [...screen.children];
|
|
682
|
-
for (const child of detachedChildren) screen.remove(child);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
renderFrozen = true;
|
|
686
|
-
|
|
687
|
-
const rows = getRows();
|
|
688
|
-
const cols = getCols();
|
|
689
|
-
processStdout.write("\x1b[2J\x1b[H");
|
|
690
|
-
processStdout.write(`\x1b[1;${rows - 1}r`);
|
|
691
|
-
processStdout.write("\x1b[H");
|
|
692
|
-
processStdout.write("\x1b[?25h");
|
|
693
|
-
setAgentBarVisible(true);
|
|
694
|
-
|
|
695
|
-
agentInputSuppressUntil = now() + 300;
|
|
696
|
-
agentViewUsesBus = Boolean(options.useBus);
|
|
697
|
-
if (agentViewUsesBus) {
|
|
698
|
-
sendBusWatch(agentId, true);
|
|
699
|
-
resetBusView(agentId);
|
|
700
|
-
renderBusView();
|
|
701
|
-
} else {
|
|
702
|
-
const sockPath = getInjectSockPath(agentId);
|
|
703
|
-
connectAgentOutput(sockPath);
|
|
704
|
-
connectAgentInput(sockPath);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
setTimeoutFn(() => {
|
|
708
|
-
sendResize(cols, Math.max(1, rows - 1));
|
|
709
|
-
requestScreenSnapshot();
|
|
710
|
-
}, 120);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
function exitAgentView() {
|
|
714
|
-
if (currentView !== "agent") return;
|
|
715
|
-
|
|
716
|
-
const rows = getRows();
|
|
717
|
-
const cols = getCols();
|
|
718
|
-
sendResize(cols, rows);
|
|
719
|
-
|
|
720
|
-
disconnectAgentOutput();
|
|
721
|
-
disconnectAgentInput();
|
|
722
|
-
if (agentViewUsesBus && viewingAgent) sendBusWatch(viewingAgent, false);
|
|
723
|
-
agentViewUsesBus = false;
|
|
724
|
-
agentOutputSuppressed = false;
|
|
725
|
-
agentBarVisible = false;
|
|
726
|
-
stopBusStatusTimer();
|
|
727
|
-
busInputValue = "";
|
|
728
|
-
busInputCursor = 0;
|
|
729
|
-
busLogLines = [];
|
|
730
|
-
busStartupAgentId = "";
|
|
731
|
-
busStartupLineCount = 0;
|
|
732
|
-
busAgentReplyActive = false;
|
|
733
|
-
|
|
734
|
-
currentView = "main";
|
|
735
|
-
viewingAgent = null;
|
|
736
|
-
|
|
737
|
-
processStdout.write(`\x1b[1;${rows}r`);
|
|
738
|
-
processStdout.write("\x1b[?25h");
|
|
739
|
-
|
|
740
|
-
if (detachedChildren) {
|
|
741
|
-
for (const child of detachedChildren) screen.append(child);
|
|
742
|
-
detachedChildren = null;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
renderFrozen = false;
|
|
746
|
-
setFocusMode("input");
|
|
747
|
-
setDashboardView("agents");
|
|
748
|
-
setSelectedAgentIndex(-1);
|
|
749
|
-
setScreenGrabKeys(false);
|
|
750
|
-
clearTargetAgent();
|
|
751
|
-
renderDashboard();
|
|
752
|
-
focusInput();
|
|
753
|
-
resizeInput();
|
|
754
|
-
forceScreenRepaint();
|
|
755
|
-
try {
|
|
756
|
-
if (screen.program && typeof screen.program.showCursor === "function") {
|
|
757
|
-
screen.program.showCursor();
|
|
758
|
-
}
|
|
759
|
-
} catch {
|
|
760
|
-
// Ignore cursor restore errors.
|
|
761
|
-
}
|
|
762
|
-
if (input && typeof input._updateCursor === "function") {
|
|
763
|
-
input._updateCursor();
|
|
764
|
-
}
|
|
765
|
-
renderScreen();
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
function enterAgentDashboardMode() {
|
|
769
|
-
setFocusMode("dashboard");
|
|
770
|
-
setDashboardView("agents");
|
|
771
|
-
setSelectedAgentIndex(0);
|
|
772
|
-
setAgentBarVisible(true);
|
|
773
|
-
renderAgentDashboard();
|
|
774
|
-
agentOutputSuppressed = true;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
function insertBusInput(text = "") {
|
|
778
|
-
const value = String(text || "");
|
|
779
|
-
if (!value) return;
|
|
780
|
-
busInputValue = busInputValue.slice(0, busInputCursor) + value + busInputValue.slice(busInputCursor);
|
|
781
|
-
busInputCursor += value.length;
|
|
782
|
-
renderBusView();
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
function inputBoundaries(text = "") {
|
|
786
|
-
const source = String(text || "");
|
|
787
|
-
if (!source) return [0];
|
|
788
|
-
try {
|
|
789
|
-
if (typeof Intl !== "undefined" && typeof Intl.Segmenter === "function") {
|
|
790
|
-
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
791
|
-
const boundaries = [0];
|
|
792
|
-
for (const part of segmenter.segment(source)) {
|
|
793
|
-
boundaries.push(part.index + part.segment.length);
|
|
794
|
-
}
|
|
795
|
-
return Array.from(new Set(boundaries)).sort((a, b) => a - b);
|
|
796
|
-
}
|
|
797
|
-
} catch {
|
|
798
|
-
// Fall through to code point boundaries.
|
|
799
|
-
}
|
|
800
|
-
const boundaries = [0];
|
|
801
|
-
let offset = 0;
|
|
802
|
-
for (const char of Array.from(source)) {
|
|
803
|
-
offset += char.length;
|
|
804
|
-
boundaries.push(offset);
|
|
805
|
-
}
|
|
806
|
-
return boundaries;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
function clampInputCursor(pos = busInputCursor) {
|
|
810
|
-
const boundaries = inputBoundaries(busInputValue);
|
|
811
|
-
const target = clamp(pos, 0, busInputValue.length);
|
|
812
|
-
let best = 0;
|
|
813
|
-
for (const boundary of boundaries) {
|
|
814
|
-
if (boundary <= target) best = boundary;
|
|
815
|
-
else break;
|
|
816
|
-
}
|
|
817
|
-
return best;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
function previousInputBoundary(pos = busInputCursor) {
|
|
821
|
-
const boundaries = inputBoundaries(busInputValue);
|
|
822
|
-
const target = clamp(pos, 0, busInputValue.length);
|
|
823
|
-
let prev = 0;
|
|
824
|
-
for (const boundary of boundaries) {
|
|
825
|
-
if (boundary < target) prev = boundary;
|
|
826
|
-
else break;
|
|
827
|
-
}
|
|
828
|
-
return prev;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
function nextInputBoundary(pos = busInputCursor) {
|
|
832
|
-
const boundaries = inputBoundaries(busInputValue);
|
|
833
|
-
const target = clamp(pos, 0, busInputValue.length);
|
|
834
|
-
for (const boundary of boundaries) {
|
|
835
|
-
if (boundary > target) return boundary;
|
|
836
|
-
}
|
|
837
|
-
return busInputValue.length;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
function deleteBusInputBeforeCursor() {
|
|
841
|
-
if (busInputCursor <= 0) return;
|
|
842
|
-
const previous = previousInputBoundary();
|
|
843
|
-
busInputValue = busInputValue.slice(0, previous) + busInputValue.slice(busInputCursor);
|
|
844
|
-
busInputCursor = previous;
|
|
845
|
-
renderBusView();
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
function clearBusInput() {
|
|
849
|
-
busInputValue = "";
|
|
850
|
-
busInputCursor = 0;
|
|
851
|
-
renderBusView();
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
function submitBusInput() {
|
|
855
|
-
const text = String(busInputValue || "").trim();
|
|
856
|
-
if (!text) {
|
|
857
|
-
renderBusView();
|
|
858
|
-
return;
|
|
859
|
-
}
|
|
860
|
-
appendBusLog(`> ${text}\n`);
|
|
861
|
-
busAgentReplyActive = false;
|
|
862
|
-
busInputValue = "";
|
|
863
|
-
busInputCursor = 0;
|
|
864
|
-
sendBusMessage(viewingAgent, text);
|
|
865
|
-
renderBusView();
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
function handleBusAgentKey(ch, key = {}) {
|
|
869
|
-
if (currentView !== "agent" || !agentViewUsesBus) return false;
|
|
870
|
-
const keyName = key && key.name;
|
|
871
|
-
|
|
872
|
-
if (keyName === "down") return false;
|
|
873
|
-
|
|
874
|
-
if (keyName === "escape") {
|
|
875
|
-
exitAgentView();
|
|
876
|
-
return true;
|
|
877
|
-
}
|
|
878
|
-
if (keyName === "return" || keyName === "enter") {
|
|
879
|
-
if (key && (key.shift || key.meta)) {
|
|
880
|
-
insertBusInput("\n");
|
|
881
|
-
} else {
|
|
882
|
-
submitBusInput();
|
|
883
|
-
}
|
|
884
|
-
return true;
|
|
885
|
-
}
|
|
886
|
-
if (key && key.ctrl && keyName === "u") {
|
|
887
|
-
clearBusInput();
|
|
888
|
-
return true;
|
|
889
|
-
}
|
|
890
|
-
if (key && key.ctrl && keyName === "a") {
|
|
891
|
-
busInputCursor = 0;
|
|
892
|
-
renderBusView();
|
|
893
|
-
return true;
|
|
894
|
-
}
|
|
895
|
-
if (key && key.ctrl && keyName === "e") {
|
|
896
|
-
busInputCursor = busInputValue.length;
|
|
897
|
-
renderBusView();
|
|
898
|
-
return true;
|
|
899
|
-
}
|
|
900
|
-
if (keyName === "left") {
|
|
901
|
-
busInputCursor = previousInputBoundary();
|
|
902
|
-
renderBusView();
|
|
903
|
-
return true;
|
|
904
|
-
}
|
|
905
|
-
if (keyName === "right") {
|
|
906
|
-
busInputCursor = nextInputBoundary();
|
|
907
|
-
renderBusView();
|
|
908
|
-
return true;
|
|
909
|
-
}
|
|
910
|
-
if (keyName === "home") {
|
|
911
|
-
busInputCursor = 0;
|
|
912
|
-
renderBusView();
|
|
913
|
-
return true;
|
|
914
|
-
}
|
|
915
|
-
if (keyName === "end") {
|
|
916
|
-
busInputCursor = busInputValue.length;
|
|
917
|
-
renderBusView();
|
|
918
|
-
return true;
|
|
919
|
-
}
|
|
920
|
-
if (keyName === "backspace") {
|
|
921
|
-
deleteBusInputBeforeCursor();
|
|
922
|
-
return true;
|
|
923
|
-
}
|
|
924
|
-
if (keyName === "delete") {
|
|
925
|
-
if (busInputCursor < busInputValue.length) {
|
|
926
|
-
const next = nextInputBoundary();
|
|
927
|
-
busInputValue = busInputValue.slice(0, busInputCursor) + busInputValue.slice(next);
|
|
928
|
-
busInputCursor = clampInputCursor();
|
|
929
|
-
renderBusView();
|
|
930
|
-
}
|
|
931
|
-
return true;
|
|
932
|
-
}
|
|
933
|
-
if (ch && ch.length > 1 && (!keyName || keyName.length !== 1)) {
|
|
934
|
-
insertBusInput(ch.replace(/\r\n/g, "\n").replace(/\r/g, "\n"));
|
|
935
|
-
return true;
|
|
936
|
-
}
|
|
937
|
-
const insertChar = (ch && ch.length === 1)
|
|
938
|
-
? ch
|
|
939
|
-
: (keyName && keyName.length === 1 ? keyName : "");
|
|
940
|
-
if (insertChar && !/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(insertChar)) {
|
|
941
|
-
insertBusInput(insertChar);
|
|
942
|
-
return true;
|
|
943
|
-
}
|
|
944
|
-
return true;
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
function sendRawToAgent(data) {
|
|
948
|
-
sendRaw(data);
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
function sendResizeToAgent(cols, rows) {
|
|
952
|
-
sendResize(cols, rows);
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
function requestAgentSnapshot() {
|
|
956
|
-
requestScreenSnapshot();
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
function writeToAgentTerm(text) {
|
|
960
|
-
if (!text) return;
|
|
961
|
-
if (currentView !== "agent") return;
|
|
962
|
-
if (agentOutputSuppressed) return;
|
|
963
|
-
|
|
964
|
-
const cleaned = text
|
|
965
|
-
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
|
|
966
|
-
.replace(/\x1b\[(?:[?>=]?[0-9]*c|[?]?6n|5n)/g, "");
|
|
967
|
-
if (agentViewUsesBus) {
|
|
968
|
-
appendBusAgentReply(cleaned);
|
|
969
|
-
renderBusView();
|
|
970
|
-
return;
|
|
971
|
-
}
|
|
972
|
-
if (cleaned) processStdout.write(cleaned);
|
|
973
|
-
if (agentBarVisible) {
|
|
974
|
-
const rows = getRows();
|
|
975
|
-
processStdout.write("\x1b7");
|
|
976
|
-
processStdout.write(`\x1b[1;${rows - 1}r`);
|
|
977
|
-
processStdout.write("\x1b8");
|
|
978
|
-
renderAgentDashboard();
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
function placeAgentCursor(cursor) {
|
|
983
|
-
if (!cursor || currentView !== "agent") return;
|
|
984
|
-
const rows = getRows();
|
|
985
|
-
const cols = getCols();
|
|
986
|
-
const row = Math.max(1, Math.min(rows - 1, (cursor.y || 0) + 1));
|
|
987
|
-
const col = Math.max(1, Math.min(cols, (cursor.x || 0) + 1));
|
|
988
|
-
processStdout.write(`\x1b[${row};${col}H\x1b[?25h`);
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
function handleResizeInAgentView() {
|
|
992
|
-
if (currentView !== "agent") return false;
|
|
993
|
-
const rows = getRows();
|
|
994
|
-
const cols = getCols();
|
|
995
|
-
processStdout.write(`\x1b[1;${rows - 1}r`);
|
|
996
|
-
sendResize(cols, Math.max(1, rows - 1));
|
|
997
|
-
if (agentViewUsesBus) {
|
|
998
|
-
renderBusView();
|
|
999
|
-
} else {
|
|
1000
|
-
renderAgentDashboard();
|
|
1001
|
-
}
|
|
1002
|
-
return true;
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
function getCurrentView() {
|
|
1006
|
-
return currentView;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
function getViewingAgent() {
|
|
1010
|
-
return viewingAgent || "";
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
function isAgentViewUsesBus() {
|
|
1014
|
-
return agentViewUsesBus;
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
function getAgentInputSuppressUntil() {
|
|
1018
|
-
return agentInputSuppressUntil;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
function getAgentOutputSuppressed() {
|
|
1022
|
-
return agentOutputSuppressed;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
function setAgentOutputSuppressed(value) {
|
|
1026
|
-
agentOutputSuppressed = Boolean(value);
|
|
1027
|
-
if (!agentOutputSuppressed && agentViewUsesBus) {
|
|
1028
|
-
renderBusView();
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
function refreshAgentView() {
|
|
1033
|
-
if (currentView !== "agent") return false;
|
|
1034
|
-
if (agentViewUsesBus) {
|
|
1035
|
-
renderBusView();
|
|
1036
|
-
} else {
|
|
1037
|
-
renderAgentDashboard();
|
|
1038
|
-
}
|
|
1039
|
-
return true;
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
function isAgentBarVisible() {
|
|
1043
|
-
return agentBarVisible;
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
return {
|
|
1047
|
-
getCurrentView,
|
|
1048
|
-
getViewingAgent,
|
|
1049
|
-
isAgentViewUsesBus,
|
|
1050
|
-
getAgentInputSuppressUntil,
|
|
1051
|
-
getAgentOutputSuppressed,
|
|
1052
|
-
setAgentOutputSuppressed,
|
|
1053
|
-
refreshAgentView,
|
|
1054
|
-
isAgentBarVisible,
|
|
1055
|
-
renderAgentDashboard,
|
|
1056
|
-
setAgentBarVisible,
|
|
1057
|
-
enterAgentView,
|
|
1058
|
-
exitAgentView,
|
|
1059
|
-
enterAgentDashboardMode,
|
|
1060
|
-
sendRawToAgent,
|
|
1061
|
-
sendResizeToAgent,
|
|
1062
|
-
requestAgentSnapshot,
|
|
1063
|
-
writeToAgentTerm,
|
|
1064
|
-
placeAgentCursor,
|
|
1065
|
-
handleResizeInAgentView,
|
|
1066
|
-
handleBusAgentKey,
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
module.exports = {
|
|
1071
|
-
createAgentViewController,
|
|
1072
|
-
};
|