Skip to content

Commit 01b333f

Browse files
committed
feat: detect headless environments and skip OAuth listener
Skip attempting to start local OAuth callback listener in SSH/headless environments to avoid unnecessary fallback warnings. Checks for SSH_CONNECTION, SSH_CLIENT, SSH_TTY, and OPENCODE_HEADLESS environment variables.
1 parent 83cdd66 commit 01b333f

File tree

1 file changed

+37
-19
lines changed

1 file changed

+37
-19
lines changed

src/plugin.ts

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -115,29 +115,47 @@ export const GeminiCLIOAuthPlugin = async (
115115
authorize: async () => {
116116
console.log("\n=== Google Gemini OAuth Setup ===");
117117

118+
// Detect headless/SSH environment
119+
const isHeadless = !!(
120+
process.env.SSH_CONNECTION ||
121+
process.env.SSH_CLIENT ||
122+
process.env.SSH_TTY ||
123+
process.env.OPENCODE_HEADLESS
124+
);
125+
118126
let listener: OAuthListener | null = null;
119-
try {
120-
listener = await startOAuthListener();
121-
const { host } = new URL(GEMINI_REDIRECT_URI);
122-
console.log("1. You'll be asked to sign in to your Google account and grant permission.");
123-
console.log(
124-
`2. We'll automatically capture the browser redirect on http://${host}. No need to paste anything back here.`,
125-
);
126-
console.log("3. Once you see the 'Authentication complete' page in your browser, return to this terminal.");
127-
} catch (error) {
127+
if (!isHeadless) {
128+
try {
129+
listener = await startOAuthListener();
130+
const { host } = new URL(GEMINI_REDIRECT_URI);
131+
console.log("1. You'll be asked to sign in to your Google account and grant permission.");
132+
console.log(
133+
`2. We'll automatically capture the browser redirect on http://${host}. No need to paste anything back here.`,
134+
);
135+
console.log("3. Once you see the 'Authentication complete' page in your browser, return to this terminal.");
136+
} catch (error) {
137+
console.log("1. You'll be asked to sign in to your Google account and grant permission.");
138+
console.log("2. After you approve, the browser will try to redirect to a 'localhost' page.");
139+
console.log(
140+
"3. This page will show an error like 'This site can't be reached'. This is perfectly normal and means it worked!",
141+
);
142+
console.log(
143+
"4. Once you see that error, copy the entire URL from the address bar, paste it back here, and press Enter.",
144+
);
145+
if (error instanceof Error) {
146+
console.log(`\nWarning: Couldn't start the local callback listener (${error.message}). Falling back to manual copy/paste.`);
147+
} else {
148+
console.log("\nWarning: Couldn't start the local callback listener. Falling back to manual copy/paste.");
149+
}
150+
}
151+
} else {
152+
console.log("Headless environment detected. Using manual OAuth flow.");
128153
console.log("1. You'll be asked to sign in to your Google account and grant permission.");
129-
console.log("2. After you approve, the browser will try to redirect to a 'localhost' page.");
154+
console.log("2. After you approve, the browser will redirect to a 'localhost' URL.");
130155
console.log(
131-
"3. This page will show an error like 'This site can’t be reached'. This is perfectly normal and means it worked!",
156+
"3. Copy the ENTIRE URL from your browser's address bar (it will look like: http://localhost:8085/oauth2callback?code=...&state=...)",
132157
);
133-
console.log(
134-
"4. Once you see that error, copy the entire URL from the address bar, paste it back here, and press Enter.",
135-
);
136-
if (error instanceof Error) {
137-
console.log(`\nWarning: Couldn't start the local callback listener (${error.message}). Falling back to manual copy/paste.`);
138-
} else {
139-
console.log("\nWarning: Couldn't start the local callback listener. Falling back to manual copy/paste.");
140-
}
158+
console.log("4. Paste the URL back here and press Enter.");
141159
}
142160
console.log("\n");
143161

0 commit comments

Comments
 (0)