@workweave/router 0.2.6 → 0.2.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.
package/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # @workweave/router
2
2
 
3
- One command, anywhere, to point Claude Code, Codex, or opencode at the Weave Router.
3
+ One command, anywhere, to point Claude Code, Codex, opencode, or pi at the Weave Router.
4
4
 
5
5
  ```bash
6
- npx @workweave/router # interactive: pick Claude Code / Codex / opencode, then scope
6
+ npx @workweave/router # interactive: pick Claude Code / Codex / opencode / pi, then scope
7
7
  npx @workweave/router --claude # skip the picker, target Claude Code
8
8
  npx @workweave/router --codex # skip the picker, target the OpenAI Codex CLI
9
9
  npx @workweave/router --opencode # skip the picker, target opencode
10
+ npx @workweave/router --pi # skip the picker, target pi + Loom UI
10
11
  npx @workweave/router --scope project # per-repo install, commit settings.json (or .codex/ / opencode.json)
11
12
  npx @workweave/router --local # self-hosted via docker-compose (localhost:8080)
12
13
  npx @workweave/router --base-url https://router.acme.internal
@@ -31,6 +32,9 @@ npx @workweave/router status --codex # is Codex on the router or direct?
31
32
  Claude Code reads its router setting at launch, so quit and reopen it after an
32
33
  on/off. Codex and opencode pick it up on their next run. Inside Claude Code the
33
34
  slash commands `/router-off`, `/router-on`, and `/router-status` do the same.
35
+ Codex installs a `$disable-routing` skill that switches its next session back
36
+ to the normal provider; Codex does not support third-party `/disable-routing`
37
+ slash commands. The shell equivalent is `npx @workweave/router disable-routing`.
34
38
  Cursor has no config file we own — toggle its base URL override in **Settings →
35
39
  Models** instead.
36
40
 
@@ -40,6 +44,7 @@ Uninstall:
40
44
  npx @workweave/router --uninstall # Claude Code, user scope
41
45
  npx @workweave/router --uninstall --codex # Codex, user scope
42
46
  npx @workweave/router --uninstall --opencode # opencode, user scope
47
+ npx @workweave/router --uninstall --pi # pi, user scope
43
48
  npx @workweave/router --uninstall --scope project # Claude Code, inside the repo
44
49
  npx @workweave/router --uninstall --codex --scope project
45
50
  ```
@@ -52,7 +57,7 @@ Node ≥ 18 — no `curl | sh`, no Git clone, no PATH fiddling. Everything the
52
57
  shell installer documents (targets, scopes, flags, environment variables)
53
58
  works identically here.
54
59
 
55
- Three install targets:
60
+ Four install targets:
56
61
 
57
62
  - **Claude Code** (default) — patches `~/.claude/settings.json` (or
58
63
  `<repo>/.claude/settings.json` with `--scope project`) so `claude` routes
@@ -60,10 +65,19 @@ Three install targets:
60
65
  api.anthropic.com.
61
66
  - **Codex** (`--codex`) — patches `~/.codex/config.toml` (or
62
67
  `<repo>/.codex/config.toml`) with a managed `[model_providers.weave]`
63
- block plus `model_provider = "weave"`. OpenAI plan credentials flow
64
- through to api.openai.com. The block lives between begin/end markers so
65
- re-running the installer rewrites it cleanly and `--uninstall --codex`
66
- removes it without touching the rest of your config.
68
+ block plus `model_provider = "weave"`. The provider preserves the existing
69
+ ChatGPT OAuth login. The public hosted endpoint sends
70
+ `X-Weave-Router-Strategy: hmm`; `--local` and custom self-hosted URLs keep
71
+ their router's configured default because its HMM sidecar is optional. HMM or forced
72
+ `gpt-5.6-sol`, `gpt-5.6-terra`, and `gpt-5.6-luna` turns use that plan;
73
+ every other selected model uses its WorkWeave deployment or BYOK credential.
74
+ The block lives between begin/end markers
75
+ so re-running the installer rewrites it cleanly and `--uninstall --codex`
76
+ removes it without touching the rest of your config. Codex does not load
77
+ third-party slash-command files; to send a router directive, type it with
78
+ one leading space (for example, ` /force-model gpt-5.6-terra`). Its
79
+ `$disable-routing` skill returns the next Codex session to the default
80
+ provider without logging out or deleting the router configuration.
67
81
  - **opencode** (`--opencode`) — merges a `provider.weave` entry (backed by
68
82
  opencode's built-in `@ai-sdk/anthropic` provider) into
69
83
  `~/.config/opencode/opencode.json` (or `<repo>/opencode.json` with
@@ -71,6 +85,12 @@ Three install targets:
71
85
  natively, so opencode talks to it unmodified. Re-install rewrites only
72
86
  the managed `provider.weave` block; `--uninstall --opencode` strips it
73
87
  and leaves your other providers and settings alone.
88
+ - **pi** (`--pi`) — registers the `weave` provider and installs this package as
89
+ a pi extension. Stock pi then gets the Loom startup header, Wooly's animated
90
+ mascot, the persistent actual-route display, cumulative session savings,
91
+ `/fm` + `/ufm` model-pin commands with a `[forced]` status, and the
92
+ context-isolated `dispatch` tool. There is no forked pi binary and no separate
93
+ Loom runtime.
74
94
 
75
95
  See the [main installer docs](https://github.com/workweave/router/tree/main/install)
76
96
  for the full reference.
@@ -79,8 +99,8 @@ for the full reference.
79
99
 
80
100
  - Node ≥ 18 (ships with `npx`)
81
101
  - `bash` on PATH (macOS / Linux native; Windows needs Git Bash or WSL)
82
- - `jq` on PATH — used by the Claude Code status line script and the
83
- opencode JSON merge. Not required for the Codex path.
102
+ - `jq` on PATH — used by the Claude Code status line and the opencode/pi JSON
103
+ merges. Not required for the Codex path.
84
104
 
85
105
  ## Why npx
86
106
 
package/cc-statusline.sh CHANGED
@@ -33,10 +33,21 @@ set -euo pipefail
33
33
  # lives in their per-user cache dir, and on no-content-change days we skip
34
34
  # the mv entirely so the repo working tree stays clean. When upstream does
35
35
  # change, the first teammate's commit propagates the new version to the rest.
36
+ # A pricing-table miss also triggers a refresh off-schedule — see
37
+ # weave_refresh_on_price_miss below.
36
38
  #
37
39
  # Opt out entirely with `export WEAVE_STATUSLINE_UPDATE=0`. Override the
38
40
  # source with `WEAVE_STATUSLINE_URL=...`, e.g. for self-hosters who fork.
41
+ #
42
+ # $1 is an optional stamp suffix, so a caller refreshing for a reason other than
43
+ # "the interval elapsed" rate-limits on its own clock rather than sharing the
44
+ # periodic check's budget. $2=1 drops the stamp again if the download fails, for
45
+ # callers recovering from a known-bad state where waiting out the full interval
46
+ # on a transient network error is worse than retrying next turn.
39
47
  weave_self_refresh() {
48
+ local stamp_suffix="${1:-}"
49
+ local retry_on_fail="${2:-0}"
50
+
40
51
  [ "${WEAVE_STATUSLINE_UPDATE:-1}" = "0" ] && return 0
41
52
  command -v curl >/dev/null 2>&1 || return 0
42
53
 
@@ -53,7 +64,7 @@ weave_self_refresh() {
53
64
  mkdir -p "$cache_dir" 2>/dev/null || return 0
54
65
  local script_slug
55
66
  script_slug="$(printf '%s' "$self" | tr -c 'A-Za-z0-9._-' '_')"
56
- local stamp="$cache_dir/checked-at${script_slug}"
67
+ local stamp="$cache_dir/checked-at${script_slug}${stamp_suffix}"
57
68
 
58
69
  local now stamp_mtime
59
70
  now="$(date +%s 2>/dev/null)" || return 0
@@ -77,7 +88,13 @@ weave_self_refresh() {
77
88
  : > "$stamp" 2>/dev/null || return 0
78
89
 
79
90
  local url="${WEAVE_STATUSLINE_URL:-https://raw.githubusercontent.com/workweave/router/main/install/cc-statusline.sh}"
80
- local tmp="${self}.tmp.$$"
91
+ # $$ alone is not unique: two calls can run in one invocation (the periodic
92
+ # check and a pricing-miss retry both fire on a cold cache) and would then
93
+ # curl -o into the same path and mv over each other, installing a truncated
94
+ # script that can never self-heal. The stamp suffix is the right key — it is
95
+ # what makes two callers mutually exclusive in the first place, so callers
96
+ # that could overlap necessarily have different suffixes.
97
+ local tmp="${self}.tmp.$$${stamp_suffix}"
81
98
  (
82
99
  # Detach stdin (CC pipes JSON to us) so curl can't accidentally consume
83
100
  # it, and silence all output so nothing leaks into the statusline.
@@ -96,6 +113,11 @@ weave_self_refresh() {
96
113
  fi
97
114
  else
98
115
  rm -f "$tmp"
116
+ # A download that never landed shouldn't spend the caller's whole
117
+ # interval; drop the stamp so the next turn can try again.
118
+ if [ "$retry_on_fail" = "1" ]; then
119
+ rm -f "$stamp"
120
+ fi
99
121
  fi
100
122
  ) >/dev/null 2>&1 &
101
123
  disown 2>/dev/null || true
@@ -142,7 +164,7 @@ prices='{
142
164
  "claude-sonnet-4-6": 0.003,
143
165
  "claude-sonnet-5": 0.003,
144
166
  "deepseek/deepseek-v4-flash": 0.0001134,
145
- "deepseek/deepseek-v4-pro": 0.001318,
167
+ "deepseek/deepseek-v4-pro": 0.00174,
146
168
  "gemini-2.0-flash": 0.0001,
147
169
  "gemini-2.0-flash-lite": 0.000075,
148
170
  "gemini-2.5-flash": 0.0003,
@@ -177,6 +199,7 @@ prices='{
177
199
  "gpt-5.6-sol": 0.005,
178
200
  "gpt-5.6-terra": 0.0025,
179
201
  "grok-4.5": 0.002,
202
+ "grok-4.6": 0.002,
180
203
  "minimax/minimax-m2.7": 0.0003,
181
204
  "minimax/minimax-m3": 0.0003,
182
205
  "mistralai/mistral-small-2603": 0.0002,
@@ -190,8 +213,9 @@ prices='{
190
213
  "qwen/qwen3-coder-next": 0.0005,
191
214
  "qwen/qwen3-next-80b-a3b-instruct": 0.00015,
192
215
  "qwen/qwen3.5-flash-02-23": 0.00005,
193
- "qwen/qwen3.6-35b-a3b": 0.000172,
216
+ "qwen/qwen3.6-35b-a3b": 0.00015,
194
217
  "qwen/qwen3.7-plus": 0.0004,
218
+ "qwen/qwen3.8-max": 0.002,
195
219
  "xiaomi/mimo-v2.5-pro": 0.001,
196
220
  "z-ai/glm-5": 0.001,
197
221
  "z-ai/glm-5.1": 0.0014,
@@ -211,7 +235,7 @@ prices='{
211
235
  "claude-sonnet-4-6": 0.015,
212
236
  "claude-sonnet-5": 0.015,
213
237
  "deepseek/deepseek-v4-flash": 0.0002791,
214
- "deepseek/deepseek-v4-pro": 0.0026361,
238
+ "deepseek/deepseek-v4-pro": 0.00348,
215
239
  "gemini-2.0-flash": 0.0004,
216
240
  "gemini-2.0-flash-lite": 0.0003,
217
241
  "gemini-2.5-flash": 0.0012,
@@ -246,6 +270,7 @@ prices='{
246
270
  "gpt-5.6-sol": 0.03,
247
271
  "gpt-5.6-terra": 0.015,
248
272
  "grok-4.5": 0.006,
273
+ "grok-4.6": 0.006,
249
274
  "minimax/minimax-m2.7": 0.0012,
250
275
  "minimax/minimax-m3": 0.0012,
251
276
  "mistralai/mistral-small-2603": 0.0006,
@@ -259,8 +284,9 @@ prices='{
259
284
  "qwen/qwen3-coder-next": 0.0012,
260
285
  "qwen/qwen3-next-80b-a3b-instruct": 0.0012,
261
286
  "qwen/qwen3.5-flash-02-23": 0.00015,
262
- "qwen/qwen3.6-35b-a3b": 0.0012002,
287
+ "qwen/qwen3.6-35b-a3b": 0.001,
263
288
  "qwen/qwen3.7-plus": 0.0016,
289
+ "qwen/qwen3.8-max": 0.006,
264
290
  "xiaomi/mimo-v2.5-pro": 0.003,
265
291
  "z-ai/glm-5": 0.0032,
266
292
  "z-ai/glm-5.1": 0.0044,
@@ -399,6 +425,42 @@ if [[ -n "$transcript_path" && -f "$transcript_path" ]]; then
399
425
  ) || true
400
426
  fi
401
427
 
428
+ # ---------- refresh when a model isn't in the pricing table ----------
429
+ #
430
+ # A model that shipped after this copy of the script has no price entry, so the
431
+ # jq guard above zeroes savings on every turn and the line reads "saved $0.00"
432
+ # — indistinguishable from "the router ran and didn't beat your selection". The
433
+ # periodic check heals that eventually, but a week late, and a model launch is
434
+ # exactly when the number gets looked at. Refresh off-schedule instead.
435
+ #
436
+ # Keyed on its own stamp per unpriced id: a model we never price (self-hosted,
437
+ # unrecognized) then costs one download per interval, not one per turn, and
438
+ # can't starve the periodic check.
439
+ weave_refresh_on_price_miss() {
440
+ local candidates="" m
441
+ for m in "$@"; do
442
+ case "$m" in
443
+ "" | "?" | failure | weave-router | "<synthetic>") continue ;;
444
+ esac
445
+ candidates="${candidates}${m}"$'\n'
446
+ done
447
+ [ -n "$candidates" ] || return 0
448
+
449
+ # A malformed $prices emits nothing here, which fails closed (no refresh)
450
+ # rather than re-downloading every turn.
451
+ local missing
452
+ missing="$(printf '%s' "$candidates" \
453
+ | jq -rR --argjson p "$prices" \
454
+ 'select($p.input[.] == null or $p.output[.] == null)' 2>/dev/null \
455
+ | head -n 1)" || return 0
456
+ [ -n "$missing" ] || return 0
457
+
458
+ local model_slug
459
+ model_slug="$(printf '%s' "$missing" | tr -c 'A-Za-z0-9._-' '_')"
460
+ weave_self_refresh ".miss.${model_slug}" 1
461
+ }
462
+ weave_refresh_on_price_miss "$requested_norm" "$routed" 2>/dev/null || true
463
+
402
464
  # Brand color (#FF6C47) on terminals that grok 24-bit truecolor — that's
403
465
  # every modern one (iTerm2, Apple Terminal, vscode, ghostty, alacritty,
404
466
  # wezterm, kitty). Falls back gracefully on any escape-stripping terminal.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: disable-routing
3
+ description: "Switch Codex back to its normal provider for future sessions."
4
+ ---
5
+
6
+ <!-- weave-router managed disable-routing skill -->
7
+
8
+ # Disable Weave routing
9
+
10
+ When the user invokes `$disable-routing`, switch the current Codex installation
11
+ off the Weave Router without logging out or deleting its router configuration.
12
+
13
+ 1. Explain that the change takes effect on the next `codex` launch and that it
14
+ can later be reversed with `npx --package @workweave/router -y -- weave-router on --codex{{SCOPE}}`.
15
+ 2. Run exactly:
16
+
17
+ ```bash
18
+ npx --package @workweave/router -y -- weave-router off --codex{{SCOPE}}
19
+ ```
20
+
21
+ 3. Report the command result. Do not uninstall the router or alter any other
22
+ Codex settings.