-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathoperator_codex.py
More file actions
110 lines (98 loc) · 3.71 KB
/
Copy pathoperator_codex.py
File metadata and controls
110 lines (98 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
from __future__ import annotations
import hashlib
import sys
import tempfile
from pathlib import Path
from typing import TextIO
from .operator import ClaudeOperator
from .terminal_ui import TerminalUI
from .utils import CODEX_SANDBOX_CHOICES, DEFAULT_CODEX_SANDBOX, RunPaths, read_text
class CodexOperator(ClaudeOperator):
backend_name = "codex"
def __init__(
self,
command: str = "codex",
model: str = "default",
codex_sandbox: str = DEFAULT_CODEX_SANDBOX,
fake_mode: bool = False,
output_stream: TextIO | None = None,
ui: TerminalUI | None = None,
stage_timeout: int = 14400,
) -> None:
normalized_sandbox = codex_sandbox.strip() if codex_sandbox.strip() else DEFAULT_CODEX_SANDBOX
if normalized_sandbox not in CODEX_SANDBOX_CHOICES:
raise ValueError(
"Unsupported Codex sandbox mode: "
f"{codex_sandbox}. Expected one of: {', '.join(sorted(CODEX_SANDBOX_CHOICES))}."
)
super().__init__(
command=command,
model=model,
fake_mode=fake_mode,
output_stream=output_stream if output_stream is not None else sys.stdout,
ui=ui,
stage_timeout=stage_timeout,
)
self.codex_sandbox = normalized_sandbox
def _prepare_invocation(
self,
prompt_path: Path,
session_id: str,
*,
paths: RunPaths,
resume: bool,
tools: str | None = None,
) -> tuple[list[str], Path, str | None]:
del tools
workspace_alias = self._ensure_workspace_alias(paths)
stdin_text = self._rewrite_prompt_for_alias(prompt_path, paths, workspace_alias)
command = [
self.command,
"-C",
str(workspace_alias),
"exec",
"--json",
"--sandbox",
self.codex_sandbox,
"--skip-git-repo-check",
]
if self.model and self.model != "default":
command.extend(["-m", self.model])
if resume:
command.extend(["resume", session_id])
command.append("-")
return command, Path(tempfile.gettempdir()), stdin_text
def _ensure_workspace_alias(self, paths: RunPaths) -> Path:
alias_root = Path(tempfile.gettempdir()) / "autor_codex_workspaces"
alias_root.mkdir(parents=True, exist_ok=True)
target = paths.run_root.resolve()
run_name = "".join(char if char.isascii() and char.isalnum() else "_" for char in paths.run_root.name)
run_name = run_name.strip("_") or "run"
digest = hashlib.sha1(str(target).encode("utf-8")).hexdigest()[:12]
for index in range(10):
suffix = "" if index == 0 else f"_{index}"
alias = alias_root / f"{run_name}_{digest}{suffix}"
if alias.is_symlink():
try:
if alias.resolve() == target:
return alias
except OSError:
pass
if not alias.exists():
alias.symlink_to(target, target_is_directory=True)
return alias
return target
def _rewrite_prompt_for_alias(self, prompt_path: Path, paths: RunPaths, workspace_alias: Path) -> str:
prompt = read_text(prompt_path)
actual_root = str(paths.run_root.resolve())
alias_root = str(workspace_alias)
return prompt.replace(actual_root, alias_root)
def _select_effective_session_id(
self,
*,
requested_session_id: str | None,
observed_session_id: str | None,
success: bool,
) -> str | None:
del success
return observed_session_id or requested_session_id