coding-cli-runtime 0.2.0__py3-none-any.whl → 0.4.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.
- coding_cli_runtime/__init__.py +31 -3
- coding_cli_runtime/codex_cli.py +10 -8
- coding_cli_runtime/headless.py +124 -0
- coding_cli_runtime/provider_contracts.py +220 -39
- coding_cli_runtime/session_logs.py +56 -0
- {coding_cli_runtime-0.2.0.dist-info → coding_cli_runtime-0.4.0.dist-info}/METADATA +163 -16
- {coding_cli_runtime-0.2.0.dist-info → coding_cli_runtime-0.4.0.dist-info}/RECORD +10 -9
- {coding_cli_runtime-0.2.0.dist-info → coding_cli_runtime-0.4.0.dist-info}/WHEEL +0 -0
- {coding_cli_runtime-0.2.0.dist-info → coding_cli_runtime-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {coding_cli_runtime-0.2.0.dist-info → coding_cli_runtime-0.4.0.dist-info}/top_level.txt +0 -0
coding_cli_runtime/__init__.py
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
__version__ = "0.2.0"
|
|
6
|
-
|
|
7
5
|
from .auth import AuthResolution, resolve_auth
|
|
8
6
|
from .codex_cli import CodexExecSpec, build_codex_exec_spec
|
|
9
7
|
from .contracts import (
|
|
@@ -15,19 +13,34 @@ from .contracts import (
|
|
|
15
13
|
ErrorCode,
|
|
16
14
|
)
|
|
17
15
|
from .failure_classification import FailureClassification, classify_provider_failure
|
|
16
|
+
from .headless import (
|
|
17
|
+
build_claude_headless_core,
|
|
18
|
+
build_codex_headless_core,
|
|
19
|
+
build_copilot_headless_core,
|
|
20
|
+
build_gemini_headless_core,
|
|
21
|
+
)
|
|
18
22
|
from .provider_contracts import (
|
|
19
23
|
ApprovalContract,
|
|
20
24
|
AuthContract,
|
|
25
|
+
DiagnosticsContract,
|
|
21
26
|
HeadlessContract,
|
|
27
|
+
IoContract,
|
|
28
|
+
OutputContract,
|
|
22
29
|
PathContract,
|
|
23
30
|
PromptPayload,
|
|
24
31
|
PromptTransport,
|
|
25
32
|
ProviderContract,
|
|
26
33
|
SandboxContract,
|
|
34
|
+
SessionDiscoveryContract,
|
|
35
|
+
WorkspaceEnvValueSource,
|
|
36
|
+
WorkspaceEnvVar,
|
|
27
37
|
build_env_overlay,
|
|
28
38
|
get_provider_contract,
|
|
39
|
+
is_provider_installed,
|
|
29
40
|
render_prompt,
|
|
30
41
|
resolve_config_paths,
|
|
42
|
+
resolve_session_search_paths,
|
|
43
|
+
resolve_workspace_env,
|
|
31
44
|
)
|
|
32
45
|
from .provider_controls import build_model_id, resolve_provider_model_controls
|
|
33
46
|
from .provider_specs import (
|
|
@@ -69,6 +82,8 @@ from .session_logs import (
|
|
|
69
82
|
)
|
|
70
83
|
from .subprocess_runner import run_cli_command, run_cli_command_sync
|
|
71
84
|
|
|
85
|
+
__version__ = "0.4.0"
|
|
86
|
+
|
|
72
87
|
__all__ = [
|
|
73
88
|
"ApprovalContract",
|
|
74
89
|
"AuthContract",
|
|
@@ -81,10 +96,13 @@ __all__ = [
|
|
|
81
96
|
"ClaudeReasoningPolicy",
|
|
82
97
|
"CliLaunchSpec",
|
|
83
98
|
"ControlSpec",
|
|
99
|
+
"DiagnosticsContract",
|
|
84
100
|
"ErrorCode",
|
|
85
101
|
"FailureClassification",
|
|
86
102
|
"HeadlessContract",
|
|
103
|
+
"IoContract",
|
|
87
104
|
"ModelSpec",
|
|
105
|
+
"OutputContract",
|
|
88
106
|
"PathContract",
|
|
89
107
|
"PromptPayload",
|
|
90
108
|
"PromptTransport",
|
|
@@ -92,12 +110,20 @@ __all__ = [
|
|
|
92
110
|
"ProviderSpec",
|
|
93
111
|
"SandboxContract",
|
|
94
112
|
"SchemaValidationError",
|
|
113
|
+
"SessionDiscoveryContract",
|
|
114
|
+
"WorkspaceEnvVar",
|
|
115
|
+
"WorkspaceEnvValueSource",
|
|
95
116
|
"InteractiveCliRunResult",
|
|
96
117
|
"SessionProgressEvent",
|
|
97
118
|
"SessionRetryDecision",
|
|
98
119
|
"SessionExecutionTimeoutError",
|
|
99
120
|
"TranscriptMirrorStrategy",
|
|
121
|
+
"build_claude_headless_core",
|
|
122
|
+
"build_codex_exec_spec",
|
|
123
|
+
"build_codex_headless_core",
|
|
124
|
+
"build_copilot_headless_core",
|
|
100
125
|
"build_env_overlay",
|
|
126
|
+
"build_gemini_headless_core",
|
|
101
127
|
"get_claude_default_model",
|
|
102
128
|
"get_claude_effort_levels",
|
|
103
129
|
"get_claude_model_candidates",
|
|
@@ -110,9 +136,9 @@ __all__ = [
|
|
|
110
136
|
"get_gemini_model_options",
|
|
111
137
|
"get_provider_contract",
|
|
112
138
|
"get_provider_spec",
|
|
139
|
+
"is_provider_installed",
|
|
113
140
|
"list_provider_specs",
|
|
114
141
|
"build_model_id",
|
|
115
|
-
"build_codex_exec_spec",
|
|
116
142
|
"classify_provider_failure",
|
|
117
143
|
"load_schema",
|
|
118
144
|
"render_prompt",
|
|
@@ -120,6 +146,8 @@ __all__ = [
|
|
|
120
146
|
"resolve_claude_reasoning_policy",
|
|
121
147
|
"resolve_config_paths",
|
|
122
148
|
"resolve_provider_model_controls",
|
|
149
|
+
"resolve_session_search_paths",
|
|
150
|
+
"resolve_workspace_env",
|
|
123
151
|
"redact_text",
|
|
124
152
|
"claude_project_key",
|
|
125
153
|
"find_claude_session",
|
coding_cli_runtime/codex_cli.py
CHANGED
|
@@ -60,18 +60,20 @@ def build_codex_exec_spec(
|
|
|
60
60
|
model_controls=model_controls,
|
|
61
61
|
)
|
|
62
62
|
reasoning_config_value = json.dumps(effective_reasoning)
|
|
63
|
-
|
|
63
|
+
|
|
64
|
+
from .headless import build_codex_headless_core
|
|
65
|
+
|
|
66
|
+
cmd_parts: list[str] = build_codex_headless_core(
|
|
67
|
+
model,
|
|
68
|
+
binary=str(codex_bin),
|
|
69
|
+
sandbox_mode=sandbox if sandbox else None,
|
|
70
|
+
full_auto=full_auto,
|
|
71
|
+
skip_git_repo_check=skip_git_repo_check,
|
|
72
|
+
)
|
|
64
73
|
if json_output:
|
|
65
74
|
cmd_parts.append("--json")
|
|
66
|
-
if full_auto:
|
|
67
|
-
cmd_parts.append("--full-auto")
|
|
68
|
-
cmd_parts.extend(["--sandbox", sandbox])
|
|
69
|
-
if skip_git_repo_check:
|
|
70
|
-
cmd_parts.append("--skip-git-repo-check")
|
|
71
75
|
cmd_parts.extend(
|
|
72
76
|
[
|
|
73
|
-
"--model",
|
|
74
|
-
model,
|
|
75
77
|
"--config",
|
|
76
78
|
f"model_reasoning_effort={reasoning_config_value}",
|
|
77
79
|
"-C",
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Per-provider headless launch core helpers.
|
|
2
|
+
|
|
3
|
+
Each helper emits the standard headless launch args for its provider,
|
|
4
|
+
derived from ``ProviderContract.headless``. Consumers append their own
|
|
5
|
+
app-specific tails (tool restrictions, output paths, prompt, etc.).
|
|
6
|
+
|
|
7
|
+
These helpers are the canonical source for headless launch flag assembly
|
|
8
|
+
within ``coding_cli_runtime``. In-repo consumers (feather, codex_cli,
|
|
9
|
+
provider_contracts builder) delegate to them. App-generation provider
|
|
10
|
+
wrappers may still assemble flags directly when their command construction
|
|
11
|
+
is interleaved with consumer-specific logic (reasoning config, output
|
|
12
|
+
format, artifact paths).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from .provider_contracts import get_provider_contract
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def build_claude_headless_core(
|
|
21
|
+
model: str,
|
|
22
|
+
*,
|
|
23
|
+
binary: str | None = None,
|
|
24
|
+
permission_mode: str | None = None,
|
|
25
|
+
skip_permissions: bool = True,
|
|
26
|
+
) -> list[str]:
|
|
27
|
+
"""Build Claude headless launch core args.
|
|
28
|
+
|
|
29
|
+
Returns args up to and including ``--model``. Does NOT include prompt,
|
|
30
|
+
output format, tool restrictions, or other app-specific flags.
|
|
31
|
+
"""
|
|
32
|
+
contract = get_provider_contract("claude")
|
|
33
|
+
h = contract.headless
|
|
34
|
+
cmd: list[str] = [binary or contract.binary]
|
|
35
|
+
cmd.extend(h.activation_args)
|
|
36
|
+
if h.approval.permission_mode_flag:
|
|
37
|
+
mode = permission_mode or h.approval.default_permission_mode
|
|
38
|
+
if mode:
|
|
39
|
+
cmd.extend([h.approval.permission_mode_flag, mode])
|
|
40
|
+
if skip_permissions and h.approval.flag:
|
|
41
|
+
cmd.append(h.approval.flag)
|
|
42
|
+
cmd.extend(["--model", model])
|
|
43
|
+
return cmd
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def build_codex_headless_core(
|
|
47
|
+
model: str,
|
|
48
|
+
*,
|
|
49
|
+
binary: str | None = None,
|
|
50
|
+
sandbox_mode: str | None = None,
|
|
51
|
+
full_auto: bool = True,
|
|
52
|
+
skip_git_repo_check: bool = True,
|
|
53
|
+
) -> list[str]:
|
|
54
|
+
"""Build Codex headless launch core args.
|
|
55
|
+
|
|
56
|
+
Returns args including ``exec``, ``--full-auto``, ``--sandbox``,
|
|
57
|
+
``--skip-git-repo-check``, and ``--model``. Does NOT include
|
|
58
|
+
``-C``, ``-o``, ``--output-schema``, or reasoning config.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
full_auto: Include ``--full-auto`` (default True).
|
|
62
|
+
skip_git_repo_check: Include ``--skip-git-repo-check`` (default True).
|
|
63
|
+
"""
|
|
64
|
+
contract = get_provider_contract("codex")
|
|
65
|
+
h = contract.headless
|
|
66
|
+
cmd: list[str] = [binary or contract.binary]
|
|
67
|
+
cmd.extend(h.activation_args)
|
|
68
|
+
if full_auto and h.noninteractive_mode_flag:
|
|
69
|
+
cmd.append(h.noninteractive_mode_flag)
|
|
70
|
+
if h.sandbox is not None:
|
|
71
|
+
mode = sandbox_mode or h.sandbox.writable_mode
|
|
72
|
+
cmd.extend([h.sandbox.flag, mode])
|
|
73
|
+
if skip_git_repo_check and h.requires_git_repo and h.skip_git_repo_flag:
|
|
74
|
+
cmd.append(h.skip_git_repo_flag)
|
|
75
|
+
cmd.extend(["--model", model])
|
|
76
|
+
return cmd
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def build_copilot_headless_core(
|
|
80
|
+
model: str,
|
|
81
|
+
*,
|
|
82
|
+
binary: str | None = None,
|
|
83
|
+
stream: str | None = None,
|
|
84
|
+
) -> list[str]:
|
|
85
|
+
"""Build Copilot headless launch core args.
|
|
86
|
+
|
|
87
|
+
Returns args including activation (``--no-ask-user``,
|
|
88
|
+
``--no-custom-instructions``), ``--allow-all``, ``--stream``,
|
|
89
|
+
and ``--model``. Does NOT include ``-p``, ``--share``, or
|
|
90
|
+
force-implementation.
|
|
91
|
+
"""
|
|
92
|
+
contract = get_provider_contract("copilot")
|
|
93
|
+
h = contract.headless
|
|
94
|
+
cmd: list[str] = [binary or contract.binary]
|
|
95
|
+
cmd.extend(h.activation_args)
|
|
96
|
+
if h.approval.flag:
|
|
97
|
+
cmd.append(h.approval.flag)
|
|
98
|
+
cmd.extend(["--model", model])
|
|
99
|
+
if h.stream_flag:
|
|
100
|
+
stream_value = stream or h.default_stream_mode
|
|
101
|
+
if stream_value:
|
|
102
|
+
cmd.extend([h.stream_flag, stream_value])
|
|
103
|
+
return cmd
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def build_gemini_headless_core(
|
|
107
|
+
model: str,
|
|
108
|
+
*,
|
|
109
|
+
binary: str | None = None,
|
|
110
|
+
) -> list[str]:
|
|
111
|
+
"""Build Gemini headless launch core args.
|
|
112
|
+
|
|
113
|
+
Returns args including approval flag (``--yolo``) and ``--model``.
|
|
114
|
+
Does NOT include ``--prompt ""`` activation (that's part of prompt
|
|
115
|
+
transport, handled by ``render_prompt()``).
|
|
116
|
+
"""
|
|
117
|
+
contract = get_provider_contract("gemini")
|
|
118
|
+
h = contract.headless
|
|
119
|
+
cmd: list[str] = [binary or contract.binary]
|
|
120
|
+
cmd.extend(h.activation_args)
|
|
121
|
+
if h.approval.flag:
|
|
122
|
+
cmd.append(h.approval.flag)
|
|
123
|
+
cmd.extend(["--model", model])
|
|
124
|
+
return cmd
|
|
@@ -5,7 +5,8 @@ config paths, and headless launch conventions. It exposes frozen dataclasses
|
|
|
5
5
|
that consumers can read selectively — no obligation to use the full structure.
|
|
6
6
|
|
|
7
7
|
Public stable API:
|
|
8
|
-
get_provider_contract, build_env_overlay, resolve_config_paths, render_prompt
|
|
8
|
+
get_provider_contract, build_env_overlay, resolve_config_paths, render_prompt,
|
|
9
|
+
resolve_workspace_env, resolve_session_search_paths, is_provider_installed
|
|
9
10
|
|
|
10
11
|
Internal (not exported from __init__):
|
|
11
12
|
_build_non_interactive_run
|
|
@@ -13,8 +14,10 @@ Internal (not exported from __init__):
|
|
|
13
14
|
|
|
14
15
|
from __future__ import annotations
|
|
15
16
|
|
|
17
|
+
import shutil
|
|
16
18
|
from dataclasses import dataclass
|
|
17
19
|
from pathlib import Path
|
|
20
|
+
from typing import Literal, TypeAlias
|
|
18
21
|
|
|
19
22
|
from .contracts import AuthMode
|
|
20
23
|
|
|
@@ -105,6 +108,51 @@ class HeadlessContract:
|
|
|
105
108
|
default_stream_mode: str | None
|
|
106
109
|
|
|
107
110
|
|
|
111
|
+
@dataclass(frozen=True)
|
|
112
|
+
class OutputContract:
|
|
113
|
+
"""How the CLI delivers structured output."""
|
|
114
|
+
|
|
115
|
+
format_flag: str | None
|
|
116
|
+
supported_formats: tuple[str, ...]
|
|
117
|
+
default_format: str | None
|
|
118
|
+
output_path_flag: str | None
|
|
119
|
+
schema_path_flag: str | None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
WorkspaceEnvValueSource: TypeAlias = Literal["execution_dir", "workspace_root"]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass(frozen=True)
|
|
126
|
+
class WorkspaceEnvVar:
|
|
127
|
+
"""An environment variable expected by the provider CLI."""
|
|
128
|
+
|
|
129
|
+
name: str
|
|
130
|
+
value_source: WorkspaceEnvValueSource
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass(frozen=True)
|
|
134
|
+
class IoContract:
|
|
135
|
+
"""Provider-specific I/O conventions beyond prompt transport."""
|
|
136
|
+
|
|
137
|
+
file_reference_prefix: str | None
|
|
138
|
+
workspace_env_vars: tuple[WorkspaceEnvVar, ...]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True)
|
|
142
|
+
class SessionDiscoveryContract:
|
|
143
|
+
"""Where session logs live and how to find them."""
|
|
144
|
+
|
|
145
|
+
session_roots: tuple[str, ...]
|
|
146
|
+
session_glob: str
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass(frozen=True)
|
|
150
|
+
class DiagnosticsContract:
|
|
151
|
+
"""Where provider diagnostic logs live."""
|
|
152
|
+
|
|
153
|
+
log_glob: str
|
|
154
|
+
|
|
155
|
+
|
|
108
156
|
@dataclass(frozen=True)
|
|
109
157
|
class ProviderContract:
|
|
110
158
|
"""Structured metadata about a provider CLI.
|
|
@@ -118,6 +166,10 @@ class ProviderContract:
|
|
|
118
166
|
auth: AuthContract
|
|
119
167
|
paths: PathContract
|
|
120
168
|
headless: HeadlessContract
|
|
169
|
+
output: OutputContract
|
|
170
|
+
io: IoContract
|
|
171
|
+
session_discovery: SessionDiscoveryContract | None
|
|
172
|
+
diagnostics: DiagnosticsContract | None
|
|
121
173
|
notes: tuple[str, ...]
|
|
122
174
|
|
|
123
175
|
|
|
@@ -178,6 +230,22 @@ _CLAUDE_CONTRACT = ProviderContract(
|
|
|
178
230
|
stream_modes=None,
|
|
179
231
|
default_stream_mode=None,
|
|
180
232
|
),
|
|
233
|
+
output=OutputContract(
|
|
234
|
+
format_flag="--output-format",
|
|
235
|
+
supported_formats=("text", "json", "stream-json"),
|
|
236
|
+
default_format="text",
|
|
237
|
+
output_path_flag=None,
|
|
238
|
+
schema_path_flag=None,
|
|
239
|
+
),
|
|
240
|
+
io=IoContract(
|
|
241
|
+
file_reference_prefix=None,
|
|
242
|
+
workspace_env_vars=(),
|
|
243
|
+
),
|
|
244
|
+
session_discovery=SessionDiscoveryContract(
|
|
245
|
+
session_roots=("projects",),
|
|
246
|
+
session_glob="*/conversation.jsonl",
|
|
247
|
+
),
|
|
248
|
+
diagnostics=None,
|
|
181
249
|
notes=(),
|
|
182
250
|
)
|
|
183
251
|
|
|
@@ -217,6 +285,22 @@ _CODEX_CONTRACT = ProviderContract(
|
|
|
217
285
|
stream_modes=None,
|
|
218
286
|
default_stream_mode=None,
|
|
219
287
|
),
|
|
288
|
+
output=OutputContract(
|
|
289
|
+
format_flag=None,
|
|
290
|
+
supported_formats=("json",),
|
|
291
|
+
default_format="json",
|
|
292
|
+
output_path_flag="-o",
|
|
293
|
+
schema_path_flag="--output-schema",
|
|
294
|
+
),
|
|
295
|
+
io=IoContract(
|
|
296
|
+
file_reference_prefix=None,
|
|
297
|
+
workspace_env_vars=(),
|
|
298
|
+
),
|
|
299
|
+
session_discovery=SessionDiscoveryContract(
|
|
300
|
+
session_roots=("sessions", "archived_sessions"),
|
|
301
|
+
session_glob="*.jsonl",
|
|
302
|
+
),
|
|
303
|
+
diagnostics=None,
|
|
220
304
|
notes=(
|
|
221
305
|
"codex exec defaults to a read-only sandbox in non-interactive mode; "
|
|
222
306
|
"use --sandbox danger-full-access for write access.",
|
|
@@ -258,9 +342,32 @@ _GEMINI_CONTRACT = ProviderContract(
|
|
|
258
342
|
stream_modes=None,
|
|
259
343
|
default_stream_mode=None,
|
|
260
344
|
),
|
|
345
|
+
output=OutputContract(
|
|
346
|
+
format_flag=None,
|
|
347
|
+
supported_formats=(),
|
|
348
|
+
default_format=None,
|
|
349
|
+
output_path_flag=None,
|
|
350
|
+
schema_path_flag=None,
|
|
351
|
+
),
|
|
352
|
+
io=IoContract(
|
|
353
|
+
file_reference_prefix="@",
|
|
354
|
+
workspace_env_vars=(
|
|
355
|
+
WorkspaceEnvVar(
|
|
356
|
+
name="GEMINI_CLI_IDE_WORKSPACE_PATH",
|
|
357
|
+
value_source="execution_dir",
|
|
358
|
+
),
|
|
359
|
+
),
|
|
360
|
+
),
|
|
361
|
+
session_discovery=SessionDiscoveryContract(
|
|
362
|
+
session_roots=("tmp",),
|
|
363
|
+
session_glob="*/chats/session-*.json",
|
|
364
|
+
),
|
|
365
|
+
diagnostics=None,
|
|
261
366
|
notes=(
|
|
262
367
|
'Gemini requires --prompt "" to activate headless mode; '
|
|
263
368
|
"the real prompt is delivered on stdin.",
|
|
369
|
+
"Gemini output format is prompt-directed, not CLI-flag-driven.",
|
|
370
|
+
"File references in prompts use @filename syntax.",
|
|
264
371
|
),
|
|
265
372
|
)
|
|
266
373
|
|
|
@@ -295,6 +402,24 @@ _COPILOT_CONTRACT = ProviderContract(
|
|
|
295
402
|
stream_modes=("on", "off"),
|
|
296
403
|
default_stream_mode="on",
|
|
297
404
|
),
|
|
405
|
+
output=OutputContract(
|
|
406
|
+
format_flag=None,
|
|
407
|
+
supported_formats=("markdown",),
|
|
408
|
+
default_format="markdown",
|
|
409
|
+
output_path_flag="--share",
|
|
410
|
+
schema_path_flag=None,
|
|
411
|
+
),
|
|
412
|
+
io=IoContract(
|
|
413
|
+
file_reference_prefix=None,
|
|
414
|
+
workspace_env_vars=(),
|
|
415
|
+
),
|
|
416
|
+
session_discovery=SessionDiscoveryContract(
|
|
417
|
+
session_roots=("session-state",),
|
|
418
|
+
session_glob="*/events.jsonl",
|
|
419
|
+
),
|
|
420
|
+
diagnostics=DiagnosticsContract(
|
|
421
|
+
log_glob="logs/process-*.log",
|
|
422
|
+
),
|
|
298
423
|
notes=(
|
|
299
424
|
"Copilot default auth is CLI login (api_key_env_var is None). "
|
|
300
425
|
"BYOK is available via COPILOT_PROVIDER_API_KEY.",
|
|
@@ -350,6 +475,33 @@ def build_env_overlay(
|
|
|
350
475
|
return overlay
|
|
351
476
|
|
|
352
477
|
|
|
478
|
+
def resolve_workspace_env(
|
|
479
|
+
contract: ProviderContract,
|
|
480
|
+
execution_dir: str | Path,
|
|
481
|
+
*,
|
|
482
|
+
workspace_root: str | Path | None = None,
|
|
483
|
+
) -> dict[str, str]:
|
|
484
|
+
"""Resolve provider workspace env vars from contract metadata."""
|
|
485
|
+
resolved: dict[str, str] = {}
|
|
486
|
+
execution_dir_str = str(Path(execution_dir).expanduser())
|
|
487
|
+
workspace_root_str = None
|
|
488
|
+
if workspace_root is not None:
|
|
489
|
+
workspace_root_str = str(Path(workspace_root).expanduser())
|
|
490
|
+
|
|
491
|
+
for item in contract.io.workspace_env_vars:
|
|
492
|
+
if item.value_source == "execution_dir":
|
|
493
|
+
resolved[item.name] = execution_dir_str
|
|
494
|
+
continue
|
|
495
|
+
if item.value_source == "workspace_root":
|
|
496
|
+
if workspace_root_str is None:
|
|
497
|
+
raise ValueError(f"{item.name} requires workspace_root, but none was provided")
|
|
498
|
+
resolved[item.name] = workspace_root_str
|
|
499
|
+
continue
|
|
500
|
+
raise ValueError(f"Unknown workspace env value source: {item.value_source!r}")
|
|
501
|
+
|
|
502
|
+
return resolved
|
|
503
|
+
|
|
504
|
+
|
|
353
505
|
def resolve_config_paths(
|
|
354
506
|
contract: ProviderContract,
|
|
355
507
|
*,
|
|
@@ -366,6 +518,23 @@ def resolve_config_paths(
|
|
|
366
518
|
return host, host
|
|
367
519
|
|
|
368
520
|
|
|
521
|
+
def resolve_session_search_paths(
|
|
522
|
+
contract: ProviderContract,
|
|
523
|
+
*,
|
|
524
|
+
config_dir: str | Path | None = None,
|
|
525
|
+
) -> tuple[Path, ...]:
|
|
526
|
+
"""Expand contract session roots into concrete host paths."""
|
|
527
|
+
discovery = contract.session_discovery
|
|
528
|
+
if discovery is None:
|
|
529
|
+
return ()
|
|
530
|
+
base_dir = (
|
|
531
|
+
Path(config_dir).expanduser()
|
|
532
|
+
if config_dir is not None
|
|
533
|
+
else Path(contract.paths.config_dir).expanduser()
|
|
534
|
+
)
|
|
535
|
+
return tuple(base_dir / root for root in discovery.session_roots)
|
|
536
|
+
|
|
537
|
+
|
|
369
538
|
def render_prompt(
|
|
370
539
|
transport: PromptTransport,
|
|
371
540
|
prompt: str,
|
|
@@ -387,6 +556,12 @@ def render_prompt(
|
|
|
387
556
|
raise ValueError(f"Unknown prompt delivery mode: {transport.delivery!r}")
|
|
388
557
|
|
|
389
558
|
|
|
559
|
+
def is_provider_installed(provider_id: str) -> bool:
|
|
560
|
+
"""Return whether the provider CLI binary is available on PATH."""
|
|
561
|
+
contract = get_provider_contract(provider_id)
|
|
562
|
+
return shutil.which(contract.binary) is not None
|
|
563
|
+
|
|
564
|
+
|
|
390
565
|
# ---------------------------------------------------------------------------
|
|
391
566
|
# Private builder (internal convenience, not public API)
|
|
392
567
|
# ---------------------------------------------------------------------------
|
|
@@ -406,47 +581,53 @@ def _build_non_interactive_run(
|
|
|
406
581
|
stream: str | None = None,
|
|
407
582
|
extra_flags: tuple[str, ...] = (),
|
|
408
583
|
) -> NonInteractiveRunSpec:
|
|
409
|
-
"""Build a non-interactive CLI run spec. Internal convenience.
|
|
410
|
-
contract = get_provider_contract(provider_id)
|
|
411
|
-
h = contract.headless
|
|
412
|
-
|
|
413
|
-
bin_name = binary or contract.binary
|
|
414
|
-
cmd: list[str] = [bin_name]
|
|
415
|
-
|
|
416
|
-
# Headless activation (e.g. "--print" for Claude, "exec" for Codex)
|
|
417
|
-
cmd.extend(h.activation_args)
|
|
418
|
-
|
|
419
|
-
# Non-interactive mode flag (e.g. "--full-auto" for Codex)
|
|
420
|
-
if h.noninteractive_mode_flag:
|
|
421
|
-
cmd.append(h.noninteractive_mode_flag)
|
|
422
|
-
|
|
423
|
-
# Sandbox (Codex)
|
|
424
|
-
if h.sandbox is not None:
|
|
425
|
-
mode = codex_sandbox_mode or h.sandbox.writable_mode
|
|
426
|
-
cmd.extend([h.sandbox.flag, mode])
|
|
584
|
+
"""Build a non-interactive CLI run spec. Internal convenience.
|
|
427
585
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if h.approval.permission_mode_flag:
|
|
438
|
-
mode_value = permission_mode or h.approval.default_permission_mode
|
|
439
|
-
if mode_value:
|
|
440
|
-
cmd.extend([h.approval.permission_mode_flag, mode_value])
|
|
586
|
+
Delegates headless core arg assembly to ``headless.build_*_headless_core()``
|
|
587
|
+
helpers, which derive flags from ``ProviderContract.headless``.
|
|
588
|
+
"""
|
|
589
|
+
from .headless import (
|
|
590
|
+
build_claude_headless_core,
|
|
591
|
+
build_codex_headless_core,
|
|
592
|
+
build_copilot_headless_core,
|
|
593
|
+
build_gemini_headless_core,
|
|
594
|
+
)
|
|
441
595
|
|
|
442
|
-
|
|
443
|
-
|
|
596
|
+
contract = get_provider_contract(provider_id)
|
|
597
|
+
h = contract.headless
|
|
598
|
+
key = provider_id.strip().lower()
|
|
444
599
|
|
|
445
|
-
#
|
|
446
|
-
if
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
600
|
+
# Headless core (binary + activation + approval + model + stream)
|
|
601
|
+
if key == "claude":
|
|
602
|
+
cmd = build_claude_headless_core(model, binary=binary, permission_mode=permission_mode)
|
|
603
|
+
elif key == "codex":
|
|
604
|
+
cmd = build_codex_headless_core(model, binary=binary, sandbox_mode=codex_sandbox_mode)
|
|
605
|
+
elif key == "copilot":
|
|
606
|
+
cmd = build_copilot_headless_core(model, binary=binary, stream=stream)
|
|
607
|
+
elif key == "gemini":
|
|
608
|
+
cmd = build_gemini_headless_core(model, binary=binary)
|
|
609
|
+
else:
|
|
610
|
+
# Fallback for unknown providers — generic assembly
|
|
611
|
+
bin_name = binary or contract.binary
|
|
612
|
+
cmd = [bin_name, *h.activation_args]
|
|
613
|
+
if h.noninteractive_mode_flag:
|
|
614
|
+
cmd.append(h.noninteractive_mode_flag)
|
|
615
|
+
if h.sandbox is not None:
|
|
616
|
+
mode = codex_sandbox_mode or h.sandbox.writable_mode
|
|
617
|
+
cmd.extend([h.sandbox.flag, mode])
|
|
618
|
+
if h.requires_git_repo and h.skip_git_repo_flag:
|
|
619
|
+
cmd.append(h.skip_git_repo_flag)
|
|
620
|
+
if h.approval.flag:
|
|
621
|
+
cmd.append(h.approval.flag)
|
|
622
|
+
if h.approval.permission_mode_flag:
|
|
623
|
+
mode_value = permission_mode or h.approval.default_permission_mode
|
|
624
|
+
if mode_value:
|
|
625
|
+
cmd.extend([h.approval.permission_mode_flag, mode_value])
|
|
626
|
+
cmd.extend(["--model", model])
|
|
627
|
+
if h.stream_flag:
|
|
628
|
+
stream_value = stream or h.default_stream_mode
|
|
629
|
+
if stream_value:
|
|
630
|
+
cmd.extend([h.stream_flag, stream_value])
|
|
450
631
|
|
|
451
632
|
# Prompt
|
|
452
633
|
payload = render_prompt(h.prompt, prompt)
|
|
@@ -5,7 +5,11 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
import re
|
|
8
|
+
from collections.abc import Callable
|
|
8
9
|
from pathlib import Path
|
|
10
|
+
from typing import TypeVar
|
|
11
|
+
|
|
12
|
+
_T = TypeVar("_T")
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
def normalize_path_str(path_str: str) -> str:
|
|
@@ -15,6 +19,58 @@ def normalize_path_str(path_str: str) -> str:
|
|
|
15
19
|
return os.path.normpath(path_str)
|
|
16
20
|
|
|
17
21
|
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Generic session-directory scanning primitive
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def scan_session_dir(
|
|
28
|
+
directory: Path,
|
|
29
|
+
*,
|
|
30
|
+
glob_pattern: str = "*.jsonl",
|
|
31
|
+
since_ts: float,
|
|
32
|
+
mtime_buffer: float = 15.0,
|
|
33
|
+
extract_fn: Callable[[Path], _T | None],
|
|
34
|
+
max_candidates: int = 200,
|
|
35
|
+
) -> list[tuple[float, Path, _T]]:
|
|
36
|
+
"""Scan a directory for session files, filter by mtime, extract metadata.
|
|
37
|
+
|
|
38
|
+
Returns a list of ``(mtime, path, extracted)`` tuples sorted by mtime
|
|
39
|
+
descending. Provider-specific ranking/selection stays with the caller.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
directory: Directory to scan.
|
|
43
|
+
glob_pattern: Glob pattern for session files (default: ``*.jsonl``).
|
|
44
|
+
since_ts: Only include files with mtime >= ``since_ts - mtime_buffer``.
|
|
45
|
+
mtime_buffer: Seconds of slack before ``since_ts`` (default: 15).
|
|
46
|
+
extract_fn: Called on each candidate path. Return ``None`` to skip.
|
|
47
|
+
max_candidates: Max number of candidates to process after mtime filter.
|
|
48
|
+
"""
|
|
49
|
+
if not directory.exists():
|
|
50
|
+
return []
|
|
51
|
+
|
|
52
|
+
candidates: list[tuple[float, Path]] = []
|
|
53
|
+
try:
|
|
54
|
+
for path in directory.rglob(glob_pattern):
|
|
55
|
+
try:
|
|
56
|
+
mtime = path.stat().st_mtime
|
|
57
|
+
except OSError:
|
|
58
|
+
continue
|
|
59
|
+
if mtime >= since_ts - mtime_buffer:
|
|
60
|
+
candidates.append((mtime, path))
|
|
61
|
+
except (OSError, RuntimeError):
|
|
62
|
+
return []
|
|
63
|
+
|
|
64
|
+
candidates.sort(key=lambda item: item[0], reverse=True)
|
|
65
|
+
|
|
66
|
+
results: list[tuple[float, Path, _T]] = []
|
|
67
|
+
for mtime, path in candidates[:max_candidates]:
|
|
68
|
+
extracted = extract_fn(path)
|
|
69
|
+
if extracted is not None:
|
|
70
|
+
results.append((mtime, path, extracted))
|
|
71
|
+
return results
|
|
72
|
+
|
|
73
|
+
|
|
18
74
|
def codex_session_roots() -> list[Path]:
|
|
19
75
|
base = Path.home() / ".codex"
|
|
20
76
|
return [base / "sessions", base / "archived_sessions"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coding-cli-runtime
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Reusable CLI runtime primitives for provider-backed automation workflows
|
|
5
5
|
Author-email: LLM Eval maintainers <llm-eval-maintainers@users.noreply.github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -40,17 +40,21 @@ code doesn't need provider-specific subprocess handling.
|
|
|
40
40
|
|
|
41
41
|
**What it does (and why not just `subprocess.run`):**
|
|
42
42
|
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
43
|
+
- Run any provider CLI with unified request/result types and timeout enforcement
|
|
44
|
+
- Query the model catalog (with user-override and live-cache fallback)
|
|
45
|
+
- Classify failures as retryable vs fatal per provider
|
|
46
|
+
- Look up provider auth, config dirs, and headless launch flags
|
|
47
|
+
- Build non-interactive launch commands without hardcoding provider flags
|
|
48
|
+
- Find session logs after a run (Codex, Claude)
|
|
49
|
+
- Run long-lived sessions with process-group cleanup and transcript mirroring
|
|
50
|
+
- No Python package dependencies — only requires the provider CLIs themselves
|
|
49
51
|
|
|
50
52
|
## Installation
|
|
51
53
|
|
|
52
54
|
```bash
|
|
53
55
|
pip install coding-cli-runtime
|
|
56
|
+
# or
|
|
57
|
+
uv add coding-cli-runtime
|
|
54
58
|
```
|
|
55
59
|
|
|
56
60
|
Requires Python 3.10+.
|
|
@@ -65,7 +69,7 @@ from pathlib import Path
|
|
|
65
69
|
from coding_cli_runtime import CliRunRequest, run_cli_command
|
|
66
70
|
|
|
67
71
|
request = CliRunRequest(
|
|
68
|
-
cmd_parts=("codex", "--model", "
|
|
72
|
+
cmd_parts=("codex", "--model", "gpt-5.4", "--quiet", "exec", "fix the tests"),
|
|
69
73
|
cwd=Path("/tmp/my-project"),
|
|
70
74
|
timeout_seconds=120,
|
|
71
75
|
)
|
|
@@ -148,6 +152,44 @@ else:
|
|
|
148
152
|
Works for all four providers. Recognizes auth failures, rate limits,
|
|
149
153
|
network transients, and other provider-specific error patterns.
|
|
150
154
|
|
|
155
|
+
### Common integration tasks
|
|
156
|
+
|
|
157
|
+
#### Check whether a provider CLI is installed
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from coding_cli_runtime import is_provider_installed
|
|
161
|
+
|
|
162
|
+
if not is_provider_installed("claude"):
|
|
163
|
+
raise RuntimeError("Claude Code is not available on PATH")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
This is intentionally minimal: it checks whether the provider binary exists on
|
|
167
|
+
PATH. Deeper CLI drift validation belongs in maintainer tooling, not the
|
|
168
|
+
runtime API.
|
|
169
|
+
|
|
170
|
+
#### Resolve workspace env vars and session search paths
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from coding_cli_runtime import (
|
|
174
|
+
get_provider_contract,
|
|
175
|
+
resolve_session_search_paths,
|
|
176
|
+
resolve_workspace_env,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
gemini = get_provider_contract("gemini")
|
|
180
|
+
|
|
181
|
+
# Derive provider-specific workspace env vars from contract metadata
|
|
182
|
+
env = resolve_workspace_env(gemini, "/tmp/run-dir")
|
|
183
|
+
# {"GEMINI_CLI_IDE_WORKSPACE_PATH": "/tmp/run-dir"}
|
|
184
|
+
|
|
185
|
+
# Expand concrete host paths for session log searches
|
|
186
|
+
paths = resolve_session_search_paths(gemini)
|
|
187
|
+
# (Path.home() / ".gemini" / "tmp",)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Use these helpers when you want the contract facts turned into concrete
|
|
191
|
+
filesystem/env values without rebuilding the same glue logic in each consumer.
|
|
192
|
+
|
|
151
193
|
### Look up provider contract metadata
|
|
152
194
|
|
|
153
195
|
```python
|
|
@@ -175,11 +217,75 @@ payload = render_prompt(contract.headless.prompt, "Fix the bug")
|
|
|
175
217
|
```
|
|
176
218
|
|
|
177
219
|
`ProviderContract` is structured as nested sub-contracts
|
|
178
|
-
(`AuthContract`, `PathContract`, `HeadlessContract`
|
|
220
|
+
(`AuthContract`, `PathContract`, `HeadlessContract`, `OutputContract`,
|
|
221
|
+
`IoContract`, `SessionDiscoveryContract`, `DiagnosticsContract`) so consumers
|
|
179
222
|
can drill into whichever aspect they need. This is reference metadata,
|
|
180
|
-
not a command-construction control plane —
|
|
223
|
+
not a command-construction control plane — callers keep their own
|
|
181
224
|
command assembly and adopt contract fields selectively.
|
|
182
225
|
|
|
226
|
+
### Query provider I/O conventions
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from coding_cli_runtime import get_provider_contract
|
|
230
|
+
|
|
231
|
+
gemini = get_provider_contract("gemini")
|
|
232
|
+
|
|
233
|
+
# Workspace env vars with value semantics
|
|
234
|
+
for wev in gemini.io.workspace_env_vars:
|
|
235
|
+
print(f"{wev.name} = {wev.value_source}")
|
|
236
|
+
# GEMINI_CLI_IDE_WORKSPACE_PATH = execution_dir
|
|
237
|
+
|
|
238
|
+
# Session discovery (where session logs live)
|
|
239
|
+
sd = gemini.session_discovery
|
|
240
|
+
print(sd.session_roots) # ("tmp",)
|
|
241
|
+
print(sd.session_glob) # "*/chats/session-*.json"
|
|
242
|
+
|
|
243
|
+
# Output format support
|
|
244
|
+
codex = get_provider_contract("codex")
|
|
245
|
+
print(codex.output.output_path_flag) # "-o"
|
|
246
|
+
print(codex.output.schema_path_flag) # "--output-schema"
|
|
247
|
+
|
|
248
|
+
# Diagnostics (Copilot only)
|
|
249
|
+
copilot = get_provider_contract("copilot")
|
|
250
|
+
if copilot.diagnostics:
|
|
251
|
+
print(copilot.diagnostics.log_glob) # "logs/process-*.log"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`WorkspaceEnvVar.value_source` uses a closed vocabulary:
|
|
255
|
+
`"execution_dir"` or `"workspace_root"`.
|
|
256
|
+
|
|
257
|
+
### Build headless launch commands
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
from coding_cli_runtime import build_claude_headless_core, build_codex_headless_core
|
|
261
|
+
|
|
262
|
+
# Claude: binary + --print + --permission-mode + --dangerously-skip-permissions + --model
|
|
263
|
+
cmd = build_claude_headless_core("claude-sonnet-4-6")
|
|
264
|
+
cmd.extend(["--output-format", "text", "--disallowedTools", "Bash,Task"])
|
|
265
|
+
|
|
266
|
+
# Codex: binary + exec + --full-auto + --sandbox + --skip-git-repo-check + --model
|
|
267
|
+
cmd = build_codex_headless_core("gpt-5.4", sandbox_mode="read-only")
|
|
268
|
+
cmd.extend(["-C", str(workdir)])
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Headless core helpers emit the standard flags for non-interactive runs.
|
|
272
|
+
Consumers append app-specific tails (tool restrictions, output paths, etc.).
|
|
273
|
+
|
|
274
|
+
### Find session logs after a run
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
import time
|
|
278
|
+
from coding_cli_runtime import find_codex_session, find_claude_session
|
|
279
|
+
|
|
280
|
+
# Find the most recent Codex session log for a given working directory
|
|
281
|
+
session = find_codex_session("/path/to/project", since_ts=time.time() - 300)
|
|
282
|
+
if session:
|
|
283
|
+
print(f"Session log: {session}") # ~/.codex/sessions/.../conversation.jsonl
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Works for Codex and Claude. Scans provider config directories for session
|
|
287
|
+
files matching the working directory and time window.
|
|
288
|
+
|
|
183
289
|
## Key types
|
|
184
290
|
|
|
185
291
|
| Type | Purpose |
|
|
@@ -188,14 +294,55 @@ command assembly and adopt contract fields selectively.
|
|
|
188
294
|
| `CliRunResult` | Result: returncode, stdout/stderr, duration, error code |
|
|
189
295
|
| `ErrorCode` | `none` · `spawn_failed` · `timed_out` · `non_zero_exit` |
|
|
190
296
|
| `ProviderSpec` | Provider catalog entry with models, controls, defaults |
|
|
191
|
-
| `ProviderContract` | Structured provider CLI metadata (auth, paths, headless
|
|
297
|
+
| `ProviderContract` | Structured provider CLI metadata (auth, paths, headless, I/O, sessions) |
|
|
298
|
+
| `WorkspaceEnvVar` | Env var with value-source semantics (`execution_dir`, `workspace_root`) |
|
|
192
299
|
| `FailureClassification` | Classified error with retryable flag and category |
|
|
193
300
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
301
|
+
### Run long-lived CLI sessions
|
|
302
|
+
|
|
303
|
+
For CLI runs that take minutes (e.g., full app generation), use
|
|
304
|
+
`run_interactive_session()` instead of `run_cli_command()`. It adds:
|
|
305
|
+
|
|
306
|
+
- Process-group cleanup (kills orphaned child processes on timeout)
|
|
307
|
+
- Transcript mirroring (streams CLI output to a file while the process runs)
|
|
308
|
+
- Automatic retries on transient failures
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
from coding_cli_runtime import run_interactive_session
|
|
312
|
+
|
|
313
|
+
result = await run_interactive_session(
|
|
314
|
+
cmd_parts=("claude", "--print", "--model", "claude-sonnet-4-6"),
|
|
315
|
+
cwd=workdir,
|
|
316
|
+
stdin_text=prompt,
|
|
317
|
+
logger=logger,
|
|
318
|
+
timeout_seconds=600,
|
|
319
|
+
)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Only `cmd_parts`, `cwd`, `stdin_text`, and `logger` are required.
|
|
323
|
+
Other parameters have sensible defaults.
|
|
324
|
+
|
|
325
|
+
## API summary
|
|
326
|
+
|
|
327
|
+
The full public API is listed in [`__init__.py`](src/coding_cli_runtime/__init__.py).
|
|
328
|
+
Key function groups:
|
|
329
|
+
|
|
330
|
+
| Group | Functions |
|
|
331
|
+
|-------|-----------|
|
|
332
|
+
| Execution | `run_cli_command`, `run_cli_command_sync`, `run_interactive_session` |
|
|
333
|
+
| Provider metadata | `get_provider_contract`, `get_provider_spec`, `list_provider_specs` |
|
|
334
|
+
| Contract helpers | `build_env_overlay`, `resolve_config_paths`, `render_prompt`, `resolve_auth`, `resolve_workspace_env`, `resolve_session_search_paths` |
|
|
335
|
+
| Headless launch | `build_claude_headless_core`, `build_codex_headless_core`, `build_copilot_headless_core`, `build_gemini_headless_core` |
|
|
336
|
+
| Codex batch | `build_codex_exec_spec` |
|
|
337
|
+
| Failure handling | `classify_provider_failure` |
|
|
338
|
+
| Installation check | `is_provider_installed` |
|
|
339
|
+
| Session logs | `find_codex_session`, `find_claude_session` |
|
|
340
|
+
| Schema | `load_schema`, `validate_payload` |
|
|
341
|
+
| Utilities | `redact_text`, `build_model_id`, `normalize_path_str` |
|
|
342
|
+
|
|
343
|
+
## Contributing
|
|
344
|
+
|
|
345
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and quality checks.
|
|
199
346
|
|
|
200
347
|
## Prerequisites
|
|
201
348
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
coding_cli_runtime/__init__.py,sha256=
|
|
1
|
+
coding_cli_runtime/__init__.py,sha256=06_cwfOA9AMqD6oj5H7_uGj1405leIL6eYc_vCFOREg,4321
|
|
2
2
|
coding_cli_runtime/auth.py,sha256=XP3TZINazvzKcrdgp-pcJcbG4s220dbVVPjF7ivI-wA,2284
|
|
3
|
-
coding_cli_runtime/codex_cli.py,sha256=
|
|
3
|
+
coding_cli_runtime/codex_cli.py,sha256=h26tfb1Kj9LyQV19OvMV-DS4NKvdWrDPXVKGhS8lALI,3002
|
|
4
4
|
coding_cli_runtime/contracts.py,sha256=teYMPDYCjL6HRwRBucJKetfZKlRnxpG82BrC1Y1OMNg,1764
|
|
5
5
|
coding_cli_runtime/copilot_reasoning_baseline.json,sha256=hEIsqm03-D8T9Snn_FvbC2RD367fXGziyTK0Ajpxrmk,1649
|
|
6
6
|
coding_cli_runtime/copilot_reasoning_logs.py,sha256=S2GD0zGgwVXAPe-DyJPKMR5j-EgOGlANnIt315mIWuo,2327
|
|
7
7
|
coding_cli_runtime/failure_classification.py,sha256=fjGOjtQaBh6Y13gEIS7ystmadcrFKsLAN-fccBuerBs,6027
|
|
8
|
+
coding_cli_runtime/headless.py,sha256=0q0L-crpIRb4A7GCOH2kyoSnyw37USGyFTzP6OOQq9Q,4186
|
|
8
9
|
coding_cli_runtime/json_io.py,sha256=1RseVXV-uPWRm_-pGIUTAvhALnMAEivCX46zvlVpri0,2665
|
|
9
|
-
coding_cli_runtime/provider_contracts.py,sha256
|
|
10
|
+
coding_cli_runtime/provider_contracts.py,sha256=-UhDva-gAWLTgk1ZFxo2gKNpb5GL6Cba68yJR9YB47w,20630
|
|
10
11
|
coding_cli_runtime/provider_controls.py,sha256=2rl1XxGYFODu6LRx3bLhptgN5M_T3klx2lqT_gAAkb0,3328
|
|
11
12
|
coding_cli_runtime/provider_specs.py,sha256=n9cptkPVI5sIBVuJlqE8nuTU7SyJwG8rkUF2enM_KZc,26777
|
|
12
13
|
coding_cli_runtime/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
@@ -14,12 +15,12 @@ coding_cli_runtime/reasoning.py,sha256=Ggyw1K9Ry4bytzeS-Jy8jmNHVJR891zH_4jRpYAsw
|
|
|
14
15
|
coding_cli_runtime/redaction.py,sha256=PALvJoNt7r0E_Dd3N02tCV9RI_0nPfSgoVAeaWxeLAY,559
|
|
15
16
|
coding_cli_runtime/schema_validation.py,sha256=WZvl2_LkAnuxNMmpS2-vjtLqny034G9xT7wh1BZ1gwM,3929
|
|
16
17
|
coding_cli_runtime/session_execution.py,sha256=U9oRrz2ORuZJzUf4WK2BS6ubCSLOHe94izg6xmn6d3E,20714
|
|
17
|
-
coding_cli_runtime/session_logs.py,sha256=
|
|
18
|
+
coding_cli_runtime/session_logs.py,sha256=wyHld9yVydWd06jSxFSW7MwzxSbiJVLNzKN4Hh6dUkU,5689
|
|
18
19
|
coding_cli_runtime/subprocess_runner.py,sha256=WqYMI6ALWFhEUySycfHXEtXuCX3m5x7s1n3Bd0TyPm0,11419
|
|
19
20
|
coding_cli_runtime/schemas/normalized_run_result.v1.json,sha256=ogVKJbDFAd9dJklmp8SUkdR9L5EX1rdHGj5leJJHXGs,1110
|
|
20
21
|
coding_cli_runtime/schemas/reasoning_metadata.v1.json,sha256=nQWhqp9-dlzJM18OARDUwAyaA-3-I8rETZYvkgTAnOc,467
|
|
21
|
-
coding_cli_runtime-0.
|
|
22
|
-
coding_cli_runtime-0.
|
|
23
|
-
coding_cli_runtime-0.
|
|
24
|
-
coding_cli_runtime-0.
|
|
25
|
-
coding_cli_runtime-0.
|
|
22
|
+
coding_cli_runtime-0.4.0.dist-info/licenses/LICENSE,sha256=hVIuaMVAgQkhTh44et0cpDtN3kGOZnKQ2bY1rJJw-MI,1078
|
|
23
|
+
coding_cli_runtime-0.4.0.dist-info/METADATA,sha256=wh9K2YDO1-741eS4nRnSwDY4t0LBpLh9eOb9QtgMzgE,13099
|
|
24
|
+
coding_cli_runtime-0.4.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
25
|
+
coding_cli_runtime-0.4.0.dist-info/top_level.txt,sha256=-tzjii3Qf_GTevxT5M46tITBY02R-K8Ew04hJRHOB2Y,19
|
|
26
|
+
coding_cli_runtime-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|