@workweave/router 0.1.7 → 0.1.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/install.sh +90 -10
- package/package.json +1 -1
package/install.sh
CHANGED
|
@@ -1035,6 +1035,19 @@ json_get() {
|
|
|
1035
1035
|
jq -r "${2} // empty" "$1" 2>/dev/null || true
|
|
1036
1036
|
}
|
|
1037
1037
|
|
|
1038
|
+
# claude_key_present returns 0 when the given settings file's
|
|
1039
|
+
# env.ANTHROPIC_CUSTOM_HEADERS carries the router key header. "On" is only valid
|
|
1040
|
+
# when this is true: in project scope the committed settings.json holds the
|
|
1041
|
+
# router URL but the key header lives only in the per-teammate settings.local.json
|
|
1042
|
+
# (or the parked sidecar), so a router URL alone doesn't mean requests can
|
|
1043
|
+
# authenticate.
|
|
1044
|
+
claude_key_present() {
|
|
1045
|
+
case "$(json_get "$1" '.env.ANTHROPIC_CUSTOM_HEADERS')" in
|
|
1046
|
+
*X-Weave-Router-Key*) return 0 ;;
|
|
1047
|
+
*) return 1 ;;
|
|
1048
|
+
esac
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1038
1051
|
# gitignore_add appends an entry to the repo .gitignore in project scope so a
|
|
1039
1052
|
# parked sidecar (which may carry the router key header) never gets committed.
|
|
1040
1053
|
# No-op for user scope and --dir, matching how install handles its own ignores.
|
|
@@ -1056,6 +1069,15 @@ toggle_claude() {
|
|
|
1056
1069
|
else
|
|
1057
1070
|
active="$settings_file"
|
|
1058
1071
|
fi
|
|
1072
|
+
# Symlink containment for the parked sidecar: project/--dir paths come from a
|
|
1073
|
+
# possibly-hostile repo, and `off` writes $parked via shell redirection (which
|
|
1074
|
+
# follows symlinks). A repo could pre-place it as a symlink to clobber an
|
|
1075
|
+
# arbitrary file or siphon the router-key-bearing parked data. The config
|
|
1076
|
+
# files themselves are already guarded during path resolution; this covers the
|
|
1077
|
+
# sidecar. User scope ($HOME) is trusted, matching the installer.
|
|
1078
|
+
if [ "$scope" = "project" ] || [ -n "$install_dir" ]; then
|
|
1079
|
+
refuse_if_symlink "$parked"
|
|
1080
|
+
fi
|
|
1059
1081
|
local parked_present="false"
|
|
1060
1082
|
if [ -f "$parked" ]; then parked_present="true"; fi
|
|
1061
1083
|
committed_base="$(json_get "$settings_file" '.env.ANTHROPIC_BASE_URL')"
|
|
@@ -1071,12 +1093,23 @@ toggle_claude() {
|
|
|
1071
1093
|
if [ -n "$local_base" ] && ! router_shaped_url "$local_base"; then
|
|
1072
1094
|
ok "Claude Code (project): ${C_BOLD}off${C_RESET} — routing directly to Anthropic. Run '$on_hint' to re-enable."
|
|
1073
1095
|
elif router_shaped_url "$committed_base"; then
|
|
1074
|
-
|
|
1096
|
+
# Router URL is committed, but it only authenticates if this teammate's
|
|
1097
|
+
# settings.local.json carries the key header. A fresh clone (shared
|
|
1098
|
+
# settings.json, no personal local file) has the URL but no key.
|
|
1099
|
+
if claude_key_present "$local_settings_file"; then
|
|
1100
|
+
ok "Claude Code (project): ${C_BOLD}on${C_RESET} — routing through $committed_base."
|
|
1101
|
+
else
|
|
1102
|
+
warn "Claude Code (project): router URL is set but your personal router key is missing (no settings.local.json) — requests won't authenticate. Run the installer to add your key."
|
|
1103
|
+
fi
|
|
1075
1104
|
else
|
|
1076
1105
|
info "Claude Code (project): not configured for the router. Run the installer first."
|
|
1077
1106
|
fi
|
|
1078
1107
|
elif router_shaped_url "$committed_base"; then
|
|
1079
|
-
|
|
1108
|
+
if claude_key_present "$settings_file"; then
|
|
1109
|
+
ok "Claude Code: ${C_BOLD}on${C_RESET} — routing through $committed_base."
|
|
1110
|
+
else
|
|
1111
|
+
warn "Claude Code: router URL is set but the router key header is missing — requests won't authenticate. Run the installer to restore it."
|
|
1112
|
+
fi
|
|
1080
1113
|
else
|
|
1081
1114
|
info "Claude Code: not configured for the router. Run the installer first."
|
|
1082
1115
|
fi
|
|
@@ -1125,22 +1158,53 @@ toggle_claude() {
|
|
|
1125
1158
|
if [ "$proj" = "true" ]; then
|
|
1126
1159
|
local_base="$(json_get "$local_settings_file" '.env.ANTHROPIC_BASE_URL')"
|
|
1127
1160
|
if [ -n "$local_base" ] && ! router_shaped_url "$local_base"; then
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1161
|
+
# We're off, but the parked sidecar is gone. The router key header
|
|
1162
|
+
# lives only in the local file / sidecar in project scope — never in
|
|
1163
|
+
# committed settings.json — so we can only re-enable cleanly if the
|
|
1164
|
+
# header survived in the local file. If it didn't, clearing the
|
|
1165
|
+
# override would point Claude Code at the router with no auth
|
|
1166
|
+
# (401s); leave the working direct setup in place and tell the user
|
|
1167
|
+
# to reinstall instead of faking success.
|
|
1168
|
+
if claude_key_present "$local_settings_file"; then
|
|
1169
|
+
merged="$(jq '(.env // {}) |= del(.ANTHROPIC_BASE_URL)
|
|
1170
|
+
| (if (.env // {} | length) == 0 then del(.env) else . end)' "$local_settings_file")"
|
|
1171
|
+
printf '%s\n' "$merged" >"$local_settings_file"
|
|
1172
|
+
chmod 600 "$local_settings_file"
|
|
1173
|
+
ok "Claude Code is now ${C_BOLD}on${C_RESET} (routing through the Weave Router). Restart Claude Code for it to take effect."
|
|
1174
|
+
else
|
|
1175
|
+
warn "Claude Code is off and the parked router key is missing (its sidecar was deleted). Re-run the installer to restore the router key — leaving the current direct-to-Anthropic setup in place so requests don't fail auth."
|
|
1176
|
+
fi
|
|
1133
1177
|
return 0
|
|
1134
1178
|
fi
|
|
1135
1179
|
fi
|
|
1136
|
-
|
|
1180
|
+
# No direct override (or user scope). "On" requires both the router URL
|
|
1181
|
+
# and the key header — a committed router URL with no local key (e.g. a
|
|
1182
|
+
# fresh clone) can't authenticate, so don't claim it's already on.
|
|
1183
|
+
if ! router_shaped_url "$committed_base"; then
|
|
1184
|
+
warn "No parked router config found. Run the installer to set up Claude Code."
|
|
1185
|
+
elif claude_key_present "$active"; then
|
|
1137
1186
|
ok "Claude Code is already on — nothing to do."
|
|
1138
1187
|
else
|
|
1139
|
-
|
|
1188
|
+
# $active is settings.local.json in project scope, settings.json for
|
|
1189
|
+
# user/--dir — name the right file so the hint isn't misleading.
|
|
1190
|
+
warn "Router URL is set but the router key is missing — run the installer to add your key (written to $(basename "$active"))."
|
|
1140
1191
|
fi
|
|
1141
1192
|
return 0
|
|
1142
1193
|
fi
|
|
1143
|
-
|
|
1194
|
+
# Sidecar present: restore it — but only if the result actually carries
|
|
1195
|
+
# the router key. An off that ran with an empty/absent settings.local.json
|
|
1196
|
+
# parks {"env":{}}; blindly restoring that would drop the direct override
|
|
1197
|
+
# and leave the committed router URL unauthenticated while printing
|
|
1198
|
+
# success. Refuse that and tell the user to reinstall.
|
|
1199
|
+
parked_env="$(jq -c '.env // {}' "$parked")"
|
|
1200
|
+
parked_has_key="false"
|
|
1201
|
+
if printf '%s' "$parked_env" | jq -e '(.ANTHROPIC_CUSTOM_HEADERS // "") | test("X-Weave-Router-Key")' >/dev/null 2>&1; then
|
|
1202
|
+
parked_has_key="true"
|
|
1203
|
+
fi
|
|
1204
|
+
if [ "$parked_has_key" != "true" ] && ! claude_key_present "$active"; then
|
|
1205
|
+
warn "Can't re-enable: the parked config has no router key (it was created without one). Re-run the installer to set up your router key — leaving the current direct-to-Anthropic setup in place."
|
|
1206
|
+
return 0
|
|
1207
|
+
fi
|
|
1144
1208
|
merged="$(jq --argjson p "$parked_env" '.env = (((.env // {}) | del(.ANTHROPIC_BASE_URL)) + $p)' "$active")"
|
|
1145
1209
|
printf '%s\n' "$merged" >"$active"
|
|
1146
1210
|
[ "$proj" = "true" ] && chmod 600 "$active"
|
|
@@ -1206,6 +1270,12 @@ toggle_codex() {
|
|
|
1206
1270
|
toggle_opencode() {
|
|
1207
1271
|
local f="$opencode_config_file" parked="$opencode_dir/.weave-parked.json"
|
|
1208
1272
|
local model="" has_weave="false" parked_present="false" on="false" restore_model merged
|
|
1273
|
+
# Symlink containment for the parked sidecar — `off` writes it via shell
|
|
1274
|
+
# redirection; a hostile project repo could pre-place it as a symlink. The
|
|
1275
|
+
# opencode.json itself is already guarded during path resolution.
|
|
1276
|
+
if [ "$scope" = "project" ] || [ -n "$install_dir" ]; then
|
|
1277
|
+
refuse_if_symlink "$parked"
|
|
1278
|
+
fi
|
|
1209
1279
|
if [ -f "$parked" ]; then parked_present="true"; fi
|
|
1210
1280
|
if [ -f "$f" ]; then
|
|
1211
1281
|
model="$(jq -r '.model // empty' "$f" 2>/dev/null || true)"
|
|
@@ -1243,6 +1313,16 @@ toggle_opencode() {
|
|
|
1243
1313
|
restore_model="$(jq -r '.model // "weave/claude-sonnet-4-6"' "$parked")"
|
|
1244
1314
|
elif [ "$has_weave" != "true" ]; then
|
|
1245
1315
|
warn "opencode isn't configured for the router. Run the installer first."; return 0
|
|
1316
|
+
else
|
|
1317
|
+
# No parked model (sidecar deleted by hand). Derive the default from the
|
|
1318
|
+
# installed provider.weave.models block rather than a hardcoded literal
|
|
1319
|
+
# that silently diverges when the installer's default changes — prefer a
|
|
1320
|
+
# sonnet entry, else the first model the installer registered.
|
|
1321
|
+
restore_model="$(jq -r '
|
|
1322
|
+
(.provider.weave.models // {} | keys) as $k
|
|
1323
|
+
| (([$k[] | select(test("sonnet"))] | first) // $k[0] // "claude-sonnet-4-6")
|
|
1324
|
+
| "weave/" + .
|
|
1325
|
+
' "$f" 2>/dev/null || echo "weave/claude-sonnet-4-6")"
|
|
1246
1326
|
fi
|
|
1247
1327
|
merged="$(jq --arg m "$restore_model" '.model = $m' "$f")"
|
|
1248
1328
|
printf '%s\n' "$merged" >"$f"
|