coding-cli-runtime 0.1.0__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.
@@ -0,0 +1,129 @@
1
+ """Session log discovery helpers shared across provider wrappers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import re
8
+ from pathlib import Path
9
+
10
+
11
+ def normalize_path_str(path_str: str) -> str:
12
+ try:
13
+ return str(Path(path_str).resolve())
14
+ except OSError:
15
+ return os.path.normpath(path_str)
16
+
17
+
18
+ def codex_session_roots() -> list[Path]:
19
+ base = Path.home() / ".codex"
20
+ return [base / "sessions", base / "archived_sessions"]
21
+
22
+
23
+ def claude_project_roots() -> list[Path]:
24
+ return [Path.home() / ".claude" / "projects"]
25
+
26
+
27
+ def claude_project_key(workdir: Path) -> str:
28
+ normalized = workdir.as_posix().lstrip("/")
29
+ if not normalized:
30
+ return "-"
31
+ slug = re.sub(r"[^A-Za-z0-9-]", "-", normalized)
32
+ return f"-{slug}"
33
+
34
+
35
+ def codex_session_matches(log_path: Path, workdir: str, *, max_lines: int = 50) -> bool:
36
+ try:
37
+ with log_path.open("r", encoding="utf-8") as handle:
38
+ for _ in range(max_lines):
39
+ line = handle.readline()
40
+ if not line:
41
+ break
42
+ try:
43
+ record = json.loads(line)
44
+ except json.JSONDecodeError:
45
+ continue
46
+ payload = record.get("payload")
47
+ if not isinstance(payload, dict):
48
+ continue
49
+ cwd = payload.get("cwd")
50
+ if isinstance(cwd, str) and normalize_path_str(cwd) == workdir:
51
+ return True
52
+ except OSError:
53
+ return False
54
+ return False
55
+
56
+
57
+ def find_codex_session(
58
+ workdir: str,
59
+ since_ts: float,
60
+ *,
61
+ session_roots: list[Path] | None = None,
62
+ max_scan: int = 200,
63
+ ) -> Path | None:
64
+ def safe_mtime(path: Path) -> float | None:
65
+ try:
66
+ return path.stat().st_mtime
67
+ except OSError:
68
+ return None
69
+
70
+ roots = session_roots if session_roots is not None else codex_session_roots()
71
+ candidates: list[tuple[float, Path]] = []
72
+ for root in roots:
73
+ if not root.exists():
74
+ continue
75
+ try:
76
+ for path in root.rglob("*.jsonl"):
77
+ mtime = safe_mtime(path)
78
+ if mtime is None:
79
+ continue
80
+ if mtime >= since_ts - 15:
81
+ candidates.append((mtime, path))
82
+ except (OSError, RuntimeError):
83
+ continue
84
+ if not candidates:
85
+ return None
86
+
87
+ candidates.sort(key=lambda item: item[0], reverse=True)
88
+ for _, path in candidates[:max_scan]:
89
+ if codex_session_matches(path, workdir):
90
+ return path
91
+ return None
92
+
93
+
94
+ def find_claude_session(
95
+ workdir: str,
96
+ since_ts: float,
97
+ *,
98
+ project_roots: list[Path] | None = None,
99
+ max_scan: int = 200,
100
+ ) -> Path | None:
101
+ def safe_mtime(path: Path) -> float | None:
102
+ try:
103
+ return path.stat().st_mtime
104
+ except OSError:
105
+ return None
106
+
107
+ workdir_path = Path(normalize_path_str(workdir))
108
+ project_key = claude_project_key(workdir_path)
109
+ roots = project_roots if project_roots is not None else claude_project_roots()
110
+ candidates: list[tuple[float, Path]] = []
111
+ for root in roots:
112
+ project_dir = root / project_key
113
+ if not project_dir.exists():
114
+ continue
115
+ try:
116
+ for path in project_dir.glob("*.jsonl"):
117
+ mtime = safe_mtime(path)
118
+ if mtime is None:
119
+ continue
120
+ if mtime >= since_ts - 10:
121
+ candidates.append((mtime, path))
122
+ except (OSError, RuntimeError):
123
+ continue
124
+
125
+ if not candidates:
126
+ return None
127
+
128
+ candidates.sort(key=lambda item: item[0], reverse=True)
129
+ return candidates[:max_scan][0][1]
@@ -0,0 +1,346 @@
1
+ """Async subprocess execution wrapper with normalized result shape."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import subprocess
7
+ import threading
8
+ import time
9
+ from pathlib import Path
10
+ from typing import BinaryIO
11
+
12
+ from .contracts import CliRunRequest, CliRunResult, ErrorCode
13
+
14
+
15
+ def _decode_output(payload: bytes | str | None) -> str:
16
+ if payload is None:
17
+ return ""
18
+ if isinstance(payload, str):
19
+ return payload
20
+ return payload.decode("utf-8", errors="replace")
21
+
22
+
23
+ def _open_stream_handle(path: Path | None) -> BinaryIO | None:
24
+ if path is None:
25
+ return None
26
+ path.parent.mkdir(parents=True, exist_ok=True)
27
+ return path.open("wb")
28
+
29
+
30
+ def _stream_pipe(
31
+ pipe: BinaryIO | None,
32
+ chunks: list[bytes],
33
+ stream_handle: BinaryIO | None,
34
+ ) -> None:
35
+ if pipe is None:
36
+ return
37
+ while True:
38
+ chunk = pipe.read(4096)
39
+ if not chunk:
40
+ break
41
+ chunks.append(chunk)
42
+ if stream_handle is not None:
43
+ stream_handle.write(chunk)
44
+ stream_handle.flush()
45
+
46
+
47
+ async def run_cli_command(request: CliRunRequest) -> CliRunResult:
48
+ started = time.monotonic()
49
+ try:
50
+ proc = await asyncio.create_subprocess_exec(
51
+ *request.cmd_parts,
52
+ stdout=asyncio.subprocess.PIPE,
53
+ stderr=asyncio.subprocess.PIPE,
54
+ stdin=(
55
+ asyncio.subprocess.PIPE
56
+ if request.stdin_text is not None
57
+ else asyncio.subprocess.DEVNULL
58
+ ),
59
+ cwd=str(request.cwd),
60
+ env=request.env,
61
+ )
62
+ except Exception as exc:
63
+ duration = time.monotonic() - started
64
+ return CliRunResult(
65
+ command=list(request.cmd_parts),
66
+ returncode=None,
67
+ stdout_text="",
68
+ stderr_text="",
69
+ duration_seconds=duration,
70
+ timed_out=False,
71
+ error_code=ErrorCode.SPAWN_FAILED,
72
+ error_message=str(exc),
73
+ )
74
+
75
+ stdin_bytes = request.stdin_text.encode("utf-8") if request.stdin_text is not None else None
76
+ try:
77
+ if request.timeout_seconds and request.timeout_seconds > 0:
78
+ stdout, stderr = await asyncio.wait_for(
79
+ proc.communicate(input=stdin_bytes), timeout=request.timeout_seconds
80
+ )
81
+ else:
82
+ stdout, stderr = await proc.communicate(input=stdin_bytes)
83
+ except TimeoutError:
84
+ try:
85
+ proc.kill()
86
+ except ProcessLookupError:
87
+ pass
88
+ await proc.wait()
89
+ duration = time.monotonic() - started
90
+ return CliRunResult(
91
+ command=list(request.cmd_parts),
92
+ returncode=None,
93
+ stdout_text="",
94
+ stderr_text="",
95
+ duration_seconds=duration,
96
+ timed_out=True,
97
+ error_code=ErrorCode.TIMED_OUT,
98
+ error_message=f"process timed out after {request.timeout_seconds}s",
99
+ )
100
+
101
+ duration = time.monotonic() - started
102
+ stdout_text = stdout.decode("utf-8", errors="replace")
103
+ stderr_text = stderr.decode("utf-8", errors="replace")
104
+
105
+ if proc.returncode != 0:
106
+ return CliRunResult(
107
+ command=list(request.cmd_parts),
108
+ returncode=proc.returncode,
109
+ stdout_text=stdout_text,
110
+ stderr_text=stderr_text,
111
+ duration_seconds=duration,
112
+ timed_out=False,
113
+ error_code=ErrorCode.NON_ZERO_EXIT,
114
+ error_message=f"process exited with code {proc.returncode}",
115
+ )
116
+
117
+ return CliRunResult(
118
+ command=list(request.cmd_parts),
119
+ returncode=proc.returncode,
120
+ stdout_text=stdout_text,
121
+ stderr_text=stderr_text,
122
+ duration_seconds=duration,
123
+ timed_out=False,
124
+ error_code=ErrorCode.NONE,
125
+ error_message=None,
126
+ )
127
+
128
+
129
+ def run_cli_command_sync(request: CliRunRequest) -> CliRunResult:
130
+ started = time.monotonic()
131
+ stdin_bytes = request.stdin_text.encode("utf-8") if request.stdin_text is not None else None
132
+ timeout = (
133
+ request.timeout_seconds if request.timeout_seconds and request.timeout_seconds > 0 else None
134
+ )
135
+ stream_requested = (
136
+ request.stdout_stream_path is not None or request.stderr_stream_path is not None
137
+ )
138
+ if stream_requested:
139
+ return _run_cli_command_sync_streaming(
140
+ request=request,
141
+ started=started,
142
+ stdin_bytes=stdin_bytes,
143
+ timeout=timeout,
144
+ )
145
+
146
+ try:
147
+ run_kwargs = {
148
+ "cwd": str(request.cwd),
149
+ "env": request.env,
150
+ "input": stdin_bytes,
151
+ "capture_output": True,
152
+ "timeout": timeout,
153
+ "check": False,
154
+ }
155
+ if request.stdin_text is None:
156
+ run_kwargs["stdin"] = subprocess.DEVNULL
157
+ completed = subprocess.run(
158
+ request.cmd_parts,
159
+ **run_kwargs, # type: ignore[call-overload]
160
+ )
161
+ duration = time.monotonic() - started
162
+ except subprocess.TimeoutExpired as exc:
163
+ duration = time.monotonic() - started
164
+ return CliRunResult(
165
+ command=list(request.cmd_parts),
166
+ returncode=None,
167
+ stdout_text=_decode_output(exc.stdout),
168
+ stderr_text=_decode_output(exc.stderr),
169
+ duration_seconds=duration,
170
+ timed_out=True,
171
+ error_code=ErrorCode.TIMED_OUT,
172
+ error_message=f"process timed out after {request.timeout_seconds}s",
173
+ )
174
+ except Exception as exc:
175
+ duration = time.monotonic() - started
176
+ return CliRunResult(
177
+ command=list(request.cmd_parts),
178
+ returncode=None,
179
+ stdout_text="",
180
+ stderr_text="",
181
+ duration_seconds=duration,
182
+ timed_out=False,
183
+ error_code=ErrorCode.SPAWN_FAILED,
184
+ error_message=str(exc),
185
+ )
186
+
187
+ stdout_text = _decode_output(completed.stdout)
188
+ stderr_text = _decode_output(completed.stderr)
189
+ if completed.returncode != 0:
190
+ return CliRunResult(
191
+ command=list(request.cmd_parts),
192
+ returncode=completed.returncode,
193
+ stdout_text=stdout_text,
194
+ stderr_text=stderr_text,
195
+ duration_seconds=duration,
196
+ timed_out=False,
197
+ error_code=ErrorCode.NON_ZERO_EXIT,
198
+ error_message=f"process exited with code {completed.returncode}",
199
+ )
200
+
201
+ return CliRunResult(
202
+ command=list(request.cmd_parts),
203
+ returncode=completed.returncode,
204
+ stdout_text=stdout_text,
205
+ stderr_text=stderr_text,
206
+ duration_seconds=duration,
207
+ timed_out=False,
208
+ error_code=ErrorCode.NONE,
209
+ error_message=None,
210
+ )
211
+
212
+
213
+ def _run_cli_command_sync_streaming(
214
+ *,
215
+ request: CliRunRequest,
216
+ started: float,
217
+ stdin_bytes: bytes | None,
218
+ timeout: float | None,
219
+ ) -> CliRunResult:
220
+ stdout_chunks: list[bytes] = []
221
+ stderr_chunks: list[bytes] = []
222
+ stdout_stream = _open_stream_handle(request.stdout_stream_path)
223
+ stderr_stream = _open_stream_handle(request.stderr_stream_path)
224
+ proc: subprocess.Popen[bytes] | None = None
225
+ stdin_thread: threading.Thread | None = None
226
+ stdout_thread: threading.Thread | None = None
227
+ stderr_thread: threading.Thread | None = None
228
+ try:
229
+ proc = subprocess.Popen(
230
+ request.cmd_parts,
231
+ cwd=str(request.cwd),
232
+ env=request.env,
233
+ stdin=(subprocess.PIPE if request.stdin_text is not None else subprocess.DEVNULL),
234
+ stdout=subprocess.PIPE,
235
+ stderr=subprocess.PIPE,
236
+ )
237
+
238
+ def _write_stdin() -> None:
239
+ if proc is None or proc.stdin is None:
240
+ return
241
+ try:
242
+ if stdin_bytes is not None:
243
+ proc.stdin.write(stdin_bytes)
244
+ proc.stdin.flush()
245
+ except (BrokenPipeError, OSError):
246
+ pass
247
+ finally:
248
+ try:
249
+ proc.stdin.close()
250
+ except OSError:
251
+ pass
252
+
253
+ if request.stdin_text is not None:
254
+ stdin_thread = threading.Thread(target=_write_stdin, daemon=True)
255
+ stdin_thread.start()
256
+ stdout_thread = threading.Thread(
257
+ target=_stream_pipe,
258
+ args=(proc.stdout, stdout_chunks, stdout_stream),
259
+ daemon=True,
260
+ )
261
+ stderr_thread = threading.Thread(
262
+ target=_stream_pipe,
263
+ args=(proc.stderr, stderr_chunks, stderr_stream),
264
+ daemon=True,
265
+ )
266
+ stdout_thread.start()
267
+ stderr_thread.start()
268
+
269
+ timed_out = False
270
+ try:
271
+ proc.wait(timeout=timeout)
272
+ except subprocess.TimeoutExpired:
273
+ timed_out = True
274
+ try:
275
+ proc.kill()
276
+ except ProcessLookupError:
277
+ pass
278
+ proc.wait()
279
+
280
+ if stdin_thread is not None:
281
+ stdin_thread.join()
282
+ if stdout_thread is not None:
283
+ stdout_thread.join()
284
+ if stderr_thread is not None:
285
+ stderr_thread.join()
286
+
287
+ duration = time.monotonic() - started
288
+ stdout_text = _decode_output(b"".join(stdout_chunks))
289
+ stderr_text = _decode_output(b"".join(stderr_chunks))
290
+ if timed_out:
291
+ return CliRunResult(
292
+ command=list(request.cmd_parts),
293
+ returncode=None,
294
+ stdout_text=stdout_text,
295
+ stderr_text=stderr_text,
296
+ duration_seconds=duration,
297
+ timed_out=True,
298
+ error_code=ErrorCode.TIMED_OUT,
299
+ error_message=f"process timed out after {request.timeout_seconds}s",
300
+ )
301
+ if proc.returncode != 0:
302
+ return CliRunResult(
303
+ command=list(request.cmd_parts),
304
+ returncode=proc.returncode,
305
+ stdout_text=stdout_text,
306
+ stderr_text=stderr_text,
307
+ duration_seconds=duration,
308
+ timed_out=False,
309
+ error_code=ErrorCode.NON_ZERO_EXIT,
310
+ error_message=f"process exited with code {proc.returncode}",
311
+ )
312
+ return CliRunResult(
313
+ command=list(request.cmd_parts),
314
+ returncode=proc.returncode,
315
+ stdout_text=stdout_text,
316
+ stderr_text=stderr_text,
317
+ duration_seconds=duration,
318
+ timed_out=False,
319
+ error_code=ErrorCode.NONE,
320
+ error_message=None,
321
+ )
322
+ except Exception as exc:
323
+ duration = time.monotonic() - started
324
+ return CliRunResult(
325
+ command=list(request.cmd_parts),
326
+ returncode=None,
327
+ stdout_text=_decode_output(b"".join(stdout_chunks)),
328
+ stderr_text=_decode_output(b"".join(stderr_chunks)),
329
+ duration_seconds=duration,
330
+ timed_out=False,
331
+ error_code=ErrorCode.SPAWN_FAILED,
332
+ error_message=str(exc),
333
+ )
334
+ finally:
335
+ if proc is not None:
336
+ for pipe in (proc.stdin, proc.stdout, proc.stderr):
337
+ if pipe is None:
338
+ continue
339
+ try:
340
+ pipe.close()
341
+ except OSError:
342
+ pass
343
+ if stdout_stream is not None:
344
+ stdout_stream.close()
345
+ if stderr_stream is not None:
346
+ stderr_stream.close()
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.4
2
+ Name: coding-cli-runtime
3
+ Version: 0.1.0
4
+ Summary: Reusable CLI runtime primitives for provider-backed automation workflows
5
+ Author-email: LLM Eval maintainers <llm-eval-maintainers@users.noreply.github.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/pj-ms/llm-eval/tree/main/packages/coding-cli-runtime
8
+ Project-URL: Repository, https://github.com/pj-ms/llm-eval
9
+ Project-URL: Issues, https://github.com/pj-ms/llm-eval/issues
10
+ Project-URL: Changelog, https://github.com/pj-ms/llm-eval/blob/main/packages/coding-cli-runtime/CHANGELOG.md
11
+ Keywords: cli,runtime,llm,automation,schema-validation
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Dynamic: license-file
26
+
27
+ # coding-cli-runtime
28
+
29
+ [![PyPI](https://img.shields.io/pypi/v/coding-cli-runtime)](https://pypi.org/project/coding-cli-runtime/)
30
+ [![Python](https://img.shields.io/pypi/pyversions/coding-cli-runtime)](https://pypi.org/project/coding-cli-runtime/)
31
+ [![Build](https://github.com/pj-ms/llm-eval/actions/workflows/ci.yml/badge.svg)](https://github.com/pj-ms/llm-eval/actions/workflows/ci.yml)
32
+ [![License](https://img.shields.io/pypi/l/coding-cli-runtime)](LICENSE)
33
+
34
+ A Python library for orchestrating LLM coding agent CLIs — [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex](https://github.com/openai/codex), [Gemini CLI](https://github.com/google-gemini/gemini-cli), and [GitHub Copilot](https://docs.github.com/en/copilot).
35
+
36
+ These CLIs each have different invocation patterns, output formats, error
37
+ shapes, and timeout behaviors. This library normalizes all of that behind
38
+ a common `CliRunRequest` → `CliRunResult` contract, so your automation
39
+ code doesn't need provider-specific subprocess handling.
40
+
41
+ **What it does (and why not just `subprocess.run`):**
42
+
43
+ - Unified request/result types across all four CLIs
44
+ - Timeout enforcement with graceful process termination
45
+ - Provider-aware failure classification (retryable vs fatal)
46
+ - Built-in model catalog with defaults, reasoning levels, and capabilities
47
+ - Interactive session management for long-running generation tasks
48
+ - Zero runtime dependencies
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pip install coding-cli-runtime
54
+ ```
55
+
56
+ Requires Python 3.10+.
57
+
58
+ ## Examples
59
+
60
+ ### Execute a provider CLI
61
+
62
+ ```python
63
+ import asyncio
64
+ from pathlib import Path
65
+ from coding_cli_runtime import CliRunRequest, run_cli_command
66
+
67
+ request = CliRunRequest(
68
+ cmd_parts=("codex", "--model", "o4-mini", "--quiet", "exec", "fix the tests"),
69
+ cwd=Path("/tmp/my-project"),
70
+ timeout_seconds=120,
71
+ )
72
+ result = asyncio.run(run_cli_command(request))
73
+
74
+ print(result.returncode) # 0
75
+ print(result.error_code) # "none"
76
+ print(result.duration_seconds) # 14.2
77
+ print(result.stdout_text[:200])
78
+ ```
79
+
80
+ Swap `codex` for `claude`, `gemini`, or `copilot` — the request/result
81
+ shape stays the same. A synchronous variant `run_cli_command_sync` is also
82
+ available.
83
+
84
+ ### Pick a model from the provider catalog
85
+
86
+ ```python
87
+ from coding_cli_runtime import get_provider_spec
88
+
89
+ codex = get_provider_spec("codex")
90
+ print(codex.default_model) # "gpt-5.3-codex"
91
+ print(codex.model_source) # "codex_cli_cache", "override", or "code"
92
+
93
+ for model in codex.models:
94
+ print(f" {model.name}: {model.description}")
95
+ ```
96
+
97
+ The catalog covers all four providers — each with model names, reasoning
98
+ levels, default settings, and visibility flags.
99
+
100
+ Model lists are resolved with a three-tier fallback:
101
+
102
+ 1. **User override** — drop a JSON file at
103
+ `~/.config/coding-cli-runtime/providers/<provider>.json` to use your own
104
+ model list immediately, without waiting for a package update.
105
+ 2. **Live CLI cache** — for Codex, the library reads
106
+ `~/.codex/models_cache.json` (auto-refreshed by the Codex CLI) when
107
+ present. Other providers fall through because their CLIs don't expose a
108
+ machine-readable model list.
109
+ 3. **Hardcoded fallback** — the model list shipped with the package.
110
+
111
+ Override file format:
112
+
113
+ ```json
114
+ {
115
+ "default_model": "claude-sonnet-4-7",
116
+ "models": [
117
+ "claude-sonnet-4-7",
118
+ {
119
+ "name": "claude-opus-5",
120
+ "description": "Latest opus model",
121
+ "controls": [
122
+ { "name": "effort", "kind": "choice", "choices": ["low", "high"], "default": "low" }
123
+ ]
124
+ }
125
+ ]
126
+ }
127
+ ```
128
+
129
+ Set `CODING_CLI_RUNTIME_CONFIG_DIR` to change the config directory
130
+ (default: `~/.config/coding-cli-runtime`).
131
+
132
+ ### Decide whether to retry a failed run
133
+
134
+ ```python
135
+ from coding_cli_runtime import classify_provider_failure
136
+
137
+ classification = classify_provider_failure(
138
+ provider="gemini",
139
+ stderr_text="429 Resource exhausted: rate limit exceeded",
140
+ )
141
+
142
+ if classification.retryable:
143
+ print(f"Retryable ({classification.category}) — will retry")
144
+ else:
145
+ print(f"Fatal ({classification.category}) — giving up")
146
+ ```
147
+
148
+ Works for all four providers. Recognizes auth failures, rate limits,
149
+ network transients, and other provider-specific error patterns.
150
+
151
+ ## Key types
152
+
153
+ | Type | Purpose |
154
+ |------|---------|
155
+ | `CliRunRequest` | Command spec: cmd, cwd, env, timeout, stream paths |
156
+ | `CliRunResult` | Result: returncode, stdout/stderr, duration, error code |
157
+ | `ErrorCode` | `none` · `spawn_failed` · `timed_out` · `non_zero_exit` |
158
+ | `ProviderSpec` | Provider catalog entry with models, controls, defaults |
159
+ | `FailureClassification` | Classified error with retryable flag and category |
160
+
161
+ `run_interactive_session()` manages long-running CLI processes with
162
+ timeout enforcement, process-group cleanup, transcript mirroring, and
163
+ automatic retries. Only `cmd_parts`, `cwd`, `stdin_text`, and `logger` are
164
+ required — observability labels like `job_name` and `phase_tag` default to
165
+ sensible values so external callers don't need to invent them.
166
+
167
+ ## Prerequisites
168
+
169
+ This package does **not** bundle any CLI binaries or credentials. You must
170
+ install and authenticate the relevant provider CLI yourself before using the
171
+ execution helpers.
172
+
173
+ ## Status
174
+
175
+ Pre-1.0. API may change between minor versions.
176
+
177
+ ## License
178
+
179
+ MIT
@@ -0,0 +1,24 @@
1
+ coding_cli_runtime/__init__.py,sha256=edCw6YCLaukPFtSjw-EEnLiPiUYnwyKfGl0UhlGxx2M,2979
2
+ coding_cli_runtime/auth.py,sha256=LVQwdODVSLv-MkDBCeB_0J1R8uE3DsL9mYeF4ljil0Y,1644
3
+ coding_cli_runtime/codex_cli.py,sha256=HFsA7Bd1vW9TWUZSsMANezVGbwTKxuuQZihxu9Hf9U8,2988
4
+ coding_cli_runtime/contracts.py,sha256=teYMPDYCjL6HRwRBucJKetfZKlRnxpG82BrC1Y1OMNg,1764
5
+ coding_cli_runtime/copilot_reasoning_baseline.json,sha256=hEIsqm03-D8T9Snn_FvbC2RD367fXGziyTK0Ajpxrmk,1649
6
+ coding_cli_runtime/copilot_reasoning_logs.py,sha256=S2GD0zGgwVXAPe-DyJPKMR5j-EgOGlANnIt315mIWuo,2327
7
+ coding_cli_runtime/failure_classification.py,sha256=fjGOjtQaBh6Y13gEIS7ystmadcrFKsLAN-fccBuerBs,6027
8
+ coding_cli_runtime/json_io.py,sha256=1RseVXV-uPWRm_-pGIUTAvhALnMAEivCX46zvlVpri0,2665
9
+ coding_cli_runtime/provider_controls.py,sha256=2rl1XxGYFODu6LRx3bLhptgN5M_T3klx2lqT_gAAkb0,3328
10
+ coding_cli_runtime/provider_specs.py,sha256=n9cptkPVI5sIBVuJlqE8nuTU7SyJwG8rkUF2enM_KZc,26777
11
+ coding_cli_runtime/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
12
+ coding_cli_runtime/reasoning.py,sha256=Ggyw1K9Ry4bytzeS-Jy8jmNHVJR891zH_4jRpYAswoA,3216
13
+ coding_cli_runtime/redaction.py,sha256=PALvJoNt7r0E_Dd3N02tCV9RI_0nPfSgoVAeaWxeLAY,559
14
+ coding_cli_runtime/schema_validation.py,sha256=WZvl2_LkAnuxNMmpS2-vjtLqny034G9xT7wh1BZ1gwM,3929
15
+ coding_cli_runtime/session_execution.py,sha256=U9oRrz2ORuZJzUf4WK2BS6ubCSLOHe94izg6xmn6d3E,20714
16
+ coding_cli_runtime/session_logs.py,sha256=B3B7MB9oe829cRyLyT4GyYySTXDjHOG3TG-WqbNfRzE,3750
17
+ coding_cli_runtime/subprocess_runner.py,sha256=WqYMI6ALWFhEUySycfHXEtXuCX3m5x7s1n3Bd0TyPm0,11419
18
+ coding_cli_runtime/schemas/normalized_run_result.v1.json,sha256=ogVKJbDFAd9dJklmp8SUkdR9L5EX1rdHGj5leJJHXGs,1110
19
+ coding_cli_runtime/schemas/reasoning_metadata.v1.json,sha256=nQWhqp9-dlzJM18OARDUwAyaA-3-I8rETZYvkgTAnOc,467
20
+ coding_cli_runtime-0.1.0.dist-info/licenses/LICENSE,sha256=hVIuaMVAgQkhTh44et0cpDtN3kGOZnKQ2bY1rJJw-MI,1078
21
+ coding_cli_runtime-0.1.0.dist-info/METADATA,sha256=QdR4cw7AAY-6AhqVwjvwEHMbCXYoC3dS-ovuuot_FB0,6465
22
+ coding_cli_runtime-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
23
+ coding_cli_runtime-0.1.0.dist-info/top_level.txt,sha256=-tzjii3Qf_GTevxT5M46tITBY02R-K8Ew04hJRHOB2Y,19
24
+ coding_cli_runtime-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+