Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ body:
id: version
attributes:
label: ipcheck version
placeholder: ipcheck 0.6.1
placeholder: ipcheck 0.7.0
validations:
required: true
- type: dropdown
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
All notable changes to ipcheck are documented here. The project follows
[Semantic Versioning](https://semver.org/).

## [0.7.0] - 2026-07-22

### Added

- `--explain-score` to show the service-path base score and the separate
download/upload adjustments in human and Markdown reports.
- Machine-readable `developer_readiness.score_breakdown` details in JSON.
- A reproducible animated terminal demo and ready-to-post launch copy.

## [0.6.1] - 2026-07-22

### Changed
Expand Down Expand Up @@ -87,6 +96,7 @@ 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`.

[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
[0.6.0]: https://github.com/jacklv-coder/ipcheck/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/jacklv-coder/ipcheck/compare/v0.4.0...v0.5.0
Expand Down
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ time-to-first-byte (TTFB), jitter, reference bandwidth, and a clear
color-coded progress, can be cancelled cleanly with `Ctrl+C`, and finish with
a direct answer plus a rule-based readiness score.

![Animated ipcheck terminal demo](assets/ipcheck-demo.gif)

```text
$ ipcheck
ipcheck v0.6.1 — AI coding network diagnostics
ipcheck v0.7.0 — AI coding network diagnostics

Developer verdict
Ready to code? YES
Expand Down Expand Up @@ -74,7 +76,7 @@ brew install ipcheck

```bash
mkdir -p "$HOME/.local/bin"
curl -fsSL https://raw.githubusercontent.com/jacklv-coder/ipcheck/v0.6.1/bin/ipcheck \
curl -fsSL https://raw.githubusercontent.com/jacklv-coder/ipcheck/v0.7.0/bin/ipcheck \
-o "$HOME/.local/bin/ipcheck"
chmod +x "$HOME/.local/bin/ipcheck"
```
Expand Down Expand Up @@ -106,12 +108,19 @@ ipcheck --json > ipcheck-report.json
ipcheck --markdown > ipcheck-report.md
```

Explain every score component:

```bash
ipcheck --explain-score
```

Tune or override a check:

```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
Expand Down Expand Up @@ -148,7 +157,8 @@ percentiles. Rule v1 starts from the service-path verdict (`90` healthy, `70`
usable but delayed, `45` reachable but temporarily unavailable, `35` poor, or
`0` blocked). Completed bandwidth ratings can then add up to 5 points or deduct
up to 10; partial samples deduct 2 points each. JSON exposes `score`,
`score_label`, and `score_method` for automation.
`score_label`, `score_method`, and the per-component `score_breakdown` for
automation.

Run `ipcheck --help` for the complete option and environment-variable list.

Expand Down
16 changes: 13 additions & 3 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
实时进度,支持通过 `Ctrl+C` 友好取消,并直接告诉你“现在是否适合开发”以及
规则化的开发适配分。

![ipcheck 终端动态演示](assets/ipcheck-demo.gif)

```text
$ ipcheck
ipcheck v0.6.1 — AI 编程网络诊断
ipcheck v0.7.0 — AI 编程网络诊断

开发建议
现在适合开发吗?适合
Expand Down Expand Up @@ -68,7 +70,7 @@ brew install ipcheck

```bash
mkdir -p "$HOME/.local/bin"
curl -fsSL https://raw.githubusercontent.com/jacklv-coder/ipcheck/v0.6.1/bin/ipcheck \
curl -fsSL https://raw.githubusercontent.com/jacklv-coder/ipcheck/v0.7.0/bin/ipcheck \
-o "$HOME/.local/bin/ipcheck"
chmod +x "$HOME/.local/bin/ipcheck"
```
Expand Down Expand Up @@ -98,12 +100,19 @@ ipcheck --json > ipcheck-report.json
ipcheck --markdown > ipcheck-report.md
```

查看每一项评分依据:

```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
Expand Down Expand Up @@ -132,7 +141,8 @@ ipcheck --no-upload
开发适配分是透明的规则评分,不代表“超过多少用户”的百分位。规则 v1 根据
服务链路结论从 90(健康)、70(可用但偏慢)、45(可达但服务暂不可用)、
35(较差)或 0(阻断)起算;完整的上下行测速最多加 5 分或扣 10 分,部分
样本每项扣 2 分。JSON 会输出 `score`、`score_label` 和 `score_method`。
样本每项扣 2 分。JSON 会输出 `score`、`score_label`、`score_method`,以及逐项的
`score_breakdown`。

运行 `ipcheck --help` 可以查看完整参数。

Expand Down
Binary file added assets/ipcheck-demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 50 additions & 13 deletions bin/ipcheck
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ unset LC_ALL
LC_NUMERIC=C
export LC_NUMERIC

VERSION="0.6.1"
VERSION="0.7.0"
SCHEMA_VERSION=1
SAMPLES=3
TIMEOUT=15
Expand All @@ -27,6 +27,7 @@ RUN_SYSTEM=0
COLOR_MODE="auto"
PROGRESS_MODE="${IPCHECK_PROGRESS:-auto}"
LANG_MODE="${IPCHECK_LANG:-auto}"
EXPLAIN_SCORE=0

usage() {
cat <<'EOF'
Expand All @@ -51,6 +52,7 @@ Options:
--progress Always emit human-readable progress to stderr.
--no-progress Disable progress output.
--lang LANG Output language: auto, en, or zh (default: auto).
--explain-score Show how the readiness score was calculated.
-v, --version Show the ipcheck version.
-h, --help Show this help.

Expand Down Expand Up @@ -151,6 +153,7 @@ while [ "$#" -gt 0 ]; do
LANG_MODE="$2"
shift
;;
--explain-score) EXPLAIN_SCORE=1 ;;
-v|--version) printf 'ipcheck %s\n' "$VERSION"; exit 0 ;;
-h|--help) usage; exit 0 ;;
*) die "unknown option: $1 (use --help)" ;;
Expand Down Expand Up @@ -1276,24 +1279,28 @@ esac
# A transparent heuristic, not a user percentile. Service-path quality carries
# most of the score; completed bandwidth samples can move it by at most 10.
case "$overall" in
good) readiness_score=90 ;;
good) readiness_score_base=90 ;;
fair)
if [ "$usable_services" -gt 0 ]; then readiness_score=70; else readiness_score=45; fi
if [ "$usable_services" -gt 0 ]; then readiness_score_base=70; else readiness_score_base=45; fi
;;
poor) readiness_score=35 ;;
*) readiness_score=0 ;;
poor) readiness_score_base=35 ;;
*) readiness_score_base=0 ;;
esac
download_score_adjustment=0
upload_score_adjustment=0
readiness_score="$readiness_score_base"
if [ "$readiness_score" -gt 0 ]; then
case "$download_rating" in
fast) readiness_score=$((readiness_score + 2)) ;;
slow) readiness_score=$((readiness_score - 5)) ;;
partial) readiness_score=$((readiness_score - 2)) ;;
fast) download_score_adjustment=2 ;;
slow) download_score_adjustment=-5 ;;
partial) download_score_adjustment=-2 ;;
esac
case "$upload_rating" in
fast) readiness_score=$((readiness_score + 3)) ;;
slow) readiness_score=$((readiness_score - 5)) ;;
partial) readiness_score=$((readiness_score - 2)) ;;
fast) upload_score_adjustment=3 ;;
slow) upload_score_adjustment=-5 ;;
partial) upload_score_adjustment=-2 ;;
esac
readiness_score=$((readiness_score + download_score_adjustment + upload_score_adjustment))
fi
[ "$readiness_score" -ge 0 ] || readiness_score=0
[ "$readiness_score" -le 100 ] || readiness_score=100
Expand Down Expand Up @@ -1336,19 +1343,39 @@ service_display() {
awk -F '\t' -v service="$1" '$1 == service { print $2; exit }' "$SERVICES_FILE"
}

signed_score() {
if [ "$1" -ge 0 ]; then printf '+%s' "$1"; else printf '%s' "$1"; fi
}

render_score_breakdown() {
download_score_label=$(localized_bandwidth_rating "$download_rating")
upload_score_label=$(localized_bandwidth_rating "$upload_rating")
if [ "$UI_LANG" = "zh" ]; then
printf ' 评分依据:服务链路 %s;下载 %s %s;上传 %s %s;总分 %s。\n' \
"$readiness_score_base" "$download_score_label" "$(signed_score "$download_score_adjustment")" \
"$upload_score_label" "$(signed_score "$upload_score_adjustment")" "$readiness_score"
else
printf ' Score breakdown: service path %s; download %s %s; upload %s %s; total %s.\n' \
"$readiness_score_base" "$download_score_label" "$(signed_score "$download_score_adjustment")" \
"$upload_score_label" "$(signed_score "$upload_score_adjustment")" "$readiness_score"
fi
}

render_human() {
if [ "$UI_LANG" = "zh" ]; then
printf '%sipcheck v%s — AI 编程网络诊断%s\n' "$C_BOLD$C_CYAN" "$VERSION" "$C_RESET"
printf '时间(UTC):%s\n网络路径:%s\n' "$timestamp" "$proxy_text"
printf '\n%s开发建议%s\n' "$C_BOLD" "$C_RESET"
printf ' 现在适合开发吗?%s%s%s\n' "$C_BOLD" "$readiness_answer" "$C_RESET"
printf ' 开发适配分:%s%s/100 · %s%s\n' "$C_BOLD" "$readiness_score" "$readiness_score_label" "$C_RESET"
[ "$EXPLAIN_SCORE" -eq 0 ] || render_score_breakdown
else
printf '%sipcheck v%s — AI coding network diagnostics%s\n' "$C_BOLD$C_CYAN" "$VERSION" "$C_RESET"
printf 'Time (UTC): %s\nNetwork path: %s\n' "$timestamp" "$proxy_text"
printf '\n%sDeveloper verdict%s\n' "$C_BOLD" "$C_RESET"
printf ' Ready to code? %s%s%s\n' "$C_BOLD" "$readiness_answer" "$C_RESET"
printf ' Readiness score: %s%s/100 · %s%s\n' "$C_BOLD" "$readiness_score" "$readiness_score_label" "$C_RESET"
[ "$EXPLAIN_SCORE" -eq 0 ] || render_score_breakdown
fi
case "$overall" in
good) verdict_color="$C_GREEN" ;;
Expand Down Expand Up @@ -1449,6 +1476,11 @@ render_markdown() {
printf -- '- 结果:**%s** — %s\n' "$(printf '%s' "$overall" | tr '[:lower:]' '[:upper:]')" "$localized_overall_reason"
printf -- '- 现在适合开发吗?**%s** — %s\n\n' "$readiness_answer" "$readiness_summary"
printf -- '- 开发适配分:**%s/100 · %s**(规则评分,不是用户百分位)\n\n' "$readiness_score" "$readiness_score_label"
if [ "$EXPLAIN_SCORE" -eq 1 ]; then
printf -- '- 评分依据:服务链路 %s;下载 %s %s;上传 %s %s;总分 %s。\n\n' \
"$readiness_score_base" "$(localized_bandwidth_rating "$download_rating")" "$(signed_score "$download_score_adjustment")" \
"$(localized_bandwidth_rating "$upload_rating")" "$(signed_score "$upload_score_adjustment")" "$readiness_score"
fi
printf '## 检测到的客户端\n\n'
printf '| 客户端 | 模型 | 路由 | 配置来源 | 结果 |\n|---|---|---|---|---|\n'
else
Expand All @@ -1458,6 +1490,11 @@ render_markdown() {
printf -- '- Result: **%s** — %s\n' "$(printf '%s' "$overall" | tr '[:lower:]' '[:upper:]')" "$localized_overall_reason"
printf -- '- Ready to code? **%s** — %s\n\n' "$readiness_answer" "$readiness_summary"
printf -- '- Readiness score: **%s/100 · %s** (rule-based, not a user percentile)\n\n' "$readiness_score" "$readiness_score_label"
if [ "$EXPLAIN_SCORE" -eq 1 ]; then
printf -- '- Score breakdown: service path %s; download %s %s; upload %s %s; total %s.\n\n' \
"$readiness_score_base" "$(localized_bandwidth_rating "$download_rating")" "$(signed_score "$download_score_adjustment")" \
"$(localized_bandwidth_rating "$upload_rating")" "$(signed_score "$upload_score_adjustment")" "$readiness_score"
fi
printf '## Detected clients\n\n'
printf '| Client | Model | Route | Source | Result |\n|---|---|---|---|---|\n'
fi
Expand Down Expand Up @@ -1534,9 +1571,9 @@ render_json() {
warnings_json="$warnings_json$separator\"$(json_escape "$warning")\""
separator=","
done < "$WARNINGS_FILE"
printf '{"schema_version":%s,"version":"%s","timestamp":"%s","proxy":"%s","result":"%s","reason":"%s","developer_readiness":{"ready":%s,"level":"%s","score":%s,"score_label":"%s","score_method":"rule_v1","summary":"%s","recommendation":"%s"},"reachable_endpoints":%s,"primary_success_rate_pct":%s,"primary_ttfb_median_ms":%s,"primary_jitter_ms":%s,"privacy":{"credentials_used":false,"billable_requests":false,"upload_payload":"zero-filled"},"warnings":[%s],"bandwidth":{"enabled":%s,"available":%s,"http_code":"%s","bytes":%s,"bytes_per_second":%s,"path":"%s","download":{"enabled":%s,"available":%s,"complete":%s,"http_code":"%s","bytes":%s,"bytes_per_second":%s,"mbps":%s,"rating":"%s"},"upload":{"enabled":%s,"available":%s,"complete":%s,"http_code":"%s","bytes":%s,"bytes_per_second":%s,"mbps":%s,"rating":"%s"}},"system":{"enabled":%s,"interface":"%s","base_rtt_ms":%s,"download_bits_per_second":%s,"upload_bits_per_second":%s,"proxy_state":"%s"},"services":[%s],"endpoints":[%s]}\n' \
printf '{"schema_version":%s,"version":"%s","timestamp":"%s","proxy":"%s","result":"%s","reason":"%s","developer_readiness":{"ready":%s,"level":"%s","score":%s,"score_label":"%s","score_method":"rule_v1","score_breakdown":{"service_path":%s,"download":%s,"upload":%s},"summary":"%s","recommendation":"%s"},"reachable_endpoints":%s,"primary_success_rate_pct":%s,"primary_ttfb_median_ms":%s,"primary_jitter_ms":%s,"privacy":{"credentials_used":false,"billable_requests":false,"upload_payload":"zero-filled"},"warnings":[%s],"bandwidth":{"enabled":%s,"available":%s,"http_code":"%s","bytes":%s,"bytes_per_second":%s,"path":"%s","download":{"enabled":%s,"available":%s,"complete":%s,"http_code":"%s","bytes":%s,"bytes_per_second":%s,"mbps":%s,"rating":"%s"},"upload":{"enabled":%s,"available":%s,"complete":%s,"http_code":"%s","bytes":%s,"bytes_per_second":%s,"mbps":%s,"rating":"%s"}},"system":{"enabled":%s,"interface":"%s","base_rtt_ms":%s,"download_bits_per_second":%s,"upload_bits_per_second":%s,"proxy_state":"%s"},"services":[%s],"endpoints":[%s]}\n' \
"$SCHEMA_VERSION" "$VERSION" "$timestamp" "$(json_escape "$proxy_text")" "$overall" "$(json_escape "$overall_reason")" \
"$readiness_ready" "$readiness_level" "$readiness_score" "$(json_escape "$readiness_score_label_en")" "$(json_escape "$readiness_summary_en")" "$(json_escape "$readiness_recommendation_en")" \
"$readiness_ready" "$readiness_level" "$readiness_score" "$(json_escape "$readiness_score_label_en")" "$readiness_score_base" "$download_score_adjustment" "$upload_score_adjustment" "$(json_escape "$readiness_summary_en")" "$(json_escape "$readiness_recommendation_en")" \
"$reachable_count" "$primary_success_rate" "$primary_ttfb" "$primary_jitter" "$warnings_json" \
"$([ "$RUN_BANDWIDTH" -eq 1 ] && printf true || printf false)" "$legacy_bandwidth_available" "$legacy_bandwidth_code" "$legacy_bandwidth_bytes" "$legacy_bandwidth_speed" "$(json_escape "$proxy_text")" \
"$([ "$RUN_BANDWIDTH" -eq 1 ] && printf true || printf false)" "$([ "$bandwidth_valid" -eq 1 ] && printf true || printf false)" "$bandwidth_complete" "$bandwidth_code" "$bandwidth_bytes" "$bandwidth_speed" "$download_mbps" "$download_rating" \
Expand Down
Loading