coderouter-cli 2.7.8__py3-none-any.whl → 2.7.9__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.
- coderouter/adapters/agent_cli.py +218 -21
- coderouter/config/schemas.py +15 -6
- {coderouter_cli-2.7.8.dist-info → coderouter_cli-2.7.9.dist-info}/METADATA +1 -1
- {coderouter_cli-2.7.8.dist-info → coderouter_cli-2.7.9.dist-info}/RECORD +7 -7
- {coderouter_cli-2.7.8.dist-info → coderouter_cli-2.7.9.dist-info}/WHEEL +0 -0
- {coderouter_cli-2.7.8.dist-info → coderouter_cli-2.7.9.dist-info}/entry_points.txt +0 -0
- {coderouter_cli-2.7.8.dist-info → coderouter_cli-2.7.9.dist-info}/licenses/LICENSE +0 -0
coderouter/adapters/agent_cli.py
CHANGED
|
@@ -18,15 +18,24 @@ CodeRouter merely performs the one conversion. Following the
|
|
|
18
18
|
``openai_compat`` precedent, a *single* adapter class fronts *multiple*
|
|
19
19
|
target agents, dispatched on the ``agent`` field.
|
|
20
20
|
|
|
21
|
-
Implemented agents (Phase 1a + 1d)
|
|
22
|
-
|
|
21
|
+
Implemented agents (Phase 1a + 1b + 1d)
|
|
22
|
+
========================================
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Three targets are implemented here:
|
|
25
25
|
|
|
26
26
|
* ``claude`` (Claude Code CLI, Phase 1a) — the most stable CLI, the most
|
|
27
27
|
fine-grained safety controls, and the only one that emits
|
|
28
28
|
``total_cost_usd`` directly (making it the reference implementation for
|
|
29
29
|
the parser / cost path).
|
|
30
|
+
* ``codex`` (codex CLI, Phase 1b) — headless one-shot via ``codex exec
|
|
31
|
+
--json``, prompt on stdin (like claude). The CLI is pre-1.0 and emits
|
|
32
|
+
defensively-parsed JSONL (one event per line); usage comes from
|
|
33
|
+
``turn.completed`` events, normalized per design §5.1.6 with
|
|
34
|
+
``cached_input_tokens`` kept as a *subset* of ``input_tokens`` (unlike
|
|
35
|
+
claude, which folds its cache buckets into ``prompt_tokens``).
|
|
36
|
+
``--skip-git-repo-check`` and ``--ephemeral`` are always passed — the
|
|
37
|
+
isolated workdir is not a git repo, and the adapter never wants
|
|
38
|
+
session persistence.
|
|
30
39
|
* ``grok`` (grok CLI, Phase 1d) — headless one-shot via ``--prompt-file`` +
|
|
31
40
|
``--output-format json``. The CLI emits no token/cost figures, so usage
|
|
32
41
|
is reported as zeros (cost stays 0 unless the operator sets
|
|
@@ -34,9 +43,9 @@ Two targets are implemented here:
|
|
|
34
43
|
so a user-level cross-session memory setting cannot leak state between
|
|
35
44
|
requests.
|
|
36
45
|
|
|
37
|
-
The remaining
|
|
38
|
-
providers.yaml is forward-compatible, but constructing an adapter for
|
|
39
|
-
raises a clear ``AdapterError`` until
|
|
46
|
+
The remaining agent (gemini) is declared in the config schema so
|
|
47
|
+
providers.yaml is forward-compatible, but constructing an adapter for it
|
|
48
|
+
raises a clear ``AdapterError`` until its phase (1c) lands.
|
|
40
49
|
|
|
41
50
|
Security (design §6, non-negotiable)
|
|
42
51
|
====================================
|
|
@@ -44,10 +53,11 @@ Security (design §6, non-negotiable)
|
|
|
44
53
|
* **allowlist argv only** — the child is launched with
|
|
45
54
|
:func:`asyncio.create_subprocess_exec` and a list argv. ``shell=True`` is
|
|
46
55
|
never used; the prompt is never subject to shell interpretation (claude
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
and codex read it from stdin — codex's argv carries a trailing ``-``
|
|
57
|
+
sentinel making that explicit; grok reads it from a private ``0600``
|
|
58
|
+
prompt file inside the resolved workdir — its ``-p`` requires the prompt
|
|
59
|
+
as an argv value, and argv would both hit Linux's ~128KiB
|
|
60
|
+
``MAX_ARG_STRLEN`` on huge prompts and leak the text into ``ps`` output).
|
|
51
61
|
* **default read-only** — ``allow_file_writes=False`` /
|
|
52
62
|
``sandbox_mode="read_only"`` are the defaults, mapped to claude's
|
|
53
63
|
``--permission-mode plan``. Writes require explicit opt-in and the sandbox
|
|
@@ -127,6 +137,17 @@ _GROK_SANDBOX_ARGS = {
|
|
|
127
137
|
"full_auto": ["--sandbox", "workspace", "--always-approve"],
|
|
128
138
|
}
|
|
129
139
|
|
|
140
|
+
# sandbox_mode → codex ``-s/--sandbox`` value (design §5.4, codex-cli
|
|
141
|
+
# 0.144.1, verified via facts-codex.md). ``codex exec`` has NO approval
|
|
142
|
+
# flag at all (non-interactive, so there is no prompt to approve/skip), so
|
|
143
|
+
# ``full_auto`` collapses onto the same ``workspace-write`` value as
|
|
144
|
+
# ``edit`` — there is nothing further to "auto" beyond granting writes.
|
|
145
|
+
_CODEX_SANDBOX_ARGS = {
|
|
146
|
+
"read_only": ["-s", "read-only"],
|
|
147
|
+
"edit": ["-s", "workspace-write"],
|
|
148
|
+
"full_auto": ["-s", "workspace-write"],
|
|
149
|
+
}
|
|
150
|
+
|
|
130
151
|
|
|
131
152
|
def _chunk_text(text: str, size: int = _STREAM_CHUNK_CHARS) -> Iterator[str]:
|
|
132
153
|
"""Split ``text`` into ``size``-char pieces for the pseudo-stream."""
|
|
@@ -135,7 +156,7 @@ def _chunk_text(text: str, size: int = _STREAM_CHUNK_CHARS) -> Iterator[str]:
|
|
|
135
156
|
|
|
136
157
|
|
|
137
158
|
class AgentCliAdapter(BaseAdapter):
|
|
138
|
-
"""Invoke an external coding-agent CLI one-shot (claude + grok).
|
|
159
|
+
"""Invoke an external coding-agent CLI one-shot (claude + codex + grok).
|
|
139
160
|
|
|
140
161
|
The ``agent`` field selects the argv builder / output parser via the
|
|
141
162
|
dispatch tables built in :meth:`__init__`, mirroring how
|
|
@@ -145,10 +166,10 @@ class AgentCliAdapter(BaseAdapter):
|
|
|
145
166
|
def __init__(self, config: ProviderConfig) -> None:
|
|
146
167
|
"""Bind to a ``ProviderConfig`` and reject unsupported agents.
|
|
147
168
|
|
|
148
|
-
Constructing an adapter for an agent other than ``claude`` /
|
|
149
|
-
raises a non-retryable :class:`AdapterError` —
|
|
150
|
-
declared in the schema but not
|
|
151
|
-
(design §9).
|
|
169
|
+
Constructing an adapter for an agent other than ``claude`` /
|
|
170
|
+
``codex`` / ``grok`` raises a non-retryable :class:`AdapterError` —
|
|
171
|
+
the remaining target (gemini) is declared in the schema but not
|
|
172
|
+
implemented until its phase (design §9).
|
|
152
173
|
"""
|
|
153
174
|
super().__init__(config)
|
|
154
175
|
if config.agent_cli is None: # pragma: no cover - schema enforces this
|
|
@@ -158,30 +179,34 @@ class AgentCliAdapter(BaseAdapter):
|
|
|
158
179
|
retryable=False,
|
|
159
180
|
)
|
|
160
181
|
self.acfg: AgentCliConfig = config.agent_cli
|
|
161
|
-
if self.acfg.agent not in ("claude", "grok"):
|
|
182
|
+
if self.acfg.agent not in ("claude", "codex", "grok"):
|
|
162
183
|
raise AdapterError(
|
|
163
184
|
f"agent {self.acfg.agent!r} is not implemented yet "
|
|
164
|
-
f"(implemented: claude, grok). Wait for Phase
|
|
185
|
+
f"(implemented: claude, codex, grok). Wait for Phase 1c.",
|
|
165
186
|
provider=config.name,
|
|
166
187
|
retryable=False,
|
|
167
188
|
)
|
|
168
189
|
# agent → argv builder / output parser dispatch tables. claude landed
|
|
169
|
-
# in Phase 1a, grok in Phase 1d; Phase
|
|
190
|
+
# in Phase 1a, codex in Phase 1b, grok in Phase 1d; Phase 1c adds
|
|
191
|
+
# gemini.
|
|
170
192
|
self._builders = {
|
|
171
193
|
"claude": self._build_claude_argv,
|
|
194
|
+
"codex": self._build_codex_argv,
|
|
172
195
|
"grok": self._build_grok_argv,
|
|
173
196
|
}
|
|
174
197
|
self._parsers = {
|
|
175
198
|
"claude": self._parse_claude,
|
|
199
|
+
"codex": self._parse_codex,
|
|
176
200
|
"grok": self._parse_grok,
|
|
177
201
|
}
|
|
178
|
-
# Prompt delivery is per-agent: claude
|
|
179
|
-
# from stdin (10MB cap
|
|
202
|
+
# Prompt delivery is per-agent: claude and codex read their prompt
|
|
203
|
+
# from stdin (10MB cap; codex's argv carries a trailing "-" sentinel
|
|
204
|
+
# making that explicit), which keeps argv free of the (potentially
|
|
180
205
|
# huge) prompt text. grok's ``-p`` REQUIRES the prompt as its argv
|
|
181
206
|
# value (piped stdin is only appended as extra context, verified on
|
|
182
207
|
# v0.2.93), so grok gets the prompt via ``--prompt-file`` instead —
|
|
183
208
|
# see ``_write_prompt_file`` for the rationale.
|
|
184
|
-
self._uses_stdin = self.acfg.agent
|
|
209
|
+
self._uses_stdin = self.acfg.agent in ("claude", "codex")
|
|
185
210
|
|
|
186
211
|
# ------------------------------------------------------------------
|
|
187
212
|
# BaseAdapter contract
|
|
@@ -465,6 +490,178 @@ class AgentCliAdapter(BaseAdapter):
|
|
|
465
490
|
usage["duration_ms"] = data["duration_ms"]
|
|
466
491
|
return usage
|
|
467
492
|
|
|
493
|
+
# ------------------------------------------------------------------
|
|
494
|
+
# codex argv builder + output parser (Phase 1b)
|
|
495
|
+
# ------------------------------------------------------------------
|
|
496
|
+
|
|
497
|
+
def _build_codex_argv(self, workdir: str, prompt_file: str | None = None) -> list[str]:
|
|
498
|
+
"""Assemble the ``codex exec`` argv (design §5.1.5 / §5.4, verified
|
|
499
|
+
against codex-cli 0.144.1 — see ``_codex/facts-codex.md``).
|
|
500
|
+
|
|
501
|
+
Shape::
|
|
502
|
+
|
|
503
|
+
codex exec --json --skip-git-repo-check --ephemeral
|
|
504
|
+
-m <model> -C <workdir> -s <read-only|workspace-write> -
|
|
505
|
+
|
|
506
|
+
The prompt is fed on stdin (not argv), so it never appears here and
|
|
507
|
+
``prompt_file`` is ignored (it exists only to keep the builder
|
|
508
|
+
signature uniform across agents) — the trailing ``-`` makes the
|
|
509
|
+
stdin intent explicit to ``codex exec``, which otherwise treats a
|
|
510
|
+
bare invocation with no PROMPT arg the same way but reads more
|
|
511
|
+
ambiguously in a fixed argv list. ``--skip-git-repo-check`` is
|
|
512
|
+
ALWAYS passed because the isolated workdir is not a git repository
|
|
513
|
+
(without it the CLI exits 1). ``--ephemeral`` is ALWAYS passed so no
|
|
514
|
+
session state persists to disk, matching the adapter's stateless
|
|
515
|
+
one-shot ethos (the same rationale as grok's ``--no-memory``). codex
|
|
516
|
+
has no ``--max-turns`` equivalent, so ``AgentCliConfig.max_turns`` is
|
|
517
|
+
silently ignored here (documented in the schema).
|
|
518
|
+
"""
|
|
519
|
+
del prompt_file # codex takes the prompt on stdin, not from a file.
|
|
520
|
+
model = self.acfg.model or self.config.model
|
|
521
|
+
argv = [
|
|
522
|
+
self.acfg.command,
|
|
523
|
+
"exec",
|
|
524
|
+
"--json",
|
|
525
|
+
"--skip-git-repo-check",
|
|
526
|
+
"--ephemeral",
|
|
527
|
+
"-m",
|
|
528
|
+
model,
|
|
529
|
+
"-C",
|
|
530
|
+
workdir,
|
|
531
|
+
]
|
|
532
|
+
argv += self._codex_sandbox_args()
|
|
533
|
+
argv += ["-"]
|
|
534
|
+
return argv
|
|
535
|
+
|
|
536
|
+
def _codex_sandbox_args(self) -> list[str]:
|
|
537
|
+
"""Map ``sandbox_mode`` → codex ``-s/--sandbox`` flags, clamped.
|
|
538
|
+
|
|
539
|
+
Same clamp as claude/grok (design §5.4): when ``allow_file_writes``
|
|
540
|
+
is False the effective mode is forced to ``read_only`` regardless of
|
|
541
|
+
``sandbox_mode``, so writes always require the explicit opt-in.
|
|
542
|
+
``codex exec`` has no approval flag in 0.144.1 (non-interactive, so
|
|
543
|
+
there is nothing to approve), so ``full_auto`` maps onto the same
|
|
544
|
+
``workspace-write`` value as ``edit`` —
|
|
545
|
+
``--dangerously-bypass-approvals-and-sandbox`` is never used.
|
|
546
|
+
"""
|
|
547
|
+
mode = self.acfg.sandbox_mode if self.acfg.allow_file_writes else "read_only"
|
|
548
|
+
return list(_CODEX_SANDBOX_ARGS[mode])
|
|
549
|
+
|
|
550
|
+
def _parse_codex(
|
|
551
|
+
self, stdout: bytes, stderr: bytes
|
|
552
|
+
) -> tuple[str, dict[str, Any], dict[str, Any]]:
|
|
553
|
+
"""Parse codex ``exec --json`` JSONL output (verified codex-cli
|
|
554
|
+
0.144.1, ``_codex/facts-codex.md``).
|
|
555
|
+
|
|
556
|
+
Real-run shape (one JSON object per line, newline-delimited)::
|
|
557
|
+
|
|
558
|
+
{"type":"thread.started","thread_id":"<uuid>"}
|
|
559
|
+
{"type":"turn.started"}
|
|
560
|
+
{"type":"item.completed","item":{"id":"item_0","type":"agent_message","text":"2"}}
|
|
561
|
+
{"type":"turn.completed","usage":{"input_tokens":13810,"cached_input_tokens":9984,"output_tokens":5,"reasoning_output_tokens":0}}
|
|
562
|
+
|
|
563
|
+
The CLI is pre-1.0 and its JSON schema is not frozen, so parsing is
|
|
564
|
+
deliberately defensive at every level: individual lines that fail to
|
|
565
|
+
parse (or parse to something other than a JSON object) are SKIPPED
|
|
566
|
+
rather than aborting the whole parse — stray non-JSON noise on
|
|
567
|
+
stdout should not sink an otherwise-valid answer. The final answer
|
|
568
|
+
is the LAST ``item.completed`` event whose ``item`` is an
|
|
569
|
+
``agent_message`` with a string ``text``. If a completed answer was
|
|
570
|
+
found, it is returned even when a later ``error`` / ``turn.failed``
|
|
571
|
+
event also appears (a completed answer beats a trailing error); if
|
|
572
|
+
no answer was found, an ``error`` / ``turn.failed`` event (or the
|
|
573
|
+
total absence of any agent_message) raises a retryable
|
|
574
|
+
:class:`AdapterError`.
|
|
575
|
+
"""
|
|
576
|
+
text = stdout.decode("utf-8", "replace")
|
|
577
|
+
if not text.strip():
|
|
578
|
+
raise AdapterError(
|
|
579
|
+
"codex produced no stdout to parse",
|
|
580
|
+
provider=self.name,
|
|
581
|
+
retryable=True,
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
final_text: str | None = None
|
|
585
|
+
thread_id: str | None = None
|
|
586
|
+
failure_event: dict[str, Any] | None = None
|
|
587
|
+
prompt_tokens = 0
|
|
588
|
+
completion_tokens = 0
|
|
589
|
+
cached_tokens = 0
|
|
590
|
+
reasoning_tokens = 0
|
|
591
|
+
|
|
592
|
+
for line in text.splitlines():
|
|
593
|
+
line = line.strip()
|
|
594
|
+
if not line:
|
|
595
|
+
continue
|
|
596
|
+
try:
|
|
597
|
+
event = json.loads(line)
|
|
598
|
+
except json.JSONDecodeError:
|
|
599
|
+
# Defensive against stray non-JSON noise (progress text that
|
|
600
|
+
# leaked onto stdout, partial writes, etc.) — skip the line.
|
|
601
|
+
continue
|
|
602
|
+
if not isinstance(event, dict):
|
|
603
|
+
continue
|
|
604
|
+
|
|
605
|
+
etype = event.get("type")
|
|
606
|
+
if etype == "thread.started":
|
|
607
|
+
tid = event.get("thread_id")
|
|
608
|
+
if isinstance(tid, str):
|
|
609
|
+
thread_id = tid
|
|
610
|
+
elif etype == "item.completed":
|
|
611
|
+
item = event.get("item")
|
|
612
|
+
if (
|
|
613
|
+
isinstance(item, dict)
|
|
614
|
+
and item.get("type") == "agent_message"
|
|
615
|
+
and isinstance(item.get("text"), str)
|
|
616
|
+
):
|
|
617
|
+
final_text = item["text"]
|
|
618
|
+
elif etype == "turn.completed":
|
|
619
|
+
usage = event.get("usage")
|
|
620
|
+
usage = usage if isinstance(usage, dict) else {}
|
|
621
|
+
|
|
622
|
+
def _int(key: str, _usage: dict[str, Any] = usage) -> int:
|
|
623
|
+
value = _usage.get(key)
|
|
624
|
+
return int(value) if isinstance(value, (int, float)) else 0
|
|
625
|
+
|
|
626
|
+
prompt_tokens += _int("input_tokens")
|
|
627
|
+
completion_tokens += _int("output_tokens")
|
|
628
|
+
cached_tokens += _int("cached_input_tokens")
|
|
629
|
+
reasoning_tokens += _int("reasoning_output_tokens")
|
|
630
|
+
elif etype in ("error", "turn.failed"):
|
|
631
|
+
failure_event = event
|
|
632
|
+
|
|
633
|
+
if final_text is None:
|
|
634
|
+
if failure_event is not None:
|
|
635
|
+
raise AdapterError(
|
|
636
|
+
f"codex reported {failure_event.get('type')}: {failure_event!r}"[:500],
|
|
637
|
+
provider=self.name,
|
|
638
|
+
retryable=True,
|
|
639
|
+
)
|
|
640
|
+
raise AdapterError(
|
|
641
|
+
"codex JSONL output contained no agent_message",
|
|
642
|
+
provider=self.name,
|
|
643
|
+
retryable=True,
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
# cached_input_tokens is a SUBSET of input_tokens (not additive) —
|
|
647
|
+
# verified sample: input 13810 ⊇ cached 9984 — so it is preserved
|
|
648
|
+
# under prompt_tokens_details rather than folded into prompt_tokens
|
|
649
|
+
# (this differs from claude's normalization, design §5.1.6).
|
|
650
|
+
usage_out: dict[str, Any] = {
|
|
651
|
+
"prompt_tokens": prompt_tokens,
|
|
652
|
+
"completion_tokens": completion_tokens,
|
|
653
|
+
"total_tokens": prompt_tokens + completion_tokens,
|
|
654
|
+
}
|
|
655
|
+
if cached_tokens > 0:
|
|
656
|
+
usage_out["prompt_tokens_details"] = {"cached_tokens": cached_tokens}
|
|
657
|
+
if reasoning_tokens > 0:
|
|
658
|
+
usage_out["completion_tokens_details"] = {"reasoning_tokens": reasoning_tokens}
|
|
659
|
+
|
|
660
|
+
meta: dict[str, Any] = {}
|
|
661
|
+
if thread_id is not None:
|
|
662
|
+
meta["coderouter_session_id"] = thread_id
|
|
663
|
+
return final_text, usage_out, meta
|
|
664
|
+
|
|
468
665
|
# ------------------------------------------------------------------
|
|
469
666
|
# grok argv builder + output parser (Phase 1d)
|
|
470
667
|
# ------------------------------------------------------------------
|
coderouter/config/schemas.py
CHANGED
|
@@ -171,10 +171,10 @@ class AgentCliConfig(BaseModel):
|
|
|
171
171
|
invokes an external coding-agent CLI (codex / gemini / grok / claude)
|
|
172
172
|
in a single one-shot ``exec`` and returns the final answer as one
|
|
173
173
|
``prompt in → text out`` transformation. ``claude`` (Claude Code CLI,
|
|
174
|
-
Phase 1a)
|
|
175
|
-
|
|
176
|
-
forward-compatible, but the adapter rejects
|
|
177
|
-
lands.
|
|
174
|
+
Phase 1a), ``codex`` (codex CLI, Phase 1b) and ``grok`` (grok CLI,
|
|
175
|
+
Phase 1d) are implemented; ``gemini`` is declared at the schema level
|
|
176
|
+
so configs are forward-compatible, but the adapter rejects it until its
|
|
177
|
+
phase (1c) lands.
|
|
178
178
|
|
|
179
179
|
Auth note (grok): the grok CLI uses OAuth credentials stored under
|
|
180
180
|
``~/.grok`` (``grok login``), which the adapter's HOME inheritance
|
|
@@ -182,6 +182,14 @@ class AgentCliConfig(BaseModel):
|
|
|
182
182
|
``GROK_CODE_XAI_API_KEY`` in ``passthrough_env`` (this is grok's key
|
|
183
183
|
env var — NOT ``XAI_API_KEY``).
|
|
184
184
|
|
|
185
|
+
Auth note (codex): the codex CLI uses a ChatGPT-plan OAuth login stored
|
|
186
|
+
under ``~/.codex`` (``codex login``), which the adapter's HOME
|
|
187
|
+
inheritance already covers — the credentials go stale after roughly 8
|
|
188
|
+
days and are auto-refreshed on use, so no extra config is needed for
|
|
189
|
+
interactive/subscription setups. For CI / API-key setups, list
|
|
190
|
+
``CODEX_API_KEY`` (exec-only) or ``OPENAI_API_KEY`` (general) in
|
|
191
|
+
``passthrough_env``.
|
|
192
|
+
|
|
185
193
|
Follows the ``extra="forbid"`` convention used across this module so a
|
|
186
194
|
typo'd key fails at config-load rather than being silently ignored.
|
|
187
195
|
"""
|
|
@@ -191,8 +199,9 @@ class AgentCliConfig(BaseModel):
|
|
|
191
199
|
agent: Literal["codex", "gemini", "grok", "claude"] = Field(
|
|
192
200
|
...,
|
|
193
201
|
description=(
|
|
194
|
-
"External coding-agent CLI to invoke. 'claude' (Phase 1a)
|
|
195
|
-
"'grok' (Phase 1d) are implemented;
|
|
202
|
+
"External coding-agent CLI to invoke. 'claude' (Phase 1a), "
|
|
203
|
+
"'codex' (Phase 1b) and 'grok' (Phase 1d) are implemented; "
|
|
204
|
+
"'gemini' (Phase 1c) pending."
|
|
196
205
|
),
|
|
197
206
|
)
|
|
198
207
|
command: str | None = Field(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coderouter-cli
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.9
|
|
4
4
|
Summary: Local-first, free-first, fallback-built-in LLM router. Claude Code / OpenAI compatible.
|
|
5
5
|
Project-URL: Homepage, https://github.com/zephel01/CodeRouter
|
|
6
6
|
Project-URL: Repository, https://github.com/zephel01/CodeRouter
|
|
@@ -16,7 +16,7 @@ coderouter/output_filters.py,sha256=0ry_rPiS_kC-FnHgaNVP6v7e6Al2djxzu9vBzZ8kEkE,
|
|
|
16
16
|
coderouter/token_estimation.py,sha256=iz22vZEEW2P7uKLB2pYvPNpIbZGbgXRO5MtfkS_-9Sk,7531
|
|
17
17
|
coderouter/token_estimation_accurate.py,sha256=GTfzrBVnvAGjeVzmzAeUdOYZvWZKLAxcxPpFiJGlzjk,4609
|
|
18
18
|
coderouter/adapters/__init__.py,sha256=7dIDSZ-FE_0iSqLSDc_lK1idRdLTKcM2hP9tCJipgPI,463
|
|
19
|
-
coderouter/adapters/agent_cli.py,sha256=
|
|
19
|
+
coderouter/adapters/agent_cli.py,sha256=ChTheoyDz9SE86Htr-ZOwSW-M7XrbuUfed1daAlvXBI,42154
|
|
20
20
|
coderouter/adapters/anthropic_native.py,sha256=CT9Hitun-c3z83YHT2zZXgwZpY3_t6eRnOfAmNud2aw,23610
|
|
21
21
|
coderouter/adapters/base.py,sha256=ykKMaiIVVGr23oFFXilK2iP_YhI8-CtsEiJyFXaC9bE,10396
|
|
22
22
|
coderouter/adapters/openai_compat.py,sha256=wcrd_UpV7N6ISpUyKvdFPGU-YjCrX5oMqGpjAFEeWfg,20356
|
|
@@ -25,7 +25,7 @@ coderouter/config/__init__.py,sha256=FODEn74fN-qZnt4INPSHswqhOlEgpL6-_onxsitSx8g
|
|
|
25
25
|
coderouter/config/capability_registry.py,sha256=RZgzHF54dXy0CCDocRCz2ee21qXGbbgTSKTkRI9iLL4,16936
|
|
26
26
|
coderouter/config/env_file.py,sha256=CoMK27fuAXm-NtoLzXb8yN2E-wDFjHQuFwiIlmgTBQw,10356
|
|
27
27
|
coderouter/config/loader.py,sha256=FUEe8m4Tnmj_aul0vSctD8vKvNW-oLRoMRbTpSKqSmc,4077
|
|
28
|
-
coderouter/config/schemas.py,sha256=
|
|
28
|
+
coderouter/config/schemas.py,sha256=fuqMuO9bafAhztUMtA6CwEBjSKNFyW-CIeen3Q0pRlg,87047
|
|
29
29
|
coderouter/data/__init__.py,sha256=uNyfD9jaCvTWsBAWtaw1Fr25OSxzv3psGMfBjT1z0Cc,328
|
|
30
30
|
coderouter/data/model-capabilities.yaml,sha256=S9jt6SC6-3s2-icZ_n-a14iEMnc2yB1C2R6q-N_tZWQ,19309
|
|
31
31
|
coderouter/guards/__init__.py,sha256=5qliYBqygvVPneej7nx0uSjxDKsz7t8VzvrDgVBJlvU,1170
|
|
@@ -69,8 +69,8 @@ coderouter/translation/__init__.py,sha256=PYXN7XVEwpG1uC8RLy6fvnGbzEZhhrEuUapH8I
|
|
|
69
69
|
coderouter/translation/anthropic.py,sha256=aZkcYH4x82b0x7efJgJb9RWn9Hbyc9pEOthXe4vjUdU,11113
|
|
70
70
|
coderouter/translation/convert.py,sha256=VV4kpu4dvLmGBmJfQnCLY5ryy_AnQcV2NuwjqRtaR8Q,52633
|
|
71
71
|
coderouter/translation/tool_repair.py,sha256=DGOKArM53yfVbIfiAveoul1vGq9NEKmjagf43Qh4IkM,57630
|
|
72
|
-
coderouter_cli-2.7.
|
|
73
|
-
coderouter_cli-2.7.
|
|
74
|
-
coderouter_cli-2.7.
|
|
75
|
-
coderouter_cli-2.7.
|
|
76
|
-
coderouter_cli-2.7.
|
|
72
|
+
coderouter_cli-2.7.9.dist-info/METADATA,sha256=foZQpq3qpofluaktg5dWZBeLH9j7RUrD08n3wNS33p4,15546
|
|
73
|
+
coderouter_cli-2.7.9.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
74
|
+
coderouter_cli-2.7.9.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
|
|
75
|
+
coderouter_cli-2.7.9.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
|
|
76
|
+
coderouter_cli-2.7.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|