relmio 0.2.10 → 0.2.12
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/CHANGELOG.md +30 -0
- package/README.md +1 -1
- package/docs/troubleshooting.md +7 -2
- package/package.json +1 -1
- package/scripts/build-npm-package.js +38 -3
- package/src/browser.js +33 -1
- package/src/services/oauth.js +94 -33
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,36 @@ checks the registry separately after publication.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## [0.2.12] - 2026-08-04
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Install the pinned `openai-oauth@2.0.0` helper with its exact compatible
|
|
15
|
+
`zod@4.1.8` peer even when inherited npm settings omit peer dependencies,
|
|
16
|
+
preventing the Windows sign-in helper from exiting before it prints a URL.
|
|
17
|
+
- Accept the same strictly validated authorization line from either helper
|
|
18
|
+
output stream, including Windows terminal framing and final drained output.
|
|
19
|
+
- Run npm package builds through the current Node.js runtime on Windows instead
|
|
20
|
+
of executing `npm.cmd` directly with `shell: false`.
|
|
21
|
+
|
|
22
|
+
## [0.2.11] - 2026-08-04
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Open the private Windows wizard URL through the documented default-browser
|
|
27
|
+
association instead of asking Explorer to treat the URL as a folder, while
|
|
28
|
+
retaining the printed URL and Enter-to-retry fallback.
|
|
29
|
+
- Make the Command Prompt installer copy call the system Windows PowerShell
|
|
30
|
+
executable directly, avoiding ambiguous `powershell` command resolution.
|
|
31
|
+
- Parse the supported `openai-oauth@2.0.0` login line across Windows terminal
|
|
32
|
+
control sequences and chunk boundaries, and report a sanitized, actionable
|
|
33
|
+
callback-port conflict instead of a generic missing-link error.
|
|
34
|
+
|
|
35
|
+
### Security
|
|
36
|
+
|
|
37
|
+
- Pin the hosted web tooling to `brace-expansion` 5.0.9, which includes the
|
|
38
|
+
upstream denial-of-service fix required by the release audit.
|
|
39
|
+
|
|
10
40
|
## [0.2.10] - 2026-08-03
|
|
11
41
|
|
|
12
42
|
### Fixed
|
package/README.md
CHANGED
|
@@ -105,7 +105,7 @@ irm https://relmio.vercel.app/install.ps1 | iex
|
|
|
105
105
|
### Windows Command Prompt
|
|
106
106
|
|
|
107
107
|
```bat
|
|
108
|
-
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://relmio.vercel.app/install.ps1 | iex"
|
|
108
|
+
"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -Command "irm https://relmio.vercel.app/install.ps1 | iex"
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
No Node.js or Git Bash installation is required first. The
|
package/docs/troubleshooting.md
CHANGED
|
@@ -49,7 +49,7 @@ irm https://relmio.vercel.app/install.ps1 | iex
|
|
|
49
49
|
Windows Command Prompt:
|
|
50
50
|
|
|
51
51
|
```bat
|
|
52
|
-
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://relmio.vercel.app/install.ps1 | iex"
|
|
52
|
+
"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -Command "irm https://relmio.vercel.app/install.ps1 | iex"
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
These commands do not require Node.js to be installed. The Windows options do
|
|
@@ -71,6 +71,11 @@ Enter to ask it to open the browser again. Otherwise, copy the newest printed
|
|
|
71
71
|
`http://127.0.0.1:...` URL into the browser. That URL contains a temporary setup
|
|
72
72
|
token: do not post it in an issue or screenshot.
|
|
73
73
|
|
|
74
|
+
The local wizard may be displayed in a VS Code embedded browser. Its validated
|
|
75
|
+
manual link, **Open fresh ChatGPT sign-in**, remains available if that embedded
|
|
76
|
+
browser blocks the popup or no new tab opens. Use that link only from the active
|
|
77
|
+
wizard attempt; it points to the fresh `auth.openai.com` authorization URL.
|
|
78
|
+
|
|
74
79
|
You do not need to sign in to npm, configure npm 2FA, or own this package to
|
|
75
80
|
run any public command. npm authentication is required only for the
|
|
76
81
|
maintainer who publishes a release.
|
|
@@ -112,7 +117,7 @@ bypassed.
|
|
|
112
117
|
| Symptom | Meaning | Fix |
|
|
113
118
|
|---|---|---|
|
|
114
119
|
| `node: command not found`, `node is not recognized`, or Node is older than 22 | The NPX fallback cannot use the local runtime. | Use the macOS/Linux curl command or the native Windows PowerShell/Command Prompt command above. Either can run with a verified temporary runtime. Do not install Node.js on the VPS for the wizard. |
|
|
115
|
-
| `curl` or `sh` is not recognized on Windows | The macOS/Linux command was pasted into a native Windows terminal. | Use the PowerShell command in PowerShell, or the
|
|
120
|
+
| `curl` or `sh` is not recognized on Windows | The macOS/Linux command was pasted into a native Windows terminal. | Use the PowerShell command in PowerShell, or the `"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile ...` command in Command Prompt. Git Bash is not required. |
|
|
116
121
|
| A bootstrap reports a checksum mismatch | The Node.js download did not match the official SHA-256 manifest, so it was not executed. | Retry on a trusted connection. Do not bypass the check. If it repeats, use an existing Node.js 22+ installation and report the sanitized error. |
|
|
117
122
|
| Windows PowerShell shows `[eval]:1` before the wizard starts | An older bootstrap passed a JavaScript expression through `node -p`; PowerShell native-argument handling can alter that expression. | Update to the latest `relmio@latest` and rerun the same PowerShell or Command Prompt command. The current bootstrap parses the literal `node --version` output and reuses Node.js 22 or newer. |
|
|
118
123
|
| Windows reports `spawn EINVAL` when starting ChatGPT sign-in | An older wizard tried to execute `npx.cmd` directly; Windows requires the current Node runtime to launch npm's JavaScript CLI. | Update to the latest `relmio@latest` release and restart the setup command. The current wizard keeps the macOS/Linux/WSL/Git Bash `npx` path unchanged. |
|
package/package.json
CHANGED
|
@@ -10,15 +10,48 @@ import {
|
|
|
10
10
|
rm,
|
|
11
11
|
} from "node:fs/promises";
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
13
|
-
import { basename, join, resolve } from "node:path";
|
|
13
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { promisify } from "node:util";
|
|
16
16
|
|
|
17
17
|
const execFileAsync = promisify(execFile);
|
|
18
|
-
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
19
18
|
const sourceRoot = fileURLToPath(new URL("..", import.meta.url));
|
|
20
19
|
const defaultOutputDirectory = join(sourceRoot, "dist", "npm");
|
|
21
20
|
|
|
21
|
+
export function resolveNpmInvocation({
|
|
22
|
+
env = process.env,
|
|
23
|
+
execPath = process.execPath,
|
|
24
|
+
platform = process.platform,
|
|
25
|
+
} = {}) {
|
|
26
|
+
if (platform !== "win32") {
|
|
27
|
+
return { command: "npm", prefixArgs: [] };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const npmExecPathKey = Object.keys(env ?? {}).find(
|
|
31
|
+
(key) => key.toLowerCase() === "npm_execpath",
|
|
32
|
+
);
|
|
33
|
+
const npmExecPath =
|
|
34
|
+
npmExecPathKey === undefined ? undefined : env[npmExecPathKey];
|
|
35
|
+
let npmCliPath;
|
|
36
|
+
|
|
37
|
+
if (typeof npmExecPath === "string") {
|
|
38
|
+
if (/(?:^|[\\/])npm-cli\.js$/iu.test(npmExecPath)) {
|
|
39
|
+
npmCliPath = resolve(npmExecPath);
|
|
40
|
+
} else if (/(?:^|[\\/])npx-cli\.js$/iu.test(npmExecPath)) {
|
|
41
|
+
npmCliPath = resolve(dirname(npmExecPath), "npm-cli.js");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
command: execPath,
|
|
47
|
+
// Windows cannot execute npm.cmd directly with shell:false.
|
|
48
|
+
prefixArgs: [
|
|
49
|
+
npmCliPath ??
|
|
50
|
+
resolve(dirname(execPath), "node_modules", "npm", "bin", "npm-cli.js"),
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
22
55
|
async function copyPublishEntry({ entry, stagingDirectory }) {
|
|
23
56
|
const source = join(sourceRoot, entry);
|
|
24
57
|
const target = join(stagingDirectory, entry);
|
|
@@ -61,9 +94,11 @@ export async function buildNpmPackage({
|
|
|
61
94
|
const expectedPath = join(outputDirectory, expectedFilename);
|
|
62
95
|
await rm(expectedPath, { force: true });
|
|
63
96
|
|
|
97
|
+
const npmInvocation = resolveNpmInvocation();
|
|
64
98
|
const { stdout } = await execFileAsync(
|
|
65
|
-
|
|
99
|
+
npmInvocation.command,
|
|
66
100
|
[
|
|
101
|
+
...npmInvocation.prefixArgs,
|
|
67
102
|
"pack",
|
|
68
103
|
stagingDirectory,
|
|
69
104
|
"--json",
|
package/src/browser.js
CHANGED
|
@@ -5,15 +5,47 @@ export function browserCommand(url, platform = process.platform) {
|
|
|
5
5
|
return { file: "open", args: [url] };
|
|
6
6
|
}
|
|
7
7
|
if (platform === "win32") {
|
|
8
|
-
return {
|
|
8
|
+
return {
|
|
9
|
+
file: "cmd.exe",
|
|
10
|
+
args: ["/d", "/c", "start", "Relmio local wizard", url],
|
|
11
|
+
};
|
|
9
12
|
}
|
|
10
13
|
return { file: "xdg-open", args: [url] };
|
|
11
14
|
}
|
|
12
15
|
|
|
16
|
+
function isPrivateWizardUrl(value) {
|
|
17
|
+
if (typeof value !== "string" || /[\u0000-\u001F\u007F]/u.test(value)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const url = new URL(value);
|
|
23
|
+
const session = url.searchParams.get("session");
|
|
24
|
+
return (
|
|
25
|
+
url.protocol === "http:" &&
|
|
26
|
+
url.hostname === "127.0.0.1" &&
|
|
27
|
+
url.port !== "" &&
|
|
28
|
+
Number(url.port) > 0 &&
|
|
29
|
+
url.pathname === "/" &&
|
|
30
|
+
url.username === "" &&
|
|
31
|
+
url.password === "" &&
|
|
32
|
+
url.hash === "" &&
|
|
33
|
+
typeof session === "string" &&
|
|
34
|
+
/^[A-Za-z0-9_-]{43}$/u.test(session) &&
|
|
35
|
+
url.search === `?session=${session}`
|
|
36
|
+
);
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
13
42
|
export function openBrowser(
|
|
14
43
|
url,
|
|
15
44
|
{ platform = process.platform, spawnProcess = spawn } = {},
|
|
16
45
|
) {
|
|
46
|
+
if (!isPrivateWizardUrl(url)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
17
49
|
const command = browserCommand(url, platform);
|
|
18
50
|
let child;
|
|
19
51
|
try {
|
package/src/services/oauth.js
CHANGED
|
@@ -12,6 +12,11 @@ const PROCESS_TIMEOUT_MS = LOGIN_TIMEOUT_MS + 15_000;
|
|
|
12
12
|
const CREDENTIAL_POLL_INTERVAL_MS = 100;
|
|
13
13
|
const LOGIN_URL_PREFIX = "OpenAI OAuth login URL: ";
|
|
14
14
|
const OPENAI_AUTH_ORIGIN = "https://auth.openai.com";
|
|
15
|
+
const SUPPORTED_LOOPBACK_REDIRECT_HOSTNAMES = new Set([
|
|
16
|
+
"localhost",
|
|
17
|
+
"127.0.0.1",
|
|
18
|
+
"[::1]",
|
|
19
|
+
]);
|
|
15
20
|
|
|
16
21
|
const wait = (milliseconds) =>
|
|
17
22
|
new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
|
|
@@ -124,6 +129,10 @@ function validateAuthorizationUrl(value) {
|
|
|
124
129
|
|
|
125
130
|
if (
|
|
126
131
|
url.origin !== OPENAI_AUTH_ORIGIN ||
|
|
132
|
+
url.username !== "" ||
|
|
133
|
+
url.password !== "" ||
|
|
134
|
+
url.hash !== "" ||
|
|
135
|
+
url.href.includes("#") ||
|
|
127
136
|
url.pathname !== "/oauth/authorize" ||
|
|
128
137
|
url.searchParams.get("response_type") !== "code" ||
|
|
129
138
|
!url.searchParams.get("state") ||
|
|
@@ -139,7 +148,14 @@ function validateAuthorizationUrl(value) {
|
|
|
139
148
|
throw new Error("The sign-in command returned an invalid callback.");
|
|
140
149
|
}
|
|
141
150
|
if (
|
|
142
|
-
|
|
151
|
+
redirect.protocol !== "http:" ||
|
|
152
|
+
redirect.username !== "" ||
|
|
153
|
+
redirect.password !== "" ||
|
|
154
|
+
redirect.search !== "" ||
|
|
155
|
+
redirect.hash !== "" ||
|
|
156
|
+
redirect.href.includes("?") ||
|
|
157
|
+
redirect.href.includes("#") ||
|
|
158
|
+
!SUPPORTED_LOOPBACK_REDIRECT_HOSTNAMES.has(redirect.hostname) ||
|
|
143
159
|
redirect.port !== "1455" ||
|
|
144
160
|
redirect.pathname !== "/auth/callback"
|
|
145
161
|
) {
|
|
@@ -149,15 +165,38 @@ function validateAuthorizationUrl(value) {
|
|
|
149
165
|
return url.toString();
|
|
150
166
|
}
|
|
151
167
|
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
168
|
+
function stripTerminalControlSequences(value) {
|
|
169
|
+
return value
|
|
170
|
+
.replace(/\u001B\][^\u0007]*(?:\u0007|\u001B\\)/gu, "")
|
|
171
|
+
.replace(/\u001B\[[0-?]*[ -/]*[@-~]/gu, "")
|
|
172
|
+
.replace(/[\u0000-\u0009\u000B-\u001F\u007F-\u009F]/gu, "");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function extractAuthorizationUrl(output, { includeFinalLine = false } = {}) {
|
|
176
|
+
const lines = stripTerminalControlSequences(output).split("\n");
|
|
177
|
+
const lineCount = includeFinalLine ? lines.length : lines.length - 1;
|
|
178
|
+
for (const line of lines.slice(0, lineCount)) {
|
|
179
|
+
const markerIndex = line.indexOf(LOGIN_URL_PREFIX);
|
|
180
|
+
if (markerIndex >= 0) {
|
|
181
|
+
return validateAuthorizationUrl(
|
|
182
|
+
line.slice(markerIndex + LOGIN_URL_PREFIX.length).trim(),
|
|
183
|
+
);
|
|
156
184
|
}
|
|
157
185
|
}
|
|
158
186
|
return null;
|
|
159
187
|
}
|
|
160
188
|
|
|
189
|
+
function loginStartupError(stderr) {
|
|
190
|
+
const callbackPortConflict =
|
|
191
|
+
"OpenAI OAuth login needs http://localhost:1455/auth/callback, but port 1455 is already in use.";
|
|
192
|
+
if (stripTerminalControlSequences(stderr).includes(callbackPortConflict)) {
|
|
193
|
+
return new Error(
|
|
194
|
+
`${callbackPortConflict} Stop the process using that port and try again.`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return new Error("The sign-in command did not return an authorization URL.");
|
|
198
|
+
}
|
|
199
|
+
|
|
161
200
|
export async function startOAuthLogin({
|
|
162
201
|
fileSystem = defaultFileSystem,
|
|
163
202
|
env = process.env,
|
|
@@ -175,7 +214,12 @@ export async function startOAuthLogin({
|
|
|
175
214
|
const args = [
|
|
176
215
|
"--yes",
|
|
177
216
|
"--ignore-scripts",
|
|
178
|
-
"
|
|
217
|
+
"--legacy-peer-deps=false",
|
|
218
|
+
"--include=peer",
|
|
219
|
+
"--package=openai-oauth@2.0.0",
|
|
220
|
+
"--package=zod@4.1.8",
|
|
221
|
+
"--",
|
|
222
|
+
"openai-oauth",
|
|
179
223
|
"login",
|
|
180
224
|
"--no-open",
|
|
181
225
|
"--login-timeout-ms",
|
|
@@ -205,9 +249,8 @@ export async function startOAuthLogin({
|
|
|
205
249
|
{ cause: error },
|
|
206
250
|
);
|
|
207
251
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
let loginOutput = "";
|
|
252
|
+
const loginOutput = { stdout: "", stderr: "" };
|
|
253
|
+
let loginOutputBytes = 0;
|
|
211
254
|
let resolveAuthorizationUrl;
|
|
212
255
|
let rejectAuthorizationUrl;
|
|
213
256
|
let authorizationUrlSettled = false;
|
|
@@ -227,20 +270,22 @@ export async function startOAuthLogin({
|
|
|
227
270
|
}
|
|
228
271
|
};
|
|
229
272
|
|
|
230
|
-
|
|
273
|
+
const captureLoginOutput = (stream, chunk) => {
|
|
231
274
|
if (authorizationUrlSettled) {
|
|
232
275
|
return;
|
|
233
276
|
}
|
|
234
|
-
|
|
235
|
-
|
|
277
|
+
const output = Buffer.from(chunk).toString("utf8");
|
|
278
|
+
loginOutputBytes += Buffer.byteLength(output);
|
|
279
|
+
if (loginOutputBytes > MAX_LOGIN_OUTPUT_BYTES) {
|
|
236
280
|
settleAuthorizationUrl(
|
|
237
281
|
new Error("The sign-in command returned too much output."),
|
|
238
282
|
);
|
|
239
283
|
child.kill?.("SIGTERM");
|
|
240
284
|
return;
|
|
241
285
|
}
|
|
286
|
+
loginOutput[stream] += output;
|
|
242
287
|
try {
|
|
243
|
-
const authorizationUrl = extractAuthorizationUrl(loginOutput);
|
|
288
|
+
const authorizationUrl = extractAuthorizationUrl(loginOutput[stream]);
|
|
244
289
|
if (authorizationUrl) {
|
|
245
290
|
settleAuthorizationUrl(null, authorizationUrl);
|
|
246
291
|
}
|
|
@@ -248,24 +293,27 @@ export async function startOAuthLogin({
|
|
|
248
293
|
settleAuthorizationUrl(error);
|
|
249
294
|
child.kill?.("SIGTERM");
|
|
250
295
|
}
|
|
251
|
-
}
|
|
296
|
+
};
|
|
252
297
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
298
|
+
child.stdout?.on?.("data", (chunk) => captureLoginOutput("stdout", chunk));
|
|
299
|
+
child.stderr?.on?.("data", (chunk) => captureLoginOutput("stderr", chunk));
|
|
300
|
+
|
|
301
|
+
let resolveProcessClose;
|
|
302
|
+
let rejectProcessClose;
|
|
303
|
+
let processCloseSettled = false;
|
|
304
|
+
const processClosePromise = new Promise((resolvePromise, rejectPromise) => {
|
|
305
|
+
resolveProcessClose = resolvePromise;
|
|
306
|
+
rejectProcessClose = rejectPromise;
|
|
259
307
|
});
|
|
260
|
-
const
|
|
261
|
-
if (
|
|
308
|
+
const settleProcessClose = (error, code) => {
|
|
309
|
+
if (processCloseSettled) {
|
|
262
310
|
return;
|
|
263
311
|
}
|
|
264
|
-
|
|
312
|
+
processCloseSettled = true;
|
|
265
313
|
if (error) {
|
|
266
|
-
|
|
314
|
+
rejectProcessClose(error);
|
|
267
315
|
} else {
|
|
268
|
-
|
|
316
|
+
resolveProcessClose(code);
|
|
269
317
|
}
|
|
270
318
|
};
|
|
271
319
|
|
|
@@ -274,15 +322,28 @@ export async function startOAuthLogin({
|
|
|
274
322
|
"The local sign-in command could not start. Install Node.js 22 and try again.",
|
|
275
323
|
);
|
|
276
324
|
settleAuthorizationUrl(error);
|
|
277
|
-
|
|
325
|
+
settleProcessClose(error);
|
|
278
326
|
});
|
|
279
|
-
child.once("
|
|
327
|
+
child.once("close", (code) => {
|
|
280
328
|
if (!authorizationUrlSettled) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
329
|
+
try {
|
|
330
|
+
const authorizationUrl =
|
|
331
|
+
extractAuthorizationUrl(loginOutput.stdout, {
|
|
332
|
+
includeFinalLine: true,
|
|
333
|
+
}) ??
|
|
334
|
+
extractAuthorizationUrl(loginOutput.stderr, {
|
|
335
|
+
includeFinalLine: true,
|
|
336
|
+
});
|
|
337
|
+
if (authorizationUrl) {
|
|
338
|
+
settleAuthorizationUrl(null, authorizationUrl);
|
|
339
|
+
} else {
|
|
340
|
+
settleAuthorizationUrl(loginStartupError(loginOutput.stderr));
|
|
341
|
+
}
|
|
342
|
+
} catch (error) {
|
|
343
|
+
settleAuthorizationUrl(error);
|
|
344
|
+
}
|
|
284
345
|
}
|
|
285
|
-
|
|
346
|
+
settleProcessClose(null, code);
|
|
286
347
|
});
|
|
287
348
|
|
|
288
349
|
const loginUrlTimeout = setTimeout(() => {
|
|
@@ -326,7 +387,7 @@ export async function startOAuthLogin({
|
|
|
326
387
|
try {
|
|
327
388
|
return await Promise.race([
|
|
328
389
|
pendingCredentialPromise,
|
|
329
|
-
|
|
390
|
+
processClosePromise.then(async (code) => {
|
|
330
391
|
if (code !== 0) {
|
|
331
392
|
throw new Error(
|
|
332
393
|
"ChatGPT sign-in did not finish. Start a fresh login.",
|
|
@@ -348,7 +409,7 @@ export async function startOAuthLogin({
|
|
|
348
409
|
keepPollingForCredential = false;
|
|
349
410
|
clearTimeout(processTimeout);
|
|
350
411
|
clearTimeout(loginUrlTimeout);
|
|
351
|
-
if (!
|
|
412
|
+
if (!processCloseSettled) {
|
|
352
413
|
child.kill?.("SIGTERM");
|
|
353
414
|
}
|
|
354
415
|
try {
|