fix(agent): ETIMEDOUT 给出「配代理」提示 —— 境内直连最高频失败形态无指引

真机(Windows 192.168.0.160)验证自更新出网时发现:
`connect ETIMEDOUT 20.205.243.166:443` 落到 describeNetError 的兜底分支,
只回一串 IP:端口超时 —— 而这正是境内直连 GitHub 最常见的形态,用户唯一
有效动作就是配代理,却完全没被告知。

修法:新增 ETIMEDOUT / EHOSTUNREACH / ENETUNREACH 分支,点明
「设置 NA_PROXY=<代理地址> 或配置系统代理后重试」。与 ECONNRESET 的
「代理或线路问题」保持区分(两类原因不同,单测钉死互不串味)。
真机复验:重新部署后同一命令已输出新提示。
This commit is contained in:
jkooe committed 2026-10-27 13:06:51 +08:00
1 parent 0b858bb343
commit c062d1cc8b
2 files changed
+26

No files matched your search

@@ -102,6 +102,12 @@ export function describeNetError(err: unknown, timeoutMs: number): string {
if (code === 'ENOTFOUND' || code === 'EAI_AGAIN' || code === 'ECONNREFUSED') {
return `${msg}(域名解析失败或目标拒绝连接;若配了代理,请核对代理地址与端口)`;
}
// 2026-10-27 真机补:境内直连 GitHub 的**最常见**失败形态就是 TCP 层 ETIMEDOUT
// (不是我们自己的 timeoutMs,也不是 ECONNRESET)。原先它落到下面兜底分支,
// 用户只看到一串 IP:443 超时,不知道该干什么 —— 而这时唯一有效的动作就是配代理。
if (code === 'ETIMEDOUT' || code === 'EHOSTUNREACH' || code === 'ENETUNREACH') {
return `${msg}(连接超时/网络不可达;若目标在境外或需要代理,请设置 NA_PROXY=<代理地址> 或配置系统代理后重试)`;
}
return msg;
}
@@ -85,3 +85,23 @@ test('错误分类:DNS/拒连单独一类,普通错误原样透出', () => {
const plain = new Error('boom');
assert.equal(describeNetError(plain, 1000), 'boom', '无法归类的错误原样透出,不编造原因');
});
test('describeNetError:TCP 层 ETIMEDOUT 必须给出「配代理」的可执行提示(2026-10-27 真机)', () => {
// 真机现场:境内 Windows 直连 github.com:443 → connect ETIMEDOUT 20.205.243.166:443。
// 这条最常见,原先落到兜底分支只回一串超时,用户不知道唯一有效动作是配代理。
const etimedout = Object.assign(new Error('connect ETIMEDOUT 20.205.243.166:443'), { code: 'ETIMEDOUT' });
const msg = describeNetError(etimedout, 20_000);
assert.match(msg, /连接超时\/网络不可达/);
assert.match(msg, /NA_PROXY/, '必须点明 NA_PROXY 这个具体开关');
assert.match(msg, /系统代理/);
// 同族错误走同一提示
for (const code of ['EHOSTUNREACH', 'ENETUNREACH']) {
assert.match(describeNetError(Object.assign(new Error('x'), { code }), 1000), /NA_PROXY/, code);
}
// 不得与 ECONNRESET 的「代理或线路问题」混为一谈(两类原因不同)
const reset = Object.assign(new Error('read ECONNRESET'), { code: 'ECONNRESET' });
assert.match(describeNetError(reset, 1000), /代理或线路/);
assert.doesNotMatch(describeNetError(reset, 1000), /NA_PROXY/);
});