python-codex 0.1.1__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.
Files changed (59) hide show
  1. pycodex/__init__.py +5 -1
  2. pycodex/agent.py +39 -41
  3. pycodex/cli.py +51 -43
  4. pycodex/collaboration.py +6 -7
  5. pycodex/compat.py +99 -0
  6. pycodex/context.py +87 -87
  7. pycodex/doctor.py +40 -40
  8. pycodex/model.py +69 -69
  9. pycodex/portable.py +33 -33
  10. pycodex/portable_server.py +22 -21
  11. pycodex/protocol.py +84 -86
  12. pycodex/runtime.py +36 -35
  13. pycodex/runtime_services.py +72 -69
  14. pycodex/tools/agent_tool_schemas.py +0 -2
  15. pycodex/tools/apply_patch_tool.py +43 -44
  16. pycodex/tools/base_tool.py +35 -36
  17. pycodex/tools/close_agent_tool.py +2 -4
  18. pycodex/tools/code_mode_manager.py +61 -61
  19. pycodex/tools/exec_command_tool.py +5 -6
  20. pycodex/tools/exec_runtime.js +3 -3
  21. pycodex/tools/exec_tool.py +3 -5
  22. pycodex/tools/grep_files_tool.py +10 -11
  23. pycodex/tools/list_dir_tool.py +8 -9
  24. pycodex/tools/read_file_tool.py +13 -14
  25. pycodex/tools/request_permissions_tool.py +2 -4
  26. pycodex/tools/request_user_input_tool.py +13 -14
  27. pycodex/tools/resume_agent_tool.py +2 -4
  28. pycodex/tools/send_input_tool.py +8 -9
  29. pycodex/tools/shell_command_tool.py +5 -6
  30. pycodex/tools/shell_tool.py +5 -6
  31. pycodex/tools/spawn_agent_tool.py +4 -5
  32. pycodex/tools/unified_exec_manager.py +79 -61
  33. pycodex/tools/update_plan_tool.py +4 -5
  34. pycodex/tools/view_image_tool.py +4 -5
  35. pycodex/tools/wait_agent_tool.py +2 -4
  36. pycodex/tools/wait_tool.py +4 -5
  37. pycodex/tools/web_search_tool.py +1 -3
  38. pycodex/tools/write_stdin_tool.py +4 -5
  39. pycodex/utils/dotenv.py +6 -6
  40. pycodex/utils/get_env.py +57 -34
  41. pycodex/utils/random_ids.py +1 -2
  42. pycodex/utils/visualize.py +79 -79
  43. {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/METADATA +15 -9
  44. python_codex-0.1.3.dist-info/RECORD +74 -0
  45. {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/WHEEL +1 -1
  46. responses_server/__init__.py +17 -0
  47. responses_server/__main__.py +5 -0
  48. responses_server/app.py +227 -0
  49. responses_server/config.py +63 -0
  50. responses_server/payload_processors.py +86 -0
  51. responses_server/server.py +63 -0
  52. responses_server/session_store.py +37 -0
  53. responses_server/stream_router.py +784 -0
  54. responses_server/tools/__init__.py +4 -0
  55. responses_server/tools/custom_adapter.py +235 -0
  56. responses_server/tools/web_search.py +263 -0
  57. python_codex-0.1.1.dist-info/RECORD +0 -62
  58. {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/entry_points.txt +0 -0
  59. {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/licenses/LICENSE +0 -0
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 | None) -> 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 | Path) -> dict[str, object] | None:
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: dict[str, object] = {}
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,20 +79,25 @@ 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
- try:
87
- return importlib.metadata.version("pycodex")
88
- except importlib.metadata.PackageNotFoundError:
89
- return "0.1.0"
86
+ for distribution_name in ("python-codex", "pycodex"):
87
+ try:
88
+ return importlib_metadata.version(distribution_name)
89
+ except importlib_metadata.PackageNotFoundError:
90
+ continue
91
+ local_version = _read_local_package_version()
92
+ if local_version is not None:
93
+ return local_version
94
+ return "0.1.0"
90
95
 
91
96
 
92
- def get_os_info() -> tuple[str, str]:
97
+ def get_os_info() -> 'typing.Tuple[str, str]':
93
98
  os_release = Path("/etc/os-release")
94
99
  if os_release.is_file():
95
- values: dict[str, str] = {}
100
+ values: 'typing.Dict[str, str]' = {}
96
101
  for line in os_release.read_text().splitlines():
97
102
  if "=" not in line:
98
103
  continue
@@ -105,7 +110,7 @@ def get_os_info() -> tuple[str, str]:
105
110
  return platform.system(), platform.release()
106
111
 
107
112
 
108
- def get_terminal_user_agent_token() -> str:
113
+ def get_terminal_user_agent_token() -> 'str':
109
114
  term_program = os.environ.get("TERM_PROGRAM", "")
110
115
  if term_program.lower() == "tmux":
111
116
  client_termname = _tmux_display_message("#{client_termname}")
@@ -118,14 +123,15 @@ def get_terminal_user_agent_token() -> str:
118
123
  return "unknown"
119
124
 
120
125
 
121
- def _git_output(cwd: Path, args: list[str]) -> str | None:
126
+ def _git_output(cwd: 'Path', args: 'typing.List[str]') -> 'typing.Union[str, None]':
122
127
  try:
123
128
  completed = subprocess.run(
124
129
  ["git", *args],
125
130
  cwd=str(cwd),
126
131
  check=True,
127
- capture_output=True,
128
- text=True,
132
+ stdout=subprocess.PIPE,
133
+ stderr=subprocess.PIPE,
134
+ universal_newlines=True,
129
135
  )
130
136
  except (OSError, subprocess.CalledProcessError):
131
137
  return None
@@ -133,11 +139,11 @@ def _git_output(cwd: Path, args: list[str]) -> str | None:
133
139
  return value or None
134
140
 
135
141
 
136
- def _git_remote_urls(cwd: Path) -> dict[str, str]:
142
+ def _git_remote_urls(cwd: 'Path') -> 'typing.Dict[str, str]':
137
143
  remote_names = _git_output(cwd, ["remote"])
138
144
  if remote_names is None:
139
145
  return {}
140
- remotes: dict[str, str] = {}
146
+ remotes: 'typing.Dict[str, str]' = {}
141
147
  for name in remote_names.splitlines():
142
148
  remote_name = name.strip()
143
149
  if not remote_name:
@@ -148,21 +154,22 @@ def _git_remote_urls(cwd: Path) -> dict[str, str]:
148
154
  return remotes
149
155
 
150
156
 
151
- def _git_has_changes(cwd: Path) -> bool | None:
157
+ def _git_has_changes(cwd: 'Path') -> 'typing.Union[bool, None]':
152
158
  try:
153
159
  completed = subprocess.run(
154
160
  ["git", "status", "--porcelain"],
155
161
  cwd=str(cwd),
156
162
  check=True,
157
- capture_output=True,
158
- text=True,
163
+ stdout=subprocess.PIPE,
164
+ stderr=subprocess.PIPE,
165
+ universal_newlines=True,
159
166
  )
160
167
  except (OSError, subprocess.CalledProcessError):
161
168
  return None
162
169
  return bool(completed.stdout.strip())
163
170
 
164
171
 
165
- def _user_agent_suffix(originator: str, version: str) -> str:
172
+ def _user_agent_suffix(originator: 'str', version: 'str') -> 'str':
166
173
  if originator == "codex_exec":
167
174
  return f" (codex-exec; {version})"
168
175
  if originator == "codex-tui":
@@ -170,7 +177,7 @@ def _user_agent_suffix(originator: str, version: str) -> str:
170
177
  return ""
171
178
 
172
179
 
173
- def _normalize_os_version(version: str) -> str:
180
+ def _normalize_os_version(version: 'str') -> 'str':
174
181
  parts = version.split(".")
175
182
  if len(parts) == 2 and all(part.isdigit() for part in parts):
176
183
  major, minor = parts
@@ -178,13 +185,28 @@ def _normalize_os_version(version: str) -> str:
178
185
  return version
179
186
 
180
187
 
181
- def _tmux_display_message(fmt: str) -> str | None:
188
+ def _read_local_package_version() -> 'typing.Union[str, None]':
189
+ pyproject_path = Path(__file__).resolve().parents[2] / "pyproject.toml"
190
+ if not pyproject_path.is_file():
191
+ return None
192
+ match = re.search(
193
+ r'^\s*version\s*=\s*"([^"]+)"\s*$',
194
+ pyproject_path.read_text(encoding="utf-8"),
195
+ flags=re.MULTILINE,
196
+ )
197
+ if match is None:
198
+ return None
199
+ return match.group(1).strip() or None
200
+
201
+
202
+ def _tmux_display_message(fmt: 'str') -> 'typing.Union[str, None]':
182
203
  try:
183
204
  output = subprocess.run(
184
205
  ["tmux", "display-message", "-p", fmt],
185
206
  check=True,
186
- capture_output=True,
187
- text=True,
207
+ stdout=subprocess.PIPE,
208
+ stderr=subprocess.PIPE,
209
+ universal_newlines=True,
188
210
  )
189
211
  except (OSError, subprocess.CalledProcessError):
190
212
  return None
@@ -192,7 +214,7 @@ def _tmux_display_message(fmt: str) -> str | None:
192
214
  return value or None
193
215
 
194
216
 
195
- def _sanitize_header_token(value: str) -> str:
217
+ def _sanitize_header_token(value: 'str') -> 'str':
196
218
  return "".join(
197
219
  character
198
220
  if (character.isalnum() or character in {"-", "_", ".", "/"})
@@ -201,13 +223,14 @@ def _sanitize_header_token(value: str) -> str:
201
223
  )
202
224
 
203
225
 
204
- def _detect_upstream_codex_version() -> str | None:
226
+ def _detect_upstream_codex_version() -> 'typing.Union[str, None]':
205
227
  try:
206
228
  output = subprocess.run(
207
229
  ["codex", "--version"],
208
230
  check=True,
209
- capture_output=True,
210
- text=True,
231
+ stdout=subprocess.PIPE,
232
+ stderr=subprocess.PIPE,
233
+ universal_newlines=True,
211
234
  )
212
235
  except (OSError, subprocess.CalledProcessError):
213
236
  return None
@@ -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)