Skip to content

Commit 9826c0b

Browse files
“bingtao”Paperclip-Paperclip
andcommitted
feat: GitHub download helper and migrate service curl paths
- Add nlt-github-download.sh with resolve + _nlt_github_download_curl - Wire nlt-common, utils (gum), new-api, code-server, nlt-progress - Add nlt-download CLI, README, selftest; extend install_smoke - Document in README/spec/plan per board request to drop parallel curl Co-Authored-By: Paperclip <noreply@paperclip.ing> Made-with: Cursor
1 parent 77af713 commit 9826c0b

14 files changed

Lines changed: 449 additions & 11 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- **celery**:Celery 安装与 worker/beat/flower 启停、状态;默认 `~/opt/celery`
1111
- **utils**:安装 **gum**`~/opt/gum`)与可选 shell 别名(`ll` / `la` / `lla`)。
1212
- **github-net**:诊断并修复「网页能开但 `git clone` 失败」的常见 HTTPS/SSH 问题。
13+
- **download**`nlt-download` — 对 GitHub 族 HTTPS 下载 URL 做可选镜像/前缀改写后调用 `curl`;仓库内 **new-api / code-server / gum 安装路径** 等已统一经 `_nlt_github_download_curl` / `nlt-github-download.sh` 调用(见 `scripts/tools/download/README.md`)。
1314
- **paperclip**:从 **GitHub 克隆** [paperclipai/paperclip](https://github.com/paperclipai/paperclip) 源码、`pnpm install`,并以 **`pnpm paperclipai run`** 启停;默认安装根 `~/opt/paperclip`**默认工作区** **`~/opt/paperclip/workspace`**(环境变量 **`PAPERCLIP_WORKSPACE`**,可改)。`start` 会在实例就绪后尽量把上游 **`~/.paperclip/instances/<id>/workspaces`** 符号链接到该目录(若该 `workspaces` 已非空则跳过)。数据目录另见上游 `~/.paperclip/…`。无实例配置时 **`start` 会先非交互执行 `onboard --yes`**(依赖 `script(1)`);也可手动 **`nlt-paperclip onboard`**(或 `NONINTERACTIVE=1 nlt-paperclip onboard`)。
1415
- **code-server**:从 **GitHub Releases** 下载官方 **standalone** 压缩包并解压到 `~/opt/code-server``nohup` 后台运行,默认绑定 `127.0.0.1:8080`;无需本机 Node.js。
1516
- **new-api**:从 **GitHub Releases** 下载 [QuantumNous/new-api](https://github.com/QuantumNous/new-api) 的预编译二进制到 `~/opt/new-api/bin`;数据目录默认 `~/opt/new-api/data`(SQLite 等),默认 **HTTP 端口 3000**;解析版本时会跳过无附件的 nightly,fallback `v0.12.6`
@@ -98,6 +99,7 @@ bash tests/progress_smoke.sh
9899
| `nlt-celery` | `scripts/services/celery/setup.sh` 全量子命令;`install` / `update``start-worker` / `stop` / `status` 等;无参为菜单 |
99100
| `nlt-utils`(可接子参数,如 `gum``all`| `scripts/tools/utils/setup.sh`|
100101
| `nlt-github-net` | `scripts/tools/github-net/setup.sh`(无参 gum;可 `install` / `update` / `reinstall` / `uninstall`|
102+
| `nlt-download` | `scripts/tools/download/setup.sh``curl` / `resolve-url`;与 `scripts/lib/nlt-github-download.sh` 同源;无参 gum) |
101103
| `nlt-port-kill` | `scripts/tools/port-kill/setup.sh``kill` / `list`;可 `source … --lib` 调用 `nlt_kill_port`;无参 gum;`NONINTERACTIVE=1` 跳过确认) |
102104
| `nlt-services` | `scripts/services/nlt-services.sh`(无参 gum;`status``install` 先选安装或卸载;非交互:`install add <模块>` / `install remove <模块>``status --no-http`|
103105
| `nlt-paperclip` | `scripts/services/paperclip/setup.sh` 全量子命令;`install` / `onboard` / `start` 等;无参为 gum 菜单 |
@@ -120,6 +122,7 @@ nltdeploy/
120122
├── scripts/
121123
│ ├── lib/
122124
│ │ ├── nlt-common.sh # _nlt_ensure_gum 等公共片段(各 setup 脚本 source)
125+
│ │ ├── nlt-github-download.sh # GitHub 下载 URL 改写 + _nlt_github_download_curl(被 common / 各域复用)
123126
│ │ └── nlt-progress.sh # 可 source 的终端进度条与 curl 下载监视(可选)
124127
│ ├── tools/ # 工具 / 环境类(非长期服务进程)
125128
│ │ ├── pip-sources/
@@ -132,6 +135,9 @@ nltdeploy/
132135
│ │ │ └── setup.sh # gum / 别名 / all
133136
│ │ ├── github-net/
134137
│ │ │ └── setup.sh # Git 连通性诊断与修复
138+
│ │ ├── download/
139+
│ │ │ ├── setup.sh # nlt-download:GitHub 友好 curl 包装
140+
│ │ │ └── selftest.sh
135141
│ │ └── port-kill/
136142
│ │ └── setup.sh # 按端口查杀进程(可 source 复用)
137143
│ └── services/ # 常驻服务与聚合入口

docs/superpowers/plans/2026-04-15-war-24-github-download-tool.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,16 @@
5858
- [ ] **Step 1:** `shellcheck` 或仓库既有静态检查(若有)对新增文件清零关键告警。
5959
- [ ] **Step 2:** Git 提交信息末尾追加 `Co-Authored-By: Paperclip <noreply@paperclip.ing>`
6060
- [ ] **Step 3:** 在父工单 [WAR-24](/WAR/issues/WAR-24) 评论中简述实现要点并链接本计划文件(仓库路径即可)。
61+
62+
### Task 6: 迁移既有 GitHub 下载(董事会:见 [WAR-24](/WAR/issues/WAR-24) 评论)
63+
64+
**要求:** 项目中 **服务/工具脚本内** 对 GitHub 资源的下载一律经 `_nlt_github_download_curl`(或 `nlt-download curl`),删除直连 GitHub 的并行实现。
65+
66+
**Files(核对清单):**
67+
68+
- [x] `scripts/lib/nlt-common.sh``_nlt_ensure_gum``curl``_nlt_github_download_curl`(并 `source nlt-github-download.sh`
69+
- [x] `scripts/tools/utils/setup.sh` — gum Release / API 探测
70+
- [x] `scripts/services/new-api/setup.sh` — Releases API + 二进制下载
71+
- [x] `scripts/services/code-server/setup.sh` — latest API + tarball
72+
- [x] `scripts/lib/nlt-progress.sh``nlt_pb_curl_to_file` / HEAD 解析路径上的 GitHub URL
73+
- [x] `install.sh``nlt-download` 包装与 `libexec` 同步(若尚未合并则补齐)

docs/superpowers/specs/2026-04-15-war-24-github-download-tool-design.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
**成功标准:**
1515

1616
- 提供稳定 CLI:`nlt-download curl …`(透传剩余参数给 `curl`),对 **识别为 GitHub 族** 的 URL 在调用前做 **可选** 的 URL 变换。
17-
- 提供 Bash 库函数(供 `new-api``code-server``utils/gum` 等后续渐进接入):输入原始 URL 与输出路径意图,返回应使用的 URL(及可选的额外 `curl` 默认参数说明)。
17+
- 提供 Bash 库函数 **`_nlt_github_download_resolve_url`** / **`_nlt_github_download_curl`****`new-api``code-server``utils`gum)、`nlt-common`(拉 gum 安装脚本)、`nlt-progress`(GitHub 资源下载)** 等已统一接入,不再保留平行的「裸 `curl` 直链 GitHub」实现路径)。
1818
- **默认行为与当前一致**(不设环境变量时不改写 URL),避免破坏现有用户。
1919
- 标准 `curl` 代理环境变量(`HTTPS_PROXY` / `ALL_PROXY` 等)继续生效,不在此工具内重复发明代理协议栈。
2020

@@ -81,4 +81,15 @@
8181

8282
- 不自动探测「哪个镜像最快」、不做周期性健康检查服务。
8383
- 不替代 `git clone`(仍由 `install.sh``github-net` 等既有路径负责)。
84-
- 不在此迭代强制改写所有历史脚本中的 `curl`(可作为后续子工单)。
84+
85+
## 6. 仓库内迁移策略(董事会补充)
86+
87+
**本仓库内** 面向 **GitHub 资源(Release 资产、GitHub API、raw 等)****文件/JSON 下载**,须经 **`_nlt_github_download_curl`**(或等价的 `nlt-download curl`)路径,**不保留**并行的「脚本内手写直连 GitHub 的 `curl`」下载逻辑。
88+
**例外**:面向 **非 GitHub 主机**(如 PyPI、astral.sh、本机 `127.0.0.1` 健康检查)的 `curl` 不变;面向用户的 **管道/bootstrap 文档** 仍可展示裸 `curl`(用户尚未安装 nltdeploy 时)。
89+
90+
---
91+
92+
## 7. 历史记录
93+
94+
- 2026-04-15:初版规格。
95+
- 2026-04-15:按董事会评论增加 §6(强制迁移 GitHub 下载实现路径)。

install.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ do_install_or_update() {
300300
mkdir -p "${NLTDEPLOY_ROOT}/bin" "${LIBEXEC}" \
301301
"${NLTDEPLOY_ROOT}/share/nltdeploy" "${NLTDEPLOY_ROOT}/etc/nltdeploy"
302302
mkdir -p "${LIBEXEC}/pip-sources" "${LIBEXEC}/python-env" "${LIBEXEC}/port-kill" \
303+
"${LIBEXEC}/download" \
303304
"${LIBEXEC}/airflow" "${LIBEXEC}/celery" "${LIBEXEC}/utils" "${LIBEXEC}/github-net" \
304305
"${LIBEXEC}/paperclip" "${LIBEXEC}/code-server" "${LIBEXEC}/new-api" \
305306
"${LIBEXEC}/services" \
@@ -312,6 +313,9 @@ do_install_or_update() {
312313
_nlt_cp_first "${LIBEXEC}/lib/nlt-progress.sh" \
313314
"${SCRIPTS}/lib/nlt-progress.sh"
314315

316+
_nlt_cp_first "${LIBEXEC}/lib/nlt-github-download.sh" \
317+
"${SCRIPTS}/lib/nlt-github-download.sh"
318+
315319
_nlt_cp_first "${LIBEXEC}/pip-sources/setup.sh" \
316320
"${SCRIPTS}/tools/pip-sources/setup.sh" \
317321
"${SCRIPTS}/pip-sources/setup.sh"
@@ -341,6 +345,12 @@ do_install_or_update() {
341345
_nlt_cp_first "${LIBEXEC}/port-kill/setup.sh" \
342346
"${SCRIPTS}/tools/port-kill/setup.sh"
343347

348+
_nlt_cp_first "${LIBEXEC}/download/setup.sh" \
349+
"${SCRIPTS}/tools/download/setup.sh"
350+
351+
_nlt_cp_first "${LIBEXEC}/download/selftest.sh" \
352+
"${SCRIPTS}/tools/download/selftest.sh"
353+
344354
_nlt_cp_first "${LIBEXEC}/paperclip/setup.sh" \
345355
"${SCRIPTS}/services/paperclip/setup.sh" \
346356
"${SCRIPTS}/paperclip/setup.sh" \
@@ -366,6 +376,7 @@ do_install_or_update() {
366376
_emit_wrapper nlt-utils utils/setup.sh
367377
_emit_wrapper nlt-github-net github-net/setup.sh
368378
_emit_wrapper nlt-port-kill port-kill/setup.sh
379+
_emit_wrapper nlt-download download/setup.sh
369380
_emit_wrapper nlt-services services/nlt-services.sh
370381

371382
_emit_wrapper nlt-airflow airflow/setup.sh

scripts/lib/nlt-common.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
[[ -n "${_NLT_COMMON_LOADED:-}" ]] && return 0
55
_NLT_COMMON_LOADED=1
66

7+
_NLT_COMMON_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8+
# shellcheck source=nlt-github-download.sh
9+
source "${_NLT_COMMON_LIB_DIR}/nlt-github-download.sh"
10+
711
_nltdeploy_raw_base() {
812
printf '%s\n' "${NLTDEPLOY_RAW_BASE:-${nltdeploy_RAW_BASE:-https://raw.githubusercontent.com/farfarfun/nltdeploy/HEAD}}"
913
}
@@ -30,7 +34,7 @@ _nlt_ensure_gum() {
3034
local _url
3135
_url="$(_nlt_gum_utils_setup_url)"
3236
echo "未检测到 gum,执行: curl -LsSf ${_url} | bash -s -- gum" >&2
33-
curl -LsSf "${_url}" | bash -s -- gum || {
37+
_nlt_github_download_curl -LsSf "${_url}" | bash -s -- gum || {
3438
echo "错误: gum 安装失败(网络或 NLTDEPLOY_RAW_BASE / nltdeploy_RAW_BASE)。" >&2
3539
return 1
3640
}

scripts/lib/nlt-github-download.sh

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env bash
2+
# GitHub 族下载 URL 改写(供其它脚本 source)。默认不改写;由环境变量显式启用。
3+
# 参见 scripts/tools/download/README.md
4+
5+
[[ -n "${_NLT_GITHUB_DOWNLOAD_LIB_LOADED:-}" ]] && return 0
6+
_NLT_GITHUB_DOWNLOAD_LIB_LOADED=1
7+
8+
_nlt_gh_dl_lc() {
9+
printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
10+
}
11+
12+
# 参数:主机名(小写)
13+
_nlt_is_github_download_host() {
14+
case "$1" in
15+
github.com | www.github.com) return 0 ;;
16+
raw.githubusercontent.com) return 0 ;;
17+
api.github.com) return 0 ;;
18+
*) return 1 ;;
19+
esac
20+
}
21+
22+
# 将 https URL 解析为 host(小写)与路径部分(以 / 开头,无路径则为 /)
23+
_nlt_gh_dl_parse_https() {
24+
local url="$1" rest host_raw host path
25+
if [[ "$url" != https://* ]]; then
26+
return 1
27+
fi
28+
rest="${url#https://}"
29+
host_raw="${rest%%/*}"
30+
host="$(_nlt_gh_dl_lc "$host_raw")"
31+
path="${rest#"${host_raw}"}"
32+
[[ -z "$path" ]] && path="/"
33+
[[ "$path" != /* ]] && path="/${path}"
34+
printf '%s\t%s\n' "$host" "$path"
35+
}
36+
37+
# 输出一行:改写后的 URL(stdout)。发生改写时 stderr 打一行诊断。
38+
_nlt_github_download_resolve_url() {
39+
local url="$1"
40+
local mode hub_pre raw_base
41+
mode="${NLTDEPLOY_GITHUB_DOWNLOAD_MODE:-off}"
42+
hub_pre="${NLTDEPLOY_GITHUB_HUB_PROXY_PREFIX:-}"
43+
raw_base="${NLTDEPLOY_GITHUB_RAW_MIRROR_BASE:-}"
44+
45+
if [[ -z "$url" ]]; then
46+
printf '%s\n' "$url"
47+
return 0
48+
fi
49+
50+
if [[ "$url" != https://* ]]; then
51+
printf '%s\n' "$url"
52+
return 0
53+
fi
54+
55+
local host path parsed
56+
if ! parsed="$(_nlt_gh_dl_parse_https "$url")"; then
57+
printf '%s\n' "$url"
58+
return 0
59+
fi
60+
host="${parsed%%$'\t'*}"
61+
path="${parsed#*$'\t'}"
62+
63+
if ! _nlt_is_github_download_host "$host"; then
64+
printf '%s\n' "$url"
65+
return 0
66+
fi
67+
68+
# 优先级:hub 前缀 > mirror_raw > off(与设计文档一致)
69+
if [[ -n "$hub_pre" ]]; then
70+
if [[ "$url" == "${hub_pre}"* ]]; then
71+
printf '%s\n' "$url"
72+
return 0
73+
fi
74+
local out="${hub_pre}${url}"
75+
printf '%s\n' "[nlt-download] URL rewrite (hub proxy): ${url} -> ${out}" >&2
76+
printf '%s\n' "$out"
77+
return 0
78+
fi
79+
80+
if [[ "$mode" == "mirror_raw" ]] && [[ -n "$raw_base" ]]; then
81+
if [[ "$host" != "raw.githubusercontent.com" ]]; then
82+
printf '%s\n' "$url"
83+
return 0
84+
fi
85+
local new_url="${raw_base%/}${path}"
86+
if [[ "$new_url" != "$url" ]]; then
87+
printf '%s\n' "[nlt-download] URL rewrite (mirror_raw): ${url} -> ${new_url}" >&2
88+
fi
89+
printf '%s\n' "$new_url"
90+
return 0
91+
fi
92+
93+
if [[ "$mode" == "hub_proxy" ]] && [[ -z "$hub_pre" ]]; then
94+
printf '%s\n' "[nlt-download] NLTDEPLOY_GITHUB_DOWNLOAD_MODE=hub_proxy 但未设置 NLTDEPLOY_GITHUB_HUB_PROXY_PREFIX,跳过改写。" >&2
95+
fi
96+
97+
printf '%s\n' "$url"
98+
return 0
99+
}
100+
101+
# 与 nlt-download curl 子命令相同:扫描参数中 https:// 开头的 token 并改写后调用 curl(非 exec)。
102+
_nlt_github_download_curl() {
103+
command -v curl >/dev/null 2>&1 || {
104+
echo "错误: 需要 curl。" >&2
105+
return 127
106+
}
107+
local args=() a new
108+
for a in "$@"; do
109+
if [[ "$a" == https://* ]]; then
110+
new="$(_nlt_github_download_resolve_url "$a")"
111+
args+=("$new")
112+
else
113+
args+=("$a")
114+
fi
115+
done
116+
curl "${args[@]}"
117+
}

scripts/lib/nlt-progress.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
[[ -n "${_NLT_PROGRESS_LOADED:-}" ]] && return 0
44
_NLT_PROGRESS_LOADED=1
55

6+
_NLT_PROGRESS_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
# shellcheck source=nlt-github-download.sh
8+
source "${_NLT_PROGRESS_LIB_DIR}/nlt-github-download.sh"
9+
610
_nlt_file_size() {
711
local f="$1"
812
[[ -f "$f" ]] || { echo 0; return 0; }
@@ -53,6 +57,7 @@ _nlt_pb_fmt_hms() {
5357

5458
_nlt_pb_parse_content_length() {
5559
local url="$1"
60+
url="$(_nlt_github_download_resolve_url "$url")"
5661
curl -sI -L "$url" 2>/dev/null | awk 'BEGIN{IGNORECASE=1} /^content-length:/ {v=$2+0} END{print v+0}'
5762
}
5863

@@ -163,6 +168,8 @@ nlt_pb_curl_to_file() {
163168
[[ -n "$url" && -n "$dest" ]] || return 2
164169
command -v curl >/dev/null 2>&1 || return 127
165170

171+
url="$(_nlt_github_download_resolve_url "$url")"
172+
166173
if [[ -z "$total" || ! "$total" =~ ^[0-9]+$ ]]; then
167174
total="$(_nlt_pb_parse_content_length "$url")"
168175
fi

scripts/services/code-server/setup.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ _detect_platform() {
100100
_fetch_latest_version() {
101101
require_curl
102102
local out ver
103-
out="$(curl -fsSL "https://api.github.com/repos/coder/code-server/releases/latest")" || return 1
103+
out="$(_nlt_github_download_curl -fsSL "https://api.github.com/repos/coder/code-server/releases/latest")" || return 1
104104
ver="$(printf '%s' "$out" | sed -n 's/.*"tag_name": *"v\([0-9][0-9.]*\)".*/\1/p' | head -1)"
105105
if [[ -n "$ver" ]]; then
106106
echo "$ver"
@@ -134,7 +134,7 @@ _download_install() {
134134
echo " ${url}" >&2
135135
tmpdir="$(mktemp -d)"
136136
trap 'rm -rf "${tmpdir}"' RETURN
137-
curl -fsSL "$url" -o "${tmpdir}/code-server.tgz"
137+
_nlt_github_download_curl -fsSL "$url" -o "${tmpdir}/code-server.tgz"
138138
rm -rf "${CODE_SERVER_SERVICE_HOME}/lib" "${CODE_SERVER_SERVICE_HOME}/bin" 2>/dev/null || true
139139
mkdir -p "${CODE_SERVER_SERVICE_HOME}"
140140
tar -xzf "${tmpdir}/code-server.tgz" -C "${CODE_SERVER_SERVICE_HOME}" --strip-components=1

scripts/services/new-api/setup.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ _resolve_tag() {
175175
fi
176176
require_curl
177177
local json picked
178-
json="$(curl -fsSL "https://api.github.com/repos/${NEW_API_GITHUB_REPO}/releases?per_page=40")" || json=""
178+
json="$(_nlt_github_download_curl -fsSL "https://api.github.com/repos/${NEW_API_GITHUB_REPO}/releases?per_page=40")" || json=""
179179
if [[ -n "$json" ]]; then
180180
picked="$(_pick_tag_from_releases_json "$json" || true)"
181181
if [[ -n "$picked" ]]; then
@@ -196,7 +196,7 @@ _download_install() {
196196
echo " ${url}" >&2
197197
tmp="$(mktemp)"
198198
trap 'rm -f "${tmp}"' RETURN
199-
curl -fsSL "$url" -o "${tmp}"
199+
_nlt_github_download_curl -fsSL "$url" -o "${tmp}"
200200
mkdir -p "${NEW_API_SERVICE_HOME}/bin"
201201
install -m 0755 "${tmp}" "${NEW_API_BIN}"
202202
[[ -x "${NEW_API_BIN}" ]] || die "安装后二进制不可执行: ${NEW_API_BIN}"

scripts/tools/download/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# nlt-download(GitHub 友好下载)
2+
3+
## 作用
4+
5+
**GitHub 族 HTTPS URL**`github.com` / `raw.githubusercontent.com` / `api.github.com`)在调用 `curl` 前做 **可选** URL 改写;其它 URL 原样透传。与 **`scripts/lib/nlt-github-download.sh`** 同源:服务脚本应 **`source` 该库并调用 `_nlt_github_download_curl`**,或直接使用 **`nlt-download curl …`**
6+
7+
## 环境变量
8+
9+
| 变量 | 说明 |
10+
|------|------|
11+
| `NLTDEPLOY_GITHUB_HUB_PROXY_PREFIX` | 非空时优先:将完整原始 URL 拼在该前缀之后(类 ghproxy)。 |
12+
| `NLTDEPLOY_GITHUB_DOWNLOAD_MODE` | `off`(默认)\| `mirror_raw` \| `hub_proxy` |
13+
| `NLTDEPLOY_GITHUB_RAW_MIRROR_BASE` | `mirror_raw` 下替换 `raw.githubusercontent.com` 的前缀 |
14+
15+
未设置时行为与直连 `curl` 一致。
16+
17+
## 用法
18+
19+
```bash
20+
# 已安装 nltdeploy(PATH 含 ~/.local/nltdeploy/bin)
21+
nlt-download curl -fsSL "https://api.github.com/repos/OWNER/REPO/releases/latest"
22+
23+
# 仅看改写结果
24+
nlt-download resolve-url "https://raw.githubusercontent.com/foo/bar/HEAD/file.txt"
25+
```
26+
27+
## 自测
28+
29+
```bash
30+
NONINTERACTIVE=1 ./setup.sh install # 会跑 selftest.sh
31+
```
32+
33+
## 设计文档
34+
35+
仓库内:`docs/superpowers/specs/2026-04-15-war-24-github-download-tool-design.md`

0 commit comments

Comments
 (0)