| name | knowledge-layered-configuration-and-runtime-settings |
| description | 覆盖 Blade 的用户/项目/local/调用级配置合并、workspace 定向解析、运行时 Store 投影和字段持久化路由。 使用时机:新增配置字段、调整覆盖优先级、排查多项目配置串扰、修改模型/Provider/MCP/LSP/插件设置或 Web 设置保存。 不包含:权限决策与 Folder Trust 细节见 permissions-and-workspace-trust,Session 资源冻结见 workspace-resource-snapshots-and-project-instructions。 关键词:ConfigManager, ConfigService, FIELD_ROUTING_TABLE, RuntimeConfig, configActions, settings.local.json, BLADE_MODEL。
|
Module Structure
配置系统将启动读盘、内存投影和磁盘持久化拆成三个边界,并为非启动 workspace 提供按 source project 重建执行配置的专用入口。
Directory Layout
packages/cli/src/config/ConfigManager.ts — 多层读取、信任过滤、归一化、验证和 workspace 定向解析
packages/cli/src/config/ConfigService.ts — 字段/作用域路由、防抖、原子写和并发协调
packages/cli/src/config/defaults.ts — 完整默认配置与默认权限
packages/cli/src/config/types.ts — BladeConfig、RuntimeConfig 和 Provider/权限类型
packages/cli/src/config/runtimeEnvironment.ts — Session 环境变量名称和值校验
packages/cli/src/store/slices/configSlice.ts — 启动进程内存配置投影
packages/cli/src/store/vanilla.ts — 面向 TUI、Server 和命令的配置动作
packages/cli/src/server/routes/config.ts — Web 公开配置投影与更新 API
packages/cli/web/src/store/ConfigStore.ts — workspace 模型选择与权限模式状态
packages/cli/web/src/store/SettingsStore.ts — 通用设置的乐观更新与失败回滚
Key Entry Points
ConfigManager.initialize() — 合并默认、用户、可信项目、local 和调用级设置
ConfigManager.loadWorkspaceModelConfig() — 为目标 source project 重建模型与 Provider 配置
ConfigManager.loadWorkspaceRuntimeSettings() — 为目标 source project 解析 env、Hook 开关、轮次和权限模式
ConfigService.save() — 按字段元数据选择文件、scope 和合并策略
configActions().updateConfig() — 先更新 Store,再持久化,失败时回滚内存
API Surface
ConfigManager
initialize(additionalSettings?) — 生成启动进程的完整 RuntimeConfig
reload() — 使用上次显式运行时覆盖重新读取配置
loadWorkspacePermissions(workspaceRoot, base) — 去除启动项目私有规则并叠加目标 workspace 规则
loadWorkspaceModelConfig(workspaceRoot, base) — 重建目标 workspace 的模型、Provider 与准入配置
loadWorkspaceRuntimeSettings(workspaceRoot, base) — 重建目标 workspace 的执行环境和行为设置
loadWorkspaceMcpServers() / loadWorkspaceLspServers() — 按目标 workspace 隔离可执行服务配置
ConfigService
save(updates, options) — 校验可持久化字段并路由到目标文件
flush() — 立即提交所有防抖写入
appendLocalPermissionRule() / appendLocalPermissionDenyRule() — 对目标 workspace 做原子追加去重
removePluginSetting() — 从指定 scope 删除插件覆盖而非写入相反值
Store Actions
configActions().updateConfig() — 内存先行并在持久化失败时恢复快照
configActions().setPermissionMode() — 仅更新本次运行状态
configActions().addModelWithProvider() — 原子更新 catalog、模型和 Provider 配置
Usage Examples
为 Session 重建目标 workspace 配置
const modelResources = await resolveWorkspaceModelResources(hookConfigRoot, config);
const runtimeConfig: BladeConfig = {
...modelResources.config,
permissions: await configManager.loadWorkspacePermissions(
hookConfigRoot,
modelResources.config.permissions
),
};
通过共享动作持久化 Web 配置
const { updates, options } = parsed.data;
await configActions().updateConfig(updates, options);
Gotchas
ConfigManager.initialize() 捕获任意加载或验证错误后会重置全局模型目录并返回 DEFAULT_CONFIG;调用方看到的可能是“没有模型”而不是原始坏配置,诊断时必须检查启动日志 (packages/cli/src/config/ConfigManager.ts)
- 进程 Store 不是多 workspace 执行配置的权威来源;目标目录与启动 cwd 不同时,模型、MCP、LSP、权限和运行时设置必须分别从用户层与该目标项目重建 (
packages/cli/src/config/ConfigManager.ts, git:3549bb1e)
- 跨 workspace 权限解析会先从传入 base 中剔除启动项目独有规则,再叠加目标项目规则;直接在 base 上追加会把服务器启动目录的本地授权泄漏给其他 Session (
packages/cli/src/config/ConfigManager.ts, packages/cli/tests/integration/config.test.ts, git:7b34bd7e)
config.json 与 settings.json 不共享统一的深合并语义:模型数组整体替换,Provider/MCP/LSP 和插件映射按键合并,permissions 数组追加去重,Hook/env 深合并 (packages/cli/src/config/ConfigManager.ts)
permissionMode 可以从启动参数或可信 settings 进入运行时,但 ConfigService 明确拒绝持久化该字段;会话恢复依赖 Session metadata,不依赖写回配置文件 (packages/cli/src/config/ConfigService.ts, packages/cli/src/store/vanilla.ts)
ConfigService.save() 对未知更新字段直接报错,但写现有文件时保留磁盘中的未知字段;这是输入契约严格、旧版本数据向前兼容的刻意组合 (packages/cli/src/config/ConfigService.ts)
- 防抖保存发生在 timer 回调内,失败只记录到
lastSaveError 而不会回抛到早先的 save() 调用;需要事务语义的权限决策和模型生命周期必须使用 immediate: true 或专用原子动作 (packages/cli/src/config/ConfigService.ts)
- 项目未信任时,项目层只能暴露 Hook 摘要并可设置
disableAllHooks=true 收紧行为;env、模型、MCP、permissionMode 和 allow 规则全部忽略 (packages/cli/src/config/ConfigManager.ts, packages/cli/tests/unit/services/workspace-model-resources.test.ts)
- Web 对指定
workspacePath 调用 setCurrentModel() 时只更新当前客户端选择,不写入启动项目全局配置;无 workspace 参数时才通过 /configs 持久化 (packages/cli/web/src/store/ConfigStore.ts)
- Web 模型发现使用递增 sequence 丢弃迟到响应;移除该保护会让项目 A 的慢响应覆盖已切换到项目 B 的模型列表 (
packages/cli/web/src/store/ConfigStore.ts, packages/cli/web/tests/store/ConfigStore.test.ts)
Architecture
ConfigManager 只负责 bootstrap/read/normalize/validate,Zustand Store 是启动表面的内存状态,ConfigService 是唯一磁盘写入路由;业务代码不应直接修改 JSON 配置 (packages/cli/src/config/ConfigManager.ts, packages/cli/src/config/ConfigService.ts, packages/cli/src/store/slices/configSlice.ts)
- 配置优先级是默认值 < 用户 config/settings < 可信项目 config/settings < local settings < 显式 invocation/CLI;环境变量插值在合并和字符串清理后递归执行 (
packages/cli/src/config/ConfigManager.ts)
resolveWorkspaceModelResources() 在深复制配置后创建 Session 私有 PiModelCatalog;CredentialStore 可共享,但 Provider definitions、endpoint 和 fallback 注册不共享可变状态 (packages/cli/src/agent/resources/WorkspaceModelResources.ts)
Decisions
- 模型凭据从
models 和 modelProviders 分离到 auth.json,旧字段会被验证器主动拒绝而不是静默迁移,避免 endpoint 与 credential 身份混淆 (packages/cli/src/config/ConfigManager.ts, packages/cli/src/config/modelProviders.ts, git:311ba368)
- 配置文件写入统一使用 per-file mutex、read-modify-write 和
0600 原子替换,既保留未知字段,也避免并发 UI、CLI 与审批写入互相覆盖 (packages/cli/src/config/ConfigService.ts)
- 插件来源策略采用 tighten-only 合并:项目层只能开启更严格布尔限制或缩小 allowlist,不能覆盖用户级限制 (
packages/cli/src/config/ConfigManager.ts, packages/cli/src/config/pluginSettings.ts)
Patterns
- 新增持久化字段时,以
FIELD_ROUTING_TABLE 为单一写入路由源,并同步 BladeConfig、DEFAULT_CONFIG、validateConfig() 和公开 API 投影 (packages/cli/src/config/ConfigService.ts, packages/cli/src/config/types.ts, packages/cli/src/config/defaults.ts)
- 涉及多个磁盘字段和运行时 catalog 的动作先保存完整内存快照,任何持久化失败都同时恢复 Store 与 catalog (
packages/cli/src/store/vanilla.ts, packages/cli/src/server/routes/provider.ts)
- 配置中的字符串会递归 trim 并移除包裹反引号,随后
$VAR、${VAR} 和 ${VAR:-default} 在嵌套对象与数组中统一展开 (packages/cli/src/config/ConfigManager.ts, git:9513f0d2)
Consumer Analysis
- SessionRuntime 是最大消费者,按 source project 组合模型、MCP、LSP、权限和 Hook,再冻结到 Runtime (
packages/cli/src/agent/runtime/SessionRuntime.ts)
- Server 路由通过
configActions 更新设置,并为多项目请求传入精确 workspace 身份 (packages/cli/src/server/routes/)
- 插件系统读取 workspace 启用状态与 tighten-only 来源策略,生命周期写入必须指定 scope (
packages/cli/src/plugins/)
- MCP 与 LSP 不复用启动 Store 的项目层,分别调用 workspace 定向解析器 (
packages/cli/src/mcp/, packages/cli/src/lsp/)
- Web Store 维护乐观 UI 状态、请求去重和失败回滚,但服务端配置仍是持久化权威 (
packages/cli/web/src/store/)