node-pty 1.1.0-beta3 → 1.1.0-beta30
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 +6 -5
- package/binding.gyp +4 -15
- package/deps/winpty/src/agent/Agent.cc +11 -3
- package/lib/unixTerminal.js +4 -27
- package/lib/unixTerminal.js.map +1 -1
- package/lib/unixTerminal.test.js +3 -5
- package/lib/unixTerminal.test.js.map +1 -1
- package/lib/windowsConoutConnection.js +0 -4
- package/lib/windowsConoutConnection.js.map +1 -1
- package/lib/windowsPtyAgent.js +41 -21
- package/lib/windowsPtyAgent.js.map +1 -1
- package/lib/windowsTerminal.js +1 -1
- package/lib/windowsTerminal.js.map +1 -1
- package/lib/windowsTerminal.test.js +38 -26
- package/lib/windowsTerminal.test.js.map +1 -1
- package/lib/worker/conoutSocketWorker.js +13 -10
- package/lib/worker/conoutSocketWorker.js.map +1 -1
- package/package.json +4 -2
- package/scripts/increment-version.js +54 -0
- package/scripts/post-install.js +51 -10
- package/scripts/publish.js +7 -4
- package/src/interfaces.ts +1 -0
- package/src/native.d.ts +5 -5
- package/src/unix/pty.cc +241 -284
- package/src/unixTerminal.test.ts +3 -6
- package/src/unixTerminal.ts +4 -18
- package/src/win/conpty.cc +264 -185
- package/src/win/conpty.h +41 -0
- package/src/win/conpty_console_list.cc +16 -15
- package/src/win/path_util.cc +39 -36
- package/src/win/path_util.h +5 -2
- package/src/win/winpty.cc +108 -151
- package/src/windowsConoutConnection.ts +0 -4
- package/src/windowsPtyAgent.ts +38 -20
- package/src/windowsTerminal.test.ts +43 -32
- package/src/windowsTerminal.ts +1 -1
- package/src/worker/conoutSocketWorker.ts +14 -11
- package/third_party/conpty/1.22.250204002/win10-arm64/OpenConsole.exe +0 -0
- package/third_party/conpty/1.22.250204002/win10-arm64/conpty.dll +0 -0
- package/third_party/conpty/1.22.250204002/win10-x64/OpenConsole.exe +0 -0
- package/third_party/conpty/1.22.250204002/win10-x64/conpty.dll +0 -0
- package/typings/node-pty.d.ts +8 -0
package/src/windowsPtyAgent.ts
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
import * as fs from 'fs';
|
|
8
8
|
import * as os from 'os';
|
|
9
9
|
import * as path from 'path';
|
|
10
|
+
import { fork } from 'child_process';
|
|
10
11
|
import { Socket } from 'net';
|
|
11
12
|
import { ArgvOrCommandLine } from './types';
|
|
12
|
-
import { fork } from 'child_process';
|
|
13
13
|
import { ConoutConnection } from './windowsConoutConnection';
|
|
14
14
|
|
|
15
15
|
let conptyNative: IConptyNative;
|
|
@@ -54,6 +54,7 @@ export class WindowsPtyAgent {
|
|
|
54
54
|
rows: number,
|
|
55
55
|
debug: boolean,
|
|
56
56
|
private _useConpty: boolean | undefined,
|
|
57
|
+
private _useConptyDll: boolean = false,
|
|
57
58
|
conptyInheritCursor: boolean = false
|
|
58
59
|
) {
|
|
59
60
|
if (this._useConpty === undefined || this._useConpty === true) {
|
|
@@ -99,7 +100,7 @@ export class WindowsPtyAgent {
|
|
|
99
100
|
// Open pty session.
|
|
100
101
|
let term: IConptyProcess | IWinptyProcess;
|
|
101
102
|
if (this._useConpty) {
|
|
102
|
-
term = (this._ptyNative as IConptyNative).startProcess(file, cols, rows, debug, this._generatePipeName(), conptyInheritCursor);
|
|
103
|
+
term = (this._ptyNative as IConptyNative).startProcess(file, cols, rows, debug, this._generatePipeName(), conptyInheritCursor, this._useConptyDll);
|
|
103
104
|
} else {
|
|
104
105
|
term = (this._ptyNative as IWinptyNative).startProcess(file, commandLine, env, cwd, cols, rows, debug);
|
|
105
106
|
this._pid = (term as IWinptyProcess).pid;
|
|
@@ -134,7 +135,7 @@ export class WindowsPtyAgent {
|
|
|
134
135
|
this._inSocket.setEncoding('utf8');
|
|
135
136
|
|
|
136
137
|
if (this._useConpty) {
|
|
137
|
-
const connect = (this._ptyNative as IConptyNative).connect(this._pty, commandLine, cwd, env, c => this._$onProcessExit(c));
|
|
138
|
+
const connect = (this._ptyNative as IConptyNative).connect(this._pty, commandLine, cwd, env, this._useConptyDll, c => this._$onProcessExit(c));
|
|
138
139
|
this._innerPid = connect.pid;
|
|
139
140
|
}
|
|
140
141
|
}
|
|
@@ -144,33 +145,43 @@ export class WindowsPtyAgent {
|
|
|
144
145
|
if (this._exitCode !== undefined) {
|
|
145
146
|
throw new Error('Cannot resize a pty that has already exited');
|
|
146
147
|
}
|
|
147
|
-
this._ptyNative.resize(this._pty, cols, rows);
|
|
148
|
+
(this._ptyNative as IConptyNative).resize(this._pty, cols, rows, this._useConptyDll);
|
|
148
149
|
return;
|
|
149
150
|
}
|
|
150
|
-
this._ptyNative.resize(this._pid, cols, rows);
|
|
151
|
+
(this._ptyNative as IWinptyNative).resize(this._pid, cols, rows);
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
public clear(): void {
|
|
154
155
|
if (this._useConpty) {
|
|
155
|
-
(this._ptyNative as IConptyNative).clear(this._pty);
|
|
156
|
+
(this._ptyNative as IConptyNative).clear(this._pty, this._useConptyDll);
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
public kill(): void {
|
|
160
|
-
this._inSocket.readable = false;
|
|
161
|
-
this._outSocket.readable = false;
|
|
162
161
|
// Tell the agent to kill the pty, this releases handles to the process
|
|
163
162
|
if (this._useConpty) {
|
|
164
|
-
this.
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
163
|
+
if (!this._useConptyDll) {
|
|
164
|
+
this._inSocket.readable = false;
|
|
165
|
+
this._outSocket.readable = false;
|
|
166
|
+
this._getConsoleProcessList().then(consoleProcessList => {
|
|
167
|
+
consoleProcessList.forEach((pid: number) => {
|
|
168
|
+
try {
|
|
169
|
+
process.kill(pid);
|
|
170
|
+
} catch (e) {
|
|
171
|
+
// Ignore if process cannot be found (kill ESRCH error)
|
|
172
|
+
}
|
|
173
|
+
});
|
|
171
174
|
});
|
|
172
|
-
(this._ptyNative as IConptyNative).kill(this._pty);
|
|
173
|
-
|
|
175
|
+
(this._ptyNative as IConptyNative).kill(this._pty, this._useConptyDll);
|
|
176
|
+
this._conoutSocketWorker.dispose();
|
|
177
|
+
} else {
|
|
178
|
+
// Close the input write handle to signal the end of session.
|
|
179
|
+
this._inSocket.destroy();
|
|
180
|
+
(this._ptyNative as IConptyNative).kill(this._pty, this._useConptyDll);
|
|
181
|
+
this._outSocket.on('data', () => {
|
|
182
|
+
this._conoutSocketWorker.dispose();
|
|
183
|
+
});
|
|
184
|
+
}
|
|
174
185
|
} else {
|
|
175
186
|
// Because pty.kill closes the handle, it will kill most processes by itself.
|
|
176
187
|
// Process IDs can be reused as soon as all handles to them are
|
|
@@ -188,7 +199,6 @@ export class WindowsPtyAgent {
|
|
|
188
199
|
}
|
|
189
200
|
});
|
|
190
201
|
}
|
|
191
|
-
this._conoutSocketWorker.dispose();
|
|
192
202
|
}
|
|
193
203
|
|
|
194
204
|
private _getConsoleProcessList(): Promise<number[]> {
|
|
@@ -232,11 +242,16 @@ export class WindowsPtyAgent {
|
|
|
232
242
|
*/
|
|
233
243
|
private _$onProcessExit(exitCode: number): void {
|
|
234
244
|
this._exitCode = exitCode;
|
|
235
|
-
this.
|
|
236
|
-
|
|
245
|
+
if (!this._useConptyDll) {
|
|
246
|
+
this._flushDataAndCleanUp();
|
|
247
|
+
this._outSocket.on('data', () => this._flushDataAndCleanUp());
|
|
248
|
+
}
|
|
237
249
|
}
|
|
238
250
|
|
|
239
251
|
private _flushDataAndCleanUp(): void {
|
|
252
|
+
if (this._useConptyDll) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
240
255
|
if (this._closeTimeout) {
|
|
241
256
|
clearTimeout(this._closeTimeout);
|
|
242
257
|
}
|
|
@@ -244,6 +259,9 @@ export class WindowsPtyAgent {
|
|
|
244
259
|
}
|
|
245
260
|
|
|
246
261
|
private _cleanUpProcess(): void {
|
|
262
|
+
if (this._useConptyDll) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
247
265
|
this._inSocket.readable = false;
|
|
248
266
|
this._outSocket.readable = false;
|
|
249
267
|
this._outSocket.destroy();
|
|
@@ -26,13 +26,16 @@ function pollForProcessState(desiredState: IProcessState, intervalMs: number = 1
|
|
|
26
26
|
psList({ all: true }).then(ps => {
|
|
27
27
|
let success = true;
|
|
28
28
|
const pids = Object.keys(desiredState).map(k => parseInt(k, 10));
|
|
29
|
+
console.log('expected pids', JSON.stringify(pids));
|
|
29
30
|
pids.forEach(pid => {
|
|
30
31
|
if (desiredState[pid]) {
|
|
31
32
|
if (!ps.some(p => p.pid === pid)) {
|
|
33
|
+
console.log(`pid ${pid} does not exist`);
|
|
32
34
|
success = false;
|
|
33
35
|
}
|
|
34
36
|
} else {
|
|
35
37
|
if (ps.some(p => p.pid === pid)) {
|
|
38
|
+
console.log(`pid ${pid} still exists`);
|
|
36
39
|
success = false;
|
|
37
40
|
}
|
|
38
41
|
}
|
|
@@ -71,6 +74,7 @@ function pollForProcessTreeSize(pid: number, size: number, intervalMs: number =
|
|
|
71
74
|
}).forEach(p => openList.push(p));
|
|
72
75
|
list.push(current);
|
|
73
76
|
}
|
|
77
|
+
console.log('list', JSON.stringify(list));
|
|
74
78
|
const success = list.length === size;
|
|
75
79
|
if (success) {
|
|
76
80
|
clearInterval(interval);
|
|
@@ -88,53 +92,55 @@ function pollForProcessTreeSize(pid: number, size: number, intervalMs: number =
|
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
if (process.platform === 'win32') {
|
|
91
|
-
[true, false].forEach((useConpty) => {
|
|
92
|
-
describe(`WindowsTerminal (useConpty = ${useConpty})`, () => {
|
|
95
|
+
[[false, false], [true, false], [true, true]].forEach(([useConpty, useConptyDll]) => {
|
|
96
|
+
describe(`WindowsTerminal (useConpty = ${useConpty}, useConptyDll = ${useConptyDll})`, () => {
|
|
93
97
|
describe('kill', () => {
|
|
94
|
-
it('should not crash parent process', (done)
|
|
95
|
-
|
|
98
|
+
it('should not crash parent process', function (done) {
|
|
99
|
+
this.timeout(20000);
|
|
100
|
+
const term = new WindowsTerminal('cmd.exe', [], { useConpty, useConptyDll });
|
|
101
|
+
term.on('exit', () => done());
|
|
96
102
|
term.kill();
|
|
97
|
-
// Add done call to deferred function queue to ensure the kill call has completed
|
|
98
|
-
(<any>term)._defer(done);
|
|
99
103
|
});
|
|
100
104
|
it('should kill the process tree', function (done: Mocha.Done): void {
|
|
101
|
-
this.timeout(
|
|
102
|
-
const term = new WindowsTerminal('cmd.exe', [], { useConpty });
|
|
105
|
+
this.timeout(20000);
|
|
106
|
+
const term = new WindowsTerminal('cmd.exe', [], { useConpty, useConptyDll });
|
|
103
107
|
// Start sub-processes
|
|
104
108
|
term.write('powershell.exe\r');
|
|
105
|
-
term.write('notepad.exe\r');
|
|
106
109
|
term.write('node.exe\r');
|
|
107
|
-
|
|
110
|
+
console.log('start poll for tree size');
|
|
111
|
+
pollForProcessTreeSize(term.pid, 3, 500, 5000).then(list => {
|
|
108
112
|
assert.strictEqual(list[0].name.toLowerCase(), 'cmd.exe');
|
|
109
113
|
assert.strictEqual(list[1].name.toLowerCase(), 'powershell.exe');
|
|
110
|
-
assert.strictEqual(list[2].name.toLowerCase(), '
|
|
111
|
-
assert.strictEqual(list[3].name.toLowerCase(), 'node.exe');
|
|
114
|
+
assert.strictEqual(list[2].name.toLowerCase(), 'node.exe');
|
|
112
115
|
term.kill();
|
|
113
116
|
const desiredState: IProcessState = {};
|
|
114
117
|
desiredState[list[0].pid] = false;
|
|
115
118
|
desiredState[list[1].pid] = false;
|
|
116
|
-
desiredState[list[2].pid] =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
done();
|
|
119
|
+
desiredState[list[2].pid] = false;
|
|
120
|
+
term.on('exit', () => {
|
|
121
|
+
pollForProcessState(desiredState, 1000, 5000).then(() => {
|
|
122
|
+
done();
|
|
123
|
+
});
|
|
122
124
|
});
|
|
123
125
|
});
|
|
124
126
|
});
|
|
125
127
|
});
|
|
126
128
|
|
|
127
129
|
describe('resize', () => {
|
|
128
|
-
it('should throw a non-native exception when resizing an invalid value', () => {
|
|
129
|
-
const term = new WindowsTerminal('cmd.exe', [], { useConpty });
|
|
130
|
+
it('should throw a non-native exception when resizing an invalid value', (done) => {
|
|
131
|
+
const term = new WindowsTerminal('cmd.exe', [], { useConpty, useConptyDll });
|
|
130
132
|
assert.throws(() => term.resize(-1, -1));
|
|
131
133
|
assert.throws(() => term.resize(0, 0));
|
|
132
134
|
assert.doesNotThrow(() => term.resize(1, 1));
|
|
135
|
+
term.on('exit', () => {
|
|
136
|
+
done();
|
|
137
|
+
});
|
|
138
|
+
term.kill();
|
|
133
139
|
});
|
|
134
140
|
it('should throw a non-native exception when resizing a killed terminal', (done) => {
|
|
135
|
-
const term = new WindowsTerminal('cmd.exe', [], { useConpty });
|
|
141
|
+
const term = new WindowsTerminal('cmd.exe', [], { useConpty, useConptyDll });
|
|
136
142
|
(<any>term)._defer(() => {
|
|
137
|
-
term.
|
|
143
|
+
term.once('exit', () => {
|
|
138
144
|
assert.throws(() => term.resize(1, 1));
|
|
139
145
|
done();
|
|
140
146
|
});
|
|
@@ -144,7 +150,8 @@ if (process.platform === 'win32') {
|
|
|
144
150
|
});
|
|
145
151
|
|
|
146
152
|
describe('Args as CommandLine', () => {
|
|
147
|
-
it('should not fail running a file containing a space in the path', (done)
|
|
153
|
+
it('should not fail running a file containing a space in the path', function (done) {
|
|
154
|
+
this.timeout(10000);
|
|
148
155
|
const spaceFolder = path.resolve(__dirname, '..', 'fixtures', 'space folder');
|
|
149
156
|
if (!fs.existsSync(spaceFolder)) {
|
|
150
157
|
fs.mkdirSync(spaceFolder);
|
|
@@ -158,7 +165,7 @@ if (process.platform === 'win32') {
|
|
|
158
165
|
// Skip test if git bash isn't installed
|
|
159
166
|
return;
|
|
160
167
|
}
|
|
161
|
-
const term = new WindowsTerminal(cmdCopiedPath, '/c echo "hello world"', { useConpty });
|
|
168
|
+
const term = new WindowsTerminal(cmdCopiedPath, '/c echo "hello world"', { useConpty, useConptyDll });
|
|
162
169
|
let result = '';
|
|
163
170
|
term.on('data', (data) => {
|
|
164
171
|
result += data;
|
|
@@ -171,8 +178,9 @@ if (process.platform === 'win32') {
|
|
|
171
178
|
});
|
|
172
179
|
|
|
173
180
|
describe('env', () => {
|
|
174
|
-
it('should set environment variables of the shell', (done)
|
|
175
|
-
|
|
181
|
+
it('should set environment variables of the shell', function (done) {
|
|
182
|
+
this.timeout(10000);
|
|
183
|
+
const term = new WindowsTerminal('cmd.exe', '/C echo %FOO%', { useConpty, useConptyDll, env: { FOO: 'BAR' }});
|
|
176
184
|
let result = '';
|
|
177
185
|
term.on('data', (data) => {
|
|
178
186
|
result += data;
|
|
@@ -185,16 +193,18 @@ if (process.platform === 'win32') {
|
|
|
185
193
|
});
|
|
186
194
|
|
|
187
195
|
describe('On close', () => {
|
|
188
|
-
it('should return process zero exit codes', (done)
|
|
189
|
-
|
|
196
|
+
it('should return process zero exit codes', function (done) {
|
|
197
|
+
this.timeout(10000);
|
|
198
|
+
const term = new WindowsTerminal('cmd.exe', '/C exit', { useConpty, useConptyDll });
|
|
190
199
|
term.on('exit', (code) => {
|
|
191
200
|
assert.strictEqual(code, 0);
|
|
192
201
|
done();
|
|
193
202
|
});
|
|
194
203
|
});
|
|
195
204
|
|
|
196
|
-
it('should return process non-zero exit codes', (done)
|
|
197
|
-
|
|
205
|
+
it('should return process non-zero exit codes', function (done) {
|
|
206
|
+
this.timeout(10000);
|
|
207
|
+
const term = new WindowsTerminal('cmd.exe', '/C exit 2', { useConpty, useConptyDll });
|
|
198
208
|
term.on('exit', (code) => {
|
|
199
209
|
assert.strictEqual(code, 2);
|
|
200
210
|
done();
|
|
@@ -203,8 +213,9 @@ if (process.platform === 'win32') {
|
|
|
203
213
|
});
|
|
204
214
|
|
|
205
215
|
describe('Write', () => {
|
|
206
|
-
it('should accept input', (done)
|
|
207
|
-
|
|
216
|
+
it('should accept input', function (done) {
|
|
217
|
+
this.timeout(10000);
|
|
218
|
+
const term = new WindowsTerminal('cmd.exe', '', { useConpty, useConptyDll });
|
|
208
219
|
term.write('exit\r');
|
|
209
220
|
term.on('exit', () => {
|
|
210
221
|
done();
|
package/src/windowsTerminal.ts
CHANGED
|
@@ -48,7 +48,7 @@ export class WindowsTerminal extends Terminal {
|
|
|
48
48
|
this._deferreds = [];
|
|
49
49
|
|
|
50
50
|
// Create new termal.
|
|
51
|
-
this._agent = new WindowsPtyAgent(file, args, parsedEnv, cwd, this._cols, this._rows, false, opt.useConpty, opt.conptyInheritCursor);
|
|
51
|
+
this._agent = new WindowsPtyAgent(file, args, parsedEnv, cwd, this._cols, this._rows, false, opt.useConpty, opt.useConptyDll, opt.conptyInheritCursor);
|
|
52
52
|
this._socket = this._agent.outSocket;
|
|
53
53
|
|
|
54
54
|
// Not available until `ready` event emitted.
|
|
@@ -4,19 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
import { parentPort, workerData } from 'worker_threads';
|
|
6
6
|
import { Socket, createServer } from 'net';
|
|
7
|
+
import * as fs from 'fs';
|
|
7
8
|
import { ConoutWorkerMessage, IWorkerData, getWorkerPipeName } from '../shared/conout';
|
|
8
9
|
|
|
9
10
|
const conoutPipeName = (workerData as IWorkerData).conoutPipeName;
|
|
10
|
-
const
|
|
11
|
+
const outSocketFD = fs.openSync(conoutPipeName, 'r');
|
|
12
|
+
const conoutSocket = new Socket({
|
|
13
|
+
fd: outSocketFD,
|
|
14
|
+
readable: true
|
|
15
|
+
});
|
|
11
16
|
conoutSocket.setEncoding('utf8');
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
conoutSocket.pipe(workerSocket);
|
|
15
|
-
});
|
|
16
|
-
server.listen(getWorkerPipeName(conoutPipeName));
|
|
17
|
-
|
|
18
|
-
if (!parentPort) {
|
|
19
|
-
throw new Error('worker_threads parentPort is null');
|
|
20
|
-
}
|
|
21
|
-
parentPort.postMessage(ConoutWorkerMessage.READY);
|
|
17
|
+
const server = createServer(workerSocket => {
|
|
18
|
+
conoutSocket.pipe(workerSocket);
|
|
22
19
|
});
|
|
20
|
+
server.listen(getWorkerPipeName(conoutPipeName));
|
|
21
|
+
|
|
22
|
+
if (!parentPort) {
|
|
23
|
+
throw new Error('worker_threads parentPort is null');
|
|
24
|
+
}
|
|
25
|
+
parentPort.postMessage(ConoutWorkerMessage.READY);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/typings/node-pty.d.ts
CHANGED
|
@@ -95,6 +95,14 @@ declare module 'node-pty' {
|
|
|
95
95
|
*/
|
|
96
96
|
useConpty?: boolean;
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* (EXPERIMENTAL)
|
|
100
|
+
*
|
|
101
|
+
* Whether to use the conpty.dll shipped with the node-pty package instead of the one built into
|
|
102
|
+
* Windows. Defaults to false.
|
|
103
|
+
*/
|
|
104
|
+
useConptyDll?: boolean;
|
|
105
|
+
|
|
98
106
|
/**
|
|
99
107
|
* Whether to use PSEUDOCONSOLE_INHERIT_CURSOR in conpty.
|
|
100
108
|
* @see https://docs.microsoft.com/en-us/windows/console/createpseudoconsole
|