spawn-term 3.1.0 → 3.1.1
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 +10 -0
- package/dist/cjs/session.js +4 -2
- package/dist/cjs/session.js.map +1 -1
- package/dist/esm/session.js +4 -2
- package/dist/esm/session.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,3 +9,13 @@ await spawn('npm', ['install'], { stdio: 'inherit' });
|
|
|
9
9
|
await spawn('npm', ['test'], { stdio: 'inherit' });
|
|
10
10
|
|
|
11
11
|
```
|
|
12
|
+
|
|
13
|
+
## Testing
|
|
14
|
+
|
|
15
|
+
Some tests verify behavior specific to non-TTY environments (e.g., CI pipelines). These tests are skipped when stdout is a TTY.
|
|
16
|
+
|
|
17
|
+
To run all tests including non-TTY tests locally, pipe the output:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm test 2>&1 | cat
|
|
21
|
+
```
|
package/dist/cjs/session.js
CHANGED
|
@@ -160,8 +160,6 @@ var SessionImpl = /*#__PURE__*/ function() {
|
|
|
160
160
|
this.closed = false;
|
|
161
161
|
this.waitCallbacks = [];
|
|
162
162
|
this.store = new _processStorets.ProcessStore(options);
|
|
163
|
-
var _options_interactive;
|
|
164
|
-
this.isInteractive = (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false;
|
|
165
163
|
// Use a very wide buffer to prevent line wrapping in xterm
|
|
166
164
|
// Actual display truncation is handled by Ink components
|
|
167
165
|
this.terminalWidth = 10000;
|
|
@@ -174,6 +172,10 @@ var SessionImpl = /*#__PURE__*/ function() {
|
|
|
174
172
|
maxFps: _constantsts.DEFAULT_MAX_FPS
|
|
175
173
|
});
|
|
176
174
|
}
|
|
175
|
+
var _options_interactive;
|
|
176
|
+
// Interactive mode requires a TTY to capture user input (e.g., press 'q' to quit)
|
|
177
|
+
// Force non-interactive when no UI is rendered, otherwise waitAndClose would hang
|
|
178
|
+
this.isInteractive = this.inkApp ? (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false : false;
|
|
177
179
|
}
|
|
178
180
|
var _proto = SessionImpl.prototype;
|
|
179
181
|
_proto.spawn = function spawn(command, args, spawnOptions, options, callback) {
|
package/dist/cjs/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/session.tsx"],"sourcesContent":["import spawn, { crossSpawn, type SpawnResult } from 'cross-spawn-cb';\nimport crypto from 'crypto';\nimport { render } from 'ink';\nimport oo from 'on-one';\nimport Queue from 'queue-cb';\n\nimport App from './components/App.ts';\nimport { DEFAULT_MAX_FPS } from './constants.ts';\nimport concatWritable from './lib/concatWritable.ts';\nimport formatArguments from './lib/formatArguments.ts';\nimport { TerminalBuffer } from './lib/TerminalBuffer.ts';\nimport { ProcessStore } from './state/processStore.ts';\nimport type { ProcessOptions, SessionOptions, SpawnError, SpawnOptions, TerminalCallback } from './types.ts';\n\nexport interface Session {\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void;\n close(): void;\n waitAndClose(callback?: () => void): void;\n}\n\nclass SessionImpl implements Session {\n private store: ProcessStore;\n private inkApp: ReturnType<typeof render> | null = null;\n private runningCount = 0;\n private closed = false;\n private waitCallbacks: (() => void)[] = [];\n private isInteractive: boolean;\n private terminalWidth: number;\n\n constructor(options: SessionOptions = {}) {\n this.store = new ProcessStore(options);\n this.isInteractive = options.interactive ?? false;\n // Use a very wide buffer to prevent line wrapping in xterm\n // Actual display truncation is handled by Ink components\n this.terminalWidth = 10000;\n\n // Only render Ink when stdout is a real terminal\n // When piped (e.g., nested spawn-term), skip Ink to avoid cursor positioning artifacts\n if (process.stdout.isTTY) {\n this.inkApp = render(<App store={this.store} />, {\n maxFps: DEFAULT_MAX_FPS,\n });\n }\n }\n\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void {\n if (this.closed) {\n throw new Error('Session is closed');\n }\n\n const { encoding, stdio, ...csOptions } = spawnOptions;\n\n if (stdio === 'inherit') {\n // When Ink is not rendering (stdout not a TTY), pass output directly to stdout\n if (!this.inkApp) {\n const cp = crossSpawn(command, args, { ...csOptions, stdio: 'inherit' });\n spawn.worker(cp, csOptions, (err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = null;\n res.stderr = null;\n res.output = [null, null, null];\n err ? callback(err) : callback(null, res);\n });\n return;\n }\n\n this.runningCount++;\n const id = crypto.randomUUID();\n\n // Create terminal buffer for ANSI sequence interpretation\n const terminalBuffer = new TerminalBuffer(this.terminalWidth);\n\n this.store.addProcess({\n id,\n title: [command].concat(formatArguments(args)).join(' '),\n state: 'running',\n lines: [],\n terminalBuffer,\n group: options.group,\n expanded: options.expanded,\n });\n\n const cp = crossSpawn(command, args, csOptions);\n\n // Pipe stdout and stderr directly to terminal buffer\n // Both streams go to the same buffer to maintain correct ordering\n if (cp.stdout) {\n cp.stdout.on('data', (chunk: Buffer) => {\n terminalBuffer.write(chunk);\n this.store.notify();\n });\n }\n if (cp.stderr) {\n cp.stderr.on('data', (chunk: Buffer) => {\n terminalBuffer.write(chunk);\n this.store.notify();\n });\n }\n\n // Wait for process to complete\n const queue = new Queue();\n if (cp.stdout) {\n queue.defer(oo.bind(null, cp.stdout, ['error', 'end', 'close']));\n }\n if (cp.stderr) {\n queue.defer(oo.bind(null, cp.stderr, ['error', 'end', 'close']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = null; // Not collecting raw output in inherit mode\n res.stderr = null;\n res.output = [null, null, null];\n this.store.updateProcess(id, { state: err ? 'error' : 'success' });\n\n this.onProcessComplete();\n err ? callback(err) : callback(null, res);\n });\n } else {\n // Non-inherit mode: collect output but don't display in UI\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof concatWritable> | null, stderr: null as ReturnType<typeof concatWritable> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = concatWritable((output) => {\n (outputs.stdout as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = concatWritable((output) => {\n (outputs.stderr as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n err ? callback(err) : callback(null, res);\n });\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup();\n }\n\n waitAndClose(callback?: () => void): void {\n if (this.closed) {\n callback?.();\n return;\n }\n\n if (callback) this.waitCallbacks.push(callback);\n\n if (this.runningCount === 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n // If runningCount > 0, will close when it hits 0 in onProcessComplete\n }\n\n private onProcessComplete(): void {\n this.runningCount--;\n if (this.runningCount === 0 && this.waitCallbacks.length > 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n }\n\n private closeAndCallWaitCallbacks(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup(() => {\n for (const cb of this.waitCallbacks) cb();\n this.waitCallbacks = [];\n });\n }\n\n private cleanup(onComplete?: () => void): void {\n // Signal exit to React component\n this.store.signalExit(() => {\n this.store.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n });\n\n // Wait for Ink to finish\n if (this.inkApp) {\n this.inkApp\n .waitUntilExit()\n .then(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n })\n .catch(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n });\n this.inkApp = null;\n } else {\n onComplete?.();\n }\n }\n}\n\nexport function createSession(options: SessionOptions = {}): Session {\n return new SessionImpl(options);\n}\n"],"names":["createSession","SessionImpl","options","inkApp","runningCount","closed","waitCallbacks","store","ProcessStore","isInteractive","interactive","terminalWidth","process","stdout","isTTY","render","App","maxFps","DEFAULT_MAX_FPS","spawn","command","args","spawnOptions","callback","Error","encoding","stdio","csOptions","cp","crossSpawn","worker","err","res","stderr","output","id","crypto","randomUUID","terminalBuffer","TerminalBuffer","addProcess","title","concat","formatArguments","join","state","lines","group","expanded","on","chunk","write","notify","queue","Queue","defer","oo","bind","await","updateProcess","onProcessComplete","outputs","concatWritable","toString","pipe","close","cleanup","waitAndClose","push","unsubscribe","subscribe","getShouldExit","closeAndCallWaitCallbacks","length","cb","onComplete","signalExit","reset","waitUntilExit","then","getExitCallback","catch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/session.tsx"],"sourcesContent":["import spawn, { crossSpawn, type SpawnResult } from 'cross-spawn-cb';\nimport crypto from 'crypto';\nimport { render } from 'ink';\nimport oo from 'on-one';\nimport Queue from 'queue-cb';\n\nimport App from './components/App.ts';\nimport { DEFAULT_MAX_FPS } from './constants.ts';\nimport concatWritable from './lib/concatWritable.ts';\nimport formatArguments from './lib/formatArguments.ts';\nimport { TerminalBuffer } from './lib/TerminalBuffer.ts';\nimport { ProcessStore } from './state/processStore.ts';\nimport type { ProcessOptions, SessionOptions, SpawnError, SpawnOptions, TerminalCallback } from './types.ts';\n\nexport interface Session {\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void;\n close(): void;\n waitAndClose(callback?: () => void): void;\n}\n\nclass SessionImpl implements Session {\n private store: ProcessStore;\n private inkApp: ReturnType<typeof render> | null = null;\n private runningCount = 0;\n private closed = false;\n private waitCallbacks: (() => void)[] = [];\n private isInteractive: boolean;\n private terminalWidth: number;\n\n constructor(options: SessionOptions = {}) {\n this.store = new ProcessStore(options);\n // Use a very wide buffer to prevent line wrapping in xterm\n // Actual display truncation is handled by Ink components\n this.terminalWidth = 10000;\n\n // Only render Ink when stdout is a real terminal\n // When piped (e.g., nested spawn-term), skip Ink to avoid cursor positioning artifacts\n if (process.stdout.isTTY) {\n this.inkApp = render(<App store={this.store} />, {\n maxFps: DEFAULT_MAX_FPS,\n });\n }\n\n // Interactive mode requires a TTY to capture user input (e.g., press 'q' to quit)\n // Force non-interactive when no UI is rendered, otherwise waitAndClose would hang\n this.isInteractive = this.inkApp ? (options.interactive ?? false) : false;\n }\n\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void {\n if (this.closed) {\n throw new Error('Session is closed');\n }\n\n const { encoding, stdio, ...csOptions } = spawnOptions;\n\n if (stdio === 'inherit') {\n // When Ink is not rendering (stdout not a TTY), pass output directly to stdout\n if (!this.inkApp) {\n const cp = crossSpawn(command, args, { ...csOptions, stdio: 'inherit' });\n spawn.worker(cp, csOptions, (err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = null;\n res.stderr = null;\n res.output = [null, null, null];\n err ? callback(err) : callback(null, res);\n });\n return;\n }\n\n this.runningCount++;\n const id = crypto.randomUUID();\n\n // Create terminal buffer for ANSI sequence interpretation\n const terminalBuffer = new TerminalBuffer(this.terminalWidth);\n\n this.store.addProcess({\n id,\n title: [command].concat(formatArguments(args)).join(' '),\n state: 'running',\n lines: [],\n terminalBuffer,\n group: options.group,\n expanded: options.expanded,\n });\n\n const cp = crossSpawn(command, args, csOptions);\n\n // Pipe stdout and stderr directly to terminal buffer\n // Both streams go to the same buffer to maintain correct ordering\n if (cp.stdout) {\n cp.stdout.on('data', (chunk: Buffer) => {\n terminalBuffer.write(chunk);\n this.store.notify();\n });\n }\n if (cp.stderr) {\n cp.stderr.on('data', (chunk: Buffer) => {\n terminalBuffer.write(chunk);\n this.store.notify();\n });\n }\n\n // Wait for process to complete\n const queue = new Queue();\n if (cp.stdout) {\n queue.defer(oo.bind(null, cp.stdout, ['error', 'end', 'close']));\n }\n if (cp.stderr) {\n queue.defer(oo.bind(null, cp.stderr, ['error', 'end', 'close']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = null; // Not collecting raw output in inherit mode\n res.stderr = null;\n res.output = [null, null, null];\n this.store.updateProcess(id, { state: err ? 'error' : 'success' });\n\n this.onProcessComplete();\n err ? callback(err) : callback(null, res);\n });\n } else {\n // Non-inherit mode: collect output but don't display in UI\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof concatWritable> | null, stderr: null as ReturnType<typeof concatWritable> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = concatWritable((output) => {\n (outputs.stdout as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = concatWritable((output) => {\n (outputs.stderr as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n err ? callback(err) : callback(null, res);\n });\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup();\n }\n\n waitAndClose(callback?: () => void): void {\n if (this.closed) {\n callback?.();\n return;\n }\n\n if (callback) this.waitCallbacks.push(callback);\n\n if (this.runningCount === 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n // If runningCount > 0, will close when it hits 0 in onProcessComplete\n }\n\n private onProcessComplete(): void {\n this.runningCount--;\n if (this.runningCount === 0 && this.waitCallbacks.length > 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n }\n\n private closeAndCallWaitCallbacks(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup(() => {\n for (const cb of this.waitCallbacks) cb();\n this.waitCallbacks = [];\n });\n }\n\n private cleanup(onComplete?: () => void): void {\n // Signal exit to React component\n this.store.signalExit(() => {\n this.store.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n });\n\n // Wait for Ink to finish\n if (this.inkApp) {\n this.inkApp\n .waitUntilExit()\n .then(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n })\n .catch(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n });\n this.inkApp = null;\n } else {\n onComplete?.();\n }\n }\n}\n\nexport function createSession(options: SessionOptions = {}): Session {\n return new SessionImpl(options);\n}\n"],"names":["createSession","SessionImpl","options","inkApp","runningCount","closed","waitCallbacks","store","ProcessStore","terminalWidth","process","stdout","isTTY","render","App","maxFps","DEFAULT_MAX_FPS","isInteractive","interactive","spawn","command","args","spawnOptions","callback","Error","encoding","stdio","csOptions","cp","crossSpawn","worker","err","res","stderr","output","id","crypto","randomUUID","terminalBuffer","TerminalBuffer","addProcess","title","concat","formatArguments","join","state","lines","group","expanded","on","chunk","write","notify","queue","Queue","defer","oo","bind","await","updateProcess","onProcessComplete","outputs","concatWritable","toString","pipe","close","cleanup","waitAndClose","push","unsubscribe","subscribe","getShouldExit","closeAndCallWaitCallbacks","length","cb","onComplete","signalExit","reset","waitUntilExit","then","getExitCallback","catch"],"mappings":"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"}
|
package/dist/esm/session.js
CHANGED
|
@@ -220,8 +220,6 @@ let SessionImpl = class SessionImpl {
|
|
|
220
220
|
this.closed = false;
|
|
221
221
|
this.waitCallbacks = [];
|
|
222
222
|
this.store = new ProcessStore(options);
|
|
223
|
-
var _options_interactive;
|
|
224
|
-
this.isInteractive = (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false;
|
|
225
223
|
// Use a very wide buffer to prevent line wrapping in xterm
|
|
226
224
|
// Actual display truncation is handled by Ink components
|
|
227
225
|
this.terminalWidth = 10000;
|
|
@@ -234,6 +232,10 @@ let SessionImpl = class SessionImpl {
|
|
|
234
232
|
maxFps: DEFAULT_MAX_FPS
|
|
235
233
|
});
|
|
236
234
|
}
|
|
235
|
+
var _options_interactive;
|
|
236
|
+
// Interactive mode requires a TTY to capture user input (e.g., press 'q' to quit)
|
|
237
|
+
// Force non-interactive when no UI is rendered, otherwise waitAndClose would hang
|
|
238
|
+
this.isInteractive = this.inkApp ? (_options_interactive = options.interactive) !== null && _options_interactive !== void 0 ? _options_interactive : false : false;
|
|
237
239
|
}
|
|
238
240
|
};
|
|
239
241
|
export function createSession(options = {}) {
|
package/dist/esm/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/session.tsx"],"sourcesContent":["import spawn, { crossSpawn, type SpawnResult } from 'cross-spawn-cb';\nimport crypto from 'crypto';\nimport { render } from 'ink';\nimport oo from 'on-one';\nimport Queue from 'queue-cb';\n\nimport App from './components/App.ts';\nimport { DEFAULT_MAX_FPS } from './constants.ts';\nimport concatWritable from './lib/concatWritable.ts';\nimport formatArguments from './lib/formatArguments.ts';\nimport { TerminalBuffer } from './lib/TerminalBuffer.ts';\nimport { ProcessStore } from './state/processStore.ts';\nimport type { ProcessOptions, SessionOptions, SpawnError, SpawnOptions, TerminalCallback } from './types.ts';\n\nexport interface Session {\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void;\n close(): void;\n waitAndClose(callback?: () => void): void;\n}\n\nclass SessionImpl implements Session {\n private store: ProcessStore;\n private inkApp: ReturnType<typeof render> | null = null;\n private runningCount = 0;\n private closed = false;\n private waitCallbacks: (() => void)[] = [];\n private isInteractive: boolean;\n private terminalWidth: number;\n\n constructor(options: SessionOptions = {}) {\n this.store = new ProcessStore(options);\n this.isInteractive = options.interactive ?? false;\n // Use a very wide buffer to prevent line wrapping in xterm\n // Actual display truncation is handled by Ink components\n this.terminalWidth = 10000;\n\n // Only render Ink when stdout is a real terminal\n // When piped (e.g., nested spawn-term), skip Ink to avoid cursor positioning artifacts\n if (process.stdout.isTTY) {\n this.inkApp = render(<App store={this.store} />, {\n maxFps: DEFAULT_MAX_FPS,\n });\n }\n }\n\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void {\n if (this.closed) {\n throw new Error('Session is closed');\n }\n\n const { encoding, stdio, ...csOptions } = spawnOptions;\n\n if (stdio === 'inherit') {\n // When Ink is not rendering (stdout not a TTY), pass output directly to stdout\n if (!this.inkApp) {\n const cp = crossSpawn(command, args, { ...csOptions, stdio: 'inherit' });\n spawn.worker(cp, csOptions, (err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = null;\n res.stderr = null;\n res.output = [null, null, null];\n err ? callback(err) : callback(null, res);\n });\n return;\n }\n\n this.runningCount++;\n const id = crypto.randomUUID();\n\n // Create terminal buffer for ANSI sequence interpretation\n const terminalBuffer = new TerminalBuffer(this.terminalWidth);\n\n this.store.addProcess({\n id,\n title: [command].concat(formatArguments(args)).join(' '),\n state: 'running',\n lines: [],\n terminalBuffer,\n group: options.group,\n expanded: options.expanded,\n });\n\n const cp = crossSpawn(command, args, csOptions);\n\n // Pipe stdout and stderr directly to terminal buffer\n // Both streams go to the same buffer to maintain correct ordering\n if (cp.stdout) {\n cp.stdout.on('data', (chunk: Buffer) => {\n terminalBuffer.write(chunk);\n this.store.notify();\n });\n }\n if (cp.stderr) {\n cp.stderr.on('data', (chunk: Buffer) => {\n terminalBuffer.write(chunk);\n this.store.notify();\n });\n }\n\n // Wait for process to complete\n const queue = new Queue();\n if (cp.stdout) {\n queue.defer(oo.bind(null, cp.stdout, ['error', 'end', 'close']));\n }\n if (cp.stderr) {\n queue.defer(oo.bind(null, cp.stderr, ['error', 'end', 'close']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = null; // Not collecting raw output in inherit mode\n res.stderr = null;\n res.output = [null, null, null];\n this.store.updateProcess(id, { state: err ? 'error' : 'success' });\n\n this.onProcessComplete();\n err ? callback(err) : callback(null, res);\n });\n } else {\n // Non-inherit mode: collect output but don't display in UI\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof concatWritable> | null, stderr: null as ReturnType<typeof concatWritable> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = concatWritable((output) => {\n (outputs.stdout as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = concatWritable((output) => {\n (outputs.stderr as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n err ? callback(err) : callback(null, res);\n });\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup();\n }\n\n waitAndClose(callback?: () => void): void {\n if (this.closed) {\n callback?.();\n return;\n }\n\n if (callback) this.waitCallbacks.push(callback);\n\n if (this.runningCount === 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n // If runningCount > 0, will close when it hits 0 in onProcessComplete\n }\n\n private onProcessComplete(): void {\n this.runningCount--;\n if (this.runningCount === 0 && this.waitCallbacks.length > 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n }\n\n private closeAndCallWaitCallbacks(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup(() => {\n for (const cb of this.waitCallbacks) cb();\n this.waitCallbacks = [];\n });\n }\n\n private cleanup(onComplete?: () => void): void {\n // Signal exit to React component\n this.store.signalExit(() => {\n this.store.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n });\n\n // Wait for Ink to finish\n if (this.inkApp) {\n this.inkApp\n .waitUntilExit()\n .then(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n })\n .catch(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n });\n this.inkApp = null;\n } else {\n onComplete?.();\n }\n }\n}\n\nexport function createSession(options: SessionOptions = {}): Session {\n return new SessionImpl(options);\n}\n"],"names":["spawn","crossSpawn","crypto","render","oo","Queue","App","DEFAULT_MAX_FPS","concatWritable","formatArguments","TerminalBuffer","ProcessStore","SessionImpl","command","args","spawnOptions","options","callback","closed","Error","encoding","stdio","csOptions","inkApp","cp","worker","err","res","stdout","stderr","output","runningCount","id","randomUUID","terminalBuffer","terminalWidth","store","addProcess","title","concat","join","state","lines","group","expanded","on","chunk","write","notify","queue","defer","bind","await","updateProcess","onProcessComplete","outputs","toString","pipe","close","cleanup","waitAndClose","waitCallbacks","push","isInteractive","unsubscribe","subscribe","getShouldExit","closeAndCallWaitCallbacks","length","cb","onComplete","signalExit","reset","process","waitUntilExit","then","getExitCallback","catch","interactive","isTTY","maxFps","createSession"],"mappings":";AAAA,OAAOA,SAASC,UAAU,QAA0B,iBAAiB;AACrE,OAAOC,YAAY,SAAS;AAC5B,SAASC,MAAM,QAAQ,MAAM;AAC7B,OAAOC,QAAQ,SAAS;AACxB,OAAOC,WAAW,WAAW;AAE7B,OAAOC,SAAS,sBAAsB;AACtC,SAASC,eAAe,QAAQ,iBAAiB;AACjD,OAAOC,oBAAoB,0BAA0B;AACrD,OAAOC,qBAAqB,2BAA2B;AACvD,SAASC,cAAc,QAAQ,0BAA0B;AACzD,SAASC,YAAY,QAAQ,0BAA0B;AASvD,IAAA,AAAMC,cAAN,MAAMA;IAyBJZ,MAAMa,OAAe,EAAEC,IAAc,EAAEC,YAA0B,EAAEC,OAAuB,EAAEC,QAA0B,EAAQ;QAC5H,IAAI,IAAI,CAACC,MAAM,EAAE;YACf,MAAM,IAAIC,MAAM;QAClB;QAEA,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAE,GAAGC,WAAW,GAAGP;QAE1C,IAAIM,UAAU,WAAW;YACvB,+EAA+E;YAC/E,IAAI,CAAC,IAAI,CAACE,MAAM,EAAE;gBAChB,MAAMC,KAAKvB,WAAWY,SAASC,MAAM;oBAAE,GAAGQ,SAAS;oBAAED,OAAO;gBAAU;gBACtErB,MAAMyB,MAAM,CAACD,IAAIF,WAAW,CAACI;oBAC3B,MAAMC,MAAOD,MAAMA,MAAM,CAAC;oBAC1BC,IAAIC,MAAM,GAAG;oBACbD,IAAIE,MAAM,GAAG;oBACbF,IAAIG,MAAM,GAAG;wBAAC;wBAAM;wBAAM;qBAAK;oBAC/BJ,MAAMT,SAASS,OAAOT,SAAS,MAAMU;gBACvC;gBACA;YACF;YAEA,IAAI,CAACI,YAAY;YACjB,MAAMC,KAAK9B,OAAO+B,UAAU;YAE5B,0DAA0D;YAC1D,MAAMC,iBAAiB,IAAIxB,eAAe,IAAI,CAACyB,aAAa;YAE5D,IAAI,CAACC,KAAK,CAACC,UAAU,CAAC;gBACpBL;gBACAM,OAAO;oBAACzB;iBAAQ,CAAC0B,MAAM,CAAC9B,gBAAgBK,OAAO0B,IAAI,CAAC;gBACpDC,OAAO;gBACPC,OAAO,EAAE;gBACTR;gBACAS,OAAO3B,QAAQ2B,KAAK;gBACpBC,UAAU5B,QAAQ4B,QAAQ;YAC5B;YAEA,MAAMpB,KAAKvB,WAAWY,SAASC,MAAMQ;YAErC,qDAAqD;YACrD,kEAAkE;YAClE,IAAIE,GAAGI,MAAM,EAAE;gBACbJ,GAAGI,MAAM,CAACiB,EAAE,CAAC,QAAQ,CAACC;oBACpBZ,eAAea,KAAK,CAACD;oBACrB,IAAI,CAACV,KAAK,CAACY,MAAM;gBACnB;YACF;YACA,IAAIxB,GAAGK,MAAM,EAAE;gBACbL,GAAGK,MAAM,CAACgB,EAAE,CAAC,QAAQ,CAACC;oBACpBZ,eAAea,KAAK,CAACD;oBACrB,IAAI,CAACV,KAAK,CAACY,MAAM;gBACnB;YACF;YAEA,+BAA+B;YAC/B,MAAMC,QAAQ,IAAI5C;YAClB,IAAImB,GAAGI,MAAM,EAAE;gBACbqB,MAAMC,KAAK,CAAC9C,GAAG+C,IAAI,CAAC,MAAM3B,GAAGI,MAAM,EAAE;oBAAC;oBAAS;oBAAO;iBAAQ;YAChE;YACA,IAAIJ,GAAGK,MAAM,EAAE;gBACboB,MAAMC,KAAK,CAAC9C,GAAG+C,IAAI,CAAC,MAAM3B,GAAGK,MAAM,EAAE;oBAAC;oBAAS;oBAAO;iBAAQ;YAChE;YACAoB,MAAMC,KAAK,CAAClD,MAAMyB,MAAM,CAAC0B,IAAI,CAAC,MAAM3B,IAAIF;YACxC2B,MAAMG,KAAK,CAAC,CAAC1B;gBACX,MAAMC,MAAOD,MAAMA,MAAM,CAAC;gBAC1BC,IAAIC,MAAM,GAAG,MAAM,4CAA4C;gBAC/DD,IAAIE,MAAM,GAAG;gBACbF,IAAIG,MAAM,GAAG;oBAAC;oBAAM;oBAAM;iBAAK;gBAC/B,IAAI,CAACM,KAAK,CAACiB,aAAa,CAACrB,IAAI;oBAAES,OAAOf,MAAM,UAAU;gBAAU;gBAEhE,IAAI,CAAC4B,iBAAiB;gBACtB5B,MAAMT,SAASS,OAAOT,SAAS,MAAMU;YACvC;QACF,OAAO;YACL,2DAA2D;YAC3D,MAAMH,KAAKvB,WAAWY,SAASC,MAAMQ;YACrC,MAAMiC,UAAU;gBAAE3B,QAAQ;gBAAkDC,QAAQ;YAAiD;YAErI,MAAMoB,QAAQ,IAAI5C;YAClB,IAAImB,GAAGI,MAAM,EAAE;gBACb2B,QAAQ3B,MAAM,GAAGpB,eAAe,CAACsB;oBAC9ByB,QAAQ3B,MAAM,CAAmCE,MAAM,GAAGA,OAAO0B,QAAQ,CAACpC,YAAY;gBACzF;gBACA6B,MAAMC,KAAK,CAAC9C,GAAG+C,IAAI,CAAC,MAAM3B,GAAGI,MAAM,CAAC6B,IAAI,CAACF,QAAQ3B,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACA,IAAIJ,GAAGK,MAAM,EAAE;gBACb0B,QAAQ1B,MAAM,GAAGrB,eAAe,CAACsB;oBAC9ByB,QAAQ1B,MAAM,CAAmCC,MAAM,GAAGA,OAAO0B,QAAQ,CAACpC,YAAY;gBACzF;gBACA6B,MAAMC,KAAK,CAAC9C,GAAG+C,IAAI,CAAC,MAAM3B,GAAGK,MAAM,CAAC4B,IAAI,CAACF,QAAQ1B,MAAM,GAAG;oBAAC;oBAAS;oBAAO;oBAAS;iBAAS;YAC/F;YACAoB,MAAMC,KAAK,CAAClD,MAAMyB,MAAM,CAAC0B,IAAI,CAAC,MAAM3B,IAAIF;YACxC2B,MAAMG,KAAK,CAAC,CAAC1B;gBACX,MAAMC,MAAOD,MAAMA,MAAM,CAAC;gBAC1BC,IAAIC,MAAM,GAAG2B,QAAQ3B,MAAM,GAAG,AAAC2B,QAAQ3B,MAAM,CAAmCE,MAAM,GAAG;gBACzFH,IAAIE,MAAM,GAAG0B,QAAQ1B,MAAM,GAAG,AAAC0B,QAAQ1B,MAAM,CAAmCC,MAAM,GAAG;gBACzFH,IAAIG,MAAM,GAAG;oBAACH,IAAIC,MAAM;oBAAED,IAAIE,MAAM;oBAAE;iBAAK;gBAC3CH,MAAMT,SAASS,OAAOT,SAAS,MAAMU;YACvC;QACF;IACF;IAEA+B,QAAc;QACZ,IAAI,IAAI,CAACxC,MAAM,EAAE;QACjB,IAAI,CAACA,MAAM,GAAG;QACd,IAAI,CAACyC,OAAO;IACd;IAEAC,aAAa3C,QAAqB,EAAQ;QACxC,IAAI,IAAI,CAACC,MAAM,EAAE;YACfD,qBAAAA,+BAAAA;YACA;QACF;QAEA,IAAIA,UAAU,IAAI,CAAC4C,aAAa,CAACC,IAAI,CAAC7C;QAEtC,IAAI,IAAI,CAACc,YAAY,KAAK,GAAG;YAC3B,IAAI,IAAI,CAACgC,aAAa,EAAE;gBACtB,yDAAyD;gBACzD,MAAMC,cAAc,IAAI,CAAC5B,KAAK,CAAC6B,SAAS,CAAC;oBACvC,IAAI,IAAI,CAAC7B,KAAK,CAAC8B,aAAa,IAAI;wBAC9BF;wBACA,IAAI,CAACG,yBAAyB;oBAChC;gBACF;YACF,OAAO;gBACL,IAAI,CAACA,yBAAyB;YAChC;QACF;IACA,sEAAsE;IACxE;IAEQb,oBAA0B;QAChC,IAAI,CAACvB,YAAY;QACjB,IAAI,IAAI,CAACA,YAAY,KAAK,KAAK,IAAI,CAAC8B,aAAa,CAACO,MAAM,GAAG,GAAG;YAC5D,IAAI,IAAI,CAACL,aAAa,EAAE;gBACtB,yDAAyD;gBACzD,MAAMC,cAAc,IAAI,CAAC5B,KAAK,CAAC6B,SAAS,CAAC;oBACvC,IAAI,IAAI,CAAC7B,KAAK,CAAC8B,aAAa,IAAI;wBAC9BF;wBACA,IAAI,CAACG,yBAAyB;oBAChC;gBACF;YACF,OAAO;gBACL,IAAI,CAACA,yBAAyB;YAChC;QACF;IACF;IAEQA,4BAAkC;QACxC,IAAI,IAAI,CAACjD,MAAM,EAAE;QACjB,IAAI,CAACA,MAAM,GAAG;QACd,IAAI,CAACyC,OAAO,CAAC;YACX,KAAK,MAAMU,MAAM,IAAI,CAACR,aAAa,CAAEQ;YACrC,IAAI,CAACR,aAAa,GAAG,EAAE;QACzB;IACF;IAEQF,QAAQW,UAAuB,EAAQ;QAC7C,iCAAiC;QACjC,IAAI,CAAClC,KAAK,CAACmC,UAAU,CAAC;YACpB,IAAI,CAACnC,KAAK,CAACoC,KAAK;YAChBC,QAAQ7C,MAAM,CAACmB,KAAK,CAAC,cAAc,cAAc;QACnD;QAEA,yBAAyB;QACzB,IAAI,IAAI,CAACxB,MAAM,EAAE;YACf,IAAI,CAACA,MAAM,CACRmD,aAAa,GACbC,IAAI,CAAC;gBACJ,MAAMN,KAAK,IAAI,CAACjC,KAAK,CAACwC,eAAe;gBACrCP,eAAAA,yBAAAA;gBACAC,uBAAAA,iCAAAA;YACF,GACCO,KAAK,CAAC;gBACL,MAAMR,KAAK,IAAI,CAACjC,KAAK,CAACwC,eAAe;gBACrCP,eAAAA,yBAAAA;gBACAC,uBAAAA,iCAAAA;YACF;YACF,IAAI,CAAC/C,MAAM,GAAG;QAChB,OAAO;YACL+C,uBAAAA,iCAAAA;QACF;IACF;IAvMA,YAAYtD,UAA0B,CAAC,CAAC,CAAE;aAPlCO,SAA2C;aAC3CQ,eAAe;aACfb,SAAS;aACT2C,gBAAgC,EAAE;QAKxC,IAAI,CAACzB,KAAK,GAAG,IAAIzB,aAAaK;YACTA;QAArB,IAAI,CAAC+C,aAAa,GAAG/C,CAAAA,uBAAAA,QAAQ8D,WAAW,cAAnB9D,kCAAAA,uBAAuB;QAC5C,2DAA2D;QAC3D,yDAAyD;QACzD,IAAI,CAACmB,aAAa,GAAG;QAErB,iDAAiD;QACjD,uFAAuF;QACvF,IAAIsC,QAAQ7C,MAAM,CAACmD,KAAK,EAAE;YACxB,IAAI,CAACxD,MAAM,GAAGpB,qBAAO,KAACG;gBAAI8B,OAAO,IAAI,CAACA,KAAK;gBAAM;gBAC/C4C,QAAQzE;YACV;QACF;IACF;AA0LF;AAEA,OAAO,SAAS0E,cAAcjE,UAA0B,CAAC,CAAC;IACxD,OAAO,IAAIJ,YAAYI;AACzB"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/session.tsx"],"sourcesContent":["import spawn, { crossSpawn, type SpawnResult } from 'cross-spawn-cb';\nimport crypto from 'crypto';\nimport { render } from 'ink';\nimport oo from 'on-one';\nimport Queue from 'queue-cb';\n\nimport App from './components/App.ts';\nimport { DEFAULT_MAX_FPS } from './constants.ts';\nimport concatWritable from './lib/concatWritable.ts';\nimport formatArguments from './lib/formatArguments.ts';\nimport { TerminalBuffer } from './lib/TerminalBuffer.ts';\nimport { ProcessStore } from './state/processStore.ts';\nimport type { ProcessOptions, SessionOptions, SpawnError, SpawnOptions, TerminalCallback } from './types.ts';\n\nexport interface Session {\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void;\n close(): void;\n waitAndClose(callback?: () => void): void;\n}\n\nclass SessionImpl implements Session {\n private store: ProcessStore;\n private inkApp: ReturnType<typeof render> | null = null;\n private runningCount = 0;\n private closed = false;\n private waitCallbacks: (() => void)[] = [];\n private isInteractive: boolean;\n private terminalWidth: number;\n\n constructor(options: SessionOptions = {}) {\n this.store = new ProcessStore(options);\n // Use a very wide buffer to prevent line wrapping in xterm\n // Actual display truncation is handled by Ink components\n this.terminalWidth = 10000;\n\n // Only render Ink when stdout is a real terminal\n // When piped (e.g., nested spawn-term), skip Ink to avoid cursor positioning artifacts\n if (process.stdout.isTTY) {\n this.inkApp = render(<App store={this.store} />, {\n maxFps: DEFAULT_MAX_FPS,\n });\n }\n\n // Interactive mode requires a TTY to capture user input (e.g., press 'q' to quit)\n // Force non-interactive when no UI is rendered, otherwise waitAndClose would hang\n this.isInteractive = this.inkApp ? (options.interactive ?? false) : false;\n }\n\n spawn(command: string, args: string[], spawnOptions: SpawnOptions, options: ProcessOptions, callback: TerminalCallback): void {\n if (this.closed) {\n throw new Error('Session is closed');\n }\n\n const { encoding, stdio, ...csOptions } = spawnOptions;\n\n if (stdio === 'inherit') {\n // When Ink is not rendering (stdout not a TTY), pass output directly to stdout\n if (!this.inkApp) {\n const cp = crossSpawn(command, args, { ...csOptions, stdio: 'inherit' });\n spawn.worker(cp, csOptions, (err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = null;\n res.stderr = null;\n res.output = [null, null, null];\n err ? callback(err) : callback(null, res);\n });\n return;\n }\n\n this.runningCount++;\n const id = crypto.randomUUID();\n\n // Create terminal buffer for ANSI sequence interpretation\n const terminalBuffer = new TerminalBuffer(this.terminalWidth);\n\n this.store.addProcess({\n id,\n title: [command].concat(formatArguments(args)).join(' '),\n state: 'running',\n lines: [],\n terminalBuffer,\n group: options.group,\n expanded: options.expanded,\n });\n\n const cp = crossSpawn(command, args, csOptions);\n\n // Pipe stdout and stderr directly to terminal buffer\n // Both streams go to the same buffer to maintain correct ordering\n if (cp.stdout) {\n cp.stdout.on('data', (chunk: Buffer) => {\n terminalBuffer.write(chunk);\n this.store.notify();\n });\n }\n if (cp.stderr) {\n cp.stderr.on('data', (chunk: Buffer) => {\n terminalBuffer.write(chunk);\n this.store.notify();\n });\n }\n\n // Wait for process to complete\n const queue = new Queue();\n if (cp.stdout) {\n queue.defer(oo.bind(null, cp.stdout, ['error', 'end', 'close']));\n }\n if (cp.stderr) {\n queue.defer(oo.bind(null, cp.stderr, ['error', 'end', 'close']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = null; // Not collecting raw output in inherit mode\n res.stderr = null;\n res.output = [null, null, null];\n this.store.updateProcess(id, { state: err ? 'error' : 'success' });\n\n this.onProcessComplete();\n err ? callback(err) : callback(null, res);\n });\n } else {\n // Non-inherit mode: collect output but don't display in UI\n const cp = crossSpawn(command, args, csOptions);\n const outputs = { stdout: null as ReturnType<typeof concatWritable> | null, stderr: null as ReturnType<typeof concatWritable> | null };\n\n const queue = new Queue();\n if (cp.stdout) {\n outputs.stdout = concatWritable((output) => {\n (outputs.stdout as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stdout.pipe(outputs.stdout), ['error', 'end', 'close', 'finish']));\n }\n if (cp.stderr) {\n outputs.stderr = concatWritable((output) => {\n (outputs.stderr as unknown as { output: string }).output = output.toString(encoding || 'utf8');\n });\n queue.defer(oo.bind(null, cp.stderr.pipe(outputs.stderr), ['error', 'end', 'close', 'finish']));\n }\n queue.defer(spawn.worker.bind(null, cp, csOptions));\n queue.await((err?: SpawnError) => {\n const res = (err ? err : {}) as SpawnResult;\n res.stdout = outputs.stdout ? (outputs.stdout as unknown as { output: string }).output : null;\n res.stderr = outputs.stderr ? (outputs.stderr as unknown as { output: string }).output : null;\n res.output = [res.stdout, res.stderr, null];\n err ? callback(err) : callback(null, res);\n });\n }\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup();\n }\n\n waitAndClose(callback?: () => void): void {\n if (this.closed) {\n callback?.();\n return;\n }\n\n if (callback) this.waitCallbacks.push(callback);\n\n if (this.runningCount === 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n // If runningCount > 0, will close when it hits 0 in onProcessComplete\n }\n\n private onProcessComplete(): void {\n this.runningCount--;\n if (this.runningCount === 0 && this.waitCallbacks.length > 0) {\n if (this.isInteractive) {\n // In interactive mode, wait for user to quit (press 'q')\n const unsubscribe = this.store.subscribe(() => {\n if (this.store.getShouldExit()) {\n unsubscribe();\n this.closeAndCallWaitCallbacks();\n }\n });\n } else {\n this.closeAndCallWaitCallbacks();\n }\n }\n }\n\n private closeAndCallWaitCallbacks(): void {\n if (this.closed) return;\n this.closed = true;\n this.cleanup(() => {\n for (const cb of this.waitCallbacks) cb();\n this.waitCallbacks = [];\n });\n }\n\n private cleanup(onComplete?: () => void): void {\n // Signal exit to React component\n this.store.signalExit(() => {\n this.store.reset();\n process.stdout.write('\\x1b[?25h'); // show cursor\n });\n\n // Wait for Ink to finish\n if (this.inkApp) {\n this.inkApp\n .waitUntilExit()\n .then(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n })\n .catch(() => {\n const cb = this.store.getExitCallback();\n cb?.();\n onComplete?.();\n });\n this.inkApp = null;\n } else {\n onComplete?.();\n }\n }\n}\n\nexport function createSession(options: SessionOptions = {}): Session {\n return new SessionImpl(options);\n}\n"],"names":["spawn","crossSpawn","crypto","render","oo","Queue","App","DEFAULT_MAX_FPS","concatWritable","formatArguments","TerminalBuffer","ProcessStore","SessionImpl","command","args","spawnOptions","options","callback","closed","Error","encoding","stdio","csOptions","inkApp","cp","worker","err","res","stdout","stderr","output","runningCount","id","randomUUID","terminalBuffer","terminalWidth","store","addProcess","title","concat","join","state","lines","group","expanded","on","chunk","write","notify","queue","defer","bind","await","updateProcess","onProcessComplete","outputs","toString","pipe","close","cleanup","waitAndClose","waitCallbacks","push","isInteractive","unsubscribe","subscribe","getShouldExit","closeAndCallWaitCallbacks","length","cb","onComplete","signalExit","reset","process","waitUntilExit","then","getExitCallback","catch","isTTY","maxFps","interactive","createSession"],"mappings":"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"}
|