codex-multi-auth 2.5.0 → 2.6.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.
Files changed (37) hide show
  1. package/.codex-plugin/plugin.json +9 -9
  2. package/README.md +4 -1
  3. package/config/codex-legacy.json +425 -0
  4. package/dist/lib/auth/browser.d.ts +4 -0
  5. package/dist/lib/auth/browser.d.ts.map +1 -1
  6. package/dist/lib/auth/browser.js +73 -19
  7. package/dist/lib/auth/callback-guidance.d.ts +39 -0
  8. package/dist/lib/auth/callback-guidance.d.ts.map +1 -0
  9. package/dist/lib/auth/callback-guidance.js +106 -0
  10. package/dist/lib/auth/server.d.ts.map +1 -1
  11. package/dist/lib/auth/server.js +1 -0
  12. package/dist/lib/codex-manager/commands/best.js +2 -2
  13. package/dist/lib/codex-manager/commands/forecast.js +4 -4
  14. package/dist/lib/codex-manager/commands/report.js +4 -4
  15. package/dist/lib/codex-manager/help.js +2 -2
  16. package/dist/lib/codex-manager/login-oauth.d.ts.map +1 -1
  17. package/dist/lib/codex-manager/login-oauth.js +11 -0
  18. package/dist/lib/codex-manager/quota-cache-helpers.d.ts +1 -1
  19. package/dist/lib/codex-manager/quota-cache-helpers.d.ts.map +1 -1
  20. package/dist/lib/codex-manager/quota-cache-helpers.js +2 -2
  21. package/dist/lib/codex-manager/repair-commands.js +4 -4
  22. package/dist/lib/config.d.ts.map +1 -1
  23. package/dist/lib/config.js +5 -1
  24. package/dist/lib/quota-probe.d.ts.map +1 -1
  25. package/dist/lib/quota-probe.js +5 -2
  26. package/dist/lib/request/helpers/model-map.d.ts +15 -1
  27. package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
  28. package/dist/lib/request/helpers/model-map.js +45 -1
  29. package/dist/lib/runtime/quota-probe.d.ts.map +1 -1
  30. package/dist/lib/runtime/quota-probe.js +5 -2
  31. package/dist/lib/types.d.ts +6 -0
  32. package/dist/lib/types.d.ts.map +1 -1
  33. package/dist/lib/wsl.d.ts +28 -0
  34. package/dist/lib/wsl.d.ts.map +1 -0
  35. package/dist/lib/wsl.js +57 -0
  36. package/package.json +1 -1
  37. package/scripts/codex.js +129 -15
@@ -1,9 +1,9 @@
1
- {
2
- "name": "codex-multi-auth",
3
- "version": "2.5.0",
4
- "description": "Install and operate codex-multi-auth for the official @openai/codex CLI with multi-account OAuth rotation, switching, health checks, and recovery tools.",
5
- "interface": {
6
- "composerIcon": "./assets/codex-multi-auth-icon.svg"
7
- },
8
- "skills": "./skills/"
9
- }
1
+ {
2
+ "name": "codex-multi-auth",
3
+ "version": "2.6.1",
4
+ "description": "Install and operate codex-multi-auth for the official @openai/codex CLI with multi-account OAuth rotation, switching, health checks, and recovery tools.",
5
+ "interface": {
6
+ "composerIcon": "./assets/codex-multi-auth-icon.svg"
7
+ },
8
+ "skills": "./skills/"
9
+ }
package/README.md CHANGED
@@ -342,6 +342,7 @@ codex-multi-auth login
342
342
  - Requests fail fast after repeated upstream 5xx errors: inspect `codex-multi-auth report --json` for runtime traffic and cooldown details
343
343
  - Storage cleanup fails with `EBUSY` / `EPERM` (Windows): run `codex-multi-auth doctor --fix` to retry, or manually remove `~/.codex/multi-auth/<project-key>/` and re-login
344
344
  - OAuth callback on port `1455` fails: free the port and re-run `codex-multi-auth login`
345
+ - Login hangs in WSL, or installing in WSL breaks a working Windows install: Windows and WSL contend for callback port `1455`, and the browser opens on the Windows host either way — see [Windows and WSL side by side](docs/troubleshooting.md#windows-and-wsl-side-by-side), or use `codex-multi-auth login --device-auth`, which needs no callback port
345
346
  - Browser launch is blocked or you are in a headless shell: prefer `codex-multi-auth login --device-auth`; use `codex-multi-auth login --manual` or `CODEX_AUTH_NO_BROWSER=1` only when you need the callback-paste fallback
346
347
  - `missing field id_token` / `token_expired` / `refresh_token_reused`: re-login affected account
347
348
 
@@ -384,7 +385,9 @@ codex-multi-auth doctor --json
384
385
 
385
386
  ## Release Notes
386
387
 
387
- - Current stable: [docs/releases/v2.4.0.md](docs/releases/v2.4.0.md) — install via `npm i -g codex-multi-auth`
388
+ - Current stable: [docs/releases/v2.6.1.md](docs/releases/v2.6.1.md) — install via `npm i -g codex-multi-auth`
389
+ - Previous stable: [docs/releases/v2.5.0.md](docs/releases/v2.5.0.md)
390
+ - Previous stable: [docs/releases/v2.4.0.md](docs/releases/v2.4.0.md)
388
391
  - Previous stable: [docs/releases/v2.3.3.md](docs/releases/v2.3.3.md)
389
392
  - Previous stable: [docs/releases/v2.3.2.md](docs/releases/v2.3.2.md)
390
393
  - Previous stable: [docs/releases/v2.3.1.md](docs/releases/v2.3.1.md)
@@ -15,6 +15,431 @@
15
15
  "store": false
16
16
  },
17
17
  "models": {
18
+ "gpt-5.6-sol-low": {
19
+ "name": "GPT 5.6 Sol Low (OAuth)",
20
+ "limit": {
21
+ "context": 372000,
22
+ "output": 128000
23
+ },
24
+ "modalities": {
25
+ "input": [
26
+ "text",
27
+ "image"
28
+ ],
29
+ "output": [
30
+ "text"
31
+ ]
32
+ },
33
+ "options": {
34
+ "reasoningEffort": "low",
35
+ "reasoningSummary": "auto",
36
+ "textVerbosity": "medium",
37
+ "include": [
38
+ "reasoning.encrypted_content"
39
+ ],
40
+ "store": false
41
+ }
42
+ },
43
+ "gpt-5.6-sol-medium": {
44
+ "name": "GPT 5.6 Sol Medium (OAuth)",
45
+ "limit": {
46
+ "context": 372000,
47
+ "output": 128000
48
+ },
49
+ "modalities": {
50
+ "input": [
51
+ "text",
52
+ "image"
53
+ ],
54
+ "output": [
55
+ "text"
56
+ ]
57
+ },
58
+ "options": {
59
+ "reasoningEffort": "medium",
60
+ "reasoningSummary": "auto",
61
+ "textVerbosity": "medium",
62
+ "include": [
63
+ "reasoning.encrypted_content"
64
+ ],
65
+ "store": false
66
+ }
67
+ },
68
+ "gpt-5.6-sol-high": {
69
+ "name": "GPT 5.6 Sol High (OAuth)",
70
+ "limit": {
71
+ "context": 372000,
72
+ "output": 128000
73
+ },
74
+ "modalities": {
75
+ "input": [
76
+ "text",
77
+ "image"
78
+ ],
79
+ "output": [
80
+ "text"
81
+ ]
82
+ },
83
+ "options": {
84
+ "reasoningEffort": "high",
85
+ "reasoningSummary": "detailed",
86
+ "textVerbosity": "medium",
87
+ "include": [
88
+ "reasoning.encrypted_content"
89
+ ],
90
+ "store": false
91
+ }
92
+ },
93
+ "gpt-5.6-sol-xhigh": {
94
+ "name": "GPT 5.6 Sol Extra High (OAuth)",
95
+ "limit": {
96
+ "context": 372000,
97
+ "output": 128000
98
+ },
99
+ "modalities": {
100
+ "input": [
101
+ "text",
102
+ "image"
103
+ ],
104
+ "output": [
105
+ "text"
106
+ ]
107
+ },
108
+ "options": {
109
+ "reasoningEffort": "xhigh",
110
+ "reasoningSummary": "detailed",
111
+ "textVerbosity": "medium",
112
+ "include": [
113
+ "reasoning.encrypted_content"
114
+ ],
115
+ "store": false
116
+ }
117
+ },
118
+ "gpt-5.6-sol-max": {
119
+ "name": "GPT 5.6 Sol Max (OAuth)",
120
+ "limit": {
121
+ "context": 372000,
122
+ "output": 128000
123
+ },
124
+ "modalities": {
125
+ "input": [
126
+ "text",
127
+ "image"
128
+ ],
129
+ "output": [
130
+ "text"
131
+ ]
132
+ },
133
+ "options": {
134
+ "reasoningEffort": "max",
135
+ "reasoningSummary": "detailed",
136
+ "textVerbosity": "medium",
137
+ "include": [
138
+ "reasoning.encrypted_content"
139
+ ],
140
+ "store": false
141
+ }
142
+ },
143
+ "gpt-5.6-sol-ultra": {
144
+ "name": "GPT 5.6 Sol Ultra (OAuth)",
145
+ "limit": {
146
+ "context": 372000,
147
+ "output": 128000
148
+ },
149
+ "modalities": {
150
+ "input": [
151
+ "text",
152
+ "image"
153
+ ],
154
+ "output": [
155
+ "text"
156
+ ]
157
+ },
158
+ "options": {
159
+ "reasoningEffort": "ultra",
160
+ "reasoningSummary": "detailed",
161
+ "textVerbosity": "medium",
162
+ "include": [
163
+ "reasoning.encrypted_content"
164
+ ],
165
+ "store": false
166
+ }
167
+ },
168
+ "gpt-5.6-terra-low": {
169
+ "name": "GPT 5.6 Terra Low (OAuth)",
170
+ "limit": {
171
+ "context": 372000,
172
+ "output": 128000
173
+ },
174
+ "modalities": {
175
+ "input": [
176
+ "text",
177
+ "image"
178
+ ],
179
+ "output": [
180
+ "text"
181
+ ]
182
+ },
183
+ "options": {
184
+ "reasoningEffort": "low",
185
+ "reasoningSummary": "auto",
186
+ "textVerbosity": "medium",
187
+ "include": [
188
+ "reasoning.encrypted_content"
189
+ ],
190
+ "store": false
191
+ }
192
+ },
193
+ "gpt-5.6-terra-medium": {
194
+ "name": "GPT 5.6 Terra Medium (OAuth)",
195
+ "limit": {
196
+ "context": 372000,
197
+ "output": 128000
198
+ },
199
+ "modalities": {
200
+ "input": [
201
+ "text",
202
+ "image"
203
+ ],
204
+ "output": [
205
+ "text"
206
+ ]
207
+ },
208
+ "options": {
209
+ "reasoningEffort": "medium",
210
+ "reasoningSummary": "auto",
211
+ "textVerbosity": "medium",
212
+ "include": [
213
+ "reasoning.encrypted_content"
214
+ ],
215
+ "store": false
216
+ }
217
+ },
218
+ "gpt-5.6-terra-high": {
219
+ "name": "GPT 5.6 Terra High (OAuth)",
220
+ "limit": {
221
+ "context": 372000,
222
+ "output": 128000
223
+ },
224
+ "modalities": {
225
+ "input": [
226
+ "text",
227
+ "image"
228
+ ],
229
+ "output": [
230
+ "text"
231
+ ]
232
+ },
233
+ "options": {
234
+ "reasoningEffort": "high",
235
+ "reasoningSummary": "detailed",
236
+ "textVerbosity": "medium",
237
+ "include": [
238
+ "reasoning.encrypted_content"
239
+ ],
240
+ "store": false
241
+ }
242
+ },
243
+ "gpt-5.6-terra-xhigh": {
244
+ "name": "GPT 5.6 Terra Extra High (OAuth)",
245
+ "limit": {
246
+ "context": 372000,
247
+ "output": 128000
248
+ },
249
+ "modalities": {
250
+ "input": [
251
+ "text",
252
+ "image"
253
+ ],
254
+ "output": [
255
+ "text"
256
+ ]
257
+ },
258
+ "options": {
259
+ "reasoningEffort": "xhigh",
260
+ "reasoningSummary": "detailed",
261
+ "textVerbosity": "medium",
262
+ "include": [
263
+ "reasoning.encrypted_content"
264
+ ],
265
+ "store": false
266
+ }
267
+ },
268
+ "gpt-5.6-terra-max": {
269
+ "name": "GPT 5.6 Terra Max (OAuth)",
270
+ "limit": {
271
+ "context": 372000,
272
+ "output": 128000
273
+ },
274
+ "modalities": {
275
+ "input": [
276
+ "text",
277
+ "image"
278
+ ],
279
+ "output": [
280
+ "text"
281
+ ]
282
+ },
283
+ "options": {
284
+ "reasoningEffort": "max",
285
+ "reasoningSummary": "detailed",
286
+ "textVerbosity": "medium",
287
+ "include": [
288
+ "reasoning.encrypted_content"
289
+ ],
290
+ "store": false
291
+ }
292
+ },
293
+ "gpt-5.6-terra-ultra": {
294
+ "name": "GPT 5.6 Terra Ultra (OAuth)",
295
+ "limit": {
296
+ "context": 372000,
297
+ "output": 128000
298
+ },
299
+ "modalities": {
300
+ "input": [
301
+ "text",
302
+ "image"
303
+ ],
304
+ "output": [
305
+ "text"
306
+ ]
307
+ },
308
+ "options": {
309
+ "reasoningEffort": "ultra",
310
+ "reasoningSummary": "detailed",
311
+ "textVerbosity": "medium",
312
+ "include": [
313
+ "reasoning.encrypted_content"
314
+ ],
315
+ "store": false
316
+ }
317
+ },
318
+ "gpt-5.6-luna-low": {
319
+ "name": "GPT 5.6 Luna Low (OAuth)",
320
+ "limit": {
321
+ "context": 372000,
322
+ "output": 128000
323
+ },
324
+ "modalities": {
325
+ "input": [
326
+ "text",
327
+ "image"
328
+ ],
329
+ "output": [
330
+ "text"
331
+ ]
332
+ },
333
+ "options": {
334
+ "reasoningEffort": "low",
335
+ "reasoningSummary": "auto",
336
+ "textVerbosity": "medium",
337
+ "include": [
338
+ "reasoning.encrypted_content"
339
+ ],
340
+ "store": false
341
+ }
342
+ },
343
+ "gpt-5.6-luna-medium": {
344
+ "name": "GPT 5.6 Luna Medium (OAuth)",
345
+ "limit": {
346
+ "context": 372000,
347
+ "output": 128000
348
+ },
349
+ "modalities": {
350
+ "input": [
351
+ "text",
352
+ "image"
353
+ ],
354
+ "output": [
355
+ "text"
356
+ ]
357
+ },
358
+ "options": {
359
+ "reasoningEffort": "medium",
360
+ "reasoningSummary": "auto",
361
+ "textVerbosity": "medium",
362
+ "include": [
363
+ "reasoning.encrypted_content"
364
+ ],
365
+ "store": false
366
+ }
367
+ },
368
+ "gpt-5.6-luna-high": {
369
+ "name": "GPT 5.6 Luna High (OAuth)",
370
+ "limit": {
371
+ "context": 372000,
372
+ "output": 128000
373
+ },
374
+ "modalities": {
375
+ "input": [
376
+ "text",
377
+ "image"
378
+ ],
379
+ "output": [
380
+ "text"
381
+ ]
382
+ },
383
+ "options": {
384
+ "reasoningEffort": "high",
385
+ "reasoningSummary": "detailed",
386
+ "textVerbosity": "medium",
387
+ "include": [
388
+ "reasoning.encrypted_content"
389
+ ],
390
+ "store": false
391
+ }
392
+ },
393
+ "gpt-5.6-luna-xhigh": {
394
+ "name": "GPT 5.6 Luna Extra High (OAuth)",
395
+ "limit": {
396
+ "context": 372000,
397
+ "output": 128000
398
+ },
399
+ "modalities": {
400
+ "input": [
401
+ "text",
402
+ "image"
403
+ ],
404
+ "output": [
405
+ "text"
406
+ ]
407
+ },
408
+ "options": {
409
+ "reasoningEffort": "xhigh",
410
+ "reasoningSummary": "detailed",
411
+ "textVerbosity": "medium",
412
+ "include": [
413
+ "reasoning.encrypted_content"
414
+ ],
415
+ "store": false
416
+ }
417
+ },
418
+ "gpt-5.6-luna-max": {
419
+ "name": "GPT 5.6 Luna Max (OAuth)",
420
+ "limit": {
421
+ "context": 372000,
422
+ "output": 128000
423
+ },
424
+ "modalities": {
425
+ "input": [
426
+ "text",
427
+ "image"
428
+ ],
429
+ "output": [
430
+ "text"
431
+ ]
432
+ },
433
+ "options": {
434
+ "reasoningEffort": "max",
435
+ "reasoningSummary": "detailed",
436
+ "textVerbosity": "medium",
437
+ "include": [
438
+ "reasoning.encrypted_content"
439
+ ],
440
+ "store": false
441
+ }
442
+ },
18
443
  "gpt-5.5-none": {
19
444
  "name": "GPT 5.5 None (OAuth)",
20
445
  "limit": {
@@ -17,6 +17,10 @@ export declare function isBrowserLaunchSuppressed(): boolean;
17
17
  * OAuth codes) from `url` before calling. Invocations are not atomic—concurrent calls may race but are
18
18
  * safe to perform.
19
19
  *
20
+ * Under WSL the browser lives on the Windows host, so `xdg-open` is the wrong tool and is usually not
21
+ * even installed. Prefer `wslview` (wslu), then Windows PowerShell via interop, before falling back to
22
+ * the Linux opener.
23
+ *
20
24
  * @param url - The URL to open; redact sensitive tokens (e.g., OAuth codes) before passing.
21
25
  * @returns `true` if a browser launch was attempted, `false` if no suitable opener was available or an exception occurred.
22
26
  */
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../lib/auth/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAED,wBAAgB,yBAAyB,IAAI,OAAO,CAQnD;AA6DD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAwCnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmDzD"}
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../lib/auth/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAED,wBAAgB,yBAAyB,IAAI,OAAO,CAQnD;AAqGD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAyCnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CA8DzD"}
@@ -6,6 +6,7 @@ import { spawn } from "node:child_process";
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
8
8
  import { PLATFORM_OPENERS } from "../constants.js";
9
+ import { isWsl } from "../wsl.js";
9
10
  const BROWSER_DISABLED_VALUES = new Set(["0", "false", "no", "off", "none"]);
10
11
  const NO_BROWSER_TRUTHY_VALUES = new Set(["1", "true", "yes", "on"]);
11
12
  /**
@@ -89,6 +90,40 @@ function commandExists(command) {
89
90
  }
90
91
  return false;
91
92
  }
93
+ /**
94
+ * Escape a value for safe interpolation into a double-quoted PowerShell string.
95
+ *
96
+ * Neutralizes backticks, `$` sub-expression injection, and embedded double quotes.
97
+ */
98
+ function escapeForPowerShell(value) {
99
+ return value.replace(/`/g, "``").replace(/\$/g, "`$").replace(/"/g, '""');
100
+ }
101
+ /**
102
+ * Run a PowerShell command, fire-and-forget.
103
+ *
104
+ * Reachable from Windows and from WSL, where `powershell.exe` resolves through
105
+ * Windows/Linux PATH interop.
106
+ *
107
+ * @param command - A PowerShell command string; interpolated values must already be escaped.
108
+ * @param stdinText - Optional text piped to the command's stdin.
109
+ * @returns `true` if the spawn was attempted, `false` if PowerShell is unavailable.
110
+ */
111
+ function spawnPowerShell(command, stdinText) {
112
+ if (!commandExists("powershell.exe")) {
113
+ return false;
114
+ }
115
+ const child = spawn("powershell.exe", ["-NoLogo", "-NoProfile", "-Command", command], {
116
+ stdio: stdinText === undefined ? "ignore" : ["pipe", "ignore", "ignore"],
117
+ });
118
+ child.on("error", () => { });
119
+ if (stdinText !== undefined) {
120
+ // A child that dies before draining stdin emits EPIPE on the stream, not
121
+ // on the child. Unhandled, that is an async throw no try/catch can catch.
122
+ child.stdin?.on("error", () => { });
123
+ child.stdin?.end(stdinText);
124
+ }
125
+ return true;
126
+ }
92
127
  /**
93
128
  * Launches the user's default browser to open the provided URL using a platform-appropriate command.
94
129
  *
@@ -98,6 +133,10 @@ function commandExists(command) {
98
133
  * OAuth codes) from `url` before calling. Invocations are not atomic—concurrent calls may race but are
99
134
  * safe to perform.
100
135
  *
136
+ * Under WSL the browser lives on the Windows host, so `xdg-open` is the wrong tool and is usually not
137
+ * even installed. Prefer `wslview` (wslu), then Windows PowerShell via interop, before falling back to
138
+ * the Linux opener.
139
+ *
101
140
  * @param url - The URL to open; redact sensitive tokens (e.g., OAuth codes) before passing.
102
141
  * @returns `true` if a browser launch was attempted, `false` if no suitable opener was available or an exception occurred.
103
142
  */
@@ -108,17 +147,22 @@ export function openBrowserUrl(url) {
108
147
  }
109
148
  // Windows: use PowerShell Start-Process to avoid cmd/start quirks with URLs containing '&' or ':'
110
149
  if (process.platform === "win32") {
111
- if (!commandExists("powershell.exe")) {
112
- return false;
150
+ return spawnPowerShell(`Start-Process "${escapeForPowerShell(url)}"`);
151
+ }
152
+ if (isWsl()) {
153
+ // wslu's browser shim: hands the URL to the Windows default browser.
154
+ if (commandExists("wslview")) {
155
+ const child = spawn("wslview", [url], {
156
+ stdio: "ignore",
157
+ shell: false,
158
+ });
159
+ child.on("error", () => { });
160
+ return true;
113
161
  }
114
- // Escape PowerShell special characters: backticks, double quotes, and $ (sub-expression injection)
115
- const psUrl = url
116
- .replace(/`/g, "``")
117
- .replace(/\$/g, "`$")
118
- .replace(/"/g, '""');
119
- const child = spawn("powershell.exe", ["-NoLogo", "-NoProfile", "-Command", `Start-Process "${psUrl}"`], { stdio: "ignore" });
120
- child.on("error", () => { });
121
- return true;
162
+ if (spawnPowerShell(`Start-Process "${escapeForPowerShell(url)}"`)) {
163
+ return true;
164
+ }
165
+ // Fall through: an X/Wayland browser inside the distro is still possible.
122
166
  }
123
167
  const opener = getBrowserOpener();
124
168
  if (!commandExists(opener)) {
@@ -151,16 +195,24 @@ export function copyTextToClipboard(text) {
151
195
  if (!text)
152
196
  return false;
153
197
  if (process.platform === "win32") {
154
- if (!commandExists("powershell.exe")) {
155
- return false;
198
+ return spawnPowerShell(`Set-Clipboard -Value "${escapeForPowerShell(text)}"`);
199
+ }
200
+ if (isWsl()) {
201
+ // The clipboard the user pastes from is the Windows one, not the distro's.
202
+ if (commandExists("clip.exe")) {
203
+ const child = spawn("clip.exe", [], {
204
+ stdio: ["pipe", "ignore", "ignore"],
205
+ shell: false,
206
+ });
207
+ child.on("error", () => { });
208
+ child.stdin?.on("error", () => { });
209
+ child.stdin?.end(text);
210
+ return true;
156
211
  }
157
- const psText = text
158
- .replace(/`/g, "``")
159
- .replace(/\$/g, "`$")
160
- .replace(/"/g, '""');
161
- const child = spawn("powershell.exe", ["-NoLogo", "-NoProfile", "-Command", `Set-Clipboard -Value "${psText}"`], { stdio: "ignore" });
162
- child.on("error", () => { });
163
- return true;
212
+ if (spawnPowerShell(`Set-Clipboard -Value "${escapeForPowerShell(text)}"`)) {
213
+ return true;
214
+ }
215
+ // Fall through to the Linux clipboard tools.
164
216
  }
165
217
  if (process.platform === "darwin") {
166
218
  if (!commandExists("pbcopy"))
@@ -170,6 +222,7 @@ export function copyTextToClipboard(text) {
170
222
  shell: false,
171
223
  });
172
224
  child.on("error", () => { });
225
+ child.stdin?.on("error", () => { });
173
226
  child.stdin?.end(text);
174
227
  return true;
175
228
  }
@@ -186,6 +239,7 @@ export function copyTextToClipboard(text) {
186
239
  shell: false,
187
240
  });
188
241
  child.on("error", () => { });
242
+ child.stdin?.on("error", () => { });
189
243
  child.stdin?.end(text);
190
244
  return true;
191
245
  }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Operator-facing guidance for OAuth callback failures on the fixed callback port.
3
+ *
4
+ * The redirect URI is registered with the provider, so the port cannot be
5
+ * negotiated away when it is contended. The best the CLI can do is name the
6
+ * likely conflict and point at a flow that does not need the port at all.
7
+ *
8
+ * Windows and WSL contend for that port as far as a browser running on the
9
+ * Windows host is concerned, so a listener on either side can swallow a callback
10
+ * intended for the other. From inside the distro this is invisible: the WSL
11
+ * listener binds cleanly and simply never receives the redirect.
12
+ */
13
+ /**
14
+ * Why the browser callback did not produce an authorization code.
15
+ *
16
+ * - `bind-failed`: the local listener could not take the callback port at all.
17
+ * - `callback-timeout`: the listener bound, but no redirect ever arrived.
18
+ */
19
+ export type CallbackFailureReason = "bind-failed" | "callback-timeout";
20
+ /** @internal */
21
+ export type CallbackFailureContext = {
22
+ /** `errno` code from a failed listen, when one is known. */
23
+ bindErrorCode?: string;
24
+ };
25
+ /**
26
+ * Build the lines shown to the user when a browser OAuth callback fails.
27
+ *
28
+ * Contention is only *asserted* when it was actually observed — that is, the
29
+ * listen failed with `EADDRINUSE`. A callback that never arrives is far more
30
+ * often a cancelled or abandoned sign-in than a stolen redirect, so that case
31
+ * is phrased as a conditional ("if you completed sign-in and still landed
32
+ * here") rather than a diagnosis.
33
+ *
34
+ * @param reason - Which half of the callback flow broke.
35
+ * @param context - Extra detail from the failed bind, when available.
36
+ * @returns Guidance lines. Empty strings are intentional blank separators.
37
+ */
38
+ export declare function describeCallbackFailure(reason: CallbackFailureReason, context?: CallbackFailureContext): string[];
39
+ //# sourceMappingURL=callback-guidance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback-guidance.d.ts","sourceRoot":"","sources":["../../../lib/auth/callback-guidance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG,kBAAkB,CAAC;AAEvE,gBAAgB;AAChB,MAAM,MAAM,sBAAsB,GAAG;IACpC,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA4CF;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACtC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,sBAA2B,GAClC,MAAM,EAAE,CA0CV"}