From 83988bb21b2ebbc90079e028dd1e31c50556fd81 Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sat, 16 May 2026 19:22:12 -0400 Subject: [PATCH 1/2] feat(B-0581): decompose slice 1 (gh auth refresh wrapper basics) --- tools/auth/gh-auth-refresh-wrapper.ts | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100755 tools/auth/gh-auth-refresh-wrapper.ts diff --git a/tools/auth/gh-auth-refresh-wrapper.ts b/tools/auth/gh-auth-refresh-wrapper.ts new file mode 100755 index 0000000000..a11e977141 --- /dev/null +++ b/tools/auth/gh-auth-refresh-wrapper.ts @@ -0,0 +1,58 @@ +#!/usr/bin/env bun +import { spawn } from "bun"; + +async function main() { + const args = process.argv.slice(2); + if (args.length === 0) { + console.error("Usage: bun run tools/auth/gh-auth-refresh-wrapper.ts "); + process.exit(1); + } + + const scopes = args[0]; + console.log(`[gh-auth-refresh-wrapper] Starting gh auth refresh for scopes: ${scopes}`); + + const proc = spawn(["gh", "auth", "refresh", "-h", "github.com", "-s", scopes], { + stdin: "piped", + stdout: "piped", + stderr: "inherit", + }); + + const reader = proc.stdout.getReader(); + const decoder = new TextDecoder(); + let codeFound = false; + + async function processStream() { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + const text = decoder.decode(value); + process.stdout.write(text); + + // Look for the Yes/No prompt + if (text.includes("Authenticate Git with your GitHub credentials?")) { + console.log("\n[gh-auth-refresh-wrapper] Auto-answering 'Y' to auth prompt..."); + proc.stdin.write("Y\n"); + proc.stdin.flush(); + } + + // Look for the one-time code + const codeMatch = text.match(/one-time code:\s*([A-Z0-9\-]+)/i); + if (codeMatch && !codeFound) { + codeFound = true; + const code = codeMatch[1]; + console.log(`\n=========================================`); + console.log(`[gh-auth-refresh-wrapper] ONE-TIME CODE CAPTURED: ${code}`); + console.log(`=========================================\n`); + // Slice 2 will add pbcopy and osascript UI. Slice 3 will add auto-Enter pumping. + } + } + } + + processStream().catch(console.error); + + const exitCode = await proc.exited; + console.log(`[gh-auth-refresh-wrapper] gh process exited with code ${exitCode}`); + process.exit(exitCode); +} + +main(); From ccc4dd6d7c87499cdc801a9feac76c7c7ee2ba7a Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Fri, 22 May 2026 14:37:08 -0400 Subject: [PATCH 2/2] fix(auth): address typescript errors and bugs --- tools/auth/gh-auth-refresh-wrapper.ts | 31 +++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/tools/auth/gh-auth-refresh-wrapper.ts b/tools/auth/gh-auth-refresh-wrapper.ts index a11e977141..287bbe6d95 100755 --- a/tools/auth/gh-auth-refresh-wrapper.ts +++ b/tools/auth/gh-auth-refresh-wrapper.ts @@ -9,17 +9,27 @@ async function main() { } const scopes = args[0]; + if (!scopes) { + console.error("Scope list cannot be empty."); + process.exit(1); + } console.log(`[gh-auth-refresh-wrapper] Starting gh auth refresh for scopes: ${scopes}`); const proc = spawn(["gh", "auth", "refresh", "-h", "github.com", "-s", scopes], { - stdin: "piped", - stdout: "piped", + stdin: "pipe", + stdout: "pipe", stderr: "inherit", }); + if (!proc.stdout || !proc.stdin) { + console.error("Failed to get piped stdin/stdout for gh process."); + process.exit(1); + } + const reader = proc.stdout.getReader(); const decoder = new TextDecoder(); let codeFound = false; + let buffer = ""; async function processStream() { while (true) { @@ -27,28 +37,31 @@ async function main() { if (done) break; const text = decoder.decode(value); process.stdout.write(text); + buffer += text; // Look for the Yes/No prompt - if (text.includes("Authenticate Git with your GitHub credentials?")) { + if (buffer.includes("Authenticate Git with your GitHub credentials?")) { console.log("\n[gh-auth-refresh-wrapper] Auto-answering 'Y' to auth prompt..."); - proc.stdin.write("Y\n"); - proc.stdin.flush(); + if(proc.stdin) { + proc.stdin.write("Y\n"); + proc.stdin.flush(); + } } // Look for the one-time code - const codeMatch = text.match(/one-time code:\s*([A-Z0-9\-]+)/i); + const codeMatch = buffer.match(/one-time code:\s*([A-Z0-9\-]+)/i); if (codeMatch && !codeFound) { codeFound = true; const code = codeMatch[1]; - console.log(`\n=========================================`); + console.log("\n========================================="); console.log(`[gh-auth-refresh-wrapper] ONE-TIME CODE CAPTURED: ${code}`); - console.log(`=========================================\n`); + console.log("=========================================\n"); // Slice 2 will add pbcopy and osascript UI. Slice 3 will add auto-Enter pumping. } } } - processStream().catch(console.error); + await processStream(); const exitCode = await proc.exited; console.log(`[gh-auth-refresh-wrapper] gh process exited with code ${exitCode}`);