@@ -71,18 +71,24 @@ ccc 不应该:
7171
7272## 字段处理策略
7373
74- ### 1. env 字段 - 特殊处理
74+ ### 1. env 字段 - 分离处理
7575
76- ** 处理方式** :清空特定键,避免配置冲突 。
76+ ** 处理方式** :区分"用户 env"和"ccc env",分别写入 settings.json 和子进程 。
7777
78- 需要清空的键:
79- 1 . 特定前缀:` ANTHROPIC_* ` 、` CLAUDE_* `
80- 2 . 与 provider env 相同的 key
78+ ** 写入 settings.json 的 env** :
79+ - 只保留用户在 settings.json 中定义的 env key
80+ - 排除与 base/provider env 冲突的 key
81+ - 排除 ` ANTHROPIC_* ` /` CLAUDE_* ` 前缀的 key
82+ - 如果过滤后为空,不写 env 字段
83+
84+ ** 传递给子进程的 env** :
85+ - 只包含 base + provider 的 env
86+ - 不包含用户 settings.json 的 env(Claude Code 自己从 settings.json 读取)
8187
8288** 原因** :
8389- provider 的环境变量通过命令行传递给 claude 子进程
84- - 如果 settings.json 中保留这些键,会产生不确定性(不确定哪边生效)
85- - 清空后确保 provider env 的行为可预测
90+ - 用户自定义的非冲突 env 需要保留在 settings.json 中供 Claude Code 使用
91+ - 子进程只需 base + provider env,避免重复
8692
8793** 示例** :
8894
@@ -92,22 +98,39 @@ ccc 不应该:
9298 "env" : {
9399 "ANTHROPIC_MODEL" : " claude-3.7-sonnet" ,
94100 "MY_CUSTOM_VAR" : " value" ,
95- "ANTHROPIC_BASE_URL " : " old-url "
101+ "DISABLE_TELEMETRY " : " 1 "
96102 }
97103}
98104
105+ // base env
106+ {
107+ "API_TIMEOUT" : " 30000" ,
108+ "DISABLE_TELEMETRY" : " 1"
109+ }
110+
99111// provider env
100112{
101113 "ANTHROPIC_BASE_URL" : " https://open.bigmodel.cn/api/anthropic" ,
102114 "ANTHROPIC_AUTH_TOKEN" : " token123" ,
103115 "ANTHROPIC_MODEL" : " glm-4.7"
104116}
105117
106- // 处理后
118+ // 写入 settings.json 的 env
107119{
108120 "env" : {
109- "MY_CUSTOM_VAR" : " value" // 保留(非 ANTHROPIC_* 且非 provider key )
121+ "MY_CUSTOM_VAR" : " value" // 保留(非冲突、 非 ANTHROPIC_*/CLAUDE_* )
110122 }
123+ // DISABLE_TELEMETRY 被过滤(与 base env 冲突)
124+ // ANTHROPIC_MODEL 被过滤(ANTHROPIC_* 前缀)
125+ }
126+
127+ // 传递给子进程的 env(base + provider)
128+ {
129+ "API_TIMEOUT" : " 30000" ,
130+ "DISABLE_TELEMETRY" : " 1" ,
131+ "ANTHROPIC_BASE_URL" : " https://open.bigmodel.cn/api/anthropic" ,
132+ "ANTHROPIC_AUTH_TOKEN" : " token123" ,
133+ "ANTHROPIC_MODEL" : " glm-4.7"
111134}
112135```
113136
@@ -237,33 +260,44 @@ func LoadSettings() (map[string]interface{}, error)
237260
238261---
239262
240- ### 2. CleanEnvInSettings ()
263+ ### 2. FilterUserEnvForSettings ()
241264
242- ** 描述** :清空 settings. env 中的特定环境变量键 。
265+ ** 描述** :过滤用户自定义 env,只保留安全的 key 。
243266
244267** 签名** :
245268``` go
246- // CleanEnvInSettings removes specific environment variable keys from settings.env.
247- // It removes:
248- // 1. Keys with specific prefixes (ANTHROPIC_*, CLAUDE_*)
249- // 2. Keys that match provider env keys
250- // Returns a new map without modifying the input.
251- func CleanEnvInSettings (settings map [string ]interface {}, providerEnvKeys []string ) map [string ]interface {}
269+ // FilterUserEnvForSettings filters user-defined env to only keep safe keys.
270+ // Removes keys in managedEnvKeys or with ANTHROPIC_*/CLAUDE_* prefix.
271+ // Returns nil if no keys remain.
272+ func FilterUserEnvForSettings (userEnv map [string ]interface {}, managedEnvKeys map [string ]bool ) map [string ]interface {}
252273```
253274
254275** 逻辑** :
255- 1 . 深拷贝 settings(不修改输入)
256- 2 . 获取 ` env ` map(不存在则跳过)
257- 3 . 遍历每个 key
258- 4 . 删除满足以下任一条件的 key:
259- - 以 ` ANTHROPIC_ ` 开头
260- - 以 ` CLAUDE_ ` 开头
261- - 存在于 ` providerEnvKeys ` 列表中
262- 5 . 返回新的 map
276+ 1 . 遍历 userEnv 的每个 key
277+ 2 . 跳过在 managedEnvKeys 中的 key(与 base/provider 冲突)
278+ 3 . 跳过 ` ANTHROPIC_* ` /` CLAUDE_* ` 前缀的 key
279+ 4 . 如果过滤后为空,返回 nil
263280
264281---
265282
266- ### 3. MergeWithPriority()
283+ ### 3. MergeEnvMaps()
284+
285+ ** 描述** :合并多个 env map,后者覆盖前者。
286+
287+ ** 签名** :
288+ ``` go
289+ // MergeEnvMaps merges multiple env maps. Later maps override earlier ones.
290+ func MergeEnvMaps (maps ...map [string ]interface {}) map [string ]interface {}
291+ ```
292+
293+ ** 逻辑** :
294+ 1 . 遍历所有 map,依次合并
295+ 2 . nil map 被跳过
296+ 3 . 如果结果为空,返回 nil
297+
298+ ---
299+
300+ ### 4. MergeWithPriority()
267301
268302** 描述** :按优先级深度合并多个配置源。
269303
@@ -289,7 +323,7 @@ func MergeWithPriority(baseSettings, providerSettings, userSettings map[string]i
289323
290324---
291325
292- ### 4 . EnsureStopHook()
326+ ### 5 . EnsureStopHook()
293327
294328** 描述** :确保 Supervisor Stop hook 存在于 settings 中。
295329
@@ -323,22 +357,30 @@ func EnsureStopHook(settings map[string]interface{}, hookCommand string) map[str
323357 ├─→ baseSettings = cfg.Settings
324358 ├─→ providerSettings = cfg.Providers[providerName]
325359 │
326- ├─→ 提取 provider env keys
360+ ├─→ 提取各来源 env(合并前)
361+ │ ├─→ userEnvMap = GetEnv(userSettings)
362+ │ ├─→ baseEnvMap = GetEnv(cfg.Settings)
363+ │ └─→ providerEnvMap = GetEnv(providerSettings)
364+ │
365+ ├─→ 构建 managedEnvKeys = base env keys + provider env keys
327366 │
328367 ├─→ MergeWithPriority(baseSettings, providerSettings, userSettings)
329368 │ │
330369 │ └─→ merged = DeepMerge(DeepCopy(baseSettings), providerSettings)
331370 │ merged = DeepMerge(merged, userSettings) ← userSettings 优先
332371 │
333- ├─→ CleanEnvInSettings(merged, providerEnvKeys)
334- │ └─→ 清空 ANTHROPIC_*, CLAUDE_*, provider env keys
335- │
336372 ├─→ EnsureStopHook(merged, hookCommand)
337373 │ └─→ 确保 Supervisor Stop hook 存在
338374 │
339- ├─→ 确保 hooks 可执行
340- │ ├─→ merged["disableAllHooks"] = false
341- │ └─→ merged["allowManagedHooksOnly"] = false
375+ ├─→ delete(merged, "env")
376+ │ └─→ 移除合并后的 env
377+ │
378+ ├─→ FilterUserEnvForSettings(userEnvMap, managedEnvKeys)
379+ │ └─→ 过滤用户 env,保留安全 key
380+ │ └─→ 如果有结果,写入 merged["env"]
381+ │
382+ ├─→ MergeEnvMaps(baseEnvMap, providerEnvMap)
383+ │ └─→ 子进程 env = base + provider(不含用户 env)
342384 │
343385 └─→ 保存 merged 到 settings.json
344386```
@@ -403,7 +445,7 @@ func EnsureStopHook(settings map[string]interface{}, hookCommand string) map[str
403445
404446---
405447
406- ### 场景 3:env 字段清空
448+ ### 场景 3:env 字段分离处理
407449
408450``` json
409451// settings.json 初始内容
@@ -423,13 +465,25 @@ func EnsureStopHook(settings map[string]interface{}, hookCommand string) map[str
423465}
424466```
425467
426- ** 处理后 ** :
468+ ** 写入 settings.json ** (只保留安全用户 env) :
427469
428470``` json
429471{
430472 "env" : {
431- "MY_CUSTOM_VAR" : " value" // 保留(非 ANTHROPIC_* 且非 CLAUDE_* 且非 provider key )
473+ "MY_CUSTOM_VAR" : " value" // 保留(非冲突、 非 ANTHROPIC_*/ CLAUDE_*)
432474 }
475+ // ANTHROPIC_MODEL 被过滤(ANTHROPIC_* 前缀)
476+ // CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR 被过滤(CLAUDE_* 前缀)
477+ }
478+ ```
479+
480+ ** 传递给子进程** (base + provider env):
481+
482+ ``` json
483+ {
484+ "ANTHROPIC_BASE_URL" : " https://open.bigmodel.cn/api/anthropic" ,
485+ "ANTHROPIC_AUTH_TOKEN" : " token123" ,
486+ "ANTHROPIC_MODEL" : " glm-4.7"
433487}
434488```
435489
@@ -481,7 +535,7 @@ func EnsureStopHook(settings map[string]interface{}, hookCommand string) map[str
481535
482536| 文件 | 修改内容 |
483537| ------| ----------|
484- | ` internal/config/config.go ` | 新增 LoadSettings、CleanEnvInSettings 、MergeWithPriority、EnsureStopHook |
538+ | ` internal/config/config.go ` | 新增 LoadSettings、FilterUserEnvForSettings、MergeEnvMaps 、MergeWithPriority、EnsureStopHook |
485539| ` internal/provider/provider.go ` | 重写 SwitchWithHook() 函数逻辑 |
486540| ` internal/config/config_test.go ` | 为新函数添加测试 |
487541
0 commit comments