agent_deply/pam_deploy_graph/script_runner.py
dark d3f5c82d98 feat: 补充 Agent 运行日志并增加 LLM 测试命令
- 新增统一日志工具,支持日志文件路径和级别配置
- 记录 CLI/chat、Agent、LLM、action、MCP、LangGraph、checkpoint 等关键流程
- 对日志中的 token、secret、api_key、Authorization 等敏感信息做脱敏
- chat 新增 llm test 命令,用于验证当前 LLM client 是否正常加载
- 同步 README、打包文档和 run.sh 帮助说明
- 补充日志脱敏和 llm test 相关测试
2026-06-04 10:51:59 +08:00

157 lines
4.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""通过子进程执行 deploy.sh / deploy.ps1 action。"""
from __future__ import annotations
import logging
import subprocess
import time
from pathlib import Path
from typing import Any
from .logging_utils import json_for_log
from .models import ActionResult
from .output_parser import parse_script_result
logger = logging.getLogger(__name__)
class ScriptActionRunner:
"""脚本 action runner负责构造命令、执行脚本并解析结果。"""
def __init__(self, script_base_dir: str | Path = "doc_scripts") -> None:
"""保存脚本所在目录。"""
self.script_base_dir = Path(script_base_dir)
def run(
self,
action: str,
*,
params: dict[str, Any],
script_entry: str,
config_path: str,
ip: str | None = None,
hash_code: str | None = None,
stop_first: bool = False,
trace_file_path: str | None = None,
timeout_sec: int | None = None,
) -> ActionResult:
"""执行一个脚本 action并返回统一 ActionResult。"""
command = self.build_command(
action,
script_entry=script_entry,
config_path=config_path,
ip=ip,
hash_code=hash_code,
stop_first=stop_first,
trace_file_path=trace_file_path,
)
started_at = time.perf_counter()
logger.info(
"脚本 action 开始 action=%s command=%s cwd=%s config=%s ip=%s trace=%s timeout=%s",
action,
json_for_log(command),
self.script_base_dir,
config_path,
ip or "",
trace_file_path or "",
timeout_sec,
)
try:
completed = subprocess.run(
command,
cwd=str(self.script_base_dir),
capture_output=True,
text=True,
timeout=timeout_sec,
check=False,
)
except Exception:
logger.exception("脚本 action 执行异常 action=%s command=%s cwd=%s", action, json_for_log(command), self.script_base_dir)
raise
duration_ms = int((time.perf_counter() - started_at) * 1000)
logger.info(
"脚本 action 结束 action=%s exit_code=%s duration_ms=%s stdout=%s stderr=%s",
action,
completed.returncode,
duration_ms,
json_for_log(completed.stdout, max_text_len=1200),
json_for_log(completed.stderr, max_text_len=1200),
)
result = parse_script_result(
action=action,
stdout=completed.stdout,
stderr=completed.stderr,
exit_code=completed.returncode,
backend="script",
tool_name=script_entry,
)
logger.info(
"脚本 action 解析完成 action=%s ok=%s values=%s error=%s",
action,
result.ok,
json_for_log(result.values),
result.error_summary,
)
return result
def build_command(
self,
action: str,
*,
script_entry: str,
config_path: str,
ip: str | None = None,
hash_code: str | None = None,
stop_first: bool = False,
trace_file_path: str | None = None,
) -> list[str]:
"""根据脚本类型构造 action 命令行参数。"""
if script_entry == "deploy.sh":
command = [
"bash",
"./deploy.sh",
"--config",
config_path,
"--action",
action,
]
if ip:
command.extend(["--ip", ip])
if hash_code:
command.extend(["--hash-code", hash_code])
if stop_first:
command.append("--stop-first")
if trace_file_path:
command.extend(["--trace-file", trace_file_path])
return command
if script_entry == "deploy.ps1":
command = [
"powershell",
"-File",
".\\deploy.ps1",
"-ConfigPath",
config_path,
"-Action",
action,
]
if ip:
command.extend(["-Ip", ip])
if hash_code:
command.extend(["-HashCode", hash_code])
if stop_first:
command.append("-RollbackStopFirst")
return command
raise ValueError(f"不支持的脚本入口: {script_entry}")
def select_script_entry(os_name: str | None = None) -> str:
"""根据操作系统选择默认脚本入口。"""
import platform
name = (os_name or platform.system()).lower()
if "windows" in name:
return "deploy.ps1"
return "deploy.sh"