node-pty 1.2.0-beta.15 → 1.2.0-beta.17

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.
@@ -261,6 +261,10 @@ var UnixTerminal = /** @class */ (function (_super) {
261
261
  };
262
262
  UnixTerminal.prototype.clear = function () {
263
263
  };
264
+ UnixTerminal.prototype.setParentWindow = function (handle) {
265
+ // Windows only
266
+ return false;
267
+ };
264
268
  UnixTerminal.prototype._sanitizeEnv = function (env) {
265
269
  // Make sure we didn't start our server from inside tmux.
266
270
  delete env['TMUX'];
@@ -181,6 +181,15 @@ var WindowsPtyAgent = /** @class */ (function () {
181
181
  WindowsPtyAgent.prototype.clear = function () {
182
182
  this._ptyNative.clear(this._pty, this._useConptyDll);
183
183
  };
184
+ /**
185
+ * Tells ConPTY which top-level window hosts the terminal (ConptyReparentPseudoConsole). The
186
+ * window becomes the owner of the console window returned by GetConsoleWindow(), so dialogs
187
+ * that console apps parent to it (e.g. WAM sign-in) stack above the host window. Returns
188
+ * whether the request was sent to ConPTY, which applies it asynchronously.
189
+ */
190
+ WindowsPtyAgent.prototype.setParentWindow = function (handle) {
191
+ return this._ptyNative.reparent(this._pty, handle, this._useConptyDll);
192
+ };
184
193
  WindowsPtyAgent.prototype.kill = function () {
185
194
  var _this = this;
186
195
  // Prevent deferred connection from completing after kill
@@ -148,6 +148,11 @@ var WindowsTerminal = /** @class */ (function (_super) {
148
148
  _this._agent.clear();
149
149
  });
150
150
  };
151
+ WindowsTerminal.prototype.setParentWindow = function (handle) {
152
+ // Not deferred: the pseudoconsole exists as soon as the agent is created, and the owner
153
+ // should be requested before the shell starts asking for it.
154
+ return this._agent.setParentWindow(handle);
155
+ };
151
156
  WindowsTerminal.prototype.destroy = function () {
152
157
  var _this = this;
153
158
  this._deferNoArgs(function () {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "author": {
5
5
  "name": "Microsoft Corporation"
6
6
  },
7
- "version": "1.2.0-beta.15",
7
+ "version": "1.2.0-beta.17",
8
8
  "license": "MIT",
9
9
  "main": "./lib/index.js",
10
10
  "types": "./typings/node-pty.d.ts",
@@ -27,6 +27,18 @@
27
27
  "bugs": {
28
28
  "url": "https://github.com/microsoft/node-pty/issues"
29
29
  },
30
+ "devEngines": {
31
+ "runtime": {
32
+ "name": "node",
33
+ "version": ">=24",
34
+ "onFail": "error"
35
+ },
36
+ "packageManager": {
37
+ "name": "npm",
38
+ "version": ">=11.6.3",
39
+ "onFail": "error"
40
+ }
41
+ },
30
42
  "keywords": [
31
43
  "pty",
32
44
  "tty",
@@ -51,15 +63,14 @@
51
63
  "node-addon-api": "^7.1.0"
52
64
  },
53
65
  "devDependencies": {
54
- "@types/mocha": "^7.0.2",
66
+ "@types/mocha": "^10.0.10",
55
67
  "@types/node": "12",
56
68
  "@typescript-eslint/eslint-plugin": "^8.0.0",
57
69
  "@typescript-eslint/parser": "^8.0.0",
58
70
  "cross-env": "^5.1.4",
59
71
  "eslint": "^10.4.0",
60
72
  "globals": "^14.0.0",
61
- "mocha": "10",
62
- "node-gyp": "^11.4.2",
73
+ "mocha": "12.0.2",
63
74
  "ps-list": "^6.0.0",
64
75
  "typescript": "^5.0.0"
65
76
  }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/src/win/conpty.cc CHANGED
@@ -15,6 +15,7 @@
15
15
  #include <assert.h>
16
16
  #include <Shlwapi.h> // PathCombine, PathIsRelative
17
17
  #include <atomic>
18
+ #include <cstring>
18
19
  #include <mutex>
19
20
  #include <sstream>
20
21
  #include <iostream>
@@ -40,6 +41,9 @@ typedef void (__stdcall *PFNRELEASEPSEUDOCONSOLE)(HPCON hpc);
40
41
 
41
42
  #endif
42
43
 
44
+ // Only exported by the bundled conpty.dll; kernel32 has no equivalent.
45
+ typedef HRESULT (__stdcall *PFNREPARENTPSEUDOCONSOLE)(HPCON hpc, HWND newParent);
46
+
43
47
  struct pty_baton {
44
48
  int id;
45
49
  HANDLE hIn;
@@ -495,7 +499,7 @@ static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
495
499
  std::lock_guard<std::mutex> lock(g_ptyHandlesMutex);
496
500
  const pty_baton* handle = get_pty_baton(lock, id);
497
501
 
498
- if (handle != nullptr) {
502
+ if (handle != nullptr && handle->hpc != nullptr) {
499
503
  HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
500
504
  bool fLoadedDll = hLibrary != nullptr;
501
505
  if (fLoadedDll)
@@ -537,7 +541,7 @@ static Napi::Value PtyClear(const Napi::CallbackInfo& info) {
537
541
  std::lock_guard<std::mutex> lock(g_ptyHandlesMutex);
538
542
  const pty_baton* handle = get_pty_baton(lock, id);
539
543
 
540
- if (handle != nullptr) {
544
+ if (handle != nullptr && handle->hpc != nullptr) {
541
545
  HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
542
546
  bool fLoadedDll = hLibrary != nullptr;
543
547
  if (fLoadedDll)
@@ -553,6 +557,65 @@ static Napi::Value PtyClear(const Napi::CallbackInfo& info) {
553
557
  return env.Undefined();
554
558
  }
555
559
 
560
+ // The handle is the bytes of an HWND in native byte order, which is how Electron's
561
+ // BrowserWindow.getNativeWindowHandle() returns it.
562
+ static bool TryGetWindowHandle(const Napi::Value& value, HWND* hwnd) {
563
+ if (!value.IsBuffer()) {
564
+ return false;
565
+ }
566
+ const auto buffer = value.As<Napi::Buffer<uint8_t>>();
567
+ if (buffer.Length() != sizeof(HWND)) {
568
+ return false;
569
+ }
570
+ std::memcpy(hwnd, buffer.Data(), sizeof(*hwnd));
571
+ return true;
572
+ }
573
+
574
+ static Napi::Value PtyReparent(const Napi::CallbackInfo& info) {
575
+ Napi::Env env(info.Env());
576
+ Napi::HandleScope scope(env);
577
+
578
+ if (info.Length() != 3 ||
579
+ !info[0].IsNumber() ||
580
+ !info[2].IsBoolean()) {
581
+ throw Napi::Error::New(env, "Usage: pty.reparent(id, hwnd, useConptyDll)");
582
+ }
583
+
584
+ int id = info[0].As<Napi::Number>().Int32Value();
585
+ HWND newParent;
586
+ if (!TryGetWindowHandle(info[1], &newParent)) {
587
+ throw Napi::Error::New(env, "pty.reparent: hwnd must be a Buffer the size of an HWND");
588
+ }
589
+ const bool useConptyDll = info[2].As<Napi::Boolean>().Value();
590
+
591
+ // ConptyReparentPseudoConsole only exists in the bundled conpty.dll.
592
+ if (!useConptyDll) {
593
+ return Napi::Boolean::New(env, false);
594
+ }
595
+
596
+ std::lock_guard<std::mutex> lock(g_ptyHandlesMutex);
597
+ const pty_baton* handle = get_pty_baton(lock, id);
598
+
599
+ bool sent = false;
600
+ if (handle != nullptr && handle->hpc != nullptr) {
601
+ HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
602
+ bool fLoadedDll = hLibrary != nullptr;
603
+ if (fLoadedDll)
604
+ {
605
+ PFNREPARENTPSEUDOCONSOLE const pfnReparentPseudoConsole = (PFNREPARENTPSEUDOCONSOLE)GetProcAddress(
606
+ (HMODULE)hLibrary, "ConptyReparentPseudoConsole");
607
+ if (pfnReparentPseudoConsole)
608
+ {
609
+ // Asks ConPTY to make newParent the owner of the console window returned by
610
+ // GetConsoleWindow(); ConPTY applies it asynchronously.
611
+ sent = SUCCEEDED(pfnReparentPseudoConsole(handle->hpc, newParent));
612
+ }
613
+ }
614
+ }
615
+
616
+ return Napi::Boolean::New(env, sent);
617
+ }
618
+
556
619
  static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
557
620
  Napi::Env env(info.Env());
558
621
  Napi::HandleScope scope(env);
@@ -577,9 +640,11 @@ static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
577
640
  PFNCLOSEPSEUDOCONSOLE const pfnClosePseudoConsole = (PFNCLOSEPSEUDOCONSOLE)GetProcAddress(
578
641
  (HMODULE)hLibrary,
579
642
  useConptyDll ? "ConptyClosePseudoConsole" : "ClosePseudoConsole");
580
- if (pfnClosePseudoConsole)
643
+ if (pfnClosePseudoConsole && handle->hpc != nullptr)
581
644
  {
582
645
  pfnClosePseudoConsole(handle->hpc);
646
+ // Closing frees the pseudoconsole, but the baton lives until the exit thread erases it.
647
+ handle->hpc = nullptr;
583
648
  }
584
649
  }
585
650
  // Defensive: if PtyConnect was never called (or failed before closing the
@@ -609,6 +674,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
609
674
  exports.Set("connect", Napi::Function::New(env, PtyConnect));
610
675
  exports.Set("resize", Napi::Function::New(env, PtyResize));
611
676
  exports.Set("clear", Napi::Function::New(env, PtyClear));
677
+ exports.Set("reparent", Napi::Function::New(env, PtyReparent));
612
678
  exports.Set("kill", Napi::Function::New(env, PtyKill));
613
679
  return exports;
614
680
  };
@@ -173,6 +173,23 @@ declare module 'node-pty' {
173
173
  */
174
174
  clear(): void;
175
175
 
176
+ /**
177
+ * Sets the handle (HWND) of the top-level window that hosts this terminal. ConPTY makes it the
178
+ * owner of the console window returned by `GetConsoleWindow()`, so windows that console
179
+ * applications parent to it (for example `GetAncestor(GetConsoleWindow(), GA_ROOTOWNER)`, which
180
+ * MSAL/WAM sign-in uses) open on top of the host window instead of behind it. Calling it right
181
+ * after spawning sends the request before the process is launched. Call it again whenever the
182
+ * terminal moves to a different window. Optional so existing implementations of `IPty` keep
183
+ * compiling.
184
+ * @param handle The window handle (HWND) as its bytes in native byte order, the way Electron's
185
+ * `BrowserWindow.getNativeWindowHandle()` returns it. Throws unless it's a Buffer the size of
186
+ * an HWND (8 bytes in a 64-bit process).
187
+ * @returns Whether the request was sent to ConPTY, which applies it asynchronously. This is
188
+ * always false unless on Windows with `useConptyDll`, and false once the pseudoconsole is
189
+ * closed.
190
+ */
191
+ setParentWindow?(handle: Buffer): boolean;
192
+
176
193
  /**
177
194
  * Writes data to the pty.
178
195
  * @param data The data to write.