1
0
mirror of https://github.com/GuDaStudio/codexmcp.git synced 2026-02-05 02:00:24 +08:00

v0.7.5:重构 codex 函数参数并优化错误提示

This commit is contained in:
GuDaStudio
2025-12-02 17:36:31 +08:00
parent e6931e6888
commit d6279bd4a7

View File

@@ -137,10 +137,9 @@ async def codex(
), ),
] = "read-only", ] = "read-only",
SESSION_ID: Annotated[ SESSION_ID: Annotated[
Optional[uuid.UUID], str,
BeforeValidator(_empty_str_to_none),
"Resume the specified session of the codex. Defaults to `None`, start a new session.", "Resume the specified session of the codex. Defaults to `None`, start a new session.",
] = None, ] = "",
skip_git_repo_check: Annotated[ skip_git_repo_check: Annotated[
bool, bool,
"Allow codex running outside a Git repository (useful for one-off directories).", "Allow codex running outside a Git repository (useful for one-off directories).",
@@ -150,39 +149,39 @@ async def codex(
"Return all messages (e.g. reasoning, tool calls, etc.) from the codex session. Set to `False` by default, only the agent's final reply message is returned.", "Return all messages (e.g. reasoning, tool calls, etc.) from the codex session. Set to `False` by default, only the agent's final reply message is returned.",
] = False, ] = False,
image: Annotated[ image: Annotated[
Optional[List[Path]], List[Path],
Field( Field(
description="Attach one or more image files to the initial prompt. Separate multiple paths with commas or repeat the flag.", description="Attach one or more image files to the initial prompt. Separate multiple paths with commas or repeat the flag.",
), ),
] = None, ] = [],
model: Annotated[ model: Annotated[
Optional[str], str,
Field( Field(
description="The model to use for the codex session. This parameter is strictly prohibited unless explicitly specified by the user.", description="The model to use for the codex session. This parameter is strictly prohibited unless explicitly specified by the user.",
), ),
] = None, ] = "",
yolo: Annotated[ yolo: Annotated[
Optional[bool], bool,
Field( Field(
description="Run every command without approvals or sandboxing. Only use when `sandbox` couldn't be applied.", description="Run every command without approvals or sandboxing. Only use when `sandbox` couldn't be applied.",
), ),
] = False, ] = False,
profile: Annotated[ profile: Annotated[
Optional[str], str,
"Configuration profile name to load from `~/.codex/config.toml`. This parameter is strictly prohibited unless explicitly specified by the user.", "Configuration profile name to load from `~/.codex/config.toml`. This parameter is strictly prohibited unless explicitly specified by the user.",
] = None, ] = "",
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Execute a Codex CLI session and return the results.""" """Execute a Codex CLI session and return the results."""
# Build command as list to avoid injection # Build command as list to avoid injection
cmd = ["codex", "exec", "--sandbox", sandbox, "--cd", str(cd), "--json"] cmd = ["codex", "exec", "--sandbox", sandbox, "--cd", str(cd), "--json"]
if image is not None: if len(image):
cmd.extend(["--image", ",".join(image)]) cmd.extend(["--image", ",".join(image)])
if model is not None: if model:
cmd.extend(["--model", model]) cmd.extend(["--model", model])
if profile is not None: if profile:
cmd.extend(["--profile", profile]) cmd.extend(["--profile", profile])
if yolo: if yolo:
@@ -191,7 +190,7 @@ async def codex(
if skip_git_repo_check: if skip_git_repo_check:
cmd.append("--skip-git-repo-check") cmd.append("--skip-git-repo-check")
if SESSION_ID is not None: if SESSION_ID:
cmd.extend(["resume", str(SESSION_ID)]) cmd.extend(["resume", str(SESSION_ID)])
if os.name == "nt": if os.name == "nt":
@@ -218,7 +217,7 @@ async def codex(
thread_id = line_dict.get("thread_id") thread_id = line_dict.get("thread_id")
if "fail" in line_dict.get("type", ""): if "fail" in line_dict.get("type", ""):
success = False if len(agent_messages) == 0 else success success = False if len(agent_messages) == 0 else success
err_message = "codex error: " + line_dict.get("error", {}).get("message", "") err_message += "\n\n[codex error] " + line_dict.get("error", {}).get("message", "")
if "error" in line_dict.get("type", ""): if "error" in line_dict.get("type", ""):
error_msg = line_dict.get("message", "") error_msg = line_dict.get("message", "")
import re import re
@@ -226,15 +225,16 @@ async def codex(
if not is_reconnecting: if not is_reconnecting:
success = False if len(agent_messages) == 0 else success success = False if len(agent_messages) == 0 else success
err_message = "codex error: " + error_msg err_message += "\n\n[codex error] " + error_msg
except json.JSONDecodeError: except json.JSONDecodeError:
import sys # import sys
print(f"Ignored non-JSON line: {line}", file=sys.stderr) # print(f"Ignored non-JSON line: {line}", file=sys.stderr)
err_message += "\n\n[json decode error] " + line
continue continue
except Exception as error: except Exception as error:
err_message = f"Unexpected error: {error}. Line: {line!r}" err_message += "\n\n[unexpected error] " + f"Unexpected error: {error}. Line: {line!r}"
success = False success = False
break break
@@ -244,7 +244,7 @@ async def codex(
if len(agent_messages) == 0: if len(agent_messages) == 0:
success = False success = False
err_message = "Failed to get `agent_messages` from the codex session. \n\n You can try to set `return_all_messages` to `True` to get the full reasoning information. \n\n " + err_message err_message = "Failed to get `agent_messages` from the codex session. \n\n You can try to set `return_all_messages` to `True` to get the full reasoning information. " + err_message
if success: if success:
result: Dict[str, Any] = { result: Dict[str, Any] = {
@@ -253,10 +253,12 @@ async def codex(
"agent_messages": agent_messages, "agent_messages": agent_messages,
# "PROMPT": PROMPT, # "PROMPT": PROMPT,
} }
if return_all_messages:
result["all_messages"] = all_messages
else: else:
result = {"success": False, "error": err_message} result = {"success": False, "error": err_message}
if return_all_messages:
result["all_messages"] = all_messages
return result return result