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
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Multiline text input for the ink-based ucode TUI.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* legacy and ink editors stay in sync (and so the existing jest coverage of
|
|
9
|
-
* those helpers protects this component too).
|
|
6
|
+
* Built on ink's useInput. Cursor math is delegated to src/ui/format so
|
|
7
|
+
* jest can cover the editor behaviour without mounting ink.
|
|
10
8
|
*
|
|
11
9
|
* Props:
|
|
12
10
|
* value (string) text contents (controlled)
|
|
@@ -39,9 +37,108 @@
|
|
|
39
37
|
|
|
40
38
|
const fmt = require("../format");
|
|
41
39
|
|
|
40
|
+
// IME cursor parking interacts with ink's frame rendering. Two facts about
|
|
41
|
+
// ink make the naive "move cursor up in useEffect" approach insufficient:
|
|
42
|
+
//
|
|
43
|
+
// 1. ink throttles onRender to 32ms (ink.js:39), so frame writes happen
|
|
44
|
+
// AFTER our useEffect, not before — anything we wrote in useEffect ends
|
|
45
|
+
// up getting overwritten by ink's parking cursor at the bottom of the
|
|
46
|
+
// next frame, which is what the user sees as "光标被结尾抢走".
|
|
47
|
+
//
|
|
48
|
+
// 2. ink's log-update emits ansi-escapes.eraseLines(N) before each frame:
|
|
49
|
+
// a sequence of `eraseLine + cursorUp` pairs starting from "wherever
|
|
50
|
+
// the cursor currently is". If our IME hack left the cursor mid-frame,
|
|
51
|
+
// ink's relative cursorUp walks past the top of the frame and tramples
|
|
52
|
+
// lines above it.
|
|
53
|
+
//
|
|
54
|
+
// Fix: wrap stdout.write once. Before any frame-shaped write (starts with
|
|
55
|
+
// ESC[2K from eraseLines, or ESC[2J from full-screen rerender), push the
|
|
56
|
+
// cursor back DOWN to the parking row so ink's math is restored. AFTER the
|
|
57
|
+
// frame write, if the IME park target is active, re-emit the cursor-up +
|
|
58
|
+
// CHA so the hardware cursor follows the inverse caret again. This way the
|
|
59
|
+
// caret stays parked at the IME-visible row even when ink's throttled write
|
|
60
|
+
// fires long after React commit.
|
|
61
|
+
const __imeStdoutState = new WeakSet();
|
|
62
|
+
const __imeCursor = {
|
|
63
|
+
active: false,
|
|
64
|
+
// Where to park the cursor: rowsUp above ink's "row after last frame line"
|
|
65
|
+
// anchor, and 0-based terminal column.
|
|
66
|
+
parkRowsUp: 0,
|
|
67
|
+
parkCol: 0,
|
|
68
|
+
// How many rows up we last actually moved the cursor — used to undo the
|
|
69
|
+
// move before ink runs its relative eraseLines. ALWAYS matches the move
|
|
70
|
+
// we last wrote, regardless of whether that was through the patched
|
|
71
|
+
// stdout write or the useEffect path.
|
|
72
|
+
movedUpRows: 0,
|
|
73
|
+
// Tracks whether the LAST frame ink wrote ended with '\n' (the log-update
|
|
74
|
+
// path) or not (the full-screen path). The anchor row is one row higher
|
|
75
|
+
// when there's no trailing newline, which shifts subsequent restore-down
|
|
76
|
+
// math by one. Without this flag, useEffect after a full-screen frame
|
|
77
|
+
// restores down too far and overshoots upward → a "ghost caret" sits one
|
|
78
|
+
// or more rows above the real caret.
|
|
79
|
+
lastFrameHadNewline: true,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
function isFrameWrite(chunk) {
|
|
83
|
+
if (typeof chunk !== "string" && !(chunk instanceof String)) return false;
|
|
84
|
+
const str = String(chunk);
|
|
85
|
+
// eraseLines(N>0) starts with ESC[2K; full-screen clear starts with ESC[2J.
|
|
86
|
+
return str.startsWith("\x1b[2K") || str.startsWith("\x1b[2J");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Compute "rows up from the current anchor to the caret". The anchor sits
|
|
90
|
+
// one row below the last frame line when the frame ended with '\n', and AT
|
|
91
|
+
// the last frame line otherwise — so a frame with no trailing newline needs
|
|
92
|
+
// one fewer row up to land on the caret.
|
|
93
|
+
function rowsUpFromAnchor() {
|
|
94
|
+
const base = __imeCursor.parkRowsUp;
|
|
95
|
+
return __imeCursor.lastFrameHadNewline ? base : Math.max(0, base - 1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function applyParkSequence(parkRowsUp) {
|
|
99
|
+
if (!__imeCursor.active) return "";
|
|
100
|
+
const up = parkRowsUp > 0 ? `\x1b[${parkRowsUp}A` : "";
|
|
101
|
+
const col = `\x1b[${__imeCursor.parkCol + 1}G`; // CHA is 1-based
|
|
102
|
+
__imeCursor.movedUpRows = parkRowsUp;
|
|
103
|
+
return `\x1b[?25h${up}${col}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function patchStdoutForIME(out) {
|
|
107
|
+
if (!out || typeof out.write !== "function" || __imeStdoutState.has(out)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
__imeStdoutState.add(out);
|
|
111
|
+
const originalWrite = out.write.bind(out);
|
|
112
|
+
out.write = function patchedWrite(chunk, encoding, callback) {
|
|
113
|
+
if (!isFrameWrite(chunk) || (typeof chunk !== "string" && !(chunk instanceof String))) {
|
|
114
|
+
return originalWrite(chunk, encoding, callback);
|
|
115
|
+
}
|
|
116
|
+
// Combine "hide cursor + restore-to-anchor + ink's frame + reposition +
|
|
117
|
+
// show cursor" into a SINGLE write so the terminal processes the whole
|
|
118
|
+
// transition atomically. With ink's eraseLines walking the cursor up
|
|
119
|
+
// through the frame mid-write, even one stray byte between escape
|
|
120
|
+
// sequences can leave the hardware cursor visible on an intermediate
|
|
121
|
+
// row for a frame — exactly the "faint cursor above the real one"
|
|
122
|
+
// ghost the user reports.
|
|
123
|
+
const str = String(chunk);
|
|
124
|
+
let prefix = "\x1b[?25l"; // hide cursor for the whole transition
|
|
125
|
+
if (__imeCursor.movedUpRows > 0) {
|
|
126
|
+
// Push the cursor back down to ink's "after last frame line" anchor
|
|
127
|
+
// so the relative cursorUp inside eraseLines walks the right rows.
|
|
128
|
+
prefix += `\x1b[${__imeCursor.movedUpRows}B`;
|
|
129
|
+
__imeCursor.movedUpRows = 0;
|
|
130
|
+
}
|
|
131
|
+
// Record which ink path this frame took so subsequent restores-down know
|
|
132
|
+
// where the anchor actually sits.
|
|
133
|
+
__imeCursor.lastFrameHadNewline = str.endsWith("\n");
|
|
134
|
+
const suffix = applyParkSequence(rowsUpFromAnchor()) || "\x1b[?25l";
|
|
135
|
+
return originalWrite(prefix + str + suffix, encoding, callback);
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
42
139
|
function createMultilineInput({ React, ink }) {
|
|
43
140
|
const { useState, useCallback, useMemo, useEffect } = React;
|
|
44
|
-
const { Box, Text, useInput } = ink;
|
|
141
|
+
const { Box, Text, useInput, useStdout } = ink;
|
|
45
142
|
const h = React.createElement;
|
|
46
143
|
|
|
47
144
|
return function MultilineInput({
|
|
@@ -61,6 +158,12 @@ function createMultilineInput({ React, ink }) {
|
|
|
61
158
|
promptPrefix = "› ",
|
|
62
159
|
promptColor = "magenta",
|
|
63
160
|
borderColor = "gray",
|
|
161
|
+
// How many terminal rows of UI sit *below* the bottom of this input box
|
|
162
|
+
// (status line, dashboard rows, etc.). The component uses this to compute
|
|
163
|
+
// how far up the hardware cursor needs to be moved after each render so
|
|
164
|
+
// the IME composition window pops up at the visible (inverse) cursor
|
|
165
|
+
// instead of at the bottom of the screen.
|
|
166
|
+
linesBelowInput = 0,
|
|
64
167
|
}) {
|
|
65
168
|
// Cursor is owned by this component. preferredCol tracks the visual
|
|
66
169
|
// column we want to keep when bouncing across lines of different widths
|
|
@@ -300,6 +403,128 @@ function createMultilineInput({ React, ink }) {
|
|
|
300
403
|
[value, wrapWidth, cursorPos]
|
|
301
404
|
);
|
|
302
405
|
|
|
406
|
+
// Hardware-cursor parking for IME support. ink hides the terminal cursor
|
|
407
|
+
// by default and parks it after the last frame line; macOS/Linux IMEs
|
|
408
|
+
// (Pinyin, kkc, etc.) anchor the candidate window to the *hardware*
|
|
409
|
+
// cursor, so without this hack Chinese input pops up at the bottom-right
|
|
410
|
+
// instead of next to the inverse-block caret. We compute the row offset
|
|
411
|
+
// of the inverse caret from the bottom of ink's rendered frame and emit
|
|
412
|
+
// ANSI cursor-position escapes after every render.
|
|
413
|
+
//
|
|
414
|
+
// ink frame layout (top→bottom)
|
|
415
|
+
// ... chat log ...
|
|
416
|
+
// ┌── input border top ──┐ <- visualRows[0]
|
|
417
|
+
// │ › row 0 │
|
|
418
|
+
// │ row 1 │
|
|
419
|
+
// └── input border bot ──┘
|
|
420
|
+
// status line <- linesBelowInput rows
|
|
421
|
+
// dashboard row(s)
|
|
422
|
+
// <ink parks cursor here>
|
|
423
|
+
const { stdout } = useStdout() || {};
|
|
424
|
+
// Find the visual (row, col) of the cursor inside the wrapped layout.
|
|
425
|
+
let cursorVisualRow = 0;
|
|
426
|
+
let cursorVisualCol = 0;
|
|
427
|
+
{
|
|
428
|
+
let placed = false;
|
|
429
|
+
for (let r = 0; r < visualRows.length && !placed; r += 1) {
|
|
430
|
+
const row = visualRows[r];
|
|
431
|
+
let col = 0;
|
|
432
|
+
for (const seg of row.segments) {
|
|
433
|
+
if (seg.cursor) {
|
|
434
|
+
cursorVisualRow = r;
|
|
435
|
+
cursorVisualCol = col;
|
|
436
|
+
placed = true;
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
col += fmt.displayCellWidth(seg.text);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (!placed) {
|
|
443
|
+
// Cursor at end-of-input on a fresh row.
|
|
444
|
+
cursorVisualRow = Math.max(0, visualRows.length - 1);
|
|
445
|
+
cursorVisualCol = 0;
|
|
446
|
+
const lastRow = visualRows[cursorVisualRow];
|
|
447
|
+
if (lastRow) {
|
|
448
|
+
for (const seg of lastRow.segments) {
|
|
449
|
+
if (seg.cursor) break;
|
|
450
|
+
cursorVisualCol += fmt.displayCellWidth(seg.text);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const promptCols = cursorVisualRow === 0
|
|
456
|
+
? fmt.displayCellWidth(promptPrefix)
|
|
457
|
+
: 2; // " " indent on continuation rows
|
|
458
|
+
const cursorTermCol = promptCols + cursorVisualCol; // 0-based column
|
|
459
|
+
|
|
460
|
+
// Distance from the cursor's row to the parking row that ink will leave
|
|
461
|
+
// behind: bottom border (1) + linesBelowInput + the trailing newline ink
|
|
462
|
+
// appends to its frame string (1, see ink/log-update.js).
|
|
463
|
+
const rowsBelowCursor = (visualRows.length - 1 - cursorVisualRow)
|
|
464
|
+
+ 1 // bottom border row of the input box
|
|
465
|
+
+ Math.max(0, Math.floor(Number(linesBelowInput) || 0))
|
|
466
|
+
+ 1; // ink appends "\n" after the frame, so the cursor sits one extra
|
|
467
|
+
// line below the last printed row
|
|
468
|
+
|
|
469
|
+
useEffect(() => {
|
|
470
|
+
const out = stdout || process.stdout;
|
|
471
|
+
if (!out || typeof out.write !== "function" || !out.isTTY) {
|
|
472
|
+
__imeCursor.active = false;
|
|
473
|
+
return undefined;
|
|
474
|
+
}
|
|
475
|
+
if (!interactive) {
|
|
476
|
+
// Hand the cursor back to ink and stop chasing the caret.
|
|
477
|
+
if (__imeCursor.movedUpRows > 0) {
|
|
478
|
+
out.write(`\x1b[${__imeCursor.movedUpRows}B`);
|
|
479
|
+
__imeCursor.movedUpRows = 0;
|
|
480
|
+
}
|
|
481
|
+
__imeCursor.active = false;
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
patchStdoutForIME(out);
|
|
485
|
+
// Publish the desired park target so the stdout monkey-patch can
|
|
486
|
+
// re-park after every throttled ink frame write.
|
|
487
|
+
__imeCursor.active = true;
|
|
488
|
+
__imeCursor.parkRowsUp = rowsBelowCursor;
|
|
489
|
+
__imeCursor.parkCol = cursorTermCol;
|
|
490
|
+
// Park immediately — covers cases where ink has nothing to render
|
|
491
|
+
// (output unchanged) and won't fire a frame write at all, and keeps
|
|
492
|
+
// the caret visible between frames. Combine hide + restore + park +
|
|
493
|
+
// show into a single write so the terminal never sees the cursor at
|
|
494
|
+
// an intermediate row.
|
|
495
|
+
//
|
|
496
|
+
// CRITICAL: the move-up amount must match the anchor that movedUpRows
|
|
497
|
+
// was measured against. If the last frame ended without '\n' (the
|
|
498
|
+
// full-screen path), the anchor is one row higher than the log-update
|
|
499
|
+
// case, so we use rowsUpFromAnchor() rather than parkRowsUp directly.
|
|
500
|
+
// Otherwise restoring down by movedUpRows then moving up parkRowsUp
|
|
501
|
+
// overshoots by one and leaves the hardware cursor one row above the
|
|
502
|
+
// inverse caret — the residual "ghost cursor" symptom.
|
|
503
|
+
let combined = "\x1b[?25l";
|
|
504
|
+
if (__imeCursor.movedUpRows > 0) {
|
|
505
|
+
combined += `\x1b[${__imeCursor.movedUpRows}B`;
|
|
506
|
+
__imeCursor.movedUpRows = 0;
|
|
507
|
+
}
|
|
508
|
+
combined += applyParkSequence(rowsUpFromAnchor());
|
|
509
|
+
out.write(combined);
|
|
510
|
+
return undefined;
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// On unmount, return the cursor to ink's expected parking row (so the
|
|
514
|
+
// next frame ink renders after us doesn't trample lines above its frame)
|
|
515
|
+
// and re-hide it so the rest of ink's lifetime behaves as before.
|
|
516
|
+
useEffect(() => () => {
|
|
517
|
+
const out = stdout || process.stdout;
|
|
518
|
+
__imeCursor.active = false;
|
|
519
|
+
if (out && typeof out.write === "function" && out.isTTY) {
|
|
520
|
+
const restore = __imeCursor.movedUpRows > 0
|
|
521
|
+
? `\x1b[${__imeCursor.movedUpRows}B`
|
|
522
|
+
: "";
|
|
523
|
+
__imeCursor.movedUpRows = 0;
|
|
524
|
+
out.write(`${restore}\x1b[?25l`);
|
|
525
|
+
}
|
|
526
|
+
}, [stdout]);
|
|
527
|
+
|
|
303
528
|
return h(Box, {
|
|
304
529
|
borderStyle: "single",
|
|
305
530
|
borderTop: true,
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Ink-based ucode TUI
|
|
5
|
-
* src/code/tui.js but rendered via React + ink.
|
|
4
|
+
* Ink-based ucode TUI rendered via React + ink.
|
|
6
5
|
*
|
|
7
|
-
* Activation:
|
|
8
|
-
* the legacy blessed renderer while it remains available as a fallback.
|
|
6
|
+
* Activation: this is the only ucode TUI.
|
|
9
7
|
*
|
|
10
8
|
* Coverage today: banner, scrolling log via <Static>, tool-call merge with
|
|
11
9
|
* Ctrl+O expand, multiline editor (see MultilineInput.js), spinner+phase
|
|
@@ -22,7 +20,7 @@ const { createMultilineInput } = require("./MultilineInput");
|
|
|
22
20
|
|
|
23
21
|
function createUcodeApp({ React, ink, props, interactive = true }) {
|
|
24
22
|
const { useEffect, useState, useCallback, useRef } = React;
|
|
25
|
-
const { Box, Text,
|
|
23
|
+
const { Box, Text, useInput, useApp, useStdout } = ink;
|
|
26
24
|
const h = React.createElement;
|
|
27
25
|
const MultilineInput = createMultilineInput({ React, ink });
|
|
28
26
|
|
|
@@ -38,6 +36,7 @@ function createUcodeApp({ React, ink, props, interactive = true }) {
|
|
|
38
36
|
banner.concat([""]).map((line, idx) => ({ id: `b-${idx}`, text: line }))
|
|
39
37
|
);
|
|
40
38
|
const [draft, setDraft] = useState("");
|
|
39
|
+
const [draftVersion, setDraftVersion] = useState(0);
|
|
41
40
|
// status: idle when message === "". `type` picks a STATUS_INDICATORS
|
|
42
41
|
// bucket; `showTimer` and `startedAt` reproduce the blessed spinner
|
|
43
42
|
// controls. The BG suffix is computed from backgroundTasksRef and
|
|
@@ -181,6 +180,7 @@ function createUcodeApp({ React, ink, props, interactive = true }) {
|
|
|
181
180
|
if (transition.moved) {
|
|
182
181
|
setHistoryIndex(transition.nextHistoryIndex);
|
|
183
182
|
setDraft(transition.nextValue);
|
|
183
|
+
setDraftVersion((v) => v + 1);
|
|
184
184
|
return;
|
|
185
185
|
}
|
|
186
186
|
}
|
|
@@ -204,6 +204,7 @@ function createUcodeApp({ React, ink, props, interactive = true }) {
|
|
|
204
204
|
if (nextIndex !== historyIndex || draft !== inputHistory[nextIndex]) {
|
|
205
205
|
setHistoryIndex(nextIndex);
|
|
206
206
|
setDraft(inputHistory[nextIndex] || "");
|
|
207
|
+
setDraftVersion((v) => v + 1);
|
|
207
208
|
return;
|
|
208
209
|
}
|
|
209
210
|
}
|
|
@@ -628,6 +629,7 @@ function createUcodeApp({ React, ink, props, interactive = true }) {
|
|
|
628
629
|
const trimmed = value.trim();
|
|
629
630
|
if (!trimmed) return;
|
|
630
631
|
setDraft("");
|
|
632
|
+
setDraftVersion((v) => v + 1);
|
|
631
633
|
setInputHistory((prev) => {
|
|
632
634
|
const next = prev.concat([trimmed]).slice(-200);
|
|
633
635
|
setHistoryIndex(next.length);
|
|
@@ -668,8 +670,10 @@ function createUcodeApp({ React, ink, props, interactive = true }) {
|
|
|
668
670
|
}, { isActive: interactive });
|
|
669
671
|
|
|
670
672
|
return h(Box, { flexDirection: "column", width: "100%" },
|
|
671
|
-
h(
|
|
672
|
-
|
|
673
|
+
h(Box, { flexDirection: "column", width: "100%" },
|
|
674
|
+
...logLines.map((item) =>
|
|
675
|
+
h(Text, { key: item.id }, item.text || " ")
|
|
676
|
+
)
|
|
673
677
|
),
|
|
674
678
|
activeMerge ? h(Box, null,
|
|
675
679
|
h(Text, { color: activeMerge.entries.some((e) => e.isError) ? "red" : "cyan" },
|
|
@@ -684,6 +688,7 @@ function createUcodeApp({ React, ink, props, interactive = true }) {
|
|
|
684
688
|
h(Box, { width: "100%" },
|
|
685
689
|
h(MultilineInput, {
|
|
686
690
|
value: draft,
|
|
691
|
+
valueVersion: draftVersion,
|
|
687
692
|
onChange: (next) => setDraft(next),
|
|
688
693
|
onSubmit: (value) => submit(value),
|
|
689
694
|
onCancel: () => {
|
|
@@ -715,6 +720,10 @@ function createUcodeApp({ React, ink, props, interactive = true }) {
|
|
|
715
720
|
interactive,
|
|
716
721
|
placeholder: "",
|
|
717
722
|
promptPrefix: targetAgent ? `›@${getAgentLabel(targetAgent)} ` : "› ",
|
|
723
|
+
// The agents footer is rendered below the input. Matching chat's
|
|
724
|
+
// IME parking contract keeps the hardware cursor aligned with the
|
|
725
|
+
// inverse caret instead of drifting to the bottom of the frame.
|
|
726
|
+
linesBelowInput: 1,
|
|
718
727
|
}),
|
|
719
728
|
),
|
|
720
729
|
h(Box, { width: "100%" },
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
* Returns a stop() function that the caller invokes on exit.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
const { createAgentSockets } = require("../../chat/agentSockets");
|
|
19
|
-
const { loadInternalAgentLogHistory } = require("../../chat/internalAgentLogHistory");
|
|
20
|
-
const { IPC_REQUEST_TYPES, IPC_RESPONSE_TYPES } = require("../../
|
|
21
|
-
const { getUfooPaths } = require("../../
|
|
18
|
+
const { createAgentSockets } = require("../../app/chat/agentSockets");
|
|
19
|
+
const { loadInternalAgentLogHistory } = require("../../app/chat/internalAgentLogHistory");
|
|
20
|
+
const { IPC_REQUEST_TYPES, IPC_RESPONSE_TYPES } = require("../../runtime/contracts/eventContract");
|
|
21
|
+
const { getUfooPaths } = require("../../coordination/state/paths");
|
|
22
22
|
const os = require("os");
|
|
23
23
|
const path = require("path");
|
|
24
24
|
const readline = require("readline");
|
|
@@ -217,10 +217,8 @@ function startAgentMirror({
|
|
|
217
217
|
const cols = stdout.columns || 80;
|
|
218
218
|
const rows = stdout.rows || 24;
|
|
219
219
|
|
|
220
|
-
// Mirror the
|
|
221
|
-
// <bus-queues-dir>/<safeName>/inject.sock.
|
|
222
|
-
// same way so a daemon launched by either TUI is reachable from the
|
|
223
|
-
// other.
|
|
220
|
+
// Mirror the daemon socket lookup:
|
|
221
|
+
// <bus-queues-dir>/<safeName>/inject.sock.
|
|
224
222
|
const safeName = String(agentId || "").replace(/[^A-Za-z0-9_-]/g, "_");
|
|
225
223
|
const sockPath = path.join(
|
|
226
224
|
getUfooPaths(projectRoot || process.cwd()).busQueuesDir,
|
|
@@ -247,7 +245,7 @@ function startAgentMirror({
|
|
|
247
245
|
// Clear screen + reserve a 1-line bar at the bottom for our exit hint.
|
|
248
246
|
writeOut("\x1b[2J\x1b[H");
|
|
249
247
|
writeOut(`\x1b[1;${Math.max(1, rows - 1)}r`);
|
|
250
|
-
writeOut(`\x1b[${rows};1H\x1b[7m esc \x1b[0m return to chat · attached to ${agentId}`);
|
|
248
|
+
writeOut(`\x1b[${rows};1H\x1b[7m esc esc \x1b[0m return to chat · attached to ${agentId}`);
|
|
251
249
|
writeOut("\x1b[H");
|
|
252
250
|
|
|
253
251
|
sockets.connectOutput(sockPath);
|
|
@@ -259,23 +257,30 @@ function startAgentMirror({
|
|
|
259
257
|
if (typeof stdin.setRawMode === "function") stdin.setRawMode(true);
|
|
260
258
|
stdin.resume();
|
|
261
259
|
|
|
260
|
+
let escCount = 0;
|
|
261
|
+
let escTimer = null;
|
|
262
|
+
|
|
262
263
|
const onData = (chunk) => {
|
|
263
264
|
if (stopped) return;
|
|
264
|
-
// Esc on its own (single 0x1b byte, no follow-up) exits the mirror.
|
|
265
|
-
// We can't perfectly distinguish a bare Esc from the start of an
|
|
266
|
-
// arrow-key sequence; the convention here is "Esc + nothing within
|
|
267
|
-
// 50ms means leave". Anything else gets forwarded as-is.
|
|
268
265
|
if (chunk.length === 1 && chunk[0] === 0x1b) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
266
|
+
escCount += 1;
|
|
267
|
+
if (escCount >= 2) {
|
|
268
|
+
clearTimeout(escTimer);
|
|
269
|
+
escCount = 0;
|
|
270
|
+
stop();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
escTimer = setTimeout(() => { escCount = 0; }, 300);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (escCount > 0) {
|
|
277
|
+
clearTimeout(escTimer);
|
|
278
|
+
escCount = 0;
|
|
279
|
+
sockets.sendRaw(Buffer.concat([Buffer.from([0x1b]), chunk]));
|
|
273
280
|
return;
|
|
274
281
|
}
|
|
275
|
-
pendingEsc = null;
|
|
276
282
|
sockets.sendRaw(chunk);
|
|
277
283
|
};
|
|
278
|
-
let pendingEsc = null;
|
|
279
284
|
|
|
280
285
|
const onResize = () => {
|
|
281
286
|
if (stopped) return;
|
|
@@ -42,6 +42,7 @@ const DASHBOARD_VIEWS = ["projects", "agents", "mode", "provider", "cron"];
|
|
|
42
42
|
const DEFAULT_PROVIDER_OPTIONS = [
|
|
43
43
|
{ label: "codex", value: "codex-cli" },
|
|
44
44
|
{ label: "claude", value: "claude-cli" },
|
|
45
|
+
{ label: "agy", value: "agy-cli" },
|
|
45
46
|
];
|
|
46
47
|
function projectRootOf(row = {}) {
|
|
47
48
|
return String((row && (row.root || row.project_root || row.projectRoot)) || "");
|
|
@@ -68,9 +69,10 @@ function createInitialState({ banner = [], globalMode = false, globalScope = "co
|
|
|
68
69
|
selectedProjectIndex: -1,
|
|
69
70
|
selectedProjectRoot: "",
|
|
70
71
|
projectListWindowStart: 0,
|
|
72
|
+
emptyProjectsDownArmed: false,
|
|
71
73
|
activeProjectRoot: "",
|
|
72
|
-
modeOptions: ["auto", "host", "terminal", "tmux", "internal
|
|
73
|
-
selectedModeIndex: Math.max(0, ["auto", "host", "terminal", "tmux", "internal
|
|
74
|
+
modeOptions: ["auto", "host", "terminal", "tmux", "internal"],
|
|
75
|
+
selectedModeIndex: Math.max(0, ["auto", "host", "terminal", "tmux", "internal"].indexOf(initialLaunchMode)),
|
|
74
76
|
providerOptions: DEFAULT_PROVIDER_OPTIONS,
|
|
75
77
|
selectedProviderIndex,
|
|
76
78
|
cronTasks: [],
|
|
@@ -132,11 +134,27 @@ function reducer(state, action) {
|
|
|
132
134
|
case "draft/clear":
|
|
133
135
|
return { ...state, draft: "" };
|
|
134
136
|
case "focus/toggle":
|
|
135
|
-
return {
|
|
137
|
+
return {
|
|
138
|
+
...state,
|
|
139
|
+
focusMode: state.focusMode === "input" ? "dashboard" : "input",
|
|
140
|
+
emptyProjectsDownArmed: state.focusMode === "input" ? state.emptyProjectsDownArmed : false,
|
|
141
|
+
};
|
|
136
142
|
case "focus/set":
|
|
137
|
-
return {
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
return {
|
|
144
|
+
...state,
|
|
145
|
+
focusMode: action.mode === "dashboard" ? "dashboard" : "input",
|
|
146
|
+
emptyProjectsDownArmed: action.mode === "dashboard" ? state.emptyProjectsDownArmed : false,
|
|
147
|
+
};
|
|
148
|
+
case "view/set": {
|
|
149
|
+
const view = action.view;
|
|
150
|
+
const inAgentsView = view === "agents";
|
|
151
|
+
return {
|
|
152
|
+
...state,
|
|
153
|
+
dashboardView: view,
|
|
154
|
+
agentSelectionMode: inAgentsView && state.focusMode === "dashboard" && state.selectedAgentIndex >= 0,
|
|
155
|
+
emptyProjectsDownArmed: view === "projects" ? state.emptyProjectsDownArmed : false,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
140
158
|
case "view/cycle": {
|
|
141
159
|
const i = DASHBOARD_VIEWS.indexOf(state.dashboardView);
|
|
142
160
|
const direction = action.direction === "left" ? -1 : 1;
|
|
@@ -204,6 +222,7 @@ function reducer(state, action) {
|
|
|
204
222
|
selectedProjectRoot: selectedIndex >= 0 ? selectedRoot : "",
|
|
205
223
|
selectedProjectIndex: selectedIndex,
|
|
206
224
|
activeProjectRoot: action.activeProjectRoot || state.activeProjectRoot,
|
|
225
|
+
emptyProjectsDownArmed: list.length === 0 ? state.emptyProjectsDownArmed : false,
|
|
207
226
|
};
|
|
208
227
|
}
|
|
209
228
|
case "projects/select":
|
|
@@ -211,9 +230,12 @@ function reducer(state, action) {
|
|
|
211
230
|
...state,
|
|
212
231
|
selectedProjectIndex: action.index,
|
|
213
232
|
selectedProjectRoot: String(action.projectRoot || projectRootOf(state.projects[action.index]) || ""),
|
|
233
|
+
emptyProjectsDownArmed: false,
|
|
214
234
|
};
|
|
215
235
|
case "projects/clearSelection":
|
|
216
|
-
return { ...state, selectedProjectIndex: -1, selectedProjectRoot: "" };
|
|
236
|
+
return { ...state, selectedProjectIndex: -1, selectedProjectRoot: "", emptyProjectsDownArmed: false };
|
|
237
|
+
case "projects/armEmptyDown":
|
|
238
|
+
return { ...state, emptyProjectsDownArmed: true };
|
|
217
239
|
case "projects/window":
|
|
218
240
|
return { ...state, projectListWindowStart: Math.max(0, action.windowStart | 0) };
|
|
219
241
|
case "scope/set":
|
package/src/bus/messageMeta.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const INJECTION_MODES = {
|
|
4
|
-
IMMEDIATE: "immediate",
|
|
5
|
-
QUEUED: "queued",
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
function normalizeInjectionMode(value, fallback = INJECTION_MODES.IMMEDIATE) {
|
|
9
|
-
const raw = String(value || "").trim().toLowerCase();
|
|
10
|
-
if (raw === INJECTION_MODES.QUEUED) return INJECTION_MODES.QUEUED;
|
|
11
|
-
if (raw === INJECTION_MODES.IMMEDIATE) return INJECTION_MODES.IMMEDIATE;
|
|
12
|
-
return fallback;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function normalizeMessageSource(value) {
|
|
16
|
-
const raw = String(value || "").trim();
|
|
17
|
-
return raw || "";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function buildMessageData(message, options = {}) {
|
|
21
|
-
const base = options && typeof options.data === "object" && options.data
|
|
22
|
-
? { ...options.data }
|
|
23
|
-
: {};
|
|
24
|
-
const data = { ...base, message };
|
|
25
|
-
data.injection_mode = normalizeInjectionMode(
|
|
26
|
-
options.injectionMode || data.injection_mode,
|
|
27
|
-
INJECTION_MODES.IMMEDIATE,
|
|
28
|
-
);
|
|
29
|
-
const source = normalizeMessageSource(options.source || data.source);
|
|
30
|
-
if (source) {
|
|
31
|
-
data.source = source;
|
|
32
|
-
} else {
|
|
33
|
-
delete data.source;
|
|
34
|
-
}
|
|
35
|
-
return data;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getInjectionModeFromEvent(evt, fallback = INJECTION_MODES.IMMEDIATE) {
|
|
39
|
-
const data = evt && typeof evt.data === "object" && evt.data ? evt.data : {};
|
|
40
|
-
return normalizeInjectionMode(
|
|
41
|
-
data.injection_mode || evt?.injection_mode,
|
|
42
|
-
fallback,
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
module.exports = {
|
|
47
|
-
INJECTION_MODES,
|
|
48
|
-
normalizeInjectionMode,
|
|
49
|
-
normalizeMessageSource,
|
|
50
|
-
buildMessageData,
|
|
51
|
-
getInjectionModeFromEvent,
|
|
52
|
-
};
|