claude-multiacc 1.0.13 → 1.0.15
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 +276 -15
- package/bin/claude +565 -46
- package/bin/claude-accounts +447 -75
- package/bin/codex +254 -26
- package/bin/codex-accounts +157 -52
- package/install.sh +196 -69
- package/lib/common.sh +262 -2
- package/lib/credential.py +336 -0
- package/lib/report.py +434 -0
- package/package.json +1 -1
- package/tests/run-tests.sh +1556 -8
package/install.sh
CHANGED
|
@@ -4,41 +4,128 @@
|
|
|
4
4
|
# its install dir, or its update machinery.
|
|
5
5
|
#
|
|
6
6
|
# ./install.sh [--server user@host] install or update (data untouched)
|
|
7
|
+
# ./install.sh --no-server install with NO sync target (local-only pool:
|
|
8
|
+
# a panel/runner daemon distributes accounts)
|
|
9
|
+
# ./install.sh --instance NAME label this install's agents (default: derived
|
|
10
|
+
# from the pool root when it is not the default)
|
|
7
11
|
# ./install.sh --uninstall remove addon (keeps ~/.claude-accounts)
|
|
8
12
|
# ./install.sh --uninstall --purge-data remove addon + all account data
|
|
13
|
+
#
|
|
14
|
+
# INSTANCE-SCOPED POOLS: export CLAUDE_ACCOUNTS_ROOT / CODEX_ACCOUNTS_ROOT before
|
|
15
|
+
# running this and the whole install serves that pool — its LaunchAgents/cron entries
|
|
16
|
+
# get their own labels and carry the roots in their environment, so several app-robot
|
|
17
|
+
# instances on one machine never touch each other's accounts or agents.
|
|
9
18
|
set -u
|
|
10
19
|
|
|
11
20
|
REPO_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
|
12
21
|
# shellcheck source=lib/common.sh
|
|
13
22
|
. "$REPO_DIR/lib/common.sh"
|
|
14
23
|
# common.sh is sourced in claude mode (ACC_ROOT = the claude pool); the codex pool
|
|
15
|
-
# root is needed here too for uninstall/purge messaging.
|
|
16
|
-
CODEX_ACC_ROOT="${CODEX_ACCOUNTS_DIR:-$HOME/.codex-accounts}"
|
|
17
|
-
|
|
18
|
-
MARK_BEGIN="# >>> claude-multiacc >>>"
|
|
19
|
-
MARK_END="# <<< claude-multiacc <<<"
|
|
20
|
-
CRON_TAG="# claude-multiacc"
|
|
21
|
-
PLIST_LIMITS="$HOME/Library/LaunchAgents/com.claude-multiacc.limits.plist"
|
|
22
|
-
PLIST_HEALTH="$HOME/Library/LaunchAgents/com.claude-multiacc.health.plist"
|
|
23
|
-
PLIST_UPDATE="$HOME/Library/LaunchAgents/com.claude-multiacc.update.plist"
|
|
24
|
-
PLIST_CODEX_LIMITS="$HOME/Library/LaunchAgents/com.claude-multiacc.codex-limits.plist"
|
|
25
|
-
PLIST_CODEX_HEALTH="$HOME/Library/LaunchAgents/com.claude-multiacc.codex-health.plist"
|
|
26
|
-
PROFILED="/etc/profile.d/claude-multiacc.sh"
|
|
24
|
+
# root is needed here too for the codex agents and uninstall/purge messaging.
|
|
25
|
+
CODEX_ACC_ROOT="${CODEX_ACCOUNTS_ROOT:-${CODEX_ACCOUNTS_DIR:-$HOME/.codex-accounts}}"
|
|
27
26
|
|
|
28
27
|
UNINSTALL=0
|
|
29
28
|
PURGE=0
|
|
30
29
|
SERVER_OVERRIDE=""
|
|
31
30
|
NO_SCHEDULE=0
|
|
31
|
+
INSTANCE_OVERRIDE=""
|
|
32
32
|
while [ $# -gt 0 ]; do
|
|
33
33
|
case "$1" in
|
|
34
34
|
--uninstall) UNINSTALL=1; shift ;;
|
|
35
35
|
--purge-data) PURGE=1; shift ;;
|
|
36
36
|
--server) SERVER_OVERRIDE="${2:?--server requires a value}"; shift 2 ;;
|
|
37
|
+
--no-server) SERVER_OVERRIDE="none"; shift ;;
|
|
38
|
+
--instance) INSTANCE_OVERRIDE="${2:?--instance requires a name}"; shift 2 ;;
|
|
37
39
|
--no-schedule) NO_SCHEDULE=1; shift ;;
|
|
38
40
|
*) echo "unknown flag: $1" >&2; exit 1 ;;
|
|
39
41
|
esac
|
|
40
42
|
done
|
|
41
43
|
|
|
44
|
+
# ---- instance scoping -----------------------------------------------------------
|
|
45
|
+
# An install whose pool roots are the defaults keeps the historical labels and rc
|
|
46
|
+
# blocks byte-for-byte. Anything else is an INSTANCE: its agents get a suffix (two
|
|
47
|
+
# instances would otherwise overwrite each other's plists) and the shell rc block is
|
|
48
|
+
# left alone, because one interactive PATH cannot point at two pools at once.
|
|
49
|
+
default_pool_roots() {
|
|
50
|
+
[ "$ACC_ROOT" = "$HOME/.claude-accounts" ] && [ "$CODEX_ACC_ROOT" = "$HOME/.codex-accounts" ]
|
|
51
|
+
}
|
|
52
|
+
short_hash() { # stable 8-char digest of $1, on macOS and Linux alike
|
|
53
|
+
if command -v shasum >/dev/null 2>&1; then printf '%s' "$1" | shasum | cut -c1-8
|
|
54
|
+
elif command -v sha1sum >/dev/null 2>&1; then printf '%s' "$1" | sha1sum | cut -c1-8
|
|
55
|
+
else printf '%s' "$1" | cksum | tr -d ' ' | cut -c1-8; fi
|
|
56
|
+
}
|
|
57
|
+
if [ -n "$INSTANCE_OVERRIDE" ]; then
|
|
58
|
+
case "$INSTANCE_OVERRIDE" in
|
|
59
|
+
*[!A-Za-z0-9-]*) echo "--instance must be letters, digits and dashes: $INSTANCE_OVERRIDE" >&2; exit 1 ;;
|
|
60
|
+
esac
|
|
61
|
+
INSTANCE="$INSTANCE_OVERRIDE"
|
|
62
|
+
elif default_pool_roots; then
|
|
63
|
+
INSTANCE=""
|
|
64
|
+
else
|
|
65
|
+
INSTANCE="i$(short_hash "$ACC_ROOT")"
|
|
66
|
+
fi
|
|
67
|
+
# The roots are interpolated into plists and crontab lines; a quote or newline in one
|
|
68
|
+
# would break out of that context, so such a root is refused up front.
|
|
69
|
+
case "$ACC_ROOT$CODEX_ACC_ROOT" in
|
|
70
|
+
*["'\"<>&"]*|*"
|
|
71
|
+
"*) echo "pool root contains a character that cannot be scheduled safely: $ACC_ROOT / $CODEX_ACC_ROOT" >&2; exit 1 ;;
|
|
72
|
+
esac
|
|
73
|
+
|
|
74
|
+
# A pool root under a temp directory is by definition gone tomorrow, but the launchd
|
|
75
|
+
# agent or crontab line naming it is not: it keeps firing forever against a path that
|
|
76
|
+
# no longer exists, one leaked set per run. That is how a Mac collected 57 orphaned
|
|
77
|
+
# agent sets and a server 4 stray cron blocks. EITHER root disqualifies the whole set,
|
|
78
|
+
# because one install writes agents for both providers: the leaked sets above named a
|
|
79
|
+
# temp CLAUDE root and the REAL codex pool, so they went right on polling a live usage
|
|
80
|
+
# endpoint every five minutes. Install the binaries for such a root; never schedule it.
|
|
81
|
+
ephemeral_root() { # $1 = a pool root
|
|
82
|
+
case "$1" in
|
|
83
|
+
/tmp/*|/private/tmp/*|/var/tmp/*|/private/var/tmp/*|/var/folders/*|/private/var/folders/*)
|
|
84
|
+
return 0 ;;
|
|
85
|
+
*) return 1 ;;
|
|
86
|
+
esac
|
|
87
|
+
}
|
|
88
|
+
if [ "$NO_SCHEDULE" != "1" ] && { ephemeral_root "$ACC_ROOT" || ephemeral_root "$CODEX_ACC_ROOT"; }; then
|
|
89
|
+
NO_SCHEDULE=1
|
|
90
|
+
echo " note: a pool root is under a temp directory (claude: $ACC_ROOT, codex: $CODEX_ACC_ROOT)" >&2
|
|
91
|
+
echo " — installing WITHOUT schedulers, which would outlive it." >&2
|
|
92
|
+
echo " Pass --no-schedule to silence this." >&2
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
LABEL="com.claude-multiacc${INSTANCE:+.$INSTANCE}"
|
|
96
|
+
MARK_BEGIN="# >>> claude-multiacc >>>"
|
|
97
|
+
MARK_END="# <<< claude-multiacc <<<"
|
|
98
|
+
CRON_TAG="# claude-multiacc${INSTANCE:+ $INSTANCE}"
|
|
99
|
+
PLIST_LIMITS="$HOME/Library/LaunchAgents/$LABEL.limits.plist"
|
|
100
|
+
PLIST_HEALTH="$HOME/Library/LaunchAgents/$LABEL.health.plist"
|
|
101
|
+
PLIST_UPDATE="$HOME/Library/LaunchAgents/$LABEL.update.plist"
|
|
102
|
+
PLIST_CODEX_LIMITS="$HOME/Library/LaunchAgents/$LABEL.codex-limits.plist"
|
|
103
|
+
PLIST_CODEX_HEALTH="$HOME/Library/LaunchAgents/$LABEL.codex-health.plist"
|
|
104
|
+
PROFILED="/etc/profile.d/claude-multiacc.sh"
|
|
105
|
+
|
|
106
|
+
# Pool roots (and any sync override) for a scheduled agent, so it refreshes THIS
|
|
107
|
+
# instance's pool. Empty for a default install — nothing changes there.
|
|
108
|
+
plist_env_block() {
|
|
109
|
+
[ -n "$INSTANCE" ] || return 0
|
|
110
|
+
printf ' <key>EnvironmentVariables</key><dict>\n'
|
|
111
|
+
printf ' <key>CLAUDE_ACCOUNTS_ROOT</key><string>%s</string>\n' "$ACC_ROOT"
|
|
112
|
+
printf ' <key>CODEX_ACCOUNTS_ROOT</key><string>%s</string>\n' "$CODEX_ACC_ROOT"
|
|
113
|
+
printf ' </dict>\n'
|
|
114
|
+
}
|
|
115
|
+
cron_env_prefix() {
|
|
116
|
+
[ -n "$INSTANCE" ] || return 0
|
|
117
|
+
printf "CLAUDE_ACCOUNTS_ROOT='%s' CODEX_ACCOUNTS_ROOT='%s' " "$ACC_ROOT" "$CODEX_ACC_ROOT"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
is_pool_root() { # true when $1 is safe to delete as an account pool
|
|
121
|
+
case "$1" in
|
|
122
|
+
''|/|"$HOME") return 1 ;;
|
|
123
|
+
*/.claude-accounts|*/.codex-accounts) return 0 ;;
|
|
124
|
+
/*) [ -f "$1/accounts.json" ] ;;
|
|
125
|
+
*) return 1 ;;
|
|
126
|
+
esac
|
|
127
|
+
}
|
|
128
|
+
|
|
42
129
|
strip_block() { # remove our marked block from a file (portable, no sed -i)
|
|
43
130
|
local f="$1"
|
|
44
131
|
[ -f "$f" ] || return 0
|
|
@@ -78,13 +165,13 @@ mac_schedule_install() {
|
|
|
78
165
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
79
166
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
80
167
|
<plist version="1.0"><dict>
|
|
81
|
-
<key>Label</key><string
|
|
168
|
+
<key>Label</key><string>$LABEL.limits</string>
|
|
82
169
|
<key>ProgramArguments</key><array>
|
|
83
170
|
<string>$REPO_DIR/bin/claude-accounts</string>
|
|
84
171
|
<string>limits</string>
|
|
85
172
|
<string>--quiet</string>
|
|
86
173
|
</array>
|
|
87
|
-
<key>StartInterval</key><integer>
|
|
174
|
+
$(plist_env_block) <key>StartInterval</key><integer>900</integer>
|
|
88
175
|
<key>RunAtLoad</key><true/>
|
|
89
176
|
<key>StandardOutPath</key><string>/dev/null</string>
|
|
90
177
|
<key>StandardErrorPath</key><string>/dev/null</string>
|
|
@@ -94,12 +181,12 @@ EOF
|
|
|
94
181
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
95
182
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
96
183
|
<plist version="1.0"><dict>
|
|
97
|
-
<key>Label</key><string
|
|
184
|
+
<key>Label</key><string>$LABEL.health</string>
|
|
98
185
|
<key>ProgramArguments</key><array>
|
|
99
186
|
<string>$REPO_DIR/bin/claude-accounts</string>
|
|
100
187
|
<string>health</string>
|
|
101
188
|
</array>
|
|
102
|
-
<key>StartCalendarInterval</key><dict>
|
|
189
|
+
$(plist_env_block) <key>StartCalendarInterval</key><dict>
|
|
103
190
|
<key>Weekday</key><integer>1</integer>
|
|
104
191
|
<key>Hour</key><integer>9</integer>
|
|
105
192
|
<key>Minute</key><integer>17</integer>
|
|
@@ -113,13 +200,13 @@ EOF
|
|
|
113
200
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
114
201
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
115
202
|
<plist version="1.0"><dict>
|
|
116
|
-
<key>Label</key><string
|
|
203
|
+
<key>Label</key><string>$LABEL.update</string>
|
|
117
204
|
<key>ProgramArguments</key><array>
|
|
118
205
|
<string>$REPO_DIR/bin/claude-accounts</string>
|
|
119
206
|
<string>self-update</string>
|
|
120
207
|
<string>--quiet</string>
|
|
121
208
|
</array>
|
|
122
|
-
<key>StartCalendarInterval</key><dict>
|
|
209
|
+
$(plist_env_block) <key>StartCalendarInterval</key><dict>
|
|
123
210
|
<key>Hour</key><integer>4</integer>
|
|
124
211
|
<key>Minute</key><integer>7</integer>
|
|
125
212
|
</dict>
|
|
@@ -133,13 +220,13 @@ EOF
|
|
|
133
220
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
134
221
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
135
222
|
<plist version="1.0"><dict>
|
|
136
|
-
<key>Label</key><string
|
|
223
|
+
<key>Label</key><string>$LABEL.codex-limits</string>
|
|
137
224
|
<key>ProgramArguments</key><array>
|
|
138
225
|
<string>$REPO_DIR/bin/codex-accounts</string>
|
|
139
226
|
<string>limits</string>
|
|
140
227
|
<string>--quiet</string>
|
|
141
228
|
</array>
|
|
142
|
-
<key>StartInterval</key><integer>
|
|
229
|
+
$(plist_env_block) <key>StartInterval</key><integer>300</integer>
|
|
143
230
|
<key>RunAtLoad</key><true/>
|
|
144
231
|
<key>StandardOutPath</key><string>/dev/null</string>
|
|
145
232
|
<key>StandardErrorPath</key><string>/dev/null</string>
|
|
@@ -149,12 +236,12 @@ EOF
|
|
|
149
236
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
150
237
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
151
238
|
<plist version="1.0"><dict>
|
|
152
|
-
<key>Label</key><string
|
|
239
|
+
<key>Label</key><string>$LABEL.codex-health</string>
|
|
153
240
|
<key>ProgramArguments</key><array>
|
|
154
241
|
<string>$REPO_DIR/bin/codex-accounts</string>
|
|
155
242
|
<string>health</string>
|
|
156
243
|
</array>
|
|
157
|
-
<key>StartCalendarInterval</key><dict>
|
|
244
|
+
$(plist_env_block) <key>StartCalendarInterval</key><dict>
|
|
158
245
|
<key>Weekday</key><integer>1</integer>
|
|
159
246
|
<key>Hour</key><integer>9</integer>
|
|
160
247
|
<key>Minute</key><integer>37</integer>
|
|
@@ -173,7 +260,7 @@ EOF
|
|
|
173
260
|
launchctl load -w "$PLIST_CODEX_LIMITS" 2>/dev/null || true
|
|
174
261
|
launchctl load -w "$PLIST_CODEX_HEALTH" 2>/dev/null || true
|
|
175
262
|
[ "${CLAUDE_MULTIACC_AUTOUPDATE:-1}" = "1" ] && launchctl load -w "$PLIST_UPDATE" 2>/dev/null || true
|
|
176
|
-
echo " launchd: limits refresh
|
|
263
|
+
echo " launchd: limits refresh (claude 15m / codex 5m) + weekly health checks + daily auto-update"
|
|
177
264
|
}
|
|
178
265
|
|
|
179
266
|
mac_schedule_remove() {
|
|
@@ -185,60 +272,80 @@ mac_schedule_remove() {
|
|
|
185
272
|
rm -f "$PLIST_LIMITS" "$PLIST_HEALTH" "$PLIST_UPDATE" "$PLIST_CODEX_LIMITS" "$PLIST_CODEX_HEALTH"
|
|
186
273
|
}
|
|
187
274
|
|
|
275
|
+
# Only THIS instance's lines are dropped: the tag is matched anchored to end of line,
|
|
276
|
+
# so a default install ("# claude-multiacc") never removes an instance's lines
|
|
277
|
+
# ("# claude-multiacc iXXXXXXXX") or the other way round.
|
|
278
|
+
cron_strip_ours() { grep -v -E "$CRON_TAG\$" || true; }
|
|
279
|
+
|
|
188
280
|
linux_schedule_install() {
|
|
189
|
-
local tmp
|
|
281
|
+
local tmp env
|
|
190
282
|
tmp="$(mktemp)"
|
|
191
|
-
|
|
283
|
+
env="$(cron_env_prefix)"
|
|
284
|
+
{ crontab -l 2>/dev/null | cron_strip_ours; } > "$tmp"
|
|
192
285
|
{
|
|
193
|
-
echo "
|
|
194
|
-
echo "
|
|
195
|
-
echo "17 9 * * 1 $REPO_DIR/bin/claude-accounts health >/dev/null 2>&1 $CRON_TAG"
|
|
196
|
-
echo "37 9 * * 1 $REPO_DIR/bin/codex-accounts health >/dev/null 2>&1 $CRON_TAG"
|
|
286
|
+
echo "*/15 * * * * $env$REPO_DIR/bin/claude-accounts limits --quiet >/dev/null 2>&1 $CRON_TAG"
|
|
287
|
+
echo "*/5 * * * * $env$REPO_DIR/bin/codex-accounts limits --quiet >/dev/null 2>&1 $CRON_TAG"
|
|
288
|
+
echo "17 9 * * 1 $env$REPO_DIR/bin/claude-accounts health >/dev/null 2>&1 $CRON_TAG"
|
|
289
|
+
echo "37 9 * * 1 $env$REPO_DIR/bin/codex-accounts health >/dev/null 2>&1 $CRON_TAG"
|
|
197
290
|
[ "${CLAUDE_MULTIACC_AUTOUPDATE:-1}" = "1" ] && \
|
|
198
|
-
echo "7 4 * * * $REPO_DIR/bin/claude-accounts self-update --quiet >/dev/null 2>&1 $CRON_TAG"
|
|
291
|
+
echo "7 4 * * * $env$REPO_DIR/bin/claude-accounts self-update --quiet >/dev/null 2>&1 $CRON_TAG"
|
|
199
292
|
} >> "$tmp"
|
|
200
293
|
crontab "$tmp"
|
|
201
294
|
rm -f "$tmp"
|
|
202
|
-
echo " cron: limits refresh
|
|
295
|
+
echo " cron: limits refresh (claude 15m / codex 5m) + weekly health checks + daily auto-update"
|
|
203
296
|
}
|
|
204
297
|
|
|
205
298
|
linux_schedule_remove() {
|
|
206
299
|
local tmp
|
|
207
300
|
tmp="$(mktemp)"
|
|
208
|
-
{ crontab -l 2>/dev/null |
|
|
301
|
+
{ crontab -l 2>/dev/null | cron_strip_ours; } > "$tmp"
|
|
209
302
|
crontab "$tmp"
|
|
210
303
|
rm -f "$tmp"
|
|
211
304
|
}
|
|
212
305
|
|
|
213
306
|
do_uninstall() {
|
|
214
307
|
echo "claude-multiacc: uninstalling (restoring stock behavior)"
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
308
|
+
# An instance install never wrote a shell rc block, a /etc/profile.d file or the
|
|
309
|
+
# /usr/local/bin shims — those belong to the DEFAULT install and are shared with it.
|
|
310
|
+
# Removing them here would disable the operator's shim while only one instance was
|
|
311
|
+
# being uninstalled, so an instance removes exactly its own agents.
|
|
312
|
+
if [ -n "$INSTANCE" ]; then
|
|
313
|
+
echo " instance $INSTANCE: removing its agents only (PATH block and shims belong to the default install)"
|
|
314
|
+
else
|
|
315
|
+
strip_block "$HOME/.zshenv"
|
|
316
|
+
strip_block "$HOME/.zprofile"
|
|
317
|
+
strip_block "$HOME/.zshrc"
|
|
318
|
+
strip_block "$HOME/.bashrc"
|
|
319
|
+
strip_block "$HOME/.bash_profile"
|
|
320
|
+
strip_block "$HOME/.profile"
|
|
321
|
+
fi
|
|
221
322
|
if [ "$(machine_kind)" = "mac" ]; then
|
|
222
323
|
mac_schedule_remove
|
|
223
324
|
else
|
|
224
325
|
linux_schedule_remove
|
|
225
|
-
[ -
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
326
|
+
if [ -z "$INSTANCE" ]; then
|
|
327
|
+
[ -f "$PROFILED" ] && rm -f "$PROFILED"
|
|
328
|
+
if [ -L /usr/local/bin/claude ] && [ "$(canon_path /usr/local/bin/claude)" = "$(canon_path "$REPO_DIR/bin/claude")" ]; then
|
|
329
|
+
rm -f /usr/local/bin/claude
|
|
330
|
+
echo " removed /usr/local/bin/claude shim symlink"
|
|
331
|
+
fi
|
|
332
|
+
if [ -L /usr/local/bin/codex ] && [ "$(canon_path /usr/local/bin/codex)" = "$(canon_path "$REPO_DIR/bin/codex")" ]; then
|
|
333
|
+
rm -f /usr/local/bin/codex
|
|
334
|
+
echo " removed /usr/local/bin/codex shim symlink"
|
|
335
|
+
fi
|
|
233
336
|
fi
|
|
234
337
|
fi
|
|
235
338
|
local root
|
|
236
339
|
for root in "$ACC_ROOT" "$CODEX_ACC_ROOT"; do
|
|
237
340
|
if [ "$PURGE" = "1" ]; then
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
341
|
+
# Either the conventional location, or a directory that is provably a pool (it
|
|
342
|
+
# holds a manifest) — an instance root lives anywhere, so the manifest is what
|
|
343
|
+
# makes `rm -rf` safe. Anything else is left alone.
|
|
344
|
+
if is_pool_root "$root"; then
|
|
345
|
+
rm -rf "$root"; echo " purged $root"
|
|
346
|
+
else
|
|
347
|
+
echo " refusing to purge unusual accounts root: $root" >&2
|
|
348
|
+
fi
|
|
242
349
|
fi
|
|
243
350
|
done
|
|
244
351
|
if [ "$PURGE" != "1" ]; then
|
|
@@ -251,6 +358,9 @@ do_install() {
|
|
|
251
358
|
local kind
|
|
252
359
|
kind="$(machine_kind)"
|
|
253
360
|
echo "claude-multiacc: installing (mode: $kind, repo: $REPO_DIR)"
|
|
361
|
+
if [ -n "$INSTANCE" ]; then
|
|
362
|
+
echo " instance: $INSTANCE (agents labelled $LABEL.*, pools $ACC_ROOT + $CODEX_ACC_ROOT)"
|
|
363
|
+
fi
|
|
254
364
|
|
|
255
365
|
[ -x "$REPO_DIR/bin/claude" ] || chmod +x "$REPO_DIR/bin/claude" 2>/dev/null || true
|
|
256
366
|
[ -x "$REPO_DIR/bin/claude-accounts" ] || chmod +x "$REPO_DIR/bin/claude-accounts" 2>/dev/null || true
|
|
@@ -289,7 +399,11 @@ with open(sys.argv[1] + '.tmp', 'w') as f:
|
|
|
289
399
|
os.replace(sys.argv[1] + '.tmp', sys.argv[1])
|
|
290
400
|
PYEOF
|
|
291
401
|
fi
|
|
292
|
-
|
|
402
|
+
if sync_target_is_local "$(sync_target)"; then
|
|
403
|
+
echo " account pool: $ACC_ROOT (manifest ready; sync target: none — local-only)"
|
|
404
|
+
else
|
|
405
|
+
echo " account pool: $ACC_ROOT (manifest ready; sync target: $(sync_target))"
|
|
406
|
+
fi
|
|
293
407
|
# The codex pool gets its own skeleton + manifest (same schema, separate root).
|
|
294
408
|
if ! "$REPO_DIR/bin/codex-accounts" init-pool "${SERVER_OVERRIDE:-}" >/dev/null 2>&1; then
|
|
295
409
|
echo " WARNING: codex pool init failed (codex-accounts init-pool)" >&2
|
|
@@ -297,31 +411,44 @@ PYEOF
|
|
|
297
411
|
echo " codex account pool: $CODEX_ACC_ROOT (manifest ready)"
|
|
298
412
|
fi
|
|
299
413
|
|
|
414
|
+
# An INSTANCE install never rewrites the shell rc blocks: one interactive PATH
|
|
415
|
+
# cannot serve two pools, and clobbering the default install's block would point
|
|
416
|
+
# the operator's shell at an instance pool. Its agents (below) carry the roots
|
|
417
|
+
# instead, and the runner daemon invokes the shims by absolute path.
|
|
418
|
+
if [ -n "$INSTANCE" ]; then
|
|
419
|
+
echo " PATH block: skipped (instance install) — for a shell against this pool:"
|
|
420
|
+
echo " export CLAUDE_ACCOUNTS_ROOT='$ACC_ROOT' CODEX_ACCOUNTS_ROOT='$CODEX_ACC_ROOT'"
|
|
421
|
+
echo " export PATH=\"$REPO_DIR/bin:\$PATH\""
|
|
422
|
+
fi
|
|
300
423
|
if [ "$kind" = "mac" ]; then
|
|
301
424
|
# .zshenv covers non-interactive zsh; the .zshrc block must be LAST so it wins
|
|
302
425
|
# over ~/.local/bin re-prepends done earlier in .zshrc/.zprofile.
|
|
303
426
|
# zsh reads: .zshenv always; .zprofile for login; .zshrc for interactive.
|
|
304
427
|
# The block must end each file that later re-prepends ~/.local/bin.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
428
|
+
if [ -z "$INSTANCE" ]; then
|
|
429
|
+
touch "$HOME/.zshenv"
|
|
430
|
+
append_block "$HOME/.zshenv"
|
|
431
|
+
touch "$HOME/.zprofile"
|
|
432
|
+
append_block "$HOME/.zprofile"
|
|
433
|
+
touch "$HOME/.zshrc"
|
|
434
|
+
append_block "$HOME/.zshrc"
|
|
435
|
+
[ -f "$HOME/.bash_profile" ] && append_block "$HOME/.bash_profile"
|
|
436
|
+
[ -f "$HOME/.bashrc" ] && append_block "$HOME/.bashrc"
|
|
437
|
+
echo " PATH block: end of ~/.zshenv, ~/.zprofile, ~/.zshrc (+ bash rc files if present)"
|
|
438
|
+
fi
|
|
314
439
|
[ "$NO_SCHEDULE" = "1" ] || mac_schedule_install
|
|
315
440
|
else
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
441
|
+
if [ -z "$INSTANCE" ]; then
|
|
442
|
+
touch "$HOME/.bashrc"
|
|
443
|
+
append_block "$HOME/.bashrc"
|
|
444
|
+
if [ -w /etc/profile.d ] 2>/dev/null || [ "$(id -u)" = "0" ]; then
|
|
445
|
+
{
|
|
446
|
+
printf '%s\n' "$MARK_BEGIN"
|
|
447
|
+
path_block_body
|
|
448
|
+
printf '%s\n' "$MARK_END"
|
|
449
|
+
} > "$PROFILED"
|
|
450
|
+
echo " PATH block: ~/.bashrc + $PROFILED"
|
|
451
|
+
fi
|
|
325
452
|
fi
|
|
326
453
|
if [ "$(id -u)" = "0" ]; then
|
|
327
454
|
if [ -e /usr/local/bin/claude ] && [ ! -L /usr/local/bin/claude ]; then
|