@workweave/router 0.1.0
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 +47 -0
- package/bin.js +55 -0
- package/cc-statusline.sh +365 -0
- package/install.sh +936 -0
- package/package.json +36 -0
- package/uninstall.sh +170 -0
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@workweave/router",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "One-command installer that points Claude Code at the Weave Router.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"weave-router": "bin.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin.js",
|
|
10
|
+
"install.sh",
|
|
11
|
+
"uninstall.sh",
|
|
12
|
+
"cc-statusline.sh",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"prepack": "node ./scripts/copy-installer.js"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/workweave/router.git",
|
|
24
|
+
"directory": "install/npm"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://weave.ai",
|
|
27
|
+
"license": "Apache-2.0",
|
|
28
|
+
"keywords": [
|
|
29
|
+
"weave",
|
|
30
|
+
"router",
|
|
31
|
+
"claude",
|
|
32
|
+
"claude-code",
|
|
33
|
+
"anthropic",
|
|
34
|
+
"llm-router"
|
|
35
|
+
]
|
|
36
|
+
}
|
package/uninstall.sh
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Weave Router uninstaller for Claude Code.
|
|
4
|
+
#
|
|
5
|
+
# Removes the env vars, statusLine, and local router auth that install.sh added.
|
|
6
|
+
# Leaves the rest of settings.json untouched.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# npx @workweave/router --uninstall # user scope
|
|
10
|
+
# npx @workweave/router --uninstall --scope project # run inside the repo
|
|
11
|
+
# npx @workweave/router --uninstall --dir /tmp/test # --dir alone (user scope, .weave/)
|
|
12
|
+
# npx @workweave/router --uninstall --scope project --dir /tmp # --dir + project scope (.claude/)
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
scope="user"
|
|
17
|
+
scope_explicit="false"
|
|
18
|
+
install_dir=""
|
|
19
|
+
|
|
20
|
+
err() { printf "\033[31merror:\033[0m %s\n" "$*" >&2; }
|
|
21
|
+
info() { printf "\033[36m==>\033[0m %s\n" "$*"; }
|
|
22
|
+
ok() { printf "\033[32m✓\033[0m %s\n" "$*"; }
|
|
23
|
+
|
|
24
|
+
# Refuse to write/delete through a symlink. Project scope reads paths from the
|
|
25
|
+
# user's git repo; a hostile checkout could ship `.claude/settings.json` (or
|
|
26
|
+
# `.claude/cc-statusline.sh`) as a symlink to e.g. `~/.ssh/authorized_keys`,
|
|
27
|
+
# and the uninstaller's `>` redirect or `rm` would silently follow that link.
|
|
28
|
+
refuse_if_symlink() {
|
|
29
|
+
local target="$1"
|
|
30
|
+
if [ -L "$target" ]; then
|
|
31
|
+
err "$target is a symlink (-> $(readlink "$target")). Refusing to operate on it."
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
while [ $# -gt 0 ]; do
|
|
37
|
+
case "$1" in
|
|
38
|
+
--scope)
|
|
39
|
+
scope="${2:-}"; shift 2
|
|
40
|
+
[ "$scope" = "user" ] || [ "$scope" = "project" ] || { err "--scope must be 'user' or 'project'"; exit 2; }
|
|
41
|
+
scope_explicit="true"
|
|
42
|
+
;;
|
|
43
|
+
--dir)
|
|
44
|
+
install_dir="${2:-}"; shift 2
|
|
45
|
+
[ -n "$install_dir" ] || { err "--dir requires a path"; exit 2; }
|
|
46
|
+
;;
|
|
47
|
+
-h|--help)
|
|
48
|
+
# awk avoids GNU `head -n -<N>` (rejected by BSD head on macOS).
|
|
49
|
+
awk 'NR<2 { next } /^set -euo/ { exit } { sub(/^# ?/, ""); print }' "$0"
|
|
50
|
+
exit 0
|
|
51
|
+
;;
|
|
52
|
+
*)
|
|
53
|
+
err "unknown flag: $1"; exit 2
|
|
54
|
+
;;
|
|
55
|
+
esac
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
59
|
+
err "jq is required."
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Resolve the base directory. When --dir is given, use it directly.
|
|
64
|
+
if [ -n "$install_dir" ]; then
|
|
65
|
+
install_dir="$(cd "$install_dir" 2>/dev/null && pwd || echo "$install_dir")"
|
|
66
|
+
settings_file="$install_dir/.claude/settings.json"
|
|
67
|
+
local_settings_file=""
|
|
68
|
+
# --dir alone (scope defaults to "user") uses .weave/; --dir --scope project
|
|
69
|
+
# uses .claude/. Match the installer's scope-dependent statusline placement.
|
|
70
|
+
if [ "$scope" = "project" ]; then
|
|
71
|
+
statusline_file="$install_dir/.claude/cc-statusline.sh"
|
|
72
|
+
else
|
|
73
|
+
statusline_file="$install_dir/.weave/cc-statusline.sh"
|
|
74
|
+
fi
|
|
75
|
+
# Symlink containment: --dir paths come from a user-supplied directory that may
|
|
76
|
+
# be hostile. The later `>` redirect on settings_file and `rm -f` on the
|
|
77
|
+
# statusline script would otherwise follow links out of the directory.
|
|
78
|
+
refuse_if_symlink "$install_dir/.claude"
|
|
79
|
+
refuse_if_symlink "$settings_file"
|
|
80
|
+
refuse_if_symlink "$statusline_file"
|
|
81
|
+
elif [ "$scope" = "user" ]; then
|
|
82
|
+
settings_file="$HOME/.claude/settings.json"
|
|
83
|
+
local_settings_file=""
|
|
84
|
+
statusline_file="$HOME/.weave/cc-statusline.sh"
|
|
85
|
+
else
|
|
86
|
+
# Project scope without --dir: mirror install.sh — directory prompt only when
|
|
87
|
+
# scope_explicit is false (interactive install path); explicit --scope project
|
|
88
|
+
# uses the git root of CWD with no prompt.
|
|
89
|
+
project_dir=""
|
|
90
|
+
if [ "$scope_explicit" = "false" ] && [ -r /dev/tty ]; then
|
|
91
|
+
default_project_dir="$(pwd)"
|
|
92
|
+
printf "Project directory to uninstall from [default: %s]: " "$default_project_dir"
|
|
93
|
+
read -r project_dir_choice </dev/tty || project_dir_choice=""
|
|
94
|
+
project_dir="${project_dir_choice:-$default_project_dir}"
|
|
95
|
+
case "$project_dir" in
|
|
96
|
+
"~") project_dir="$HOME" ;;
|
|
97
|
+
"~/"*) project_dir="$HOME/${project_dir#~/}" ;;
|
|
98
|
+
esac
|
|
99
|
+
if [ ! -d "$project_dir" ]; then
|
|
100
|
+
err "directory does not exist: $project_dir"
|
|
101
|
+
exit 1
|
|
102
|
+
fi
|
|
103
|
+
project_dir="$(cd "$project_dir" && pwd)"
|
|
104
|
+
fi
|
|
105
|
+
if [ -n "${project_dir:-}" ]; then
|
|
106
|
+
settings_base="$project_dir"
|
|
107
|
+
else
|
|
108
|
+
if ! git_root="$(git rev-parse --show-toplevel 2>/dev/null)"; then
|
|
109
|
+
err "--scope project must be run inside a git repo, or use --dir <path>."
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
settings_base="$git_root"
|
|
113
|
+
fi
|
|
114
|
+
settings_file="$settings_base/.claude/settings.json"
|
|
115
|
+
local_settings_file="$settings_base/.claude/settings.local.json"
|
|
116
|
+
statusline_file="$settings_base/.claude/cc-statusline.sh"
|
|
117
|
+
# Symlink containment: paths come from a git repo or user-supplied directory
|
|
118
|
+
# that may be hostile. The later `>` redirect on settings_file and `rm -f` on
|
|
119
|
+
# the scripts would otherwise follow links out of the repo.
|
|
120
|
+
refuse_if_symlink "$settings_base/.claude"
|
|
121
|
+
refuse_if_symlink "$settings_file"
|
|
122
|
+
refuse_if_symlink "$local_settings_file"
|
|
123
|
+
refuse_if_symlink "$statusline_file"
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
if [ -f "$settings_file" ]; then
|
|
127
|
+
# Only remove keys we actually installed: scrub our two env vars, and only
|
|
128
|
+
# delete `statusLine` / `apiKeyHelper` when they point at scripts this
|
|
129
|
+
# installer used in older versions. Otherwise an unrelated user-configured
|
|
130
|
+
# statusLine or apiKeyHelper would be silently clobbered.
|
|
131
|
+
cleaned="$(jq '
|
|
132
|
+
if .env then
|
|
133
|
+
.env |= (del(.ANTHROPIC_BASE_URL, .ANTHROPIC_AUTH_TOKEN, .ANTHROPIC_CUSTOM_HEADERS))
|
|
134
|
+
| (if (.env | length) == 0 then del(.env) else . end)
|
|
135
|
+
else . end
|
|
136
|
+
| (if (.statusLine.command // "" | tostring | endswith("cc-statusline.sh"))
|
|
137
|
+
then del(.statusLine) else . end)
|
|
138
|
+
| (if (.apiKeyHelper // "" | tostring | endswith("weave-key.sh"))
|
|
139
|
+
then del(.apiKeyHelper) else . end)
|
|
140
|
+
' "$settings_file")"
|
|
141
|
+
printf '%s\n' "$cleaned" >"$settings_file"
|
|
142
|
+
ok "Cleaned $settings_file"
|
|
143
|
+
else
|
|
144
|
+
info "No settings file at $settings_file (already uninstalled?)"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
if [ -n "$local_settings_file" ] && [ -f "$local_settings_file" ]; then
|
|
148
|
+
cleaned="$(jq '
|
|
149
|
+
if .env then
|
|
150
|
+
.env |= (del(.ANTHROPIC_AUTH_TOKEN, .ANTHROPIC_CUSTOM_HEADERS))
|
|
151
|
+
| (if (.env | length) == 0 then del(.env) else . end)
|
|
152
|
+
else . end
|
|
153
|
+
| del(.apiKeyHelper)
|
|
154
|
+
' "$local_settings_file")"
|
|
155
|
+
printf '%s\n' "$cleaned" >"$local_settings_file"
|
|
156
|
+
ok "Cleaned $local_settings_file"
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
for f in "$statusline_file"; do
|
|
160
|
+
if [ -f "$f" ]; then
|
|
161
|
+
rm -f "$f"
|
|
162
|
+
ok "Removed $f"
|
|
163
|
+
fi
|
|
164
|
+
done
|
|
165
|
+
|
|
166
|
+
if [ -n "$install_dir" ]; then
|
|
167
|
+
ok "Weave Router uninstalled from $install_dir."
|
|
168
|
+
else
|
|
169
|
+
ok "Weave Router uninstalled (scope=$scope)."
|
|
170
|
+
fi
|