Skip to content

Cursor CLI 绿色便携版:从零构建一个免安装 AI 工具包

把 Cursor 的 AI Agent 能力做成一个 U 盘级别的便携工具——解压即用、用完即删、不污染宿主系统。这个需求来自一个真实场景:需要在不同用户的 Windows 机器上快速启动 AI 助手协助排查问题,而这些机器上不可能预装 Cursor IDE。

本文记录整个工具包从方案选型到最终落地的过程,重点放在环境隔离、API Key 安全存储和进程管理这几个实际踩坑比较多的地方。

需求和约束

目标很明确:一个自包含的 ZIP 压缩包,解压到任意 Windows 10/11 x64 机器上,双击 start.bat 就能使用 Cursor AI Agent 的全部能力(读写文件、执行命令、代码分析)。具体约束:

  • 零安装:不依赖宿主系统的 Node.js、Python 或任何运行时
  • 零残留:所有临时文件、配置、缓存都在工具包内部,删除文件夹即彻底清除
  • 安全传输:API Key 不能明文存储,工具包可以安全地通过即时通讯传给别人
  • 一键启动:用户不需要懂技术,双击 bat 就能用

方案选型

最初考虑过几个方向:

方案优点否决原因
Docker 容器完美隔离用户机器没装 Docker,且镜像体积大
Windows Sandbox系统级隔离需要 Hyper-V,家庭版不支持
QEMU 虚拟机跨平台启动慢、资源占用高、体积 GB 级
直接打包二进制简单轻量最终选择

容器方案全部否决的核心原因是一样的:它们解决的是「开发环境一致性」问题,但我要解决的是「在陌生机器上快速跑起来」的问题。后者对启动速度和体积有极高要求。

架构设计

最终的目录结构:

CursorToolkit/
├── start.bat          # 入口(纯 ASCII,只设环境变量)
├── toolkit.py         # 交互界面 + 全部业务逻辑
├── README.txt
├── bin/               # Cursor CLI(node.exe + index.js + agent.cmd + rg.exe)
├── python/            # Python 3.12 嵌入式包 + pip
│   ├── python.exe
│   ├── Scripts/pip.exe
│   └── python312._pth
└── data/              # 所有运行时数据(隔离边界)
    ├── config/key.enc       # 加密的 API Key
    ├── temp/                # 临时文件(重定向 %TEMP%)
    ├── node_cache/          # Node 编译缓存
    ├── .cursor/
    │   ├── mcp.json         # MCP 配置
    │   └── skills/          # Skills 定义
    └── workspace/           # 默认工作目录
        ├── .cursor/rules/   # Agent 规则
        ├── AGENTS.md
        ├── CLAUDE.md
        └── .cursorignore

分层很清晰:bin/python/ 是不可变的运行时,data/ 是可变的用户数据和配置。整个 data/ 目录就是隔离边界——通过环境变量重定向,CLI 和 Python 的所有读写操作都被限制在这个目录内。

环境隔离:让 CLI「以为」自己装在这台机器上

这是整个方案最关键的部分。Cursor CLI 基于 Node.js,运行时会读写多个系统路径:%TEMP% 存临时文件、%USERPROFILE%\.cursor\ 存配置、%LOCALAPPDATA%\cursor-compile-cache\ 存编译缓存。如果不处理,这些文件会散落在宿主系统的各个角落。

start.bat 的核心就是在启动 Python 之前,把所有相关环境变量重定向到 data/ 下:

bat
@echo off
chcp 65001 >nul 2>&1
set "PYTHONIOENCODING=utf-8"
set "T=%~dp0"
if "%T:~-1%"=="\" set "T=%T:~0,-1%"
set "PATH=%T%\bin;%T%\python;%T%\python\Scripts;%PATH%"
set "TEMP=%T%\data\temp"
set "TMP=%T%\data\temp"
set "CURSOR_CONFIG_DIR=%T%\data\config"
set "NODE_COMPILE_CACHE=%T%\data\node_cache"
set "USERPROFILE=%T%\data"
set "HOME=%T%\data"
set "HOMEDRIVE=%T:~0,2%"
set "HOMEPATH=%T:~2%\data"

这里有几个踩坑点:

USERPROFILEHOME 必须同时重定向。Cursor CLI 内部通过 Node.js 的 os.homedir() 定位 ~/.cursor/ 目录,而 os.homedir() 在 Windows 上的查找顺序是 HOMEUSERPROFILEHOMEDRIVE+HOMEPATH。三个都得改,否则某些代码路径会读到真实的用户目录。

set 命令必须用引号包裹set TEMP=%T%\data\tempset "TEMP=%T%\data\temp" 的区别在于:前者在值末尾可能带一个不可见的空格(取决于编辑器和换行符),导致路径拼接出 data \temp 这种诡异的结果。调试这个问题花了不少时间。

chcp 65001 放在第一行。CLI 的输出包含 Unicode 字符(进度条、图标),如果控制台代码页是默认的 GBK (936),这些字符会变成乱码或导致渲染错位。

Python 嵌入式包 vs 虚拟环境

Python 官方提供了一种「嵌入式包」(embeddable package),是一个不到 12MB 的 ZIP,解压后就是一个最小化的 Python 运行时。和完整安装器的区别:

嵌入式包完整安装 / venv
体积~12 MB~100+ MB
注册表不写写入
PATH不改可选修改
pip需手动安装自带
site-packages默认禁用正常

嵌入式包默认禁用了 import site,意味着 pip 安装的第三方包不会被加载。需要编辑 python312._pth 文件,把 #import site 的注释去掉:

python
python312.zip
.
import site

pip 本身还有一个坑:pip.exe 内部硬编码了 Python 的绝对路径。如果在 A 机器上打包、在 B 机器上使用,路径不匹配就会报 Fatal error in launcher: Unable to create process。解决方案是生成 CMD 包装器,用相对路径调用 python -m pip

bat
@echo off
"%~dp0..\python\python.exe" -m pip %*

API Key 安全存储

工具包需要通过网络传输,API Key 不能明文存储。但 CLI 不支持 OAuth 等交互式登录(--trust 只能在 headless 模式使用),只能通过 CURSOR_API_KEY 环境变量认证。

最终方案是用户设置一个密码,Key 用密码派生的密钥加密后存盘。启动时输入密码解密到内存,关闭窗口后内存清除:

python
_PBKDF2_ITER = 100_000
_VERIFY_PREFIX = b"OK:"

def _encrypt(password: str, api_key: str) -> bytes:
    plaintext = _VERIFY_PREFIX + api_key.encode("utf-8")
    salt = os.urandom(16)
    dk = hashlib.pbkdf2_hmac(
        "sha256", password.encode("utf-8"), salt, _PBKDF2_ITER, dklen=len(plaintext)
    )
    encrypted = bytes(a ^ b for a, b in zip(plaintext, dk))
    return base64.b64encode(salt + encrypted)

几个设计决策:

  • PBKDF2 而非 AES:XOR 加密配合 PBKDF2 派生的等长密钥,在密码学上等价于流密码。优势是不需要引入 cryptography 等第三方库,纯标准库实现。
  • OK: 前缀:用于验证密码是否正确。解密后检查前缀匹配,不匹配说明密码错误。
  • 随机 salt:每次加密生成不同的密文,防止彩虹表攻击。
  • 10 万次迭代:PBKDF2 的迭代次数,暴力破解一个 6 位密码需要数年。

密码输入使用自定义的 _mask_input 函数,通过 msvcrt.getwch() 逐字符读取,显示 * 号,支持退格。这比 getpass 的体验好,因为用户能看到自己输入了几个字符。

进程管理:防止僵尸进程

Cursor CLI 启动后会 fork 出多个子进程(Node.js 主进程、MCP Server 进程等)。如果用户直接关闭 CMD 窗口(点 X 而不是 Ctrl+D),Python 主进程被终止,但这些子进程可能变成孤儿进程继续占用资源。

解决方案是三层防护:

python
_active_pid = None

def _kill_tree(pid):
    try:
        subprocess.run(
            ["taskkill", "/T", "/F", "/PID", str(pid)],
            capture_output=True, timeout=5,
        )
    except Exception:
        pass

def _cleanup_on_exit():
    if _active_pid:
        _kill_tree(_active_pid)

# 第一层:正常退出时清理
atexit.register(_cleanup_on_exit)

# 第二层:Ctrl+Break / 窗口关闭时清理
if os.name == "nt":
    def _ctrl_handler(sig, frame):
        _cleanup_on_exit()
        sys.exit(1)
    signal.signal(signal.SIGBREAK, _ctrl_handler)

# 第三层:Agent 退出后主动清理进程树
proc = subprocess.Popen(cmd, cwd=str(WORKSPACE))
_active_pid = proc.pid
proc.wait()
_kill_tree(proc.pid)     # 即使 Agent 正常退出,也清理残留的 MCP 进程
_active_pid = None

taskkill /T /F /PID/T 参数会终止整个进程树(包括所有子进程),这比只杀主进程靠谱得多。

BAT + Python 混合架构

最初尝试把所有逻辑写在 BAT 里,包括菜单、密码输入、加密解密。很快发现这是条死路:

  • BAT 不支持 UTF-8 字符串操作,中文界面要么乱码要么需要用 chcp 配合 BOM 文件,极其脆弱
  • BAT 没有加密库,实现 PBKDF2 不现实
  • BAT 的 set /p 不支持密码掩码
  • BAT 的错误处理几乎不存在

最终架构是 BAT 做环境、Python 做逻辑start.bat 是一个纯 ASCII 文件,只负责设置环境变量和调用 python toolkit.py。所有交互界面、业务逻辑、错误处理都在 toolkit.py 里。这样 BAT 永远不会遇到编码问题,Python 侧有完整的标准库可用。

打包脚本

整个工具包的构建由一个 Python 脚本完成(cursor_toolkit_pack.py)。它的工作流程:

  1. 从 Cursor CDN 下载最新 CLI(解析安装脚本获取版本号和下载地址)
  2. 下载 Python 3.12 嵌入式包,启用 import site,安装 pip
  3. 检查 CLI 自带的 rg.exe(ripgrep),没有则单独下载
  4. 生成 start.battoolkit.pyREADME.txt
  5. 生成配置模板文件(mcp.jsonAGENTS.md 等)
  6. 生成 pip CMD 包装器
  7. 打包为 ZIP

CLI 的获取优先从 CDN 下载,失败时回退到本地已安装的 CLI(%LOCALAPPDATA%\cursor-agent\versions\),再失败则提示用户手动指定路径。这种三级回退保证了脚本在不同网络环境下都能工作。

最终产物是一个约 56 MB 的 ZIP,解压后约 152 MB(CLI 占 120 MB,Python 占 32 MB)。

中文显示和输入

中文乱码

Windows CMD 默认编码是 GBK,而 CLI 输出是 UTF-8,两边对不上就会乱码。解决方法是在 BAT 脚本最前面加一行:

bat
chcp 65001 >nul 2>&1

这行命令把 CMD 切换成 UTF-8 编码,加了之后中文就正常了。>nul 2>&1 只是不让它打印多余的提示。

如果用 Python 脚本捕获 CLI 输出,也要手动指定 UTF-8 解码,否则 Python 会按系统默认的 GBK 去解读然后报错:

python
r = subprocess.run(["agent.cmd", "-p", "--trust", "reply OK"], capture_output=True)
output = r.stdout.decode("utf-8", errors="replace")

中文输入

CLI 的 TUI 基于 Node.js 终端渲染库,对 Windows CMD 下的中文输入法支持不够好——输入法候选窗口位置偏移、有时需要按回车才能刷新显示。这是上游的问题,目前的缓解方案:

  • 在记事本等应用中打好中文,粘贴到 Agent 对话框
  • 用 Windows Terminal(wt)替代传统 CMD 启动,IME 兼容性更好
  • 使用 headless 模式:start.bat -p "你的问题",直接得到回答

回顾

最终的工具包在实际使用中达到了预期效果:传 ZIP 给同事,解压,双击,输入 Key 和密码,就能在他的机器上用 AI Agent 帮他分析日志、读截图、搜文件。用完删除文件夹,机器上没有任何残留。

核心挑战不在于打包本身,而在于 环境隔离的彻底性。漏掉任何一个环境变量的重定向,CLI 就会在宿主系统上留下痕迹。这类问题在开发机上很难发现(因为开发机上已经有 Cursor 的完整安装),只有在干净的测试机上才会暴露。建议配合自动化测试脚本,检查每个环境变量的指向和每个目录的创建情况。

最后更新于:

Hosted by GitHub Pages