diff --git a/.github/workflows/star-history.yml b/.github/workflows/star-history.yml new file mode 100644 index 00000000..bce7a8b3 --- /dev/null +++ b/.github/workflows/star-history.yml @@ -0,0 +1,97 @@ +# After GitHub restricted public stargazers API (2026-06-30), live api.star-history.com +# embeds need a collaborator token. Official guidance for README: +# render with a token that can read THIS repo, do not put a raw PAT in the public file. +# +# This workflow uses the repo GITHUB_TOKEN + star-history's chart renderer (via +# narayann7/star-history-action, which star-history maintainers point to when you +# prefer not to hand a fine-grained token to the public star-history API). +# Ref: https://github.com/marketplace/actions/star-history-action +# https://www.star-history.com/blog/github-stargazer-api-restriction + +name: Star History + +on: + schedule: + - cron: "0 */6 * * *" + watch: + types: [started] + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: star-history + cancel-in-progress: true + +jobs: + star-history: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Render star history chart + uses: narayann7/star-history-action@v1 + with: + repos: ${{ github.repository }} + token: ${{ github.token }} + type: Date + themes: light,dark + output-dir: assets/star-history + update-readme: true + readme: README.md + readme-format: picture + commit: false + + - name: Sync chart block into README.zh-CN.md + run: | + set -euo pipefail + python3 <<'PY' + from pathlib import Path + import re + + start = "" + end = "" + + def extract_block(text: str) -> str: + m = re.search( + re.escape(start) + r"(.*?)" + re.escape(end), + text, + flags=re.S, + ) + if not m: + raise SystemExit("star-history markers missing in README.md") + return m.group(0) + + def replace_block(text: str, block: str) -> str: + if start not in text or end not in text: + raise SystemExit("star-history markers missing in README.zh-CN.md") + return re.sub( + re.escape(start) + r".*?" + re.escape(end), + block, + text, + count=1, + flags=re.S, + ) + + en = Path("README.md").read_text(encoding="utf-8") + zh_path = Path("README.zh-CN.md") + zh = zh_path.read_text(encoding="utf-8") + block = extract_block(en) + zh_path.write_text(replace_block(zh, block), encoding="utf-8") + print("synced star-history block into README.zh-CN.md") + PY + + - name: Commit if changed + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add assets/star-history README.md README.zh-CN.md + if git diff --staged --quiet; then + echo "No star-history changes" + exit 0 + fi + git commit -m "chore(docs): refresh star history chart [skip ci]" + git push diff --git a/README.md b/README.md index 70d1e99b..ea7b65b8 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,11 @@ 🤖 MCP

+

+ Sponsored by + 华龙中转站 / Hualong Transfer Station +

+ --- ## Why GoNavi? @@ -440,12 +445,40 @@ fc-cache -fv --- +## 💖 Sponsors + +

+ + 华龙中转站 Hualong Transfer Station + +

+ +

+ 华龙中转站 · Hualong Transfer Station
+ AI API Gateway · multi-model routing · domestic high-speed endpoint +

+ +Special thanks to **[华龙中转站 (Hualong Transfer Station)](https://api-fast.hualong.online/)** for sponsoring GoNavi open-source development. + +| | | +|---|---| +| **What they are** | An **AI API Gateway** that unifies multi-model access behind an OpenAI-compatible surface — so you can ship LLM features without fighting provider fragmentation and network friction. | +| **What you get** | Multi-model routing · OpenAI-compatible API · **domestic high-speed direct endpoint** · practical for indie devs and open-source projects that need reliable LLM access | +| **Why we recommend them** | GoNavi is AI/MCP-first. Stable model access is core infrastructure for coding agents, schema-aware assistants, and day-to-day development. | + +**Looking for a solid LLM relay?** +👉 [https://api-fast.hualong.online/](https://api-fast.hualong.online/) — console, docs, and the domestic high-speed endpoint. + +> Hualong also sponsored a **shared 50B token pool** for GoNavi **co-maintainers**, used via **API Key** (not 50B per person). Rules and how to apply: [Issue #671](https://github.com/Syngnat/GoNavi/issues/671). + +--- + ## 🤝 Contributing Issues and PRs welcome. Branch from **`dev`**, PR against **`dev`**. - [CONTRIBUTING.md](CONTRIBUTING.md) -- Call for contributors: [Issue #671](https://github.com/Syngnat/GoNavi/issues/671) +- Call for contributors (incl. sponsored token pool): [Issue #671](https://github.com/Syngnat/GoNavi/issues/671) --- @@ -462,18 +495,21 @@ Issues and PRs welcome. Branch from **`dev`**, PR against **`dev`**. ## ⭐ Star History - + + - - - Star History Chart + + + Star History Chart + --- ## Links +- [华龙中转站 · Hualong Transfer Station](https://api-fast.hualong.online/) — AI API Gateway (GoNavi sponsor) - [linux.do](https://linux.do/) - [AIBook](https://aibook.ren/) diff --git a/README.zh-CN.md b/README.zh-CN.md index ec5cb370..5a7e08b5 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -39,6 +39,11 @@ 🤖 MCP

+

+ 赞助商 · + 华龙中转站 +

+ --- ## 为什么是 GoNavi? @@ -440,12 +445,40 @@ fc-cache -fv --- +## 💖 赞助商 + +

+ + 华龙中转站 + +

+ +

+ 华龙中转站 · Hualong Transfer Station
+ AI API Gateway · 多模型统一接入 · 国内直连高速端点 +

+ +特别感谢 **[华龙中转站](https://api-fast.hualong.online/)** 赞助 GoNavi 开源开发。 + +| | | +|---|---| +| **他们是谁** | **AI API 中转 / Gateway**:以 OpenAI 兼容接口统一多模型接入,帮你少踩「渠道碎片化 + 网络不稳定」的坑。 | +| **核心能力** | 多模型统一路由 · OpenAI 兼容 · **国内直连高速端点** · 适合个人开发者与开源项目快速接通 LLM | +| **为什么推荐** | GoNavi 自身是 AI / MCP 一等公民产品;稳定、好用的模型通道,是写代码、做 Agent、联调多 Provider 的基础设施。 | + +**需要靠谱的 LLM 中转?** +👉 [https://api-fast.hualong.online/](https://api-fast.hualong.online/) — 控制台、文档与国内高速端点。 + +> 华龙另向本仓库赞助了 **共享 500 亿(50B)token** 额度,供 GoNavi **共建者** 以 **API Key** 方式协作开发使用(非整库人均 50B)。申请与规则见共建入口:[Issue #671](https://github.com/Syngnat/GoNavi/issues/671)。 + +--- + ## 🤝 贡献 欢迎 Issue 与 PR。请从 **`dev`** 拉分支,并向 **`dev`** 提 PR。 - [CONTRIBUTING.zh-CN.md](CONTRIBUTING.zh-CN.md) -- 共建邀请:[Issue #671](https://github.com/Syngnat/GoNavi/issues/671) +- 共建邀请(含赞助 token 池说明):[Issue #671](https://github.com/Syngnat/GoNavi/issues/671) --- @@ -462,18 +495,21 @@ fc-cache -fv ## ⭐ Star History - + + - - - Star History Chart + + + Star History Chart + --- ## 友情链接 +- [华龙中转站](https://api-fast.hualong.online/) — AI API Gateway(GoNavi 赞助商) - [linux.do](https://linux.do/) - [AI全书](https://aibook.ren/) diff --git a/assets/sponsors/hualong-logo-readme.png b/assets/sponsors/hualong-logo-readme.png new file mode 100644 index 00000000..8761d193 Binary files /dev/null and b/assets/sponsors/hualong-logo-readme.png differ diff --git a/assets/sponsors/hualong-logo.png b/assets/sponsors/hualong-logo.png new file mode 100644 index 00000000..8761d193 Binary files /dev/null and b/assets/sponsors/hualong-logo.png differ diff --git a/assets/sponsors/hualong-mark.png b/assets/sponsors/hualong-mark.png new file mode 100644 index 00000000..8761d193 Binary files /dev/null and b/assets/sponsors/hualong-mark.png differ diff --git a/assets/star-history/star-history-dark.svg b/assets/star-history/star-history-dark.svg new file mode 100644 index 00000000..76e6ef84 --- /dev/null +++ b/assets/star-history/star-history-dark.svg @@ -0,0 +1,28 @@ + + + GoNavi Star History + + + + + + + + GoNavi · Star History + 1773 stars · updated 2026-07-23 + + 0 + + 443 + + 886 + + 1329 + + 1773 + + + 2026-02-02 + 2026-05-02 + 2026-07-23 + diff --git a/assets/star-history/star-history-light.svg b/assets/star-history/star-history-light.svg new file mode 100644 index 00000000..76e6ef84 --- /dev/null +++ b/assets/star-history/star-history-light.svg @@ -0,0 +1,28 @@ + + + GoNavi Star History + + + + + + + + GoNavi · Star History + 1773 stars · updated 2026-07-23 + + 0 + + 443 + + 886 + + 1329 + + 1773 + + + 2026-02-02 + 2026-05-02 + 2026-07-23 + diff --git a/tools/generate_star_history_svg.py b/tools/generate_star_history_svg.py new file mode 100644 index 00000000..36f1dcfe --- /dev/null +++ b/tools/generate_star_history_svg.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +"""Generate a static star-history SVG for README (no third-party token prompts).""" + +from __future__ import annotations + +import json +import subprocess +import sys +from collections import Counter +from datetime import datetime, timezone +from pathlib import Path + + +def gh_api(path: str, accept: str | None = None) -> object: + cmd = ["gh", "api", path] + if accept: + cmd.extend(["-H", f"Accept: {accept}"]) + r = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8") + if r.returncode != 0: + raise RuntimeError(r.stderr or r.stdout or f"gh api failed: {path}") + return json.loads(r.stdout or "null") + + +def fetch_star_days(repo: str) -> list[str]: + days: list[str] = [] + page = 1 + while page <= 80: + path = f"repos/{repo}/stargazers?per_page=100&page={page}" + batch = gh_api(path, accept="application/vnd.github.star+json") + if not isinstance(batch, list) or not batch: + break + for item in batch: + starred_at = item.get("starred_at") if isinstance(item, dict) else None + if starred_at: + days.append(str(starred_at)[:10]) + print(f"page {page}: +{len(batch)} total={len(days)}", flush=True) + if len(batch) < 100: + break + page += 1 + return days + + +def build_svg( + cum: list[tuple[str, int]], + title: str = "GoNavi · Star History", + generated_on: str | None = None, +) -> str: + width, height = 900, 360 + pad_l, pad_r, pad_t, pad_b = 56, 24, 36, 48 + plot_w = width - pad_l - pad_r + plot_h = height - pad_t - pad_b + n = len(cum) + max_y = max(y for _, y in cum) + min_y = 0 + stamp = generated_on or "" + + def xy(i: int, y: int) -> tuple[float, float]: + x = pad_l + (plot_w * i / max(1, n - 1)) + yy = pad_t + plot_h * (1 - (y - min_y) / max(1, max_y - min_y)) + return x, yy + + pts = [xy(i, y) for i, (_, y) in enumerate(cum)] + poly = " ".join(f"{x:.1f},{y:.1f}" for x, y in pts) + area = ( + f"M {pts[0][0]:.1f},{pad_t + plot_h:.1f} L " + + " ".join(f"{x:.1f},{y:.1f}" for x, y in pts) + + f" L {pts[-1][0]:.1f},{pad_t + plot_h:.1f} Z" + ) + + subtitle = f"{max_y} stars" + if stamp: + subtitle = f"{max_y} stars · updated {stamp}" + + parts: list[str] = [ + '', + f'', + " GoNavi Star History", + " ", + ' ', + ' ', + ' ', + " ", + " ", + ' ', + f' {title}', + f' ' + f"{subtitle}", + ] + + for k in range(5): + val = int(max_y * k / 4) + yy = pad_t + plot_h * (1 - k / 4) + parts.append( + f' ' + ) + parts.append( + f' {val}' + ) + + parts.append(f' ') + parts.append( + f' ' + ) + + for idx in (0, n // 2, n - 1): + day, y = cum[idx] + x, _ = xy(idx, y) + parts.append( + f' {day}' + ) + + parts.append("") + return "\n".join(parts) + "\n" + + +def main() -> int: + repo = "Syngnat/GoNavi" + out = Path(__file__).resolve().parents[1] / "assets" / "star-history.svg" + + days = fetch_star_days(repo) + if not days: + print("no star timestamps returned", file=sys.stderr) + return 1 + + by_day = Counter(days) + ordered = sorted(by_day) + cum: list[tuple[str, int]] = [] + total = 0 + for day in ordered: + total += by_day[day] + cum.append((day, total)) + + if len(cum) > 90: + step = max(1, len(cum) // 80) + sampled = cum[::step] + if sampled[-1] != cum[-1]: + sampled.append(cum[-1]) + cum = sampled + + today = datetime.now(timezone.utc).strftime("%Y-%m-%d") + svg = build_svg(cum, generated_on=today) + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(svg, encoding="utf-8") + print(f"wrote {out} ({out.stat().st_size} bytes, points={len(cum)}, max={cum[-1][1]})") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())