coderouter-cli 2.7.10__py3-none-any.whl → 2.8.1__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.
@@ -2,14 +2,79 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from typing import TYPE_CHECKING
6
+
5
7
  from coderouter.adapters.anthropic_native import AnthropicAdapter
6
8
  from coderouter.adapters.base import BaseAdapter
7
9
  from coderouter.adapters.openai_compat import OpenAICompatAdapter
8
10
  from coderouter.config.schemas import ProviderConfig
11
+ from coderouter.logging import get_logger
12
+
13
+ if TYPE_CHECKING:
14
+ from coderouter.plugins.registry import PluginRegistry
15
+
16
+ logger = get_logger(__name__)
17
+
18
+ # in-core kinds, in resolution order. Kept as a tuple (not derived from
19
+ # the if-chain below) so the "Unknown adapter kind" error message and
20
+ # the plugin-shadow guard have a single source of truth.
21
+ _IN_CORE_KINDS: tuple[str, ...] = ("openai_compat", "anthropic", "agent_cli")
22
+
23
+ # Module-level flag so the Phase 2b in-core agent_cli deprecation warning
24
+ # (docs/designs/agent-cli-plugin-extraction.md §5.1) fires at most once per
25
+ # process, regardless of how many agent_cli providers are built.
26
+ _agent_cli_deprecation_logged = False
27
+
28
+
29
+ def _plugin_kinds(plugin_registry: PluginRegistry | None) -> list[str]:
30
+ """``kind`` values served by enabled adapter plugins, for error text."""
31
+ if plugin_registry is None:
32
+ return []
33
+ return [factory.kind for factory in plugin_registry.adapters]
34
+
35
+
36
+ def _warn_agent_cli_in_core_deprecated_once() -> None:
37
+ """Log ``agent-cli-in-core-deprecated`` once per process (§5.1).
38
+
39
+ Only called when the in-core ``agent_cli`` branch is taken AND an
40
+ adapter plugin has also registered a ``kind="agent_cli"`` factory —
41
+ i.e. the operator already has ``coderouter-plugin-agents`` installed
42
+ and enabled, but Core's in-core copy still wins resolution order
43
+ (§3.2) during the Phase 2b migration window. Nudges them toward the
44
+ plugin path ahead of its Phase 2c removal from Core.
45
+ """
46
+ global _agent_cli_deprecation_logged
47
+ if _agent_cli_deprecation_logged:
48
+ return
49
+ _agent_cli_deprecation_logged = True
50
+ logger.warning(
51
+ "agent-cli-in-core-deprecated",
52
+ extra={
53
+ "hint": (
54
+ "an 'agent_cli' adapter plugin is installed and enabled, "
55
+ "but Core's in-core agent_cli implementation still served "
56
+ "this request (in-core wins resolution during the Phase 2b "
57
+ "migration window). The in-core copy will be removed in "
58
+ "Phase 2c — no action is required yet, but new setups "
59
+ "should already be relying on the plugin path "
60
+ "(coderouter-plugin-agents)."
61
+ ),
62
+ },
63
+ )
64
+
9
65
 
66
+ def build_adapter(
67
+ provider: ProviderConfig,
68
+ plugin_registry: PluginRegistry | None = None,
69
+ ) -> BaseAdapter:
70
+ """Construct an adapter from a ProviderConfig.
10
71
 
11
- def build_adapter(provider: ProviderConfig) -> BaseAdapter:
12
- """Construct an adapter from a ProviderConfig."""
72
+ Resolution order (docs/designs/agent-cli-plugin-extraction.md §3.2):
73
+ in-core kinds first, then plugin-provided kinds, then a fail-fast
74
+ error. In-core kinds are checked first so a plugin can never shadow
75
+ a kind Core itself guarantees (``openai_compat`` / ``anthropic`` /,
76
+ during the Phase 2b migration window, ``agent_cli``).
77
+ """
13
78
  if provider.kind == "openai_compat":
14
79
  return OpenAICompatAdapter(provider)
15
80
  if provider.kind == "anthropic":
@@ -19,5 +84,19 @@ def build_adapter(provider: ProviderConfig) -> BaseAdapter:
19
84
  # os plumbing) is only pulled in when a config actually uses it.
20
85
  from coderouter.adapters.agent_cli import AgentCliAdapter
21
86
 
87
+ if plugin_registry is not None and any(
88
+ factory.kind == "agent_cli" for factory in plugin_registry.adapters
89
+ ):
90
+ _warn_agent_cli_in_core_deprecated_once()
22
91
  return AgentCliAdapter(provider)
23
- raise ValueError(f"Unknown adapter kind: {provider.kind!r}")
92
+ if plugin_registry is not None:
93
+ for factory in plugin_registry.adapters:
94
+ if factory.kind == provider.kind:
95
+ return factory.build(provider)
96
+ raise ValueError(
97
+ f"Unknown adapter kind {provider.kind!r}. "
98
+ f"in-core kinds: {', '.join(_IN_CORE_KINDS)}; "
99
+ f"plugin-provided kinds: {_plugin_kinds(plugin_registry)}. "
100
+ f"If a plugin should provide {provider.kind!r}, ensure it is "
101
+ f"installed AND listed in plugins.enabled."
102
+ )
@@ -342,13 +342,23 @@ class ProviderConfig(BaseModel):
342
342
  model_config = ConfigDict(extra="forbid")
343
343
 
344
344
  name: str = Field(..., description="Unique identifier used in profiles.yaml")
345
- kind: Literal["openai_compat", "anthropic", "agent_cli"] = Field(
345
+ # v2.8.0: widened from Literal["openai_compat", "anthropic", "agent_cli"]
346
+ # to str so a provider can name a kind served by an adapter plugin
347
+ # (docs/designs/agent-cli-plugin-extraction.md §3). Config loading
348
+ # happens before plugin discovery (coderouter/ingress/app.py
349
+ # ``load_config`` then ``discover_and_load``), so pydantic can't know
350
+ # the full set of valid kinds at this point — an unknown kind still
351
+ # fails fast, just one step later, in ``build_adapter`` when the
352
+ # engine builds its adapter cache at startup.
353
+ kind: str = Field(
346
354
  default="openai_compat",
347
355
  description=(
348
356
  "Adapter type. 'openai_compat' covers llama.cpp / Ollama / "
349
357
  "OpenRouter / LM Studio / Together / Groq. 'anthropic' is the "
350
358
  "native Anthropic Messages API passthrough (v0.3.x). 'agent_cli' "
351
- "invokes an external coding-agent CLI one-shot (see AgentCliConfig)."
359
+ "invokes an external coding-agent CLI one-shot (see AgentCliConfig). "
360
+ "Other values must be served by an adapter plugin listed in "
361
+ "plugins.enabled."
352
362
  ),
353
363
  )
354
364
  # base_url is required for HTTP-backed adapters (openai_compat / anthropic)
@@ -1,11 +1,13 @@
1
- """Plugin SDK Protocol contracts (v2.3.0).
1
+ """Plugin SDK Protocol contracts (v2.3.0, Adapter wired in v2.8.0).
2
2
 
3
- Six extension points are defined here. Two are wired into the engine
4
- in v2.3.0 (:class:`InputFilter`, :class:`Observer`); four are
5
- Protocol-only (:class:`Frontend`, :class:`Guard`, :class:`OutputFilter`,
6
- :class:`Adapter`) and will get engine integration when a real plugin
7
- drives the requirement see ``docs/inside/plugin-architecture-draft.md``
8
- §3 for the full design rationale.
3
+ Six extension points are defined here. Three are wired into the engine
4
+ (:class:`InputFilter`, :class:`Observer` since v2.3.0; :class:`Adapter`
5
+ since v2.8.0 see ``docs/designs/agent-cli-plugin-extraction.md`` §2);
6
+ three remain Protocol-only (:class:`Frontend`, :class:`Guard`,
7
+ :class:`OutputFilter`) and will get engine integration when a real
8
+ plugin drives the requirement see
9
+ ``docs/inside/plugin-architecture-draft.md`` §3 for the full design
10
+ rationale.
9
11
 
10
12
  Why declare contracts ahead of integration? It lets a plugin author
11
13
  build against the SDK *now* (and ship a working ``coderouter.frontend``
@@ -14,9 +16,11 @@ backward-incompatible Protocol revision later. ``runtime_checkable``
14
16
  is used so :func:`isinstance` checks work in the loader for clearer
15
17
  error messages.
16
18
 
17
- All hooks are async. Failures must NEVER block the engine response —
18
- the engine wraps every hook call in try/except and degrades gracefully
19
- (see ``coderouter/routing/fallback.py`` integration site).
19
+ InputFilter / Observer are async and run repeatedly on the hot path;
20
+ failures must NEVER block the engine response, so the engine wraps
21
+ every hook call in try/except and degrades gracefully (see
22
+ ``coderouter/routing/fallback.py`` integration site). Adapter is
23
+ different in shape — see its docstring below.
20
24
  """
21
25
  from __future__ import annotations
22
26
 
@@ -25,7 +29,8 @@ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
25
29
  if TYPE_CHECKING:
26
30
  # Avoid circular imports at runtime — Protocol typing only needs
27
31
  # these for documentation and static analysis.
28
- from coderouter.config.schemas import CodeRouterConfig
32
+ from coderouter.adapters.base import BaseAdapter
33
+ from coderouter.config.schemas import CodeRouterConfig, ProviderConfig
29
34
  from coderouter.translation.anthropic import (
30
35
  AnthropicRequest,
31
36
  AnthropicResponse,
@@ -93,6 +98,44 @@ class Observer(Protocol):
93
98
  async def on_event(self, event_type: str, payload: dict[str, Any]) -> None: ...
94
99
 
95
100
 
101
+ # ====================================================================
102
+ # Adapter hook (engine integration in v2.8.0)
103
+ # ====================================================================
104
+
105
+
106
+ @runtime_checkable
107
+ class Adapter(Protocol):
108
+ """New ``kind`` value in providers.yaml, backed by a plugin.
109
+
110
+ The plugin declares the ``kind`` string it serves and a factory
111
+ that turns a matching :class:`~coderouter.config.schemas.ProviderConfig`
112
+ into a :class:`~coderouter.adapters.base.BaseAdapter` instance —
113
+ the same surface :func:`coderouter.adapters.registry.build_adapter`
114
+ uses for in-core kinds, so the engine treats plugin adapters
115
+ indistinguishably from built-ins once registered. See
116
+ ``docs/designs/agent-cli-plugin-extraction.md`` §2 for the design
117
+ rationale.
118
+
119
+ Shape-wise this hook differs from :class:`InputFilter` /
120
+ :class:`Observer`: those are instances the engine calls repeatedly
121
+ on the hot path, so they're async. An adapter provider is instead
122
+ a **factory** consulted once per provider at engine startup (and
123
+ again if the provider is re-registered at runtime) — construction
124
+ is I/O-free, so :meth:`build` is synchronous, matching
125
+ ``build_adapter``.
126
+ """
127
+
128
+ name: str
129
+ # The ``kind`` value this plugin serves in providers.yaml. Distinct
130
+ # from ``name`` (the plugin's own identifier, used in
131
+ # ``plugins.enabled`` / logs) — kept 1:1 for now; a future
132
+ # ``kinds: tuple[str, ...]`` can generalize this if a real plugin
133
+ # needs to serve more than one kind.
134
+ kind: str
135
+
136
+ def build(self, config: ProviderConfig) -> BaseAdapter: ...
137
+
138
+
96
139
  # ====================================================================
97
140
  # Future hooks (Protocol-only, engine integration in v2.4+)
98
141
  # ====================================================================
@@ -151,18 +194,3 @@ class OutputFilter(Protocol):
151
194
  async def transform(
152
195
  self, response: AnthropicResponse
153
196
  ) -> AnthropicResponse: ...
154
-
155
-
156
- @runtime_checkable
157
- class Adapter(Protocol):
158
- """New ``kind`` value in providers.yaml (e.g. ``bedrock-native``).
159
-
160
- Plugins implement the same async surface as
161
- :class:`coderouter.adapters.base.BaseAdapter` so the engine can
162
- treat them indistinguishably from built-in adapters once the
163
- loader registers the new ``kind`` mapping.
164
-
165
- Not yet integrated — Protocol contract only.
166
- """
167
-
168
- name: str
@@ -38,10 +38,14 @@ if TYPE_CHECKING:
38
38
 
39
39
  logger = get_logger(__name__)
40
40
 
41
- # Active hook groups in v2.3.0. The engine wires these into the
42
- # request flow; plugins targeting them will see their methods called
43
- # at runtime.
44
- PLUGIN_GROUPS_V2_3: tuple[str, ...] = ("input_filter", "observer")
41
+ # Active hook groups. The engine wires these into the request flow;
42
+ # plugins targeting them will see their methods called at runtime.
43
+ # ``input_filter`` / ``observer`` since v2.3.0; ``adapter`` joined in
44
+ # v2.8.0 (docs/designs/agent-cli-plugin-extraction.md §2.4) its
45
+ # factories are consulted from ``build_adapter``, not iterated on the
46
+ # hot path like the other two, but it goes through the same
47
+ # discover-then-enable-gate machinery below.
48
+ PLUGIN_GROUPS_V2_3: tuple[str, ...] = ("input_filter", "observer", "adapter")
45
49
 
46
50
  # Hook groups whose Protocol contracts are stable but whose engine
47
51
  # integration is deferred. Listing them here means a plugin author
@@ -51,7 +55,6 @@ PLUGIN_GROUPS_FUTURE: tuple[str, ...] = (
51
55
  "frontend",
52
56
  "guard",
53
57
  "output_filter",
54
- "adapter",
55
58
  )
56
59
 
57
60
 
@@ -20,8 +20,8 @@ from typing import Any
20
20
  class PluginRegistry:
21
21
  """In-memory registry of loaded plugin instances grouped by hook kind.
22
22
 
23
- Use the typed ``input_filters`` / ``observers`` properties on the
24
- hot path; plugin code should not iterate ``_by_group`` directly.
23
+ Use the typed ``input_filters`` / ``observers`` / ``adapters``
24
+ properties; plugin code should not iterate ``_by_group`` directly.
25
25
  """
26
26
 
27
27
  def __init__(self) -> None:
@@ -65,6 +65,17 @@ class PluginRegistry:
65
65
  """Plugins registered as ``coderouter.observer``."""
66
66
  return list(self._by_group.get("observer", ()))
67
67
 
68
+ @property
69
+ def adapters(self) -> list[Any]:
70
+ """Plugins registered as ``coderouter.adapter`` (kind factories).
71
+
72
+ Unlike ``input_filters`` / ``observers``, these aren't called
73
+ repeatedly on the hot path — :func:`coderouter.adapters.registry.build_adapter`
74
+ consults this list once per provider to resolve a plugin-served
75
+ ``kind`` (v2.8.0, docs/designs/agent-cli-plugin-extraction.md §2.4).
76
+ """
77
+ return list(self._by_group.get("adapter", ()))
78
+
68
79
  def is_empty(self) -> bool:
69
80
  """True iff no plugin instance has been registered, in any group.
70
81
 
@@ -1104,9 +1104,13 @@ class FallbackEngine:
1104
1104
  # via ``add_done_callback(_observer_tasks.discard)`` in
1105
1105
  # :meth:`_fanout_observers`.
1106
1106
  self._observer_tasks: set[asyncio.Task[None]] = set()
1107
- # Cache adapters so we don't re-instantiate per request
1107
+ # Cache adapters so we don't re-instantiate per request. v2.8.0:
1108
+ # pass the plugin registry through so a provider whose ``kind``
1109
+ # is served by an enabled adapter plugin resolves via
1110
+ # ``build_adapter``'s plugin lookup (agent-cli-plugin-extraction
1111
+ # §3.5) instead of raising "Unknown adapter kind".
1108
1112
  self._adapters: dict[str, BaseAdapter] = {
1109
- p.name: build_adapter(p) for p in config.providers
1113
+ p.name: build_adapter(p, self._plugin_registry) for p in config.providers
1110
1114
  }
1111
1115
  # v1.9-C: per-process adaptive routing adjuster (rolling-window
1112
1116
  # latency + error-rate observations, debounced rank changes).
@@ -1217,7 +1221,7 @@ class FallbackEngine:
1217
1221
  break
1218
1222
  if not replaced:
1219
1223
  self.config.providers.append(provider)
1220
- self._adapters[provider.name] = build_adapter(provider)
1224
+ self._adapters[provider.name] = build_adapter(provider, self._plugin_registry)
1221
1225
 
1222
1226
  try:
1223
1227
  chain = self.config.profile_by_name(profile_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coderouter-cli
3
- Version: 2.7.10
3
+ Version: 2.8.1
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
@@ -20,12 +20,12 @@ coderouter/adapters/agent_cli.py,sha256=0g0V92drEQLJACXq-Dn0hE-J6e3vmsuBr5UdnAkX
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
23
- coderouter/adapters/registry.py,sha256=A2LNhp70LOSNodM_iKO6EL-MBadB3kli-YPWRe1U7pQ,979
23
+ coderouter/adapters/registry.py,sha256=hwMhdBg-wurjJKa4MJIuDoMn1ClOOsorGppJ-s5pnF0,4311
24
24
  coderouter/config/__init__.py,sha256=FODEn74fN-qZnt4INPSHswqhOlEgpL6-_onxsitSx8g,274
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=sizuuxBgavGWaxBi7MtbLi0al-Ba72H7iVgI7CRYgMY,88835
28
+ coderouter/config/schemas.py,sha256=AJIJ-zrEgI5czTlcD6YxWQe9rnJFsr6AcKSwwlCaLk8,89447
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
@@ -50,15 +50,15 @@ coderouter/metrics/__init__.py,sha256=7Es351DPS7yLM0yVF_F0eesmiD83n7Zzhie44chht3
50
50
  coderouter/metrics/collector.py,sha256=wdGuX5yOBWEHCz1KVIfaUBZP5NXW3U34yyYlLGwgoCE,61212
51
51
  coderouter/metrics/prometheus.py,sha256=THYkvrcpQK3ZNW-BV0h3O-PoT2ppNZ0dapQuu1nyHwE,24410
52
52
  coderouter/plugins/__init__.py,sha256=76hMLe5dV_ilripHXzWn3HSYoIALjzlw4EJVyI-GyIM,1974
53
- coderouter/plugins/base.py,sha256=n9hsck2NCSqi6oeHIumKC5zhQ8JGwCXUz7J5AZQCQss,5772
54
- coderouter/plugins/loader.py,sha256=xAIf6bIuth0QXCzwxO_ja6aSUlLzIqZNbrbQNJDgSE8,6841
55
- coderouter/plugins/registry.py,sha256=Tx0QHJHozZ5LTUliGylBdNVcdzHTBV0nedCUwGlbLMM,3236
53
+ coderouter/plugins/base.py,sha256=HACgppdfPyQRlHWXgwSvUeImaG_KRB0uLjhhQr1Ohyk,7267
54
+ coderouter/plugins/loader.py,sha256=KLnKgO6_GE7OJulwo292cTYPvxqWZlLL8S0k_O3ZDcg,7139
55
+ coderouter/plugins/registry.py,sha256=8UDmRpHnRdB-ES_xpprvrvbDlhIhonOvFFXobR6MGJA,3739
56
56
  coderouter/routing/__init__.py,sha256=g2vhutbozRx5QBThReqwPN3imk5qXdpDiaogILd3IRc,257
57
57
  coderouter/routing/adaptive.py,sha256=G2o377twGSjbUh65wiIFx6klnpFGjsD_nI3oDvcBwhY,21257
58
58
  coderouter/routing/auto_router.py,sha256=y4v0c8u5F9f98Vmhx1vRcKPiOgAvpzbFqr6TIh058h0,13341
59
59
  coderouter/routing/budget.py,sha256=PblmVKJGs_BwNa9uDHAA8hmZ4XIVKv38mHAeU0V3OMs,8451
60
60
  coderouter/routing/capability.py,sha256=rRQhzTgQYFTFDNYcSLKvq3xjdw6B1w7T-3jS7PTMI14,27864
61
- coderouter/routing/fallback.py,sha256=WNfDT9XDfKk584rQYHwSesq8-n3Axvp4j_e7qQ5ghuQ,144789
61
+ coderouter/routing/fallback.py,sha256=x3Y_LHA1NZhW4L1e7E0okuYpQn6-HNw5An7f7HX547M,145111
62
62
  coderouter/state/__init__.py,sha256=XoGcPmmBQSiZWML2S0juSveQ78xfhtdeCliNnVyzu7E,1088
63
63
  coderouter/state/audit_log.py,sha256=n7vuDsTfd5iuNUJhlRQPakJ2tMSsT9EfgPCzs1GEaac,10941
64
64
  coderouter/state/replay.py,sha256=Z_YHKroTKZdrL8qObFxcoLOAQWWXZvXFdLfxzvBhEJg,11230
@@ -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.10.dist-info/METADATA,sha256=4AzX8XCERv5Hd1iywyybjR1gjsyfBh72bFF24YLH85U,15547
73
- coderouter_cli-2.7.10.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
74
- coderouter_cli-2.7.10.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
75
- coderouter_cli-2.7.10.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
76
- coderouter_cli-2.7.10.dist-info/RECORD,,
72
+ coderouter_cli-2.8.1.dist-info/METADATA,sha256=qRLxuyz0gjzAwHy4eSWMXl_E23g7fwG6AYuVonimdlg,15546
73
+ coderouter_cli-2.8.1.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
74
+ coderouter_cli-2.8.1.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
75
+ coderouter_cli-2.8.1.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
76
+ coderouter_cli-2.8.1.dist-info/RECORD,,