chrome-devtools-mcp-for-extension 0.9.36 → 0.10.1
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.
|
@@ -13,12 +13,33 @@ import { defineTool } from './ToolDefinition.js';
|
|
|
13
13
|
*/
|
|
14
14
|
const CHAT_SESSIONS_FILE = path.join(process.cwd(), 'docs/ask/chatgpt/.chat-sessions.json');
|
|
15
15
|
/**
|
|
16
|
-
* Load chat sessions from JSON file
|
|
16
|
+
* Load chat sessions from JSON file with backward compatibility
|
|
17
17
|
*/
|
|
18
18
|
async function loadChatSessions() {
|
|
19
19
|
try {
|
|
20
20
|
const data = await fs.promises.readFile(CHAT_SESSIONS_FILE, 'utf-8');
|
|
21
|
-
|
|
21
|
+
const parsed = JSON.parse(data);
|
|
22
|
+
// Migrate from old format (single object) to new format (array)
|
|
23
|
+
const migrated = {};
|
|
24
|
+
for (const [projectName, value] of Object.entries(parsed)) {
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
// Already in new format
|
|
27
|
+
migrated[projectName] = value;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// Old format - convert to array
|
|
31
|
+
const oldSession = value;
|
|
32
|
+
migrated[projectName] = [{
|
|
33
|
+
chatId: oldSession.chatId,
|
|
34
|
+
url: oldSession.url,
|
|
35
|
+
lastUsed: oldSession.lastUsed,
|
|
36
|
+
title: oldSession.title,
|
|
37
|
+
createdAt: oldSession.lastUsed, // Use lastUsed as createdAt for old sessions
|
|
38
|
+
conversationCount: 1,
|
|
39
|
+
}];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return migrated;
|
|
22
43
|
}
|
|
23
44
|
catch {
|
|
24
45
|
return {};
|
|
@@ -29,7 +50,27 @@ async function loadChatSessions() {
|
|
|
29
50
|
*/
|
|
30
51
|
async function saveChatSession(projectName, session) {
|
|
31
52
|
const sessions = await loadChatSessions();
|
|
32
|
-
|
|
53
|
+
// Initialize project array if it doesn't exist
|
|
54
|
+
if (!sessions[projectName]) {
|
|
55
|
+
sessions[projectName] = [];
|
|
56
|
+
}
|
|
57
|
+
// Check if session with same chatId already exists
|
|
58
|
+
const existingIndex = sessions[projectName].findIndex(s => s.chatId === session.chatId);
|
|
59
|
+
if (existingIndex >= 0) {
|
|
60
|
+
// Update existing session
|
|
61
|
+
sessions[projectName][existingIndex] = {
|
|
62
|
+
...sessions[projectName][existingIndex],
|
|
63
|
+
...session,
|
|
64
|
+
lastUsed: new Date().toISOString(),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Add new session to array
|
|
69
|
+
sessions[projectName].push({
|
|
70
|
+
...session,
|
|
71
|
+
createdAt: session.createdAt || new Date().toISOString(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
33
74
|
// Ensure directory exists
|
|
34
75
|
const dir = path.dirname(CHAT_SESSIONS_FILE);
|
|
35
76
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
@@ -146,11 +187,14 @@ export const askChatGPTWeb = defineTool({
|
|
|
146
187
|
if (!createNewChat) {
|
|
147
188
|
// Try to load existing session for this project
|
|
148
189
|
const sessions = await loadChatSessions();
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
190
|
+
const projectSessions = sessions[project] || [];
|
|
191
|
+
if (projectSessions.length > 0) {
|
|
192
|
+
// Get the most recently used session
|
|
193
|
+
const sortedSessions = [...projectSessions].sort((a, b) => new Date(b.lastUsed).getTime() - new Date(a.lastUsed).getTime());
|
|
194
|
+
const latestSession = sortedSessions[0];
|
|
195
|
+
response.appendResponseLine(`既存のプロジェクトチャットを使用: ${latestSession.url}`);
|
|
196
|
+
await page.goto(latestSession.url, { waitUntil: 'networkidle2' });
|
|
197
|
+
sessionChatId = latestSession.chatId;
|
|
154
198
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
155
199
|
}
|
|
156
200
|
else {
|
|
@@ -416,11 +460,14 @@ export const askChatGPTWeb = defineTool({
|
|
|
416
460
|
const chatIdMatch = chatUrl.match(/\/c\/([a-f0-9-]+)/);
|
|
417
461
|
if (chatIdMatch) {
|
|
418
462
|
const chatId = chatIdMatch[1];
|
|
463
|
+
const now = new Date().toISOString();
|
|
419
464
|
await saveChatSession(project, {
|
|
420
465
|
chatId,
|
|
421
466
|
url: chatUrl,
|
|
422
|
-
lastUsed:
|
|
467
|
+
lastUsed: now,
|
|
468
|
+
createdAt: now,
|
|
423
469
|
title: `[Project: ${project}]`,
|
|
470
|
+
conversationCount: 1,
|
|
424
471
|
});
|
|
425
472
|
sessionChatId = chatId;
|
|
426
473
|
response.appendResponseLine(`✅ チャットセッション保存: ${chatId}`);
|
|
@@ -430,14 +477,19 @@ export const askChatGPTWeb = defineTool({
|
|
|
430
477
|
}
|
|
431
478
|
}
|
|
432
479
|
else {
|
|
433
|
-
// Update last used timestamp for existing session
|
|
480
|
+
// Update last used timestamp and conversation count for existing session
|
|
434
481
|
if (sessionChatId) {
|
|
435
482
|
const chatUrl = page.url();
|
|
483
|
+
const sessions = await loadChatSessions();
|
|
484
|
+
const projectSessions = sessions[project] || [];
|
|
485
|
+
const existingSession = projectSessions.find(s => s.chatId === sessionChatId);
|
|
436
486
|
await saveChatSession(project, {
|
|
437
487
|
chatId: sessionChatId,
|
|
438
488
|
url: chatUrl,
|
|
439
489
|
lastUsed: new Date().toISOString(),
|
|
440
|
-
|
|
490
|
+
createdAt: existingSession?.createdAt || new Date().toISOString(),
|
|
491
|
+
title: existingSession?.title || `[Project: ${project}]`,
|
|
492
|
+
conversationCount: (existingSession?.conversationCount || 0) + 1,
|
|
441
493
|
});
|
|
442
494
|
}
|
|
443
495
|
}
|
|
@@ -13,12 +13,33 @@ import { defineTool } from './ToolDefinition.js';
|
|
|
13
13
|
*/
|
|
14
14
|
const CHAT_SESSIONS_FILE = path.join(process.cwd(), 'docs/ask/chatgpt/.chat-sessions.json');
|
|
15
15
|
/**
|
|
16
|
-
* Load chat sessions from JSON file
|
|
16
|
+
* Load chat sessions from JSON file with backward compatibility
|
|
17
17
|
*/
|
|
18
18
|
async function loadChatSessions() {
|
|
19
19
|
try {
|
|
20
20
|
const data = await fs.promises.readFile(CHAT_SESSIONS_FILE, 'utf-8');
|
|
21
|
-
|
|
21
|
+
const parsed = JSON.parse(data);
|
|
22
|
+
// Migrate from old format (single object) to new format (array)
|
|
23
|
+
const migrated = {};
|
|
24
|
+
for (const [projectName, value] of Object.entries(parsed)) {
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
// Already in new format
|
|
27
|
+
migrated[projectName] = value;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// Old format - convert to array
|
|
31
|
+
const oldSession = value;
|
|
32
|
+
migrated[projectName] = [{
|
|
33
|
+
chatId: oldSession.chatId,
|
|
34
|
+
url: oldSession.url,
|
|
35
|
+
lastUsed: oldSession.lastUsed,
|
|
36
|
+
title: oldSession.title,
|
|
37
|
+
createdAt: oldSession.lastUsed, // Use lastUsed as createdAt for old sessions
|
|
38
|
+
conversationCount: 1,
|
|
39
|
+
}];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return migrated;
|
|
22
43
|
}
|
|
23
44
|
catch {
|
|
24
45
|
return {};
|
|
@@ -29,7 +50,27 @@ async function loadChatSessions() {
|
|
|
29
50
|
*/
|
|
30
51
|
async function saveChatSession(projectName, session) {
|
|
31
52
|
const sessions = await loadChatSessions();
|
|
32
|
-
|
|
53
|
+
// Initialize project array if it doesn't exist
|
|
54
|
+
if (!sessions[projectName]) {
|
|
55
|
+
sessions[projectName] = [];
|
|
56
|
+
}
|
|
57
|
+
// Check if session with same chatId already exists
|
|
58
|
+
const existingIndex = sessions[projectName].findIndex(s => s.chatId === session.chatId);
|
|
59
|
+
if (existingIndex >= 0) {
|
|
60
|
+
// Update existing session
|
|
61
|
+
sessions[projectName][existingIndex] = {
|
|
62
|
+
...sessions[projectName][existingIndex],
|
|
63
|
+
...session,
|
|
64
|
+
lastUsed: new Date().toISOString(),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// Add new session to array
|
|
69
|
+
sessions[projectName].push({
|
|
70
|
+
...session,
|
|
71
|
+
createdAt: session.createdAt || new Date().toISOString(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
33
74
|
const dir = path.dirname(CHAT_SESSIONS_FILE);
|
|
34
75
|
await fs.promises.mkdir(dir, { recursive: true });
|
|
35
76
|
await fs.promises.writeFile(CHAT_SESSIONS_FILE, JSON.stringify(sessions, null, 2), 'utf-8');
|
|
@@ -218,56 +259,31 @@ async function detectDeepResearchMode(page) {
|
|
|
218
259
|
async function enableDeepResearchMode(page, response) {
|
|
219
260
|
try {
|
|
220
261
|
response.appendResponseLine('DeepResearchモードを有効化中...');
|
|
221
|
-
// Step 1:
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const aria = btn.getAttribute('aria-label') || '';
|
|
226
|
-
return aria.includes('ファイルの追加');
|
|
227
|
-
});
|
|
228
|
-
if (!plusButton)
|
|
229
|
-
return { success: false, error: '+ボタン(ファイルの追加など)が見つかりません' };
|
|
230
|
-
// Return selector info instead of clicking
|
|
231
|
-
const ariaLabel = plusButton.getAttribute('aria-label');
|
|
232
|
-
return { success: true, ariaLabel };
|
|
233
|
-
});
|
|
234
|
-
if (!plusButtonSelector.success) {
|
|
235
|
-
return { success: false, error: plusButtonSelector.error };
|
|
262
|
+
// Step 1: +ボタンをクリック(Puppeteer click)
|
|
263
|
+
const plusButton = await page.$('button[aria-label*="ファイルの追加"]');
|
|
264
|
+
if (!plusButton) {
|
|
265
|
+
return { success: false, error: '+ボタンが見つかりません' };
|
|
236
266
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
//
|
|
241
|
-
await page
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
success: false,
|
|
250
|
-
error: `DeepResearch menuitemradio が見つかりません (found: ${menuItems.length} items: ${menuItems.map(m => m.textContent?.trim()).join(', ')})`,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
// Check if already checked
|
|
254
|
-
const isChecked = deepResearchItem.getAttribute('aria-checked') === 'true';
|
|
255
|
-
if (!isChecked) {
|
|
256
|
-
deepResearchItem.click();
|
|
267
|
+
await plusButton.click();
|
|
268
|
+
response.appendResponseLine('✅ +ボタンをクリック');
|
|
269
|
+
await page.waitForTimeout(500);
|
|
270
|
+
// Step 2: Deep Research menuitemradio をクリック(Puppeteer click)
|
|
271
|
+
const menuItems = await page.$$('[role="menuitemradio"]');
|
|
272
|
+
let deepResearchItem = null;
|
|
273
|
+
for (const item of menuItems) {
|
|
274
|
+
const text = await item.evaluate((el) => el.textContent);
|
|
275
|
+
if (text?.includes('Deep Research') || text?.includes('リサーチ')) {
|
|
276
|
+
deepResearchItem = item;
|
|
277
|
+
break;
|
|
257
278
|
}
|
|
258
|
-
return { success: true, alreadyEnabled: isChecked };
|
|
259
|
-
});
|
|
260
|
-
if (!deepResearchResult.success) {
|
|
261
|
-
return { success: false, error: deepResearchResult.error };
|
|
262
|
-
}
|
|
263
|
-
if (deepResearchResult.alreadyEnabled) {
|
|
264
|
-
response.appendResponseLine('✅ DeepResearch は既に有効です');
|
|
265
279
|
}
|
|
266
|
-
|
|
267
|
-
|
|
280
|
+
if (!deepResearchItem) {
|
|
281
|
+
return { success: false, error: 'Deep Research menuitemradio が見つかりません' };
|
|
268
282
|
}
|
|
269
|
-
await
|
|
270
|
-
|
|
283
|
+
await deepResearchItem.click();
|
|
284
|
+
response.appendResponseLine('✅ Deep Research menuitemradio をクリック');
|
|
285
|
+
await page.waitForTimeout(1000);
|
|
286
|
+
// Step 3: 検証(composer-pill確認)
|
|
271
287
|
const verification = await detectDeepResearchMode(page);
|
|
272
288
|
if (!verification.isEnabled) {
|
|
273
289
|
return {
|
|
@@ -564,10 +580,13 @@ export const deepResearchChatGPT = defineTool({
|
|
|
564
580
|
if (reuseSession) {
|
|
565
581
|
// Try to load existing session
|
|
566
582
|
const sessions = await loadChatSessions();
|
|
567
|
-
const
|
|
568
|
-
if (
|
|
569
|
-
|
|
570
|
-
|
|
583
|
+
const projectSessions = sessions[project] || [];
|
|
584
|
+
if (projectSessions.length > 0) {
|
|
585
|
+
// Get the most recently used session
|
|
586
|
+
const sortedSessions = [...projectSessions].sort((a, b) => new Date(b.lastUsed).getTime() - new Date(a.lastUsed).getTime());
|
|
587
|
+
const latestSession = sortedSessions[0];
|
|
588
|
+
response.appendResponseLine(`既存のプロジェクトチャットを使用: ${latestSession.url}`);
|
|
589
|
+
await page.goto(latestSession.url, { waitUntil: 'networkidle2' });
|
|
571
590
|
needsNewChat = false;
|
|
572
591
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
573
592
|
}
|
|
@@ -653,11 +672,14 @@ export const deepResearchChatGPT = defineTool({
|
|
|
653
672
|
const chatIdMatch = chatUrl.match(/\/c\/([a-f0-9-]+)/);
|
|
654
673
|
if (chatIdMatch) {
|
|
655
674
|
const chatId = chatIdMatch[1];
|
|
675
|
+
const now = new Date().toISOString();
|
|
656
676
|
await saveChatSession(project, {
|
|
657
677
|
chatId,
|
|
658
678
|
url: chatUrl,
|
|
659
|
-
lastUsed:
|
|
679
|
+
lastUsed: now,
|
|
680
|
+
createdAt: now,
|
|
660
681
|
title: `[DeepResearch: ${project}]`,
|
|
682
|
+
conversationCount: 1,
|
|
661
683
|
});
|
|
662
684
|
response.appendResponseLine(`💾 チャットセッション保存: ${chatId}`);
|
|
663
685
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepResearch ON/OFF切り替え手順記録システム
|
|
3
|
+
*
|
|
4
|
+
* 目的: 毎回要素を探す必要をなくし、記録された手順で確実に切り替え
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
const PROCEDURE_FILE = path.join(process.cwd(), 'docs/deepresearch-procedure.json');
|
|
9
|
+
/**
|
|
10
|
+
* デフォルトの手順定義
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_PROCEDURE = {
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
lastUpdated: new Date().toISOString(),
|
|
15
|
+
steps: {
|
|
16
|
+
enable: [
|
|
17
|
+
{
|
|
18
|
+
action: 'click',
|
|
19
|
+
selector: 'button[aria-label*="ファイルの追加"]',
|
|
20
|
+
description: '+ボタンをクリックしてメニューを開く'
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
action: 'wait',
|
|
24
|
+
waitMs: 500,
|
|
25
|
+
description: 'メニューが表示されるまで待機'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
action: 'click',
|
|
29
|
+
selector: '[role="menuitemradio"]',
|
|
30
|
+
textContent: 'Deep Research',
|
|
31
|
+
description: 'Deep Research menuitemradio をクリック'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
action: 'wait',
|
|
35
|
+
waitMs: 1000,
|
|
36
|
+
description: 'DeepResearch有効化完了を待機'
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
disable: [
|
|
40
|
+
{
|
|
41
|
+
action: 'click',
|
|
42
|
+
selector: 'button[aria-label*="リサーチ:クリックして削除"]',
|
|
43
|
+
description: 'リサーチpillボタンをクリックして無効化'
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
verify: [
|
|
47
|
+
{
|
|
48
|
+
action: 'verify',
|
|
49
|
+
selector: 'button.__composer-pill[aria-label*="リサーチ"]',
|
|
50
|
+
description: 'リサーチpillボタンの存在確認'
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* 手順をファイルから読み込み
|
|
57
|
+
*/
|
|
58
|
+
export async function loadProcedure() {
|
|
59
|
+
try {
|
|
60
|
+
if (fs.existsSync(PROCEDURE_FILE)) {
|
|
61
|
+
const data = await fs.promises.readFile(PROCEDURE_FILE, 'utf-8');
|
|
62
|
+
return JSON.parse(data);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error(`手順ファイルの読み込みエラー: ${error}`);
|
|
67
|
+
}
|
|
68
|
+
return DEFAULT_PROCEDURE;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 手順をファイルに保存
|
|
72
|
+
*/
|
|
73
|
+
export async function saveProcedure(procedure) {
|
|
74
|
+
try {
|
|
75
|
+
const dir = path.dirname(PROCEDURE_FILE);
|
|
76
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
77
|
+
await fs.promises.writeFile(PROCEDURE_FILE, JSON.stringify(procedure, null, 2), 'utf-8');
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error(`手順ファイルの保存エラー: ${error}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 手順を実行
|
|
85
|
+
*/
|
|
86
|
+
export async function executeProcedure(page, steps) {
|
|
87
|
+
for (const step of steps) {
|
|
88
|
+
try {
|
|
89
|
+
if (step.action === 'click') {
|
|
90
|
+
const element = await page.$(step.selector);
|
|
91
|
+
if (!element) {
|
|
92
|
+
return { success: false, error: `要素が見つかりません: ${step.selector}` };
|
|
93
|
+
}
|
|
94
|
+
// textContent指定がある場合は検証
|
|
95
|
+
if (step.textContent) {
|
|
96
|
+
const text = await page.evaluate((el) => el.textContent, element);
|
|
97
|
+
if (!text?.includes(step.textContent)) {
|
|
98
|
+
return { success: false, error: `テキスト不一致: 期待="${step.textContent}", 実際="${text}"` };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
await element.click();
|
|
102
|
+
}
|
|
103
|
+
else if (step.action === 'wait') {
|
|
104
|
+
await page.waitForTimeout(step.waitMs || 500);
|
|
105
|
+
}
|
|
106
|
+
else if (step.action === 'verify') {
|
|
107
|
+
const element = await page.$(step.selector);
|
|
108
|
+
if (!element) {
|
|
109
|
+
return { success: false, error: `検証失敗: ${step.selector} が見つかりません` };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
error: `ステップ実行エラー: ${step.description} - ${error}`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { success: true };
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* DeepResearchを有効化
|
|
124
|
+
*/
|
|
125
|
+
export async function enableDeepResearch(page) {
|
|
126
|
+
const procedure = await loadProcedure();
|
|
127
|
+
return await executeProcedure(page, procedure.steps.enable);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* DeepResearchを無効化
|
|
131
|
+
*/
|
|
132
|
+
export async function disableDeepResearch(page) {
|
|
133
|
+
const procedure = await loadProcedure();
|
|
134
|
+
return await executeProcedure(page, procedure.steps.disable);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* DeepResearch状態を確認
|
|
138
|
+
*/
|
|
139
|
+
export async function verifyDeepResearch(page) {
|
|
140
|
+
const procedure = await loadProcedure();
|
|
141
|
+
const result = await executeProcedure(page, procedure.steps.verify);
|
|
142
|
+
if (!result.success) {
|
|
143
|
+
return { enabled: false, error: result.error };
|
|
144
|
+
}
|
|
145
|
+
return { enabled: true };
|
|
146
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepResearch ON/OFF 切り替えツール(簡潔版)
|
|
3
|
+
*
|
|
4
|
+
* 目的: 最小限のコードで確実にDeepResearchを切り替え
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* DeepResearch有効化(シンプル実装)
|
|
8
|
+
*/
|
|
9
|
+
export async function enableDeepResearch(page) {
|
|
10
|
+
try {
|
|
11
|
+
// Step 1: +ボタンをクリック
|
|
12
|
+
const plusButton = await page.$('button[aria-label*="ファイルの追加"]');
|
|
13
|
+
if (!plusButton) {
|
|
14
|
+
return { success: false, error: '+ボタンが見つかりません' };
|
|
15
|
+
}
|
|
16
|
+
await plusButton.click();
|
|
17
|
+
await page.waitForTimeout(500);
|
|
18
|
+
// Step 2: Deep Research menuitemradio をクリック
|
|
19
|
+
const menuItems = await page.$$('[role="menuitemradio"]');
|
|
20
|
+
let deepResearchItem = null;
|
|
21
|
+
for (const item of menuItems) {
|
|
22
|
+
const text = await item.evaluate((el) => el.textContent);
|
|
23
|
+
if (text?.includes('Deep Research') || text?.includes('リサーチ')) {
|
|
24
|
+
deepResearchItem = item;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!deepResearchItem) {
|
|
29
|
+
return { success: false, error: 'Deep Research menuitemradio が見つかりません' };
|
|
30
|
+
}
|
|
31
|
+
await deepResearchItem.click();
|
|
32
|
+
await page.waitForTimeout(1000);
|
|
33
|
+
// Step 3: 検証(composer-pill確認)
|
|
34
|
+
const pill = await page.$('button.__composer-pill[aria-label*="リサーチ"]');
|
|
35
|
+
if (!pill) {
|
|
36
|
+
return { success: false, error: 'DeepResearch pill が表示されませんでした' };
|
|
37
|
+
}
|
|
38
|
+
return { success: true };
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return { success: false, error: `エラー: ${error}` };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* DeepResearch無効化(シンプル実装)
|
|
46
|
+
*/
|
|
47
|
+
export async function disableDeepResearch(page) {
|
|
48
|
+
try {
|
|
49
|
+
// リサーチpillボタンをクリック
|
|
50
|
+
const pill = await page.$('button[aria-label*="リサーチ:クリックして削除"]');
|
|
51
|
+
if (!pill) {
|
|
52
|
+
return { success: false, error: 'リサーチpillボタンが見つかりません(既にOFFの可能性)' };
|
|
53
|
+
}
|
|
54
|
+
await pill.click();
|
|
55
|
+
await page.waitForTimeout(500);
|
|
56
|
+
// 検証: pillが消えたことを確認
|
|
57
|
+
const stillExists = await page.$('button[aria-label*="リサーチ:クリックして削除"]');
|
|
58
|
+
if (stillExists) {
|
|
59
|
+
return { success: false, error: 'DeepResearch無効化に失敗しました' };
|
|
60
|
+
}
|
|
61
|
+
return { success: true };
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return { success: false, error: `エラー: ${error}` };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* DeepResearch状態確認(シンプル実装)
|
|
69
|
+
*/
|
|
70
|
+
export async function checkDeepResearch(page) {
|
|
71
|
+
try {
|
|
72
|
+
const pill = await page.$('button.__composer-pill[aria-label*="リサーチ"]');
|
|
73
|
+
if (pill) {
|
|
74
|
+
const text = await pill.evaluate((el) => el.textContent?.trim());
|
|
75
|
+
return { enabled: true, indicator: `composer-pill: "${text}"` };
|
|
76
|
+
}
|
|
77
|
+
return { enabled: false };
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
return { enabled: false };
|
|
81
|
+
}
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-devtools-mcp-for-extension",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
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": "./build/src/index.js",
|