生成模型:Claude Opus 4.6 (anthropic/claude-opus-4-6) Token 消耗:输入 ~215k tokens,输出 ~5k tokens(本节)
配置文件中难免包含敏感信息——API 密钥、数据库密码、第三方服务的令牌。将这些值直接写在 JSON5 文件中既不安全(可能被误提交到版本控制),又不灵活(不同环境需要不同的值)。OpenClaw 通过环境变量替换机制解决这个问题:在配置文件中用 ${VAR_NAME} 占位,加载时自动替换为对应的环境变量值。本节分析这套机制的实现细节。
31.5.1 环境变量替换(src/config/env-substitution.ts)
在任何配置字符串值中,可以使用 ${VAR_NAME} 引用环境变量:
{
models: {
providers: {
"vercel-gateway": {
apiKey: "${VERCEL_GATEWAY_API_KEY}"
}
}
},
gateway: {
auth: {
token: "${OPENCLAW_AUTH_TOKEN}"
}
}
}
加载时,${VERCEL_GATEWAY_API_KEY} 会被替换为 process.env.VERCEL_GATEWAY_API_KEY 的值。
环境变量名必须匹配严格的正则模式——仅允许大写字母、数字和下划线,且必须以字母或下划线开头:
这意味着:
${OPENCLAW_TOKEN_123} — 合法
大写限制是一个有意的设计选择——它防止配置作者误将 JSON 键名当作环境变量(如 ${apiKey}),同时符合 Unix 环境变量的命名惯例。
如果需要在配置值中包含字面量 ${...}(而非变量替换),使用双美元符号转义:
substituteString 函数逐字符扫描字符串,处理三种情况:
错误处理:MissingEnvVarError
当引用的环境变量未设置或为空字符串时,系统抛出 MissingEnvVarError,附带变量名和配置路径,方便定位问题:
例如:如果 models.providers.vercel-gateway.apiKey 引用了 ${VERCEL_GATEWAY_API_KEY} 但该环境变量未设置,错误消息会是:
resolveConfigEnvVars 作为入口函数,通过 substituteAny 递归遍历整个配置对象,对所有字符串值执行替换:
几个设计细节:
路径追踪:path 参数在递归过程中构建完整的配置路径(如 models.providers.vercel-gateway.apiKey),一旦出错就能精确定位。
仅处理字符串:数字、布尔值、null 直接通过,不会尝试替换。
非破坏性:函数返回新对象,不修改输入(纯函数语义)。
31.5.2 环境变量收集(src/config/env-vars.ts)
OpenClaw 允许用户在配置文件中通过 env 段预设环境变量,这些变量会在配置加载早期注入到 process.env 中:
collectConfigEnvVars
collectConfigEnvVars 函数从配置中收集所有需要注入的环境变量,合并两个来源:
两个保留键的处理:
vars:这是结构化变量声明的容器,已在第一步处理过,不应作为环境变量名。
shellEnv:这是一个布尔值配置项,控制是否将 shell 环境变量传递给子进程,不应被当作环境变量。
回顾 23.1 节介绍的 12 步配置加载流水线,环境变量相关的处理出现在第 3、4 步:
这个顺序至关重要——先注入 config.env 中声明的变量到 process.env,然后再执行 ${VAR} 替换。这意味着用户可以在 env.vars 中定义一个变量,然后在配置的其他位置引用它:
衍生解释:这种"先声明后引用"的设计在配置系统中很常见。Kubernetes 的 ConfigMap、Docker Compose 的 .env 文件、GitHub Actions 的 env 段都采用类似模式。其核心优势是"单一事实来源"——敏感值只在一个地方定义,其他位置通过引用使用,避免复制粘贴带来的不一致。
${VAR_NAME} 语法允许在配置字符串中引用环境变量,变量名必须全大写且匹配 [A-Z_][A-Z0-9_]* 模式。
$${VAR} 转义输出字面量 ${VAR},用于配置中需要包含模板语法的场景。
MissingEnvVarError 在变量未设置或为空时抛出,附带变量名和精确的配置路径,便于调试。
递归替换遍历整个配置对象树,仅对字符串值执行替换,数字和布尔值直接通过。
config.env 段支持两种环境变量声明方式:env.vars(结构化)和顶级快捷键(向后兼容)。
加载顺序保证:先注入 config.env 到 process.env,再执行 ${VAR} 替换——允许配置内部自引用。