claude-cac 1.1.5 → 1.1.7

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.
Files changed (2) hide show
  1. package/cac +127 -136
  2. package/package.json +1 -1
package/cac CHANGED
@@ -10,7 +10,7 @@ VERSIONS_DIR="$CAC_DIR/versions"
10
10
  # ━━━ utils.sh ━━━
11
11
  # ── utils: 颜色、读写、UUID、proxy 解析 ───────────────────────
12
12
 
13
- CAC_VERSION="1.1.5"
13
+ CAC_VERSION="1.1.7"
14
14
 
15
15
  _read() { [[ -f "$1" ]] && tr -d '[:space:]' < "$1" || echo "${2:-}"; }
16
16
  _die() { printf '%b\n' "$(_red "error:") $*" >&2; exit 1; }
@@ -1195,20 +1195,20 @@ _ensure_initialized() {
1195
1195
  mkdir -p "$CAC_DIR" "$ENVS_DIR" "$VERSIONS_DIR"
1196
1196
 
1197
1197
  # Always sync JS hooks + dns-guard (they update with cac versions)
1198
- # Resolve symlink — npm global install creates: .nvm/.../bin/cac → ../lib/node_modules/claude-cac/cac
1199
- local _self_path="${BASH_SOURCE[0]:-$0}"
1200
- # $0 may be just "cac" (no path) when invoked via PATH — resolve it
1201
- if [[ "$_self_path" != /* ]]; then
1202
- _self_path="$(command -v "$_self_path" 2>/dev/null || which "$_self_path" 2>/dev/null || echo "$_self_path")"
1203
- fi
1204
- # Follow symlink to real package directory
1205
- if [[ -L "$_self_path" ]]; then
1206
- local _link; _link="$(readlink "$_self_path")"
1207
- [[ "$_link" != /* ]] && _link="$(dirname "$_self_path")/$_link"
1208
- _self_path="$_link"
1209
- fi
1210
- local _self_dir
1211
- _self_dir="$(cd "$(dirname "$_self_path")" && pwd)"
1198
+ # Find the real package directory — npm creates symlinks:
1199
+ # ~/.nvm/.../bin/cac → ../lib/node_modules/claude-cac/cac
1200
+ # relay.js and fingerprint-hook.js live alongside the real cac script
1201
+ local _self_dir=""
1202
+ local _cac_bin; _cac_bin="$(command -v cac 2>/dev/null || true)"
1203
+ if [[ -n "$_cac_bin" ]] && [[ -L "$_cac_bin" ]]; then
1204
+ local _link; _link="$(readlink "$_cac_bin")"
1205
+ [[ "$_link" != /* ]] && _link="$(dirname "$_cac_bin")/$_link"
1206
+ _self_dir="$(cd "$(dirname "$_link")" && pwd)"
1207
+ fi
1208
+ # Fallback: directory of the running script
1209
+ if [[ -z "$_self_dir" ]] || [[ ! -f "$_self_dir/relay.js" ]]; then
1210
+ _self_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
1211
+ fi
1212
1212
  [[ -f "$_self_dir/fingerprint-hook.js" ]] && cp "$_self_dir/fingerprint-hook.js" "$CAC_DIR/fingerprint-hook.js"
1213
1213
  [[ -f "$_self_dir/relay.js" ]] && cp "$_self_dir/relay.js" "$CAC_DIR/relay.js"
1214
1214
  _write_dns_guard_js 2>/dev/null || true
@@ -1694,16 +1694,16 @@ cmd_relay() {
1694
1694
  # ━━━ cmd_check.sh ━━━
1695
1695
  # ── cmd: check ─────────────────────────────────────────────────
1696
1696
 
1697
- # 检测本地代理软件冲突(Clash / Surge / Shadowrocket 等)
1697
+ # 检测本地代理软件冲突
1698
1698
  _check_proxy_conflict() {
1699
- local proxy="$1" proxy_ip="$2"
1699
+ local proxy="$1" proxy_ip="$2" verbose="$3"
1700
1700
  local proxy_hp; proxy_hp=$(_proxy_host_port "$proxy")
1701
1701
  local proxy_host="${proxy_hp%%:*}"
1702
1702
 
1703
1703
  local os; os=$(_detect_os)
1704
1704
  local conflicts=()
1705
1705
 
1706
- # ── 1. 检测 TUN 模式进程 ──
1706
+ # 检测 TUN 模式进程
1707
1707
  local tun_procs="clash|mihomo|sing-box|surge|shadowrocket|v2ray|xray|hysteria|tuic|nekoray"
1708
1708
  local running
1709
1709
  if [[ "$os" == "macos" ]]; then
@@ -1711,29 +1711,22 @@ _check_proxy_conflict() {
1711
1711
  else
1712
1712
  running=$(ps -eo comm 2>/dev/null | grep -iE "$tun_procs" || true)
1713
1713
  fi
1714
-
1715
1714
  if [[ -n "$running" ]]; then
1716
1715
  local proc_names
1717
1716
  proc_names=$(echo "$running" | grep -ioE "$tun_procs" | sort -u | head -3)
1718
- if [[ -n "$proc_names" ]]; then
1719
- conflicts+=("检测到本地代理进程: $(echo "$proc_names" | tr '\n' ' ')")
1720
- fi
1717
+ [[ -n "$proc_names" ]] && conflicts+=("本地代理进程: $(echo "$proc_names" | tr '\n' ' ')")
1721
1718
  fi
1722
1719
 
1723
- # ── 2. 检测 TUN 网卡(utun / tun)──
1720
+ # 检测 TUN 网卡
1724
1721
  if [[ "$os" == "macos" ]]; then
1725
1722
  local tun_count
1726
1723
  tun_count=$(ifconfig 2>/dev/null | grep -cE '^utun[0-9]+' || echo 0)
1727
- if [[ "$tun_count" -gt 3 ]]; then
1728
- conflicts+=("检测到多个 TUN 网卡 (${tun_count} 个),可能有代理软件启用了 TUN 模式")
1729
- fi
1724
+ [[ "$tun_count" -gt 3 ]] && conflicts+=("TUN 网卡 ${tun_count} 个")
1730
1725
  elif [[ "$os" == "linux" ]]; then
1731
- if ip link show tun0 >/dev/null 2>&1; then
1732
- conflicts+=("检测到 tun0 网卡,可能有代理软件启用了 TUN 模式")
1733
- fi
1726
+ ip link show tun0 >/dev/null 2>&1 && conflicts+=("检测到 tun0")
1734
1727
  fi
1735
1728
 
1736
- # ── 3. 检测系统代理是否指向本机(macOS)──
1729
+ # 检测系统代理(macOS
1737
1730
  if [[ "$os" == "macos" ]]; then
1738
1731
  local net_service
1739
1732
  net_service=$(networksetup -listallnetworkservices 2>/dev/null | grep -iE 'Wi-Fi|Ethernet|以太网' | head -1 || true)
@@ -1741,172 +1734,170 @@ _check_proxy_conflict() {
1741
1734
  local sys_http_proxy
1742
1735
  sys_http_proxy=$(networksetup -getwebproxy "$net_service" 2>/dev/null || true)
1743
1736
  if echo "$sys_http_proxy" | grep -qi "Enabled: Yes"; then
1744
- local sys_host sys_port
1745
- sys_host=$(echo "$sys_http_proxy" | awk '/^Server:/{print $2}')
1746
- sys_port=$(echo "$sys_http_proxy" | awk '/^Port:/{print $2}')
1747
- [[ -n "$sys_host" ]] && conflicts+=("系统 HTTP 代理已开启: ${sys_host}:${sys_port}")
1737
+ local sys_host; sys_host=$(echo "$sys_http_proxy" | awk '/^Server:/{print $2}')
1738
+ local sys_port; sys_port=$(echo "$sys_http_proxy" | awk '/^Port:/{print $2}')
1739
+ [[ -n "$sys_host" ]] && conflicts+=("系统代理: ${sys_host}:${sys_port}")
1748
1740
  fi
1749
1741
  fi
1750
1742
  fi
1751
1743
 
1752
- # ── 4. 检测代理流量是否被二次转发(复用已获取的 proxy_ip)──
1744
+ # 检测双重代理
1753
1745
  local direct_ip
1754
1746
  direct_ip=$(curl -s --noproxy '*' --connect-timeout 5 https://api.ipify.org 2>/dev/null || true)
1755
1747
  if [[ -n "$direct_ip" ]] && [[ -n "$proxy_ip" ]] && [[ "$direct_ip" == "$proxy_ip" ]]; then
1756
- conflicts+=("代理出口 IP ($proxy_ip) 与直连出口 IP 相同,代理流量可能被本地软件拦截")
1748
+ conflicts+=("出口 IP 与直连相同,可能被拦截")
1757
1749
  fi
1758
1750
 
1759
- # ── 输出结果 ──
1751
+ # 无冲突
1760
1752
  if [[ ${#conflicts[@]} -eq 0 ]]; then
1761
- echo "$(_green "✓") 未检测到代理冲突"
1762
1753
  return 0
1763
1754
  fi
1764
1755
 
1765
- echo "$(_yellow "检测到本地代理软件"):"
1766
- for msg in "${conflicts[@]}"; do
1767
- echo " $(_dim "•") $msg"
1768
- done
1769
-
1770
- # 验证 relay 能否实际绕过
1756
+ # 验证 relay 能否绕过
1757
+ local relay_ok=false
1771
1758
  if _relay_is_running 2>/dev/null; then
1772
1759
  local rport; rport=$(_read "$CAC_DIR/relay.port" "")
1773
1760
  local relay_ip
1774
1761
  relay_ip=$(curl -s --proxy "http://127.0.0.1:$rport" --connect-timeout 8 https://api.ipify.org 2>/dev/null || true)
1775
- if [[ -n "$relay_ip" ]]; then
1776
- echo " $(_green "→") relay 已启动并连通 (出口: $(_cyan "$relay_ip")),冲突已自动绕过"
1777
- return 0
1778
- fi
1779
- fi
1780
-
1781
- # relay 未运行,尝试启动测试
1782
- if [[ -f "$CAC_DIR/relay.js" ]]; then
1762
+ [[ -n "$relay_ip" ]] && relay_ok=true
1763
+ elif [[ -f "$CAC_DIR/relay.js" ]]; then
1783
1764
  local _test_env; _test_env=$(_current_env)
1784
1765
  if _relay_start "$_test_env" 2>/dev/null; then
1785
1766
  local rport; rport=$(_read "$CAC_DIR/relay.port" "")
1786
1767
  local relay_ip
1787
1768
  relay_ip=$(curl -s --proxy "http://127.0.0.1:$rport" --connect-timeout 8 https://api.ipify.org 2>/dev/null || true)
1788
1769
  _relay_stop 2>/dev/null || true
1789
- if [[ -n "$relay_ip" ]]; then
1790
- echo " $(_green "→") relay 测试连通 (出口: $(_cyan "$relay_ip")),claude 启动时会自动绕过"
1791
- return 0
1792
- fi
1770
+ [[ -n "$relay_ip" ]] && relay_ok=true
1793
1771
  fi
1794
1772
  fi
1795
1773
 
1796
- # relay 也不行,给手动方案
1797
- echo " $(_yellow "") relay 无法绕过此冲突,需手动处理:"
1798
- echo " 在本地代理软件中为代理服务器 IP 添加 DIRECT 规则"
1799
- echo " 代理服务器:$(_bold "$proxy_host")"
1774
+ if [[ "$relay_ok" == "true" ]]; then
1775
+ echo "$(_green "") relay 自动绕过"
1776
+ if [[ "$verbose" == "true" ]]; then
1777
+ for msg in "${conflicts[@]}"; do
1778
+ echo " $(_dim "$msg")"
1779
+ done
1780
+ fi
1781
+ return 0
1782
+ fi
1783
+
1784
+ # relay 失败
1785
+ echo "$(_red "✗") 代理冲突,需手动处理"
1786
+ for msg in "${conflicts[@]}"; do
1787
+ echo " $(_yellow "•") $msg"
1788
+ done
1789
+ echo " 解决:在代理软件中为 $(_bold "$proxy_host") 添加 DIRECT 规则"
1800
1790
  return 1
1801
1791
  }
1802
1792
 
1803
1793
  cmd_check() {
1804
1794
  _require_setup
1805
1795
 
1796
+ local verbose=false
1797
+ [[ "${1:-}" == "-d" || "${1:-}" == "--detail" ]] && verbose=true
1798
+
1806
1799
  local current; current=$(_current_env)
1807
1800
 
1808
1801
  if [[ -f "$CAC_DIR/stopped" ]]; then
1809
- echo "$(_yellow "⚠ cac 已停用(cac stop)") claude 裸跑中"
1810
- echo " 恢复:cac ${current:-<name>}"
1802
+ echo "$(_yellow "⚠") cac 已停用运行 'cac <name>' 恢复"
1811
1803
  return
1812
1804
  fi
1813
-
1814
1805
  if [[ -z "$current" ]]; then
1815
1806
  echo "错误:未激活任何环境,运行 'cac <name>'" >&2; exit 1
1816
1807
  fi
1817
1808
 
1818
1809
  local env_dir="$ENVS_DIR/$current"
1819
1810
  local proxy; proxy=$(_read "$env_dir/proxy" "")
1811
+ local ver; ver=$(_read "$env_dir/version" "system")
1820
1812
 
1821
- echo "当前环境:$(_bold "$current")"
1822
- echo " 代理 :${proxy:-(无代理)}"
1823
- echo " UUID :$(_read "$env_dir/uuid")"
1824
- echo " stable_id :$(_read "$env_dir/stable_id")"
1825
- echo " user_id :$(_read "$env_dir/user_id" "(旧环境,无此字段)")"
1826
- echo " 版本 :$(_read "$env_dir/version" "system")"
1827
- echo " TZ :$(_read "$env_dir/tz" "(未设置)")"
1828
- echo " LANG :$(_read "$env_dir/lang" "(未设置)")"
1829
- echo
1813
+ # ── 基本信息 ──
1814
+ echo "$(_bold "$current") (claude: $(_cyan "$ver"))"
1830
1815
 
1831
- # ── 网络连通性(仅在有代理时检查)──
1832
- if [[ -n "$proxy" ]]; then
1833
- printf " TCP 连通 ... "
1834
- if ! _proxy_reachable "$proxy"; then
1835
- echo "$(_red " 不通")"; return
1836
- fi
1837
- echo "$(_green "✓")"
1838
-
1839
- printf " 出口 IP ... "
1840
- local proxy_ip
1841
- proxy_ip=$(curl -s --proxy "$proxy" \
1842
- --connect-timeout 8 https://api.ipify.org 2>/dev/null || true)
1843
- if [[ -n "$proxy_ip" ]]; then
1844
- echo "$(_green "$proxy_ip")"
1845
- else
1846
- echo "$(_yellow "获取失败")"
1847
- fi
1848
- else
1849
- echo " $(_dim "(无代理,跳过网络检查)")"
1816
+ if [[ "$verbose" == "true" ]]; then
1817
+ echo " UUID : $(_read "$env_dir/uuid")"
1818
+ echo " stable_id : $(_read "$env_dir/stable_id")"
1819
+ echo " user_id : $(_read "$env_dir/user_id" "—")"
1820
+ echo " TZ : $(_read "$env_dir/tz" "—")"
1821
+ echo " LANG : $(_read "$env_dir/lang" "—")"
1850
1822
  fi
1851
1823
 
1852
- # ── 本地代理冲突检测(仅在有代理时)──
1824
+ # ── 网络 ──
1825
+ local proxy_ip=""
1853
1826
  if [[ -n "$proxy" ]]; then
1854
- echo
1855
- echo "── 冲突检测 ────────────────────────────────────────────"
1856
- printf " 代理冲突 ... "
1857
- _check_proxy_conflict "$proxy" "${proxy_ip:-}"
1858
- fi
1827
+ printf " 代理 : "
1828
+ if ! _proxy_reachable "$proxy"; then
1829
+ echo "$(_red "✗ 不通") ($proxy)"
1830
+ return
1831
+ fi
1832
+ proxy_ip=$(curl -s --proxy "$proxy" --connect-timeout 8 https://api.ipify.org 2>/dev/null || true)
1833
+ echo "$(_green "✓") ${proxy_ip:-?} $(_dim "via ${proxy%%@*}@...")"
1859
1834
 
1860
- echo
1861
- echo "── Relay 中转 ────────────────────────────────────────"
1862
- if _relay_is_running; then
1863
- local rpid; rpid=$(_read "$CAC_DIR/relay.pid")
1864
- local rport; rport=$(_read "$CAC_DIR/relay.port" "未知")
1865
- echo " Relay ... $(_green "运行中") (PID=$rpid, 端口=$rport)"
1866
- elif [[ -n "$proxy" ]]; then
1867
- echo " Relay ... $(_dim "未运行(claude 启动时按需自动启用)")"
1835
+ # 冲突检测
1836
+ printf " 冲突检测 : "
1837
+ _check_proxy_conflict "$proxy" "${proxy_ip:-}" "$verbose"
1868
1838
  else
1869
- echo " Relay ... $(_dim "(无代理,不需要)")"
1839
+ echo " 代理 : $(_dim "无(API Key 模式)")"
1870
1840
  fi
1871
1841
 
1872
- echo
1873
- echo "── 安全防护状态 ──────────────────────────────────────"
1874
-
1875
- # ── NS 层级 DNS 拦截 ──
1876
- printf " DNS 拦截 ... "
1877
- _check_dns_block "statsig.anthropic.com"
1878
-
1879
- # ── 多层环境变量保护(单次读取 wrapper 文件)──
1880
- echo " 环境变量保护:"
1842
+ # ── 安全防护(简洁模式只显示计数)──
1881
1843
  local wrapper_file="$CAC_DIR/bin/claude"
1882
1844
  local wrapper_content=""
1883
1845
  [[ -f "$wrapper_file" ]] && wrapper_content=$(<"$wrapper_file")
1884
1846
  local env_vars=(
1885
- "CLAUDE_CODE_ENABLE_TELEMETRY"
1886
- "DO_NOT_TRACK"
1887
- "OTEL_SDK_DISABLED"
1888
- "OTEL_TRACES_EXPORTER"
1889
- "OTEL_METRICS_EXPORTER"
1890
- "OTEL_LOGS_EXPORTER"
1891
- "SENTRY_DSN"
1892
- "DISABLE_ERROR_REPORTING"
1893
- "DISABLE_BUG_COMMAND"
1894
- "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"
1895
- "TELEMETRY_DISABLED"
1896
- "DISABLE_TELEMETRY"
1847
+ "CLAUDE_CODE_ENABLE_TELEMETRY" "DO_NOT_TRACK"
1848
+ "OTEL_SDK_DISABLED" "OTEL_TRACES_EXPORTER" "OTEL_METRICS_EXPORTER" "OTEL_LOGS_EXPORTER"
1849
+ "SENTRY_DSN" "DISABLE_ERROR_REPORTING" "DISABLE_BUG_COMMAND"
1850
+ "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC" "TELEMETRY_DISABLED" "DISABLE_TELEMETRY"
1897
1851
  )
1852
+ local env_ok=0 env_total=${#env_vars[@]}
1898
1853
  for var in "${env_vars[@]}"; do
1899
- printf " %-32s" "$var"
1900
- if [[ "$wrapper_content" == *"$var"* ]]; then
1901
- echo "$(_green "✓ 已配置")"
1902
- else
1903
- echo "$(_red "✗ 未找到")"
1904
- fi
1854
+ [[ "$wrapper_content" == *"$var"* ]] && (( env_ok++ )) || true
1905
1855
  done
1906
1856
 
1907
- # ── mTLS 证书 ──
1908
- printf " mTLS 认证 ... "
1909
- _check_mtls "$env_dir"
1857
+ # DNS 拦截
1858
+ local dns_ok=false
1859
+ if [[ -f "$CAC_DIR/cac-dns-guard.js" ]]; then
1860
+ local dns_result
1861
+ dns_result=$(node -e '
1862
+ require(process.argv[1]);
1863
+ var dns = require("dns");
1864
+ dns.lookup(process.argv[2], function(err) {
1865
+ process.stdout.write(err && (err.code === "ECONNREFUSED" || err.code === "ENOTFOUND") ? "BLOCKED" : "OPEN");
1866
+ });
1867
+ ' "$CAC_DIR/cac-dns-guard.js" "statsig.anthropic.com" 2>/dev/null || echo "ERROR")
1868
+ [[ "$dns_result" == "BLOCKED" ]] && dns_ok=true
1869
+ fi
1870
+
1871
+ # mTLS
1872
+ local mtls_ok=false
1873
+ local ca_cert="$CAC_DIR/ca/ca_cert.pem"
1874
+ local client_cert="$env_dir/client_cert.pem"
1875
+ [[ -f "$ca_cert" ]] && [[ -f "$client_cert" ]] && \
1876
+ openssl verify -CAfile "$ca_cert" "$client_cert" >/dev/null 2>&1 && mtls_ok=true
1877
+
1878
+ if [[ "$verbose" == "true" ]]; then
1879
+ printf " DNS 拦截 : "
1880
+ [[ "$dns_ok" == "true" ]] && echo "$(_green "✓")" || echo "$(_red "✗")"
1881
+ echo " 遥测屏蔽 : ${env_ok}/${env_total} 环境变量"
1882
+ for var in "${env_vars[@]}"; do
1883
+ printf " %-36s" "$var"
1884
+ [[ "$wrapper_content" == *"$var"* ]] && echo "$(_green "✓")" || echo "$(_red "✗")"
1885
+ done
1886
+ printf " mTLS : "
1887
+ [[ "$mtls_ok" == "true" ]] && echo "$(_green "✓")" || echo "$(_yellow "—")"
1888
+ else
1889
+ local checks=() fails=()
1890
+ [[ "$dns_ok" == "true" ]] && checks+=("DNS 拦截") || fails+=("DNS 拦截")
1891
+ [[ "$env_ok" -eq "$env_total" ]] && checks+=("遥测屏蔽 ${env_ok}/${env_total}") || fails+=("遥测屏蔽 ${env_ok}/${env_total}")
1892
+ [[ "$mtls_ok" == "true" ]] && checks+=("mTLS") || true
1893
+
1894
+ printf " 防护 : "
1895
+ if [[ ${#fails[@]} -eq 0 ]]; then
1896
+ echo "$(_green "✓") $(IFS=', '; echo "${checks[*]}")"
1897
+ else
1898
+ echo "$(_yellow "⚠") $(IFS=', '; echo "${fails[*]}")"
1899
+ fi
1900
+ fi
1910
1901
  }
1911
1902
 
1912
1903
  # ━━━ cmd_stop.sh ━━━
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-cac",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Isolate, protect, and manage your Claude Code — versions, environments, identity, and proxy.",
5
5
  "bin": {
6
6
  "cac": "cac"