📌 新手提示:本文结尾提供了一键部署脚本(Windows / macOS / Linux 通用),不想手动逐条敲命令的同学,可以直接拉到文末下载运行,脚本会自动检测网络环境并选择最快的下载源。

前言

如果你正在寻找一个开源、免费、不绑定任何厂商的终端 AI 编程助手,那你来对地方了。

先交代背景:市面上最强的 AI 编程工具——Claude Code——是闭源的,绑定 Anthropic 一家厂商,还要求海外信用卡。对国内用户来说,这意味着要翻墙、要绑卡、要担心封号,还不便宜。

OpenCode(原名 SST OpenCode,现由 anomalyco 团队维护)正是为了解决这个问题而生的。它的定位非常清晰:

一个开源的终端 AI 编程助手,原生支持 75 种以上的大模型提供商,你想用谁就用谁。

打个比方:

  • Claude Code 像 iPhone——很好用,但只能用苹果官方的 App Store,硬件也绑死。
  • OpenCode 像 Android——开源、自由,你可以装任何应用商店,甚至自己编译系统。

更具体地说,OpenCode 能做什么?

  • 在终端里用自然语言描述需求,AI 直接帮你读代码、写代码、改文件、跑命令
  • 支持 Anthropic(Claude 系列)、OpenAI(GPT 系列)、DeepSeek、Google Gemini、本地 Ollama 等 75+ 家模型提供商
  • 所有配置都是明文 JSON 文件,你可以把请求地址改成任何一个中转服务——包括国内可以直接访问的 API 中转站
  • 内置 build(全权限开发)和 plan(只读分析)两种工作模式,按 Tab 键一秒切换
  • 完全开源(GitHub 仓库已获 近 20 万星),代码公开可审计

本教程面向完全零基础的用户。只要你有一台电脑、能看懂文字、照着复制粘贴,就一定能成功启动 OpenCode。

海外用户可直连 GitHub:https://github.com/anomalyco/opencode。国内用户如果访问 GitHub 较慢,可以尝试 ghproxy.com 等镜像服务(注意:部分镜像对大型仓库可能不稳定,遇到问题可尝试切换镜像或直连)。

一、前置准备(只需要三样东西)

1. 一个现代终端

OpenCode 是一个**终端用户界面(Terminal User Interface,简称 TUI)**工具——也就是说,它在你电脑的命令行窗口里运行,用键盘操作,看起来像一个带颜色和布局的文本界面。

它推荐的终端有:

  • macOS:系统自带 Terminal 即可,或者用 WezTerm、Alacritty、Ghostty、Kitty 等第三方终端(体验更好)
  • Linux:系统自带终端即可,推荐 WezTerm / Alacritty / Kitty
  • Windows强烈推荐使用 WSL(Windows Subsystem for Linux,即 Windows 自带的 Linux 子系统)。原生 PowerShell 或 CMD 虽然也能跑,但体验不如 WSL 流畅

如果你从未用过命令行,不要怕——每条命令我都会写清楚,你只要复制粘贴即可。

2. Node.js 18 或以上版本

OpenCode 是通过 npm(Node.js 的包管理器)安装的,所以需要先装 Node.js。

安装完成后,在终端输入以下命令验证:

node --version

如果看到类似 v20.11.0 这样的版本号,就说明安装成功了。

3. 一个 API Key(用来调用大模型)

OpenCode 本身是一个"壳"——它不包含任何 AI 模型,而是通过 API(应用程序编程接口,简单理解就是"通过网络调用远程 AI 服务的通道")去连接各大模型提供商。

你需要至少一个提供商的 API Key(密钥)。常见的选择:

提供商需要什么国内能否直连
Anthropic(Claude)海外手机号 + 信用卡基本不行
OpenAI(GPT)海外手机号 + 信用卡基本不行
DeepSeek国内手机号注册即可可以
阿里云百炼 / 智谱 GLM国内手机号注册即可可以
本地 Ollama不需要(完全免费,跑在自己电脑上)可以

初学者如果不想折腾海外账号,有两个最简单的入门方案:

  • 方案 A:用 DeepSeek 官方的 API Key(国内注册,价格便宜,编程能力也不错)
  • 方案 B:用 API 中转服务(后文会详细介绍)——一个 Key 就能用上 GPT、Claude、DeepSeek 等几十种模型,无需翻墙,无需绑卡

本教程会教你怎么配置任意一家提供商,你想用谁就用谁。后文还会重点演示如何把 OpenCode 接到一个国内直连、价格更低的中转服务上。

二、安装 OpenCode

OpenCode 提供了多种安装方式,适配不同操作系统。以下按推荐程度从高到低排列。

⚠️ 注意:如果你之前安装过 0.1.x 之前的老版本,请先卸载再装新版(npm uninstall -g opencode-aibrew uninstall opencode)。

方法一:npm 全局安装(所有平台通用,最推荐)

如果你的电脑已经有 Node.js,这是最简单的方式:

npm install -g opencode-ai@latest

国内用户:加上国内镜像源加速下载:

npm install -g opencode-ai@latest --registry=https://registry.npmmirror.com

你也可以用 bun、pnpm 或 yarn 代替 npm:

bun add -g opencode-ai@latest
pnpm add -g opencode-ai@latest
yarn global add opencode-ai@latest

方法二:Homebrew(macOS / Linux 推荐)

brew install anomalyco/tap/opencode

方法三:官方一键脚本(所有平台通用)

curl -fsSL https://opencode.ai/install | bash

国内用户若无法直连 opencode.ai,此方法可能超时,请换用方法一(npm + 国内镜像)。

方法四:Windows 包管理器

# Scoop
scoop install opencode

# Chocolatey
choco install opencode

方法五:Arch Linux

# 稳定版
sudo pacman -S opencode

# 最新版(AUR)
paru -S opencode-bin

方法六:版本管理工具

如果你使用 mise 或 Nix 来管理开发工具版本:

mise use -g opencode
nix run nixpkgs#opencode

验证安装

安装完成后,在终端输入:

opencode --version

如果看到版本号,就说明安装成功了。如果提示 command not found,请关闭终端重新打开,或检查 npm 的全局 bin 目录是否在系统 PATH 中(环境变量 PATH 是操作系统用来查找可执行程序的目录列表)。

三、配置 Provider——让 OpenCode 连上你的 AI 模型

这是整个教程最关键的一步。OpenCode 安装后只是一个空壳,你需要告诉它"用哪个 AI 模型、怎么连过去"。

OpenCode 提供了两种配置方式:

方式一:交互式命令 /connect(最简单,推荐新手)

在终端输入 opencode 启动 TUI 界面后,输入斜杠命令:

/connect

OpenCode 会展示一个内置的提供商列表,你只需要:

  1. 用方向键选择你要用的提供商(比如 Anthropic、OpenAI、DeepSeek、Ollama 等)
  2. 粘贴你的 API Key
  3. 回车确认

OpenCode 会自动把凭证保存到 ~/.local/share/opencode/auth.json(这是 OpenCode 存放登录凭证的专用文件),下次启动时自动读取。

如果你用的是本地 Ollama(Ollama 是一个可以在自己电脑上运行开源大模型的工具,完全免费,不需要联网),选 ollama 即可,不需要 Key——Ollama 默认监听本地 http://localhost:11434,OpenCode 会自动识别。

方式二:手动编辑配置文件 opencode.json(更灵活,推荐进阶用户)

在项目根目录创建一个 opencode.json(或 opencode.jsonc,带注释的 JSON 格式)文件。OpenCode 启动时会自动从当前目录向上查找最近的这个文件并加载。

最小配置示例(使用 Anthropic 官方 API):

// opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "anthropic": {
      "options": {
        "apiKey": "sk-ant-xxxxxxxxxxxxx"
      }
    }
  },
  "model": "anthropic/claude-sonnet-4-5-20250929"
}

字段解释

  • provider:你要用的模型提供商,OpenCode 内置了 75+ 家的连接信息
  • options.apiKey:你的 API 密钥(OpenCode 也支持从环境变量读取,把 apiKey 换成 apiKeyEnv 即可,例如 "apiKeyEnv": "ANTHROPIC_API_KEY"
  • model:指定默认使用的模型,格式为 提供商ID/模型ID。模型 ID 必须精确,不能简化——比如 claude-sonnet-4-5-20250929 带日期后缀,不能简写成 claude-sonnet-4.5

配置文件加载顺序

OpenCode 会按以下优先级加载配置(数字越小优先级越高):

  1. 系统级强制配置(Linux 下 /etc/opencode/,由管理员统一推送)
  2. 环境变量 OPENCODE_CONFIG 指定的路径
  3. 项目级配置(当前目录下的 opencode.json + tui.json,向上查到最近的 Git 仓库根目录)
  4. 全局配置~/.local/share/opencode/opencode.json
  5. 远程配置(OpenCode Zen 推送的云端配置)

一般用户只需要关心第 3 和第 4 级。如果你一个项目想用 Claude、另一个项目想用 DeepSeek,就在各自的项目根目录放不同的 opencode.json

四、TUI 交互入门——第一次和 OpenCode 对话

配置完成后,进入你的项目目录:

cd /你的项目路径

然后输入:

opencode

首次启动会做一些初始化。稍等片刻,你会看到 OpenCode 的 TUI 界面——一个带颜色、分区域的文本界面。

OpenCode TUI 交互界面

初始化项目:生成 AGENTS.md

进入 TUI 后,第一件事建议执行:

/init

这个命令会让 OpenCode 分析你的整个项目结构,自动生成一个 AGENTS.md 文件(放在项目根目录)。这个文件相当于给 AI 的一份"项目说明书"——告诉它你的项目是做什么的、代码怎么组织的、有什么约定。把它 commit 进 Git 仓库后,团队里每个人用 OpenCode 打开这个项目时,AI 都会自动理解项目上下文。

如果你用过 Claude Code,可以把它理解为 CLAUDE.md 的对应物。

两种工作模式:Build vs Plan

OpenCode 内置了两个核心 Agent(智能体,即 AI 的行为模式):

模式能力适用场景
build(默认)读文件、写文件、执行命令、git 操作——全部自动日常开发、改代码、跑测试
plan只读分析,涉及文件修改或命令执行时会先问你确认探索陌生代码库、规划大改动

Tab 键 可以在 build 和 plan 之间一键切换。这是一个非常实用的设计——比如你接手一个陌生项目,先用 plan 模式让 AI 帮你摸清代码结构,确认方案没问题后,切到 build 模式让它动手改。

常用交互方式

直接提问(自然语言,支持中文):

这个项目的认证逻辑是怎么实现的?

引用文件(在 prompt 中加 @ 前缀):

帮我重构 @src/utils/auth.ts 这个文件,把里面的硬编码配置抽出来

OpenCode 会自动模糊匹配文件名,你不需要输入完整路径。

执行 Shell 命令(在 prompt 中加 ! 前缀):

! npm run test

命令的输出会自动作为上下文追加到当前对话中,AI 可以根据输出继续帮你分析。

拖图片到终端:如果你用的终端支持(如 WezTerm、Kitty),可以直接把截图拖进 OpenCode 窗口,图片会自动附加到当前 prompt 中。这在调试 UI 问题时特别有用。

常用快捷键速查

快捷键对应命令功能
Tab切换 build / plan 模式
Ctrl+X C/compact压缩当前会话(清理上下文,节省 token)
Ctrl+X E/editor用外部编辑器($EDITOR 环境变量指定的)编辑当前消息
Ctrl+X L/sessions列出所有历史会话,可切换
Ctrl+X M/models列出当前提供商所有可用模型
Ctrl+X N/new新建一个空白会话
Ctrl+X R/redo重做(仅在使用 /undo 撤销后可用)
Ctrl+X T/themes列出所有可用主题
Ctrl+X U/undo撤销最近一条消息及其产生的文件改动(依赖 Git)
Ctrl+X X/export导出当前会话为 Markdown 文件
Ctrl+X Q/exit退出 OpenCode

完整斜杠命令速查

命令别名说明
/connect添加新的模型提供商(交互式输入 API Key)
/compact/summarize压缩当前会话上下文
/details切换工具执行的详细程度显示
/editor用外部编辑器编辑消息
/exit/quit, /q退出 OpenCode
/export导出会话为 Markdown
/help打开帮助弹窗
/init引导生成项目 AGENTS.md
/models列出可用模型
/new/clear新建会话
/redo重做(仅 /undo 后可用)
/sessions/resume, /continue列出并切换历史会话
/share分享当前会话(默认不公开)
/themes列出可用主题
/thinking切换 thinking/reasoning 块的显示
/undo撤销最近一条消息及文件改动
/unshare取消分享

非交互模式:单次执行

如果你不想进入 TUI 界面,只想让 OpenCode 执行一条指令就退出:

opencode run "帮我找出 src/ 下所有未被引用的函数"

执行完毕后自动退出,不会留在 TUI 里。适合集成到脚本或 CI/CD 流程中。

五、会话管理——保存、恢复、分享你的工作

OpenCode 会自动保存每一次对话为"会话(Session)"。你可以随时查看、切换、导出或分享。

查看历史会话

/sessions

或在终端中直接执行:

opencode session

你会看到一个会话列表,每个会话有唯一的 ID 和创建时间。用方向键选择即可恢复。

继续上次的会话

# 启动时自动续接最近一次会话
opencode --continue

# 续接指定 ID 的会话
opencode --session <会话ID>

导出会话

在 TUI 中按 Ctrl+X X,或执行 /export,当前会话会导出为一个 Markdown 文件。方便存档、分享给同事、或贴到文档里。

你也可以在终端中批量导出:

opencode export

分享会话

输入 /share 即可生成一个分享链接。默认不公开,只有拿到链接的人才能看到。如果之后想取消分享,执行 /unshare

压缩会话

当你和 AI 聊了很久,上下文越来越长,响应速度会变慢(因为每次请求都要把之前所有对话发给模型)。这时候按 Ctrl+X C 或执行 /compact,OpenCode 会自动把历史对话提炼成摘要,释放上下文空间,让后续对话恢复流畅。

六、进阶:把 OpenCode 切到 API 中转站——不翻墙用上 GPT + Claude

前面我们演示了如何配置 Anthropic 和 OpenAI 官方 API。但前面也提到,官方 API 对国内用户有三个现实问题:

  1. 注册门槛高:需要海外手机号、海外信用卡
  2. 价格不便宜:Claude Sonnet 输入 $3/百万 tokens,重度使用一个月轻松上千元
  3. 封号风险:Anthropic 对非支持地区管控严格,已有多轮大规模封号

OpenCode 最大的优势之一,就是它不绑定任何厂商。由于所有配置都是明文 JSON,你只需要改一个字段——baseURL(API 请求的目标地址)——就能把请求从官方服务器切到任何一个中转服务。

什么是 baseURL?

简单说,baseURL 就是"AI 模型服务在互联网上的门牌号"。默认情况下,OpenCode 会把请求发到 Anthropic 的官方地址(https://api.anthropic.com)。但如果你把 baseURL 改成中转站的地址,所有请求就会先到中转站,由中转站帮你转发到目标模型、再把结果返回给你。

这样一来,你不需要直接访问海外 API,中转站帮你搞定跨境通信;你也不需要注册各家海外账号,中转站用一个统一 Key 就能调用所有模型。

实战:接入云间 API 中转站

这里以云间 APIhttps://cloudzone-api.cyou/)为例,演示完整的配置过程。选择它是因为:

  • 国内直连:香港核心节点,国模延迟低至 30ms,不需要任何网络工具
  • 价格低:GPT 低至官方 0.05 折、DeepSeek 低至 0.3 折、Claude(CC MAX 组)低至 0.5 折
  • 模型全:覆盖 90+ 模型,GPT / Claude / DeepSeek / GLM / Kimi / Grok / Qwen 全系列
  • OpenAI 兼容:标准 OpenAI API 格式,替换 base_url 即可接入
  • Anthropic 兼容:原生支持 Claude API,与 Claude Code 和 OpenCode 均完美兼容
  • 按量计费:充多少用多少,无月租,无隐藏费用

在浏览器打开 https://cloudzone-api.cyou/ 注册并充值,然后在"我的 API Key"页面复制一个 Key(以 sk- 开头)。整个过程 2 分钟搞定。如果你想先白嫖体验,也可以先用 DeepSeek 官方 API(国内注册)跑通流程,等需要更强模型时再切过来。

拿到 Key 之后,在项目根目录创建 opencode.json

// opencode.json — 接入云间 API 中转站
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    // OpenAI 兼容组:GPT 系列、DeepSeek V4、GLM、Kimi 等
    // 走标准 OpenAI API 格式,baseURL 指向云间 API 的 OpenAI 兼容端点
    "openai": {
      "options": {
        "apiKey": "sk-你的云间API-Key",
        "baseURL": "https://cloudzone-api.cyou/v1"
      },
      "models": {
        "gpt-5.4":       { "name": "GPT 5.4" },
        "gpt-5.1-codex": { "name": "GPT 5.1 Codex" },
        "deepseek-v4-flash": { "name": "DeepSeek V4 Flash" }
      }
    },
    // Anthropic 兼容组(CC MAX):Claude 全系列
    // 走原生 Anthropic API 格式,baseURL 指向云间 API 的 Anthropic 兼容端点
    "anthropic": {
      "options": {
        "apiKey": "sk-你的云间API-Key",
        "baseURL": "https://cloudzone-api.cyou/v1"
      },
      "models": {
        "claude-sonnet-4-5-20250929": { "name": "Claude Sonnet 4.5" },
        "claude-opus-4-5":            { "name": "Claude Opus 4.5" }
      }
    }
  },
  // 默认模型:写代码推荐 GPT 5.1 Codex,性价比高
  "model": "openai/gpt-5.1-codex"
}

配置说明

  • baseURL 是核心——它告诉 OpenCode"把请求发到云间 API 而不是官方"。OpenCode 内置的 75+ 家提供商全部支持 baseURL 改写,你不需要改一行 OpenCode 源码
  • models 字段可以不写——写了会在模型列表里显示友好名称,不写也能用(OpenCode 会自动从云间 API 拉取可用模型列表)
  • 不同提供商的 baseURL 路径可能不同。云间 API 的 OpenAI 兼容组和 Anthropic 兼容组都走同一个 /v1 端点,具体以云间 API 面板的文档为准
  • 如果你只想用某几个模型,可以用 blacklist(黑名单)隐藏不需要的模型;或者用 whitelist(白名单)只显示指定的模型

验证配置

配置完成后,启动 OpenCode 并执行:

/models

你应该能看到云间 API 提供的所有模型列表。如果看到自己配置的模型(如 GPT 5.4、Claude Sonnet 4.5),说明配置成功。

切换模型也很简单:

/m models openai/gpt-5.4

或者直接修改 opencode.json 中的 model 字段,重启即可。

关于黑名单和白名单

如果你觉得模型列表太长,可以只显示你需要的:

{
  "provider": {
    "openai": {
      "options": {
        "apiKey": "sk-xxx",
        "baseURL": "https://cloudzone-api.cyou/v1"
      },
      // 白名单:只显示这三个模型
      "whitelist": ["gpt-5.4", "gpt-5.1-codex", "deepseek-v4-flash"]
    }
  }
}

blacklist 则是反过来——隐藏指定模型,其余全显示。

补充:OpenCode 官方 Zen 网关也是同一套逻辑

OpenCode 团队自己也运营了一个叫 Zen 的网关(https://opencode.ai/zen/v1),本质也是"中转"——请求先到 Zen 网关,再转发到各家模型。Zen 和云端 API 中转站用的是完全相同的 baseURL 机制,只是目的地不同。

所以,你已经学会了"怎么把 OpenCode 接到任意中转站"——这就是 OpenCode 开源架构带来的自由度。

七、TUI 美化——换个主题

在 TUI 中输入:

/themes

会列出所有可用主题。选一个你喜欢的,然后 OpenCode 会自动把主题名写入 tui.json(TUI 的配置文件,和 opencode.json 放在同一目录)。

以下是 tui.json 的常见配置项:

// tui.json
{
  "$schema": "https://opencode.ai/tui.json",
  "theme": "opencode",           // 主题名
  "leader_timeout": 2000,        // 快捷键前缀的超时时间(毫秒)
  "scroll_speed": 3,             // 滚动速度
  "mouse": true,                 // 是否启用鼠标
  "cursor": {
    "style": "block",            // 光标样式:block / underline / bar
    "blinking": true             // 光标是否闪烁
  },
  "diff_style": "auto",          // 代码差异展示风格
  "attention": {
    "enabled": true,             // 任务完成时是否提醒
    "notifications": true,       // 是否发送系统通知
    "sound": true,               // 是否播放提示音
    "volume": 0.4
  }
}

八、进阶:自定义 Agent 和权限控制

OpenCode 允许你创建自定义 Agent,为不同场景分配不同权限。

内置 Agent

Agent类型权限
build主 Agent(默认)全部权限:读文件、写文件、执行命令、Git 操作
plan主 Agent只读分析,涉及修改或命令时需确认
general子 Agent复杂多步搜索,用 @general 在对话中触发

创建自定义 Agent

opencode agent create

这会启动一个交互式向导,引导你设置 Agent 的名称、描述、模式(all / primary / subagent)和权限。

权限列表(可多选,逗号分隔):

权限含义
bash执行终端命令
read读取文件
edit修改文件
glob文件名匹配搜索
grep文本内容搜索
webfetch抓取网页内容
websearch网络搜索
task创建子任务
todowrite写 TODO 列表
lsp语言服务器协议(代码跳转、补全等)
skill调用技能

例如,创建一个"只读代码审查员"Agent:

opencode agent create \
  --path .opencode/agent/code-reviewer.md \
  --description "只读代码审查,不修改文件" \
  --mode primary \
  --permissions "read,glob,grep,lsp"

生成的文件会保存在 .opencode/agent/code-reviewer.md,可以在 TUI 中用 --agent 参数指定使用。

九、常见问题排查

Q1:安装后提示 command not found

  • 关闭终端重新打开,让系统重新加载 PATH
  • 如果是 npm 安装的,确认 npm 全局安装目录在 PATH 中。可以执行 npm config get prefix 查看全局安装路径,然后确认该路径的 bin 子目录在 PATH 中
  • 如果是 Homebrew 安装的,确认 Homebrew 的 bin 目录在 PATH 中(通常为 /usr/local/bin/opt/homebrew/bin

Q2:启动后提示认证失败 / API Key 无效

  • 检查 opencode.json 中的 apiKey 是否正确
  • 如果你用的是环境变量方式(apiKeyEnv),确认环境变量已设置:echo $YOUR_ENV_VAR_NAME
  • 如果用的是云间 API,登录 https://cloudzone-api.cyou/ 检查 Key 是否有效且有余额
  • 确认 baseURL 路径是否正确。不同中转站的端点路径可能不同,请以中转站面板的文档为准

Q3:响应很慢或超时

  • 如果用的是海外官方 API,可能是网络问题。尝试切换到国内中转站(如云间 API 的香港节点)
  • 如果会话已经对话了很多轮,上下文可能过长——按 Ctrl+X C 压缩一下
  • 试试切换更快的模型:DeepSeek V4 Flash 响应速度很快,GPT 5.1 Codex 在编程任务上也很高效

Q4:/undo 撤销不了?

  • /undo 依赖 Git 跟踪文件改动。如果你的项目不在 Git 仓库中,/undo 无法工作
  • 另外,/undo 只能撤销最近一条消息的操作。如果需要撤销更多,需要用 Git 本身(git checkoutgit reset

Q5:OpenCode 和 Claude Code 怎么选?

  • 选 Claude Code:如果你已经绑定了 Anthropic 账号,只需要 Claude 模型,且不介意闭源
  • 选 OpenCode:如果你想用多种模型(GPT + Claude + DeepSeek + 本地 Ollama)、想用中转站省钱、在意开源可审计、或者不想被一家厂商绑定

两者可以共存——你可以两个都装,根据不同项目切换使用。

十、总结

到这里,你已经完成了 OpenCode 的完整入门:

  1. 安装了 OpenCode
  2. 配置了模型提供商(通过 /connectopencode.json
  3. 学会了 TUI 的基本操作(build/plan 切换、文件引用、斜杠命令)
  4. 掌握了会话管理(保存、恢复、导出、分享)
  5. 学会了把 baseURL 切到中转站,不翻墙用上 GPT + Claude

OpenCode 最大的价值在于自由——自由选择模型、自由切换提供商、自由配置中转。它不要求你绑定信用卡,不限制你只能用某一家 AI,代码完全开源,任何人都可以审查。对于一个开源项目来说,GitHub 近 20 万星的认可已经说明了一切。

而让这种自由真正落地的关键,是一个稳定、低价、国内直连的 API 入口云间 API 提供了 GPT 低至 0.05 折、DeepSeek 低至 0.3 折、Claude 低至 0.5 折的定价,覆盖 90+ 模型,香港节点国模延迟低至 30ms。你不需要翻墙、不需要海外信用卡,注册就能用——和 OpenCode 的开源自由理念天然契合。

当然,这不是唯一的选择。OpenCode 的 75+ 提供商列表里,DeepSeek 官方 API、本地 Ollama、阿里云百炼等都可以作为你的起点。选择一个最符合你需求和预算的方案,然后开始写代码。

现在就试试吧。 打开终端,输入 opencode,然后说一句:“帮我写一个 TODO 应用”。你会发现,开源的 AI 编程体验,一点不比闭源差。

十一、一键部署脚本

如果你觉得上面的步骤还是有点多,我们为你准备了一键部署脚本。脚本会自动完成:检测网络环境 → 安装 Node.js(如未安装)→ 安装 OpenCode → 交互式配置 API Key,全程引导式交互,国内国外网络都能用。

下载

平台脚本文件
macOS / Linux / WSLinstall-opencode.sh
Windowsinstall-opencode.ps1

不方便下载的同学,可以直接复制文末的完整源码,新建文本文档粘贴后改后缀为 .sh.ps1 运行。

使用说明

macOS / Linux / WSL:

chmod +x install-opencode.sh
./install-opencode.sh

Windows:

Windows 默认会阻止运行未签名的 PowerShell 脚本(这是 Windows 的安全策略,不是杀毒软件拦截)。在 PowerShell 中执行以下命令临时放行当前会话的执行策略,再运行脚本:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
.\install-opencode.ps1

🔒 关于"为什么要放行执行策略":Windows 出于安全考虑,默认禁止运行未经数字签名(我们没有购买代码签名证书)的 .ps1 脚本。-Scope Process 表示仅在当前 PowerShell 窗口生效,关闭窗口后自动恢复原策略,不会永久改变你系统的安全设置。如果你仍不放心,可以先把脚本完整源码复制给任意 AI(包括 Claude、ChatGPT 等),让它帮你判断脚本是否安全,再决定是否运行。这就是我们公开全部源码的原因。

脚本完整源码

下载链接见文末:static/scripts/install-opencode-guide.sh / install-opencode-guide.ps1

macOS / Linux / WSL 版(install-opencode.sh)

#!/usr/bin/env bash
# ============================================================
#  OpenCode 入门教程 一键脚本(macOS / Linux / WSL)
#  公开源码,欢迎审查 —— 不放心可先复制给 AI 判断
#  默认配置:云间 API 中转站(https://cloudzone-api.cyou/)
# ============================================================

set -u

CLOUDZONE_URL="https://cloudzone-api.cyou"
CLOUDZONE_API_BASE="https://cloudzone-api.cyou/v1"

GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'
info()  { echo -e "${GREEN}[INFO]${NC} $1"; }
warn()  { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
step()  { echo -e "${CYAN}[STEP]${NC} $1"; }

# ---------- 第 0 步:检测网络环境(国内 / 国外)----------
detect_network() {
  step "检测网络环境(国内 / 国外)..."
  if curl -fsI --max-time 5 "https://claude.ai" >/dev/null 2>&1; then
    info "可直连 claude.ai,判定为海外网络"
    echo "overseas"
  else
    warn "无法直连 claude.ai,判定为国内网络环境"
    echo "domestic"
  fi
}

# ---------- 第 1 步:安装 Node.js 18+ ----------
install_nodejs() {
  local net="$1"
  step "检查 Node.js 环境..."

  if command -v node >/dev/null 2>&1; then
    local NODE_MAJOR
    NODE_MAJOR=$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0)
    if [ "$NODE_MAJOR" -ge 18 ]; then
      info "Node.js $(node --version) 已就绪"
      return 0
    fi
    warn "Node.js $(node --version) 版本过低(需要 18+),将尝试安装新版本..."
  fi

  # 尝试 Homebrew(macOS / Linux)
  if command -v brew >/dev/null 2>&1; then
    info "通过 Homebrew 安装 Node.js..."
    if brew install node 2>/dev/null; then
      info "Homebrew 安装 Node.js 成功"
      return 0
    fi
    warn "Homebrew 安装失败,尝试其他方式"
  fi

  # 尝试 apt(Debian / Ubuntu)
  if command -v apt-get >/dev/null 2>&1; then
    info "通过 apt 安装 Node.js..."
    curl -fsSL --max-time 30 "https://deb.nodesource.com/setup_20.x" 2>/dev/null | sudo -E bash - 2>/dev/null
    if sudo apt-get install -y nodejs 2>/dev/null; then
      info "apt 安装 Node.js 成功"
      return 0
    fi
    warn "apt 安装失败,尝试其他方式"
  fi

  # 尝试 dnf(Fedora)
  if command -v dnf >/dev/null 2>&1; then
    info "通过 dnf 安装 Node.js..."
    if sudo dnf install -y nodejs 2>/dev/null; then
      info "dnf 安装 Node.js 成功"
      return 0
    fi
    warn "dnf 安装失败,尝试其他方式"
  fi

  # 尝试 yum(RHEL / CentOS)
  if command -v yum >/dev/null 2>&1; then
    info "通过 yum 安装 Node.js..."
    curl -fsSL --max-time 30 "https://rpm.nodesource.com/setup_20.x" 2>/dev/null | sudo -E bash - 2>/dev/null
    if sudo yum install -y nodejs 2>/dev/null; then
      info "yum 安装 Node.js 成功"
      return 0
    fi
    warn "yum 安装失败,尝试其他方式"
  fi

  # 尝试 pacman(Arch Linux)
  if command -v pacman >/dev/null 2>&1; then
    info "通过 pacman 安装 Node.js..."
    if sudo pacman -S --noconfirm nodejs 2>/dev/null; then
      info "pacman 安装 Node.js 成功"
      return 0
    fi
    warn "pacman 安装失败,尝试其他方式"
  fi

  # 回退:手动下载二进制包
  local NODE_VER="v20.18.0"
  local OS
  OS="$(uname -s)"
  local ARCH
  ARCH="$(uname -m)"
  local NODE_ARCHIVE=""
  local NODE_DIRNAME=""

  case "$OS" in
    Linux)
      case "$ARCH" in
        x86_64)  NODE_ARCHIVE="node-${NODE_VER}-linux-x64.tar.xz"; NODE_DIRNAME="node-${NODE_VER}-linux-x64" ;;
        aarch64) NODE_ARCHIVE="node-${NODE_VER}-linux-arm64.tar.xz"; NODE_DIRNAME="node-${NODE_VER}-linux-arm64" ;;
        armv7l)  NODE_ARCHIVE="node-${NODE_VER}-linux-armv7l.tar.xz"; NODE_DIRNAME="node-${NODE_VER}-linux-armv7l" ;;
        *) error "不支持的 Linux 架构:$ARCH"; return 1 ;;
      esac
      ;;
    Darwin)
      case "$ARCH" in
        x86_64) NODE_ARCHIVE="node-${NODE_VER}-darwin-x64.tar.gz"; NODE_DIRNAME="node-${NODE_VER}-darwin-x64" ;;
        arm64)  NODE_ARCHIVE="node-${NODE_VER}-darwin-arm64.tar.gz"; NODE_DIRNAME="node-${NODE_VER}-darwin-arm64" ;;
        *) error "不支持的 macOS 架构:$ARCH"; return 1 ;;
      esac
      ;;
    *) error "不支持的操作系统:$OS"; return 1 ;;
  esac

  local NODE_URL
  if [ "$net" = "domestic" ]; then
    NODE_URL="https://npmmirror.com/mirrors/node/${NODE_VER}/${NODE_ARCHIVE}"
  else
    NODE_URL="https://nodejs.org/dist/${NODE_VER}/${NODE_ARCHIVE}"
  fi

  step "下载 Node.js ${NODE_VER} (${OS} ${ARCH})..."
  local TMP_FILE="/tmp/${NODE_ARCHIVE}"
  if ! curl -fsSL --max-time 120 "$NODE_URL" -o "$TMP_FILE"; then
    warn "主源下载失败,尝试备用源..."
    local FALLBACK_URL
    if [ "$net" = "domestic" ]; then
      FALLBACK_URL="https://nodejs.org/dist/${NODE_VER}/${NODE_ARCHIVE}"
    else
      FALLBACK_URL="https://npmmirror.com/mirrors/node/${NODE_VER}/${NODE_ARCHIVE}"
    fi
    if ! curl -fsSL --max-time 120 "$FALLBACK_URL" -o "$TMP_FILE"; then
      error "所有源均无法下载 Node.js。请手动安装:https://nodejs.org/ (海外)或 https://npmmirror.com/mirrors/node/ (国内镜像)"
      return 1
    fi
  fi

  info "解压 Node.js 到 /usr/local..."
  cd /tmp || return 1
  rm -rf "/tmp/${NODE_DIRNAME}"
  if [[ "$NODE_ARCHIVE" == *.tar.xz ]]; then
    tar -xJf "$TMP_FILE"
  else
    tar -xzf "$TMP_FILE"
  fi

  if sudo cp -r "/tmp/${NODE_DIRNAME}/"* /usr/local/ 2>/dev/null; then
    info "已安装到 /usr/local(需要 sudo 权限)"
  else
    # 无 sudo 权限:安装到用户目录
    warn "无 sudo 权限,安装到 $HOME/.local/node"
    mkdir -p "$HOME/.local/node"
    cp -r "/tmp/${NODE_DIRNAME}/"* "$HOME/.local/node/"
    export PATH="$HOME/.local/node/bin:$PATH"
    # 写入 shell 配置文件(永久生效)
    local SHELL_RC="${HOME}/.bashrc"
    case "${SHELL:-/bin/bash}" in */zsh) SHELL_RC="$HOME/.zshrc" ;; esac
    if ! grep -q '.local/node/bin' "$SHELL_RC" 2>/dev/null; then
      echo 'export PATH="$HOME/.local/node/bin:$PATH"' >> "$SHELL_RC"
      info "PATH 已写入 $SHELL_RC,新开终端窗口后生效"
    fi
  fi

  rm -f "$TMP_FILE"; rm -rf "/tmp/${NODE_DIRNAME}"
  if command -v node >/dev/null 2>&1; then
    info "Node.js $(node --version) 安装完成"
    return 0
  else
    error "Node.js 安装后仍无法使用,请手动安装:https://nodejs.org/ (海外)或 https://npmmirror.com/mirrors/node/ (国内镜像)"
    return 1
  fi
}

# ---------- 第 2 步:安装 OpenCode ----------
install_opencode() {
  local net="$1"
  step "安装 OpenCode..."

  if command -v opencode >/dev/null 2>&1; then
    info "已检测到 OpenCode:$(opencode --version 2>/dev/null || echo '已安装')"
    return 0
  fi

  # 国内:使用 npmmirror 镜像加速
  if [ "$net" = "domestic" ]; then
    info "国内网络,通过 npm + 国内镜像(npmmirror.com)安装..."
    if npm install -g opencode-ai@latest --registry="https://registry.npmmirror.com" 2>/dev/null; then
      info "npm(国内镜像)安装成功"
      return 0
    fi
    warn "国内镜像失败,尝试官方 npm registry..."
  fi

  # 海外/回退:使用官方 npm registry
  info "通过 npm(官方源)安装..."
  if npm install -g opencode-ai@latest 2>/dev/null; then
    info "npm 安装成功"
    return 0
  fi

  error "所有安装方式均失败。请参考官方文档手动安装:https://github.com/anomalyco/opencode"
  return 1
}

# ---------- 第 3 步:配置 API Key ----------
configure_api() {
  step "配置 API Key..."
  echo ""
  echo "OpenCode 需要 API Key 才能调用 AI 模型。默认配置云间 API 中转站:"
  echo "  · 价格不足官方两折,国内直连无需翻墙"
  echo "  · 支持 90+ 模型,按量计费无月租"
  echo ""
  echo "是否现在跳转注册并获取 API Key?"
  echo "  Y - 立即跳转至 ${CLOUDZONE_URL}"
  echo "  N - 我已有 Key,自行输入"
  read -r -p "请选择 [Y/N]: " choice

  local API_KEY=""
  if [ "${choice:-N}" = "Y" ] || [ "${choice:-N}" = "y" ]; then
    info "正在打开浏览器..."
    if command -v xdg-open >/dev/null 2>&1; then
      xdg-open "$CLOUDZONE_URL" >/dev/null 2>&1
    elif command -v open >/dev/null 2>&1; then
      open "$CLOUDZONE_URL" >/dev/null 2>&1          # macOS
    else
      warn "无法自动打开浏览器,请手动访问:$CLOUDZONE_URL"
    fi
    echo "注册后在「我的 API Key」页面复制 Key(以 sk- 开头)。"
    read -r -p "粘贴你的 API Key: " API_KEY
  else
    # 关闭回显,避免 Key 明文留在终端滚动区
    read -r -s -p "请输入你的 API Key (sk-...): " API_KEY
    echo
  fi

  if [ -z "${API_KEY:-}" ]; then
    error "未输入 API Key,跳过配置。可稍后手动设置。"
    return 1
  fi

  # 写入 OpenCode 全局配置文件
  local OPCONF_DIR="$HOME/.local/share/opencode"
  mkdir -p "$OPCONF_DIR"
  local OPCONF="$OPCONF_DIR/opencode.json"

  if [ -f "$OPCONF" ]; then
    warn "已有配置文件 $OPCONF,将备份为 opencode.json.bak"
    cp "$OPCONF" "$OPCONF_DIR/opencode.json.bak"
  fi

  # 使用占位符 + sed 替换,避免 API Key 中的特殊字符破坏 JSON
  cat > "$OPCONF" << 'OPCONFEOF'
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "openai": {
      "options": {
        "apiKey": "__API_KEY__",
        "baseURL": "__BASE_URL__"
      }
    },
    "anthropic": {
      "options": {
        "apiKey": "__API_KEY__",
        "baseURL": "__BASE_URL__"
      }
    }
  },
  "model": "openai/gpt-5.1-codex"
}
OPCONFEOF

  # macOS 和 Linux 的 sed -i 语法不同,分别处理
  if [[ "$(uname -s)" == "Darwin" ]]; then
    sed -i '' "s|__API_KEY__|${API_KEY}|g; s|__BASE_URL__|${CLOUDZONE_API_BASE}|g" "$OPCONF"
  else
    sed -i "s|__API_KEY__|${API_KEY}|g; s|__BASE_URL__|${CLOUDZONE_API_BASE}|g" "$OPCONF"
  fi

  info "配置完成!"
  echo "  配置文件: $OPCONF"
  echo "  API Key : ${API_KEY:0:10}********"
  echo "  Base URL: $CLOUDZONE_API_BASE"
  echo ""
  info "启动 OpenCode 后输入 /models 即可看到可用模型列表"
}

# ---------- 第 4 步:验证安装 ----------
verify_installation() {
  step "验证安装..."
  if command -v opencode >/dev/null 2>&1; then
    info "OpenCode 安装成功:$(opencode --version 2>/dev/null || echo '版本检测完成')"
    return 0
  else
    warn "opencode 命令未在 PATH 中找到。请尝试关闭终端重新打开,或执行:"
    warn "  export PATH=\"\$(npm config get prefix)/bin:\$PATH\""
    return 0
  fi
}

# ---------- 主流程 ----------
main() {
  echo "============================================"
  echo "  OpenCode 入门教程 一键脚本"
  echo "  适用于 macOS / Linux / WSL"
  echo "  默认接入:云间 API 中转站"
  echo "============================================"
  echo ""

  local NET
  NET=$(detect_network)

  if ! install_nodejs "$NET"; then
    error "Node.js 安装失败,脚本终止。请按上方提示手动处理。"
    exit 1
  fi

  if ! install_opencode "$NET"; then
    error "OpenCode 安装失败,脚本终止。请按上方提示手动处理。"
    exit 1
  fi

  configure_api

  verify_installation

  echo ""
  echo "============================================"
  info "全部完成!"
  echo ""
  echo "  启动方式:在终端输入 opencode 回车"
  echo "  首次使用:在 TUI 中输入 /init 分析项目,生成 AGENTS.md"
  echo "  切换模型:输入 /models 查看可用模型列表"
  echo "  获取帮助:输入 /help 或见博客正文"
  echo "============================================"
}

main "$@"

Windows 版(install-opencode.ps1)

# ============================================================
#  OpenCode 入门教程 一键脚本(Windows PowerShell)
#  公开源码,欢迎审查 —— 不放心可先复制给 AI 判断
#  默认配置:云间 API 中转站(https://cloudzone-api.cyou/)
# ============================================================

$ErrorActionPreference = "Stop"
$CLOUDZONE_URL = "https://cloudzone-api.cyou"
$CLOUDZONE_API_BASE = "https://cloudzone-api.cyou/v1"

function Write-Info  { Write-Host "[INFO] $args" -ForegroundColor Green }
function Write-Warn  { Write-Host "[WARN] $args" -ForegroundColor Yellow }
function Write-Err   { Write-Host "[ERROR] $args" -ForegroundColor Red }
function Write-Step  { Write-Host "[STEP] $args" -ForegroundColor Cyan }

# ---------- 第 0 步:检测网络环境(国内 / 国外)----------
function Detect-Network {
    Write-Step "检测网络环境(国内 / 国外)..."
    try {
        $null = Invoke-WebRequest -Uri "https://claude.ai" -Method Head -TimeoutSec 5 -UseBasicParsing
        Write-Info "可直连 claude.ai,判定为海外网络"
        return "overseas"
    } catch {
        Write-Warn "无法直连 claude.ai,判定为国内网络环境"
        return "domestic"
    }
}

# ---------- 第 1 步:安装 Node.js 18+ ----------
function Install-NodeJS {
    param([string]$Net)
    Write-Step "检查 Node.js 环境..."

    if (Get-Command node -ErrorAction SilentlyContinue) {
        try {
            $major = [int](node -p 'process.versions.node.split(".")[0]' 2>$null)
            if ($major -ge 18) {
                $nodeVer = node --version 2>$null
                Write-Info "Node.js $nodeVer 已就绪"
                return $true
            }
        } catch {}
        $nodeVer = node --version 2>$null
        Write-Warn "Node.js $nodeVer 版本过低(需要 18+),将尝试安装新版本..."
    }

    $nodeVer = "v20.18.0"
    $msiName = "node-$nodeVer-x64.msi"
    $nodeUrl = if ($Net -eq "domestic") {
        "https://npmmirror.com/mirrors/node/$nodeVer/$msiName"
    } else {
        "https://nodejs.org/dist/$nodeVer/$msiName"
    }

    Write-Step "下载 Node.js $nodeVer..."
    $msiPath = "$env:TEMP\$msiName"
    try {
        Invoke-WebRequest -Uri $nodeUrl -OutFile $msiPath -TimeoutSec 120 -UseBasicParsing
        Write-Info "主源下载成功"
    } catch {
        Write-Warn "主源下载失败:$_"
        $altUrl = if ($Net -eq "domestic") {
            "https://nodejs.org/dist/$nodeVer/$msiName"
        } else {
            "https://npmmirror.com/mirrors/node/$nodeVer/$msiName"
        }
        Write-Warn "尝试备用源:$altUrl"
        try {
            Invoke-WebRequest -Uri $altUrl -OutFile $msiPath -TimeoutSec 120 -UseBasicParsing
            Write-Info "备用源下载成功"
        } catch {
            Write-Err "所有源均无法下载 Node.js。请手动安装:https://nodejs.org/ (海外)或 https://npmmirror.com/mirrors/node/ (国内镜像)"
            return $false
        }
    }

    Write-Info "安装 Node.js(静默安装,需要管理员权限)..."
    Write-Warn "即将弹出用户账户控制(UAC)窗口,请点击「是」允许。"
    Write-Warn "(UAC 是 Windows 的安全机制,安装系统级软件必须经过你的确认——这不是恶意行为,是正常流程。)"
    $proc = Start-Process msiexec.exe -ArgumentList "/i `"$msiPath`" /quiet /norestart" -Wait -PassThru -Verb RunAs
    if ($proc.ExitCode -ne 0) {
        Write-Err "Node.js 安装失败(退出码: $($proc.ExitCode))。请手动安装:https://nodejs.org/ (海外)或 https://npmmirror.com/mirrors/node/ (国内镜像)"
        return $false
    }

    # 刷新 PATH 环境变量(当前会话生效)
    $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
    # (PATH 写入"用户级"是由 Node.js 安装程序自动完成的,脚本只刷新当前会话读取新 PATH)
    Write-Info "Node.js 安装完成"
    return $true
}

# ---------- 第 2 步:安装 OpenCode ----------
function Install-OpenCode {
    param([string]$Net)
    Write-Step "安装 OpenCode..."

    if (Get-Command opencode -ErrorAction SilentlyContinue) {
        try {
            $ver = opencode --version 2>$null
            Write-Info "已检测到 OpenCode:$ver"
            return $true
        } catch {
            Write-Info "已检测到 OpenCode(已安装)"
            return $true
        }
    }

    # 国内:使用 npmmirror 镜像加速
    if ($Net -eq "domestic") {
        Write-Info "国内网络,通过 npm + 国内镜像(npmmirror.com)安装..."
        try {
            npm install -g opencode-ai@latest --registry="https://registry.npmmirror.com"
            Write-Info "npm(国内镜像)安装成功"
            return $true
        } catch {
            Write-Warn "国内镜像失败:$_"
        }
    }

    # 海外/回退:使用官方 npm registry
    Write-Info "通过 npm(官方源)安装..."
    try {
        npm install -g opencode-ai@latest
        Write-Info "npm 安装成功"
        return $true
    } catch {
        Write-Err "所有安装方式均失败:$_"
        Write-Err "请参考官方文档手动安装:https://github.com/anomalyco/opencode"
        return $false
    }
}

# ---------- 第 3 步:配置 API Key ----------
function Configure-Api {
    Write-Step "配置 API Key..."
    Write-Host ""
    Write-Host "OpenCode 需要 API Key 才能调用 AI 模型。默认配置云间 API 中转站:"
    Write-Host "  · 价格不足官方两折,国内直连无需翻墙"
    Write-Host "  · 支持 90+ 模型,按量计费无月租"
    Write-Host ""
    Write-Host "是否现在跳转注册并获取 API Key?"
    Write-Host "  Y - 立即跳转至 $CLOUDZONE_URL"
    Write-Host "  N - 我已有 Key,自行输入"
    $choice = Read-Host "请选择 [Y/N]"

    $apiKey = ""
    if ($choice -eq "Y" -or $choice -eq "y") {
        Write-Info "正在打开浏览器..."
        try {
            Start-Process "$CLOUDZONE_URL"        # Windows 默认浏览器打开
        } catch {
            Write-Warn "无法自动打开浏览器,请手动访问:$CLOUDZONE_URL"
        }
        Write-Host "注册后在「我的 API Key」页面复制 Key(以 sk- 开头)。"
        # Read-Host -AsSecureString 不回显,但返回 SecureString 需转明文用于写入配置文件
        $sec = Read-Host "粘贴你的 API Key" -AsSecureString
        $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec)
        $apiKey = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
        [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
    } else {
        $sec = Read-Host "请输入你的 API Key (sk-...)" -AsSecureString
        $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec)
        $apiKey = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
        [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
    }

    if ([string]::IsNullOrWhiteSpace($apiKey)) {
        Write-Err "未输入 API Key,跳过配置。可稍后手动设置。"
        return $false
    }

    # 写入 OpenCode 全局配置文件
    $opConfDir = "$env:USERPROFILE\.local\share\opencode"
    if (-not (Test-Path $opConfDir)) {
        New-Item -ItemType Directory -Path $opConfDir -Force | Out-Null
    }
    $opConf = "$opConfDir\opencode.json"

    if (Test-Path $opConf) {
        Write-Warn "已有配置文件 $opConf,将备份为 opencode.json.bak"
        Copy-Item $opConf "$opConfDir\opencode.json.bak" -Force
    }

    # 使用 PowerShell 哈希表构建 JSON(安全,不会因 Key 中的特殊字符破坏 JSON 结构)
    $config = [ordered]@{
        '$schema' = 'https://opencode.ai/config.json'
        provider  = [ordered]@{
            openai = [ordered]@{
                options = [ordered]@{
                    apiKey  = $apiKey
                    baseURL = $CLOUDZONE_API_BASE
                }
            }
            anthropic = [ordered]@{
                options = [ordered]@{
                    apiKey  = $apiKey
                    baseURL = $CLOUDZONE_API_BASE
                }
            }
        }
        model = 'openai/gpt-5.1-codex'
    }
    $config | ConvertTo-Json -Depth 5 | Set-Content -Path $opConf -Encoding UTF8

    $masked = if ($apiKey.Length -gt 10) { $apiKey.Substring(0, 10) + "********" } else { "********" }
    Write-Info "配置完成!"
    Write-Host "  配置文件: $opConf"
    Write-Host "  API Key : $masked"
    Write-Host "  Base URL: $CLOUDZONE_API_BASE"
    Write-Host ""
    Write-Info "启动 OpenCode 后输入 /models 即可看到可用模型列表"
    return $true
}

# ---------- 第 4 步:验证安装 ----------
function Verify-Installation {
    Write-Step "验证安装..."
    if (Get-Command opencode -ErrorAction SilentlyContinue) {
        try {
            $ver = opencode --version 2>$null
            if ($ver) { Write-Info "OpenCode 安装成功:$ver" }
        } catch {
            Write-Info "OpenCode 安装成功"
        }
        return $true
    } else {
        # npm 全局安装的 bin 目录可能不在 PATH 中
        try {
            $npmPrefix = npm config get prefix 2>$null
            if ($npmPrefix) {
                Write-Warn "opencode 命令未在 PATH 中找到。请尝试:"
                Write-Warn "  关闭 PowerShell 窗口重新打开,或手动将以下路径添加到 PATH:"
                Write-Warn "  $npmPrefix"
            }
        } catch {}
        return $true
    }
}

# ---------- 主流程 ----------
Write-Host "============================================"
Write-Host "  OpenCode 入门教程 一键脚本"
Write-Host "  适用于 Windows"
Write-Host "  默认接入:云间 API 中转站"
Write-Host "============================================"
Write-Host ""

$net = Detect-Network

if (-not (Install-NodeJS -Net $net)) {
    Write-Err "Node.js 安装失败,脚本终止。请按上方提示手动处理。"
    exit 1
}

if (-not (Install-OpenCode -Net $net)) {
    Write-Err "OpenCode 安装失败,脚本终止。请按上方提示手动处理。"
    exit 1
}

Configure-Api | Out-Null

Verify-Installation | Out-Null

Write-Host ""
Write-Host "============================================"
Write-Info "全部完成!"
Write-Host ""
Write-Host "  启动方式:在终端输入 opencode 回车"
Write-Host "  首次使用:在 TUI 中输入 /init 分析项目,生成 AGENTS.md"
Write-Host "  切换模型:输入 /models 查看可用模型列表"
Write-Host "  获取帮助:输入 /help 或见博客正文"
Write-Host "============================================"