python-codex 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycodex/__init__.py +5 -1
- pycodex/agent.py +39 -41
- pycodex/cli.py +43 -42
- pycodex/collaboration.py +6 -7
- pycodex/compat.py +99 -0
- pycodex/context.py +87 -87
- pycodex/doctor.py +40 -40
- pycodex/model.py +69 -69
- pycodex/portable.py +33 -33
- pycodex/portable_server.py +22 -21
- pycodex/protocol.py +84 -86
- pycodex/runtime.py +36 -35
- pycodex/runtime_services.py +69 -69
- pycodex/tools/agent_tool_schemas.py +0 -2
- pycodex/tools/apply_patch_tool.py +43 -44
- pycodex/tools/base_tool.py +35 -36
- pycodex/tools/close_agent_tool.py +2 -4
- pycodex/tools/code_mode_manager.py +61 -61
- pycodex/tools/exec_command_tool.py +5 -6
- pycodex/tools/exec_runtime.js +3 -3
- pycodex/tools/exec_tool.py +2 -4
- pycodex/tools/grep_files_tool.py +10 -11
- pycodex/tools/list_dir_tool.py +8 -9
- pycodex/tools/read_file_tool.py +13 -14
- pycodex/tools/request_permissions_tool.py +2 -4
- pycodex/tools/request_user_input_tool.py +13 -14
- pycodex/tools/resume_agent_tool.py +2 -4
- pycodex/tools/send_input_tool.py +8 -9
- pycodex/tools/shell_command_tool.py +5 -6
- pycodex/tools/shell_tool.py +5 -6
- pycodex/tools/spawn_agent_tool.py +4 -5
- pycodex/tools/unified_exec_manager.py +62 -61
- pycodex/tools/update_plan_tool.py +4 -5
- pycodex/tools/view_image_tool.py +4 -5
- pycodex/tools/wait_agent_tool.py +2 -4
- pycodex/tools/wait_tool.py +4 -5
- pycodex/tools/web_search_tool.py +1 -3
- pycodex/tools/write_stdin_tool.py +4 -5
- pycodex/utils/dotenv.py +6 -6
- pycodex/utils/get_env.py +37 -33
- pycodex/utils/random_ids.py +1 -2
- pycodex/utils/visualize.py +79 -79
- {python_codex-0.1.2.dist-info → python_codex-0.1.3.dist-info}/METADATA +15 -9
- python_codex-0.1.3.dist-info/RECORD +74 -0
- {python_codex-0.1.2.dist-info → python_codex-0.1.3.dist-info}/WHEEL +1 -1
- responses_server/app.py +29 -19
- responses_server/config.py +17 -17
- responses_server/payload_processors.py +16 -16
- responses_server/server.py +11 -11
- responses_server/session_store.py +10 -10
- responses_server/stream_router.py +58 -58
- responses_server/tools/custom_adapter.py +12 -12
- responses_server/tools/web_search.py +33 -33
- python_codex-0.1.2.dist-info/RECORD +0 -73
- {python_codex-0.1.2.dist-info → python_codex-0.1.3.dist-info}/entry_points.txt +0 -0
- {python_codex-0.1.2.dist-info → python_codex-0.1.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,11 +10,10 @@ Expected behavior:
|
|
|
10
10
|
confirmation text Codex uses.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
13
|
from ..protocol import JSONDict, JSONValue
|
|
16
14
|
from ..runtime_services import PlanItem, PlanStore
|
|
17
15
|
from .base_tool import BaseTool, ToolContext
|
|
16
|
+
import typing
|
|
18
17
|
|
|
19
18
|
VALID_PLAN_STATUSES = {"pending", "in_progress", "completed"}
|
|
20
19
|
|
|
@@ -52,16 +51,16 @@ class UpdatePlanTool(BaseTool):
|
|
|
52
51
|
}
|
|
53
52
|
supports_parallel = False
|
|
54
53
|
|
|
55
|
-
def __init__(self, plan_store: PlanStore) -> None:
|
|
54
|
+
def __init__(self, plan_store: 'PlanStore') -> 'None':
|
|
56
55
|
self._plan_store = plan_store
|
|
57
56
|
|
|
58
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
57
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
59
58
|
del context
|
|
60
59
|
raw_plan = args.get("plan")
|
|
61
60
|
if not isinstance(raw_plan, list):
|
|
62
61
|
return "Error: `plan` must be a list."
|
|
63
62
|
|
|
64
|
-
plan_items:
|
|
63
|
+
plan_items: 'typing.List[PlanItem]' = []
|
|
65
64
|
for item in raw_plan:
|
|
66
65
|
if not isinstance(item, dict):
|
|
67
66
|
return "Error: each `plan` item must be an object."
|
pycodex/tools/view_image_tool.py
CHANGED
|
@@ -12,14 +12,13 @@ Expected behavior:
|
|
|
12
12
|
item that Codex uses when feeding image tool output back to the model.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
15
|
import base64
|
|
18
16
|
import mimetypes
|
|
19
17
|
from pathlib import Path
|
|
20
18
|
|
|
21
19
|
from ..protocol import JSONDict, JSONValue
|
|
22
20
|
from .base_tool import BaseTool, StructuredToolOutput, ToolContext
|
|
21
|
+
import typing
|
|
23
22
|
|
|
24
23
|
VIEW_IMAGE_OUTPUT_SCHEMA = {
|
|
25
24
|
"type": "object",
|
|
@@ -62,10 +61,10 @@ class ViewImageTool(BaseTool):
|
|
|
62
61
|
}
|
|
63
62
|
output_schema = VIEW_IMAGE_OUTPUT_SCHEMA
|
|
64
63
|
|
|
65
|
-
def __init__(self, cwd: str
|
|
64
|
+
def __init__(self, cwd: 'typing.Union[typing.Union[str, Path], None]' = None) -> 'None':
|
|
66
65
|
self._workspace_root = Path(cwd or Path.cwd()).resolve()
|
|
67
66
|
|
|
68
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
67
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
69
68
|
del context
|
|
70
69
|
path_value = str(args.get("path", "")).strip()
|
|
71
70
|
if not path_value:
|
|
@@ -102,7 +101,7 @@ class ViewImageTool(BaseTool):
|
|
|
102
101
|
"image_url": image_url,
|
|
103
102
|
"detail": detail,
|
|
104
103
|
}
|
|
105
|
-
image_item: JSONDict = {
|
|
104
|
+
image_item: 'JSONDict' = {
|
|
106
105
|
"type": "input_image",
|
|
107
106
|
"image_url": image_url,
|
|
108
107
|
}
|
pycodex/tools/wait_agent_tool.py
CHANGED
|
@@ -9,8 +9,6 @@ Expected behavior:
|
|
|
9
9
|
wait times out.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
12
|
from ..protocol import JSONDict, JSONValue
|
|
15
13
|
from ..runtime_services import SubAgentManager
|
|
16
14
|
from .agent_tool_schemas import AGENT_STATUS_SCHEMA
|
|
@@ -60,10 +58,10 @@ class WaitAgentTool(BaseTool):
|
|
|
60
58
|
output_schema = WAIT_AGENT_OUTPUT_SCHEMA
|
|
61
59
|
supports_parallel = False
|
|
62
60
|
|
|
63
|
-
def __init__(self, subagent_manager: SubAgentManager) -> None:
|
|
61
|
+
def __init__(self, subagent_manager: 'SubAgentManager') -> 'None':
|
|
64
62
|
self._subagent_manager = subagent_manager
|
|
65
63
|
|
|
66
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
64
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
67
65
|
del context
|
|
68
66
|
ids = args.get("ids")
|
|
69
67
|
if not isinstance(ids, list) or not ids:
|
pycodex/tools/wait_tool.py
CHANGED
|
@@ -9,11 +9,10 @@ Expected behavior:
|
|
|
9
9
|
- Return only the new output since the previous `exec` / `wait` snapshot.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
12
|
from ..protocol import JSONDict, JSONValue
|
|
15
13
|
from .base_tool import BaseTool, ToolContext
|
|
16
14
|
from .code_mode_manager import DEFAULT_WAIT_YIELD_TIME_MS, CodeModeManager
|
|
15
|
+
import typing
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
class WaitTool(BaseTool):
|
|
@@ -46,10 +45,10 @@ class WaitTool(BaseTool):
|
|
|
46
45
|
}
|
|
47
46
|
supports_parallel = False
|
|
48
47
|
|
|
49
|
-
def __init__(self, manager: CodeModeManager) -> None:
|
|
48
|
+
def __init__(self, manager: 'CodeModeManager') -> 'None':
|
|
50
49
|
self._manager = manager
|
|
51
50
|
|
|
52
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
51
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
53
52
|
del context
|
|
54
53
|
cell_id = str(args.get("cell_id", "")).strip()
|
|
55
54
|
if not cell_id:
|
|
@@ -61,7 +60,7 @@ class WaitTool(BaseTool):
|
|
|
61
60
|
terminate=bool(args.get("terminate", False)),
|
|
62
61
|
)
|
|
63
62
|
|
|
64
|
-
def _optional_int(self, args: JSONDict, key: str) -> int
|
|
63
|
+
def _optional_int(self, args: 'JSONDict', key: 'str') -> 'typing.Union[int, None]':
|
|
65
64
|
value = args.get(key)
|
|
66
65
|
if value in (None, ""):
|
|
67
66
|
return None
|
pycodex/tools/web_search_tool.py
CHANGED
|
@@ -10,8 +10,6 @@ Expected behavior:
|
|
|
10
10
|
- Never expect a local tool-call round-trip result from the model.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
13
|
from ..protocol import JSONDict, JSONValue
|
|
16
14
|
from .base_tool import BaseTool, ToolContext
|
|
17
15
|
|
|
@@ -25,6 +23,6 @@ class WebSearchTool(BaseTool):
|
|
|
25
23
|
}
|
|
26
24
|
supports_parallel = False
|
|
27
25
|
|
|
28
|
-
async def run(self, context: ToolContext, args: JSONValue) -> JSONValue:
|
|
26
|
+
async def run(self, context: 'ToolContext', args: 'JSONValue') -> 'JSONValue':
|
|
29
27
|
del context, args
|
|
30
28
|
return "Error: web_search is provider-native and should not be executed locally."
|
|
@@ -10,8 +10,6 @@ Expected behavior:
|
|
|
10
10
|
- Reuse the same `session_id` until the process exits.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
13
|
from ..protocol import JSONDict, JSONValue
|
|
16
14
|
from .base_tool import BaseTool, ToolContext
|
|
17
15
|
from .unified_exec_manager import (
|
|
@@ -19,6 +17,7 @@ from .unified_exec_manager import (
|
|
|
19
17
|
UNIFIED_EXEC_OUTPUT_SCHEMA,
|
|
20
18
|
UnifiedExecManager,
|
|
21
19
|
)
|
|
20
|
+
import typing
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
class WriteStdinTool(BaseTool):
|
|
@@ -50,10 +49,10 @@ class WriteStdinTool(BaseTool):
|
|
|
50
49
|
output_schema = UNIFIED_EXEC_OUTPUT_SCHEMA
|
|
51
50
|
supports_parallel = False
|
|
52
51
|
|
|
53
|
-
def __init__(self, manager: UnifiedExecManager) -> None:
|
|
52
|
+
def __init__(self, manager: 'UnifiedExecManager') -> 'None':
|
|
54
53
|
self._manager = manager
|
|
55
54
|
|
|
56
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
55
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
57
56
|
del context
|
|
58
57
|
session_id = args.get("session_id")
|
|
59
58
|
if session_id is None:
|
|
@@ -68,7 +67,7 @@ class WriteStdinTool(BaseTool):
|
|
|
68
67
|
max_output_tokens=self._optional_int(args, "max_output_tokens"),
|
|
69
68
|
)
|
|
70
69
|
|
|
71
|
-
def _optional_int(self, args: JSONDict, key: str) -> int
|
|
70
|
+
def _optional_int(self, args: 'JSONDict', key: 'str') -> 'typing.Union[int, None]':
|
|
72
71
|
value = args.get(key)
|
|
73
72
|
if value in (None, ""):
|
|
74
73
|
return None
|
pycodex/utils/dotenv.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
1
|
|
|
3
2
|
import os
|
|
4
3
|
from pathlib import Path
|
|
4
|
+
import typing
|
|
5
5
|
|
|
6
6
|
ILLEGAL_ENV_VAR_PREFIX = "CODEX_"
|
|
7
7
|
DOTENV_FILENAME = ".env"
|
|
8
|
-
_LOADED_CODEX_DOTENV_HOMES:
|
|
8
|
+
_LOADED_CODEX_DOTENV_HOMES: 'typing.Set[str]' = set()
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def load_codex_dotenv(config_path: str
|
|
11
|
+
def load_codex_dotenv(config_path: 'typing.Union[str, Path]') -> 'None':
|
|
12
12
|
codex_home = str(Path(config_path).resolve().parent)
|
|
13
13
|
if codex_home in _LOADED_CODEX_DOTENV_HOMES:
|
|
14
14
|
return
|
|
@@ -26,8 +26,8 @@ def load_codex_dotenv(config_path: str | Path) -> None:
|
|
|
26
26
|
_LOADED_CODEX_DOTENV_HOMES.add(codex_home)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def parse_dotenv(text: str) ->
|
|
30
|
-
values:
|
|
29
|
+
def parse_dotenv(text: 'str') -> 'typing.Dict[str, str]':
|
|
30
|
+
values: 'typing.Dict[str, str]' = {}
|
|
31
31
|
for raw_line in text.splitlines():
|
|
32
32
|
line = raw_line.strip()
|
|
33
33
|
if not line or line.startswith("#"):
|
|
@@ -45,7 +45,7 @@ def parse_dotenv(text: str) -> dict[str, str]:
|
|
|
45
45
|
return values
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def parse_dotenv_value(raw_value: str) -> str:
|
|
48
|
+
def parse_dotenv_value(raw_value: 'str') -> 'str':
|
|
49
49
|
if not raw_value:
|
|
50
50
|
return ""
|
|
51
51
|
|
pycodex/utils/get_env.py
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import importlib.metadata
|
|
4
1
|
import os
|
|
5
2
|
import platform
|
|
6
3
|
import re
|
|
7
4
|
from datetime import datetime
|
|
8
5
|
from pathlib import Path
|
|
9
6
|
import subprocess
|
|
7
|
+
import typing
|
|
8
|
+
|
|
9
|
+
from ..compat import importlib_metadata
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def get_shell_name() -> str:
|
|
12
|
+
def get_shell_name() -> 'str':
|
|
13
13
|
shell_path = os.environ.get("SHELL")
|
|
14
14
|
if shell_path:
|
|
15
15
|
return Path(shell_path).name or shell_path
|
|
16
16
|
return "bash"
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def get_timezone_name() -> str:
|
|
19
|
+
def get_timezone_name() -> 'str':
|
|
20
20
|
timezone_env = os.environ.get("TZ")
|
|
21
21
|
if timezone_env:
|
|
22
22
|
return timezone_env
|
|
@@ -33,7 +33,7 @@ def get_timezone_name() -> str:
|
|
|
33
33
|
return "Etc/UTC"
|
|
34
34
|
name = str(timezone)
|
|
35
35
|
return name or "Etc/UTC"
|
|
36
|
-
def get_sandbox_tag(sandbox_mode: str
|
|
36
|
+
def get_sandbox_tag(sandbox_mode: 'typing.Union[str, None]') -> 'str':
|
|
37
37
|
if sandbox_mode == "danger-full-access":
|
|
38
38
|
return "none"
|
|
39
39
|
if sandbox_mode == "read-only":
|
|
@@ -43,7 +43,7 @@ def get_sandbox_tag(sandbox_mode: str | None) -> str:
|
|
|
43
43
|
return "none"
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
def get_workspace_turn_metadata(cwd: str
|
|
46
|
+
def get_workspace_turn_metadata(cwd: 'typing.Union[str, Path]') -> 'typing.Union[typing.Dict[str, object], None]':
|
|
47
47
|
resolved_cwd = Path(cwd).resolve()
|
|
48
48
|
repo_root = _git_output(
|
|
49
49
|
resolved_cwd,
|
|
@@ -52,7 +52,7 @@ def get_workspace_turn_metadata(cwd: str | Path) -> dict[str, object] | None:
|
|
|
52
52
|
if repo_root is None:
|
|
53
53
|
return None
|
|
54
54
|
|
|
55
|
-
workspace:
|
|
55
|
+
workspace: 'typing.Dict[str, object]' = {}
|
|
56
56
|
head = _git_output(resolved_cwd, ["rev-parse", "HEAD"])
|
|
57
57
|
if head is not None:
|
|
58
58
|
workspace["latest_git_commit_hash"] = head
|
|
@@ -70,7 +70,7 @@ def get_workspace_turn_metadata(cwd: str | Path) -> dict[str, object] | None:
|
|
|
70
70
|
return {"workspaces": {repo_root: workspace}}
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def build_user_agent(originator: str) -> str:
|
|
73
|
+
def build_user_agent(originator: 'str') -> 'str':
|
|
74
74
|
version = get_package_version()
|
|
75
75
|
terminal = get_terminal_user_agent_token()
|
|
76
76
|
os_name, os_version = get_os_info()
|
|
@@ -79,14 +79,14 @@ def build_user_agent(originator: str) -> str:
|
|
|
79
79
|
return f"{originator}/{version} ({os_name} {os_version}; {arch}) {terminal}{suffix}"
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def get_package_version() -> str:
|
|
82
|
+
def get_package_version() -> 'str':
|
|
83
83
|
detected = _detect_upstream_codex_version()
|
|
84
84
|
if detected is not None:
|
|
85
85
|
return detected
|
|
86
86
|
for distribution_name in ("python-codex", "pycodex"):
|
|
87
87
|
try:
|
|
88
|
-
return
|
|
89
|
-
except
|
|
88
|
+
return importlib_metadata.version(distribution_name)
|
|
89
|
+
except importlib_metadata.PackageNotFoundError:
|
|
90
90
|
continue
|
|
91
91
|
local_version = _read_local_package_version()
|
|
92
92
|
if local_version is not None:
|
|
@@ -94,10 +94,10 @@ def get_package_version() -> str:
|
|
|
94
94
|
return "0.1.0"
|
|
95
95
|
|
|
96
96
|
|
|
97
|
-
def get_os_info() ->
|
|
97
|
+
def get_os_info() -> 'typing.Tuple[str, str]':
|
|
98
98
|
os_release = Path("/etc/os-release")
|
|
99
99
|
if os_release.is_file():
|
|
100
|
-
values:
|
|
100
|
+
values: 'typing.Dict[str, str]' = {}
|
|
101
101
|
for line in os_release.read_text().splitlines():
|
|
102
102
|
if "=" not in line:
|
|
103
103
|
continue
|
|
@@ -110,7 +110,7 @@ def get_os_info() -> tuple[str, str]:
|
|
|
110
110
|
return platform.system(), platform.release()
|
|
111
111
|
|
|
112
112
|
|
|
113
|
-
def get_terminal_user_agent_token() -> str:
|
|
113
|
+
def get_terminal_user_agent_token() -> 'str':
|
|
114
114
|
term_program = os.environ.get("TERM_PROGRAM", "")
|
|
115
115
|
if term_program.lower() == "tmux":
|
|
116
116
|
client_termname = _tmux_display_message("#{client_termname}")
|
|
@@ -123,14 +123,15 @@ def get_terminal_user_agent_token() -> str:
|
|
|
123
123
|
return "unknown"
|
|
124
124
|
|
|
125
125
|
|
|
126
|
-
def _git_output(cwd: Path, args:
|
|
126
|
+
def _git_output(cwd: 'Path', args: 'typing.List[str]') -> 'typing.Union[str, None]':
|
|
127
127
|
try:
|
|
128
128
|
completed = subprocess.run(
|
|
129
129
|
["git", *args],
|
|
130
130
|
cwd=str(cwd),
|
|
131
131
|
check=True,
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
stdout=subprocess.PIPE,
|
|
133
|
+
stderr=subprocess.PIPE,
|
|
134
|
+
universal_newlines=True,
|
|
134
135
|
)
|
|
135
136
|
except (OSError, subprocess.CalledProcessError):
|
|
136
137
|
return None
|
|
@@ -138,11 +139,11 @@ def _git_output(cwd: Path, args: list[str]) -> str | None:
|
|
|
138
139
|
return value or None
|
|
139
140
|
|
|
140
141
|
|
|
141
|
-
def _git_remote_urls(cwd: Path) ->
|
|
142
|
+
def _git_remote_urls(cwd: 'Path') -> 'typing.Dict[str, str]':
|
|
142
143
|
remote_names = _git_output(cwd, ["remote"])
|
|
143
144
|
if remote_names is None:
|
|
144
145
|
return {}
|
|
145
|
-
remotes:
|
|
146
|
+
remotes: 'typing.Dict[str, str]' = {}
|
|
146
147
|
for name in remote_names.splitlines():
|
|
147
148
|
remote_name = name.strip()
|
|
148
149
|
if not remote_name:
|
|
@@ -153,21 +154,22 @@ def _git_remote_urls(cwd: Path) -> dict[str, str]:
|
|
|
153
154
|
return remotes
|
|
154
155
|
|
|
155
156
|
|
|
156
|
-
def _git_has_changes(cwd: Path) -> bool
|
|
157
|
+
def _git_has_changes(cwd: 'Path') -> 'typing.Union[bool, None]':
|
|
157
158
|
try:
|
|
158
159
|
completed = subprocess.run(
|
|
159
160
|
["git", "status", "--porcelain"],
|
|
160
161
|
cwd=str(cwd),
|
|
161
162
|
check=True,
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
stdout=subprocess.PIPE,
|
|
164
|
+
stderr=subprocess.PIPE,
|
|
165
|
+
universal_newlines=True,
|
|
164
166
|
)
|
|
165
167
|
except (OSError, subprocess.CalledProcessError):
|
|
166
168
|
return None
|
|
167
169
|
return bool(completed.stdout.strip())
|
|
168
170
|
|
|
169
171
|
|
|
170
|
-
def _user_agent_suffix(originator: str, version: str) -> str:
|
|
172
|
+
def _user_agent_suffix(originator: 'str', version: 'str') -> 'str':
|
|
171
173
|
if originator == "codex_exec":
|
|
172
174
|
return f" (codex-exec; {version})"
|
|
173
175
|
if originator == "codex-tui":
|
|
@@ -175,7 +177,7 @@ def _user_agent_suffix(originator: str, version: str) -> str:
|
|
|
175
177
|
return ""
|
|
176
178
|
|
|
177
179
|
|
|
178
|
-
def _normalize_os_version(version: str) -> str:
|
|
180
|
+
def _normalize_os_version(version: 'str') -> 'str':
|
|
179
181
|
parts = version.split(".")
|
|
180
182
|
if len(parts) == 2 and all(part.isdigit() for part in parts):
|
|
181
183
|
major, minor = parts
|
|
@@ -183,7 +185,7 @@ def _normalize_os_version(version: str) -> str:
|
|
|
183
185
|
return version
|
|
184
186
|
|
|
185
187
|
|
|
186
|
-
def _read_local_package_version() -> str
|
|
188
|
+
def _read_local_package_version() -> 'typing.Union[str, None]':
|
|
187
189
|
pyproject_path = Path(__file__).resolve().parents[2] / "pyproject.toml"
|
|
188
190
|
if not pyproject_path.is_file():
|
|
189
191
|
return None
|
|
@@ -197,13 +199,14 @@ def _read_local_package_version() -> str | None:
|
|
|
197
199
|
return match.group(1).strip() or None
|
|
198
200
|
|
|
199
201
|
|
|
200
|
-
def _tmux_display_message(fmt: str) -> str
|
|
202
|
+
def _tmux_display_message(fmt: 'str') -> 'typing.Union[str, None]':
|
|
201
203
|
try:
|
|
202
204
|
output = subprocess.run(
|
|
203
205
|
["tmux", "display-message", "-p", fmt],
|
|
204
206
|
check=True,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
+
stdout=subprocess.PIPE,
|
|
208
|
+
stderr=subprocess.PIPE,
|
|
209
|
+
universal_newlines=True,
|
|
207
210
|
)
|
|
208
211
|
except (OSError, subprocess.CalledProcessError):
|
|
209
212
|
return None
|
|
@@ -211,7 +214,7 @@ def _tmux_display_message(fmt: str) -> str | None:
|
|
|
211
214
|
return value or None
|
|
212
215
|
|
|
213
216
|
|
|
214
|
-
def _sanitize_header_token(value: str) -> str:
|
|
217
|
+
def _sanitize_header_token(value: 'str') -> 'str':
|
|
215
218
|
return "".join(
|
|
216
219
|
character
|
|
217
220
|
if (character.isalnum() or character in {"-", "_", ".", "/"})
|
|
@@ -220,13 +223,14 @@ def _sanitize_header_token(value: str) -> str:
|
|
|
220
223
|
)
|
|
221
224
|
|
|
222
225
|
|
|
223
|
-
def _detect_upstream_codex_version() -> str
|
|
226
|
+
def _detect_upstream_codex_version() -> 'typing.Union[str, None]':
|
|
224
227
|
try:
|
|
225
228
|
output = subprocess.run(
|
|
226
229
|
["codex", "--version"],
|
|
227
230
|
check=True,
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
stdout=subprocess.PIPE,
|
|
232
|
+
stderr=subprocess.PIPE,
|
|
233
|
+
universal_newlines=True,
|
|
230
234
|
)
|
|
231
235
|
except (OSError, subprocess.CalledProcessError):
|
|
232
236
|
return None
|
pycodex/utils/random_ids.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
1
|
|
|
3
2
|
import random
|
|
4
3
|
import time
|
|
5
4
|
import uuid
|
|
6
5
|
|
|
7
6
|
|
|
8
|
-
def uuid7_string() -> str:
|
|
7
|
+
def uuid7_string() -> 'str':
|
|
9
8
|
timestamp_ms = int(time.time() * 1000) & ((1 << 48) - 1)
|
|
10
9
|
rand_a = random.getrandbits(12)
|
|
11
10
|
rand_b = random.getrandbits(62)
|