claude-cac 1.5.0-beta.2 → 1.5.1-beta.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/README.md +4 -4
- package/cac +60 -19
- package/fingerprint-hook.js +16 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,10 +110,10 @@ cac ls # = cac env ls
|
|
|
110
110
|
| `cac claude ls` | 列出已安装版本 |
|
|
111
111
|
| `cac claude pin <ver>` | 当前环境绑定版本 |
|
|
112
112
|
| **环境管理** | |
|
|
113
|
-
| `cac env create <name> [-p proxy] [-c ver] [--clone]` | 创建环境(自动激活,`--
|
|
113
|
+
| `cac env create <name> [-p proxy] [-c ver] [--clone] [--telemetry mode] [--persona preset]` | 创建环境(自动激活,`--telemetry transparent/stealth/paranoid` 控制遥测,`--persona macos-vscode/...` 用于容器) |
|
|
114
114
|
| `cac env ls` | 列出环境 |
|
|
115
115
|
| `cac env rm <name>` | 删除环境 |
|
|
116
|
-
| `cac env set [name] <key> <value>` | 修改环境(proxy / version) |
|
|
116
|
+
| `cac env set [name] <key> <value>` | 修改环境(proxy / version / telemetry / persona) |
|
|
117
117
|
| `cac env check [-d]` | 验证当前环境(`-d` 显示详情) |
|
|
118
118
|
| `cac <name>` | 激活环境 |
|
|
119
119
|
| **自管理** | |
|
|
@@ -293,10 +293,10 @@ Each environment is fully isolated:
|
|
|
293
293
|
| `cac claude ls` | List installed versions |
|
|
294
294
|
| `cac claude pin <ver>` | Pin current env to version |
|
|
295
295
|
| **Environment management** | |
|
|
296
|
-
| `cac env create <name> [-p proxy] [-c ver] [--clone]` | Create environment (auto-activates, `--
|
|
296
|
+
| `cac env create <name> [-p proxy] [-c ver] [--clone] [--telemetry mode] [--persona preset]` | Create environment (auto-activates, `--telemetry transparent/stealth/paranoid` for telemetry control, `--persona macos-vscode/...` for containers) |
|
|
297
297
|
| `cac env ls` | List environments |
|
|
298
298
|
| `cac env rm <name>` | Remove environment |
|
|
299
|
-
| `cac env set [name] <key> <value>` | Modify environment (proxy / version) |
|
|
299
|
+
| `cac env set [name] <key> <value>` | Modify environment (proxy / version / telemetry / persona) |
|
|
300
300
|
| `cac env check [-d]` | Verify current environment (`-d` for details) |
|
|
301
301
|
| `cac <name>` | Activate environment |
|
|
302
302
|
| **Self-management** | |
|
package/cac
CHANGED
|
@@ -11,7 +11,7 @@ VERSIONS_DIR="$CAC_DIR/versions"
|
|
|
11
11
|
# ── utils: colors, read/write, UUID, proxy parsing ───────────────────────
|
|
12
12
|
|
|
13
13
|
# shellcheck disable=SC2034 # used in build-concatenated cac script
|
|
14
|
-
CAC_VERSION="1.5.
|
|
14
|
+
CAC_VERSION="1.5.1-beta.1"
|
|
15
15
|
|
|
16
16
|
_read() { [[ -f "$1" ]] && tr -d '[:space:]' < "$1" || echo "${2:-}"; }
|
|
17
17
|
_die() { printf '%b\n' "$(_red "error:") $*" >&2; exit 1; }
|
|
@@ -63,7 +63,7 @@ _new_hostname() { echo "host-$(_gen_uuid | cut -d- -f1 | tr '[:upper:]' '[:lower
|
|
|
63
63
|
_new_mac() { printf '02:%02x:%02x:%02x:%02x:%02x' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)); }
|
|
64
64
|
_new_git_remote() { echo "https://github.com/user-$(_gen_uuid | cut -d- -f1)/project-$(_gen_uuid | cut -d- -f2).git"; }
|
|
65
65
|
_new_git_email() { echo "user-$(_gen_uuid | cut -d- -f1 | tr '[:upper:]' '[:lower:]')@users.noreply.github.com"; }
|
|
66
|
-
_new_device_token() {
|
|
66
|
+
_new_device_token() { _new_user_id; }
|
|
67
67
|
|
|
68
68
|
# Get real command path (bypass shim)
|
|
69
69
|
_get_real_cmd() {
|
|
@@ -1232,12 +1232,10 @@ if [[ -n "$PROXY" ]]; then
|
|
|
1232
1232
|
fi
|
|
1233
1233
|
|
|
1234
1234
|
# ── git identity spoofing ──
|
|
1235
|
-
#
|
|
1235
|
+
# Intercept `git config --get user.email` at process level (telemetry read only)
|
|
1236
|
+
# Do NOT set GIT_AUTHOR_EMAIL/GIT_COMMITTER_EMAIL — those would affect real git commits
|
|
1236
1237
|
if [[ -f "$_env_dir/git_email" ]]; then
|
|
1237
|
-
|
|
1238
|
-
export GIT_AUTHOR_EMAIL="$_git_email"
|
|
1239
|
-
export GIT_COMMITTER_EMAIL="$_git_email"
|
|
1240
|
-
export CAC_GIT_EMAIL="$_git_email"
|
|
1238
|
+
export CAC_GIT_EMAIL=$(tr -d '[:space:]' < "$_env_dir/git_email")
|
|
1241
1239
|
fi
|
|
1242
1240
|
|
|
1243
1241
|
# ── repository fingerprint (rh) spoofing ──
|
|
@@ -1255,31 +1253,29 @@ fi
|
|
|
1255
1253
|
# ── persona (Docker/server environment spoofing) ──
|
|
1256
1254
|
if [[ -f "$_env_dir/persona" ]]; then
|
|
1257
1255
|
_persona=$(tr -d '[:space:]' < "$_env_dir/persona")
|
|
1256
|
+
export TERM="xterm-256color"
|
|
1258
1257
|
case "$_persona" in
|
|
1259
1258
|
macos-vscode)
|
|
1260
1259
|
export TERM_PROGRAM="vscode"
|
|
1261
1260
|
export VSCODE_GIT_ASKPASS_MAIN="/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/git/dist/askpass-main.js"
|
|
1262
1261
|
export __CFBundleIdentifier="com.microsoft.VSCode"
|
|
1263
|
-
export TERM="xterm-256color"
|
|
1264
1262
|
;;
|
|
1265
1263
|
macos-cursor)
|
|
1266
1264
|
export TERM_PROGRAM="vscode"
|
|
1267
|
-
|
|
1265
|
+
[[ -f "$_env_dir/cursor_trace_id" ]] || printf 'cursor-%s' "$(od -An -tx1 -N8 /dev/urandom | tr -d ' \n')" > "$_env_dir/cursor_trace_id"
|
|
1266
|
+
export CURSOR_TRACE_ID=$(tr -d '[:space:]' < "$_env_dir/cursor_trace_id")
|
|
1268
1267
|
export __CFBundleIdentifier="com.todesktop.230313mzl4w4u92"
|
|
1269
|
-
export TERM="xterm-256color"
|
|
1270
1268
|
;;
|
|
1271
1269
|
macos-iterm)
|
|
1272
1270
|
export TERM_PROGRAM="iTerm.app"
|
|
1273
1271
|
export __CFBundleIdentifier="com.googlecode.iterm2"
|
|
1274
|
-
|
|
1275
|
-
export ITERM_SESSION_ID
|
|
1272
|
+
[[ -f "$_env_dir/iterm_session_id" ]] || printf 'w0t0p0:%s' "$(od -An -tx1 -N16 /dev/urandom | tr -d ' \n')" > "$_env_dir/iterm_session_id"
|
|
1273
|
+
export ITERM_SESSION_ID=$(tr -d '[:space:]' < "$_env_dir/iterm_session_id")
|
|
1276
1274
|
;;
|
|
1277
1275
|
linux-desktop)
|
|
1278
1276
|
export TERM_PROGRAM="vscode"
|
|
1279
|
-
export TERM="xterm-256color"
|
|
1280
1277
|
;;
|
|
1281
1278
|
esac
|
|
1282
|
-
# Hide Docker signals when persona is active
|
|
1283
1279
|
export CAC_HIDE_DOCKER=1
|
|
1284
1280
|
fi
|
|
1285
1281
|
|
|
@@ -1393,6 +1389,40 @@ if [[ -n "$PROXY" ]] && [[ -f "$CAC_DIR/relay.js" ]]; then
|
|
|
1393
1389
|
echo "$_rport" > "$_relay_port_file"
|
|
1394
1390
|
fi
|
|
1395
1391
|
|
|
1392
|
+
# env-level watchdog singleton: auto-restarts relay if it crashes
|
|
1393
|
+
# - one watchdog per environment, shared across all sessions
|
|
1394
|
+
# - exits automatically when relay is intentionally stopped (relay.proxy removed)
|
|
1395
|
+
_relay_watchdog_file="$CAC_DIR/relay.watchdog.pid"
|
|
1396
|
+
_wd_running=false
|
|
1397
|
+
if [[ -f "$_relay_watchdog_file" ]]; then
|
|
1398
|
+
_wpid=$(tr -d '[:space:]' < "$_relay_watchdog_file")
|
|
1399
|
+
[[ -n "$_wpid" ]] && kill -0 "$_wpid" 2>/dev/null && _wd_running=true
|
|
1400
|
+
fi
|
|
1401
|
+
if [[ "$_wd_running" != "true" ]]; then
|
|
1402
|
+
(
|
|
1403
|
+
trap 'rm -f "$CAC_DIR/relay.watchdog.pid"' EXIT
|
|
1404
|
+
set +e
|
|
1405
|
+
while true; do
|
|
1406
|
+
sleep 5
|
|
1407
|
+
# relay.proxy removed by _relay_stop — intentional stop, exit watchdog
|
|
1408
|
+
[[ -f "$CAC_DIR/relay.proxy" ]] || exit 0
|
|
1409
|
+
# relay alive — nothing to do
|
|
1410
|
+
if [[ -f "$CAC_DIR/relay.pid" ]]; then
|
|
1411
|
+
_rpid=$(tr -d '[:space:]' < "$CAC_DIR/relay.pid")
|
|
1412
|
+
kill -0 "$_rpid" 2>/dev/null && continue
|
|
1413
|
+
fi
|
|
1414
|
+
# relay dead — restart on same port with same proxy
|
|
1415
|
+
_rport=$(tr -d '[:space:]' < "$CAC_DIR/relay.port" 2>/dev/null || true)
|
|
1416
|
+
_rproxy=$(tr -d '[:space:]' < "$CAC_DIR/relay.proxy" 2>/dev/null || true)
|
|
1417
|
+
[[ -n "$_rport" ]] && [[ -n "$_rproxy" ]] || exit 0
|
|
1418
|
+
node "$CAC_DIR/relay.js" "$_rport" "$_rproxy" "$CAC_DIR/relay.pid" </dev/null >>"$CAC_DIR/relay.log" 2>&1 &
|
|
1419
|
+
done
|
|
1420
|
+
) &
|
|
1421
|
+
_new_wpid=$!
|
|
1422
|
+
echo "$_new_wpid" > "$_relay_watchdog_file"
|
|
1423
|
+
disown "$_new_wpid"
|
|
1424
|
+
fi
|
|
1425
|
+
|
|
1396
1426
|
# override proxy to point to local relay
|
|
1397
1427
|
if [[ -f "$_relay_port_file" ]]; then
|
|
1398
1428
|
_rport=$(tr -d '[:space:]' < "$_relay_port_file")
|
|
@@ -1632,7 +1662,7 @@ _env_cmd_create() {
|
|
|
1632
1662
|
esac
|
|
1633
1663
|
done
|
|
1634
1664
|
|
|
1635
|
-
[[ -n "$name" ]] || _die "usage: cac env create <name> [-p <proxy>] [-c <version>]"
|
|
1665
|
+
[[ -n "$name" ]] || _die "usage: cac env create <name> [-p <proxy>] [-c <version>] [--telemetry <mode>] [--persona <preset>]"
|
|
1636
1666
|
[[ "$name" =~ ^[a-zA-Z0-9_-]+$ ]] || _die "invalid name '$name' (use alphanumeric, dash, underscore)"
|
|
1637
1667
|
|
|
1638
1668
|
local env_dir="$ENVS_DIR/$name"
|
|
@@ -1924,7 +1954,9 @@ _env_cmd_set() {
|
|
|
1924
1954
|
echo " $(_green "set") [name] proxy --remove Remove proxy"
|
|
1925
1955
|
echo " $(_green "set") [name] version <ver|latest> Change Claude version"
|
|
1926
1956
|
echo " $(_green "set") [name] telemetry <stealth|paranoid|transparent>"
|
|
1957
|
+
echo " Telemetry blocking: stealth (1p_events only), paranoid (max), transparent (none)"
|
|
1927
1958
|
echo " $(_green "set") [name] persona <macos-vscode|macos-cursor|macos-iterm|linux-desktop|--remove>"
|
|
1959
|
+
echo " Terminal preset: inject desktop env vars, hide Docker signals (for containers)"
|
|
1928
1960
|
echo
|
|
1929
1961
|
echo " $(_dim "If name is omitted, uses the current active environment.")"
|
|
1930
1962
|
echo
|
|
@@ -2025,10 +2057,10 @@ cmd_env() {
|
|
|
2025
2057
|
echo
|
|
2026
2058
|
echo " $(_bold "cac env") — environment management"
|
|
2027
2059
|
echo
|
|
2028
|
-
echo " $(_green "create") <name> [-p proxy] [-c ver] [--
|
|
2029
|
-
echo "
|
|
2030
|
-
echo " $(_green "set") [name]
|
|
2031
|
-
echo "
|
|
2060
|
+
echo " $(_green "create") <name> [-p proxy] [-c ver] [--telemetry mode] [--persona preset]"
|
|
2061
|
+
echo " Create isolated environment (auto-activates)"
|
|
2062
|
+
echo " $(_green "set") [name] <key> <value> Modify environment"
|
|
2063
|
+
echo " proxy, version, telemetry, or persona"
|
|
2032
2064
|
echo " $(_green "ls") List all environments"
|
|
2033
2065
|
echo " $(_green "rm") <name> Remove an environment"
|
|
2034
2066
|
echo " $(_green "check") Verify current environment"
|
|
@@ -2106,6 +2138,15 @@ _relay_stop() {
|
|
|
2106
2138
|
fi
|
|
2107
2139
|
rm -f "$CAC_DIR/relay.port" "$CAC_DIR/relay.proxy"
|
|
2108
2140
|
|
|
2141
|
+
# stop watchdog (relay.proxy already removed above, watchdog will self-exit within 5s;
|
|
2142
|
+
# kill it immediately for clean teardown)
|
|
2143
|
+
local wd_file="$CAC_DIR/relay.watchdog.pid"
|
|
2144
|
+
if [[ -f "$wd_file" ]]; then
|
|
2145
|
+
local wd_pid; wd_pid=$(tr -d '[:space:]' < "$wd_file")
|
|
2146
|
+
[[ -n "$wd_pid" ]] && kill "$wd_pid" 2>/dev/null || true
|
|
2147
|
+
rm -f "$wd_file"
|
|
2148
|
+
fi
|
|
2149
|
+
|
|
2109
2150
|
# cleanup route
|
|
2110
2151
|
_relay_remove_route 2>/dev/null || true
|
|
2111
2152
|
}
|
package/fingerprint-hook.js
CHANGED
|
@@ -115,11 +115,7 @@ if (fakeGitRemote) {
|
|
|
115
115
|
const _origExecSyncFp = child_process.execSync.bind(child_process);
|
|
116
116
|
child_process.execSync = function(cmd, options) {
|
|
117
117
|
var cmdStr = typeof cmd === 'string' ? cmd : cmd.toString();
|
|
118
|
-
if (isGitRemoteCmd(cmdStr))
|
|
119
|
-
var result = fakeGitRemote + '\n';
|
|
120
|
-
return (typeof options === 'string' || (options && options.encoding))
|
|
121
|
-
? result : Buffer.from(result);
|
|
122
|
-
}
|
|
118
|
+
if (isGitRemoteCmd(cmdStr)) return fakeResult(options, fakeGitRemote + '\n');
|
|
123
119
|
return _origExecSyncFp(cmd, options);
|
|
124
120
|
};
|
|
125
121
|
|
|
@@ -130,11 +126,7 @@ if (fakeGitRemote) {
|
|
|
130
126
|
var cb = typeof args[args.length - 1] === 'function' ? args[args.length - 1] : null;
|
|
131
127
|
if (isGitRemoteCmd(cmdStr)) {
|
|
132
128
|
if (cb) process.nextTick(cb, null, fakeGitRemote + '\n', '');
|
|
133
|
-
|
|
134
|
-
var cp = new EventEmitter();
|
|
135
|
-
cp.stdout = new EventEmitter(); cp.stderr = new EventEmitter();
|
|
136
|
-
cp.stdin = null; cp.pid = 0; cp.kill = function() { return false; };
|
|
137
|
-
return cp;
|
|
129
|
+
return makeFakeChildProcess();
|
|
138
130
|
}
|
|
139
131
|
return _origExecFp.apply(child_process, args);
|
|
140
132
|
};
|
|
@@ -145,9 +137,7 @@ if (fakeGitRemote) {
|
|
|
145
137
|
var fullCmd = file + ' ' + fileArgs.join(' ');
|
|
146
138
|
if (isGitRemoteCmd(fullCmd)) {
|
|
147
139
|
var opts = Array.isArray(argsOrOpts) ? options : argsOrOpts;
|
|
148
|
-
|
|
149
|
-
return (typeof opts === 'string' || (opts && opts.encoding))
|
|
150
|
-
? result : Buffer.from(result);
|
|
140
|
+
return fakeResult(opts, fakeGitRemote + '\n');
|
|
151
141
|
}
|
|
152
142
|
return _origExecFileSyncFp(file, argsOrOpts, options);
|
|
153
143
|
};
|
|
@@ -158,14 +148,11 @@ if (fakeGitRemote) {
|
|
|
158
148
|
// Intercept to prevent real email leakage (wrapper also sets GIT_AUTHOR_EMAIL)
|
|
159
149
|
const fakeGitEmail = process.env.CAC_GIT_EMAIL;
|
|
160
150
|
if (fakeGitEmail) {
|
|
161
|
-
// Re-wrap execSync if not already wrapped for git remote
|
|
162
151
|
var _prevExecSync = child_process.execSync;
|
|
163
152
|
child_process.execSync = function(cmd, options) {
|
|
164
153
|
var cmdStr = typeof cmd === 'string' ? cmd : cmd.toString();
|
|
165
154
|
if (/git\s+config\s+(--global\s+|--get\s+)*user\.email/i.test(cmdStr)) {
|
|
166
|
-
|
|
167
|
-
return (typeof options === 'string' || (options && options.encoding))
|
|
168
|
-
? result : Buffer.from(result);
|
|
155
|
+
return fakeResult(options, fakeGitEmail + '\n');
|
|
169
156
|
}
|
|
170
157
|
return _prevExecSync(cmd, options);
|
|
171
158
|
};
|
|
@@ -183,20 +170,18 @@ if (process.env.CAC_HIDE_DOCKER === '1') {
|
|
|
183
170
|
};
|
|
184
171
|
|
|
185
172
|
// Intercept /proc/1/cgroup reads to remove docker references
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
};
|
|
199
|
-
}
|
|
173
|
+
var _prevReadFileSync = fs.readFileSync;
|
|
174
|
+
fs.readFileSync = function(path, options) {
|
|
175
|
+
var ps = typeof path === 'string' ? path : (path && path.toString ? path.toString() : '');
|
|
176
|
+
if (ps === '/proc/1/cgroup') {
|
|
177
|
+
var content;
|
|
178
|
+
try { content = _prevReadFileSync(path, options); } catch(e) { throw e; }
|
|
179
|
+
var str = typeof content === 'string' ? content : content.toString();
|
|
180
|
+
str = str.replace(/docker|containerd|kubepods/gi, 'system.slice');
|
|
181
|
+
return fakeResult(options, str);
|
|
182
|
+
}
|
|
183
|
+
return _prevReadFileSync(path, options);
|
|
184
|
+
};
|
|
200
185
|
}
|
|
201
186
|
|
|
202
187
|
// --- Windows: intercept child_process for wmic / reg queries ---
|