codexapi 0.1.2__tar.gz → 0.1.7__tar.gz

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.
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.1
2
+ Name: codexapi
3
+ Version: 0.1.7
4
+ Summary: Minimal Python API for running the Codex CLI.
5
+ License: MIT
6
+ Keywords: codex,agent,cli,openai
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+
13
+ # CodexAPI
14
+
15
+ Use OpenAI's codex from python as easily as calling a function with your codex credits instead of the API.
16
+
17
+ *Note: this project is not affiliated with OpenAI in any way. Thanks for the awesome tools and models though!*
18
+
19
+ ## Requirements
20
+
21
+ - Codex CLI installed and authenticated (`codex` must be on your PATH).
22
+ - Python 3.8+.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install codexapi
28
+ ```
29
+
30
+ ## Quickstart
31
+
32
+ ```python
33
+ from codexapi import agent, Agent, Task
34
+
35
+ # Run one-shot tasks as a function call
36
+ print(agent("Say hello"))
37
+
38
+ # Run a multi-turn conversation as a session
39
+ session = Agent(cwd="/path/to/project")
40
+ print(session("Summarize this repo."))
41
+ print(session("Now list any risks."))
42
+
43
+ # Save and resume a session later
44
+ thread_id = session.thread_id
45
+ session2 = Agent(cwd="/path/to/project", thread_id=thread_id)
46
+ print(session2("Continue from where we left off."))
47
+
48
+ # Define a task with a checker
49
+ class RepoTask(Task):
50
+ def check(self):
51
+ # Return an error string if something is wrong, or None/"" if OK
52
+ return None
53
+
54
+ task = RepoTask("Summarize this repo.", cwd="/path/to/project")
55
+ result = task()
56
+ print(result.success, result.summary)
57
+ ```
58
+
59
+ ## CLI
60
+
61
+ After installing, use the `codexapi` command:
62
+
63
+ ```bash
64
+ codexapi "Summarize this repo."
65
+ codexapi --cwd /path/to/project "Fix the failing tests."
66
+ echo "Say hello." | codexapi
67
+ ```
68
+
69
+ Task mode exits with code 0 on success and 1 on failure, printing the summary.
70
+
71
+ Show running sessions and their latest activity:
72
+
73
+ ```bash
74
+ codexapi top
75
+ ```
76
+ Press `h` for keys.
77
+
78
+ Resume a session and print the thread id to stderr:
79
+
80
+ ```bash
81
+ codexapi --thread-id THREAD_ID --print-thread-id "Continue where we left off."
82
+ ```
83
+
84
+ ## API
85
+
86
+ ### `agent(prompt, cwd=None, yolo=False, flags=None) -> str`
87
+
88
+ Runs a single Codex turn and returns only the agent's message. Any reasoning
89
+ items are filtered out.
90
+
91
+ - `prompt` (str): prompt to send to Codex.
92
+ - `cwd` (str | PathLike | None): working directory for the Codex session.
93
+ - `yolo` (bool): pass `--yolo` to Codex when true.
94
+ - `flags` (str | None): extra CLI flags to pass to Codex.
95
+
96
+ ### `Agent(cwd=None, yolo=False, thread_id=None, flags=None)`
97
+
98
+ Creates a stateful session wrapper. Calling the instance sends the prompt into
99
+ the same conversation and returns only the agent's message.
100
+
101
+ - `__call__(prompt) -> str`: send a prompt to Codex and return the message.
102
+ - `thread_id -> str | None`: expose the underlying session id once created.
103
+ - `yolo` (bool): pass `--yolo` to Codex when true.
104
+ - `flags` (str | None): extra CLI flags to pass to Codex.
105
+
106
+ ### `task(prompt, check=None, n=10, cwd=None, yolo=False, flags=None) -> str`
107
+
108
+ Runs a task with checker-driven retries and returns the success summary.
109
+ Raises `TaskFailed` when the maximum attempts are reached.
110
+
111
+ - `check` (str | None | False): custom check prompt, default checker, or `False` to skip.
112
+ - `n` (int): maximum number of retries after a failed check.
113
+
114
+ ### `task_result(prompt, check=None, n=10, cwd=None, yolo=False, flags=None) -> TaskResult`
115
+
116
+ Runs a task with checker-driven retries and returns a `TaskResult` without
117
+ raising `TaskFailed`.
118
+
119
+ ### `Task(prompt, max_attempts=10, cwd=None, yolo=False, thread_id=None, flags=None)`
120
+
121
+ Runs a Codex task with checker-driven retries. Subclass it and implement
122
+ `check()` to return an error string when the task is incomplete, or return
123
+ `None`/`""` when the task passes.
124
+
125
+ - `__call__() -> TaskResult`: run the task.
126
+ - `set_up()`: optional setup hook.
127
+ - `tear_down()`: optional cleanup hook.
128
+ - `check() -> str | None`: return an error description or `None`/`""`.
129
+ - `on_success(result)`: optional success hook.
130
+ - `on_failure(result)`: optional failure hook.
131
+
132
+ ### `TaskResult(success, summary, attempts, errors, thread_id)`
133
+
134
+ Simple result object returned by `Task.__call__`.
135
+
136
+ - `success` (bool): whether the task completed successfully.
137
+ - `summary` (str): agent summary of what happened.
138
+ - `attempts` (int): how many attempts were used.
139
+ - `errors` (str | None): last checker error, if any.
140
+ - `thread_id` (str | None): Codex thread id for the session.
141
+
142
+ ### `TaskFailed`
143
+
144
+ Exception raised by `task()` when retries are exhausted.
145
+
146
+ - `summary` (str): failure summary text.
147
+ - `attempts` (int | None): attempts made when the task failed.
148
+ - `errors` (str | None): last checker error, if any.
149
+
150
+ ## Behavior notes
151
+
152
+ - Uses `codex exec --json` and parses JSONL events for `agent_message` items.
153
+ - Automatically passes `--skip-git-repo-check` so it can run outside a git repo.
154
+ - Passes `--full-auto` unless `--yolo` is enabled.
155
+ - Passes `--yolo` when enabled (use with care).
156
+ - Raises `RuntimeError` if Codex exits non-zero or returns no agent message.
157
+
158
+ ## Configuration
159
+
160
+ Set `CODEX_BIN` to point at a non-default Codex binary:
161
+
162
+ ```bash
163
+ export CODEX_BIN=/path/to/codex
164
+ ```
@@ -0,0 +1,152 @@
1
+ # CodexAPI
2
+
3
+ Use OpenAI's codex from python as easily as calling a function with your codex credits instead of the API.
4
+
5
+ *Note: this project is not affiliated with OpenAI in any way. Thanks for the awesome tools and models though!*
6
+
7
+ ## Requirements
8
+
9
+ - Codex CLI installed and authenticated (`codex` must be on your PATH).
10
+ - Python 3.8+.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install codexapi
16
+ ```
17
+
18
+ ## Quickstart
19
+
20
+ ```python
21
+ from codexapi import agent, Agent, Task
22
+
23
+ # Run one-shot tasks as a function call
24
+ print(agent("Say hello"))
25
+
26
+ # Run a multi-turn conversation as a session
27
+ session = Agent(cwd="/path/to/project")
28
+ print(session("Summarize this repo."))
29
+ print(session("Now list any risks."))
30
+
31
+ # Save and resume a session later
32
+ thread_id = session.thread_id
33
+ session2 = Agent(cwd="/path/to/project", thread_id=thread_id)
34
+ print(session2("Continue from where we left off."))
35
+
36
+ # Define a task with a checker
37
+ class RepoTask(Task):
38
+ def check(self):
39
+ # Return an error string if something is wrong, or None/"" if OK
40
+ return None
41
+
42
+ task = RepoTask("Summarize this repo.", cwd="/path/to/project")
43
+ result = task()
44
+ print(result.success, result.summary)
45
+ ```
46
+
47
+ ## CLI
48
+
49
+ After installing, use the `codexapi` command:
50
+
51
+ ```bash
52
+ codexapi "Summarize this repo."
53
+ codexapi --cwd /path/to/project "Fix the failing tests."
54
+ echo "Say hello." | codexapi
55
+ ```
56
+
57
+ Task mode exits with code 0 on success and 1 on failure, printing the summary.
58
+
59
+ Show running sessions and their latest activity:
60
+
61
+ ```bash
62
+ codexapi top
63
+ ```
64
+ Press `h` for keys.
65
+
66
+ Resume a session and print the thread id to stderr:
67
+
68
+ ```bash
69
+ codexapi --thread-id THREAD_ID --print-thread-id "Continue where we left off."
70
+ ```
71
+
72
+ ## API
73
+
74
+ ### `agent(prompt, cwd=None, yolo=False, flags=None) -> str`
75
+
76
+ Runs a single Codex turn and returns only the agent's message. Any reasoning
77
+ items are filtered out.
78
+
79
+ - `prompt` (str): prompt to send to Codex.
80
+ - `cwd` (str | PathLike | None): working directory for the Codex session.
81
+ - `yolo` (bool): pass `--yolo` to Codex when true.
82
+ - `flags` (str | None): extra CLI flags to pass to Codex.
83
+
84
+ ### `Agent(cwd=None, yolo=False, thread_id=None, flags=None)`
85
+
86
+ Creates a stateful session wrapper. Calling the instance sends the prompt into
87
+ the same conversation and returns only the agent's message.
88
+
89
+ - `__call__(prompt) -> str`: send a prompt to Codex and return the message.
90
+ - `thread_id -> str | None`: expose the underlying session id once created.
91
+ - `yolo` (bool): pass `--yolo` to Codex when true.
92
+ - `flags` (str | None): extra CLI flags to pass to Codex.
93
+
94
+ ### `task(prompt, check=None, n=10, cwd=None, yolo=False, flags=None) -> str`
95
+
96
+ Runs a task with checker-driven retries and returns the success summary.
97
+ Raises `TaskFailed` when the maximum attempts are reached.
98
+
99
+ - `check` (str | None | False): custom check prompt, default checker, or `False` to skip.
100
+ - `n` (int): maximum number of retries after a failed check.
101
+
102
+ ### `task_result(prompt, check=None, n=10, cwd=None, yolo=False, flags=None) -> TaskResult`
103
+
104
+ Runs a task with checker-driven retries and returns a `TaskResult` without
105
+ raising `TaskFailed`.
106
+
107
+ ### `Task(prompt, max_attempts=10, cwd=None, yolo=False, thread_id=None, flags=None)`
108
+
109
+ Runs a Codex task with checker-driven retries. Subclass it and implement
110
+ `check()` to return an error string when the task is incomplete, or return
111
+ `None`/`""` when the task passes.
112
+
113
+ - `__call__() -> TaskResult`: run the task.
114
+ - `set_up()`: optional setup hook.
115
+ - `tear_down()`: optional cleanup hook.
116
+ - `check() -> str | None`: return an error description or `None`/`""`.
117
+ - `on_success(result)`: optional success hook.
118
+ - `on_failure(result)`: optional failure hook.
119
+
120
+ ### `TaskResult(success, summary, attempts, errors, thread_id)`
121
+
122
+ Simple result object returned by `Task.__call__`.
123
+
124
+ - `success` (bool): whether the task completed successfully.
125
+ - `summary` (str): agent summary of what happened.
126
+ - `attempts` (int): how many attempts were used.
127
+ - `errors` (str | None): last checker error, if any.
128
+ - `thread_id` (str | None): Codex thread id for the session.
129
+
130
+ ### `TaskFailed`
131
+
132
+ Exception raised by `task()` when retries are exhausted.
133
+
134
+ - `summary` (str): failure summary text.
135
+ - `attempts` (int | None): attempts made when the task failed.
136
+ - `errors` (str | None): last checker error, if any.
137
+
138
+ ## Behavior notes
139
+
140
+ - Uses `codex exec --json` and parses JSONL events for `agent_message` items.
141
+ - Automatically passes `--skip-git-repo-check` so it can run outside a git repo.
142
+ - Passes `--full-auto` unless `--yolo` is enabled.
143
+ - Passes `--yolo` when enabled (use with care).
144
+ - Raises `RuntimeError` if Codex exits non-zero or returns no agent message.
145
+
146
+ ## Configuration
147
+
148
+ Set `CODEX_BIN` to point at a non-default Codex binary:
149
+
150
+ ```bash
151
+ export CODEX_BIN=/path/to/codex
152
+ ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codexapi"
7
- version = "0.1.2"
7
+ version = "0.1.7"
8
8
  description = "Minimal Python API for running the Codex CLI."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -17,6 +17,9 @@ classifiers = [
17
17
 
18
18
  dependencies = []
19
19
 
20
+ [project.scripts]
21
+ codexapi = "codexapi.cli:main"
22
+
20
23
  [tool.setuptools]
21
24
  package-dir = {"" = "src"}
22
25
 
@@ -0,0 +1,15 @@
1
+ """Minimal Python API for running the Codex CLI."""
2
+
3
+ from .agent import Agent, agent
4
+ from .task import Task, TaskFailed, TaskResult, task, task_result
5
+
6
+ __all__ = [
7
+ "Agent",
8
+ "Task",
9
+ "TaskFailed",
10
+ "TaskResult",
11
+ "agent",
12
+ "task",
13
+ "task_result",
14
+ ]
15
+ __version__ = "0.1.7"
@@ -0,0 +1,5 @@
1
+ from .cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ main()
@@ -1,46 +1,26 @@
1
1
  """Codex CLI wrapper used by the codexapi public interface."""
2
2
 
3
- from __future__ import annotations
4
-
5
3
  import json
6
4
  import os
7
5
  import shlex
8
6
  import subprocess
9
- from typing import Optional, Tuple, Union
10
-
11
- Pathish = Union[str, os.PathLike]
12
7
 
13
8
  _CODEX_BIN = os.environ.get("CODEX_BIN", "codex")
14
9
 
15
10
 
16
- def agent(
17
- prompt: str,
18
- cwd: Optional[Pathish] = None,
19
- *,
20
- yolo: bool = False,
21
- agent: str = "codex",
22
- flags: Optional[str] = None,
23
- ) -> str:
11
+ def agent(prompt, cwd=None, yolo=False, flags=None):
24
12
  """Run a single Codex turn and return only the agent's message.
25
13
 
26
14
  Args:
27
15
  prompt: The user prompt to send to Codex.
28
16
  cwd: Optional working directory for the Codex session.
29
17
  yolo: Whether to pass --yolo to Codex.
30
- agent: Agent backend to use (only "codex" is supported).
31
18
  flags: Additional raw CLI flags to pass to Codex.
32
19
 
33
20
  Returns:
34
21
  The agent's visible response text with reasoning traces removed.
35
22
  """
36
- _require_codex_agent(agent)
37
- message, _thread_id = _run_codex(
38
- prompt=prompt,
39
- cwd=cwd,
40
- thread_id=None,
41
- yolo=yolo,
42
- flags=flags,
43
- )
23
+ message, _thread_id = _run_codex(prompt, cwd, None, yolo, flags)
44
24
  return message
45
25
 
46
26
 
@@ -55,13 +35,11 @@ class Agent:
55
35
 
56
36
  def __init__(
57
37
  self,
58
- cwd: Optional[Pathish] = None,
59
- *,
60
- yolo: bool = False,
61
- agent: str = "codex",
62
- trace_id: Optional[str] = None,
63
- flags: Optional[str] = None,
64
- ) -> None:
38
+ cwd=None,
39
+ yolo=False,
40
+ thread_id=None,
41
+ flags=None,
42
+ ):
65
43
  """Create a new session wrapper.
66
44
 
67
45
  Args:
@@ -71,41 +49,28 @@ class Agent:
71
49
  trace_id: Optional Codex thread id to resume from the first call.
72
50
  flags: Additional raw CLI flags to pass to Codex.
73
51
  """
74
- _require_codex_agent(agent)
75
- self._cwd = cwd
52
+ self.cwd = cwd
76
53
  self._yolo = yolo
77
54
  self._flags = flags
78
- self._thread_id: Optional[str] = trace_id
55
+ self.thread_id = thread_id
79
56
 
80
- def __call__(self, prompt: str) -> str:
57
+ def __call__(self, prompt):
81
58
  """Send a prompt to Codex and return only the agent's message."""
82
59
  message, thread_id = _run_codex(
83
- prompt=prompt,
84
- cwd=self._cwd,
85
- thread_id=self._thread_id,
86
- yolo=self._yolo,
87
- flags=self._flags,
60
+ prompt,
61
+ self.cwd,
62
+ self.thread_id,
63
+ self._yolo,
64
+ self._flags,
88
65
  )
89
66
  if thread_id:
90
- self._thread_id = thread_id
67
+ self.thread_id = thread_id
91
68
  return message
92
69
 
93
- @property
94
- def thread_id(self) -> Optional[str]:
95
- """Return the current Codex session id, if any."""
96
- return self._thread_id
97
70
 
98
-
99
- def _run_codex(
100
- *,
101
- prompt: str,
102
- cwd: Optional[Pathish],
103
- thread_id: Optional[str],
104
- yolo: bool,
105
- flags: Optional[str],
106
- ) -> Tuple[str, Optional[str]]:
71
+ def _run_codex(prompt, cwd, thread_id, yolo, flags):
107
72
  """Invoke the Codex CLI and return the message plus thread id (if any)."""
108
- cmd = [
73
+ command = [
109
74
  _CODEX_BIN,
110
75
  "exec",
111
76
  "--json",
@@ -114,22 +79,24 @@ def _run_codex(
114
79
  "--skip-git-repo-check",
115
80
  ]
116
81
  if yolo:
117
- cmd.append("--yolo")
82
+ command.append("--yolo")
83
+ else:
84
+ command.append("--full-auto")
118
85
  if flags:
119
- cmd.extend(shlex.split(flags))
120
- if cwd is not None:
121
- cmd.extend(["--cd", os.fspath(cwd)])
86
+ command.extend(shlex.split(flags))
87
+ if cwd:
88
+ command.extend(["--cd", os.fspath(cwd)])
122
89
  if thread_id:
123
- cmd.extend(["resume", thread_id, "-"])
90
+ command.extend(["resume", thread_id, "-"])
124
91
  else:
125
- cmd.append("-")
92
+ command.append("-")
126
93
 
127
94
  result = subprocess.run(
128
- cmd,
95
+ command,
129
96
  input=prompt,
130
97
  text=True,
131
98
  capture_output=True,
132
- cwd=os.fspath(cwd) if cwd is not None else None,
99
+ cwd=os.fspath(cwd) if cwd else None,
133
100
  )
134
101
  if result.returncode != 0:
135
102
  stderr = result.stderr.strip()
@@ -141,16 +108,11 @@ def _run_codex(
141
108
  return _parse_jsonl(result.stdout)
142
109
 
143
110
 
144
- def _require_codex_agent(agent_name: str) -> None:
145
- if agent_name != "codex":
146
- raise ValueError('Only agent="codex" is supported right now.')
147
-
148
-
149
- def _parse_jsonl(output: str) -> Tuple[str, Optional[str]]:
111
+ def _parse_jsonl(output):
150
112
  """Extract agent messages and the latest thread id from Codex JSONL output."""
151
- thread_id: Optional[str] = None
152
- messages: list[str] = []
153
- raw_lines: list[str] = []
113
+ thread_id = None
114
+ messages = []
115
+ raw_lines = []
154
116
 
155
117
  for line in output.splitlines():
156
118
  line = line.strip()