chrome-devtools-mcp-for-extension 0.9.18 → 0.9.20
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/chatgpt-web.js +82 -110
- package/package.json +1 -1
|
@@ -8,6 +8,33 @@ import path from 'node:path';
|
|
|
8
8
|
import z from 'zod';
|
|
9
9
|
import { ToolCategories } from './categories.js';
|
|
10
10
|
import { defineTool } from './ToolDefinition.js';
|
|
11
|
+
/**
|
|
12
|
+
* Path to store chat session data
|
|
13
|
+
*/
|
|
14
|
+
const CHAT_SESSIONS_FILE = path.join(process.cwd(), 'docs/ask/chatgpt/.chat-sessions.json');
|
|
15
|
+
/**
|
|
16
|
+
* Load chat sessions from JSON file
|
|
17
|
+
*/
|
|
18
|
+
async function loadChatSessions() {
|
|
19
|
+
try {
|
|
20
|
+
const data = await fs.promises.readFile(CHAT_SESSIONS_FILE, 'utf-8');
|
|
21
|
+
return JSON.parse(data);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Save a chat session for a project
|
|
29
|
+
*/
|
|
30
|
+
async function saveChatSession(projectName, session) {
|
|
31
|
+
const sessions = await loadChatSessions();
|
|
32
|
+
sessions[projectName] = session;
|
|
33
|
+
// Ensure directory exists
|
|
34
|
+
const dir = path.dirname(CHAT_SESSIONS_FILE);
|
|
35
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
36
|
+
await fs.promises.writeFile(CHAT_SESSIONS_FILE, JSON.stringify(sessions, null, 2), 'utf-8');
|
|
37
|
+
}
|
|
11
38
|
/**
|
|
12
39
|
* Sanitize question to remove sensitive information like passwords
|
|
13
40
|
*/
|
|
@@ -107,60 +134,30 @@ export const askChatGPTWeb = defineTool({
|
|
|
107
134
|
return;
|
|
108
135
|
}
|
|
109
136
|
response.appendResponseLine('✅ ログイン確認完了');
|
|
110
|
-
// Step 2:
|
|
137
|
+
// Step 2: Load existing session or create new chat
|
|
138
|
+
let isNewChat = false;
|
|
139
|
+
let sessionChatId;
|
|
111
140
|
if (!createNewChat) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// Search for project chat
|
|
125
|
-
const chatFound = await page.evaluate((projectName) => {
|
|
126
|
-
const searchInput = document.querySelector('input[placeholder*="チャットを検索"]');
|
|
127
|
-
if (searchInput) {
|
|
128
|
-
searchInput.value = `[Project: ${projectName}]`;
|
|
129
|
-
searchInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
return false;
|
|
133
|
-
}, project);
|
|
134
|
-
if (chatFound) {
|
|
135
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
136
|
-
// Try to find and click the chat
|
|
137
|
-
const existingChat = await page.evaluate((projectName) => {
|
|
138
|
-
const chatLinks = Array.from(document.querySelectorAll('a[href^="/c/"]'));
|
|
139
|
-
const targetChat = chatLinks.find((link) => link.textContent?.includes(`[Project: ${projectName}]`));
|
|
140
|
-
if (targetChat) {
|
|
141
|
-
targetChat.click();
|
|
142
|
-
return {
|
|
143
|
-
found: true,
|
|
144
|
-
href: targetChat.href,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
return { found: false };
|
|
148
|
-
}, project);
|
|
149
|
-
if (existingChat.found) {
|
|
150
|
-
response.appendResponseLine(`✅ 既存チャットを使用: ${existingChat.href}`);
|
|
151
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
response.appendResponseLine('既存チャットが見つかりませんでした。新規作成します。');
|
|
155
|
-
}
|
|
156
|
-
}
|
|
141
|
+
// Try to load existing session for this project
|
|
142
|
+
const sessions = await loadChatSessions();
|
|
143
|
+
const existingSession = sessions[project];
|
|
144
|
+
if (existingSession) {
|
|
145
|
+
response.appendResponseLine(`既存のプロジェクトチャットを使用: ${existingSession.url}`);
|
|
146
|
+
await page.goto(existingSession.url, { waitUntil: 'networkidle2' });
|
|
147
|
+
sessionChatId = existingSession.chatId;
|
|
148
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
response.appendResponseLine('既存チャットが見つかりませんでした。新規作成します。');
|
|
152
|
+
isNewChat = true;
|
|
157
153
|
}
|
|
158
154
|
}
|
|
155
|
+
else {
|
|
156
|
+
isNewChat = true;
|
|
157
|
+
}
|
|
159
158
|
// Step 3: Create new chat if needed
|
|
160
|
-
|
|
161
|
-
if (createNewChat || page.url() === 'https://chatgpt.com/') {
|
|
159
|
+
if (isNewChat) {
|
|
162
160
|
response.appendResponseLine('新規チャットを作成中...');
|
|
163
|
-
isNewChat = true;
|
|
164
161
|
// Click "新しいチャット"
|
|
165
162
|
await page.evaluate(() => {
|
|
166
163
|
const newChatLink = document.querySelector('a[href="/"]');
|
|
@@ -207,14 +204,8 @@ export const askChatGPTWeb = defineTool({
|
|
|
207
204
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
208
205
|
// Click send button
|
|
209
206
|
const sent = await page.evaluate(() => {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
const svg = btn.querySelector('svg');
|
|
213
|
-
return (svg &&
|
|
214
|
-
!btn.disabled &&
|
|
215
|
-
btn.offsetParent !== null);
|
|
216
|
-
});
|
|
217
|
-
if (sendButton) {
|
|
207
|
+
const sendButton = document.querySelector('button[data-testid="send-button"]');
|
|
208
|
+
if (sendButton && !sendButton.disabled) {
|
|
218
209
|
sendButton.click();
|
|
219
210
|
return true;
|
|
220
211
|
}
|
|
@@ -224,6 +215,11 @@ export const askChatGPTWeb = defineTool({
|
|
|
224
215
|
response.appendResponseLine('❌ 送信ボタンが見つかりません');
|
|
225
216
|
return;
|
|
226
217
|
}
|
|
218
|
+
// Wait for message to actually be sent (user message appears in DOM)
|
|
219
|
+
await page.waitForFunction(() => {
|
|
220
|
+
const messages = document.querySelectorAll('[data-message-author-role="user"]');
|
|
221
|
+
return messages.length > 0;
|
|
222
|
+
}, { timeout: 10000 });
|
|
227
223
|
response.appendResponseLine('✅ 質問送信完了');
|
|
228
224
|
// Step 5: Monitor streaming with progress updates
|
|
229
225
|
response.appendResponseLine('ChatGPTの回答を待機中... (10秒ごとに進捗を表示)');
|
|
@@ -269,61 +265,37 @@ export const askChatGPTWeb = defineTool({
|
|
|
269
265
|
if (status.thinkingTime) {
|
|
270
266
|
response.appendResponseLine(`🤔 思考時間: ${status.thinkingTime}秒`);
|
|
271
267
|
}
|
|
272
|
-
//
|
|
268
|
+
// Save chat session if it's a new chat
|
|
273
269
|
if (isNewChat) {
|
|
274
|
-
response.appendResponseLine('
|
|
275
|
-
//
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
270
|
+
response.appendResponseLine('チャットセッションを保存中...');
|
|
271
|
+
// Extract chat ID from URL
|
|
272
|
+
const chatUrl = page.url();
|
|
273
|
+
const chatIdMatch = chatUrl.match(/\/c\/([a-f0-9-]+)/);
|
|
274
|
+
if (chatIdMatch) {
|
|
275
|
+
const chatId = chatIdMatch[1];
|
|
276
|
+
await saveChatSession(project, {
|
|
277
|
+
chatId,
|
|
278
|
+
url: chatUrl,
|
|
279
|
+
lastUsed: new Date().toISOString(),
|
|
280
|
+
title: `[Project: ${project}]`,
|
|
281
|
+
});
|
|
282
|
+
sessionChatId = chatId;
|
|
283
|
+
response.appendResponseLine(`✅ チャットセッション保存: ${chatId}`);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
response.appendResponseLine('⚠️ チャットIDが取得できませんでした');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
// Update last used timestamp for existing session
|
|
291
|
+
if (sessionChatId) {
|
|
292
|
+
const chatUrl = page.url();
|
|
293
|
+
await saveChatSession(project, {
|
|
294
|
+
chatId: sessionChatId,
|
|
295
|
+
url: chatUrl,
|
|
296
|
+
lastUsed: new Date().toISOString(),
|
|
297
|
+
title: `[Project: ${project}]`,
|
|
299
298
|
});
|
|
300
|
-
if (renameClicked) {
|
|
301
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
302
|
-
// Enter new name
|
|
303
|
-
await page.evaluate((projectName) => {
|
|
304
|
-
const textbox = document.querySelector('input[type="text"]');
|
|
305
|
-
if (textbox) {
|
|
306
|
-
textbox.value = `[Project: ${projectName}]`;
|
|
307
|
-
textbox.dispatchEvent(new Event('input', { bubbles: true }));
|
|
308
|
-
// Press Enter to confirm
|
|
309
|
-
textbox.dispatchEvent(new KeyboardEvent('keydown', {
|
|
310
|
-
key: 'Enter',
|
|
311
|
-
code: 'Enter',
|
|
312
|
-
keyCode: 13,
|
|
313
|
-
bubbles: true,
|
|
314
|
-
}));
|
|
315
|
-
textbox.blur();
|
|
316
|
-
}
|
|
317
|
-
}, project);
|
|
318
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
319
|
-
response.appendResponseLine(`✅ チャット名を「[Project: ${project}]」に変更`);
|
|
320
|
-
// Close the menu popup by clicking outside
|
|
321
|
-
await page.evaluate(() => {
|
|
322
|
-
const body = document.body;
|
|
323
|
-
body.click();
|
|
324
|
-
});
|
|
325
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
326
|
-
}
|
|
327
299
|
}
|
|
328
300
|
}
|
|
329
301
|
// Save conversation log
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-devtools-mcp-for-extension",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.20",
|
|
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",
|