@dst-justin/relay 2.1.0 → 2.1.1
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/package.json +3 -2
- package/postinstall.js +84 -0
- package/relay +114 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dst-justin/relay",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Multi-account switcher for Claude Code — instant credential swap across macOS, Linux, and Windows",
|
|
5
5
|
"bin": {
|
|
6
6
|
"relay": "./relay.js"
|
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
"relay.js",
|
|
11
11
|
"relay.ps1",
|
|
12
12
|
"relay.cmd",
|
|
13
|
+
"postinstall.js",
|
|
13
14
|
"README.md"
|
|
14
15
|
],
|
|
15
16
|
"scripts": {
|
|
16
|
-
"postinstall": "node -e \"if(process.platform!=='win32'){try{require('fs').chmodSync(require('path').join(__dirname,'relay'),'755')}catch(_){}}\""
|
|
17
|
+
"postinstall": "node -e \"if(process.platform!=='win32'){try{require('fs').chmodSync(require('path').join(__dirname,'relay'),'755')}catch(_){}}\" && node postinstall.js"
|
|
17
18
|
},
|
|
18
19
|
"engines": {
|
|
19
20
|
"node": ">=16"
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Runs after `npm install -g @dst-justin/relay`.
|
|
3
|
+
// If the npm global bin dir is not in PATH, appends it to shell config files.
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
const { existsSync, readFileSync, appendFileSync, mkdirSync, writeFileSync } = require('fs');
|
|
8
|
+
const { join } = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
|
|
11
|
+
// ── Windows ───────────────────────────────────────────────────────────────────
|
|
12
|
+
// npm on Windows already manages PATH via the installer; nothing to do here.
|
|
13
|
+
if (process.platform === 'win32') {
|
|
14
|
+
// Verify relay.js can reach relay.ps1 (sanity check)
|
|
15
|
+
if (!existsSync(join(__dirname, 'relay.ps1'))) {
|
|
16
|
+
console.warn(' relay: warning — relay.ps1 not found, Windows support may be broken');
|
|
17
|
+
}
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ── Get npm global bin dir ────────────────────────────────────────────────────
|
|
22
|
+
// npm sets npm_config_prefix during install; fall back to `npm prefix -g`
|
|
23
|
+
let prefix = process.env.npm_config_prefix;
|
|
24
|
+
if (!prefix) {
|
|
25
|
+
try {
|
|
26
|
+
prefix = execSync('npm prefix -g', { encoding: 'utf8', stdio: ['pipe','pipe','pipe'] }).trim();
|
|
27
|
+
} catch (_) {
|
|
28
|
+
process.exit(0); // can't determine, give up silently
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const binDir = join(prefix, 'bin');
|
|
32
|
+
|
|
33
|
+
// Already in PATH — nothing to do
|
|
34
|
+
const pathDirs = (process.env.PATH || '').split(':');
|
|
35
|
+
if (pathDirs.includes(binDir)) process.exit(0);
|
|
36
|
+
|
|
37
|
+
const home = os.homedir();
|
|
38
|
+
|
|
39
|
+
// ── POSIX shells: bash / zsh / sh ────────────────────────────────────────────
|
|
40
|
+
const exportLine = `export PATH="${binDir}:$PATH"`;
|
|
41
|
+
const marker = '# added by relay';
|
|
42
|
+
const block = `\n${exportLine} ${marker}\n`;
|
|
43
|
+
|
|
44
|
+
const rcFiles = ['.bashrc', '.zshrc', '.profile'].map(f => join(home, f));
|
|
45
|
+
let patched = false;
|
|
46
|
+
|
|
47
|
+
for (const rc of rcFiles) {
|
|
48
|
+
if (!existsSync(rc)) continue;
|
|
49
|
+
try {
|
|
50
|
+
if (readFileSync(rc, 'utf8').includes(marker)) continue; // idempotent
|
|
51
|
+
appendFileSync(rc, block);
|
|
52
|
+
console.log(` relay: added ${binDir} to PATH in ~/${require('path').basename(rc)}`);
|
|
53
|
+
patched = true;
|
|
54
|
+
} catch (_) {}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// If no shell rc found, create ~/.bashrc as a last resort
|
|
58
|
+
if (!patched) {
|
|
59
|
+
const bashrc = join(home, '.bashrc');
|
|
60
|
+
try {
|
|
61
|
+
appendFileSync(bashrc, block);
|
|
62
|
+
console.log(` relay: created ~/.bashrc with PATH entry for ${binDir}`);
|
|
63
|
+
patched = true;
|
|
64
|
+
} catch (_) {}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Fish shell ────────────────────────────────────────────────────────────────
|
|
68
|
+
const fishConfig = join(home, '.config', 'fish', 'config.fish');
|
|
69
|
+
const fishMarker = '# added by relay';
|
|
70
|
+
const fishLine = `\nset -gx PATH "${binDir}" $PATH ${fishMarker}\n`;
|
|
71
|
+
|
|
72
|
+
if (existsSync(fishConfig)) {
|
|
73
|
+
try {
|
|
74
|
+
if (!readFileSync(fishConfig, 'utf8').includes(fishMarker)) {
|
|
75
|
+
appendFileSync(fishConfig, fishLine);
|
|
76
|
+
console.log(` relay: added ${binDir} to PATH in ~/.config/fish/config.fish`);
|
|
77
|
+
patched = true;
|
|
78
|
+
}
|
|
79
|
+
} catch (_) {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (patched) {
|
|
83
|
+
console.log(` relay: restart your shell (or 'source ~/.bashrc' / 'source ~/.zshrc') then try: relay list`);
|
|
84
|
+
}
|
package/relay
CHANGED
|
@@ -12,6 +12,7 @@ RELAY_DIR="${HOME}/.claude-relay"
|
|
|
12
12
|
CREDS_STORE="${RELAY_DIR}/credentials"
|
|
13
13
|
META_STORE="${RELAY_DIR}/meta"
|
|
14
14
|
CURRENT_FILE="${RELAY_DIR}/current"
|
|
15
|
+
UPDATE_CACHE="${RELAY_DIR}/.update_cache"
|
|
15
16
|
CLAUDE_DIR="${HOME}/.claude"
|
|
16
17
|
CLAUDE_JSON="${HOME}/.claude.json"
|
|
17
18
|
REAL_CLAUDE=$(command -v claude 2>/dev/null || echo "")
|
|
@@ -371,14 +372,21 @@ _sync_current_creds() {
|
|
|
371
372
|
[[ -n "${live}" ]] && printf '%s' "${live}" > "$(account_creds "${cur}")"
|
|
372
373
|
}
|
|
373
374
|
|
|
374
|
-
cmd_quick() {
|
|
375
|
+
cmd_quick() {
|
|
376
|
+
_check_update_bg
|
|
377
|
+
_sync_current_creds
|
|
378
|
+
render_table quick "${CREDS_STORE}" "${META_STORE}" "$(current_name)" "$@"
|
|
379
|
+
_show_update_notice
|
|
380
|
+
}
|
|
375
381
|
|
|
376
382
|
cmd_list() {
|
|
383
|
+
_check_update_bg
|
|
377
384
|
hdr "Account List"
|
|
378
385
|
_sync_current_creds
|
|
379
386
|
render_table full "${CREDS_STORE}" "${META_STORE}" "$(current_name)" "$@"
|
|
380
387
|
echo ""
|
|
381
388
|
ok "Inside Claude Code: ${CY}!relay <index>${R} to switch"
|
|
389
|
+
_show_update_notice
|
|
382
390
|
}
|
|
383
391
|
|
|
384
392
|
cmd_status() {
|
|
@@ -450,6 +458,8 @@ EOF
|
|
|
450
458
|
[[ -d "${CLAUDE_DIR}/projects" ]] && \
|
|
451
459
|
n=$(find "${CLAUDE_DIR}/projects" -name "*.jsonl" 2>/dev/null | wc -l | tr -d ' ')
|
|
452
460
|
printf "\n ${B}Sessions:${R} %s (shared across all accounts in ~/.claude/projects/)\n" "${n}"
|
|
461
|
+
_check_update_bg
|
|
462
|
+
_show_update_notice
|
|
453
463
|
}
|
|
454
464
|
|
|
455
465
|
cmd_add() {
|
|
@@ -599,6 +609,8 @@ print()
|
|
|
599
609
|
print(f' {total} session(s)' if total else ' No sessions found')
|
|
600
610
|
EOF
|
|
601
611
|
log "${CY}claude -c${R} resume last ${D}|${R} ${CY}claude -r${R} pick one ${D}|${R} ${CY}claude --resume <id>${R}"
|
|
612
|
+
_check_update_bg
|
|
613
|
+
_show_update_notice
|
|
602
614
|
}
|
|
603
615
|
|
|
604
616
|
cmd_install() {
|
|
@@ -1190,6 +1202,68 @@ cmd_version() {
|
|
|
1190
1202
|
printf "relay %s\n" "$(_read_version)"
|
|
1191
1203
|
}
|
|
1192
1204
|
|
|
1205
|
+
# ── Update notification helpers ───────────────────────────────────────────────
|
|
1206
|
+
# Cache format: "<epoch>:<version>" TTL = 24h
|
|
1207
|
+
_check_update_bg() {
|
|
1208
|
+
(
|
|
1209
|
+
local ttl=86400
|
|
1210
|
+
if [[ -f "${UPDATE_CACHE}" ]]; then
|
|
1211
|
+
local cached; cached=$(cat "${UPDATE_CACHE}" 2>/dev/null)
|
|
1212
|
+
local ts="${cached%%:*}"
|
|
1213
|
+
local now; now=$(date +%s)
|
|
1214
|
+
[[ $(( now - ts )) -lt ${ttl} ]] && exit 0
|
|
1215
|
+
fi
|
|
1216
|
+
local ver
|
|
1217
|
+
ver=$("${PY}" - 2>/dev/null <<'PYEOF'
|
|
1218
|
+
import urllib.request, json, sys
|
|
1219
|
+
def fetch(url, h={}):
|
|
1220
|
+
r = urllib.request.Request(url, headers=h)
|
|
1221
|
+
with urllib.request.urlopen(r, timeout=6) as resp:
|
|
1222
|
+
return json.loads(resp.read())
|
|
1223
|
+
try:
|
|
1224
|
+
d = fetch('https://api.github.com/repos/darkstar1227/relay/releases/latest',
|
|
1225
|
+
{'User-Agent': 'relay-update'})
|
|
1226
|
+
print(d['tag_name'].lstrip('v')); sys.exit(0)
|
|
1227
|
+
except Exception: pass
|
|
1228
|
+
try:
|
|
1229
|
+
d = fetch('https://registry.npmjs.org/@dst-justin%2frelay/latest')
|
|
1230
|
+
print(d['version'])
|
|
1231
|
+
except Exception: sys.exit(1)
|
|
1232
|
+
PYEOF
|
|
1233
|
+
)
|
|
1234
|
+
[[ -n "${ver}" ]] && printf '%s:%s' "$(date +%s)" "${ver}" > "${UPDATE_CACHE}"
|
|
1235
|
+
) >/dev/null 2>&1 &
|
|
1236
|
+
disown 2>/dev/null || true
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
_show_update_notice() {
|
|
1240
|
+
local current; current=$(_read_version)
|
|
1241
|
+
local latest=""
|
|
1242
|
+
if [[ -f "${UPDATE_CACHE}" ]]; then
|
|
1243
|
+
local cached; cached=$(cat "${UPDATE_CACHE}" 2>/dev/null)
|
|
1244
|
+
latest="${cached#*:}"
|
|
1245
|
+
fi
|
|
1246
|
+
if [[ -n "${latest}" && "${latest}" != "${current}" ]]; then
|
|
1247
|
+
printf "\n ${D}relay version: ${B}${current}${R}${D} → ${CY}${B}${latest}${R}${D} available — run ${CY}relay update${R}${D} to install${R}\n"
|
|
1248
|
+
else
|
|
1249
|
+
local ver_display="${latest:-${current}}"
|
|
1250
|
+
printf "\n ${D}relay version: ${B}${ver_display}${R}${D} (up to date)${R}\n"
|
|
1251
|
+
fi
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
# Detect how relay was originally installed:
|
|
1255
|
+
# npm — package.json present in script dir (npm unpacks the full package)
|
|
1256
|
+
# git — .git dir present in script dir
|
|
1257
|
+
# direct — bare script copy (no package.json, no .git)
|
|
1258
|
+
_detect_install_method() {
|
|
1259
|
+
local d; d=$(_script_dir)
|
|
1260
|
+
# .git check first: git clone has both .git AND package.json; npm publish strips .git
|
|
1261
|
+
if [[ -d "${d}/.git" ]]; then echo "git"
|
|
1262
|
+
elif [[ -f "${d}/package.json" ]]; then echo "npm"
|
|
1263
|
+
else echo "direct"
|
|
1264
|
+
fi
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1193
1267
|
cmd_update() {
|
|
1194
1268
|
hdr "Update relay"
|
|
1195
1269
|
|
|
@@ -1229,18 +1303,43 @@ PYEOF
|
|
|
1229
1303
|
fi
|
|
1230
1304
|
|
|
1231
1305
|
local relay_dir; relay_dir=$(_script_dir)
|
|
1232
|
-
local
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
npm
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1306
|
+
local method; method=$(_detect_install_method)
|
|
1307
|
+
|
|
1308
|
+
case "${method}" in
|
|
1309
|
+
npm)
|
|
1310
|
+
local npm_cmd; npm_cmd=$(command -v npm 2>/dev/null)
|
|
1311
|
+
if [[ -n "${npm_cmd}" ]]; then
|
|
1312
|
+
log "Updating via npm (original install method)..."
|
|
1313
|
+
npm install -g @dst-justin/relay@latest
|
|
1314
|
+
ok "Updated to $(_read_version)"
|
|
1315
|
+
else
|
|
1316
|
+
err "npm not found — reinstall npm and retry"
|
|
1317
|
+
log "Or update manually: ${CY}npm install -g @dst-justin/relay@latest${R}"
|
|
1318
|
+
fi ;;
|
|
1319
|
+
git)
|
|
1320
|
+
log "Updating via git pull (original install method)..."
|
|
1321
|
+
git -C "${relay_dir}" pull ;;
|
|
1322
|
+
direct)
|
|
1323
|
+
log "Updating via direct download (original install method)..."
|
|
1324
|
+
local script_path; script_path=$(readlink -f "$0" 2>/dev/null || echo "$0")
|
|
1325
|
+
"${PY}" - "${script_path}" "${latest}" <<'PYEOF'
|
|
1326
|
+
import urllib.request, sys, os, stat
|
|
1327
|
+
script_path, version = sys.argv[1], sys.argv[2]
|
|
1328
|
+
url = f'https://raw.githubusercontent.com/darkstar1227/relay/v{version}/relay'
|
|
1329
|
+
try:
|
|
1330
|
+
with urllib.request.urlopen(url, timeout=15) as r:
|
|
1331
|
+
content = r.read()
|
|
1332
|
+
tmp = script_path + '.tmp'
|
|
1333
|
+
with open(tmp, 'wb') as f: f.write(content)
|
|
1334
|
+
os.chmod(tmp, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
|
|
1335
|
+
os.replace(tmp, script_path)
|
|
1336
|
+
except Exception as e:
|
|
1337
|
+
print(f' download failed: {e}', file=sys.stderr); sys.exit(1)
|
|
1338
|
+
PYEOF
|
|
1339
|
+
ok "Updated ${script_path} to ${latest}" ;;
|
|
1340
|
+
esac
|
|
1341
|
+
# Invalidate update cache so next display shows fresh state
|
|
1342
|
+
rm -f "${UPDATE_CACHE}" 2>/dev/null || true
|
|
1244
1343
|
}
|
|
1245
1344
|
|
|
1246
1345
|
cmd_uninstall() {
|
|
@@ -1297,6 +1396,8 @@ cmd_help() {
|
|
|
1297
1396
|
echo ""
|
|
1298
1397
|
printf " ${D}switches the OAuth credential (macOS Keychain / Linux ~/.claude/.credentials.json); sessions are shared${R}\n"
|
|
1299
1398
|
printf " ${D}after switching: claude -c to resume, claude --resume <id> for a specific session${R}\n\n"
|
|
1399
|
+
_check_update_bg
|
|
1400
|
+
_show_update_notice
|
|
1300
1401
|
}
|
|
1301
1402
|
|
|
1302
1403
|
# ══════════════════════════════════════════════════════════════════
|