agent-harness 0.5.6 → 0.5.8
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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +26 -0
- data/README.md +216 -3
- data/lib/agent_harness/authentication.rb +28 -9
- data/lib/agent_harness/command_executor.rb +453 -32
- data/lib/agent_harness/docker_command_executor.rb +23 -3
- data/lib/agent_harness/error_taxonomy.rb +10 -0
- data/lib/agent_harness/errors.rb +5 -0
- data/lib/agent_harness/orchestration/conductor.rb +40 -16
- data/lib/agent_harness/orchestration/provider_manager.rb +46 -18
- data/lib/agent_harness/provider_health_check.rb +243 -63
- data/lib/agent_harness/provider_runtime.rb +20 -3
- data/lib/agent_harness/providers/adapter.rb +717 -0
- data/lib/agent_harness/providers/aider.rb +59 -0
- data/lib/agent_harness/providers/anthropic.rb +98 -0
- data/lib/agent_harness/providers/base.rb +46 -10
- data/lib/agent_harness/providers/codex.rb +68 -9
- data/lib/agent_harness/providers/cursor.rb +90 -2
- data/lib/agent_harness/providers/gemini.rb +43 -0
- data/lib/agent_harness/providers/github_copilot.rb +38 -6
- data/lib/agent_harness/providers/kilocode.rb +39 -0
- data/lib/agent_harness/providers/mistral_vibe.rb +13 -0
- data/lib/agent_harness/providers/opencode.rb +77 -1
- data/lib/agent_harness/providers/registry.rb +446 -18
- data/lib/agent_harness/version.rb +1 -1
- data/lib/agent_harness.rb +105 -6
- metadata +21 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93f5a11872f464b3eb9834c849f076d9f393f9e1174bc9824952f4c217c9278f
|
|
4
|
+
data.tar.gz: 25bbd835fd6739f84597d0d5653443ecd238b0e89f20f5e0b20029cb5d8bb119
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '081955132b518b5d94498bfeb54ddd7641f1d6968fcb53debf143f7762a666edd40dde6a1369553a42c0dc4985cbff9cb41d7ade8dd727fa80572e7a7293cd2f'
|
|
7
|
+
data.tar.gz: 0345fc32a87b27ba001ae0a6cd26c5b1cc2765f64d7d81bad025b461182cdb0a814c8cdd031478b4e7dcf8bbe1f6b2638e3814cbf3c9537e038dc8bca1255a1f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.8](https://github.com/viamin/agent-harness/compare/agent-harness/v0.5.7...agent-harness/v0.5.8) (2026-04-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* 64: feat(installers): make Claude CLI installation/version support a first-class provider contract ([#78](https://github.com/viamin/agent-harness/issues/78)) ([0d83590](https://github.com/viamin/agent-harness/commit/0d83590dc9bb9bc7c8d621660bcd73b8eb613d43))
|
|
9
|
+
* 70: feat(installers): make Cursor agent CLI installation/version support a first-class provider contract ([#82](https://github.com/viamin/agent-harness/issues/82)) ([483e9be](https://github.com/viamin/agent-harness/commit/483e9be752de9548020135a86170d978d2a23ae8))
|
|
10
|
+
* 71: feat(installers): make Aider CLI installation/version support a first-class provider contract ([#84](https://github.com/viamin/agent-harness/issues/84)) ([3cfcc1c](https://github.com/viamin/agent-harness/commit/3cfcc1cac831060c12fd8a39130ee7bb9b048aa5))
|
|
11
|
+
* 74: feat(providers): expose provider capability/installability/auth metadata for downstream apps ([#88](https://github.com/viamin/agent-harness/issues/88)) ([4f9b3ba](https://github.com/viamin/agent-harness/commit/4f9b3ba6a53c830eaf53e9233e8df38970c52c8c))
|
|
12
|
+
|
|
13
|
+
## [0.5.7](https://github.com/viamin/agent-harness/compare/agent-harness/v0.5.6...agent-harness/v0.5.7) (2026-04-07)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* 59: fix(codex): replace invalid '--sandbox none' for externally sandboxed runs ([#89](https://github.com/viamin/agent-harness/issues/89)) ([6b0fac5](https://github.com/viamin/agent-harness/commit/6b0fac5e9b83c70c3a55004d9c5ad354a6d3dced))
|
|
19
|
+
* 60: feat: support per-request executor overrides for orchestrated send_message ([#87](https://github.com/viamin/agent-harness/issues/87)) ([7711770](https://github.com/viamin/agent-harness/commit/7711770ab3c9a6659171df1033038acf08b8d5a9))
|
|
20
|
+
* 61: feat: support idle-timeout and streaming execution hooks for long-running provider runs ([#90](https://github.com/viamin/agent-harness/issues/90)) ([adf7558](https://github.com/viamin/agent-harness/commit/adf75583994f78af9d6f5b44eba729b616b46f02))
|
|
21
|
+
* 62: feat: support per-request environment unsets for provider execution ([#76](https://github.com/viamin/agent-harness/issues/76)) ([2dfdb8e](https://github.com/viamin/agent-harness/commit/2dfdb8e6138f21ca0dd1a9e0b2d7cc17d1776612))
|
|
22
|
+
* 63: fix(copilot): align GithubCopilot provider with a real installable CLI contract ([#75](https://github.com/viamin/agent-harness/issues/75)) ([9e585e7](https://github.com/viamin/agent-harness/commit/9e585e79252ac8431fb99bc16bc9ebbecb83439e))
|
|
23
|
+
* 65: feat(installers): make Codex CLI installation/version support a first-class provider contract ([#79](https://github.com/viamin/agent-harness/issues/79)) ([a3f5849](https://github.com/viamin/agent-harness/commit/a3f5849db1bcfa162af98fa5339cf8b8f5314920))
|
|
24
|
+
* 66: feat(installers): make Gemini CLI installation/version support a first-class provider contract ([#80](https://github.com/viamin/agent-harness/issues/80)) ([e349ab6](https://github.com/viamin/agent-harness/commit/e349ab64601cb2fe9bafb488e6b4f96fbcbf012b))
|
|
25
|
+
* 67: feat(installers): make Kilocode CLI installation/version support a first-class provider contract ([#81](https://github.com/viamin/agent-harness/issues/81)) ([547baf5](https://github.com/viamin/agent-harness/commit/547baf5be93939a00cdac1e3d17824eb6dbfb324))
|
|
26
|
+
* 68: feat(installers): make OpenCode CLI installation/version support a first-class provider contract ([#83](https://github.com/viamin/agent-harness/issues/83)) ([21caaf4](https://github.com/viamin/agent-harness/commit/21caaf4e003c41a7fa127e7244c4a61abad7f1cb))
|
|
27
|
+
* 72: feat(providers): add first-class smoke-test/health-check execution contracts for CLI providers ([#85](https://github.com/viamin/agent-harness/issues/85)) ([7c0db3a](https://github.com/viamin/agent-harness/commit/7c0db3a5aa93e62e38240821dfb00b489ad3793b))
|
|
28
|
+
|
|
3
29
|
## [0.5.6](https://github.com/viamin/agent-harness/compare/agent-harness/v0.5.5...agent-harness/v0.5.6) (2026-03-30)
|
|
4
30
|
|
|
5
31
|
|
data/README.md
CHANGED
|
@@ -5,7 +5,7 @@ A unified Ruby interface for CLI-based AI coding agents like Claude Code, Cursor
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Unified Interface**: Single API for multiple AI coding agents
|
|
8
|
-
- **
|
|
8
|
+
- **9 Built-in Providers**: Claude Code, Cursor, Gemini CLI, GitHub Copilot, Codex, Aider, OpenCode, Kilocode, Mistral Vibe
|
|
9
9
|
- **Full Orchestration**: Provider switching, circuit breakers, rate limiting, and health monitoring
|
|
10
10
|
- **Flexible Configuration**: YAML, Ruby DSL, or environment variables
|
|
11
11
|
- **Token Tracking**: Monitor usage across providers for cost and limit management
|
|
@@ -106,7 +106,61 @@ end
|
|
|
106
106
|
| `:codex` | `codex` | OpenAI Codex CLI |
|
|
107
107
|
| `:aider` | `aider` | Aider coding assistant |
|
|
108
108
|
| `:opencode` | `opencode` | OpenCode CLI |
|
|
109
|
-
| `:kilocode` | `
|
|
109
|
+
| `:kilocode` | `kilo` | Kilocode CLI |
|
|
110
|
+
| `:mistral_vibe` | `mistral-vibe` | Mistral Vibe CLI |
|
|
111
|
+
|
|
112
|
+
### Provider Install Contracts
|
|
113
|
+
|
|
114
|
+
Provider classes can expose install metadata for downstream apps that build
|
|
115
|
+
their own agent images.
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
contract = AgentHarness.provider_install_contract(:gemini)
|
|
119
|
+
|
|
120
|
+
contract[:package_name]
|
|
121
|
+
# => "@google/gemini-cli"
|
|
122
|
+
|
|
123
|
+
contract[:default_version]
|
|
124
|
+
# => "0.35.3"
|
|
125
|
+
|
|
126
|
+
contract[:install_command]
|
|
127
|
+
# => ["npm", "install", "-g", "--ignore-scripts", "@google/gemini-cli@0.35.3"]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Cursor also exposes a first-class install contract for container/image builds.
|
|
131
|
+
The contract publishes checksums for both the installer script and the default
|
|
132
|
+
Linux x64 artifact so consumers can verify downloads independently:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
cursor_install = AgentHarness::Providers::Cursor.install_metadata
|
|
136
|
+
|
|
137
|
+
cursor_install
|
|
138
|
+
# => {
|
|
139
|
+
# source: {
|
|
140
|
+
# type: :shell_script,
|
|
141
|
+
# url: "https://cursor.com/install",
|
|
142
|
+
# resolved_version: "2026.03.30-a5d3e17",
|
|
143
|
+
# default_artifact_url: "https://downloads.cursor.com/lab/2026.03.30-a5d3e17/linux/x64/agent-cli-package.tar.gz"
|
|
144
|
+
# },
|
|
145
|
+
# checksum: {
|
|
146
|
+
# strategy: :sha256,
|
|
147
|
+
# targets: {
|
|
148
|
+
# script: { url: "https://cursor.com/install", value: "8371..." },
|
|
149
|
+
# artifacts: { "linux/x64" => { url: "https://downloads.cursor.com/...", value: "e0d4..." } }
|
|
150
|
+
# }
|
|
151
|
+
# },
|
|
152
|
+
# binary: {
|
|
153
|
+
# name: "cursor-agent",
|
|
154
|
+
# path: "$HOME/.local/bin/cursor-agent",
|
|
155
|
+
# suggested_global_path: "/usr/local/bin/cursor-agent"
|
|
156
|
+
# },
|
|
157
|
+
# version: {
|
|
158
|
+
# default: "latest",
|
|
159
|
+
# supported: "latest",
|
|
160
|
+
# command: ["cursor-agent", "--version"]
|
|
161
|
+
# }
|
|
162
|
+
# }
|
|
163
|
+
```
|
|
110
164
|
|
|
111
165
|
### Direct Provider Access
|
|
112
166
|
|
|
@@ -120,11 +174,157 @@ if AgentHarness::Providers::Registry.instance.get(:claude).available?
|
|
|
120
174
|
puts "Claude CLI is installed"
|
|
121
175
|
end
|
|
122
176
|
|
|
177
|
+
# Ask the harness which Claude CLI install contract it supports
|
|
178
|
+
contract = AgentHarness.install_contract(:claude)
|
|
179
|
+
puts contract[:install][:command]
|
|
180
|
+
# => "tmp_script=$(mktemp) && ... && bash \"$tmp_script\" 2.1.92"
|
|
181
|
+
puts contract[:install][:post_install_binary_path]
|
|
182
|
+
# => "$HOME/.local/bin/claude"
|
|
183
|
+
puts contract[:supported_versions][:default]
|
|
184
|
+
# => "2.1.92"
|
|
185
|
+
puts contract[:supported_versions][:requirement]
|
|
186
|
+
# => ">= 2.1.92, < 2.2.0"
|
|
187
|
+
|
|
123
188
|
# List all registered providers
|
|
124
189
|
AgentHarness::Providers::Registry.instance.all
|
|
125
|
-
# => [:claude, :cursor, :gemini, :github_copilot, :codex, :opencode, :kilocode, :aider]
|
|
190
|
+
# => [:claude, :cursor, :gemini, :github_copilot, :codex, :opencode, :kilocode, :aider, :mistral_vibe]
|
|
126
191
|
```
|
|
127
192
|
|
|
193
|
+
For Claude, the install contract is the first-class source of truth for:
|
|
194
|
+
|
|
195
|
+
- the official install recipe the current harness release expects
|
|
196
|
+
- the expected binary name and normalized PATH entry that recipe leaves behind
|
|
197
|
+
- the supported Claude CLI version boundary the current harness release validates (`default` plus the compatible version range)
|
|
198
|
+
|
|
199
|
+
### Provider Installation Contracts
|
|
200
|
+
|
|
201
|
+
Downstream apps can ask `agent-harness` for provider-specific CLI install
|
|
202
|
+
metadata instead of hardcoding package names, binary names, or supported
|
|
203
|
+
versions out-of-band.
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
contract = AgentHarness.provider_installation_contract(:kilocode, version: "7.1.3")
|
|
207
|
+
|
|
208
|
+
contract
|
|
209
|
+
# {
|
|
210
|
+
# source: { type: :npm, package: "@kilocode/cli" },
|
|
211
|
+
# install_command: ["npm", "install", "-g", "--ignore-scripts", "@kilocode/cli@7.1.3"],
|
|
212
|
+
# binary_name: "kilo",
|
|
213
|
+
# default_version: "7.1.3",
|
|
214
|
+
# supported_version_requirement: "= 7.1.3"
|
|
215
|
+
# }
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The Kilocode runtime adapter expects the `kilo` binary and executes prompts via
|
|
219
|
+
`kilo run ...`, so the install contract and runtime behavior stay aligned in
|
|
220
|
+
tests.
|
|
221
|
+
|
|
222
|
+
Providers that expose installation contracts can also be queried through the
|
|
223
|
+
generic API:
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
codex_install = AgentHarness.installation_contract(:codex)
|
|
227
|
+
aider_install = AgentHarness.installation_contract(:aider)
|
|
228
|
+
opencode_install = AgentHarness.installation_contract(:opencode)
|
|
229
|
+
|
|
230
|
+
opencode_install
|
|
231
|
+
# => {
|
|
232
|
+
# source: :npm,
|
|
233
|
+
# package_name: "opencode-ai",
|
|
234
|
+
# version: "1.3.2",
|
|
235
|
+
# version_requirement: [">= 1.3.2", "< 1.4.0"],
|
|
236
|
+
# binary_name: "opencode",
|
|
237
|
+
# install_command: ["npm", "install", "-g", "--ignore-scripts", "opencode-ai@1.3.2"]
|
|
238
|
+
# }
|
|
239
|
+
|
|
240
|
+
aider_install
|
|
241
|
+
# => {
|
|
242
|
+
# source: :uv_tool,
|
|
243
|
+
# bootstrap_package: "uv==0.8.17",
|
|
244
|
+
# package_name: "aider-chat",
|
|
245
|
+
# version: "0.86.2",
|
|
246
|
+
# binary_name: "aider",
|
|
247
|
+
# binary_path: "/usr/local/bin/aider",
|
|
248
|
+
# install_environment: {
|
|
249
|
+
# "UV_TOOL_BIN_DIR" => "/usr/local/bin",
|
|
250
|
+
# "UV_TOOL_DIR" => "/opt/uv/tools",
|
|
251
|
+
# "UV_PYTHON_INSTALL_DIR" => "/opt/uv/python"
|
|
252
|
+
# },
|
|
253
|
+
# bootstrap_commands: [
|
|
254
|
+
# ["python3", "-m", "pip", "install", "--no-cache-dir", "--break-system-packages", "uv==0.8.17"]
|
|
255
|
+
# ],
|
|
256
|
+
# install_command: ["uv", "tool", "install", "--force", "--python", "python3.12", "--with", "pip", "aider-chat==0.86.2"]
|
|
257
|
+
# }
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
For supported providers like Codex and Aider, the install contract tracks
|
|
261
|
+
the CLI version supported by the current `agent-harness` release. The
|
|
262
|
+
contract includes bootstrap requirements, install command shape, and the
|
|
263
|
+
expected runtime binary, and the provider specs assert that runtime
|
|
264
|
+
expectations remain aligned with the published install metadata.
|
|
265
|
+
|
|
266
|
+
### Provider Metadata
|
|
267
|
+
|
|
268
|
+
Downstream apps can also query a consolidated provider metadata contract for
|
|
269
|
+
configuration UIs and policy decisions.
|
|
270
|
+
|
|
271
|
+
The following example shows how to retrieve metadata for the Anthropic provider:
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
metadata = AgentHarness.provider_metadata(:anthropic)
|
|
275
|
+
|
|
276
|
+
metadata
|
|
277
|
+
# => {
|
|
278
|
+
# provider: :claude,
|
|
279
|
+
# canonical_provider: :claude,
|
|
280
|
+
# aliases: [:anthropic],
|
|
281
|
+
# auth: {
|
|
282
|
+
# default_mode: :oauth,
|
|
283
|
+
# supported_modes: [:oauth],
|
|
284
|
+
# service: :anthropic,
|
|
285
|
+
# api_family: :anthropic
|
|
286
|
+
# },
|
|
287
|
+
# runtime: {
|
|
288
|
+
# interface: :cli,
|
|
289
|
+
# requires_cli: true,
|
|
290
|
+
# installable: false,
|
|
291
|
+
# installation: nil,
|
|
292
|
+
# supports_mcp: true,
|
|
293
|
+
# supports_dangerous_mode: true
|
|
294
|
+
# },
|
|
295
|
+
# health_check: {
|
|
296
|
+
# supports_registry_checks: true,
|
|
297
|
+
# auth_check_supported: true,
|
|
298
|
+
# lightweight: true
|
|
299
|
+
# },
|
|
300
|
+
# identity: {
|
|
301
|
+
# bot_usernames: ["claude", "anthropic"]
|
|
302
|
+
# }
|
|
303
|
+
# }
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
To enumerate the full catalog:
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
AgentHarness.provider_metadata_catalog
|
|
310
|
+
# => { claude: {...}, cursor: {...}, gemini: {...}, ... }
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Provider metadata is cached so repeated catalog reads stay cheap.
|
|
314
|
+
Pass `refresh: true` to rebuild metadata and re-run live availability checks when needed:
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
AgentHarness.provider_metadata(:anthropic, refresh: true)
|
|
318
|
+
AgentHarness.provider_metadata_catalog(refresh: true)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
For providers with install contracts, the metadata tracks the CLI version
|
|
322
|
+
supported by the current `agent-harness` release, and the runtime adapter
|
|
323
|
+
tests assert that the expected binary remains aligned with that contract.
|
|
324
|
+
`runtime[:installation]` is normalized to a stable shape with
|
|
325
|
+
`source_type`, `package_name`, version fields, and install commands so
|
|
326
|
+
downstream apps do not need provider-specific branching.
|
|
327
|
+
|
|
128
328
|
### Custom Providers
|
|
129
329
|
|
|
130
330
|
```ruby
|
|
@@ -138,6 +338,19 @@ class MyProvider < AgentHarness::Providers::Base
|
|
|
138
338
|
"my-cli"
|
|
139
339
|
end
|
|
140
340
|
|
|
341
|
+
def install_contract(version: "1.2.3")
|
|
342
|
+
{
|
|
343
|
+
provider: provider_name,
|
|
344
|
+
source_type: :npm,
|
|
345
|
+
package_name: "@acme/my-cli",
|
|
346
|
+
supported_version_requirement: Gem::Requirement.new("~> 1.2"),
|
|
347
|
+
default_version: "1.2.3",
|
|
348
|
+
resolved_version: version,
|
|
349
|
+
binary_name: binary_name,
|
|
350
|
+
install_command: ["npm", "install", "-g", "@acme/my-cli@#{version}"]
|
|
351
|
+
}
|
|
352
|
+
end
|
|
353
|
+
|
|
141
354
|
def available?
|
|
142
355
|
system("which my-cli > /dev/null 2>&1")
|
|
143
356
|
end
|
|
@@ -94,15 +94,26 @@ module AgentHarness
|
|
|
94
94
|
|
|
95
95
|
def resolve_provider(provider_name)
|
|
96
96
|
klass = Providers::Registry.instance.get(provider_name)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
executor:
|
|
104
|
-
|
|
105
|
-
|
|
97
|
+
canonical_name = Providers::Registry.instance.canonical_name(provider_name)
|
|
98
|
+
config = provider_config_for(provider_name, canonical_name: canonical_name)
|
|
99
|
+
executor = AgentHarness.configuration.command_executor
|
|
100
|
+
logger = AgentHarness.logger
|
|
101
|
+
|
|
102
|
+
provider = if klass.respond_to?(:build_provider_instance, true)
|
|
103
|
+
klass.send(:build_provider_instance, config: config, executor: executor, logger: logger)
|
|
104
|
+
else
|
|
105
|
+
klass.new(config: config, executor: executor, logger: logger)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Ensure the executor is available even when the provider constructor
|
|
109
|
+
# accepts only a subset of keywords (e.g. config: only).
|
|
110
|
+
if provider.respond_to?(:executor=) && provider.executor.nil?
|
|
111
|
+
provider.executor = executor
|
|
112
|
+
elsif !provider.respond_to?(:executor)
|
|
113
|
+
provider.define_singleton_method(:executor) { executor }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
provider
|
|
106
117
|
rescue ConfigurationError
|
|
107
118
|
raise ProviderNotFoundError, "Unknown provider: #{provider_name}"
|
|
108
119
|
end
|
|
@@ -152,6 +163,14 @@ module AgentHarness
|
|
|
152
163
|
"https://claude.ai/oauth/authorize"
|
|
153
164
|
end
|
|
154
165
|
|
|
166
|
+
def provider_config_for(requested_name, canonical_name:)
|
|
167
|
+
requested_key = requested_name.to_sym
|
|
168
|
+
canonical_key = canonical_name.to_sym
|
|
169
|
+
|
|
170
|
+
AgentHarness.configuration.providers[requested_key] ||
|
|
171
|
+
AgentHarness.configuration.providers[canonical_key]
|
|
172
|
+
end
|
|
173
|
+
|
|
155
174
|
def refresh_claude_auth(token: nil)
|
|
156
175
|
raise ArgumentError, "token must be a non-empty string" unless token.is_a?(String) && !token.strip.empty?
|
|
157
176
|
|