@gramatr/client 0.6.5 → 0.6.6

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.
Files changed (2) hide show
  1. package/bin/gmtr-login.ts +23 -10
  2. package/package.json +1 -1
package/bin/gmtr-login.ts CHANGED
@@ -148,10 +148,11 @@ function isHeadless(forceFlag: boolean = false): boolean {
148
148
  if (forceFlag) return true;
149
149
  if (process.env.GRAMATR_LOGIN_HEADLESS === '1') return true;
150
150
 
151
- // SSH session without display forwarding
152
- if (process.env.SSH_CONNECTION || process.env.SSH_TTY) {
153
- if (!process.env.DISPLAY && process.platform !== 'darwin') return true;
154
- }
151
+ // SSH session always go headless. Even on macOS (which has a local
152
+ // display), `open` would launch Safari on the Mac's *physical* screen,
153
+ // not on the remote terminal where the user actually is. Device flow
154
+ // lets them paste the code on whichever machine they're sitting at.
155
+ if (process.env.SSH_CONNECTION || process.env.SSH_TTY) return true;
155
156
  // Docker / CI / no TTY
156
157
  if (process.env.CI || process.env.DOCKER) return true;
157
158
  // Linux without display
@@ -493,23 +494,35 @@ async function loginBrowser(opts: { forceHeadless?: boolean } = {}): Promise<voi
493
494
  const code = url.searchParams.get('code');
494
495
  const returnedState = url.searchParams.get('state');
495
496
  const error = url.searchParams.get('error');
497
+ // v0.6.6: `server.close()` alone does not terminate keep-alive
498
+ // sockets — the browser holds the connection open and the Node
499
+ // event loop never exits, so the CLI prints "Authenticated" and
500
+ // then hangs until Ctrl+C. Send `Connection: close` on the
501
+ // response and call `closeAllConnections()` to forcibly drop
502
+ // lingering sockets after we respond.
503
+ const shutdown = () => {
504
+ callbackServer.close();
505
+ if (typeof (callbackServer as any).closeAllConnections === 'function') {
506
+ (callbackServer as any).closeAllConnections();
507
+ }
508
+ };
496
509
  if (error) {
497
- res.writeHead(200, { 'Content-Type': 'text/html' });
510
+ res.writeHead(200, { 'Content-Type': 'text/html', Connection: 'close' });
498
511
  res.end(errorPage('Authentication Failed', error));
499
- callbackServer.close();
512
+ shutdown();
500
513
  reject(new Error(`OAuth error: ${error}`));
501
514
  return;
502
515
  }
503
516
  if (!code || returnedState !== state) {
504
- res.writeHead(400, { 'Content-Type': 'text/html' });
517
+ res.writeHead(400, { 'Content-Type': 'text/html', Connection: 'close' });
505
518
  res.end(errorPage('Invalid Callback', 'Missing code or state mismatch. Please try again.'));
506
- callbackServer.close();
519
+ shutdown();
507
520
  reject(new Error('Invalid callback'));
508
521
  return;
509
522
  }
510
- res.writeHead(200, { 'Content-Type': 'text/html' });
523
+ res.writeHead(200, { 'Content-Type': 'text/html', Connection: 'close' });
511
524
  res.end(successPage());
512
- callbackServer.close();
525
+ shutdown();
513
526
  resolve(code);
514
527
  });
515
528
  codeTimeoutHandle = setTimeout(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gramatr/client",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },