fix: improve terminal timeout messaging
This commit is contained in:
parent
7472028997
commit
a013abb3c4
@ -1544,7 +1544,7 @@ class MainTerminal:
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "terminal_input",
|
||||
"description": "向活动终端发送命令或输入。禁止启动会占用终端界面的程序(python/node/nano/vim 等);如遇卡死请结合 terminal_snapshot 并使用 terminal_reset 恢复。timeout 可填秒数(最大300,超时会强制打断命令)或填 never(不封装超时、不杀进程,可能无输出,无法仅靠快照判断是否成功,需要用 curl/ps 等主动检查)。若不确定上一条命令是否结束,先用 terminal_snapshot 确认后再继续输入。",
|
||||
"description": "向活动终端发送命令或输入。禁止启动会占用终端界面的程序(python/node/nano/vim 等);如遇卡死请结合 terminal_snapshot 并使用 terminal_reset 恢复。timeout 必填:\n1) 传入数字(秒,最大300)会对命令进行硬超时封装。系统终端执行环境若存在 timeout/gtimeout,会采用类似 timeout -k 2 {秒}s sh -c '命令; echo __CMD_DONE__...__' 的封装;若没有 timeout/gtimeout(少见情况),则退化为外层 sh -c 的 sleep/kill 包装,例如:sh -c '运行的指令 & CMD_PID=$!; (sleep 300 && kill -s INT $CMD_PID >/dev/null 2>&1 && sleep 2 && kill -s KILL $CMD_PID >/dev/null 2>&1) & WAITER=$!; wait $CMD_PID; CMD_STATUS=$?; kill $WAITER >/dev/null 2>&1; echo __CMD_DONE__1770826047975__; exit $CMD_STATUS'。超时后会先 INT 再 KILL,进程会被不可恢复地打断(可能留下半写文件、锁或残留子进程)。\n2) 传入 never 表示不封装、不杀进程,命令原样进入终端并维护状态;此时快照可能无法判断完成情况,需要用 curl/ps/ls 等主动验证。\n适合 timeout=never 的场景示例:启动常驻服务/开发服务器(npm run dev、python web_server.py、uvicorn ...)、开启后台进程后在另一个终端测试、在后台运行时间极长的任务同时做其他事情、持续输出/长时间任务(tail -f 日志、长时间编译/训练/备份/大下载)、需要维持会话状态的操作(例如登录远程服务器后连续执行多条命令)。适合用数字超时的示例:ls/rg/pytest/短脚本等快速命令。\n若不确定上一条命令是否结束,先用 terminal_snapshot 确认后再继续输入。",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": self._inject_intent({
|
||||
@ -1558,7 +1558,7 @@ class MainTerminal:
|
||||
},
|
||||
"timeout": {
|
||||
"type": ["number", "string"],
|
||||
"description": "等待输出的最长秒数,必填,最大300,或填 never 表示不封装超时且不中断进程"
|
||||
"description": "等待输出的最长秒数,必填,最大300;填 never 表示不封装超时且不中断进程(数字超时会触发外层封装)"
|
||||
}
|
||||
}),
|
||||
"required": ["command", "timeout"]
|
||||
|
||||
@ -76,8 +76,16 @@ def handle_disconnect():
|
||||
"""客户端断开"""
|
||||
print(f"[WebSocket] 客户端断开: {request.sid}")
|
||||
username = connection_users.pop(request.sid, None)
|
||||
# 若同一用户仍有其他活跃连接,不因断开而停止任务
|
||||
has_other_connection = False
|
||||
if username:
|
||||
for sid, user in connection_users.items():
|
||||
if user == username:
|
||||
has_other_connection = True
|
||||
break
|
||||
|
||||
task_info = get_stop_flag(request.sid, username)
|
||||
if isinstance(task_info, dict):
|
||||
if isinstance(task_info, dict) and not has_other_connection:
|
||||
task_info['stop'] = True
|
||||
pending_task = task_info.get('task')
|
||||
if pending_task and not pending_task.done():
|
||||
|
||||
@ -334,25 +334,38 @@ def _plain_command_output(result_data: Dict[str, Any]) -> str:
|
||||
message = result_data.get("message")
|
||||
|
||||
prefixes = []
|
||||
partial_output_note = None
|
||||
partial_no_output_note = None
|
||||
if status in {"timeout"}:
|
||||
appended = False
|
||||
# 1) 优先使用数值型 timeout
|
||||
if isinstance(timeout, (int, float)) and timeout > 0:
|
||||
prefixes.append(f"[timeout after {int(timeout)}s]")
|
||||
appended = True
|
||||
# 2) 字符串数字
|
||||
elif isinstance(timeout, str) and timeout.strip().isdigit():
|
||||
prefixes.append(f"[timeout after {int(timeout.strip())}s]")
|
||||
appended = True
|
||||
# 3) 未设置超时(never),用 elapsed_ms 近似
|
||||
elif (isinstance(timeout, str) and timeout.lower() == "never") or result_data.get("never_timeout"):
|
||||
never_timeout = (
|
||||
(isinstance(timeout, str) and timeout.lower() == "never")
|
||||
or result_data.get("never_timeout")
|
||||
)
|
||||
if never_timeout:
|
||||
elapsed_ms = result_data.get("elapsed_ms")
|
||||
secs = None
|
||||
if isinstance(elapsed_ms, (int, float)) and elapsed_ms > 0:
|
||||
secs = max(1, int(round(elapsed_ms / 1000)))
|
||||
prefixes.append(f"[timeout after ~{secs}s]")
|
||||
if secs:
|
||||
prefixes.append(f"[partial_output ~{secs}s]")
|
||||
partial_output_note = f"已返回约{secs}秒内输出,命令可能仍在运行"
|
||||
partial_no_output_note = f"已等待约{secs}秒未捕获输出,命令可能仍在运行"
|
||||
else:
|
||||
prefixes.append("[partial_output]")
|
||||
partial_output_note = "已返回当前输出,命令可能仍在运行"
|
||||
partial_no_output_note = "未捕获输出,命令可能仍在运行"
|
||||
else:
|
||||
appended = False
|
||||
# 1) 优先使用数值型 timeout
|
||||
if isinstance(timeout, (int, float)) and timeout > 0:
|
||||
prefixes.append(f"[timeout after {int(timeout)}s]")
|
||||
appended = True
|
||||
if not appended:
|
||||
prefixes.append("[timeout]")
|
||||
# 2) 字符串数字
|
||||
elif isinstance(timeout, str) and timeout.strip().isdigit():
|
||||
prefixes.append(f"[timeout after {int(timeout.strip())}s]")
|
||||
appended = True
|
||||
if not appended:
|
||||
prefixes.append("[timeout]")
|
||||
elif status in {"killed"}:
|
||||
prefixes.append("[killed]")
|
||||
elif status in {"awaiting_input"}:
|
||||
@ -369,7 +382,7 @@ def _plain_command_output(result_data: Dict[str, Any]) -> str:
|
||||
|
||||
# 如果执行失败且没有输出,优先显示错误信息
|
||||
if not result_data.get("success") and not output:
|
||||
err_text = error or message
|
||||
err_text = partial_no_output_note or error or message
|
||||
if err_text:
|
||||
prefix_text = "".join(prefixes) if prefixes else "[error]"
|
||||
return f"{prefix_text} {err_text}" if prefix_text else err_text
|
||||
@ -377,8 +390,12 @@ def _plain_command_output(result_data: Dict[str, Any]) -> str:
|
||||
|
||||
prefix_text = "".join(prefixes)
|
||||
if prefix_text and output:
|
||||
if partial_output_note:
|
||||
prefix_text = f"{prefix_text} {partial_output_note}"
|
||||
return f"{prefix_text}\n{output}"
|
||||
if prefix_text:
|
||||
if partial_no_output_note:
|
||||
prefix_text = f"{prefix_text} {partial_no_output_note}"
|
||||
return prefix_text
|
||||
if not output:
|
||||
return "[no_output]"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user