@workweave/router 0.2.3 → 0.2.5
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.
- package/cc-statusline.sh +48 -33
- package/install.sh +134 -112
- package/opencode-weave/README.md +58 -39
- package/opencode-weave/src/index.ts +315 -98
- package/package.json +1 -1
- package/uninstall.sh +14 -12
package/cc-statusline.sh
CHANGED
|
@@ -130,14 +130,18 @@ normalize_model() {
|
|
|
130
130
|
prices='{
|
|
131
131
|
"input": {
|
|
132
132
|
"claude-fable-5": 0.01,
|
|
133
|
-
"claude-haiku-4-5": 0.
|
|
133
|
+
"claude-haiku-4-5": 0.001,
|
|
134
|
+
"claude-opus-4-0": 0.015,
|
|
135
|
+
"claude-opus-4-1": 0.015,
|
|
136
|
+
"claude-opus-4-5": 0.005,
|
|
134
137
|
"claude-opus-4-6": 0.005,
|
|
135
138
|
"claude-opus-4-7": 0.005,
|
|
136
139
|
"claude-opus-4-8": 0.005,
|
|
137
140
|
"claude-sonnet-4-5": 0.003,
|
|
138
141
|
"claude-sonnet-4-6": 0.003,
|
|
139
|
-
"
|
|
140
|
-
"deepseek/deepseek-v4-
|
|
142
|
+
"claude-sonnet-5": 0.003,
|
|
143
|
+
"deepseek/deepseek-v4-flash": 0.0001134,
|
|
144
|
+
"deepseek/deepseek-v4-pro": 0.001318,
|
|
141
145
|
"gemini-2.0-flash": 0.0001,
|
|
142
146
|
"gemini-2.0-flash-lite": 0.000075,
|
|
143
147
|
"gemini-2.5-flash": 0.0003,
|
|
@@ -148,6 +152,7 @@ prices='{
|
|
|
148
152
|
"gemini-3.1-flash-lite-preview": 0.0001,
|
|
149
153
|
"gemini-3.1-pro-preview": 0.002,
|
|
150
154
|
"gemini-3.5-flash": 0.0015,
|
|
155
|
+
"google/gemma-4-26b-a4b-it": 0.00015,
|
|
151
156
|
"gpt-4.1": 0.002,
|
|
152
157
|
"gpt-4.1-mini": 0.0004,
|
|
153
158
|
"gpt-4.1-nano": 0.0001,
|
|
@@ -157,10 +162,10 @@ prices='{
|
|
|
157
162
|
"gpt-5-chat": 0.0025,
|
|
158
163
|
"gpt-5-mini": 0.0005,
|
|
159
164
|
"gpt-5-nano": 0.0001,
|
|
160
|
-
"gpt-5.4": 0.
|
|
161
|
-
"gpt-5.4-mini": 0.
|
|
162
|
-
"gpt-5.4-nano": 0.
|
|
163
|
-
"gpt-5.4-pro": 0.
|
|
165
|
+
"gpt-5.4": 0.0025,
|
|
166
|
+
"gpt-5.4-mini": 0.00075,
|
|
167
|
+
"gpt-5.4-nano": 0.0002,
|
|
168
|
+
"gpt-5.4-pro": 0.03,
|
|
164
169
|
"gpt-5.5": 0.005,
|
|
165
170
|
"gpt-5.5-mini": 0.0005,
|
|
166
171
|
"gpt-5.5-nano": 0.00015,
|
|
@@ -177,22 +182,27 @@ prices='{
|
|
|
177
182
|
"qwen/qwen3-coder-next": 0.0005,
|
|
178
183
|
"qwen/qwen3-next-80b-a3b-instruct": 0.00015,
|
|
179
184
|
"qwen/qwen3.5-flash-02-23": 0.00005,
|
|
180
|
-
"qwen/qwen3.6-35b-a3b": 0.
|
|
185
|
+
"qwen/qwen3.6-35b-a3b": 0.000172,
|
|
181
186
|
"qwen/qwen3.7-plus": 0.0004,
|
|
182
187
|
"xiaomi/mimo-v2.5-pro": 0.001,
|
|
183
|
-
"z-ai/glm-5": 0.
|
|
184
|
-
"z-ai/glm-5.1": 0.
|
|
188
|
+
"z-ai/glm-5": 0.001,
|
|
189
|
+
"z-ai/glm-5.1": 0.0014,
|
|
190
|
+
"z-ai/glm-5.2": 0.0014
|
|
185
191
|
},
|
|
186
192
|
"output": {
|
|
187
193
|
"claude-fable-5": 0.05,
|
|
188
|
-
"claude-haiku-4-5": 0.
|
|
194
|
+
"claude-haiku-4-5": 0.005,
|
|
195
|
+
"claude-opus-4-0": 0.075,
|
|
196
|
+
"claude-opus-4-1": 0.075,
|
|
197
|
+
"claude-opus-4-5": 0.025,
|
|
189
198
|
"claude-opus-4-6": 0.025,
|
|
190
199
|
"claude-opus-4-7": 0.025,
|
|
191
200
|
"claude-opus-4-8": 0.025,
|
|
192
201
|
"claude-sonnet-4-5": 0.015,
|
|
193
202
|
"claude-sonnet-4-6": 0.015,
|
|
194
|
-
"
|
|
195
|
-
"deepseek/deepseek-v4-
|
|
203
|
+
"claude-sonnet-5": 0.015,
|
|
204
|
+
"deepseek/deepseek-v4-flash": 0.0002791,
|
|
205
|
+
"deepseek/deepseek-v4-pro": 0.0026361,
|
|
196
206
|
"gemini-2.0-flash": 0.0004,
|
|
197
207
|
"gemini-2.0-flash-lite": 0.0003,
|
|
198
208
|
"gemini-2.5-flash": 0.0012,
|
|
@@ -203,6 +213,7 @@ prices='{
|
|
|
203
213
|
"gemini-3.1-flash-lite-preview": 0.0004,
|
|
204
214
|
"gemini-3.1-pro-preview": 0.008,
|
|
205
215
|
"gemini-3.5-flash": 0.009,
|
|
216
|
+
"google/gemma-4-26b-a4b-it": 0.0006,
|
|
206
217
|
"gpt-4.1": 0.008,
|
|
207
218
|
"gpt-4.1-mini": 0.0016,
|
|
208
219
|
"gpt-4.1-nano": 0.0004,
|
|
@@ -212,14 +223,14 @@ prices='{
|
|
|
212
223
|
"gpt-5-chat": 0.01,
|
|
213
224
|
"gpt-5-mini": 0.002,
|
|
214
225
|
"gpt-5-nano": 0.0004,
|
|
215
|
-
"gpt-5.4": 0.
|
|
216
|
-
"gpt-5.4-mini": 0.
|
|
217
|
-
"gpt-5.4-nano": 0.
|
|
218
|
-
"gpt-5.4-pro": 0.
|
|
219
|
-
"gpt-5.5": 0.
|
|
226
|
+
"gpt-5.4": 0.015,
|
|
227
|
+
"gpt-5.4-mini": 0.0045,
|
|
228
|
+
"gpt-5.4-nano": 0.00125,
|
|
229
|
+
"gpt-5.4-pro": 0.18,
|
|
230
|
+
"gpt-5.5": 0.03,
|
|
220
231
|
"gpt-5.5-mini": 0.0025,
|
|
221
232
|
"gpt-5.5-nano": 0.0006,
|
|
222
|
-
"gpt-5.5-pro": 0.
|
|
233
|
+
"gpt-5.5-pro": 0.18,
|
|
223
234
|
"minimax/minimax-m2.7": 0.0012,
|
|
224
235
|
"minimax/minimax-m3": 0.0012,
|
|
225
236
|
"mistralai/mistral-small-2603": 0.0006,
|
|
@@ -232,11 +243,12 @@ prices='{
|
|
|
232
243
|
"qwen/qwen3-coder-next": 0.0012,
|
|
233
244
|
"qwen/qwen3-next-80b-a3b-instruct": 0.0012,
|
|
234
245
|
"qwen/qwen3.5-flash-02-23": 0.00015,
|
|
235
|
-
"qwen/qwen3.6-35b-a3b": 0.
|
|
246
|
+
"qwen/qwen3.6-35b-a3b": 0.0012002,
|
|
236
247
|
"qwen/qwen3.7-plus": 0.0016,
|
|
237
248
|
"xiaomi/mimo-v2.5-pro": 0.003,
|
|
238
|
-
"z-ai/glm-5": 0.
|
|
239
|
-
"z-ai/glm-5.1": 0.
|
|
249
|
+
"z-ai/glm-5": 0.0032,
|
|
250
|
+
"z-ai/glm-5.1": 0.0044,
|
|
251
|
+
"z-ai/glm-5.2": 0.0044
|
|
240
252
|
}
|
|
241
253
|
}'
|
|
242
254
|
# END_GENERATED_PRICES
|
|
@@ -425,20 +437,23 @@ elif [[ "$routed" == "failure" ]]; then
|
|
|
425
437
|
# swap or compute savings against a non-model.
|
|
426
438
|
printf '%s — %s%s' "$brand" "$routed" "$tokens_clause"
|
|
427
439
|
elif [[ -n "$routed" ]]; then
|
|
428
|
-
#
|
|
440
|
+
# Always show the savings clause, flooring a non-positive total at $0.00.
|
|
429
441
|
# session_savings is "0.0000" on fresh sessions or sessions where every
|
|
430
|
-
# turn routed back to the selected model
|
|
431
|
-
#
|
|
432
|
-
#
|
|
433
|
-
#
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
442
|
+
# turn routed back to the selected model, and can go negative when routing
|
|
443
|
+
# upgrades a turn to a pricier model (e.g. a sticky/hard-pinned side-call,
|
|
444
|
+
# or a hard turn escalated to opus). "saved -$X" would mislead, so clamp
|
|
445
|
+
# the display to $0.00 rather than dropping the clause — a $0.00 readout
|
|
446
|
+
# tells the user the router ran and simply didn't beat their selection.
|
|
447
|
+
# When the CC selection is unknown ("?" or empty) there's nothing to
|
|
448
|
+
# compare against, so drop the "← selection" arrow and just show routed.
|
|
449
|
+
display_savings="$session_savings"
|
|
450
|
+
if [[ -z "$display_savings" ]] \
|
|
451
|
+
|| awk -v v="$display_savings" 'BEGIN{exit !(v+0 < 0)}'; then
|
|
452
|
+
display_savings="0"
|
|
438
453
|
fi
|
|
439
|
-
if [[ "$
|
|
454
|
+
if [[ -n "$selected_display" && "$selected_display" != "?" ]]; then
|
|
440
455
|
printf '%s — %s ← %s · saved %s%s' \
|
|
441
|
-
"$brand" "$routed" "$selected_display" "$(fmt_money "$
|
|
456
|
+
"$brand" "$routed" "$selected_display" "$(fmt_money "$display_savings")" "$tokens_clause"
|
|
442
457
|
else
|
|
443
458
|
printf '%s — %s%s' "$brand" "$routed" "$tokens_clause"
|
|
444
459
|
fi
|
package/install.sh
CHANGED
|
@@ -533,20 +533,26 @@ TOML
|
|
|
533
533
|
chmod 600 "$config_file"
|
|
534
534
|
}
|
|
535
535
|
|
|
536
|
-
# write_opencode_config merges
|
|
537
|
-
# opencode.json plus the bundled
|
|
538
|
-
# - provider.weave :
|
|
539
|
-
#
|
|
540
|
-
#
|
|
541
|
-
#
|
|
542
|
-
#
|
|
543
|
-
#
|
|
544
|
-
#
|
|
545
|
-
#
|
|
536
|
+
# write_opencode_config merges the managed Weave provider(s) into opencode's
|
|
537
|
+
# opencode.json plus the bundled subscription plugin:
|
|
538
|
+
# - provider.weave : OpenAI/Responses-shaped (@ai-sdk/openai → /v1/responses).
|
|
539
|
+
# The single request provider. The router routes every
|
|
540
|
+
# turn across all models the caller's subscriptions +
|
|
541
|
+
# Weave key can pay for, and bills the plan matching the
|
|
542
|
+
# model it served. The default.
|
|
543
|
+
# - provider.weave-claude : login-only storage for a Claude (Pro/Max) subscription
|
|
544
|
+
# (no models, never serves requests). Written only when
|
|
545
|
+
# the bundled plugin is present, since the Claude login
|
|
546
|
+
# method lives in the plugin. The `weave` loader reads
|
|
547
|
+
# this slot and attaches the Claude sub.
|
|
546
548
|
# The plugin (src bundled at $script_dir/opencode-weave/src/index.ts) is dropped
|
|
547
549
|
# into $opencode_dir/.weave/ and registered via opencode.json's `plugin` array
|
|
548
|
-
# by absolute path — scope-independent (no reliance on an auto-load dir).
|
|
549
|
-
#
|
|
550
|
+
# by absolute path — scope-independent (no reliance on an auto-load dir). It owns
|
|
551
|
+
# both the ChatGPT (on `weave`) and Claude (on `weave-claude`) logins and attaches
|
|
552
|
+
# whichever subscriptions are connected to every request via the router's
|
|
553
|
+
# dedicated X-Weave-*-Subscription headers. Re-running rewrites the blocks
|
|
554
|
+
# in-place via jq (and strips the legacy `weave-codex` provider); uninstall
|
|
555
|
+
# strips them.
|
|
550
556
|
#
|
|
551
557
|
# Usage: write_opencode_config <config_file_path> <base_url> <api_key> [user_email] [user_name]
|
|
552
558
|
write_opencode_config() {
|
|
@@ -573,61 +579,53 @@ write_opencode_config() {
|
|
|
573
579
|
|
|
574
580
|
# Headline models we surface in opencode's picker. The router re-routes
|
|
575
581
|
# each request anyway, so this list is mostly UX — what shows up when the
|
|
576
|
-
# user runs /models inside opencode.
|
|
577
|
-
#
|
|
582
|
+
# user runs /models inside opencode. We list a mix of GPT + Claude so the
|
|
583
|
+
# picker reflects that the router spans families; whichever model serves the
|
|
584
|
+
# turn, its matching subscription (if connected) pays.
|
|
578
585
|
#
|
|
579
|
-
#
|
|
580
|
-
# @ai-sdk/
|
|
581
|
-
#
|
|
582
|
-
#
|
|
583
|
-
#
|
|
584
|
-
#
|
|
585
|
-
#
|
|
586
|
-
#
|
|
587
|
-
# root. opencode's @ai-sdk/anthropic (Vercel) appends only /messages to
|
|
588
|
-
# baseURL (its default is api.anthropic.com/v1), so /v1 yields the correct
|
|
589
|
-
# /v1/messages. Dropping it would hit /messages and 404. (pi uses the official
|
|
590
|
-
# @anthropic-ai/sdk, which appends /v1/messages to a root baseURL — opposite
|
|
591
|
-
# convention.) Verified by install/pi-router/test/opencode_smoke.sh.
|
|
586
|
+
# npm is @ai-sdk/openai and baseURL KEEPS its /v1 here: opencode's
|
|
587
|
+
# @ai-sdk/openai provider appends /responses, yielding the router's
|
|
588
|
+
# /v1/responses surface (the canonical inbound — the router translates to
|
|
589
|
+
# Anthropic/OSS as it routes, and ships verbatim to the Codex backend for GPT
|
|
590
|
+
# turns). apiKey is the router key as a parse-time placeholder; the router
|
|
591
|
+
# authenticates off X-Weave-Router-Key (planted in headers) and the plugin's
|
|
592
|
+
# loader attaches subscriptions via the dedicated X-Weave-*-Subscription
|
|
593
|
+
# headers, so the apiKey value is never used upstream.
|
|
592
594
|
local block
|
|
593
595
|
block="$(jq -n \
|
|
594
596
|
--arg url "$block_url/v1" \
|
|
595
597
|
--arg key "$block_key" \
|
|
596
598
|
--argjson headers "$headers_json" '
|
|
597
599
|
{
|
|
598
|
-
npm: "@ai-sdk/
|
|
600
|
+
npm: "@ai-sdk/openai",
|
|
599
601
|
name: "Weave Router",
|
|
600
602
|
options: { apiKey: $key, baseURL: $url, headers: $headers },
|
|
601
603
|
models: {
|
|
602
|
-
"claude-
|
|
603
|
-
"claude-opus-4-8": { name: "Claude Opus 4.8 (via Weave Router)" },
|
|
604
|
-
"claude-opus-4-7": { name: "Claude Opus 4.7 (via Weave Router)" },
|
|
604
|
+
"claude-sonnet-5": { name: "Claude Sonnet 5 (via Weave Router)" },
|
|
605
605
|
"claude-sonnet-4-6": { name: "Claude Sonnet 4.6 (via Weave Router)" },
|
|
606
|
-
"claude-
|
|
606
|
+
"claude-opus-4-8": { name: "Claude Opus 4.8 (via Weave Router)" },
|
|
607
|
+
"claude-haiku-4-5": { name: "Claude Haiku 4.5 (via Weave Router)" },
|
|
608
|
+
"gpt-5.5": { name: "GPT-5.5 (via Weave Router)" },
|
|
609
|
+
"gpt-5.4": { name: "GPT-5.4 (via Weave Router)" }
|
|
607
610
|
}
|
|
608
611
|
}
|
|
609
612
|
')"
|
|
610
613
|
|
|
611
|
-
#
|
|
612
|
-
#
|
|
613
|
-
#
|
|
614
|
-
#
|
|
615
|
-
# the
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
local codex_block
|
|
619
|
-
codex_block="$(jq -n \
|
|
614
|
+
# Login-only storage provider for a Claude (Pro/Max) subscription. It serves
|
|
615
|
+
# no requests (no models), so its npm/baseURL are inert — opencode just needs
|
|
616
|
+
# it registered so the plugin's Claude login method has a home and `opencode
|
|
617
|
+
# auth login` lists it. The `weave` loader reads this slot off disk and
|
|
618
|
+
# attaches the Claude sub. Reuses the same router key + identity headers.
|
|
619
|
+
local claude_block
|
|
620
|
+
claude_block="$(jq -n \
|
|
620
621
|
--arg url "$block_url/v1" \
|
|
621
622
|
--arg key "$block_key" \
|
|
622
623
|
--argjson headers "$headers_json" '
|
|
623
624
|
{
|
|
624
625
|
npm: "@ai-sdk/openai",
|
|
625
|
-
name: "Weave Router
|
|
626
|
+
name: "Weave Router — Claude plan",
|
|
626
627
|
options: { apiKey: $key, baseURL: $url, headers: $headers },
|
|
627
|
-
models: {
|
|
628
|
-
"gpt-5.5": { name: "GPT-5.5 — Codex subscription (via Weave Router)" },
|
|
629
|
-
"gpt-5.4": { name: "GPT-5.4 — Codex subscription (via Weave Router)" }
|
|
630
|
-
}
|
|
628
|
+
models: {}
|
|
631
629
|
}
|
|
632
630
|
')"
|
|
633
631
|
|
|
@@ -653,7 +651,7 @@ write_opencode_config() {
|
|
|
653
651
|
chmod 644 "$plugin_spec"
|
|
654
652
|
plugin_arg="$plugin_spec"
|
|
655
653
|
else
|
|
656
|
-
warn "opencode
|
|
654
|
+
warn "opencode subscription plugin source not found at $plugin_src — skipping the Claude login + subscription routing. (Use a packaged 'npx @workweave/router' install.)"
|
|
657
655
|
fi
|
|
658
656
|
|
|
659
657
|
# Merge into any existing opencode.json. We always overwrite provider.weave
|
|
@@ -661,21 +659,23 @@ write_opencode_config() {
|
|
|
661
659
|
# file (other providers, mcp, agent settings) untouched. Top-level `model` is
|
|
662
660
|
# only set when the user hasn't already picked one.
|
|
663
661
|
#
|
|
664
|
-
# The weave-
|
|
665
|
-
# the bundled plugin was present and copied ($plugin non-empty)
|
|
666
|
-
#
|
|
667
|
-
#
|
|
668
|
-
#
|
|
669
|
-
#
|
|
662
|
+
# The weave-claude login provider AND the plugin entry are written together
|
|
663
|
+
# only when the bundled plugin was present and copied ($plugin non-empty): the
|
|
664
|
+
# Claude login method lives in the plugin, so registering the provider without
|
|
665
|
+
# it (e.g. the `curl | sh` path, which carries no plugin source) would leave a
|
|
666
|
+
# non-working login — instead we omit it (and strip any stale one). The legacy
|
|
667
|
+
# `weave-codex` provider is always stripped: the single Responses `weave`
|
|
668
|
+
# provider supersedes it.
|
|
670
669
|
local merged
|
|
671
670
|
if [ -f "$config_file" ]; then
|
|
672
671
|
merged="$(jq \
|
|
673
672
|
--argjson block "$block" \
|
|
674
|
-
--argjson
|
|
673
|
+
--argjson claude "$claude_block" \
|
|
675
674
|
--arg plugin "$plugin_arg" \
|
|
676
675
|
--arg pluginspec "$plugin_spec" '
|
|
677
676
|
.provider = ((.provider // {}) | .weave = $block)
|
|
678
|
-
| (
|
|
677
|
+
| (.provider |= del(."weave-codex"))
|
|
678
|
+
| (if $plugin != "" then .provider["weave-claude"] = $claude else .provider |= del(."weave-claude") end)
|
|
679
679
|
# Register the managed plugin path when we installed it; otherwise strip a
|
|
680
680
|
# stale entry left by a prior install (the provider was just removed, so a
|
|
681
681
|
# lingering plugin reference would be dead weight).
|
|
@@ -686,23 +686,24 @@ write_opencode_config() {
|
|
|
686
686
|
else . end)
|
|
687
687
|
end)
|
|
688
688
|
| (if (.model // "") == "" then .model = "weave/claude-sonnet-4-6" else . end)
|
|
689
|
-
#
|
|
690
|
-
#
|
|
691
|
-
#
|
|
692
|
-
| (if
|
|
689
|
+
# Reset the default model if it points at a provider we just removed: the
|
|
690
|
+
# retired weave-codex, or weave-claude on a plugin-less re-install (it has
|
|
691
|
+
# no models anyway). Otherwise opencode boots with a dangling model.
|
|
692
|
+
| (if (.model // "" | tostring | startswith("weave-codex/")) then .model = "weave/claude-sonnet-4-6" else . end)
|
|
693
|
+
| (if $plugin == "" and (.model // "" | tostring | startswith("weave-claude/")) then .model = "weave/claude-sonnet-4-6" else . end)
|
|
693
694
|
| (.["$schema"] //= "https://opencode.ai/config.json")
|
|
694
695
|
' "$config_file")"
|
|
695
696
|
else
|
|
696
697
|
merged="$(jq -n \
|
|
697
698
|
--argjson block "$block" \
|
|
698
|
-
--argjson
|
|
699
|
+
--argjson claude "$claude_block" \
|
|
699
700
|
--arg plugin "$plugin_arg" '
|
|
700
701
|
{
|
|
701
702
|
"$schema": "https://opencode.ai/config.json",
|
|
702
703
|
model: "weave/claude-sonnet-4-6",
|
|
703
704
|
provider: { weave: $block }
|
|
704
705
|
}
|
|
705
|
-
| (if $plugin != "" then .provider["weave-
|
|
706
|
+
| (if $plugin != "" then .provider["weave-claude"] = $claude | .plugin = [$plugin] else . end)
|
|
706
707
|
')"
|
|
707
708
|
fi
|
|
708
709
|
printf '%s\n' "$merged" >"$config_file"
|
|
@@ -1402,9 +1403,9 @@ toggle_claude() {
|
|
|
1402
1403
|
else
|
|
1403
1404
|
# Park just the router-owned env keys, then strip them so Claude Code
|
|
1404
1405
|
# falls back to its own Anthropic login.
|
|
1405
|
-
jq '{env: ((.env // {}) | {ANTHROPIC_BASE_URL, ANTHROPIC_CUSTOM_HEADERS} | with_entries(select(.value != null)))}' "$settings_file" >"$parked"
|
|
1406
|
+
jq '{env: ((.env // {}) | {ANTHROPIC_BASE_URL, ANTHROPIC_CUSTOM_HEADERS, ENABLE_TOOL_SEARCH} | with_entries(select(.value != null)))}' "$settings_file" >"$parked"
|
|
1406
1407
|
chmod 600 "$parked"
|
|
1407
|
-
merged="$(jq '(.env // {}) |= del(.ANTHROPIC_BASE_URL, .ANTHROPIC_CUSTOM_HEADERS)
|
|
1408
|
+
merged="$(jq '(.env // {}) |= del(.ANTHROPIC_BASE_URL, .ANTHROPIC_CUSTOM_HEADERS, .ENABLE_TOOL_SEARCH)
|
|
1408
1409
|
| (if (.env // {} | length) == 0 then del(.env) else . end)' "$settings_file")"
|
|
1409
1410
|
printf '%s\n' "$merged" >"$settings_file"
|
|
1410
1411
|
fi
|
|
@@ -1543,8 +1544,8 @@ toggle_opencode() {
|
|
|
1543
1544
|
model="$(jq -r '.model // empty' "$f" 2>/dev/null || true)"
|
|
1544
1545
|
if [ "$(jq -r '((.provider // {}) | has("weave"))' "$f" 2>/dev/null || true)" = "true" ]; then has_weave="true"; fi
|
|
1545
1546
|
fi
|
|
1546
|
-
#
|
|
1547
|
-
#
|
|
1547
|
+
# A managed model counts as router-on: the Responses-shaped `weave/…` default,
|
|
1548
|
+
# or a legacy `weave-codex/…` model parked by a pre-upgrade install.
|
|
1548
1549
|
case "$model" in weave/* | weave-codex/*) on="true" ;; esac
|
|
1549
1550
|
|
|
1550
1551
|
case "$mode" in
|
|
@@ -1588,10 +1589,10 @@ toggle_opencode() {
|
|
|
1588
1589
|
| "weave/" + .
|
|
1589
1590
|
' "$f" 2>/dev/null || echo "weave/claude-sonnet-4-6")"
|
|
1590
1591
|
fi
|
|
1591
|
-
# Never restore a weave-codex model whose provider is no longer
|
|
1592
|
-
# registered (
|
|
1593
|
-
#
|
|
1594
|
-
#
|
|
1592
|
+
# Never restore a legacy weave-codex model whose provider is no longer
|
|
1593
|
+
# registered (every current install strips weave-codex). Fall back to the
|
|
1594
|
+
# `weave` default so `on` can't leave opencode pointing at a missing
|
|
1595
|
+
# provider.
|
|
1595
1596
|
case "$restore_model" in
|
|
1596
1597
|
weave-codex/*)
|
|
1597
1598
|
if [ "$(jq -r '((.provider // {}) | has("weave-codex"))' "$f" 2>/dev/null || true)" != "true" ]; then
|
|
@@ -1849,13 +1850,14 @@ if [ "$target" = "opencode" ]; then
|
|
|
1849
1850
|
printf "\n"
|
|
1850
1851
|
printf "%s✓%s %s%sWeave Router installed for opencode.%s\n" \
|
|
1851
1852
|
"$C_GREEN" "$C_RESET" "$C_BOLD" "$C_BRAND" "$C_RESET"
|
|
1852
|
-
# Surface the optional
|
|
1853
|
-
# registered the weave-
|
|
1854
|
-
#
|
|
1855
|
-
#
|
|
1856
|
-
# that stripped the provider, which would
|
|
1857
|
-
|
|
1858
|
-
|
|
1853
|
+
# Surface the optional subscription-routing path only when this run actually
|
|
1854
|
+
# registered the weave-claude login provider (which is written together with
|
|
1855
|
+
# the plugin). Gate on its presence in the written config (authoritative)
|
|
1856
|
+
# rather than a plugin file on disk — a leftover plugin from a prior install
|
|
1857
|
+
# can outlive a plugin-less re-install that stripped the provider, which would
|
|
1858
|
+
# make these instructions misleading.
|
|
1859
|
+
if jq -e '(.provider // {}) | has("weave-claude")' "$opencode_config_file" >/dev/null 2>&1; then
|
|
1860
|
+
info "Optional: connect your AI plans so they pay for the matching turns. Run ${C_BOLD}opencode auth login${C_RESET} → ${C_BOLD}Weave Router${C_RESET} for ${C_BOLD}ChatGPT Pro/Plus${C_RESET} (GPT/Codex turns) and/or ${C_BOLD}Weave Router — Claude plan${C_RESET} for ${C_BOLD}Claude Pro/Max${C_RESET} (Claude turns). The router still routes every turn; your Weave key pays for the rest."
|
|
1859
1861
|
fi
|
|
1860
1862
|
if [ -n "$install_dir" ]; then
|
|
1861
1863
|
# --dir installs land outside opencode's discovery roots, so the caller
|
|
@@ -2069,14 +2071,18 @@ normalize_model() {
|
|
|
2069
2071
|
prices='{
|
|
2070
2072
|
"input": {
|
|
2071
2073
|
"claude-fable-5": 0.01,
|
|
2072
|
-
"claude-haiku-4-5": 0.
|
|
2074
|
+
"claude-haiku-4-5": 0.001,
|
|
2075
|
+
"claude-opus-4-0": 0.015,
|
|
2076
|
+
"claude-opus-4-1": 0.015,
|
|
2077
|
+
"claude-opus-4-5": 0.005,
|
|
2073
2078
|
"claude-opus-4-6": 0.005,
|
|
2074
2079
|
"claude-opus-4-7": 0.005,
|
|
2075
2080
|
"claude-opus-4-8": 0.005,
|
|
2076
2081
|
"claude-sonnet-4-5": 0.003,
|
|
2077
2082
|
"claude-sonnet-4-6": 0.003,
|
|
2078
|
-
"
|
|
2079
|
-
"deepseek/deepseek-v4-
|
|
2083
|
+
"claude-sonnet-5": 0.003,
|
|
2084
|
+
"deepseek/deepseek-v4-flash": 0.0001134,
|
|
2085
|
+
"deepseek/deepseek-v4-pro": 0.001318,
|
|
2080
2086
|
"gemini-2.0-flash": 0.0001,
|
|
2081
2087
|
"gemini-2.0-flash-lite": 0.000075,
|
|
2082
2088
|
"gemini-2.5-flash": 0.0003,
|
|
@@ -2087,6 +2093,7 @@ prices='{
|
|
|
2087
2093
|
"gemini-3.1-flash-lite-preview": 0.0001,
|
|
2088
2094
|
"gemini-3.1-pro-preview": 0.002,
|
|
2089
2095
|
"gemini-3.5-flash": 0.0015,
|
|
2096
|
+
"google/gemma-4-26b-a4b-it": 0.00015,
|
|
2090
2097
|
"gpt-4.1": 0.002,
|
|
2091
2098
|
"gpt-4.1-mini": 0.0004,
|
|
2092
2099
|
"gpt-4.1-nano": 0.0001,
|
|
@@ -2096,10 +2103,10 @@ prices='{
|
|
|
2096
2103
|
"gpt-5-chat": 0.0025,
|
|
2097
2104
|
"gpt-5-mini": 0.0005,
|
|
2098
2105
|
"gpt-5-nano": 0.0001,
|
|
2099
|
-
"gpt-5.4": 0.
|
|
2100
|
-
"gpt-5.4-mini": 0.
|
|
2101
|
-
"gpt-5.4-nano": 0.
|
|
2102
|
-
"gpt-5.4-pro": 0.
|
|
2106
|
+
"gpt-5.4": 0.0025,
|
|
2107
|
+
"gpt-5.4-mini": 0.00075,
|
|
2108
|
+
"gpt-5.4-nano": 0.0002,
|
|
2109
|
+
"gpt-5.4-pro": 0.03,
|
|
2103
2110
|
"gpt-5.5": 0.005,
|
|
2104
2111
|
"gpt-5.5-mini": 0.0005,
|
|
2105
2112
|
"gpt-5.5-nano": 0.00015,
|
|
@@ -2116,22 +2123,27 @@ prices='{
|
|
|
2116
2123
|
"qwen/qwen3-coder-next": 0.0005,
|
|
2117
2124
|
"qwen/qwen3-next-80b-a3b-instruct": 0.00015,
|
|
2118
2125
|
"qwen/qwen3.5-flash-02-23": 0.00005,
|
|
2119
|
-
"qwen/qwen3.6-35b-a3b": 0.
|
|
2126
|
+
"qwen/qwen3.6-35b-a3b": 0.000172,
|
|
2120
2127
|
"qwen/qwen3.7-plus": 0.0004,
|
|
2121
2128
|
"xiaomi/mimo-v2.5-pro": 0.001,
|
|
2122
|
-
"z-ai/glm-5": 0.
|
|
2123
|
-
"z-ai/glm-5.1": 0.
|
|
2129
|
+
"z-ai/glm-5": 0.001,
|
|
2130
|
+
"z-ai/glm-5.1": 0.0014,
|
|
2131
|
+
"z-ai/glm-5.2": 0.0014
|
|
2124
2132
|
},
|
|
2125
2133
|
"output": {
|
|
2126
2134
|
"claude-fable-5": 0.05,
|
|
2127
|
-
"claude-haiku-4-5": 0.
|
|
2135
|
+
"claude-haiku-4-5": 0.005,
|
|
2136
|
+
"claude-opus-4-0": 0.075,
|
|
2137
|
+
"claude-opus-4-1": 0.075,
|
|
2138
|
+
"claude-opus-4-5": 0.025,
|
|
2128
2139
|
"claude-opus-4-6": 0.025,
|
|
2129
2140
|
"claude-opus-4-7": 0.025,
|
|
2130
2141
|
"claude-opus-4-8": 0.025,
|
|
2131
2142
|
"claude-sonnet-4-5": 0.015,
|
|
2132
2143
|
"claude-sonnet-4-6": 0.015,
|
|
2133
|
-
"
|
|
2134
|
-
"deepseek/deepseek-v4-
|
|
2144
|
+
"claude-sonnet-5": 0.015,
|
|
2145
|
+
"deepseek/deepseek-v4-flash": 0.0002791,
|
|
2146
|
+
"deepseek/deepseek-v4-pro": 0.0026361,
|
|
2135
2147
|
"gemini-2.0-flash": 0.0004,
|
|
2136
2148
|
"gemini-2.0-flash-lite": 0.0003,
|
|
2137
2149
|
"gemini-2.5-flash": 0.0012,
|
|
@@ -2142,6 +2154,7 @@ prices='{
|
|
|
2142
2154
|
"gemini-3.1-flash-lite-preview": 0.0004,
|
|
2143
2155
|
"gemini-3.1-pro-preview": 0.008,
|
|
2144
2156
|
"gemini-3.5-flash": 0.009,
|
|
2157
|
+
"google/gemma-4-26b-a4b-it": 0.0006,
|
|
2145
2158
|
"gpt-4.1": 0.008,
|
|
2146
2159
|
"gpt-4.1-mini": 0.0016,
|
|
2147
2160
|
"gpt-4.1-nano": 0.0004,
|
|
@@ -2151,14 +2164,14 @@ prices='{
|
|
|
2151
2164
|
"gpt-5-chat": 0.01,
|
|
2152
2165
|
"gpt-5-mini": 0.002,
|
|
2153
2166
|
"gpt-5-nano": 0.0004,
|
|
2154
|
-
"gpt-5.4": 0.
|
|
2155
|
-
"gpt-5.4-mini": 0.
|
|
2156
|
-
"gpt-5.4-nano": 0.
|
|
2157
|
-
"gpt-5.4-pro": 0.
|
|
2158
|
-
"gpt-5.5": 0.
|
|
2167
|
+
"gpt-5.4": 0.015,
|
|
2168
|
+
"gpt-5.4-mini": 0.0045,
|
|
2169
|
+
"gpt-5.4-nano": 0.00125,
|
|
2170
|
+
"gpt-5.4-pro": 0.18,
|
|
2171
|
+
"gpt-5.5": 0.03,
|
|
2159
2172
|
"gpt-5.5-mini": 0.0025,
|
|
2160
2173
|
"gpt-5.5-nano": 0.0006,
|
|
2161
|
-
"gpt-5.5-pro": 0.
|
|
2174
|
+
"gpt-5.5-pro": 0.18,
|
|
2162
2175
|
"minimax/minimax-m2.7": 0.0012,
|
|
2163
2176
|
"minimax/minimax-m3": 0.0012,
|
|
2164
2177
|
"mistralai/mistral-small-2603": 0.0006,
|
|
@@ -2171,11 +2184,12 @@ prices='{
|
|
|
2171
2184
|
"qwen/qwen3-coder-next": 0.0012,
|
|
2172
2185
|
"qwen/qwen3-next-80b-a3b-instruct": 0.0012,
|
|
2173
2186
|
"qwen/qwen3.5-flash-02-23": 0.00015,
|
|
2174
|
-
"qwen/qwen3.6-35b-a3b": 0.
|
|
2187
|
+
"qwen/qwen3.6-35b-a3b": 0.0012002,
|
|
2175
2188
|
"qwen/qwen3.7-plus": 0.0016,
|
|
2176
2189
|
"xiaomi/mimo-v2.5-pro": 0.003,
|
|
2177
|
-
"z-ai/glm-5": 0.
|
|
2178
|
-
"z-ai/glm-5.1": 0.
|
|
2190
|
+
"z-ai/glm-5": 0.0032,
|
|
2191
|
+
"z-ai/glm-5.1": 0.0044,
|
|
2192
|
+
"z-ai/glm-5.2": 0.0044
|
|
2179
2193
|
}
|
|
2180
2194
|
}'
|
|
2181
2195
|
# END_GENERATED_PRICES
|
|
@@ -2364,20 +2378,23 @@ elif [[ "$routed" == "failure" ]]; then
|
|
|
2364
2378
|
# swap or compute savings against a non-model.
|
|
2365
2379
|
printf '%s — %s%s' "$brand" "$routed" "$tokens_clause"
|
|
2366
2380
|
elif [[ -n "$routed" ]]; then
|
|
2367
|
-
#
|
|
2381
|
+
# Always show the savings clause, flooring a non-positive total at $0.00.
|
|
2368
2382
|
# session_savings is "0.0000" on fresh sessions or sessions where every
|
|
2369
|
-
# turn routed back to the selected model
|
|
2370
|
-
#
|
|
2371
|
-
#
|
|
2372
|
-
#
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2383
|
+
# turn routed back to the selected model, and can go negative when routing
|
|
2384
|
+
# upgrades a turn to a pricier model (e.g. a sticky/hard-pinned side-call,
|
|
2385
|
+
# or a hard turn escalated to opus). "saved -$X" would mislead, so clamp
|
|
2386
|
+
# the display to $0.00 rather than dropping the clause — a $0.00 readout
|
|
2387
|
+
# tells the user the router ran and simply didn't beat their selection.
|
|
2388
|
+
# When the CC selection is unknown ("?" or empty) there's nothing to
|
|
2389
|
+
# compare against, so drop the "← selection" arrow and just show routed.
|
|
2390
|
+
display_savings="$session_savings"
|
|
2391
|
+
if [[ -z "$display_savings" ]] \
|
|
2392
|
+
|| awk -v v="$display_savings" 'BEGIN{exit !(v+0 < 0)}'; then
|
|
2393
|
+
display_savings="0"
|
|
2394
|
+
fi
|
|
2395
|
+
if [[ -n "$selected_display" && "$selected_display" != "?" ]]; then
|
|
2379
2396
|
printf '%s — %s ← %s · saved %s%s' \
|
|
2380
|
-
"$brand" "$routed" "$selected_display" "$(fmt_money "$
|
|
2397
|
+
"$brand" "$routed" "$selected_display" "$(fmt_money "$display_savings")" "$tokens_clause"
|
|
2381
2398
|
else
|
|
2382
2399
|
printf '%s — %s%s' "$brand" "$routed" "$tokens_clause"
|
|
2383
2400
|
fi
|
|
@@ -2414,14 +2431,19 @@ if [ -n "$user_name" ]; then
|
|
|
2414
2431
|
fi
|
|
2415
2432
|
custom_headers="$custom_headers"$'\n'"X-App: claude-code"
|
|
2416
2433
|
|
|
2434
|
+
# Setting ANTHROPIC_BASE_URL makes Claude Code treat us as non-first-party and
|
|
2435
|
+
# disable MCP tool-search deferral, inlining every tool schema into every request
|
|
2436
|
+
# — a large uncompactable prefix that can push a session into an autocompact
|
|
2437
|
+
# thrash loop. ENABLE_TOOL_SEARCH=auto restores on-demand loading (Claude Code's
|
|
2438
|
+
# own first-party default).
|
|
2417
2439
|
if [ "$scope" = "project" ] && [ -z "$install_dir" ]; then
|
|
2418
2440
|
jq -n --arg url "$base_url" --arg sl "$statusline_path_for_settings" '{
|
|
2419
|
-
env: { ANTHROPIC_BASE_URL: $url },
|
|
2441
|
+
env: { ANTHROPIC_BASE_URL: $url, ENABLE_TOOL_SEARCH: "auto" },
|
|
2420
2442
|
statusLine: { type: "command", command: $sl }
|
|
2421
2443
|
}' >"$tmp_patch"
|
|
2422
2444
|
else
|
|
2423
2445
|
jq -n --arg url "$base_url" --arg header "$custom_headers" --arg sl "$statusline_path_for_settings" '{
|
|
2424
|
-
env: { ANTHROPIC_BASE_URL: $url, ANTHROPIC_CUSTOM_HEADERS: $header },
|
|
2446
|
+
env: { ANTHROPIC_BASE_URL: $url, ANTHROPIC_CUSTOM_HEADERS: $header, ENABLE_TOOL_SEARCH: "auto" },
|
|
2425
2447
|
statusLine: { type: "command", command: $sl }
|
|
2426
2448
|
}' >"$tmp_patch"
|
|
2427
2449
|
fi
|