evolclaw 3.1.9 → 3.1.10

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.1.10 (2026-06-04)
4
+
5
+ ### Bug Fixes
6
+
7
+ - **Windows `evolclaw watch web` 仍 ENOENT(续 3.1.9)** — `where` 会列出同名的全部文件,npm 全局 bin 同时生成无后缀 sh 包装脚本和 `.cmd`/`.ps1`;3.1.9 取首行恰好选中无法在 Windows 执行的 sh 包装。改为优先选 PATHEXT 可执行后缀(`.cmd`/`.exe`/`.bat`/`.com`)。另外 Node 18.20+/20+/22 起 `execFile` 拒绝直接 spawn `.cmd`/`.bat`(CVE-2024-27980),改为 `shell:true` + 引号包裹参数执行
8
+
3
9
  ## v3.1.9 (2026-06-04)
4
10
 
5
11
  ### Bug Fixes
package/dist/cli/index.js CHANGED
@@ -2125,7 +2125,16 @@ async function cmdWatchWeb() {
2125
2125
  process.stderr.write('❌ 已安装 evolclaw-web 但无法定位可执行文件。\n 请重新打开终端后再次运行,或手动执行: evolclaw-web --home ' + home + '\n');
2126
2126
  process.exit(1);
2127
2127
  }
2128
- execFileSync(exe, ['--home', home], { stdio: 'inherit' });
2128
+ // Node 18.20+/20+/22 起,execFile 拒绝直接 spawn .cmd/.bat(CVE-2024-27980),必须 shell:true。
2129
+ // shell 模式下含空格的路径/参数需加引号。
2130
+ const isBatch = /\.(cmd|bat)$/i.test(exe);
2131
+ if (isBatch) {
2132
+ const q = (s) => `"${s}"`;
2133
+ execFileSync(q(exe), ['--home', q(home)], { stdio: 'inherit', shell: true });
2134
+ }
2135
+ else {
2136
+ execFileSync(exe, ['--home', home], { stdio: 'inherit' });
2137
+ }
2129
2138
  }
2130
2139
  async function cmdRestartMonitor() {
2131
2140
  const p = resolvePaths();
@@ -162,7 +162,7 @@ export class IMRenderer {
162
162
  }
163
163
  /** 添加工具调用 */
164
164
  addToolCall(name, input, callId, descText, turn, outputTokens) {
165
- this.emitProgress('tool_call', outputTokens, turn);
165
+ this.emitProgress('tool_call', outputTokens, turn, { toolName: name, callId });
166
166
  if (this.opts.envelope.chatmode === 'proactive')
167
167
  return;
168
168
  if (this.opts.suppressActivities)
@@ -181,7 +181,7 @@ export class IMRenderer {
181
181
  }
182
182
  /** 添加工具结果 */
183
183
  addToolResult(name, ok, result, error, callId, durationMs, descText) {
184
- this.emitProgress('tool_result');
184
+ this.emitProgress('tool_result', undefined, undefined, { toolName: name, callId, ok, durationMs });
185
185
  if (this.opts.envelope.chatmode === 'proactive')
186
186
  return;
187
187
  if (this.opts.suppressActivities)
@@ -369,8 +369,19 @@ export class IMRenderer {
369
369
  }
370
370
  }
371
371
  // ── 内部:status.progress 发送 ──
372
- emitProgress(activityType, outputTokens, turn) {
373
- const payload = { kind: 'status.progress', metadata: { activityType, ...(turn != null && { turn }), ...(outputTokens != null && { outputTokens }) } };
372
+ emitProgress(activityType, outputTokens, turn, extra) {
373
+ const payload = {
374
+ kind: 'status.progress',
375
+ metadata: {
376
+ activityType,
377
+ ...(turn != null && { turn }),
378
+ ...(outputTokens != null && { outputTokens }),
379
+ ...(extra?.toolName != null && { toolName: extra.toolName }),
380
+ ...(extra?.callId != null && { callId: extra.callId }),
381
+ ...(extra?.ok != null && { ok: extra.ok }),
382
+ ...(extra?.durationMs != null && { durationMs: extra.durationMs }),
383
+ },
384
+ };
374
385
  this.opts.send(payload).catch(() => { });
375
386
  }
376
387
  // ── 内部:proactive 模式(逐事件 activity.batch[1 item]) ──
@@ -392,7 +403,12 @@ export class IMRenderer {
392
403
  const outputTokens = event.outputTokens;
393
404
  const turn = event.turn;
394
405
  const activityType = item.kind === 'text' ? 'text' : item.kind === 'tool_call' ? 'tool_call' : 'tool_result';
395
- this.emitProgress(activityType, outputTokens, turn);
406
+ const extra = item.kind === 'tool_call'
407
+ ? { toolName: item.name, callId: item.call_id }
408
+ : item.kind === 'tool_result'
409
+ ? { toolName: item.name, callId: item.call_id, ok: item.ok, durationMs: item.duration_ms }
410
+ : undefined;
411
+ this.emitProgress(activityType, outputTokens, turn, extra);
396
412
  const payload = { kind: 'activity.batch', items: [item] };
397
413
  // fire-and-forget
398
414
  this.opts.send(payload).catch(err => {
@@ -29,7 +29,7 @@ export class MessageBridge {
29
29
  this.messageQueue = messageQueue;
30
30
  this.cmdHandler = cmdHandler;
31
31
  this.eventBus = eventBus;
32
- this.defaultDebounce = defaultDebounce ?? 2;
32
+ this.defaultDebounce = defaultDebounce ?? 0;
33
33
  }
34
34
  /** Inject EvolAgentRegistry so owner lookups/writes route to agent.json for agent-owned channels. */
35
35
  setAgentRegistry(registry) {
@@ -135,7 +135,9 @@ export function commandExists(cmd) {
135
135
  }
136
136
  /**
137
137
  * 解析命令的真实可执行文件绝对路径。
138
- * Windows: `where` 返回首个匹配(自动含 .cmd/.exe 后缀),解决 execFileSync 不补后缀的问题。
138
+ * Windows: `where` 会列出全部同名文件(npm 全局 bin 同时生成无后缀 sh 包装、.cmd、.ps1),
139
+ * 其中无后缀的那个是 Unix sh 脚本,Windows 无法直接 spawn(ENOENT)。
140
+ * 因此优先选 PATHEXT 可执行后缀(.cmd/.exe/.bat/.com),都没有才退回首行。
139
141
  * 失败返回 null。不缓存——刚安装的命令需要重新探测。
140
142
  */
141
143
  export function resolveCommandPath(cmd) {
@@ -144,8 +146,12 @@ export function resolveCommandPath(cmd) {
144
146
  const r = spawnSync('where', [cmd], { encoding: 'utf-8', stdio: 'pipe', windowsHide: true });
145
147
  if (r.status !== 0 || !r.stdout)
146
148
  return null;
147
- const first = r.stdout.split(/\r?\n/).map(s => s.trim()).filter(Boolean)[0];
148
- return first || null;
149
+ const candidates = r.stdout.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
150
+ if (candidates.length === 0)
151
+ return null;
152
+ const execExts = ['.cmd', '.exe', '.bat', '.com'];
153
+ const runnable = candidates.find(p => execExts.some(ext => p.toLowerCase().endsWith(ext)));
154
+ return runnable || candidates[0];
149
155
  }
150
156
  else {
151
157
  const out = execFileSync('which', [cmd], { encoding: 'utf-8', stdio: 'pipe' }).trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evolclaw",
3
- "version": "3.1.9",
3
+ "version": "3.1.10",
4
4
  "description": "Lightweight AI Agent gateway connecting Claude Agent SDK to messaging channels (Feishu, ACP) with multi-project session management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",