claude-cac 1.5.1-beta.2 → 1.5.2-beta.2
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 +24 -20
- package/cac +37 -38
- package/fingerprint-hook.js +49 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,18 @@
|
|
|
41
41
|
- **配置继承** — `--clone` 从宿主或其他环境继承配置,`~/.cac/settings.json` 全局偏好
|
|
42
42
|
- **零配置** — 无需 setup,首次使用自动初始化
|
|
43
43
|
|
|
44
|
+
### 注意事项
|
|
45
|
+
|
|
46
|
+
> **封号风险说明**:cac 提供设备指纹层保护(UUID、主机名、MAC、遥测阻断、配置隔离),但**无法影响账号层风险**——包括 OAuth 账号本身、支付方式指纹、IP 信誉评分,以及 Anthropic 的服务端封禁决策。封号是账号层问题,cac 对此无能为力。详见 [封号风险 FAQ](https://cac.nextmind.space/docs/zh/guides/ban-risk)。
|
|
47
|
+
|
|
48
|
+
> **代理工具冲突**:如果本地启动了 Clash、Shadowrocket、Surge、sing-box 等代理/VPN 工具,建议在使用 cac 时先关闭。TUN 模式兼容性仍属实验性功能。即使发生冲突,cac 也会自动停止连接(fail-closed),**不会泄露你的真实 IP**。
|
|
49
|
+
|
|
50
|
+
- **首次登录**:启动 `claude` 后,输入 `/login` 完成 OAuth 授权
|
|
51
|
+
- **安全验证**:随时运行 `cac env check` 确认隐私保护状态,也可以 `which claude` 确认使用的是 cac 托管的 claude
|
|
52
|
+
- **自动安全检查**:每次启动 Claude Code 会话时,cac 会快速检查环境。如有异常会终止会话,不会发送任何数据
|
|
53
|
+
- **网络稳定性**:流量严格走代理——代理断开时流量完全停止,不会回退直连。内置心跳检测和自动恢复,断线后无需手动重启
|
|
54
|
+
- **IPv6**:建议系统级关闭,防止真实地址泄露
|
|
55
|
+
|
|
44
56
|
### 安装
|
|
45
57
|
|
|
46
58
|
```bash
|
|
@@ -196,16 +208,6 @@ cac docker port 6287 # 端口转发
|
|
|
196
208
|
|
|
197
209
|
代理格式:`ip:port:user:pass`(SOCKS5)、`ss://...`、`vmess://...`、`vless://...`、`trojan://...`
|
|
198
210
|
|
|
199
|
-
### 注意事项
|
|
200
|
-
|
|
201
|
-
> **代理工具冲突**:如果本地启动了 Clash、Shadowrocket、Surge、sing-box 等代理/VPN 工具,建议在使用 cac 时先关闭。TUN 模式兼容性仍属实验性功能。即使发生冲突,cac 也会自动停止连接(fail-closed),**不会泄露你的真实 IP**。
|
|
202
|
-
|
|
203
|
-
- **首次登录**:启动 `claude` 后,输入 `/login` 完成 OAuth 授权
|
|
204
|
-
- **安全验证**:随时运行 `cac env check` 确认隐私保护状态,也可以 `which claude` 确认使用的是 cac 托管的 claude
|
|
205
|
-
- **自动安全检查**:每次启动 Claude Code 会话时,cac 会快速检查环境。如有异常会终止会话,不会发送任何数据
|
|
206
|
-
- **网络稳定性**:流量严格走代理——代理断开时流量完全停止,不会回退直连。内置心跳检测和自动恢复,断线后无需手动重启
|
|
207
|
-
- **IPv6**:建议系统级关闭,防止真实地址泄露
|
|
208
|
-
|
|
209
211
|
---
|
|
210
212
|
|
|
211
213
|
<a id="english"></a>
|
|
@@ -224,6 +226,18 @@ cac docker port 6287 # 端口转发
|
|
|
224
226
|
- **Config inheritance** — `--clone` inherits config from host or other envs, `~/.cac/settings.json` for global preferences
|
|
225
227
|
- **Zero config** — no setup needed, auto-initializes on first use
|
|
226
228
|
|
|
229
|
+
### Notes
|
|
230
|
+
|
|
231
|
+
> **Account ban notice**: cac provides device fingerprint layer protection (UUID, hostname, MAC, telemetry blocking, config isolation), but **cannot affect account-layer risks** — including your OAuth account, payment method fingerprint, IP reputation score, or Anthropic's server-side ban decisions. Account bans are an account-layer issue that cac does not address. See the [Ban Risk FAQ](https://cac.nextmind.space/docs/guides/ban-risk) for details.
|
|
232
|
+
|
|
233
|
+
> **Proxy tool conflicts**: If you have Clash, Shadowrocket, Surge, sing-box or other proxy/VPN tools running locally, turn them off before using cac. TUN-mode compatibility is still experimental. Even if a conflict occurs, cac will fail-closed — **your real IP is never exposed**.
|
|
234
|
+
|
|
235
|
+
- **First login**: Run `claude`, then type `/login`. Health check is automatically bypassed.
|
|
236
|
+
- **Verify your setup**: Run `cac env check` anytime. Use `which claude` to confirm you're using the cac-managed wrapper.
|
|
237
|
+
- **Automatic safety checks**: Every new Claude Code session runs a quick cac check. If anything is wrong, the session is terminated before any data is sent.
|
|
238
|
+
- **Network resilience**: Traffic is strictly routed through your proxy. If the proxy drops, traffic stops entirely — no fallback to direct connection. Built-in heartbeat detection and auto-recovery — no manual restart needed after disconnections.
|
|
239
|
+
- **IPv6**: Recommend disabling system-wide to prevent real address exposure.
|
|
240
|
+
|
|
227
241
|
### Install
|
|
228
242
|
|
|
229
243
|
```bash
|
|
@@ -371,16 +385,6 @@ cac docker port 6287 # port forwarding
|
|
|
371
385
|
|
|
372
386
|
Proxy formats: `ip:port:user:pass` (SOCKS5), `ss://...`, `vmess://...`, `vless://...`, `trojan://...`
|
|
373
387
|
|
|
374
|
-
### Notes
|
|
375
|
-
|
|
376
|
-
> **Proxy tool conflicts**: If you have Clash, Shadowrocket, Surge, sing-box or other proxy/VPN tools running locally, turn them off before using cac. TUN-mode compatibility is still experimental. Even if a conflict occurs, cac will fail-closed — **your real IP is never exposed**.
|
|
377
|
-
|
|
378
|
-
- **First login**: Run `claude`, then type `/login`. Health check is automatically bypassed.
|
|
379
|
-
- **Verify your setup**: Run `cac env check` anytime. Use `which claude` to confirm you're using the cac-managed wrapper.
|
|
380
|
-
- **Automatic safety checks**: Every new Claude Code session runs a quick cac check. If anything is wrong, the session is terminated before any data is sent.
|
|
381
|
-
- **Network resilience**: Traffic is strictly routed through your proxy. If the proxy drops, traffic stops entirely — no fallback to direct connection. Built-in heartbeat detection and auto-recovery — no manual restart needed after disconnections.
|
|
382
|
-
- **IPv6**: Recommend disabling system-wide to prevent real address exposure.
|
|
383
|
-
|
|
384
388
|
---
|
|
385
389
|
|
|
386
390
|
<div align="center">
|
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.2-beta.2"
|
|
15
15
|
|
|
16
16
|
_read() { [[ -f "$1" ]] && tr -d '[:space:]' < "$1" || echo "${2:-}"; }
|
|
17
17
|
_die() { printf '%b\n' "$(_red "error:") $*" >&2; exit 1; }
|
|
@@ -56,10 +56,23 @@ _gen_uuid() {
|
|
|
56
56
|
fi
|
|
57
57
|
}
|
|
58
58
|
_new_uuid() { _gen_uuid | tr '[:lower:]' '[:upper:]'; }
|
|
59
|
-
_new_sid() { _gen_uuid | tr '[:upper:]' '[:lower:]'; }
|
|
60
59
|
_new_user_id() { python3 -c "import os; print(os.urandom(32).hex())" || _die "python3 required"; }
|
|
61
60
|
_new_machine_id() { _gen_uuid | tr -d '-' | tr '[:upper:]' '[:lower:]'; }
|
|
62
|
-
_new_hostname() {
|
|
61
|
+
_new_hostname() {
|
|
62
|
+
local -a _first_names=(
|
|
63
|
+
"James" "John" "Robert" "Michael" "William" "David" "Richard" "Joseph"
|
|
64
|
+
"Thomas" "Charles" "Daniel" "Matthew" "Anthony" "Donald" "Mark" "Paul"
|
|
65
|
+
"Steven" "Andrew" "Kenneth" "Joshua" "Kevin" "Brian" "George" "Timothy"
|
|
66
|
+
"Emma" "Olivia" "Sophia" "Isabella" "Mia" "Charlotte" "Amelia" "Harper"
|
|
67
|
+
"Evelyn" "Abigail" "Emily" "Elizabeth" "Sofia" "Avery" "Ella" "Scarlett"
|
|
68
|
+
"Liam" "Noah" "Oliver" "Elijah" "Lucas" "Mason" "Ethan" "Aiden"
|
|
69
|
+
"Alex" "Ryan" "Tyler" "Jordan" "Taylor" "Morgan" "Casey" "Riley"
|
|
70
|
+
)
|
|
71
|
+
local -a _models=("MacBook-Pro" "MacBook-Air" "MacBook-Pro" "MacBook-Pro")
|
|
72
|
+
local _name="${_first_names[$((RANDOM % ${#_first_names[@]}))]}"
|
|
73
|
+
local _model="${_models[$((RANDOM % ${#_models[@]}))]}"
|
|
74
|
+
echo "${_name}s-${_model}.local"
|
|
75
|
+
}
|
|
63
76
|
_new_mac() { printf '02:%02x:%02x:%02x:%02x:%02x' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)); }
|
|
64
77
|
_new_git_remote() { echo "https://github.com/user-$(_gen_uuid | cut -d- -f1)/project-$(_gen_uuid | cut -d- -f2).git"; }
|
|
65
78
|
_new_git_email() { echo "user-$(_gen_uuid | cut -d- -f1 | tr '[:upper:]' '[:lower:]')@users.noreply.github.com"; }
|
|
@@ -382,20 +395,6 @@ _remove_path_from_rc() {
|
|
|
382
395
|
fi
|
|
383
396
|
}
|
|
384
397
|
|
|
385
|
-
_update_statsig() {
|
|
386
|
-
local sid="$1"
|
|
387
|
-
local config_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
388
|
-
local statsig="$config_dir/statsig"
|
|
389
|
-
[[ -d "$statsig" ]] || return 0
|
|
390
|
-
local found=false
|
|
391
|
-
for f in "$statsig"/statsig.stable_id.*; do
|
|
392
|
-
[[ -f "$f" ]] && { printf '"%s"' "$sid" > "$f"; found=true; }
|
|
393
|
-
done
|
|
394
|
-
if [[ "$found" == "false" ]]; then
|
|
395
|
-
printf '"%s"' "$sid" > "$statsig/statsig.stable_id.local"
|
|
396
|
-
fi
|
|
397
|
-
}
|
|
398
|
-
|
|
399
398
|
_update_claude_json_user_id() {
|
|
400
399
|
local user_id="$1"
|
|
401
400
|
local config_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
@@ -1156,19 +1155,6 @@ if [[ -n "$PROXY" ]]; then
|
|
|
1156
1155
|
fi
|
|
1157
1156
|
fi
|
|
1158
1157
|
|
|
1159
|
-
# inject statsig stable_id
|
|
1160
|
-
if [[ -f "$_env_dir/stable_id" ]]; then
|
|
1161
|
-
_sid=$(tr -d '[:space:]' < "$_env_dir/stable_id")
|
|
1162
|
-
_config_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
1163
|
-
if [[ -d "$_config_dir/statsig" ]]; then
|
|
1164
|
-
_sid_found=false
|
|
1165
|
-
for _f in "$_config_dir/statsig"/statsig.stable_id.*; do
|
|
1166
|
-
[[ -f "$_f" ]] && { printf '"%s"' "$_sid" > "$_f"; _sid_found=true; }
|
|
1167
|
-
done
|
|
1168
|
-
[[ "$_sid_found" == "false" ]] && printf '"%s"' "$_sid" > "$_config_dir/statsig/statsig.stable_id.local"
|
|
1169
|
-
fi
|
|
1170
|
-
fi
|
|
1171
|
-
|
|
1172
1158
|
# inject env vars — proxy (only when proxy is configured)
|
|
1173
1159
|
if [[ -n "$PROXY" ]]; then
|
|
1174
1160
|
export _CAC_PROXY="$PROXY"
|
|
@@ -1253,6 +1239,13 @@ fi
|
|
|
1253
1239
|
# ── persona (Docker/server environment spoofing) ──
|
|
1254
1240
|
if [[ -f "$_env_dir/persona" ]]; then
|
|
1255
1241
|
_persona=$(tr -d '[:space:]' < "$_env_dir/persona")
|
|
1242
|
+
# Clear all high-priority detectTerminal() variables before injecting persona,
|
|
1243
|
+
# so real env vars from the host (e.g. CURSOR_TRACE_ID in Cursor) don't override.
|
|
1244
|
+
unset CURSOR_TRACE_ID VSCODE_GIT_ASKPASS_MAIN TERMINAL_EMULATOR VisualStudioVersion
|
|
1245
|
+
unset ITERM_SESSION_ID TERM_PROGRAM __CFBundleIdentifier
|
|
1246
|
+
unset TMUX STY KONSOLE_VERSION GNOME_TERMINAL_SERVICE XTERM_VERSION VTE_VERSION
|
|
1247
|
+
unset TERMINATOR_UUID KITTY_WINDOW_ID ALACRITTY_LOG TILIX_ID WT_SESSION
|
|
1248
|
+
unset MSYSTEM ConEmuANSI ConEmuPID ConEmuTask WSL_DISTRO_NAME
|
|
1256
1249
|
export TERM="xterm-256color"
|
|
1257
1250
|
case "$_persona" in
|
|
1258
1251
|
macos-vscode)
|
|
@@ -1280,7 +1273,9 @@ if [[ -f "$_env_dir/persona" ]]; then
|
|
|
1280
1273
|
fi
|
|
1281
1274
|
|
|
1282
1275
|
# ── NS-level DNS interception ──
|
|
1283
|
-
|
|
1276
|
+
# Use -r (readable) not -f (exists) — root-owned files with mode 600 exist but
|
|
1277
|
+
# can't be read by normal user, causing bun/node to crash silently.
|
|
1278
|
+
if [[ -r "$CAC_DIR/cac-dns-guard.js" ]]; then
|
|
1284
1279
|
case "${NODE_OPTIONS:-}" in
|
|
1285
1280
|
*cac-dns-guard.js*) ;; # already injected, skip
|
|
1286
1281
|
*) export NODE_OPTIONS="${NODE_OPTIONS:-} --require $CAC_DIR/cac-dns-guard.js" ;;
|
|
@@ -1291,7 +1286,7 @@ if [[ -f "$CAC_DIR/cac-dns-guard.js" ]]; then
|
|
|
1291
1286
|
esac
|
|
1292
1287
|
fi
|
|
1293
1288
|
# fallback layer: HOSTALIASES (gethostbyname level)
|
|
1294
|
-
[[ -
|
|
1289
|
+
[[ -r "$CAC_DIR/blocked_hosts" ]] && export HOSTALIASES="$CAC_DIR/blocked_hosts"
|
|
1295
1290
|
|
|
1296
1291
|
# ── mTLS client certificate ──
|
|
1297
1292
|
if [[ -f "$_env_dir/client_cert.pem" ]] && [[ -f "$_env_dir/client_key.pem" ]]; then
|
|
@@ -1318,7 +1313,8 @@ fi
|
|
|
1318
1313
|
[[ -f "$_env_dir/mac_address" ]] && export CAC_MAC=$(tr -d '[:space:]' < "$_env_dir/mac_address")
|
|
1319
1314
|
[[ -f "$_env_dir/machine_id" ]] && export CAC_MACHINE_ID=$(tr -d '[:space:]' < "$_env_dir/machine_id")
|
|
1320
1315
|
export CAC_USERNAME="user-$(echo "$_name" | cut -c1-8)"
|
|
1321
|
-
|
|
1316
|
+
export USER="$CAC_USERNAME" LOGNAME="$CAC_USERNAME"
|
|
1317
|
+
if [[ -r "$CAC_DIR/fingerprint-hook.js" ]]; then
|
|
1322
1318
|
case "${NODE_OPTIONS:-}" in
|
|
1323
1319
|
*fingerprint-hook.js*) ;;
|
|
1324
1320
|
*) export NODE_OPTIONS="--require $CAC_DIR/fingerprint-hook.js ${NODE_OPTIONS:-}" ;;
|
|
@@ -1587,8 +1583,14 @@ _ensure_initialized() {
|
|
|
1587
1583
|
if [[ -z "$_self_dir" ]] || [[ ! -f "$_self_dir/relay.js" ]]; then
|
|
1588
1584
|
_self_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
1589
1585
|
fi
|
|
1590
|
-
|
|
1591
|
-
|
|
1586
|
+
# Warn if running as root — files written here become root-owned and break
|
|
1587
|
+
# normal-user invocations (wrapper has set -e and will silently exit).
|
|
1588
|
+
if [[ $EUID -eq 0 ]]; then
|
|
1589
|
+
echo "[cac] warning: running as root may corrupt ~/.cac/ file ownership" >&2
|
|
1590
|
+
echo "[cac] hint: run as your normal user instead" >&2
|
|
1591
|
+
fi
|
|
1592
|
+
[[ -f "$_self_dir/fingerprint-hook.js" ]] && cp "$_self_dir/fingerprint-hook.js" "$CAC_DIR/fingerprint-hook.js" 2>/dev/null || true
|
|
1593
|
+
[[ -f "$_self_dir/relay.js" ]] && cp "$_self_dir/relay.js" "$CAC_DIR/relay.js" 2>/dev/null || true
|
|
1592
1594
|
_write_dns_guard_js 2>/dev/null || true
|
|
1593
1595
|
_write_blocked_hosts 2>/dev/null || true
|
|
1594
1596
|
|
|
@@ -1736,7 +1738,6 @@ _env_cmd_create() {
|
|
|
1736
1738
|
mkdir -p "$env_dir"
|
|
1737
1739
|
[[ -n "$proxy_url" ]] && echo "$proxy_url" > "$env_dir/proxy"
|
|
1738
1740
|
echo "$(_new_uuid)" > "$env_dir/uuid"
|
|
1739
|
-
echo "$(_new_sid)" > "$env_dir/stable_id"
|
|
1740
1741
|
echo "$(_new_user_id)" > "$env_dir/user_id"
|
|
1741
1742
|
echo "$(_new_machine_id)" > "$env_dir/machine_id"
|
|
1742
1743
|
echo "$(_new_hostname)" > "$env_dir/hostname"
|
|
@@ -1830,7 +1831,6 @@ MERGE_EOF
|
|
|
1830
1831
|
if [[ -d "$env_dir/.claude" ]]; then
|
|
1831
1832
|
export CLAUDE_CONFIG_DIR="$env_dir/.claude"
|
|
1832
1833
|
fi
|
|
1833
|
-
_update_statsig "$(_read "$env_dir/stable_id")" 2>/dev/null || true
|
|
1834
1834
|
_update_claude_json_user_id "$(_read "$env_dir/user_id")" 2>/dev/null || true
|
|
1835
1835
|
|
|
1836
1836
|
local elapsed; elapsed=$(_timer_elapsed)
|
|
@@ -1927,7 +1927,6 @@ _env_cmd_activate() {
|
|
|
1927
1927
|
export CLAUDE_CONFIG_DIR="$ENVS_DIR/$name/.claude"
|
|
1928
1928
|
fi
|
|
1929
1929
|
|
|
1930
|
-
_update_statsig "$(_read "$ENVS_DIR/$name/stable_id")"
|
|
1931
1930
|
_update_claude_json_user_id "$(_read "$ENVS_DIR/$name/user_id")"
|
|
1932
1931
|
|
|
1933
1932
|
# Relay lifecycle
|
package/fingerprint-hook.js
CHANGED
|
@@ -99,9 +99,57 @@ if (fakeMachineId) {
|
|
|
99
99
|
|
|
100
100
|
// --- Repository fingerprint (rh) interception ---
|
|
101
101
|
// Claude Code computes rh = SHA256(normalized_git_remote_url).hex.slice(0,16)
|
|
102
|
-
// and sends it with every 1p_event — cross-account linkage vector
|
|
102
|
+
// and sends it with every 1p_event — cross-account linkage vector.
|
|
103
|
+
//
|
|
104
|
+
// CC 2.1.88 reads the remote URL via gitFilesystem.ts which calls
|
|
105
|
+
// fs.readFileSync('.git/config') directly — NOT via git subprocess.
|
|
106
|
+
// We intercept both paths for defense in depth.
|
|
103
107
|
const fakeGitRemote = process.env.CAC_FAKE_GIT_REMOTE;
|
|
104
108
|
if (fakeGitRemote) {
|
|
109
|
+
// Path 1: intercept .git/config reads (primary path in CC 2.1.88)
|
|
110
|
+
// Replaces the [remote "origin"] url line with our fake remote URL.
|
|
111
|
+
function isGitConfigPath(p) {
|
|
112
|
+
var s = typeof p === 'string' ? p : (p && p.toString ? p.toString() : '');
|
|
113
|
+
return s === '.git/config' || s.endsWith('/.git/config') || s.endsWith('\\.git\\config');
|
|
114
|
+
}
|
|
115
|
+
function patchGitConfig(content) {
|
|
116
|
+
var str = typeof content === 'string' ? content : content.toString('utf8');
|
|
117
|
+
// Replace url = <anything> under [remote "origin"] section
|
|
118
|
+
str = str.replace(
|
|
119
|
+
/(\[remote\s+"origin"\][^\[]*?url\s*=\s*)[^\n]*/,
|
|
120
|
+
'$1' + fakeGitRemote
|
|
121
|
+
);
|
|
122
|
+
return str;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Patch readFileSync (sync path used by gitFilesystem.ts)
|
|
126
|
+
var _origReadFileSyncRh = fs.readFileSync;
|
|
127
|
+
fs.readFileSync = function(path, options) {
|
|
128
|
+
var result = _origReadFileSyncRh.apply(fs, arguments);
|
|
129
|
+
if (isGitConfigPath(path)) {
|
|
130
|
+
var patched = patchGitConfig(result);
|
|
131
|
+
return fakeResult(options, patched);
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Patch fs.promises.readFile (async path)
|
|
137
|
+
try {
|
|
138
|
+
var fspRh = require('fs').promises || require('fs/promises');
|
|
139
|
+
if (fspRh && fspRh.readFile) {
|
|
140
|
+
var _origPromiseReadFileRh = fspRh.readFile.bind(fspRh);
|
|
141
|
+
fspRh.readFile = function(path, options) {
|
|
142
|
+
if (isGitConfigPath(path)) {
|
|
143
|
+
return _origPromiseReadFileRh(path, options).then(function(content) {
|
|
144
|
+
return fakeResult(options, patchGitConfig(content));
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return _origPromiseReadFileRh(path, options);
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
} catch (_) {}
|
|
151
|
+
|
|
152
|
+
// Path 2: intercept git subprocess calls (fallback / older CC versions)
|
|
105
153
|
const GIT_REMOTE_PATTERNS = [
|
|
106
154
|
/git\s+remote\s+get-url/i,
|
|
107
155
|
/git\s+remote\s+-v/i,
|