diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 37002ec..19d476e 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -12,7 +12,7 @@ body:
id: version
attributes:
label: ipcheck version
- placeholder: ipcheck 0.8.0
+ placeholder: ipcheck 0.8.1
validations:
required: true
- type: dropdown
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d16fdcf..d79530b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,16 @@ All notable changes to ipcheck are documented here. The project follows
## [Unreleased]
+## [0.8.1] - 2026-07-23
+
+### Changed
+
+- Refocused the English and Chinese READMEs on quick installation, the
+ difference from generic speed tests, common commands, and privacy guarantees;
+ moved scoring, network, and automation details into bilingual reference docs.
+- Updated both terminal demos to transition from a healthy network to a
+ realistically limited proxy route with actionable bottleneck hints.
+
## [0.8.0] - 2026-07-23
### Added
@@ -122,6 +132,8 @@ All notable changes to ipcheck are documented here. The project follows
- Homebrew and direct-download packaging.
- Median/P95 TTFB, jitter, reference bandwidth, and macOS `networkQuality`.
+[Unreleased]: https://github.com/jacklv-coder/ipcheck/compare/v0.8.1...HEAD
+[0.8.1]: https://github.com/jacklv-coder/ipcheck/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/jacklv-coder/ipcheck/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/jacklv-coder/ipcheck/compare/v0.6.1...v0.7.0
[0.6.1]: https://github.com/jacklv-coder/ipcheck/compare/v0.6.0...v0.6.1
diff --git a/README.md b/README.md
index 89f82c5..d7d6268 100644
--- a/README.md
+++ b/README.md
@@ -6,177 +6,110 @@
[简体中文](README.zh-CN.md)
-Know whether your AI coding CLI is slow, blocked, or using the wrong gateway.
+Know whether **Codex** or **Claude Code** is slow, blocked, or using the wrong
+proxy before it breaks your coding flow.
-`ipcheck` is a zero-dependency Bash CLI for the real network paths used by
-**Codex** and **Claude Code**. It auto-detects installed clients and safe,
-non-secret routing configuration, then reports reachability, median/P95
-time-to-first-byte (TTFB), jitter, reference bandwidth, and a clear
-`GOOD`, `FAIR`, `POOR`, or `BLOCKED` result. Interactive runs show animated,
-color-coded progress, can be cancelled cleanly with `Ctrl+C`, and finish with
-an adaptive terminal dashboard plus a rule-based readiness score.
+`ipcheck` is a zero-dependency Bash CLI that tests the real service routes used
+by AI coding clients. It reports reachability, median/P95 time to first byte,
+jitter, reference download/upload speed, and a plain-language readiness score.
+It never reads API keys, sends prompts, or makes billable model requests.
-
+
-```text
-$ ipcheck
-ipcheck v0.8.0 AI coding network check
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+## Quick start
- ✓ Ready to code? YES
- 100/100 ████████████████████ COMFORTABLE
-
- This network is ready for AI-assisted development.
-
-◆ AI service latency
- ✓ REACH ChatGPT Codex HTTP 401 · median 260 ms
- ✓ REACH Claude Messages API Claude Code HTTP 403 · median 220 ms
-
-◆ AI service results
- ● Codex GOOD · ● Claude Code GOOD
-
-◆ Network bandwidth
- Download 80.0 Mbps FAST Cloudflare, up to 2 MB
- Upload 16.0 Mbps FAST Cloudflare, up to 1 MB zero-filled
+```bash
+brew install jacklv-coder/tap/ipcheck
+ipcheck
```
-## Why ipcheck
-
-- Tests the routes the clients actually use, including Claude-compatible
- `${ANTHROPIC_BASE_URL}/v1/messages` gateways.
-- Detects Codex `config.toml`, custom model providers, Claude Code
- `settings.json`, `ANTHROPIC_BASE_URL`, and `ANTHROPIC_MODEL`.
-- Understands OpenAI, Anthropic, LiteLLM-style gateways, and Alibaba Cloud
- Model Studio/DashScope Anthropic-compatible routes.
-- Reports each client separately, so one healthy service cannot hide another
- blocked service.
-- Never extracts, stores, prints, or sends API keys; never sends a prompt or
- creates a billable model request.
-- Produces human, Markdown, and stable versioned JSON output for support tickets
- and automation.
-- Runs on the Bash and curl already available on macOS and Linux.
-
-For coding agents, TTFB, failures, and jitter usually matter more than peak
-download bandwidth. `ipcheck` scores the service path separately from its
-optional Cloudflare reference download.
-
-## Install
-
-### Homebrew
+Already installed?
```bash
-brew tap jacklv-coder/tap
-brew install ipcheck
+brew upgrade ipcheck
```
-### Direct download
+
+Install without Homebrew
```bash
mkdir -p "$HOME/.local/bin"
-curl -fsSL https://raw.githubusercontent.com/jacklv-coder/ipcheck/v0.8.0/bin/ipcheck \
+curl -fsSL https://raw.githubusercontent.com/jacklv-coder/ipcheck/v0.8.1/bin/ipcheck \
-o "$HOME/.local/bin/ipcheck"
chmod +x "$HOME/.local/bin/ipcheck"
```
Make sure `$HOME/.local/bin` is on `PATH`.
-## Usage
+
-Auto-detect installed clients:
+## Why not just use Speedtest?
-```bash
-ipcheck
-ipcheck --quick
-```
+| Tool | What it tells you |
+| --- | --- |
+| Speedtest | Peak bandwidth to a nearby test server |
+| `ping` / `curl` | Basic reachability to one address |
+| `ipcheck` | Codex/Claude routes, proxy path, TTFB, P95, jitter, failures, bandwidth, and coding readiness |
-Select a client explicitly:
+For coding agents, a 500 Mbps connection can still feel slow when the first byte
+takes five seconds or the proxy route is unstable. `ipcheck` measures those
+interactive bottlenecks directly.
-```bash
-ipcheck codex
-ipcheck claude
-ipcheck all
-ipcheck --service claude
-```
+## What makes it useful
-Create shareable reports:
+- Tests each detected client separately, so one healthy route cannot hide a
+ broken one.
+- Understands OpenAI, Anthropic, LiteLLM-style gateways, and Alibaba Cloud
+ Model Studio/DashScope Anthropic-compatible routes.
+- Explains whether the main problem is reachability, TTFB, P95, jitter,
+ download speed, or upload speed.
+- Produces human, Markdown, and stable versioned JSON reports.
+- Shows animated progress, adapts to narrow terminals, and exits cleanly with
+ status `130` when cancelled with `Ctrl+C`.
-```bash
-ipcheck --json > ipcheck-report.json
-ipcheck --markdown > ipcheck-report.md
-```
+## Supported clients and routes
-Explain every score component:
+| Client | Configuration detected | Route tested |
+| --- | --- | --- |
+| Codex | `$CODEX_HOME/config.toml`, `model`, `openai_base_url`, custom provider | ChatGPT/OpenAI defaults or configured `/v1/responses` |
+| Claude Code | `settings.json`, `ANTHROPIC_BASE_URL`, `ANTHROPIC_MODEL` | Configured `${ANTHROPIC_BASE_URL}/v1/messages` |
+| Custom | `--endpoint`, `IPCHECK_ENDPOINTS` | User-provided HTTP/HTTPS endpoint |
-```bash
-ipcheck --explain-score
-```
+## Common commands
-Tune or override a check:
+| Command | Purpose |
+| --- | --- |
+| `ipcheck` | Auto-detect installed clients and run the standard check |
+| `ipcheck --quick` | One sample, shorter timeout, no bandwidth transfer |
+| `ipcheck codex` / `ipcheck claude` | Test only one client |
+| `ipcheck all` | Test Codex and Claude Code together |
+| `ipcheck --explain-score` | Show every score component |
+| `ipcheck --json` | Emit structured output for automation |
+| `ipcheck --markdown` | Create a shareable support report |
+| `ipcheck --system` | Add macOS `networkQuality` measurements |
+| `ipcheck --no-bandwidth` | Skip capped Cloudflare download/upload checks |
-```bash
-ipcheck --samples 10
-ipcheck --timeout 30
-ipcheck --system
-ipcheck --explain-score
-ipcheck --endpoint https://your-gateway.example.com/health
-ipcheck --lang zh
-ipcheck --no-progress --no-color
-ipcheck --no-upload
-```
+Run `ipcheck --help` for every option and environment variable.
-The human and Markdown reports default to the terminal/system language.
-English and Simplified Chinese are currently supported. Any unsupported system
-language falls back to English. Override detection with
-`--lang en`, `--lang zh`, or `IPCHECK_LANG=en|zh`. JSON field names, enum values,
-and diagnostic reasons remain stable English for automation. Progress is written
-to stderr only for human output and can be controlled with
-`IPCHECK_PROGRESS=auto|always|never`.
-
-Interactive progress starts with `Press Ctrl+C to cancel at any time.` An
-interrupt clears the live progress line, prints `Cancelled.`, removes temporary
-files, and exits with the conventional status `130`.
-
-The service table measures time to first byte and jitter; it is not a transfer
-speed test. The separate bandwidth section downloads up to 2 MB and uploads up to 1 MB of
-zero-filled data through the reported proxy/network path. It rates throughput
-for common development work without allowing bandwidth to hide a slow or
-unstable AI service. Use `--no-upload` to skip only the upload or
-`--no-bandwidth` to skip both directions.
-
-Bandwidth ratings use development-oriented thresholds: download is `FAST` at
-25 Mbps or higher and `ADEQUATE` from 5 Mbps; upload is `FAST` at 10 Mbps or
-higher and `ADEQUATE` from 2 Mbps. Lower measurements are `SLOW`. The optional
-`--system` test delegates to macOS `networkQuality`, which can transfer
-substantially more data than ipcheck's capped Cloudflare checks.
-
-The readiness score is a transparent heuristic, not a claim about user
-percentiles. Rule v2 gives the least healthy detected client up to 90
-service-path points: reachability and success rate contribute 35, median TTFB
-35, P95 10, and jitter 10. Each bandwidth direction adds 5 points when fast,
-3 when adequate, deducts 5 when slow, and stays neutral when unavailable; an
-incomplete sample deducts 2 more without hiding its measured speed class.
-Usable-but-delayed paths are capped at 89, temporarily unavailable or poor
-paths at 64, and blocked paths at 0. Score labels are `COMFORTABLE` from 90,
-`GOOD` from 75, `USABLE` from 65, and `LIMITED` below 65. JSON exposes `score`,
-`score_label`, `score_method`, and the per-component `score_breakdown` for
-automation.
-
-Run `ipcheck --help` for the complete option and environment-variable list.
+## Reading the result
-## Supported clients and routes
+| Result | Meaning |
+| --- | --- |
+| `GOOD` | Reachable with comfortable first-byte latency and stability |
+| `FAIR` | Usable, but delayed, intermittent, rate-limited, or temporarily unhealthy |
+| `POOR` | Very slow, unstable, mostly unavailable, or using an invalid API route |
+| `BLOCKED` | No service response, or the proxy rejected the request first |
-| Client | Configuration detected | Network route tested |
-| --- | --- | --- |
-| Codex | `$CODEX_HOME/config.toml`, `model`, `openai_base_url`, selected custom provider | ChatGPT/OpenAI defaults or the configured `/v1/responses` route |
-| Claude Code | `$CLAUDE_CONFIG_DIR/settings.json`, `~/.claude/settings.json`, `ANTHROPIC_BASE_URL`, `ANTHROPIC_MODEL` | Configured `${ANTHROPIC_BASE_URL}/v1/messages` route |
-| Custom | `--endpoint`, `IPCHECK_ENDPOINTS` | User-provided GET endpoint(s) |
+HTTP `401` and `403` count as reachable because DNS, proxying, TLS, and HTTP
+reached the API route. HTTP `407` means the proxy blocked the request.
-Legacy `CODEX_NETWORK_ENDPOINTS` remains supported. Claude-specific endpoint
-overrides can use `CLAUDE_NETWORK_ENDPOINTS`.
+The 0–100 score is a transparent rule, not a user percentile. It prioritizes
+service reachability and latency over peak bandwidth. See
+[Scoring and result rules](docs/scoring.md) for the exact calculation.
-### Alibaba Cloud + Claude Code
+## Claude Code gateway example
-This common Claude Code configuration is auto-detected:
+This configuration is auto-detected:
```json
{
@@ -188,86 +121,38 @@ This common Claude Code configuration is auto-detected:
}
```
-`ipcheck claude` probes
-`https://dashscope.aliyuncs.com/apps/anthropic/v1/messages` with an empty,
-unauthenticated protocol check. It does not read `ANTHROPIC_AUTH_TOKEN`.
-
-## Proxy behavior
-
-`HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY`, and lowercase variants are reported
-with credentials redacted. Claude probes intentionally ignore `ALL_PROXY` when
-neither `HTTPS_PROXY` nor `HTTP_PROXY` is configured, matching Claude Code's
-documented support boundary. On macOS, Codex and custom checks can fall back to
-the configured system HTTPS proxy when an HTTPS proxy environment variable is
-absent.
-
-Claude Code supports `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY`, but does not
-support SOCKS proxies. `ipcheck` warns when its detected proxy configuration is
-incompatible or likely to behave differently from curl or Codex. See Anthropic's
-[corporate proxy documentation](https://docs.anthropic.com/en/docs/claude-code/corporate-proxy).
-
-## Results
-
-| Result | Meaning |
-| --- | --- |
-| `GOOD` | Every primary sample succeeded with acceptable TTFB and jitter. |
-| `FAIR` | Reachable, but latency is elevated, samples failed, or the API is rate-limited/unhealthy. |
-| `POOR` | Mostly unavailable, very slow/unstable, or the configured API route returned HTTP 404. |
-| `BLOCKED` | No primary endpoint returned an HTTP response, or the proxy returned HTTP 407. |
-
-HTTP 401 and 403 count as network-reachable because they prove DNS, proxying,
-TLS, and HTTP reached the API route. HTTP 407 does not count because the proxy
-rejected the request first. A 404 on a configured `/v1/messages` or
-`/v1/responses` route is reported as `POOR` with a base-URL hint.
-
-The default thresholds are:
-
-- `GOOD`: 100% primary success, median TTFB below 800 ms, jitter below 1,000 ms.
-- `FAIR`: all samples succeed and median TTFB is below 3,000 ms, or the path is
- otherwise reachable with a recoverable warning.
-- `POOR`: primary success below 60%, median TTFB at least 3,000 ms, or an invalid
- configured API route.
-
-## Privacy and security
-
-`ipcheck` is intentionally a network-layer diagnostic:
+`ipcheck claude` safely probes the corresponding `/v1/messages` route without
+reading or sending `ANTHROPIC_AUTH_TOKEN`.
-- The settings parser selects only named routing/model fields. Authentication
- values are never extracted into shell variables or temporary files.
-- API keys, bearer tokens, and cookies are never printed or passed to curl.
-- Every curl invocation starts with `-q`, so user-level `.curlrc` files cannot
- inject headers, cookies, credentials, or alternate routes.
-- Claude/OpenAI protocol probes use an empty JSON object and cannot invoke a
- model successfully without authentication.
-- Proxy credentials are masked. Endpoint URLs are restricted to credential-free
- HTTP/HTTPS paths; URL userinfo, query strings, and fragments are rejected.
-- Temporary metrics are deleted on exit.
+## Privacy by design
-Please report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
+- Authentication values never enter shell variables or temporary files.
+- API keys, bearer tokens, cookies, and prompts are never sent to curl.
+- Proxy credentials are redacted; unsafe endpoint URLs are rejected.
+- Every curl invocation ignores user `.curlrc` files.
+- Temporary metrics are removed on normal exit and cancellation.
-## JSON and exit codes
+See [SECURITY.md](SECURITY.md) for private vulnerability reporting.
-JSON output includes `schema_version`, per-service results, per-endpoint
-measurements, privacy guarantees, warnings, bandwidth, and optional macOS
-network quality. Additive fields may appear without changing the schema version;
-breaking field changes increment it.
+## Documentation
-- `0`: at least one selected primary service path was reachable.
-- `1`: every selected primary service path was blocked/unavailable.
-- `2`: invalid invocation or missing dependency.
+- [Scoring and result rules](docs/scoring.md)
+- [Routes, proxies, and bandwidth](docs/network.md)
+- [Reports, automation, and exit codes](docs/reporting.md)
+- [Release history](CHANGELOG.md)
## Requirements
+- macOS or Linux
- Bash 3.2+
-- curl
-- awk, sed, sort
+- curl, awk, sed, sort
- Optional on macOS: `networkQuality`
## Contributing
-Issues and pull requests are welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md)
-and the [Code of Conduct](CODE_OF_CONDUCT.md). See [CHANGELOG.md](CHANGELOG.md)
-for release history.
+Issues and pull requests are welcome. Start with
+[CONTRIBUTING.md](CONTRIBUTING.md) and the
+[Code of Conduct](CODE_OF_CONDUCT.md).
## License
diff --git a/README.zh-CN.md b/README.zh-CN.md
index b93cf34..4461c2e 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -6,160 +6,104 @@
[English](README.md)
-快速判断 AI 编程 CLI 到底是网络慢、代理被拦,还是网关地址配错。
+在代理或网络打断编码节奏之前,判断 **Codex**、**Claude Code** 到底是慢、
+被阻断,还是走错了链路。
-`ipcheck` 是一个零依赖 Bash 命令行工具,面向 **Codex** 和
-**Claude Code** 的真实网络路径。它会自动识别本机客户端以及不含密钥的路由
-配置,测量可达性、首字节延迟(TTFB)中位数/P95、抖动和参考带宽,并给出
-明确的 `GOOD`、`FAIR`、`POOR` 或 `BLOCKED` 结论。交互运行时会显示带颜色的
-动态进度,支持通过 `Ctrl+C` 友好取消,并用自适应终端仪表盘直接告诉你
-“现在是否适合开发”以及规则化的开发适配分。
+`ipcheck` 是一个零依赖 Bash CLI,检测 AI 编程客户端实际使用的服务路径,
+提供可达性、TTFB 中位数/P95、抖动、参考上下行速度和直白的开发适配分。
+它不会读取 API Key、发送 Prompt,也不会产生模型调用费用。
-
+
-```text
-$ ipcheck
-ipcheck v0.8.0 AI 编程网络体检
-━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+## 快速开始
- ✓ 现在适合开发吗?适合
- 100/100 ████████████████████ 舒适
-
- 当前网络适合进行 AI 辅助开发。
-
-◆ AI 服务延迟
- ✓ 可达 ChatGPT Codex HTTP 401 · 中位 260 ms
- ✓ 可达 Claude Messages API Claude Code HTTP 403 · 中位 220 ms
-
-◆ AI 服务结论
- ● Codex 良好 · ● Claude Code 良好
-
-◆ 网络带宽
- 下载 80.0 Mbps 快 Cloudflare,最多 2 MB
- 上传 16.0 Mbps 快 Cloudflare,最多 1 MB 零字节
+```bash
+brew install jacklv-coder/tap/ipcheck
+ipcheck
```
-## 核心能力
-
-- 检测客户端真实协议路径,包括 Claude 兼容网关的
- `${ANTHROPIC_BASE_URL}/v1/messages`。
-- 自动识别 Codex `config.toml`、自定义模型提供商、Claude Code
- `settings.json`、`ANTHROPIC_BASE_URL` 和 `ANTHROPIC_MODEL`。
-- 兼容 OpenAI、Anthropic、LiteLLM 类网关,以及阿里云百炼/DashScope
- Anthropic 兼容入口。
-- Codex 与 Claude Code 分别评分,避免一个正常服务掩盖另一个被拦服务。
-- 不提取、不保存、不显示也不发送 API Key;不发送 Prompt,不产生模型调用费用。
-- 支持终端、Markdown 和带版本号的 JSON 报告,方便提工单或接入自动化。
-
-对 AI 编程工具而言,首字节延迟、失败率和抖动通常比峰值下载带宽更重要。
-因此服务路径与 Cloudflare 参考下载会分开评分。
-
-## 安装
-
-Homebrew:
+已经安装过:
```bash
-brew tap jacklv-coder/tap
-brew install ipcheck
+brew upgrade ipcheck
```
-直接下载:
+
+不使用 Homebrew 安装
```bash
mkdir -p "$HOME/.local/bin"
-curl -fsSL https://raw.githubusercontent.com/jacklv-coder/ipcheck/v0.8.0/bin/ipcheck \
+curl -fsSL https://raw.githubusercontent.com/jacklv-coder/ipcheck/v0.8.1/bin/ipcheck \
-o "$HOME/.local/bin/ipcheck"
chmod +x "$HOME/.local/bin/ipcheck"
```
-## 使用
+请确认 `$HOME/.local/bin` 已加入 `PATH`。
-自动识别客户端:
+
-```bash
-ipcheck
-ipcheck --quick
-```
-
-只检查指定客户端:
-
-```bash
-ipcheck codex
-ipcheck claude
-ipcheck all
-ipcheck --service claude
-```
+## 为什么不只用 Speedtest?
-生成可分享报告:
-
-```bash
-ipcheck --json > ipcheck-report.json
-ipcheck --markdown > ipcheck-report.md
-```
+| 工具 | 能告诉你什么 |
+| --- | --- |
+| Speedtest | 到附近测速服务器的峰值带宽 |
+| `ping` / `curl` | 单个地址的基础连通性 |
+| `ipcheck` | Codex/Claude 实际路径、代理、TTFB、P95、抖动、失败率、带宽和开发适配分 |
-查看每一项评分依据:
+即使带宽达到 500 Mbps,只要首字节需要五秒或代理链路不稳定,AI 编程体验
+仍然会很慢。`ipcheck` 直接测量这些影响交互节奏的瓶颈。
-```bash
-ipcheck --explain-score
-```
+## 核心价值
-其他常用选项:
-
-```bash
-ipcheck --samples 10
-ipcheck --timeout 30
-ipcheck --system
-ipcheck --explain-score
-ipcheck --endpoint https://your-gateway.example.com/health
-ipcheck --lang en
-ipcheck --no-progress --no-color
-ipcheck --no-upload
-```
+- 分别检测每个客户端,避免一条健康链路掩盖另一条异常链路。
+- 兼容 OpenAI、Anthropic、LiteLLM 类网关,以及阿里云百炼/DashScope
+ Anthropic 兼容入口。
+- 直接指出主要问题来自可达性、TTFB、P95、抖动、下载还是上传。
+- 支持终端、Markdown 和稳定的版本化 JSON 报告。
+- 提供动态进度、窄终端适配,并能通过 `Ctrl+C` 干净退出,返回状态码 `130`。
-终端和 Markdown 报告默认跟随终端/系统语言,目前支持中文和英文;任何尚未
-支持的系统语言都会回退到英文。可以使用
-`--lang zh`、`--lang en`,或 `IPCHECK_LANG=zh|en` 明确指定。JSON 的字段名、
-枚举值和诊断原因始终保持英文,方便自动化脚本稳定解析。实时进度只会在普通
-终端报告中写入 stderr,可通过 `IPCHECK_PROGRESS=auto|always|never` 控制。
+## 支持的客户端与路径
-交互运行开始时会提示“按 Ctrl+C 可随时取消”。收到中断后,ipcheck 会清除
-动态进度行、显示“已取消”、删除临时文件,并使用标准退出码 `130` 结束。
+| 客户端 | 自动识别的配置 | 检测路径 |
+| --- | --- | --- |
+| Codex | `$CODEX_HOME/config.toml`、`model`、`openai_base_url`、自定义 provider | ChatGPT/OpenAI 默认路径或 `/v1/responses` |
+| Claude Code | `settings.json`、`ANTHROPIC_BASE_URL`、`ANTHROPIC_MODEL` | `${ANTHROPIC_BASE_URL}/v1/messages` |
+| 自定义 | `--endpoint`、`IPCHECK_ENDPOINTS` | 用户指定的 HTTP/HTTPS 地址 |
-“服务链路”测量的是首字节延迟和抖动,并不是下载速度。独立的“网络带宽”模块
-会通过报告中注明的代理/网络路径下载最多 2 MB,并上传最多 1 MB 全零测试数据,然后按
-日常开发需求分别评级。带宽不会掩盖 AI 服务本身的高延迟或不稳定。使用
-`--no-upload` 只跳过上传,使用 `--no-bandwidth` 同时跳过下载和上传。
+## 常用命令
-评级采用面向开发场景的阈值:下载达到 25 Mbps 为“快”,达到 5 Mbps 为
-“够用”;上传达到 10 Mbps 为“快”,达到 2 Mbps 为“够用”;低于这些范围为
-“慢”。可选的 `--system` 会交给 macOS `networkQuality` 测试,消耗的数据量
-可能明显高于 ipcheck 自带的限量 Cloudflare 测试。
+| 命令 | 用途 |
+| --- | --- |
+| `ipcheck` | 自动识别客户端并执行标准检测 |
+| `ipcheck --quick` | 单次采样、缩短超时、不传输测速数据 |
+| `ipcheck codex` / `ipcheck claude` | 只检测一个客户端 |
+| `ipcheck all` | 同时检测 Codex 和 Claude Code |
+| `ipcheck --explain-score` | 展开全部评分依据 |
+| `ipcheck --json` | 输出供自动化使用的结构化数据 |
+| `ipcheck --markdown` | 生成可分享的支持报告 |
+| `ipcheck --system` | 增加 macOS `networkQuality` 数据 |
+| `ipcheck --no-bandwidth` | 跳过限量 Cloudflare 上下行检测 |
-开发适配分是透明的规则评分,不代表“超过多少用户”的百分位。规则 v2 对最弱的
-客户端链路计算最多 90 分:可达性和成功率占 35 分、TTFB 中位数占 35 分、
-P95 占 10 分、抖动占 10 分。下载和上传分别在“快”时加 5 分、“够用”时加
-3 分、“慢”时扣 5 分,不可用时不加不扣;部分样本会在保留实际速度等级的
-基础上再扣 2 分。可用但偏慢的链路最高 89 分,暂时不可用或较差的链路最高
-64 分,阻断链路为 0 分。90 分起为“舒适”、75 分起为“良好”、65 分起为
-“可用”,低于 65 分为“受限”。JSON 会输出 `score`、`score_label`、
-`score_method`,以及逐项的 `score_breakdown`。
+运行 `ipcheck --help` 可以查看全部选项和环境变量。
-运行 `ipcheck --help` 可以查看完整参数。
+## 如何理解结论
-## 支持的客户端与配置
+| 结果 | 含义 |
+| --- | --- |
+| `GOOD` | 链路可达,首字节延迟和稳定性处于舒适范围 |
+| `FAIR` | 可以使用,但有延迟、波动、限流或临时服务异常 |
+| `POOR` | 非常慢、不稳定、多数不可用,或 API 路径配置错误 |
+| `BLOCKED` | 没有服务响应,或请求先被代理拒绝 |
-| 客户端 | 自动读取的非敏感配置 | 检测路径 |
-| --- | --- | --- |
-| Codex | `$CODEX_HOME/config.toml`、`model`、`openai_base_url`、当前自定义 provider | ChatGPT/OpenAI 默认路径或自定义 `/v1/responses` |
-| Claude Code | `$CLAUDE_CONFIG_DIR/settings.json`、`~/.claude/settings.json`、`ANTHROPIC_BASE_URL`、`ANTHROPIC_MODEL` | `${ANTHROPIC_BASE_URL}/v1/messages` |
-| 自定义 | `--endpoint`、`IPCHECK_ENDPOINTS` | 用户指定的 GET 地址 |
+HTTP `401` 和 `403` 代表链路可达,因为 DNS、代理、TLS 和 HTTP 已经到达
+API 路径;HTTP `407` 表示请求被代理拦截。
-原有 `CODEX_NETWORK_ENDPOINTS` 保持兼容;Claude 可使用
-`CLAUDE_NETWORK_ENDPOINTS` 覆盖端点。
+0–100 分是透明的规则评分,不代表用户百分位,并且服务可达性和交互延迟
+比峰值带宽更重要。精确计算方式见[评分与结论规则](docs/scoring.zh-CN.md)。
-### 阿里云百炼 + Claude Code
+## Claude Code 网关示例
-下面这种配置会被自动识别:
+下面的配置会被自动识别:
```json
{
@@ -171,80 +115,38 @@ P95 占 10 分、抖动占 10 分。下载和上传分别在“快”时加 5
}
```
-执行 `ipcheck claude` 时会检测:
-
-```text
-https://dashscope.aliyuncs.com/apps/anthropic/v1/messages
-```
-
-检测请求不携带 `ANTHROPIC_AUTH_TOKEN`,也不会触发模型推理计费。
-
-## 代理行为
-
-工具会显示 `HTTPS_PROXY`、`HTTP_PROXY`、`ALL_PROXY` 及其小写形式,并对
-用户名、密码进行脱敏。若没有配置 `HTTPS_PROXY` 或 `HTTP_PROXY`,Claude
-检测会忽略其官方未声明支持的 `ALL_PROXY`。在 macOS 上,Codex/自定义检测
-还可以在没有 HTTPS 代理环境变量时使用系统 HTTPS 代理。
-
-Claude Code 支持 `HTTPS_PROXY`、`HTTP_PROXY` 和 `NO_PROXY`,但不支持
-SOCKS。检测到不兼容或与 Codex/curl 路径可能不同的配置时,`ipcheck` 会明确警告。
-详见 Anthropic 的
-[企业代理文档](https://docs.anthropic.com/en/docs/claude-code/corporate-proxy)。
-
-## 结论含义
-
-| 结果 | 含义 |
-| --- | --- |
-| `GOOD` | 主端点全部采样成功,TTFB 与抖动均在合理范围 |
-| `FAIR` | 网络可达,但延迟偏高、存在失败,或 API 正在限流/异常 |
-| `POOR` | 多数不可用、非常慢/不稳定,或配置的 API 路径返回 404 |
-| `BLOCKED` | 主端点没有 HTTP 响应,或代理返回 HTTP 407 |
-
-HTTP 401/403 仍算网络可达,因为这表示 DNS、代理、TLS 和 HTTP 已到达 API
-路径;HTTP 407 表示请求先被代理拒绝。配置的 `/v1/messages` 或
-`/v1/responses` 返回 404 时,会判定为 `POOR` 并提示检查 Base URL。
+`ipcheck claude` 会安全检测对应的 `/v1/messages`,不会读取或发送
+`ANTHROPIC_AUTH_TOKEN`。
-默认阈值:
+## 隐私设计
-- `GOOD`:主路径 100% 成功,中位 TTFB 小于 800 ms,抖动小于 1,000 ms。
-- `FAIR`:全部成功且中位 TTFB 小于 3,000 ms,或出现可恢复的服务警告。
-- `POOR`:成功率低于 60%、中位 TTFB 至少 3,000 ms,或 API 路径无效。
-
-## 隐私与安全
-
-- 配置解析只提取指定的路由和模型字段,认证值不会进入 Shell 变量或临时文件。
-- 不显示 API Key、Bearer Token 或 Cookie,也不会把它们传给 curl。
-- 所有 curl 调用均以 `-q` 开始,用户级 `.curlrc` 无法注入请求头、Cookie、
- 凭据或替换检测地址。
-- Claude/OpenAI 协议探测只发送空 JSON,且不带认证,因此不会调用模型。
-- 代理凭据会被隐藏;端点只允许无凭据的 HTTP/HTTPS 路径,URL 用户信息、
- 查询参数和片段会被拒绝。
-- 临时指标文件会在程序退出时删除。
+- 认证值不会进入 Shell 变量或临时文件。
+- 不会向 curl 发送 API Key、Bearer Token、Cookie 或 Prompt。
+- 代理凭据会被脱敏,不安全的端点 URL 会被拒绝。
+- 每次 curl 调用都会忽略用户的 `.curlrc`。
+- 正常退出或取消时都会删除临时指标。
安全问题请按照 [SECURITY.md](SECURITY.md) 私下报告。
-## JSON 与退出码
-
-JSON 包含 `schema_version`、每个客户端与端点的独立结论、隐私声明、警告、
-带宽和可选的 macOS `networkQuality` 数据。增加兼容字段不会升级 schema;
-破坏性字段调整才会升级版本。
+## 详细文档
-- `0`:至少一个主服务路径可达。
-- `1`:全部主服务路径被阻断或不可用。
-- `2`:参数错误或缺少运行依赖。
+- [评分与结论规则](docs/scoring.zh-CN.md)
+- [服务路径、代理与带宽](docs/network.zh-CN.md)
+- [报告、自动化与退出码](docs/reporting.zh-CN.md)
+- [版本历史](CHANGELOG.md)
## 运行要求
+- macOS 或 Linux
- Bash 3.2+
-- curl
-- awk、sed、sort
+- curl、awk、sed、sort
- macOS 可选:`networkQuality`
## 参与贡献
-欢迎提交 Issue 和 Pull Request。请先阅读 [CONTRIBUTING.md](CONTRIBUTING.md)
-和 [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)。版本历史见
-[CHANGELOG.md](CHANGELOG.md)。
+欢迎提交 Issue 和 Pull Request。请先阅读
+[CONTRIBUTING.md](CONTRIBUTING.md) 和
+[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)。
## 许可证
diff --git a/assets/ipcheck-demo-zh.gif b/assets/ipcheck-demo-zh.gif
index d5e0019..b7f91e1 100644
Binary files a/assets/ipcheck-demo-zh.gif and b/assets/ipcheck-demo-zh.gif differ
diff --git a/assets/ipcheck-demo.gif b/assets/ipcheck-demo.gif
index bd57568..fc8c002 100644
Binary files a/assets/ipcheck-demo.gif and b/assets/ipcheck-demo.gif differ
diff --git a/assets/ipcheck-preview-zh.png b/assets/ipcheck-preview-zh.png
index f85eafa..53fea8a 100644
Binary files a/assets/ipcheck-preview-zh.png and b/assets/ipcheck-preview-zh.png differ
diff --git a/assets/ipcheck-preview.png b/assets/ipcheck-preview.png
index 60cae3d..ba0ae1e 100644
Binary files a/assets/ipcheck-preview.png and b/assets/ipcheck-preview.png differ
diff --git a/bin/ipcheck b/bin/ipcheck
index e42a290..02047a2 100755
--- a/bin/ipcheck
+++ b/bin/ipcheck
@@ -10,7 +10,7 @@ unset LC_ALL
LC_NUMERIC=C
export LC_NUMERIC
-VERSION="0.8.0"
+VERSION="0.8.1"
SCHEMA_VERSION=1
SAMPLES=3
TIMEOUT=15
diff --git a/demo/render_demo.py b/demo/render_demo.py
index 7d95317..1a269ac 100644
--- a/demo/render_demo.py
+++ b/demo/render_demo.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-"""Render English and Chinese README GIFs from the real ipcheck CLI.
+"""Render healthy-to-limited English and Chinese demos from the real CLI.
Requires Python 3, Pillow, and a CJK font for the Chinese demo. No live service
or credential is used.
@@ -67,7 +67,7 @@ def font(size: int, language: str, bold: bool = False) -> ImageFont.FreeTypeFont
return ImageFont.load_default()
-def demo_output(language: str) -> list[str]:
+def demo_output(language: str, scenario: str) -> list[str]:
with tempfile.TemporaryDirectory(prefix="ipcheck-demo-") as directory:
fixture = Path(directory)
home = fixture / "home"
@@ -95,11 +95,21 @@ def demo_output(language: str) -> list[str]:
esac
done
if [ "$is_upload" -eq 1 ]; then
- printf '200\\t1000000\\t2000000'
+ if [ "${IPCHECK_DEMO_SCENARIO-}" = "limited" ]; then
+ printf '200\\t1000000\\t425000'
+ else
+ printf '200\\t1000000\\t2000000'
+ fi
elif [ "$is_download" -eq 1 ]; then
- printf '200\\t2000000\\t10000000'
+ if [ "${IPCHECK_DEMO_SCENARIO-}" = "limited" ]; then
+ printf '200\\t2000000\\t400000'
+ else
+ printf '200\\t2000000\\t10000000'
+ fi
elif [ "$is_claude" -eq 1 ]; then
printf '403\\t0.020\\t0.040\\t0.060\\t0.220\\t0.240\\t151\\t1000'
+elif [ "${IPCHECK_DEMO_SCENARIO-}" = "limited" ]; then
+ printf '401\\t0.080\\t0.180\\t0.320\\t4.881\\t5.100\\t151\\t1000'
else
printf '401\\t0.020\\t0.040\\t0.060\\t0.260\\t0.280\\t151\\t1000'
fi
@@ -135,6 +145,7 @@ def demo_output(language: str) -> list[str]:
"HTTPS_PROXY": "http://127.0.0.1:1080",
"IPCHECK_LANG": language,
"IPCHECK_PROGRESS": "always",
+ "IPCHECK_DEMO_SCENARIO": scenario,
"COLUMNS": "100",
}
)
@@ -173,6 +184,8 @@ def demo_output(language: str) -> list[str]:
" 上传",
" 当前网络",
" 可以正常开发",
+ " 建议先",
+ " !",
"◆ 检测到的客户端",
" Codex",
" Claude Code",
@@ -211,6 +224,8 @@ def demo_output(language: str) -> list[str]:
" Upload",
" This network",
" You can work normally",
+ " Switch proxy routes",
+ " !",
"◆ Detected clients",
" Codex",
" Claude Code",
@@ -229,7 +244,11 @@ def demo_output(language: str) -> list[str]:
def line_color(line: str) -> str:
- if line.strip().startswith("█") or any(token in line for token in ("100/100", "Ready to code? YES", "现在适合开发吗?适合")):
+ score_match = re.search(r"\b(\d+)/100\b", line)
+ if score_match:
+ score = int(score_match.group(1))
+ return GREEN if score >= 90 else YELLOW if score >= 65 else RED
+ if any(token in line for token in ("Ready to code? YES", "现在适合开发吗?适合")):
return GREEN
if line.startswith(("◆ Detected clients", "◆ AI service latency", "◆ AI service results", "◆ Network bandwidth", "◆ 检测到的客户端", "◆ AI 服务延迟", "◆ AI 服务结论", "◆ 网络带宽", "ipcheck v")):
return CYAN
@@ -251,13 +270,44 @@ def clip_line(draw: ImageDraw.ImageDraw, line: str, body_font: ImageFont.ImageFo
return clipped + "..."
+def wrap_lines(
+ draw: ImageDraw.ImageDraw,
+ lines: list[str],
+ body_font: ImageFont.ImageFont,
+) -> list[str]:
+ wrapped: list[str] = []
+ max_width = WIDTH - 96
+ for original in lines:
+ if original and set(original) in ({"━"}, {"─"}):
+ wrapped.append(original)
+ continue
+ line = original
+ indent = line[: len(line) - len(line.lstrip())]
+ while draw.textlength(line, font=body_font) > max_width:
+ split_at = len(line)
+ while split_at > len(indent) and draw.textlength(line[:split_at], font=body_font) > max_width:
+ split_at -= 1
+ word_break = line.rfind(" ", len(indent) + 1, split_at)
+ if word_break > len(indent) + 12:
+ split_at = word_break
+ wrapped.append(line[:split_at].rstrip())
+ line = f"{indent} {line[split_at:].lstrip()}"
+ wrapped.append(line)
+ return wrapped
+
+
def draw_status_line(
draw: ImageDraw.ImageDraw,
line: str,
position: tuple[int, int],
body_font: ImageFont.ImageFont,
) -> bool:
- colors = {"✓": GREEN, "●": GREEN, "✕": RED, "!": YELLOW}
+ dot_color = GREEN
+ if any(token in line for token in ("POOR", "BLOCKED", "较差", "阻断")):
+ dot_color = RED
+ elif any(token in line for token in ("FAIR", "一般")):
+ dot_color = YELLOW
+ colors = {"✓": GREEN, "●": dot_color, "✕": RED, "!": YELLOW}
if not any(symbol in line for symbol in colors):
return False
x, y = position
@@ -286,7 +336,13 @@ def frame(command: str, lines: list[str], language: str) -> Image.Image:
draw.text((76, 98), command, font=body_font, fill=TEXT)
max_lines = 28
- visible = lines[-max_lines:]
+ visible = wrap_lines(draw, lines, body_font)[-max_lines:]
+ score_color = GREEN
+ for candidate in lines:
+ score_match = re.search(r"\b(\d+)/100\b", candidate)
+ if score_match:
+ score = int(score_match.group(1))
+ score_color = GREEN if score >= 90 else YELLOW if score >= 65 else RED
y = 138
for line in visible:
if line and set(line) in ({"━"}, {"─"}):
@@ -294,6 +350,10 @@ def frame(command: str, lines: list[str], language: str) -> Image.Image:
y += 23
continue
clipped = clip_line(draw, line, body_font)
+ if clipped.strip().startswith(("█", "░")):
+ draw.text((48, y), clipped, font=body_font, fill=score_color)
+ y += 23
+ continue
if not draw_status_line(draw, clipped, (48, y), body_font):
draw.text((48, y), clipped, font=body_font, fill=line_color(clipped))
y += 23
@@ -301,7 +361,8 @@ def frame(command: str, lines: list[str], language: str) -> Image.Image:
def render_demo(language: str) -> None:
- output = demo_output(language)
+ healthy_output = demo_output(language, "healthy")
+ limited_output = demo_output(language, "limited")
command = "ipcheck --lang zh" if language == "zh" else "ipcheck"
frames: list[Image.Image] = []
durations: list[int] = []
@@ -312,9 +373,16 @@ def render_demo(language: str) -> None:
frames.append(frame(command, [], language))
durations.append(350)
- for index in range(1, len(output) + 1):
- frames.append(frame(command, output[:index], language))
- durations.append(95 if output[index - 1] else 45)
+ for index in range(1, len(healthy_output) + 1):
+ frames.append(frame(command, healthy_output[:index], language))
+ durations.append(85 if healthy_output[index - 1] else 45)
+ durations[-1] = 1800
+
+ frames.append(frame(command, [], language))
+ durations.append(450)
+ for index in range(1, len(limited_output) + 1):
+ frames.append(frame(command, limited_output[:index], language))
+ durations.append(85 if limited_output[index - 1] else 45)
durations[-1] = 3200
output_path = OUTPUTS[language]
@@ -329,7 +397,7 @@ def render_demo(language: str) -> None:
disposal=1,
)
screenshot_path = SCREENSHOTS[language]
- frame(command, output, language).save(screenshot_path, optimize=True)
+ frame(command, limited_output, language).save(screenshot_path, optimize=True)
print(f"Rendered {output_path} ({output_path.stat().st_size / 1024:.0f} KiB, {len(frames)} frames)")
print(f"Rendered {screenshot_path} ({screenshot_path.stat().st_size / 1024:.0f} KiB)")
diff --git a/docs/launch-kit.md b/docs/launch-kit.md
index a4a827a..fe4cc23 100644
--- a/docs/launch-kit.md
+++ b/docs/launch-kit.md
@@ -23,8 +23,7 @@ model request is needed.
Install:
```sh
-brew tap jacklv-coder/tap
-brew install ipcheck
+brew install jacklv-coder/tap/ipcheck
ipcheck
```
@@ -71,8 +70,7 @@ Repo and demo: https://github.com/jacklv-coder/ipcheck
安装:
```sh
-brew tap jacklv-coder/tap
-brew install ipcheck
+brew install jacklv-coder/tap/ipcheck
ipcheck
```
diff --git a/docs/network.md b/docs/network.md
new file mode 100644
index 0000000..6dc059b
--- /dev/null
+++ b/docs/network.md
@@ -0,0 +1,73 @@
+# Routes, proxies, and bandwidth
+
+[README](../README.md) · [简体中文](network.zh-CN.md)
+
+## Routes tested
+
+`ipcheck` tests the protocol route the detected client is expected to use:
+
+| Client | Route |
+| --- | --- |
+| Codex defaults | ChatGPT and OpenAI reachability paths |
+| Codex custom provider | Configured base URL plus `/v1/responses` when needed |
+| Claude Code | `${ANTHROPIC_BASE_URL}/v1/messages` |
+| Custom | Credential-free URL supplied with `--endpoint` or `IPCHECK_ENDPOINTS` |
+
+`CODEX_NETWORK_ENDPOINTS` remains supported for compatibility. Claude-specific
+overrides can use `CLAUDE_NETWORK_ENDPOINTS`.
+
+## Proxy behavior
+
+`HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY`, and lowercase variants are reported
+with credentials redacted.
+
+- Codex and custom checks can fall back to the macOS system HTTPS proxy when no
+ HTTPS proxy environment variable is configured.
+- Claude Code documents `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY`, but not
+ `ALL_PROXY` or SOCKS proxies.
+- Claude probes therefore ignore `ALL_PROXY` unless `HTTPS_PROXY` or
+ `HTTP_PROXY` is also configured.
+- `ipcheck` warns when the detected proxy is incompatible or likely to differ
+ from the client's actual route.
+
+See Anthropic's
+[corporate proxy documentation](https://docs.anthropic.com/en/docs/claude-code/corporate-proxy).
+
+## Alibaba Cloud / DashScope example
+
+Given this Claude Code configuration:
+
+```json
+{
+ "env": {
+ "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY",
+ "ANTHROPIC_BASE_URL": "https://dashscope.aliyuncs.com/apps/anthropic",
+ "ANTHROPIC_MODEL": "deepseek-v4-flash"
+ }
+}
+```
+
+`ipcheck claude` probes
+`https://dashscope.aliyuncs.com/apps/anthropic/v1/messages` with an empty,
+unauthenticated protocol request. It never reads `ANTHROPIC_AUTH_TOKEN`.
+
+## Reference bandwidth
+
+The service table measures time to first byte and jitter; it is not a transfer
+speed test. The separate bandwidth section:
+
+- downloads at most 2 MB from Cloudflare;
+- uploads at most 1 MB of zero-filled data;
+- uses the reported proxy/network path;
+- treats incomplete transfers as estimates;
+- never lets high bandwidth hide an unhealthy service route.
+
+Use `--no-upload` to skip upload only, or `--no-bandwidth` to skip both. The
+optional `--system` flag runs macOS `networkQuality`, which may transfer
+substantially more data than ipcheck's capped checks.
+
+## Endpoint safety
+
+Custom endpoints must use HTTP or HTTPS and cannot contain URL credentials,
+query strings, or fragments. Every curl invocation starts with `-q`, so a user
+`.curlrc` cannot inject credentials, headers, cookies, or alternate routes.
diff --git a/docs/network.zh-CN.md b/docs/network.zh-CN.md
new file mode 100644
index 0000000..55a50f0
--- /dev/null
+++ b/docs/network.zh-CN.md
@@ -0,0 +1,71 @@
+# 服务路径、代理与带宽
+
+[中文 README](../README.zh-CN.md) · [English](network.md)
+
+## 检测的服务路径
+
+`ipcheck` 会检测客户端实际预期使用的协议路径:
+
+| 客户端 | 检测路径 |
+| --- | --- |
+| Codex 默认配置 | ChatGPT 和 OpenAI 可达路径 |
+| Codex 自定义 provider | 配置的 Base URL,必要时补充 `/v1/responses` |
+| Claude Code | `${ANTHROPIC_BASE_URL}/v1/messages` |
+| 自定义 | 通过 `--endpoint` 或 `IPCHECK_ENDPOINTS` 提供的无凭据 URL |
+
+`CODEX_NETWORK_ENDPOINTS` 保持兼容;Claude 可使用
+`CLAUDE_NETWORK_ENDPOINTS` 覆盖端点。
+
+## 代理行为
+
+`HTTPS_PROXY`、`HTTP_PROXY`、`ALL_PROXY` 及其小写形式会被显示,其中凭据
+会被脱敏。
+
+- 没有 HTTPS 代理环境变量时,Codex 和自定义检测可以使用 macOS 系统
+ HTTPS 代理。
+- Claude Code 官方声明支持 `HTTPS_PROXY`、`HTTP_PROXY` 和 `NO_PROXY`,
+ 但没有声明支持 `ALL_PROXY` 或 SOCKS。
+- 因此没有同时配置 `HTTPS_PROXY` 或 `HTTP_PROXY` 时,Claude 检测会忽略
+ `ALL_PROXY`。
+- 检测到不兼容或可能与客户端实际链路不同的代理时,`ipcheck` 会明确警告。
+
+详见 Anthropic 的
+[企业代理文档](https://docs.anthropic.com/en/docs/claude-code/corporate-proxy)。
+
+## 阿里云百炼 / DashScope 示例
+
+假设 Claude Code 使用下面的配置:
+
+```json
+{
+ "env": {
+ "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY",
+ "ANTHROPIC_BASE_URL": "https://dashscope.aliyuncs.com/apps/anthropic",
+ "ANTHROPIC_MODEL": "deepseek-v4-flash"
+ }
+}
+```
+
+`ipcheck claude` 会向
+`https://dashscope.aliyuncs.com/apps/anthropic/v1/messages` 发送空的、未认证的
+协议检测请求,并且不会读取 `ANTHROPIC_AUTH_TOKEN`。
+
+## 参考带宽
+
+服务表测量的是首字节延迟和抖动,并不是传输测速。独立的带宽模块会:
+
+- 从 Cloudflare 下载最多 2 MB;
+- 上传最多 1 MB 全零数据;
+- 使用报告中显示的代理/网络路径;
+- 将未完成传输明确标记为估算;
+- 不允许高带宽掩盖异常的服务路径。
+
+使用 `--no-upload` 只跳过上传,使用 `--no-bandwidth` 跳过上下行。可选的
+`--system` 会运行 macOS `networkQuality`,消耗的数据量可能明显高于 ipcheck
+的限量检测。
+
+## 端点安全
+
+自定义端点必须使用 HTTP 或 HTTPS,并且不能包含 URL 凭据、查询参数或片段。
+每次 curl 调用都以 `-q` 开始,因此用户 `.curlrc` 无法注入凭据、请求头、
+Cookie 或替换检测路径。
diff --git a/docs/reporting.md b/docs/reporting.md
new file mode 100644
index 0000000..cc4f19c
--- /dev/null
+++ b/docs/reporting.md
@@ -0,0 +1,68 @@
+# Reports, automation, and exit codes
+
+[README](../README.md) · [简体中文](reporting.zh-CN.md)
+
+## Human output
+
+Human output follows the terminal or system language. English and Simplified
+Chinese are supported; other languages fall back to English. Override detection
+with `--lang en`, `--lang zh`, or `IPCHECK_LANG=en|zh`.
+
+Interactive progress is written to stderr and controlled with
+`IPCHECK_PROGRESS=auto|always|never`, `--progress`, or `--no-progress`.
+`Ctrl+C` clears the live line, removes temporary files, prints a localized
+cancellation message, and exits with status 130.
+
+Use `--no-color` or `NO_COLOR=1` for plain output.
+
+## Markdown reports
+
+```bash
+ipcheck --markdown > ipcheck-report.md
+```
+
+Markdown reports are intended for support tickets and issue descriptions. Add
+`--explain-score` when the recipient needs the full scoring calculation.
+
+## JSON reports
+
+```bash
+ipcheck --json > ipcheck-report.json
+```
+
+JSON field names, enum values, and diagnostic reasons remain English for stable
+automation. Output includes:
+
+- `schema_version` and ipcheck version;
+- overall result and developer readiness;
+- per-service and per-endpoint measurements;
+- score method and component breakdown;
+- warnings and redacted network path;
+- download/upload and optional macOS system measurements;
+- explicit privacy guarantees.
+
+Additive fields may appear without changing `schema_version`; breaking field
+changes increment it. Progress is disabled for JSON and Markdown modes.
+
+## Exit codes
+
+| Code | Meaning |
+| ---: | --- |
+| `0` | At least one selected primary service path was reachable |
+| `1` | Every selected primary service path was blocked or unavailable |
+| `2` | Invalid invocation or missing dependency |
+| `130` | Cancelled with `Ctrl+C` |
+
+## Useful overrides
+
+| Setting | Purpose |
+| --- | --- |
+| `--samples N` | Requests per endpoint |
+| `--timeout N` | Per-request timeout in seconds |
+| `--endpoint URL` | Test a custom credential-free GET endpoint |
+| `IPCHECK_ENDPOINTS` | Pipe-separated custom endpoints |
+| `CODEX_NETWORK_ENDPOINTS` | Codex endpoint override |
+| `CLAUDE_NETWORK_ENDPOINTS` | Claude endpoint override |
+| `IPCHECK_SERVICES` | `auto`, `codex`, `claude`, or `all` |
+
+Run `ipcheck --help` for the authoritative complete list.
diff --git a/docs/reporting.zh-CN.md b/docs/reporting.zh-CN.md
new file mode 100644
index 0000000..f250d09
--- /dev/null
+++ b/docs/reporting.zh-CN.md
@@ -0,0 +1,65 @@
+# 报告、自动化与退出码
+
+[中文 README](../README.zh-CN.md) · [English](reporting.md)
+
+## 终端输出
+
+普通终端输出会跟随终端或系统语言。目前支持英文和简体中文,其他语言会回退
+到英文。可以使用 `--lang en`、`--lang zh` 或 `IPCHECK_LANG=en|zh` 覆盖。
+
+交互进度写入 stderr,可以通过 `IPCHECK_PROGRESS=auto|always|never`、
+`--progress` 或 `--no-progress` 控制。按下 `Ctrl+C` 后会清除动态行、删除临时
+文件、显示本地化取消提示,并返回状态码 130。
+
+使用 `--no-color` 或 `NO_COLOR=1` 可以获得无颜色输出。
+
+## Markdown 报告
+
+```bash
+ipcheck --markdown > ipcheck-report.md
+```
+
+Markdown 适合支持工单和 Issue 描述。需要完整评分计算时可以增加
+`--explain-score`。
+
+## JSON 报告
+
+```bash
+ipcheck --json > ipcheck-report.json
+```
+
+为了稳定自动化,JSON 字段名、枚举值和诊断原因始终使用英文。内容包括:
+
+- `schema_version` 和 ipcheck 版本;
+- 总体结论和开发适配结果;
+- 每个服务、每个端点的测量数据;
+- 评分方法和逐项分数;
+- 警告及脱敏后的网络路径;
+- 上下行带宽和可选的 macOS 系统数据;
+- 明确的隐私保证。
+
+增加兼容字段不会修改 `schema_version`,破坏性变更才会升级。JSON 和 Markdown
+模式不会输出动态进度。
+
+## 退出码
+
+| 状态码 | 含义 |
+| ---: | --- |
+| `0` | 至少一个选中的主服务路径可达 |
+| `1` | 所有选中的主服务路径均被阻断或不可用 |
+| `2` | 参数错误或缺少依赖 |
+| `130` | 通过 `Ctrl+C` 取消 |
+
+## 常用覆盖项
+
+| 设置 | 用途 |
+| --- | --- |
+| `--samples N` | 每个端点的请求次数 |
+| `--timeout N` | 单次请求超时秒数 |
+| `--endpoint URL` | 检测自定义的无凭据 GET 地址 |
+| `IPCHECK_ENDPOINTS` | 使用竖线分隔的自定义端点 |
+| `CODEX_NETWORK_ENDPOINTS` | Codex 端点覆盖 |
+| `CLAUDE_NETWORK_ENDPOINTS` | Claude 端点覆盖 |
+| `IPCHECK_SERVICES` | `auto`、`codex`、`claude` 或 `all` |
+
+运行 `ipcheck --help` 可以查看权威的完整列表。
diff --git a/docs/scoring.md b/docs/scoring.md
new file mode 100644
index 0000000..10f9a71
--- /dev/null
+++ b/docs/scoring.md
@@ -0,0 +1,74 @@
+# Scoring and result rules
+
+[README](../README.md) · [简体中文](scoring.zh-CN.md)
+
+`ipcheck` uses two related signals:
+
+- a service result: `GOOD`, `FAIR`, `POOR`, or `BLOCKED`;
+- a 0–100 development-readiness score calculated with `rule_v2`.
+
+The score is a transparent heuristic, not a user percentile or a benchmark of
+model intelligence.
+
+## Service results
+
+| Result | Default rule |
+| --- | --- |
+| `GOOD` | 100% primary success, median TTFB below 800 ms, jitter below 1,000 ms |
+| `FAIR` | Reachable with median TTFB below 3,000 ms, recoverable failures, rate limiting, or server errors |
+| `POOR` | Primary success below 60%, median TTFB at least 3,000 ms, or HTTP 404 on a configured API route |
+| `BLOCKED` | No primary HTTP response, or proxy authentication stopped the request with HTTP 407 |
+
+With multiple clients, results remain independent. If every client is blocked,
+the overall result is `BLOCKED`; if blocked and reachable clients are mixed, it
+is `POOR`; otherwise the least healthy client result is used. The readiness
+score always uses the lowest-scoring service path, so a healthy Claude route
+cannot hide a broken Codex route.
+
+## Rule v2 service-path points
+
+The selected service path contributes up to 90 points.
+
+| Component | Maximum | Rule |
+| --- | ---: | --- |
+| Reachability | 35 | 35 at 100% success, 22 at 60–99%, 10 above 0%, otherwise 0 |
+| Median TTFB | 35 | 35 below 800 ms, 30 below 1,500, 22 below 3,000, 14 below 5,000, otherwise 6 |
+| P95 TTFB | 10 | 10 below 2,000 ms, 7 below 4,000, 4 below 6,000, otherwise 0 |
+| Jitter | 10 | 10 below 200 ms, 7 below 500, 4 below 1,000, otherwise 0 |
+
+Blocked paths receive zero latency and stability points.
+
+## Bandwidth adjustments
+
+Download and upload are scored independently.
+
+| Rating | Points per direction |
+| --- | ---: |
+| `FAST` | +5 |
+| `ADEQUATE` | +3 |
+| `SLOW` | -5 |
+| Unavailable or skipped | 0 |
+| Incomplete sample | an additional -2 |
+
+Download is `FAST` from 25 Mbps and `ADEQUATE` from 5 Mbps. Upload is `FAST`
+from 10 Mbps and `ADEQUATE` from 2 Mbps. These thresholds represent common
+development work, not general-purpose ISP quality.
+
+## Caps and labels
+
+Service health limits the final score so bandwidth cannot conceal an unhealthy
+AI route:
+
+- usable `FAIR` paths are capped at 89;
+- temporarily unavailable `FAIR` and `POOR` paths are capped at 64;
+- `BLOCKED` paths are capped at 0.
+
+| Score | Label |
+| ---: | --- |
+| 90–100 | `COMFORTABLE` |
+| 75–89 | `GOOD` |
+| 65–74 | `USABLE` |
+| 0–64 | `LIMITED` |
+
+Run `ipcheck --explain-score` to see every component. JSON reports expose the
+same calculation under `developer_readiness.score_breakdown`.
diff --git a/docs/scoring.zh-CN.md b/docs/scoring.zh-CN.md
new file mode 100644
index 0000000..9d91836
--- /dev/null
+++ b/docs/scoring.zh-CN.md
@@ -0,0 +1,69 @@
+# 评分与结论规则
+
+[中文 README](../README.zh-CN.md) · [English](scoring.md)
+
+`ipcheck` 同时给出两类信号:
+
+- 服务结论:`GOOD`、`FAIR`、`POOR` 或 `BLOCKED`;
+- 使用 `rule_v2` 计算的 0–100 开发适配分。
+
+分数是透明的启发式规则,不代表用户百分位,也不评价模型智能水平。
+
+## 服务结论
+
+| 结果 | 默认规则 |
+| --- | --- |
+| `GOOD` | 主路径 100% 成功,TTFB 中位数低于 800 ms,抖动低于 1,000 ms |
+| `FAIR` | 网络可达且中位 TTFB 低于 3,000 ms,或出现可恢复失败、限流、服务器异常 |
+| `POOR` | 主路径成功率低于 60%、中位 TTFB 至少 3,000 ms,或配置的 API 路径返回 404 |
+| `BLOCKED` | 主路径没有 HTTP 响应,或代理先返回 HTTP 407 |
+
+检测多个客户端时,各客户端保持独立结论:全部阻断时总体为 `BLOCKED`;阻断
+与可达客户端并存时总体为 `POOR`;否则采用最弱客户端的结论。开发适配分始终
+使用得分最低的服务链路,因此健康的 Claude 链路不会掩盖异常的 Codex 链路。
+
+## 规则 v2 的服务链路分
+
+选中的最弱服务链路最多贡献 90 分。
+
+| 项目 | 最高分 | 规则 |
+| --- | ---: | --- |
+| 可达性 | 35 | 成功率 100% 得 35,60–99% 得 22,大于 0% 得 10,否则 0 |
+| TTFB 中位数 | 35 | 低于 800 ms 得 35,低于 1,500 得 30,低于 3,000 得 22,低于 5,000 得 14,否则 6 |
+| TTFB P95 | 10 | 低于 2,000 ms 得 10,低于 4,000 得 7,低于 6,000 得 4,否则 0 |
+| 抖动 | 10 | 低于 200 ms 得 10,低于 500 得 7,低于 1,000 得 4,否则 0 |
+
+阻断链路不会获得延迟和稳定性分。
+
+## 带宽调整
+
+下载和上传分别计分。
+
+| 评级 | 每个方向的分数 |
+| --- | ---: |
+| 快 | +5 |
+| 够用 | +3 |
+| 慢 | -5 |
+| 不可用或跳过 | 0 |
+| 未完成样本 | 额外 -2 |
+
+下载达到 25 Mbps 为“快”,达到 5 Mbps 为“够用”;上传达到 10 Mbps 为“快”,
+达到 2 Mbps 为“够用”。这些阈值面向日常开发,不是通用的运营商网络评级。
+
+## 结论限幅与标签
+
+服务健康度会限制最终得分,避免带宽掩盖异常的 AI 服务路径:
+
+- 可用的 `FAIR` 链路最高 89 分;
+- 临时不可用的 `FAIR` 和 `POOR` 链路最高 64 分;
+- `BLOCKED` 链路为 0 分。
+
+| 分数 | 标签 |
+| ---: | --- |
+| 90–100 | 舒适 |
+| 75–89 | 良好 |
+| 65–74 | 可用 |
+| 0–64 | 受限 |
+
+运行 `ipcheck --explain-score` 可以查看每个项目。JSON 会在
+`developer_readiness.score_breakdown` 中提供相同的计算数据。
diff --git a/package.json b/package.json
index 8239e15..3b65407 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@jacklv-coder/ipcheck",
"private": true,
- "version": "0.8.0",
+ "version": "0.8.1",
"description": "Diagnose proxy, latency, and API routes used by Codex and Claude Code.",
"license": "MIT",
"author": "Jintao",
diff --git a/test/smoke.sh b/test/smoke.sh
index 3e2da06..2b8ee28 100755
--- a/test/smoke.sh
+++ b/test/smoke.sh
@@ -199,7 +199,7 @@ run_ipcheck_direct() {
bash -n "$PROJECT_DIR/bin/ipcheck"
"$PROJECT_DIR/bin/ipcheck" --help | grep -q '^ipcheck - diagnose Codex and Claude Code'
-[ "$("$PROJECT_DIR/bin/ipcheck" --version)" = "ipcheck 0.8.0" ]
+[ "$("$PROJECT_DIR/bin/ipcheck" --version)" = "ipcheck 0.8.1" ]
"$PROJECT_DIR/bin/ipcheck" --help | grep -q -- '--explain-score'
: > "$CURL_LOG"