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
|
-
|
|
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 = {
|
|
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
|
-
|
|
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 ??
|
|
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`
|
|
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
|
|
148
|
-
|
|
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