回答:这台机器上可以同时有 Node 18 / 20 / 22,当前 shell 用哪一个?
典型命令:nvm install --lts、nvm use 22。
一句话:
nvm负责选出一份 Node;Node.js负责跑 JavaScript;npm负责从仓库把包装到这份 Node 上。现在多数 Agent CLI 走npm install -g,是因为它们本身就是 TypeScript 程序,而 npm 是把这类程序变成跨平台命令的最短路径。
它们不是并列的三个“差不多的工具”,而是三层职责。从上到下,一层管一层:
版本管理器。往本机装多份 Node,并用 PATH 决定“现在用哪一份”。不管包,也不跑业务代码。
JavaScript 运行时。提供 node 二进制、V8、libuv 和标准库。没有它,后面的 CLI 只是一堆跑不起来的 JS。
包管理器。随 Node 发行版一起到来:一边是命令行 npm,一边是公共仓库 registry。负责下载、安装、声明依赖。
真正敲进终端的命令:claude、gemini、codex。它们是 npm 包,入口脚本由当前这份 Node 执行。
读这张图时,最容易错的是把 npm 当成“Node 的替代品”,或把 nvm 当成“装 CLI 的工具”。nvm 从不装 claude;npm 也不提供 JavaScript 运行时。
回答:这台机器上可以同时有 Node 18 / 20 / 22,当前 shell 用哪一个?
典型命令:nvm install --lts、nvm use 22。
回答:一段 JS/TS 编译产物如何在浏览器之外执行?如何访问文件、网络、子进程?
典型命令:node app.js、node -v。
回答:这个程序的依赖从哪来、装到哪、哪个文件变成终端命令?
典型命令:npm install -g @scope/cli、npx some-tool。
用操作系统的语言重说一遍:
| 名字 | 更接近什么 | 它改的是什么 |
|---|---|---|
| nvm | update-alternatives / 多版本 SDK 切换器 |
PATH,让 node 指向某一份安装前缀 |
| Node.js | JVM、CPython、go 运行时 |
进程:解释/执行 JS,提供系统调用封装 |
| npm | pip、cargo、apt(但只服务 JS 生态) |
磁盘上的包目录,以及 bin/ 里的可执行入口 |
nvm 的同类还有 fnm、volta、asdf、Windows 上的 nvs;npm 的同类还有 yarn、pnpm、bun。换实现,不换这三层分工。
从图上看,数据是从左到右流过的:
bin 插到 PATH 最前
系统软件视角里,关键不在“装上了”,而在当前 PATH 指向哪一份前缀。下面五步都展开写,不依赖脚本切换。
一份发行版通常包括 bin/node、bin/npm、bin/npx,以及配套的 npm 自身。所以“装了 Node”几乎总是“同时得到一对 node + npm”。
默认前缀类似 ~/.nvm/versions/node/v22.18.0/。全局包以后也会进这个树,而不是进 /usr/local。
它把该版本的 bin 目录插到 PATH 最前面。之后 which node 和 which npm 必须落在同一棵目录树上,否则就是环境被污染了(系统包、Homebrew、nvm 各装了一份)。
新开终端能否自动切到同一版本,取决于你有没有 nvm alias default,以及 shell 启动脚本有没有加载 nvm。
全局安装不是装到操作系统,而是装到 npm prefix -g。用 nvm 时,这个前缀就是当前 Node 版本目录。包的 JS 落在 lib/node_modules/,可执行入口被链到 bin/。
package.json 里的 bin 字段决定命令名。例如把 claude 指到 cli.js 后,安装才会出现这个命令名。
全局 bin 里的文件通常第一行是 #!/usr/bin/env node。shell 按 PATH 找到它,内核按 shebang 再启动 node,把后面的 JS 跑起来。
所以 Agent CLI “看起来像原生二进制”,执行模型仍是:当前 PATH 上的 node + 一段 JS。Node 没了,或版本不对,命令名还在也会立刻坏。
nvm use 20 之后,PATH 指向 v20 的 bin。你在 v22 里装的 claude 不会自动出现。这不是丢包,是隔离:每份 Node 自带自己的 npm 和全局 CLI。
系统向的记法:全局 npm 包的生命周期绑在 Node 前缀上,不绑在用户账户上。 要跨版本复用,只能重装,或改用 Volta 这类按项目锁定工具链的方案。
系统包管理器、官方安装包、Homebrew、nvm 各装了一份 Node。表面上 node -v 还能用,which node 和 which npm 却不在同一目录。结果是:用 A 的 npm 把包装进 B 的前缀,或 CLI 找到了命令却用错运行时。排查时先跑 which node、which npm、npm prefix -g。
| 名字 | 它是什么 | 不是什么 |
|---|---|---|
| Node.js | JS 运行时 | 包仓库,也不负责选版本 |
| npm | 包管理器 + 公共 registry | 运行时;离开 node 它自己也跑不了 |
| npx | npm 的“临时执行器” | 另一种包管理器 |
| nvm | Node 版本管理器 | 装 CLI 的工具;它不读 package.json |
| yarn / pnpm | 另一套 JS 包管理器 | 不替换 Node,也不替换 nvm |
| Corepack | Node 官方用来启用 yarn/pnpm 的薄封装 | 版本管理器 |
npx pkg 的意义是:不必先 npm install -g,也能拉一份包并立刻执行。很多文档写成 npx @scope/cli,就是为了避免污染全局前缀。Agent CLI 之所以仍推荐全局安装,是因为它们是天天开着的长驻命令,不是一次性脚手架。
2024–2026 这一波编码 Agent,安装说明里反复出现同一句:
npm install -g @anthropic-ai/claude-code
npm install -g @google/gemini-cli
npm install -g @openai/codex
这不是“AI 必须依赖 npm”,而是发行约束撞上了 JS 工具链的局部最优。
对话循环、流式 HTTP、工具调用、TUI,用 TS 写起来最快。编译产物是 JS,天然宿主就是 Node。发行渠道跟着语言走,是最省事的选择。
package.json 的 bin + #!/usr/bin/env node,不必为 Windows / macOS / Linux 各交一份原生二进制。维护者改一行 JS,用户 npm update -g 就能跟上日更节奏。
npm 仓库、semver、scoped 包(@anthropic-ai/...)是现成基础设施。官方不用自建安装器,也不用先过一遍各系统的包审核。
写前端、全栈、AI 应用的人,机器上本来就有 Node。一条 npm i -g 的摩擦,低于再让他们装一份 Go/Rust 工具链,也低于维护三套原生安装包。
读文件、起子进程、拉 SSE、画终端 UI,都是 Node 的日常工作。Agent CLI 本质是“LLM 客户端 + 本地工具运行器”,不是要和内核比性能的系统守护进程。
Claude Code、Gemini CLI、Codex CLI 带起习惯后,新工具默认抄同一套安装句。用户也形成预期:Agent 命令 ≈ 一个全局 npm 包。
npm 在这里扮演的是跨平台软件分发层,不是“前端项目的依赖工具”。Node 则是这些 CLI 的 ABI:稳定、够用、到处都有。nvm 只是让这份 ABI 可以多版本共存。三者叠好之后,厂商才用一行命令把 Agent 送到开发者终端里。
@anthropic-ai/claude-code@google/gemini-cli@openai/codexpip / uv / pipxgo install、cargo install、GitHub Release规律很简单:实现语言决定默认安装器。TS 项目用 npm,Python 项目用 pip,静态语言项目发二进制。2026 年的 coding agent 里 TypeScript 占比高,所以你看到的安装说明被 npm 主导。
系统向的目标不是“能跑”,而是 一份 Node、一对 npm、一组全局 CLI,全部住在同一前缀。
# 1. 只用 nvm 提供 Node,避免和系统包、Homebrew 抢 PATH
nvm install --lts
nvm alias default 'lts/*'
nvm use default
# 2. 三份路径必须落在同一棵树
which node
which npm
npm prefix -g
# 3. 再装长驻 CLI
npm install -g @anthropic-ai/claude-code
自检时看三件事:
node -v 与 npm -v 来自同一发行版;npm prefix -g 指向 nvm 的当前版本目录,而不是 /usr/local;which claude 也在这个 bin 里。任何一项漂到别处,优先修 PATH,而不是再 sudo npm install。用 nvm 时几乎永远不该对 npm 使用 sudo:权限一抬,包就会被写进系统目录,和 nvm 的前缀再次分家。
发行版自带的 Node 往往偏旧,全局目录还需要 root。Agent CLI 更新快、依赖新,最稳的是:nvm(或 fnm/volta)管版本,npm 只管往这份用户态 Node 里装包。
| nvm | Node.js | npm | |
|---|---|---|---|
| 角色 | 版本管理器 | 运行时 | 包管理器 + 仓库客户端 |
| 安装对象 | 多份 Node 发行版 | JS 程序 | JS 包 / CLI |
| 关键副作用 | 改 PATH | 提供 node 进程 |
写 node_modules 和 bin |
| 没有它会怎样 | 仍可只用系统/官网那一份 Node | 所有 JS CLI 无法执行 | 很难获取和更新那些 CLI |
| 和 Agent 的关系 | 保证 Node 版本够新、可切换 | 真正执行 Agent 的 JS | 把 Agent 下载并登记成命令 |
如果你只带走一张图,带走图 1;如果只带走一句话,带走这句:
nvm 选哪份 Node,npm 往这份 Node 里装软件,Node 负责把装上的 Agent CLI 跑起来。