Mass Block [bsky] Reposts [and more]
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: print OAuth URL and add manual callback paste fallback for SSH/WSL

Winter bb89ce09 de009030

+58 -2
+58 -2
index.js
··· 352 352 const { sessionPromise, close, ctx } = await startCallbackServer(); 353 353 ctx.oauthClient = oauthClient; 354 354 355 + let authUrl; 355 356 try { 356 357 const { url } = await oauthClient.authorize({ 357 358 target: { type: "account", identifier: handle }, 358 359 scope: SCOPE, 359 360 }); 361 + authUrl = url.toString(); 360 362 363 + // Fire-and-forget: errors suppressed (fails silently in SSH/WSL) 361 364 const { execFile } = await import("node:child_process"); 362 365 const cmd = 363 366 process.platform === "darwin" ? "open" : 364 367 process.platform === "win32" ? "start" : 365 368 "xdg-open"; 366 - execFile(cmd, [url.toString()]); 369 + execFile(cmd, [authUrl], () => {}); 367 370 } catch (err) { 368 371 close(); 369 372 throw new Error(`failed to start oauth flow: ${err.message}`); 370 373 } 371 374 372 - const session = await sessionPromise; 375 + p.log.info(`open this URL in your browser:\n ${color.cyan(authUrl)}`); 376 + p.log.info(`SSH/WSL: if the redirect fails, copy the full ${color.dim("http://127.0.0.1:" + PORT + "/callback?code=...")} URL from your browser and paste it below`); 377 + 378 + // Race: server callback vs manual paste 379 + let serverWon = false; 380 + 381 + // When server callback arrives, dismiss the paste prompt via a synthetic keypress 382 + sessionPromise.then(() => { 383 + serverWon = true; 384 + process.stdin.emit("keypress", "\r", { name: "return", ctrl: false, meta: false, shift: false }); 385 + }).catch(() => {}); // suppress unhandled rejection if paste wins first 386 + 387 + const pastePrompt = new Prompt({ 388 + validate(value) { 389 + if (serverWon) return; // server resolved -- accept empty input to dismiss 390 + if (!value) return "waiting for browser redirect... or paste the full callback URL"; 391 + try { 392 + const u = new URL(value); 393 + if (!u.searchParams.has("code")) return "paste the full redirect URL from your browser (must include ?code=...)"; 394 + } catch { 395 + return "not a valid URL"; 396 + } 397 + }, 398 + render() { 399 + const prefix = color.gray("│"); 400 + switch (this.state) { 401 + case "submit": 402 + return `${color.gray("◇")} ${serverWon ? "authenticated via browser" : "callback URL received"}`; 403 + case "error": 404 + return `${color.yellow("▲")} waiting for OAuth\n${prefix} ${this.value || ""}\n${prefix} ${color.yellow(this.error)}`; 405 + default: 406 + return `${color.cyan("◆")} waiting for OAuth (paste callback URL if redirect failed)\n${prefix} ${this.value || color.dim("http://127.0.0.1:" + PORT + "/callback?code=...")}`; 407 + } 408 + }, 409 + }); 410 + 411 + const pastedInput = await pastePrompt.prompt(); 412 + 413 + if (p.isCancel(pastedInput)) { 414 + close(); 415 + p.cancel("cancelled."); 416 + process.exit(0); 417 + } 418 + 419 + if (serverWon || !pastedInput) { 420 + // Browser callback completed 421 + const session = await sessionPromise; 422 + close(); 423 + return session; 424 + } 425 + 426 + // User pasted the callback URL manually 373 427 close(); 428 + const params = new URL(pastedInput).searchParams; 429 + const { session } = await oauthClient.callback(params); 374 430 return session; 375 431 } 376 432