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. -![Animated ipcheck terminal demo](assets/ipcheck-demo.gif) +![Animated ipcheck terminal demo showing healthy and limited networks](assets/ipcheck-demo.gif) -```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,也不会产生模型调用费用。 -![ipcheck 中文终端动态演示](assets/ipcheck-demo-zh.gif) +![ipcheck 健康网络与受限网络动态演示](assets/ipcheck-demo-zh.gif) -```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"