@workweave/router 0.2.1 → 0.2.3
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/bin.js +3 -3
- package/cc-statusline.sh +49 -2
- package/commands/fm.md +6 -0
- package/commands/force-model.md +1 -1
- package/commands/rf.md +6 -0
- package/commands/router-feedback.md +6 -0
- package/commands/router-off.md +1 -1
- package/commands/router-on.md +1 -1
- package/commands/router-status.md +2 -2
- package/commands/ufm.md +5 -0
- package/commands/unforce-model.md +1 -1
- package/install.sh +199 -43
- package/opencode-weave/README.md +59 -0
- package/opencode-weave/package.json +9 -0
- package/opencode-weave/src/index.ts +438 -0
- package/package.json +17 -6
- package/pi-router/src/dispatch.ts +2 -2
- package/pi-router/src/index.ts +1 -1
- package/pi-router/src/provider.ts +1 -1
- package/pi-router/src/routed-model.ts +1 -1
- package/uninstall.sh +37 -13
package/bin.js
CHANGED
|
@@ -17,7 +17,7 @@ const script = path.join(__dirname, scriptName);
|
|
|
17
17
|
|
|
18
18
|
if (!existsSync(script)) {
|
|
19
19
|
console.error(
|
|
20
|
-
`
|
|
20
|
+
`Weave Router: ${scriptName} missing from package — report it at https://github.com/workweave/router/issues`,
|
|
21
21
|
);
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
@@ -25,7 +25,7 @@ if (!existsSync(script)) {
|
|
|
25
25
|
const bash = pickBash();
|
|
26
26
|
if (!bash) {
|
|
27
27
|
console.error(
|
|
28
|
-
"
|
|
28
|
+
"Weave Router: bash is required. On Windows install Git Bash or run inside WSL.",
|
|
29
29
|
);
|
|
30
30
|
process.exit(1);
|
|
31
31
|
}
|
|
@@ -36,7 +36,7 @@ const result = spawnSync(bash, [script, ...args], {
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
if (result.error) {
|
|
39
|
-
console.error("
|
|
39
|
+
console.error("Weave Router:", result.error.message);
|
|
40
40
|
process.exit(1);
|
|
41
41
|
}
|
|
42
42
|
process.exit(result.status ?? 1);
|
package/cc-statusline.sh
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
# Claude Code statusline for the Weave
|
|
3
|
+
# Claude Code statusline for the Weave Router. CC pipes a JSON blob on stdin
|
|
4
4
|
# whose `transcript_path` points at the JSONL log of the current session and
|
|
5
5
|
# whose `model.display_name` is the user's CC-side model selection. The
|
|
6
6
|
# router rewrites each request's `model` field before forwarding, so
|
|
@@ -129,6 +129,7 @@ normalize_model() {
|
|
|
129
129
|
# BEGIN_GENERATED_PRICES
|
|
130
130
|
prices='{
|
|
131
131
|
"input": {
|
|
132
|
+
"claude-fable-5": 0.01,
|
|
132
133
|
"claude-haiku-4-5": 0.0008,
|
|
133
134
|
"claude-opus-4-6": 0.005,
|
|
134
135
|
"claude-opus-4-7": 0.005,
|
|
@@ -165,9 +166,11 @@ prices='{
|
|
|
165
166
|
"gpt-5.5-nano": 0.00015,
|
|
166
167
|
"gpt-5.5-pro": 0.03,
|
|
167
168
|
"minimax/minimax-m2.7": 0.0003,
|
|
169
|
+
"minimax/minimax-m3": 0.0003,
|
|
168
170
|
"mistralai/mistral-small-2603": 0.0002,
|
|
169
171
|
"moonshotai/kimi-k2.5": 0.0006,
|
|
170
172
|
"moonshotai/kimi-k2.6": 0.00095,
|
|
173
|
+
"moonshotai/kimi-k2.7": 0.00095,
|
|
171
174
|
"qwen/qwen3-235b-a22b-2507": 0.0002266,
|
|
172
175
|
"qwen/qwen3-30b-a3b-instruct-2507": 0.00015,
|
|
173
176
|
"qwen/qwen3-coder": 0.0009,
|
|
@@ -175,11 +178,13 @@ prices='{
|
|
|
175
178
|
"qwen/qwen3-next-80b-a3b-instruct": 0.00015,
|
|
176
179
|
"qwen/qwen3.5-flash-02-23": 0.00005,
|
|
177
180
|
"qwen/qwen3.6-35b-a3b": 0.00015,
|
|
181
|
+
"qwen/qwen3.7-plus": 0.0004,
|
|
178
182
|
"xiaomi/mimo-v2.5-pro": 0.001,
|
|
179
183
|
"z-ai/glm-5": 0.0006,
|
|
180
184
|
"z-ai/glm-5.1": 0.00105
|
|
181
185
|
},
|
|
182
186
|
"output": {
|
|
187
|
+
"claude-fable-5": 0.05,
|
|
183
188
|
"claude-haiku-4-5": 0.004,
|
|
184
189
|
"claude-opus-4-6": 0.025,
|
|
185
190
|
"claude-opus-4-7": 0.025,
|
|
@@ -216,9 +221,11 @@ prices='{
|
|
|
216
221
|
"gpt-5.5-nano": 0.0006,
|
|
217
222
|
"gpt-5.5-pro": 0.12,
|
|
218
223
|
"minimax/minimax-m2.7": 0.0012,
|
|
224
|
+
"minimax/minimax-m3": 0.0012,
|
|
219
225
|
"mistralai/mistral-small-2603": 0.0006,
|
|
220
226
|
"moonshotai/kimi-k2.5": 0.003,
|
|
221
227
|
"moonshotai/kimi-k2.6": 0.004,
|
|
228
|
+
"moonshotai/kimi-k2.7": 0.004,
|
|
222
229
|
"qwen/qwen3-235b-a22b-2507": 0.0009064,
|
|
223
230
|
"qwen/qwen3-30b-a3b-instruct-2507": 0.0006,
|
|
224
231
|
"qwen/qwen3-coder": 0.0027,
|
|
@@ -226,6 +233,7 @@ prices='{
|
|
|
226
233
|
"qwen/qwen3-next-80b-a3b-instruct": 0.0012,
|
|
227
234
|
"qwen/qwen3.5-flash-02-23": 0.00015,
|
|
228
235
|
"qwen/qwen3.6-35b-a3b": 0.00095,
|
|
236
|
+
"qwen/qwen3.7-plus": 0.0016,
|
|
229
237
|
"xiaomi/mimo-v2.5-pro": 0.003,
|
|
230
238
|
"z-ai/glm-5": 0.00208,
|
|
231
239
|
"z-ai/glm-5.1": 0.0035
|
|
@@ -234,6 +242,8 @@ prices='{
|
|
|
234
242
|
# END_GENERATED_PRICES
|
|
235
243
|
|
|
236
244
|
routed=""
|
|
245
|
+
forced="false"
|
|
246
|
+
forced_model=""
|
|
237
247
|
session_savings=""
|
|
238
248
|
tot_in=0
|
|
239
249
|
tot_out=0
|
|
@@ -271,6 +281,36 @@ if [[ -n "$transcript_path" && -f "$transcript_path" ]]; then
|
|
|
271
281
|
routed="$(normalize_model "$routed")"
|
|
272
282
|
fi
|
|
273
283
|
|
|
284
|
+
# Detect an active /force-model pin from the router's synthetic ack turns —
|
|
285
|
+
# the only turns stamped message.model == "weave-router". The router emits
|
|
286
|
+
# one whenever a pin changes state:
|
|
287
|
+
# * /force-model → "force-model applied: <model> (<provider>) …"
|
|
288
|
+
# * /unforce-model → "force-model cleared …"
|
|
289
|
+
# * loop / no-progress break → "… clearing the session pin …" (expires the
|
|
290
|
+
# pin, including a user-forced one)
|
|
291
|
+
# * unrecognized model → "… isn't a recognized model · keeping
|
|
292
|
+
# automatic routing" — a NO-OP: the prior
|
|
293
|
+
# pin, if any, is left untouched
|
|
294
|
+
# These persist on disk (the ingress stripper only scrubs them from upstream
|
|
295
|
+
# requests). Classify each weave-router turn newest-first, skip the no-op
|
|
296
|
+
# "rejected" acks, and let the latest real state change decide: an "applied"
|
|
297
|
+
# marker means the session is pinned (and names the model); anything else
|
|
298
|
+
# (cleared / loop-break / no-progress) means automatic routing has resumed.
|
|
299
|
+
# Restricting to weave-router turns keeps a normal reply that merely quotes
|
|
300
|
+
# these phrases from flipping the tag. (A silent server-side TTL expiry emits
|
|
301
|
+
# no turn and so can't be reflected here — the pin TTL outlives a session.)
|
|
302
|
+
force_state="$("${reverse[@]}" "$transcript_path" 2>/dev/null \
|
|
303
|
+
| jq -r 'select(.type=="assistant" and .message.model=="weave-router")
|
|
304
|
+
| ([.message.content[]? | select(.type? == "text") | .text] | join(" ") | gsub("[\n\r]"; " ")) as $t
|
|
305
|
+
| if ($t | test("force-model applied:")) then "APPLIED " + ($t | capture("force-model applied: (?<m>[^ ]+)").m)
|
|
306
|
+
elif ($t | test("isn.t a recognized model")) then "REJECTED"
|
|
307
|
+
else "CLEARED" end' 2>/dev/null \
|
|
308
|
+
| grep -m1 -v '^REJECTED$' || true)"
|
|
309
|
+
if [[ "$force_state" == APPLIED\ * ]]; then
|
|
310
|
+
forced="true"
|
|
311
|
+
forced_model="${force_state#APPLIED }"
|
|
312
|
+
fi
|
|
313
|
+
|
|
274
314
|
# Compute a session running total: savings across every assistant turn
|
|
275
315
|
# whose marker reports a requested ≠ routed swap, plus cumulative token
|
|
276
316
|
# counts across every assistant turn (rerouted or not — total work the
|
|
@@ -373,7 +413,14 @@ if [[ "$tot_in" -gt 0 || "$tot_out" -gt 0 || "$tot_cache_read" -gt 0 || "$tot_ca
|
|
|
373
413
|
fi
|
|
374
414
|
fi
|
|
375
415
|
|
|
376
|
-
if [[ "$
|
|
416
|
+
if [[ "$forced" == "true" ]]; then
|
|
417
|
+
# Session is pinned via /force-model. The "← selection · saved $X" clause
|
|
418
|
+
# describes automatic routing and would be misleading on a manual pin, so
|
|
419
|
+
# show the pinned model with a [forced] tag instead. forced_model comes from
|
|
420
|
+
# the marker; fall back to the routed/selected id if parsing came up empty.
|
|
421
|
+
forced_display="${forced_model:-${routed:-$selected_display}}"
|
|
422
|
+
printf '%s — %s [forced]%s' "$brand" "$forced_display" "$tokens_clause"
|
|
423
|
+
elif [[ "$routed" == "failure" ]]; then
|
|
377
424
|
# Latest turn was a CC-synthesized error stub — don't claim a routing
|
|
378
425
|
# swap or compute savings against a non-model.
|
|
379
426
|
printf '%s — %s%s' "$brand" "$routed" "$tokens_clause"
|
package/commands/fm.md
ADDED
package/commands/force-model.md
CHANGED
package/commands/rf.md
ADDED
package/commands/router-off.md
CHANGED
package/commands/router-on.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Show whether Claude Code is routing through the Weave Router or directly to Anthropic
|
|
2
|
+
description: Show whether Claude Code is routing through the Weave Router or directly to Anthropic.
|
|
3
3
|
allowed-tools: Bash(npx:*)
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Report whether Claude Code is
|
|
6
|
+
Report whether Claude Code is using the Weave Router by running:
|
|
7
7
|
|
|
8
8
|
`npx @workweave/router status --claude{{SCOPE}}`
|
|
9
9
|
|
package/commands/ufm.md
ADDED