mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 15:34:16 +08:00
fix(ai): Codex CLI 状态检测实跑 --version 并移除老版本不支持的 --ephemeral
状态检测原为浅检查, 误报已连接; 改为 codex --version 实测。 --ephemeral 在 codex 0.58 等老版本不存在(unexpected argument), 隔离已由临时 CODEX_HOME 保证, 移除该参数。
This commit is contained in:
@@ -249,11 +249,25 @@ def normalize_openai_base_url(url: str) -> str:
|
||||
|
||||
|
||||
def codex_cli_available() -> bool:
|
||||
"""Codex CLI 是否真正可用: 解析命令后实跑一次 --version。
|
||||
|
||||
仅 which 到二进制不够 — npm 壳缺平台原生二进制时同样存在于 PATH,
|
||||
但一运行就报错(如 "Codex CLI not available"), 状态页会误报已连接。
|
||||
"""
|
||||
try:
|
||||
_codex_base_command()
|
||||
return True
|
||||
base = _codex_base_command()
|
||||
except RuntimeError:
|
||||
return False
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
[*base, "--version"],
|
||||
capture_output=True,
|
||||
timeout=15,
|
||||
check=False,
|
||||
)
|
||||
except (OSError, subprocess.TimeoutExpired):
|
||||
return False
|
||||
return proc.returncode == 0
|
||||
|
||||
|
||||
def ai_configured(provider: str | None = None) -> bool:
|
||||
@@ -589,10 +603,11 @@ async def _run_codex_cli(
|
||||
output_path = codex_home_path / "last-message.txt"
|
||||
_prepare_codex_home(codex_home_path)
|
||||
|
||||
# 不传 --ephemeral: 老版本 codex(如 0.58)无此参数, 传了直接报
|
||||
# unexpected argument; 会话隔离已由一次性临时 CODEX_HOME 保证(跑完即删)。
|
||||
args = [
|
||||
*_codex_base_command(),
|
||||
"exec",
|
||||
"--ephemeral",
|
||||
"--sandbox",
|
||||
"read-only",
|
||||
"--skip-git-repo-check",
|
||||
|
||||
@@ -487,3 +487,79 @@ def test_codex_config_preserves_remote_provider_without_docker_rewrite(monkeypat
|
||||
assert provider["base_url"] == "https://custom.example/v1"
|
||||
assert provider["wire_api"] == "responses"
|
||||
assert provider["requires_openai_auth"] is True
|
||||
|
||||
|
||||
# ---- Codex CLI 可用性检测 (实跑 --version, 不再仅 which) ----
|
||||
|
||||
|
||||
class _FakeCompleted:
|
||||
def __init__(self, returncode: int):
|
||||
self.returncode = returncode
|
||||
self.stdout = b""
|
||||
self.stderr = b""
|
||||
|
||||
|
||||
def test_codex_cli_available_runs_version_check(monkeypatch):
|
||||
"""实跑 --version: 能发现 npm 壳存在但原生二进制跑不起来的情况。"""
|
||||
calls: list[list[str]] = []
|
||||
|
||||
def fake_run(args, **kwargs):
|
||||
calls.append(list(args))
|
||||
return _FakeCompleted(0)
|
||||
|
||||
monkeypatch.setattr(ai_provider, "_codex_base_command", lambda: ["codex"])
|
||||
monkeypatch.setattr(ai_provider.subprocess, "run", fake_run)
|
||||
assert ai_provider.codex_cli_available() is True
|
||||
assert calls == [["codex", "--version"]]
|
||||
|
||||
|
||||
def test_codex_cli_available_false_when_version_fails(monkeypatch):
|
||||
import subprocess
|
||||
|
||||
monkeypatch.setattr(ai_provider, "_codex_base_command", lambda: ["codex"])
|
||||
monkeypatch.setattr(
|
||||
ai_provider.subprocess, "run", lambda a, **k: _FakeCompleted(1)
|
||||
)
|
||||
assert ai_provider.codex_cli_available() is False
|
||||
# 壳报 "Codex CLI not available" 这类非零退出同样判定不可用
|
||||
monkeypatch.setattr(
|
||||
ai_provider.subprocess,
|
||||
"run",
|
||||
lambda a, **k: (_ for _ in ()).throw(subprocess.TimeoutExpired("codex", 1)),
|
||||
)
|
||||
assert ai_provider.codex_cli_available() is False
|
||||
|
||||
|
||||
def test_codex_cli_available_false_when_command_missing(monkeypatch):
|
||||
def raise_not_found():
|
||||
raise RuntimeError("未找到 Codex CLI 命令: codex")
|
||||
|
||||
monkeypatch.setattr(ai_provider, "_codex_base_command", raise_not_found)
|
||||
assert ai_provider.codex_cli_available() is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_codex_exec_args_exclude_ephemeral(monkeypatch):
|
||||
"""exec 参数不含 --ephemeral: 老版本 codex(如 0.58)无此参数, 传了直接报错。"""
|
||||
captured: dict = {}
|
||||
|
||||
def fake_run_process(args, prompt, env, timeout):
|
||||
captured["args"] = list(args)
|
||||
return 0, b"ok", b""
|
||||
|
||||
monkeypatch.setattr(ai_provider, "_codex_base_command", lambda: ["codex"])
|
||||
monkeypatch.setattr(ai_provider, "_prepare_codex_home", lambda p: None)
|
||||
monkeypatch.setattr(ai_provider, "_codex_process_env", lambda p: {})
|
||||
monkeypatch.setattr(ai_provider, "_run_codex_process", fake_run_process)
|
||||
monkeypatch.setattr(ai_provider, "_read_output_file", lambda p: "ok")
|
||||
monkeypatch.setattr(ai_provider, "_remove_tree_best_effort", lambda p: None)
|
||||
monkeypatch.setattr(ai_provider, "current_ai_model", lambda: "gpt-5.6-sol")
|
||||
|
||||
out = await ai_provider._run_codex_cli(
|
||||
[{"role": "user", "content": "hi"}], max_tokens=None, timeout=1.0,
|
||||
)
|
||||
assert out == "ok"
|
||||
args = captured["args"]
|
||||
assert "--ephemeral" not in args
|
||||
assert "exec" in args and "--skip-git-repo-check" in args
|
||||
assert args[args.index("--model") + 1] == "gpt-5.6-sol"
|
||||
|
||||
Reference in New Issue
Block a user