spawn-term 3.0.5 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/App.js +18 -9
- package/dist/cjs/components/App.js.map +1 -1
- package/dist/cjs/index-esm.js.map +1 -1
- package/dist/cjs/lib/TerminalBuffer.js +139 -0
- package/dist/cjs/lib/TerminalBuffer.js.map +1 -0
- package/dist/cjs/session.js +129 -56
- package/dist/cjs/session.js.map +1 -1
- package/dist/cjs/src/index-esm.d.ts +1 -0
- package/dist/cjs/src/lib/TerminalBuffer.d.ts +32 -0
- package/dist/cjs/src/state/processStore.d.ts +5 -1
- package/dist/cjs/src/types.d.ts +2 -0
- package/dist/cjs/state/processStore.js +57 -7
- package/dist/cjs/state/processStore.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/components/App.js +18 -9
- package/dist/esm/components/App.js.map +1 -1
- package/dist/esm/index-esm.js.map +1 -1
- package/dist/esm/lib/TerminalBuffer.js +62 -0
- package/dist/esm/lib/TerminalBuffer.js.map +1 -0
- package/dist/esm/session.js +112 -35
- package/dist/esm/session.js.map +1 -1
- package/dist/esm/src/index-esm.d.ts +1 -0
- package/dist/esm/src/lib/TerminalBuffer.d.ts +32 -0
- package/dist/esm/src/state/processStore.d.ts +5 -1
- package/dist/esm/src/types.d.ts +2 -0
- package/dist/esm/state/processStore.js +35 -5
- package/dist/esm/state/processStore.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +2 -1
|
@@ -39,19 +39,23 @@ function AppContent(param) {
|
|
|
39
39
|
var scrollOffset = (0, _react.useSyncExternalStore)(store.subscribe, store.getScrollOffset);
|
|
40
40
|
var listScrollOffset = (0, _react.useSyncExternalStore)(store.subscribe, store.getListScrollOffset);
|
|
41
41
|
var errorFooterExpanded = (0, _react.useSyncExternalStore)(store.subscribe, store.getErrorFooterExpanded);
|
|
42
|
+
// Subscribe to buffer version to trigger re-renders when terminal buffer content changes
|
|
43
|
+
var _bufferVersion = (0, _react.useSyncExternalStore)(store.subscribe, store.getBufferVersion);
|
|
42
44
|
// Subscribed state that triggers re-renders
|
|
43
45
|
var header = (0, _react.useSyncExternalStore)(store.subscribe, store.getHeader);
|
|
44
46
|
var showStatusBar = (0, _react.useSyncExternalStore)(store.subscribe, store.getShowStatusBar);
|
|
45
47
|
var isInteractive = (0, _react.useSyncExternalStore)(store.subscribe, store.getIsInteractive);
|
|
46
|
-
// Calculate visible process count (reserve lines for header, divider, status bar)
|
|
47
|
-
|
|
48
|
+
// Calculate visible process count (reserve lines for header, divider, status bar, expanded output)
|
|
49
|
+
// When a process is expanded, reserve space for the expanded output to prevent terminal scrolling
|
|
50
|
+
var expandedHeight = expandedId ? _constantsts.EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint
|
|
51
|
+
var reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight;
|
|
48
52
|
var visibleProcessCount = Math.max(1, terminalHeight - reservedLines);
|
|
49
53
|
// Derived state (computed from processes which is already subscribed)
|
|
50
54
|
var runningCount = store.getRunningCount();
|
|
51
55
|
var doneCount = store.getDoneCount();
|
|
52
56
|
var errorCount = store.getErrorCount();
|
|
53
57
|
var errorLineCount = store.getErrorLineCount();
|
|
54
|
-
var
|
|
58
|
+
var _isAllComplete = store.isAllComplete();
|
|
55
59
|
var errorLines = store.getErrorLines();
|
|
56
60
|
// Handle exit signal
|
|
57
61
|
(0, _react.useEffect)(function() {
|
|
@@ -62,13 +66,13 @@ function AppContent(param) {
|
|
|
62
66
|
shouldExit,
|
|
63
67
|
exit
|
|
64
68
|
]);
|
|
65
|
-
// Auto-enter interactive mode when
|
|
69
|
+
// Auto-enter interactive mode immediately when interactive flag is set
|
|
70
|
+
// This allows selecting and viewing logs of running processes
|
|
66
71
|
(0, _react.useEffect)(function() {
|
|
67
|
-
if (
|
|
72
|
+
if (isInteractive && mode === 'normal') {
|
|
68
73
|
store.setMode('interactive');
|
|
69
74
|
}
|
|
70
75
|
}, [
|
|
71
|
-
isAllComplete,
|
|
72
76
|
isInteractive,
|
|
73
77
|
mode,
|
|
74
78
|
store
|
|
@@ -132,8 +136,13 @@ function AppContent(param) {
|
|
|
132
136
|
]);
|
|
133
137
|
// Normal/Interactive view - render in original registration order
|
|
134
138
|
var showSelection = mode === 'interactive';
|
|
139
|
+
// Force full re-render when layout HEIGHT changes (not content)
|
|
140
|
+
// Combined with incrementalRendering: false in session.tsx, this ensures clean redraws
|
|
141
|
+
// Note: scrollOffset is NOT included - scrolling within expansion doesn't change height
|
|
142
|
+
var layoutKey = "".concat(listScrollOffset, "-").concat(expandedId, "-").concat(errorCount, "-").concat(errorFooterExpanded);
|
|
135
143
|
return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_ink.Box, {
|
|
136
144
|
flexDirection: "column",
|
|
145
|
+
height: terminalHeight,
|
|
137
146
|
children: [
|
|
138
147
|
header && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
|
|
139
148
|
children: [
|
|
@@ -155,13 +164,13 @@ function AppContent(param) {
|
|
|
155
164
|
isSelected: showSelection && originalIndex === selectedIndex
|
|
156
165
|
}),
|
|
157
166
|
expandedId === item.id && /*#__PURE__*/ (0, _jsxruntime.jsx)(_ExpandedOutputts.default, {
|
|
158
|
-
lines: item.
|
|
167
|
+
lines: store.getProcessLines(item.id),
|
|
159
168
|
scrollOffset: scrollOffset
|
|
160
169
|
})
|
|
161
170
|
]
|
|
162
171
|
}, item.id);
|
|
163
172
|
})
|
|
164
|
-
}
|
|
173
|
+
}),
|
|
165
174
|
showStatusBar && processes.length > 0 && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
|
|
166
175
|
children: [
|
|
167
176
|
/*#__PURE__*/ (0, _jsxruntime.jsx)(_Dividerts.default, {}),
|
|
@@ -178,7 +187,7 @@ function AppContent(param) {
|
|
|
178
187
|
isExpanded: errorFooterExpanded
|
|
179
188
|
})
|
|
180
189
|
]
|
|
181
|
-
});
|
|
190
|
+
}, layoutKey);
|
|
182
191
|
}
|
|
183
192
|
function App(param) {
|
|
184
193
|
var store = param.store;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';\nimport { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { ProcessStore } from '../state/processStore.ts';\nimport { StoreContext } from '../state/StoreContext.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorFooter from './ErrorFooter.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\n\ninterface AppProps {\n store: ProcessStore;\n}\n\nfunction AppContent({ store }: AppProps): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Subscribe to store state\n const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);\n const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);\n const mode = useSyncExternalStore(store.subscribe, store.getMode);\n const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);\n const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);\n const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);\n const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);\n const errorFooterExpanded = useSyncExternalStore(store.subscribe, store.getErrorFooterExpanded);\n\n // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(store.subscribe, store.getHeader);\n const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);\n const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);\n\n // Calculate visible process count (reserve lines for header, divider, status bar)\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0);\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from processes which is already subscribed)\n const runningCount = store.getRunningCount();\n const doneCount = store.getDoneCount();\n const errorCount = store.getErrorCount();\n const errorLineCount = store.getErrorLineCount();\n const isAllComplete = store.isAllComplete();\n const errorLines = store.getErrorLines();\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode when all complete and interactive flag is set\n useEffect(() => {\n if (isAllComplete && isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isAllComplete, isInteractive, mode, store]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n if (mode === 'normal') {\n // In non-interactive mode, 'e' toggles error footer\n if (input === 'e' && errorCount > 0) {\n store.toggleErrorFooter();\n }\n } else if (mode === 'interactive') {\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse();\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand();\n } else if (key.downArrow) {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow) {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n } else if (input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Slice processes to visible viewport in interactive mode\n const visibleProcesses = useMemo(() => {\n if (mode === 'interactive') {\n return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);\n }\n return processes;\n }, [processes, mode, listScrollOffset, visibleProcessCount]);\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n\n return (\n <Box flexDirection=\"column\">\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Visible processes - key forces clean re-render on scroll */}\n <Box key={`processes-${listScrollOffset}`} flexDirection=\"column\">\n {visibleProcesses.map((item) => {\n const originalIndex = processes.indexOf(item);\n return (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && originalIndex === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={item.lines} scrollOffset={scrollOffset} />}\n </Box>\n );\n })}\n </Box>\n\n {/* Status bar */}\n {showStatusBar && processes.length > 0 && (\n <>\n <Divider />\n <StatusBar running={runningCount} done={doneCount} errors={errorCount} errorLines={errorLineCount} />\n </>\n )}\n\n {/* Error footer (non-interactive mode only) */}\n {!isInteractive && errorCount > 0 && <ErrorFooter errors={errorLines} isExpanded={errorFooterExpanded} />}\n </Box>\n );\n}\n\n// Wrapper component that provides store context\nexport default function App({ store }: AppProps): React.JSX.Element {\n return (\n <StoreContext.Provider value={store}>\n <AppContent store={store} />\n </StoreContext.Provider>\n );\n}\n"],"names":["App","AppContent","store","exit","useApp","isRawModeSupported","useStdin","stdout","useStdout","terminalHeight","rows","processes","useSyncExternalStore","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","isAllComplete","errorLines","getErrorLines","useEffect","setMode","useInput","input","key","toggleErrorFooter","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","EXPANDED_MAX_VISIBLE_LINES","selectNext","upArrow","scrollUp","selectPrev","isActive","visibleProcesses","useMemo","slice","showSelection","Box","flexDirection","Text","Divider","map","item","originalIndex","indexOf","CompactProcessLine","isSelected","id","ExpandedOutput","lines","length","StatusBar","running","done","errors","ErrorFooter","isExpanded","StoreContext","Provider","value"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/App.tsx"],"sourcesContent":["import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';\nimport { useEffect, useMemo, useSyncExternalStore } from 'react';\nimport { EXPANDED_MAX_VISIBLE_LINES } from '../constants.ts';\nimport type { ProcessStore } from '../state/processStore.ts';\nimport { StoreContext } from '../state/StoreContext.ts';\nimport CompactProcessLine from './CompactProcessLine.ts';\nimport Divider from './Divider.ts';\nimport ErrorFooter from './ErrorFooter.ts';\nimport ExpandedOutput from './ExpandedOutput.ts';\nimport StatusBar from './StatusBar.ts';\n\ninterface AppProps {\n store: ProcessStore;\n}\n\nfunction AppContent({ store }: AppProps): React.JSX.Element {\n const { exit } = useApp();\n const { isRawModeSupported } = useStdin();\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Subscribe to store state\n const processes = useSyncExternalStore(store.subscribe, store.getSnapshot);\n const shouldExit = useSyncExternalStore(store.subscribe, store.getShouldExit);\n const mode = useSyncExternalStore(store.subscribe, store.getMode);\n const selectedIndex = useSyncExternalStore(store.subscribe, store.getSelectedIndex);\n const expandedId = useSyncExternalStore(store.subscribe, store.getExpandedId);\n const scrollOffset = useSyncExternalStore(store.subscribe, store.getScrollOffset);\n const listScrollOffset = useSyncExternalStore(store.subscribe, store.getListScrollOffset);\n const errorFooterExpanded = useSyncExternalStore(store.subscribe, store.getErrorFooterExpanded);\n // Subscribe to buffer version to trigger re-renders when terminal buffer content changes\n const _bufferVersion = useSyncExternalStore(store.subscribe, store.getBufferVersion);\n\n // Subscribed state that triggers re-renders\n const header = useSyncExternalStore(store.subscribe, store.getHeader);\n const showStatusBar = useSyncExternalStore(store.subscribe, store.getShowStatusBar);\n const isInteractive = useSyncExternalStore(store.subscribe, store.getIsInteractive);\n\n // Calculate visible process count (reserve lines for header, divider, status bar, expanded output)\n // When a process is expanded, reserve space for the expanded output to prevent terminal scrolling\n const expandedHeight = expandedId ? EXPANDED_MAX_VISIBLE_LINES + 1 : 0; // +1 for scroll hint\n const reservedLines = (header ? 2 : 0) + (showStatusBar ? 2 : 0) + expandedHeight;\n const visibleProcessCount = Math.max(1, terminalHeight - reservedLines);\n\n // Derived state (computed from processes which is already subscribed)\n const runningCount = store.getRunningCount();\n const doneCount = store.getDoneCount();\n const errorCount = store.getErrorCount();\n const errorLineCount = store.getErrorLineCount();\n const _isAllComplete = store.isAllComplete();\n const errorLines = store.getErrorLines();\n\n // Handle exit signal\n useEffect(() => {\n if (shouldExit) {\n exit();\n }\n }, [shouldExit, exit]);\n\n // Auto-enter interactive mode immediately when interactive flag is set\n // This allows selecting and viewing logs of running processes\n useEffect(() => {\n if (isInteractive && mode === 'normal') {\n store.setMode('interactive');\n }\n }, [isInteractive, mode, store]);\n\n // Keyboard handling (only active when raw mode is supported)\n useInput(\n (input, key) => {\n if (mode === 'normal') {\n // In non-interactive mode, 'e' toggles error footer\n if (input === 'e' && errorCount > 0) {\n store.toggleErrorFooter();\n }\n } else if (mode === 'interactive') {\n if (input === 'q' || key.escape) {\n if (expandedId) {\n store.collapse();\n } else {\n store.signalExit(() => {});\n }\n } else if (key.return) {\n store.toggleExpand();\n } else if (key.downArrow) {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (key.upArrow) {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n } else if (input === 'j') {\n if (expandedId) {\n store.scrollDown(EXPANDED_MAX_VISIBLE_LINES);\n } else {\n store.selectNext(visibleProcessCount);\n }\n } else if (input === 'k') {\n if (expandedId) {\n store.scrollUp();\n } else {\n store.selectPrev(visibleProcessCount);\n }\n }\n }\n },\n { isActive: isRawModeSupported === true }\n );\n\n // Slice processes to visible viewport in interactive mode\n const visibleProcesses = useMemo(() => {\n if (mode === 'interactive') {\n return processes.slice(listScrollOffset, listScrollOffset + visibleProcessCount);\n }\n return processes;\n }, [processes, mode, listScrollOffset, visibleProcessCount]);\n\n // Normal/Interactive view - render in original registration order\n const showSelection = mode === 'interactive';\n\n // Force full re-render when layout HEIGHT changes (not content)\n // Combined with incrementalRendering: false in session.tsx, this ensures clean redraws\n // Note: scrollOffset is NOT included - scrolling within expansion doesn't change height\n const layoutKey = `${listScrollOffset}-${expandedId}-${errorCount}-${errorFooterExpanded}`;\n\n return (\n <Box key={layoutKey} flexDirection=\"column\" height={terminalHeight}>\n {/* Header */}\n {header && (\n <>\n <Text>{header}</Text>\n <Divider />\n </>\n )}\n\n {/* Visible processes */}\n <Box flexDirection=\"column\">\n {visibleProcesses.map((item) => {\n const originalIndex = processes.indexOf(item);\n return (\n <Box key={item.id} flexDirection=\"column\">\n <CompactProcessLine item={item} isSelected={showSelection && originalIndex === selectedIndex} />\n {expandedId === item.id && <ExpandedOutput lines={store.getProcessLines(item.id)} scrollOffset={scrollOffset} />}\n </Box>\n );\n })}\n </Box>\n\n {/* Status bar */}\n {showStatusBar && processes.length > 0 && (\n <>\n <Divider />\n <StatusBar running={runningCount} done={doneCount} errors={errorCount} errorLines={errorLineCount} />\n </>\n )}\n\n {/* Error footer (non-interactive mode only) */}\n {!isInteractive && errorCount > 0 && <ErrorFooter errors={errorLines} isExpanded={errorFooterExpanded} />}\n </Box>\n );\n}\n\n// Wrapper component that provides store context\nexport default function App({ store }: AppProps): React.JSX.Element {\n return (\n <StoreContext.Provider value={store}>\n <AppContent store={store} />\n </StoreContext.Provider>\n );\n}\n"],"names":["App","AppContent","store","exit","useApp","isRawModeSupported","useStdin","stdout","useStdout","terminalHeight","rows","processes","useSyncExternalStore","subscribe","getSnapshot","shouldExit","getShouldExit","mode","getMode","selectedIndex","getSelectedIndex","expandedId","getExpandedId","scrollOffset","getScrollOffset","listScrollOffset","getListScrollOffset","errorFooterExpanded","getErrorFooterExpanded","_bufferVersion","getBufferVersion","header","getHeader","showStatusBar","getShowStatusBar","isInteractive","getIsInteractive","expandedHeight","EXPANDED_MAX_VISIBLE_LINES","reservedLines","visibleProcessCount","Math","max","runningCount","getRunningCount","doneCount","getDoneCount","errorCount","getErrorCount","errorLineCount","getErrorLineCount","_isAllComplete","isAllComplete","errorLines","getErrorLines","useEffect","setMode","useInput","input","key","toggleErrorFooter","escape","collapse","signalExit","return","toggleExpand","downArrow","scrollDown","selectNext","upArrow","scrollUp","selectPrev","isActive","visibleProcesses","useMemo","slice","showSelection","layoutKey","Box","flexDirection","height","Text","Divider","map","item","originalIndex","indexOf","CompactProcessLine","isSelected","id","ExpandedOutput","lines","getProcessLines","length","StatusBar","running","done","errors","ErrorFooter","isExpanded","StoreContext","Provider","value"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/index-esm.ts"],"sourcesContent":["export { default as figures } from './lib/figures.ts';\nexport { default as formatArguments } from './lib/formatArguments.ts';\nexport * from './types.ts';\n\nimport type { createSession as createSessionType, Session } from './createSessionWrapper.ts';\nexport type { Session };\n\nconst major = +process.versions.node.split('.')[0];\n\nimport { createSession as createSessionImpl } from './createSessionWrapper.ts';\nexport const createSession = major > 18 ? createSessionImpl : (undefined as typeof createSessionType);\n"],"names":["createSession","figures","formatArguments","major","process","versions","node","split","createSessionImpl","undefined"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/index-esm.ts"],"sourcesContent":["export { default as figures } from './lib/figures.ts';\nexport { default as formatArguments } from './lib/formatArguments.ts';\nexport type { TerminalBuffer } from './lib/TerminalBuffer.ts';\nexport * from './types.ts';\n\nimport type { createSession as createSessionType, Session } from './createSessionWrapper.ts';\nexport type { Session };\n\nconst major = +process.versions.node.split('.')[0];\n\nimport { createSession as createSessionImpl } from './createSessionWrapper.ts';\nexport const createSession = major > 18 ? createSessionImpl : (undefined as typeof createSessionType);\n"],"names":["createSession","figures","formatArguments","major","process","versions","node","split","createSessionImpl","undefined"],"mappings":";;;;;;;;;;;QAWaA;eAAAA;;QAXOC;eAAAA,kBAAO;;QACPC;eAAAA,0BAAe;;;gEADA;wEACQ;qBAE7B;sCAOqC;;;;;;;;;;;;;;;;;;;AAFnD,IAAMC,QAAQ,CAACC,QAAQC,QAAQ,CAACC,IAAI,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE;AAG3C,IAAMP,gBAAgBG,QAAQ,KAAKK,qCAAiB,GAAIC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "TerminalBuffer", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return TerminalBuffer;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
var _headless = /*#__PURE__*/ _interop_require_wildcard(require("@xterm/headless"));
|
|
12
|
+
function _class_call_check(instance, Constructor) {
|
|
13
|
+
if (!(instance instanceof Constructor)) {
|
|
14
|
+
throw new TypeError("Cannot call a class as a function");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function _defineProperties(target, props) {
|
|
18
|
+
for(var i = 0; i < props.length; i++){
|
|
19
|
+
var descriptor = props[i];
|
|
20
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
21
|
+
descriptor.configurable = true;
|
|
22
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
23
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function _create_class(Constructor, protoProps, staticProps) {
|
|
27
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
28
|
+
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
29
|
+
return Constructor;
|
|
30
|
+
}
|
|
31
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
32
|
+
if (typeof WeakMap !== "function") return null;
|
|
33
|
+
var cacheBabelInterop = new WeakMap();
|
|
34
|
+
var cacheNodeInterop = new WeakMap();
|
|
35
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
36
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
37
|
+
})(nodeInterop);
|
|
38
|
+
}
|
|
39
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
40
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
41
|
+
return obj;
|
|
42
|
+
}
|
|
43
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
44
|
+
return {
|
|
45
|
+
default: obj
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
49
|
+
if (cache && cache.has(obj)) {
|
|
50
|
+
return cache.get(obj);
|
|
51
|
+
}
|
|
52
|
+
var newObj = {
|
|
53
|
+
__proto__: null
|
|
54
|
+
};
|
|
55
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
56
|
+
for(var key in obj){
|
|
57
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
58
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
59
|
+
if (desc && (desc.get || desc.set)) {
|
|
60
|
+
Object.defineProperty(newObj, key, desc);
|
|
61
|
+
} else {
|
|
62
|
+
newObj[key] = obj[key];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
newObj.default = obj;
|
|
67
|
+
if (cache) {
|
|
68
|
+
cache.set(obj, newObj);
|
|
69
|
+
}
|
|
70
|
+
return newObj;
|
|
71
|
+
}
|
|
72
|
+
var _xterm_default;
|
|
73
|
+
// Handle both ESM and CJS module formats
|
|
74
|
+
var Terminal = _headless.Terminal || ((_xterm_default = _headless.default) === null || _xterm_default === void 0 ? void 0 : _xterm_default.Terminal);
|
|
75
|
+
var TerminalBuffer = /*#__PURE__*/ function() {
|
|
76
|
+
"use strict";
|
|
77
|
+
function TerminalBuffer(cols) {
|
|
78
|
+
var scrollback = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 10000;
|
|
79
|
+
_class_call_check(this, TerminalBuffer);
|
|
80
|
+
this.terminal = new Terminal({
|
|
81
|
+
cols: cols,
|
|
82
|
+
rows: 50,
|
|
83
|
+
scrollback: scrollback,
|
|
84
|
+
allowProposedApi: true
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
var _proto = TerminalBuffer.prototype;
|
|
88
|
+
/**
|
|
89
|
+
* Write raw data to the terminal buffer.
|
|
90
|
+
* The terminal interprets all ANSI sequences automatically.
|
|
91
|
+
*/ _proto.write = function write(data) {
|
|
92
|
+
var str = typeof data === 'string' ? data : data.toString('utf8');
|
|
93
|
+
this.terminal.write(str);
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Resize the terminal width.
|
|
97
|
+
*/ _proto.resize = function resize(cols) {
|
|
98
|
+
this.terminal.resize(cols, this.terminal.rows);
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Extract the rendered lines from the terminal buffer.
|
|
102
|
+
* This returns the actual visible content after all ANSI sequences
|
|
103
|
+
* have been processed.
|
|
104
|
+
*/ _proto.getLines = function getLines() {
|
|
105
|
+
var buffer = this.terminal.buffer.active;
|
|
106
|
+
var lines = [];
|
|
107
|
+
for(var i = 0; i < buffer.length; i++){
|
|
108
|
+
var line = buffer.getLine(i);
|
|
109
|
+
if (line) {
|
|
110
|
+
// translateToString(trimRight) - trim trailing whitespace
|
|
111
|
+
// Also trim leading whitespace - tools like ncu/npm use cursor positioning
|
|
112
|
+
// which creates lines with leading spaces when interpreted by xterm
|
|
113
|
+
lines.push(line.translateToString(true).trimStart());
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Trim trailing empty lines
|
|
117
|
+
while(lines.length > 0 && lines[lines.length - 1] === ''){
|
|
118
|
+
lines.pop();
|
|
119
|
+
}
|
|
120
|
+
return lines;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Clean up terminal resources.
|
|
124
|
+
*/ _proto.dispose = function dispose() {
|
|
125
|
+
this.terminal.dispose();
|
|
126
|
+
};
|
|
127
|
+
_create_class(TerminalBuffer, [
|
|
128
|
+
{
|
|
129
|
+
key: "lineCount",
|
|
130
|
+
get: /**
|
|
131
|
+
* Get the number of rendered lines.
|
|
132
|
+
*/ function get() {
|
|
133
|
+
return this.getLines().length;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
]);
|
|
137
|
+
return TerminalBuffer;
|
|
138
|
+
}();
|
|
139
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/lib/TerminalBuffer.ts"],"sourcesContent":["import * as xterm from '@xterm/headless';\n\n// Handle both ESM and CJS module formats\nconst Terminal = (xterm as { Terminal: typeof xterm.Terminal; default?: { Terminal: typeof xterm.Terminal } }).Terminal || (xterm as { default?: { Terminal: typeof xterm.Terminal } }).default?.Terminal;\n\n/**\n * Wrapper around @xterm/headless Terminal that provides a virtual terminal buffer.\n * Interprets ANSI escape sequences (cursor movement, line clearing, etc.) to produce\n * the actual rendered output rather than raw intermediate states.\n */\nexport class TerminalBuffer {\n private terminal: InstanceType<typeof Terminal>;\n\n constructor(cols: number, scrollback = 10000) {\n this.terminal = new Terminal({\n cols,\n rows: 50, // Visible rows (doesn't matter much for headless)\n scrollback,\n allowProposedApi: true,\n });\n }\n\n /**\n * Write raw data to the terminal buffer.\n * The terminal interprets all ANSI sequences automatically.\n */\n write(data: string | Buffer): void {\n const str = typeof data === 'string' ? data : data.toString('utf8');\n this.terminal.write(str);\n }\n\n /**\n * Resize the terminal width.\n */\n resize(cols: number): void {\n this.terminal.resize(cols, this.terminal.rows);\n }\n\n /**\n * Extract the rendered lines from the terminal buffer.\n * This returns the actual visible content after all ANSI sequences\n * have been processed.\n */\n getLines(): string[] {\n const buffer = this.terminal.buffer.active;\n const lines: string[] = [];\n\n for (let i = 0; i < buffer.length; i++) {\n const line = buffer.getLine(i);\n if (line) {\n // translateToString(trimRight) - trim trailing whitespace\n // Also trim leading whitespace - tools like ncu/npm use cursor positioning\n // which creates lines with leading spaces when interpreted by xterm\n lines.push(line.translateToString(true).trimStart());\n }\n }\n\n // Trim trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n\n return lines;\n }\n\n /**\n * Get the number of rendered lines.\n */\n get lineCount(): number {\n return this.getLines().length;\n }\n\n /**\n * Clean up terminal resources.\n */\n dispose(): void {\n this.terminal.dispose();\n }\n}\n"],"names":["TerminalBuffer","Terminal","xterm","default","cols","scrollback","terminal","rows","allowProposedApi","write","data","str","toString","resize","getLines","buffer","active","lines","i","length","line","getLine","push","translateToString","trimStart","pop","dispose","lineCount"],"mappings":";;;;+BAUaA;;;eAAAA;;;gEAVU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAGoG;AAD3H,yCAAyC;AACzC,IAAMC,WAAW,AAACC,UAA6FD,QAAQ,MAAI,iBAAA,AAACC,UAA4DC,OAAO,cAApE,qCAAA,eAAsEF,QAAQ;AAOlM,IAAA,AAAMD,+BAAN;;aAAMA,eAGCI,IAAY;YAAEC,aAAAA,iEAAa;gCAH5BL;QAIT,IAAI,CAACM,QAAQ,GAAG,IAAIL,SAAS;YAC3BG,MAAAA;YACAG,MAAM;YACNF,YAAAA;YACAG,kBAAkB;QACpB;;iBATSR;IAYX;;;GAGC,GACDS,OAAAA,KAGC,GAHDA,SAAAA,MAAMC,IAAqB;QACzB,IAAMC,MAAM,OAAOD,SAAS,WAAWA,OAAOA,KAAKE,QAAQ,CAAC;QAC5D,IAAI,CAACN,QAAQ,CAACG,KAAK,CAACE;IACtB;IAEA;;GAEC,GACDE,OAAAA,MAEC,GAFDA,SAAAA,OAAOT,IAAY;QACjB,IAAI,CAACE,QAAQ,CAACO,MAAM,CAACT,MAAM,IAAI,CAACE,QAAQ,CAACC,IAAI;IAC/C;IAEA;;;;GAIC,GACDO,OAAAA,QAoBC,GApBDA,SAAAA;QACE,IAAMC,SAAS,IAAI,CAACT,QAAQ,CAACS,MAAM,CAACC,MAAM;QAC1C,IAAMC,QAAkB,EAAE;QAE1B,IAAK,IAAIC,IAAI,GAAGA,IAAIH,OAAOI,MAAM,EAAED,IAAK;YACtC,IAAME,OAAOL,OAAOM,OAAO,CAACH;YAC5B,IAAIE,MAAM;gBACR,0DAA0D;gBAC1D,2EAA2E;gBAC3E,oEAAoE;gBACpEH,MAAMK,IAAI,CAACF,KAAKG,iBAAiB,CAAC,MAAMC,SAAS;YACnD;QACF;QAEA,4BAA4B;QAC5B,MAAOP,MAAME,MAAM,GAAG,KAAKF,KAAK,CAACA,MAAME,MAAM,GAAG,EAAE,KAAK,GAAI;YACzDF,MAAMQ,GAAG;QACX;QAEA,OAAOR;IACT;IASA;;GAEC,GACDS,OAAAA,OAEC,GAFDA,SAAAA;QACE,IAAI,CAACpB,QAAQ,CAACoB,OAAO;IACvB;kBAnEW1B;;YA0DP2B,KAAAA;iBAAJ,AAHA;;GAEC,GACD;gBACE,OAAO,IAAI,CAACb,QAAQ,GAAGK,MAAM;YAC/B;;;WA5DWnB"}
|
package/dist/cjs/session.js
CHANGED
|
@@ -16,16 +16,28 @@ var _onone = /*#__PURE__*/ _interop_require_default(require("on-one"));
|
|
|
16
16
|
var _queuecb = /*#__PURE__*/ _interop_require_default(require("queue-cb"));
|
|
17
17
|
var _Appts = /*#__PURE__*/ _interop_require_default(require("./components/App.js"));
|
|
18
18
|
var _constantsts = require("./constants.js");
|
|
19
|
-
var _addLinests = /*#__PURE__*/ _interop_require_default(require("./lib/addLines.js"));
|
|
20
19
|
var _concatWritablets = /*#__PURE__*/ _interop_require_default(require("./lib/concatWritable.js"));
|
|
21
20
|
var _formatArgumentsts = /*#__PURE__*/ _interop_require_default(require("./lib/formatArguments.js"));
|
|
21
|
+
var _TerminalBufferts = require("./lib/TerminalBuffer.js");
|
|
22
22
|
var _processStorets = require("./state/processStore.js");
|
|
23
|
-
var _typests = require("./types.js");
|
|
24
23
|
function _class_call_check(instance, Constructor) {
|
|
25
24
|
if (!(instance instanceof Constructor)) {
|
|
26
25
|
throw new TypeError("Cannot call a class as a function");
|
|
27
26
|
}
|
|
28
27
|
}
|
|
28
|
+
function _define_property(obj, key, value) {
|
|
29
|
+
if (key in obj) {
|
|
30
|
+
Object.defineProperty(obj, key, {
|
|
31
|
+
value: value,
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
obj[key] = value;
|
|
38
|
+
}
|
|
39
|
+
return obj;
|
|
40
|
+
}
|
|
29
41
|
function _interop_require_default(obj) {
|
|
30
42
|
return obj && obj.__esModule ? obj : {
|
|
31
43
|
default: obj
|
|
@@ -72,6 +84,45 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
72
84
|
}
|
|
73
85
|
return newObj;
|
|
74
86
|
}
|
|
87
|
+
function _object_spread(target) {
|
|
88
|
+
for(var i = 1; i < arguments.length; i++){
|
|
89
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
90
|
+
var ownKeys = Object.keys(source);
|
|
91
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
92
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
93
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
ownKeys.forEach(function(key) {
|
|
97
|
+
_define_property(target, key, source[key]);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return target;
|
|
101
|
+
}
|
|
102
|
+
function ownKeys(object, enumerableOnly) {
|
|
103
|
+
var keys = Object.keys(object);
|
|
104
|
+
if (Object.getOwnPropertySymbols) {
|
|
105
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
106
|
+
if (enumerableOnly) {
|
|
107
|
+
symbols = symbols.filter(function(sym) {
|
|
108
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
keys.push.apply(keys, symbols);
|
|
112
|
+
}
|
|
113
|
+
return keys;
|
|
114
|
+
}
|
|
115
|
+
function _object_spread_props(target, source) {
|
|
116
|
+
source = source != null ? source : {};
|
|
117
|
+
if (Object.getOwnPropertyDescriptors) {
|
|
118
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
119
|
+
} else {
|
|
120
|
+
ownKeys(Object(source)).forEach(function(key) {
|
|
121
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return target;
|
|
125
|
+
}
|
|
75
126
|
function _object_without_properties(source, excluded) {
|
|
76
127
|
if (source == null) return {};
|
|
77
128
|
var target = _object_without_properties_loose(source, excluded);
|
|
@@ -111,13 +162,21 @@ var SessionImpl = /*#__PURE__*/ function() {
|
|
|
111
162
|
this.store = new _processStorets.ProcessStore(options);
|
|
112
163
|
var _options_interactive;
|
|
113
164
|
this.isInteractive = (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false;
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
165
|
+
// Use a very wide buffer to prevent line wrapping in xterm
|
|
166
|
+
// Actual display truncation is handled by Ink components
|
|
167
|
+
this.terminalWidth = 10000;
|
|
168
|
+
// Only render Ink when stdout is a real terminal
|
|
169
|
+
// When piped (e.g., nested spawn-term), skip Ink to avoid cursor positioning artifacts
|
|
170
|
+
if (process.stdout.isTTY) {
|
|
171
|
+
// Note: incrementalRendering disabled to prevent corruption when content shifts vertically
|
|
172
|
+
// (e.g., error footer appearing, processes completing, scroll position changes)
|
|
173
|
+
this.inkApp = (0, _ink.render)(/*#__PURE__*/ (0, _jsxruntime.jsx)(_Appts.default, {
|
|
174
|
+
store: this.store
|
|
175
|
+
}), {
|
|
176
|
+
incrementalRendering: false,
|
|
177
|
+
maxFps: _constantsts.DEFAULT_MAX_FPS
|
|
178
|
+
});
|
|
179
|
+
}
|
|
121
180
|
}
|
|
122
181
|
var _proto = SessionImpl.prototype;
|
|
123
182
|
_proto.spawn = function spawn(command, args, spawnOptions, options, callback) {
|
|
@@ -130,8 +189,28 @@ var SessionImpl = /*#__PURE__*/ function() {
|
|
|
130
189
|
"stdio"
|
|
131
190
|
]);
|
|
132
191
|
if (stdio === 'inherit') {
|
|
192
|
+
// When Ink is not rendering (stdout not a TTY), pass output directly to stdout
|
|
193
|
+
if (!this.inkApp) {
|
|
194
|
+
var cp = (0, _crossspawncb.crossSpawn)(command, args, _object_spread_props(_object_spread({}, csOptions), {
|
|
195
|
+
stdio: 'inherit'
|
|
196
|
+
}));
|
|
197
|
+
_crossspawncb.default.worker(cp, csOptions, function(err) {
|
|
198
|
+
var res = err ? err : {};
|
|
199
|
+
res.stdout = null;
|
|
200
|
+
res.stderr = null;
|
|
201
|
+
res.output = [
|
|
202
|
+
null,
|
|
203
|
+
null,
|
|
204
|
+
null
|
|
205
|
+
];
|
|
206
|
+
err ? callback(err) : callback(null, res);
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
133
210
|
this.runningCount++;
|
|
134
211
|
var id = _crypto.default.randomUUID();
|
|
212
|
+
// Create terminal buffer for ANSI sequence interpretation
|
|
213
|
+
var terminalBuffer = new _TerminalBufferts.TerminalBuffer(this.terminalWidth);
|
|
135
214
|
this.store.addProcess({
|
|
136
215
|
id: id,
|
|
137
216
|
title: [
|
|
@@ -139,55 +218,49 @@ var SessionImpl = /*#__PURE__*/ function() {
|
|
|
139
218
|
].concat((0, _formatArgumentsts.default)(args)).join(' '),
|
|
140
219
|
state: 'running',
|
|
141
220
|
lines: [],
|
|
221
|
+
terminalBuffer: terminalBuffer,
|
|
142
222
|
group: options.group,
|
|
143
223
|
expanded: options.expanded
|
|
144
224
|
});
|
|
145
|
-
var
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
outputs.stdout = (0, _addLinests.default)(function(lines) {
|
|
153
|
-
_this.store.appendLines(id, lines.map(function(text) {
|
|
154
|
-
return {
|
|
155
|
-
type: _typests.LineType.stdout,
|
|
156
|
-
text: text
|
|
157
|
-
};
|
|
158
|
-
}));
|
|
225
|
+
var cp1 = (0, _crossspawncb.crossSpawn)(command, args, csOptions);
|
|
226
|
+
// Pipe stdout and stderr directly to terminal buffer
|
|
227
|
+
// Both streams go to the same buffer to maintain correct ordering
|
|
228
|
+
if (cp1.stdout) {
|
|
229
|
+
cp1.stdout.on('data', function(chunk) {
|
|
230
|
+
terminalBuffer.write(chunk);
|
|
231
|
+
_this.store.notify();
|
|
159
232
|
});
|
|
160
|
-
|
|
233
|
+
}
|
|
234
|
+
if (cp1.stderr) {
|
|
235
|
+
cp1.stderr.on('data', function(chunk) {
|
|
236
|
+
terminalBuffer.write(chunk);
|
|
237
|
+
_this.store.notify();
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
// Wait for process to complete
|
|
241
|
+
var queue = new _queuecb.default();
|
|
242
|
+
if (cp1.stdout) {
|
|
243
|
+
queue.defer(_onone.default.bind(null, cp1.stdout, [
|
|
161
244
|
'error',
|
|
162
245
|
'end',
|
|
163
|
-
'close'
|
|
164
|
-
'finish'
|
|
246
|
+
'close'
|
|
165
247
|
]));
|
|
166
248
|
}
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
_this.store.appendLines(id, lines.map(function(text) {
|
|
170
|
-
return {
|
|
171
|
-
type: _typests.LineType.stderr,
|
|
172
|
-
text: text
|
|
173
|
-
};
|
|
174
|
-
}));
|
|
175
|
-
});
|
|
176
|
-
queue.defer(_onone.default.bind(null, cp.stderr.pipe(outputs.stderr), [
|
|
249
|
+
if (cp1.stderr) {
|
|
250
|
+
queue.defer(_onone.default.bind(null, cp1.stderr, [
|
|
177
251
|
'error',
|
|
178
252
|
'end',
|
|
179
|
-
'close'
|
|
180
|
-
'finish'
|
|
253
|
+
'close'
|
|
181
254
|
]));
|
|
182
255
|
}
|
|
183
|
-
queue.defer(_crossspawncb.default.worker.bind(null,
|
|
256
|
+
queue.defer(_crossspawncb.default.worker.bind(null, cp1, csOptions));
|
|
184
257
|
queue.await(function(err) {
|
|
185
258
|
var res = err ? err : {};
|
|
186
|
-
res.stdout =
|
|
187
|
-
res.stderr =
|
|
259
|
+
res.stdout = null; // Not collecting raw output in inherit mode
|
|
260
|
+
res.stderr = null;
|
|
188
261
|
res.output = [
|
|
189
|
-
|
|
190
|
-
|
|
262
|
+
null,
|
|
263
|
+
null,
|
|
191
264
|
null
|
|
192
265
|
];
|
|
193
266
|
_this.store.updateProcess(id, {
|
|
@@ -198,39 +271,39 @@ var SessionImpl = /*#__PURE__*/ function() {
|
|
|
198
271
|
});
|
|
199
272
|
} else {
|
|
200
273
|
// Non-inherit mode: collect output but don't display in UI
|
|
201
|
-
var
|
|
202
|
-
var
|
|
274
|
+
var cp2 = (0, _crossspawncb.crossSpawn)(command, args, csOptions);
|
|
275
|
+
var outputs = {
|
|
203
276
|
stdout: null,
|
|
204
277
|
stderr: null
|
|
205
278
|
};
|
|
206
279
|
var queue1 = new _queuecb.default();
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
280
|
+
if (cp2.stdout) {
|
|
281
|
+
outputs.stdout = (0, _concatWritablets.default)(function(output) {
|
|
282
|
+
outputs.stdout.output = output.toString(encoding || 'utf8');
|
|
210
283
|
});
|
|
211
|
-
queue1.defer(_onone.default.bind(null,
|
|
284
|
+
queue1.defer(_onone.default.bind(null, cp2.stdout.pipe(outputs.stdout), [
|
|
212
285
|
'error',
|
|
213
286
|
'end',
|
|
214
287
|
'close',
|
|
215
288
|
'finish'
|
|
216
289
|
]));
|
|
217
290
|
}
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
|
|
291
|
+
if (cp2.stderr) {
|
|
292
|
+
outputs.stderr = (0, _concatWritablets.default)(function(output) {
|
|
293
|
+
outputs.stderr.output = output.toString(encoding || 'utf8');
|
|
221
294
|
});
|
|
222
|
-
queue1.defer(_onone.default.bind(null,
|
|
295
|
+
queue1.defer(_onone.default.bind(null, cp2.stderr.pipe(outputs.stderr), [
|
|
223
296
|
'error',
|
|
224
297
|
'end',
|
|
225
298
|
'close',
|
|
226
299
|
'finish'
|
|
227
300
|
]));
|
|
228
301
|
}
|
|
229
|
-
queue1.defer(_crossspawncb.default.worker.bind(null,
|
|
302
|
+
queue1.defer(_crossspawncb.default.worker.bind(null, cp2, csOptions));
|
|
230
303
|
queue1.await(function(err) {
|
|
231
304
|
var res = err ? err : {};
|
|
232
|
-
res.stdout =
|
|
233
|
-
res.stderr =
|
|
305
|
+
res.stdout = outputs.stdout ? outputs.stdout.output : null;
|
|
306
|
+
res.stderr = outputs.stderr ? outputs.stderr.output : null;
|
|
234
307
|
res.output = [
|
|
235
308
|
res.stdout,
|
|
236
309
|
res.stderr,
|