Skip to content

Commit 660949e

Browse files
committed
fix(runtime): Node 18 支持结束(engines >=20)、exe 加载期崩溃、打包器换到 @yao-pkg/pkg
起因是 4.19.0 新增的 smoke-node18 作业首跑就红。 1. engines >=18.17 → >=20。fastify 5 用了 Node 20 才有的 diagnostics_channel.tracingChannel,所以自 dependabot 把 fastify 从 4.29.1 升到 5.12.3 起,Node 18 就已经起不来了——主矩阵跑在 20/22 上,engines 只是声明、npm 默认不拦,声明的下限从来没有人执行过。该作业本身是对的,不要为了让它变绿而放宽它。 2. 打包后的 exe 此前在加载期就崩(src/utils/version.ts)。CJS 打包下 import.meta.url 是空字符串,new URL('../../package.json', '') 抛 ERR_INVALID_URL,而抛出点在 "为坏候选兜底"的 try 之外(它在数组字面量求值时发生),于是模块加载失败、进程在起 监听之前就死了。改为单独包住这一句、降级到兜底路径。 3. 打包器 pkg → @yao-pkg/pkg(6.x),目标 node18-win-x64 → node22-win-x64。原来那条链 是被 vercel/pkg 的天花板钉住的:它已停止维护、最高只支持 node18,engines 与 target 都是它的产物,不是独立的产品决策。换到维护中的 fork 后 exe 内嵌 node22。 顺带:smoke-node18 → smoke-node20,改为验证 engines 下限本身(能起在 20 上就蕴含能起 在 22 上);README 的 node badge 与 build:win 说明同步;package-lock 跟着更新。 安全:曾评估"回退 fastify 到 4.x 以保住 Node 18"——技术上可行(389 项全绿、零代码 改动),但 4.29.1 是 4.x 终点、不会再有安全补丁,npm audit 会常驻 fastify 与 find-my-way 两个 high,故不采用。本版 audit 清零(原 2 高危 + 1 中危)。 验证:389 项全绿,tsc --noEmit / eslint 0 错误;真实 Node 20.20.2 上跑 /health 与 仪表盘 HTML 两个断言均通过。exe 的静态资源仍未生效,属既存缺陷,见 CHANGELOG 的「已知问题」,未在本次修复。
1 parent e59d7da commit 660949e

6 files changed

Lines changed: 687 additions & 581 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
# 下限是 22.12,CI 一直把测试 Runner 跑在它自己声明的支持范围之下——
2323
# engines 只是建议、npm 默认不拦,所以现在是"恰好能跑",
2424
# vitest 补丁版一旦用到 20 没有的 API 就会莫名其妙炸 CI。
25-
# 真正交付给用户的 Node 18 由下面的 smoke job 覆盖(那里跑不了 vitest)。
25+
# 真正交付给用户的 Node ≥20 下限由下面的 smoke job 覆盖(那里跑不了 vitest)。
2626
- uses: actions/setup-node@v7
2727
with:
2828
node-version: '22'
@@ -51,8 +51,8 @@ jobs:
5151
if: matrix.os == 'ubuntu-latest'
5252
run: npx vitest run --coverage --coverage.reporter=text-summary --coverage.thresholds.statements=65
5353

54-
# 把构建产物交给 smoke job 用,省掉在 Node 18 上装整套 devDependencies
55-
# (vitest/eslint 在 18 上根本装不干净)。
54+
# 把构建产物交给 smoke job 用,省掉在 Node 20 上装整套 devDependencies
55+
# (vitest@5 在 20 上根本装不干净)。
5656
- name: Upload dist
5757
if: matrix.os == 'ubuntu-latest'
5858
uses: actions/upload-artifact@v4
@@ -61,10 +61,12 @@ jobs:
6161
path: dist/
6262
retention-days: 1
6363

64-
# 交付运行时冒烟:pkg 打的是 node18-win-x64、package.json 声明 engines >=18.17,
65-
# 而主 job 从不执行那个运行时 —— 声明的支持面里唯一没人验证过的就是它。
66-
# 这里不跑测试框架,只起真实进程打两个请求:/health 与仪表盘 HTML。
67-
smoke-node18:
64+
# 交付运行时冒烟:package.json 声明 engines >=20,而主 job 跑在 22 上
65+
# (vitest@5 要求 >=22.12),于是**声明的支持面下限没有任何人验证过**。
66+
# 这里验的就是那个下限:能起得来,说明 >=20 这句话是真的,也顺带覆盖了
67+
# pkg 打包进去的 node22 运行时(22 只会比 20 更宽)。
68+
# 不跑测试框架,只起真实进程打两个请求:/health 与仪表盘 HTML。
69+
smoke-node20:
6870
needs: test
6971
strategy:
7072
fail-fast: false
@@ -76,9 +78,10 @@ jobs:
7678

7779
- uses: actions/setup-node@v7
7880
with:
79-
node-version: '18.17'
81+
node-version: '20'
8082

81-
# 只装生产依赖:vitest/eslint 在 18 上装不干净,而这里也不需要它们
83+
# 只装生产依赖:这里验的是运行时能起来,不需要 vitest/eslint
84+
# (vitest@5 在 20 上也装不干净,而本 job 本来也不跑它)
8285
- name: Install production dependencies only
8386
run: npm ci --omit=dev
8487

@@ -116,4 +119,4 @@ jobs:
116119
# 仪表盘 HTML 必须发得出来(public/ 是 pkg assets,也是运行时依赖)
117120
curl -fsS --max-time 5 -o "$DIR/index.html" http://127.0.0.1:9099/
118121
grep -qi "<title" "$DIR/index.html"
119-
echo "Node 18 smoke OK: $(node -v)"
122+
echo "Node 20 smoke OK: $(node -v)"

‎CHANGELOG.md‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
所有主要版本更新都记录在此文件。
44

5+
## [4.19.1] - 2026-09-24
6+
7+
### 修复
8+
- **`engines` 从 `>=18.17` 抬到 `>=20`,Node 18 支持结束**。4.19.0 新增的 `smoke-node18` 作业**首跑就红了**:`TypeError: diagnostics.tracingChannel is not a function`——fastify 5 用到的 `diagnostics_channel.tracingChannel` 是 Node 20 才有的 API。
9+
这**不是新引入的缺陷**,而是"声明的支持面下限从来没人执行过":主矩阵跑在 20/22 上,`engines` 只是声明、npm 默认不拦,所以自 dependabot 把 fastify 从 4.29.1 升到 5.12.3 起,Node 18 就已经起不来了,直到这个作业出现才被发现。**该作业本身是对的,不要为了让它变绿而放宽它。**
10+
- **打包后的 exe 此前在加载期就崩,根本起不来**(`src/utils/version.ts`)。CJS 打包下 `import.meta.url` 是空字符串,`new URL('../../package.json', '')` 抛 `ERR_INVALID_URL`——而抛出点在"为坏候选兜底"的那个 `try` **之外**(它在数组字面量求值时发生,不是读取文件时),于是模块加载失败、进程在起监听之前就死了。改为单独包住这一句、让它降级到下面的兜底路径。
11+
实测:修复前 exe 退出码 1、无监听;修复后 exe 正常启动(banner 报 4.19.1、`/health` 200、`netstat` 确认端口归属 `commandcode-proxy-v4.exe`)。
12+
13+
### 构建
14+
- **打包器 `pkg` → `@yao-pkg/pkg`(6.x),目标 `node18-win-x64` → `node22-win-x64`**。原来那条链是被 vercel/pkg 的天花板钉住的:pkg 已停止维护、最高只支持 node18,`engines >=18.17` 与 `--target node18-win-x64` 都是它的产物,**不是独立的产品决策**。换到维护中的 fork 后 exe 内嵌 node22,"零依赖 exe"这项能力不变(静态资源另有问题,见文末「已知问题」)。
15+
注意:打包需要 Node ≥22 来运行 `@yao-pkg/pkg` 自身(其预编译二进制由 `@yao-pkg/pkg-fetch` 提供,覆盖 node22/24/26)。
16+
17+
### CI
18+
- **`smoke-node18` → `smoke-node20`**,改为验证 `engines` 的**下限本身**。选下限而不是 22:能起在 20 上就蕴含能起在 22 上,一个作业同时覆盖"声明的下限是真的"和"pkg 内嵌的 node22 跑得起来"。
19+
20+
### 安全
21+
- **`npm audit` 清零(此前 2 高危 + 1 中危)**。曾评估"把 fastify 回退到 4.x 以保住 Node 18"这条路线:技术上可行(`^4.29.1` 下 typecheck/lint/build 0 错误、389 项全绿、零代码改动),但 4.29.1 是 4.x 终点、不会再有安全补丁,`npm audit` 会常驻 fastify 与 find-my-way 两个 **high**(唯一修复是升到 `fastify@5.12.5` 这个 semver-major),**故不采用**——不值得为一个已 EOL 的运行时长期背一组修不掉的公告。顺带把 vercel/pkg 那条 `no fix` 的 moderate 也一并去掉了。
22+
23+
### 测试
24+
- 全量 **389 项通过**,`tsc --noEmit`、`eslint .` 无错误。
25+
- 交付运行时冒烟在**真实 Node 20.20.2** 上实测通过(跑的是 `node dist/index.js`,即 CI 里那个作业的形态):`/health` 返回 `{"status":"ok","version":"4.19.1"}`,`/` 返回 99,986 字节、含 `<title>` 的仪表盘 HTML。
26+
27+
### 已知问题
28+
- **打包后的 exe 仍不能提供仪表盘**:exe 起得来、`/health` 与 `/v1/*` 正常,但 `GET /` 返回 500 `Dashboard assets missing: public/index.html not found.`。
29+
根因是**静态资源的落点与代码读取的路径不一致**,且本次没能定位到正确落点,故不写成"已修复"。实测数据:exe 内 `__dirname` = `C:\snapshot\dist`,而 `C:\snapshot\public\index.html` 不存在(构建日志里连 `Cannot stat` 警告都没有——它既不报缺,也没落在预期位置)。
30+
临时办法:把仓库的 `public/` 目录放在 exe 同级再运行(那是代码的第一候选路径)。定位 pkg 的资源落点需要单独一轮,届时再决定是修正 assets 路径还是改成构建期内嵌。
31+
> 这条与本次改动无关,是**既存缺陷**:修复前 exe 连启动都到不了,所以从未暴露过。
32+
533
## [4.19.0] - 2026-09-23
634

735
架构审查(批次 B)修复。取**次版本号**而非修订号:含四处行为变更(下方标 ⚠️)与一处内部契约变更(`saveConfigFile` 的返回值),不是单纯补丁。

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
[![version](https://img.shields.io/github/package-json/v/wjf1/commandcode-proxy?style=flat-square&color=6366f1&label=v4)](https://github.com/wjf1/commandcode-proxy/blob/main/package.json)
66
[![CI](https://img.shields.io/github/actions/workflow/status/wjf1/commandcode-proxy/ci.yml?branch=main&style=flat-square&label=CI)](https://github.com/wjf1/commandcode-proxy/actions/workflows/ci.yml)
7-
[![node](https://img.shields.io/badge/node-%E2%89%A518.17-339933?style=flat-square)](./package.json)
7+
[![node](https://img.shields.io/badge/node-%E2%89%A520-339933?style=flat-square)](./package.json)
88
[![license](https://img.shields.io/badge/license-MIT-94a3b8?style=flat-square)](./LICENSE)
99

1010
**本地部署的 OpenAI / Anthropic 双协议网关,透明代理 CommandCode AI**
@@ -113,7 +113,7 @@ npm run build && npm start
113113
或独立二进制:
114114

115115
```bash
116-
npm run build:win # dist/commandcode-proxy-v4.exe —— 零依赖运行
116+
npm run build:win # dist/commandcode-proxy-v4.exe —— 零依赖运行(打包需 Node ≥22)
117117
```
118118

119119
> [!TIP]

0 commit comments

Comments
 (0)