claude-coder 1.9.1 → 1.9.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-coder",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "description": "Claude Coder — Autonomous coding agent harness powered by Claude Code SDK. Scan, plan, code, validate, git-commit in a loop.",
5
5
  "bin": {
6
6
  "claude-coder": "bin/cli.js"
@@ -3,6 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const os = require('os');
5
5
  const path = require('path');
6
+ const http = require('http');
6
7
  const { execSync } = require('child_process');
7
8
  const { loadConfig, log } = require('../common/config');
8
9
  const { assets } = require('../common/assets');
@@ -277,36 +278,91 @@ function authExtension() {
277
278
  log('info', '确保 Chrome/Edge 已运行且 Playwright MCP Bridge 扩展已启用');
278
279
  }
279
280
 
280
- function authChromeDevTools() {
281
- console.log('Chrome DevTools MCP 配置:');
282
- console.log('');
283
- console.log(' 此模式通过 Chrome DevTools Protocol 连接到已打开的 Chrome 浏览器。');
284
- console.log(' 直接复用浏览器中已有的登录态、扩展和 DevTools 调试能力。');
285
- console.log('');
286
- console.log(' 前置条件:');
287
- console.log(' 1. Node.js v20.19+(npx 自动下载 chrome-devtools-mcp 包)');
288
- console.log(' 2. Chrome 144+ 版本');
289
- console.log(' 3. 打开 chrome://inspect/#remote-debugging 启用远程调试');
290
- console.log(' 4. 允许传入调试连接');
291
- console.log('');
292
- console.log(' 功能:');
293
- console.log(' - 输入自动化: 点击、输入、表单填写');
294
- console.log(' - 页面导航: 多页面管理、截图');
295
- console.log(' - 性能分析: Trace 录制、Core Web Vitals、Lighthouse 审计');
296
- console.log(' - 调试工具: Console 消息、网络请求检查、内存快照');
297
- console.log('');
298
- console.log(` 注意: 单实例限制,同一时间只能连接一个 Chrome 调试会话。`);
299
- console.log(` 如需多实例并行,请配置 Playwright MCP(claude-coder setup)。`);
300
- console.log('');
281
+ function getChromeCommand() {
282
+ if (process.platform === 'win32') {
283
+ const prefixes = [
284
+ process.env['PROGRAMFILES(X86)'],
285
+ process.env.PROGRAMFILES,
286
+ process.env.LOCALAPPDATA,
287
+ ].filter(Boolean);
288
+ for (const prefix of prefixes) {
289
+ const p = path.join(prefix, 'Google', 'Chrome', 'Application', 'chrome.exe');
290
+ if (fs.existsSync(p)) return `"${p}"`;
291
+ }
292
+ return 'start chrome';
293
+ }
294
+ if (process.platform === 'darwin') return 'open -a "Google Chrome" --args';
295
+ return 'google-chrome';
296
+ }
301
297
 
298
+ function checkCdpConnection(port = 9222, timeoutMs = 5000) {
299
+ return new Promise(resolve => {
300
+ const req = http.get(`http://127.0.0.1:${port}/json/version`, res => {
301
+ let data = '';
302
+ res.on('data', chunk => { data += chunk; });
303
+ res.on('end', () => {
304
+ try {
305
+ const info = JSON.parse(data);
306
+ resolve({ ok: true, browser: info.Browser || 'Chrome', wsUrl: info.webSocketDebuggerUrl || '' });
307
+ } catch {
308
+ resolve({ ok: false });
309
+ }
310
+ });
311
+ });
312
+ req.on('error', () => resolve({ ok: false }));
313
+ req.setTimeout(timeoutMs, () => { req.destroy(); resolve({ ok: false }); });
314
+ });
315
+ }
316
+
317
+ async function authChromeDevTools(url) {
302
318
  const mcpPath = assets.path('mcpConfig');
303
319
  updateMcpConfig(mcpPath, 'chrome-devtools');
304
320
  enableWebTestEnv('chrome-devtools');
305
321
 
322
+ log('ok', '.mcp.json 已配置完成');
306
323
  console.log('');
307
- log('ok', '配置完成!');
308
- log('info', 'Chrome DevTools MCP 使用 autoConnect 模式');
309
- log('info', '确保 Chrome 已启动且已在 chrome://inspect 中启用远程调试');
324
+
325
+ log('info', '正在检测 Chrome DevTools 连接...');
326
+ let conn = await checkCdpConnection();
327
+
328
+ if (!conn.ok && url) {
329
+ log('info', '未检测到 Chrome 远程调试实例,尝试启动 Chrome...');
330
+ const chromeCmd = getChromeCommand();
331
+ const launchCmd = `${chromeCmd} --remote-debugging-port=9222 "${url}"`;
332
+ try {
333
+ const { spawn } = require('child_process');
334
+ const child = spawn(launchCmd, { shell: true, detached: true, stdio: 'ignore' });
335
+ child.unref();
336
+ } catch (err) {
337
+ log('warn', `Chrome 启动失败: ${err.message}`);
338
+ }
339
+
340
+ for (let i = 0; i < 6; i++) {
341
+ await new Promise(r => setTimeout(r, 2000));
342
+ conn = await checkCdpConnection();
343
+ if (conn.ok) break;
344
+ }
345
+ }
346
+
347
+ console.log('');
348
+ if (conn.ok) {
349
+ log('ok', `Chrome DevTools 连接成功: ${conn.browser}`);
350
+ if (conn.wsUrl) log('info', `WebSocket: ${conn.wsUrl}`);
351
+ log('ok', '配置验证通过!MCP 可以正常连接 Chrome。');
352
+ } else {
353
+ log('warn', '未检测到 Chrome 远程调试实例');
354
+ console.log('');
355
+ console.log(' 请确保:');
356
+ console.log(' 1. Chrome 144+ 已安装');
357
+ console.log(' 2. 打开 chrome://inspect/#remote-debugging 启用远程调试');
358
+ console.log(' 3. 允许传入调试连接');
359
+ console.log('');
360
+ console.log(' 或手动启动带远程调试的 Chrome:');
361
+ const chromeCmd = getChromeCommand();
362
+ console.log(` ${chromeCmd} --remote-debugging-port=9222`);
363
+ console.log('');
364
+ log('info', '.mcp.json 已配置,Chrome 就绪后 MCP 会自动连接 (autoConnect)');
365
+ }
310
366
  }
311
367
 
312
368
  async function auth(url) {
@@ -328,9 +384,11 @@ async function auth(url) {
328
384
  log('info', '升级后重新运行此命令');
329
385
  return;
330
386
  }
387
+ const targetUrl = normalizeUrl(url) || null;
331
388
  log('info', '浏览器工具: Chrome DevTools MCP');
389
+ if (targetUrl) log('info', `目标 URL: ${targetUrl}`);
332
390
  console.log('');
333
- authChromeDevTools();
391
+ await authChromeDevTools(targetUrl);
334
392
  return;
335
393
  }
336
394
 
@@ -21,7 +21,7 @@ function log(level, msg) {
21
21
  warn: `${COLOR.yellow}[WARN]${COLOR.reset} `,
22
22
  error: `${COLOR.red}[ERROR]${COLOR.reset}`,
23
23
  };
24
- console.error(`${tags[level] || ''} ${msg}`);
24
+ console.error(`${tags[level] || ''} ${msg} \n`);
25
25
  }
26
26
 
27
27
  // --------------- .env parsing ---------------
@@ -308,8 +308,17 @@ async function onFailure(session, { headBefore, taskId, sessionResult, validateR
308
308
  });
309
309
  }
310
310
 
311
- async function onStall(session, { headBefore, taskId, sessionResult, consecutiveFailures }) {
312
- log('warn', `Session ${session} 因停顿超时中断,跳过校验直接重试`);
311
+ async function onStall(session, { headBefore, taskId, sessionResult, consecutiveFailures, config }) {
312
+ log('warn', `Session ${session} 因停顿超时中断,尝试校验任务是否已完成...`);
313
+
314
+ const validateResult = await validate(config, headBefore, taskId);
315
+
316
+ if (!validateResult.fatal) {
317
+ log('ok', `停顿超时但任务已完成,按成功处理${validateResult.hasWarnings ? ' (有警告)' : ''}`);
318
+ return onSuccess(session, { taskId, sessionResult, validateResult });
319
+ }
320
+
321
+ log('warn', '停顿超时且校验未通过,回滚重试');
313
322
  return _handleRetryOrSkip(session, {
314
323
  headBefore, taskId, sessionResult, consecutiveFailures,
315
324
  result: 'stalled', reason: '停顿超时',
@@ -420,7 +429,11 @@ async function executeRun(config, opts = {}) {
420
429
  });
421
430
 
422
431
  if (sessionResult.stalled) {
423
- state = await onStall(session, { headBefore, taskId, sessionResult, ...state });
432
+ state = await onStall(session, { headBefore, taskId, sessionResult, config, ...state });
433
+ if (state.consecutiveFailures === 0) {
434
+ if (shouldSimplify(config)) await tryRunSimplify(config);
435
+ tryPush(projectRoot);
436
+ }
424
437
  continue;
425
438
  }
426
439
 
@@ -164,21 +164,30 @@ class Session {
164
164
  const messages = [];
165
165
  const querySession = sdk.query({ prompt, options: queryOpts });
166
166
 
167
- for await (const message of querySession) {
168
- if (this._isStalled()) {
169
- log('warn', '停顿超时,中断消息循环');
170
- break;
171
- }
172
- messages.push(message);
173
- this._logMessage(message);
167
+ try {
168
+ for await (const message of querySession) {
169
+ if (this._isStalled()) {
170
+ log('warn', '停顿超时,中断消息循环');
171
+ break;
172
+ }
173
+ messages.push(message);
174
+ this._logMessage(message);
174
175
 
175
- if (opts.onMessage) {
176
- const action = opts.onMessage(message, messages);
177
- if (action === 'break') break;
176
+ if (opts.onMessage) {
177
+ const action = opts.onMessage(message, messages);
178
+ if (action === 'break') break;
179
+ }
180
+ }
181
+ } catch (err) {
182
+ if (this._isStalled()) {
183
+ log('warn', 'SDK 会话因停顿超时中断');
184
+ } else {
185
+ throw err;
178
186
  }
179
187
  }
180
188
 
181
189
  const sdkResult = extractResult(messages);
190
+ this.logStream.write(`\n[SDK_RESULT] ${JSON.stringify(sdkResult, null, 2)}\n\n`);
182
191
  const cost = sdkResult?.total_cost_usd || null;
183
192
  const usage = sdkResult?.usage || null;
184
193
  const turns = sdkResult?.num_turns || null;
@@ -196,7 +205,7 @@ class Session {
196
205
  console.log('----- SESSION END -----');
197
206
  log('info', `session 统计: ${summary}`);
198
207
  if (this.logStream?.writable) {
199
- this.logStream.write(`[SESSION_STATS] ${summary}\n`);
208
+ this.logStream.write(`[SESSION_INFO] ${summary}\n`);
200
209
  }
201
210
  }
202
211