chrome-devtools-mcp-for-extension 0.18.4 → 0.18.6
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/build/src/tools/gemini-web.js +53 -10
- package/package.json +1 -1
|
@@ -10,6 +10,32 @@ import { ToolCategories } from './categories.js';
|
|
|
10
10
|
import { defineTool } from './ToolDefinition.js';
|
|
11
11
|
import { GEMINI_CONFIG } from '../config.js';
|
|
12
12
|
import { isLoginRequired } from '../login-helper.js';
|
|
13
|
+
/**
|
|
14
|
+
* Navigate with retry logic for handling ERR_ABORTED and other network errors
|
|
15
|
+
*/
|
|
16
|
+
async function navigateWithRetry(page, url, options = { waitUntil: 'networkidle2', maxRetries: 3 }) {
|
|
17
|
+
const { waitUntil, maxRetries = 3 } = options;
|
|
18
|
+
let lastError = null;
|
|
19
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
20
|
+
try {
|
|
21
|
+
await page.goto(url, { waitUntil, timeout: 30000 });
|
|
22
|
+
return; // Success
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
26
|
+
// Check if it's a retryable error
|
|
27
|
+
const isRetryable = lastError.message.includes('ERR_ABORTED') ||
|
|
28
|
+
lastError.message.includes('ERR_CONNECTION_RESET') ||
|
|
29
|
+
lastError.message.includes('net::ERR_');
|
|
30
|
+
if (!isRetryable || attempt === maxRetries) {
|
|
31
|
+
throw lastError;
|
|
32
|
+
}
|
|
33
|
+
// Wait before retry (exponential backoff)
|
|
34
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
throw lastError;
|
|
38
|
+
}
|
|
13
39
|
/**
|
|
14
40
|
* Path to store chat session data
|
|
15
41
|
*/
|
|
@@ -146,7 +172,7 @@ export const askGeminiWeb = defineTool({
|
|
|
146
172
|
const page = context.getSelectedPage();
|
|
147
173
|
try {
|
|
148
174
|
response.appendResponseLine('Geminiに接続中...');
|
|
149
|
-
await page
|
|
175
|
+
await navigateWithRetry(page, GEMINI_CONFIG.DEFAULT_URL, { waitUntil: 'networkidle2' });
|
|
150
176
|
const needsLogin = await isLoginRequired(page);
|
|
151
177
|
if (needsLogin) {
|
|
152
178
|
response.appendResponseLine('\n❌ Geminiへのログインが必要です');
|
|
@@ -163,7 +189,7 @@ export const askGeminiWeb = defineTool({
|
|
|
163
189
|
const sortedSessions = [...projectSessions].sort((a, b) => new Date(b.lastUsed).getTime() - new Date(a.lastUsed).getTime());
|
|
164
190
|
const latestSession = sortedSessions[0];
|
|
165
191
|
response.appendResponseLine(`既存のチャットを使用: ${latestSession.url}`);
|
|
166
|
-
await page
|
|
192
|
+
await navigateWithRetry(page, latestSession.url, { waitUntil: 'networkidle2' });
|
|
167
193
|
sessionChatId = latestSession.chatId;
|
|
168
194
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
169
195
|
}
|
|
@@ -176,7 +202,7 @@ export const askGeminiWeb = defineTool({
|
|
|
176
202
|
}
|
|
177
203
|
if (isNewChat) {
|
|
178
204
|
response.appendResponseLine('新規チャットを作成中...');
|
|
179
|
-
await page
|
|
205
|
+
await navigateWithRetry(page, GEMINI_CONFIG.BASE_URL + 'app', { waitUntil: 'networkidle2' });
|
|
180
206
|
}
|
|
181
207
|
response.appendResponseLine('質問を送信中...');
|
|
182
208
|
// Input text using the textbox element
|
|
@@ -255,17 +281,34 @@ export const askGeminiWeb = defineTool({
|
|
|
255
281
|
b.textContent?.includes('Stop') ||
|
|
256
282
|
b.getAttribute('aria-label')?.includes('Stop'));
|
|
257
283
|
// Check for "プロンプトを送信" button - this indicates response is complete
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
b.
|
|
261
|
-
|
|
284
|
+
// Must be enabled (not disabled) to indicate completion
|
|
285
|
+
const sendButton = buttons.find(b => {
|
|
286
|
+
const hasLabel = b.textContent?.includes('プロンプトを送信') ||
|
|
287
|
+
b.getAttribute('aria-label')?.includes('プロンプトを送信') ||
|
|
288
|
+
b.getAttribute('aria-label')?.includes('Send message');
|
|
289
|
+
return hasLabel && !b.disabled;
|
|
290
|
+
});
|
|
291
|
+
// Check for status text and thinking indicators
|
|
262
292
|
const bodyText = document.body.innerText;
|
|
263
293
|
const isTyping = bodyText.includes('Gemini が入力中です') ||
|
|
264
294
|
bodyText.includes('Gemini is typing');
|
|
265
|
-
|
|
295
|
+
// Check for thinking/analyzing indicators (Gemini shows these during processing)
|
|
296
|
+
const isThinking = bodyText.includes('Analyzing') ||
|
|
297
|
+
bodyText.includes('分析中') ||
|
|
298
|
+
bodyText.includes('Crafting') ||
|
|
299
|
+
bodyText.includes('作成中') ||
|
|
300
|
+
bodyText.includes('Thinking') ||
|
|
301
|
+
bodyText.includes('思考中') ||
|
|
302
|
+
bodyText.includes('Researching') ||
|
|
303
|
+
bodyText.includes('調査中');
|
|
304
|
+
// Check for loading spinners or progress indicators
|
|
305
|
+
const hasSpinner = document.querySelector('[role="progressbar"]') !== null ||
|
|
306
|
+
document.querySelector('.loading') !== null ||
|
|
307
|
+
document.querySelector('[aria-busy="true"]') !== null;
|
|
308
|
+
const isComplete = (bodyText.includes('Gemini が回答しました') ||
|
|
266
309
|
bodyText.includes('Gemini has responded') ||
|
|
267
|
-
!!sendButton
|
|
268
|
-
const isGenerating = !!stopButton || isTyping;
|
|
310
|
+
!!sendButton) && !isThinking && !hasSpinner;
|
|
311
|
+
const isGenerating = !!stopButton || isTyping || isThinking || hasSpinner;
|
|
269
312
|
// Get the response content from model-response elements
|
|
270
313
|
const modelResponses = Array.from(document.querySelectorAll('model-response'));
|
|
271
314
|
let responseContent = '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-devtools-mcp-for-extension",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.6",
|
|
4
4
|
"description": "MCP server for Chrome extension development with Web Store automation. Fork of chrome-devtools-mcp with extension-specific tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./scripts/cli.mjs",
|