opencode-gemini-auth 1.1.3 → 1.1.5
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/package.json +1 -1
- package/src/plugin/request.ts +32 -0
- package/src/plugin/token.ts +3 -4
- package/src/plugin.ts +37 -19
package/package.json
CHANGED
package/src/plugin/request.ts
CHANGED
|
@@ -162,6 +162,38 @@ export async function transformGeminiResponse(
|
|
|
162
162
|
try {
|
|
163
163
|
const text = await response.text();
|
|
164
164
|
const headers = new Headers(response.headers);
|
|
165
|
+
|
|
166
|
+
// Extract retry timing from Google's structured error response
|
|
167
|
+
// Google returns retry timing in error.details[].retryDelay: "55.846891726s"
|
|
168
|
+
if (!response.ok && text) {
|
|
169
|
+
try {
|
|
170
|
+
const errorBody = JSON.parse(text);
|
|
171
|
+
if (errorBody?.error?.details && Array.isArray(errorBody.error.details)) {
|
|
172
|
+
// Look for RetryInfo type
|
|
173
|
+
const retryInfo = errorBody.error.details.find(
|
|
174
|
+
(detail: any) => detail['@type'] === 'type.googleapis.com/google.rpc.RetryInfo'
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (retryInfo?.retryDelay) {
|
|
178
|
+
// Parse "55.846891726s" format
|
|
179
|
+
const match = retryInfo.retryDelay.match(/^([\d.]+)s$/);
|
|
180
|
+
if (match && match[1]) {
|
|
181
|
+
const retrySeconds = parseFloat(match[1]);
|
|
182
|
+
if (!isNaN(retrySeconds) && retrySeconds > 0) {
|
|
183
|
+
// Add both formats for compatibility
|
|
184
|
+
const retryAfterSec = Math.ceil(retrySeconds).toString();
|
|
185
|
+
const retryAfterMs = Math.ceil(retrySeconds * 1000).toString();
|
|
186
|
+
headers.set('Retry-After', retryAfterSec);
|
|
187
|
+
headers.set('retry-after-ms', retryAfterMs);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
} catch (parseError) {
|
|
193
|
+
// If JSON parsing fails, continue without retry headers
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
165
197
|
const init = {
|
|
166
198
|
status: response.status,
|
|
167
199
|
statusText: response.statusText,
|
package/src/plugin/token.ts
CHANGED
|
@@ -139,14 +139,13 @@ export async function refreshAccessToken(
|
|
|
139
139
|
storeCachedAuth(updatedAuth);
|
|
140
140
|
invalidateProjectContextCache(auth.refresh);
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
typeof payload.refresh_token === "string" && payload.refresh_token !== parts.refreshToken;
|
|
144
|
-
|
|
145
|
-
if (refreshTokenRotated) {
|
|
142
|
+
try {
|
|
146
143
|
await client.auth.set({
|
|
147
144
|
path: { id: GEMINI_PROVIDER_ID },
|
|
148
145
|
body: updatedAuth,
|
|
149
146
|
});
|
|
147
|
+
} catch (storeError) {
|
|
148
|
+
console.error("Failed to persist refreshed Gemini OAuth credentials:", storeError);
|
|
150
149
|
}
|
|
151
150
|
|
|
152
151
|
return updatedAuth;
|
package/src/plugin.ts
CHANGED
|
@@ -115,29 +115,47 @@ export const GeminiCLIOAuthPlugin = async (
|
|
|
115
115
|
authorize: async () => {
|
|
116
116
|
console.log("\n=== Google Gemini OAuth Setup ===");
|
|
117
117
|
|
|
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
|
+
|
|
118
126
|
let listener: OAuthListener | null = null;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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.");
|
|
128
153
|
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
|
|
154
|
+
console.log("2. After you approve, the browser will redirect to a 'localhost' URL.");
|
|
130
155
|
console.log(
|
|
131
|
-
"3.
|
|
156
|
+
"3. Copy the ENTIRE URL from your browser's address bar (it will look like: http://localhost:8085/oauth2callback?code=...&state=...)",
|
|
132
157
|
);
|
|
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.");
|
|
141
159
|
}
|
|
142
160
|
console.log("\n");
|
|
143
161
|
|