Code Reader
首页
帮助
设计文档
首页
帮助
设计文档
  • 配置系统架构

配置系统架构

Moltbot 的配置加载、验证、迁移和热重载机制

概述

配置系统是 Moltbot 的基础组件,负责加载、验证、管理配置文件。支持环境变量替换、包含机制、遗留配置迁移和运行时热重载。

设计目标

  • 层次结构: 清晰的配置层次和类型安全
  • 环境变量: 支持 ${VAR} 语法替换
  • 包含机制: 支持 $include 指令合并多个文件
  • 迁移系统: 自动迁移遗留配置
  • 热重载: 部分配置支持无需重启的热重载

配置结构层次

主配置类型

文件: src/config/types.ts

MoltbotConfig
├── meta                    // 元数据(版本、时间戳)
├── env                     // 环境变量配置
│   ├── shellEnv            // Shell 环境变量加载
│   └── vars                // 自定义环境变量
├── wizard                  // 向导状态
├── diagnostics             // 诊断配置
├── logging                 // 日志配置
├── update                  // 更新通道配置
├── browser                 // 浏览器控制配置
├── ui                      // UI 配置
├── auth                    // 认证配置
├── models                  // 模型目录
├── nodeHost                // 节点主机配置
├── agents                  // 代理配置
│   ├── defaults            // 默认值
│   └── list               // 代理列表
├── tools                   // 工具配置
├── bindings                // 绑定配置
├── broadcast               // 广播配置
├── audio                   // 音频配置
├── messages                // 消息配置
├── commands                // 命令配置
├── approvals               // 审批配置
├── session                 // 会话配置
├── cron                    // Cron 任务配置
├── hooks                   // 钩子配置
├── web                     // Web 提供商配置
├── channels                // 消息通道配置
├── discovery               // 发现配置
├── canvasHost              // Canvas 主机配置
├── talk                    // Talk 语音配置
├── gateway                 // 网关配置
├── skills                  // 技能配置
└── plugins                 // 插件配置

配置加载流程

加载流程图

配置路径解析

文件: src/config/paths.ts

// 路径解析优先级:
// 1. MOLTBOT_CONFIG_PATH (新)
// 2. CLAWDBOT_CONFIG_PATH (遗留)
// 3. $MOLTBOT_STATE_DIR/moltbot.json
// 4. $CLAWDBOT_STATE_DIR/moltbot.json
// 5. ~/.moltbot/moltbot.json
// 6. ~/.clawdbot/moltbot.json

export function resolveConfigPath(
  env: NodeJS.ProcessEnv,
  stateDir: string,
  homedir: () => string
): string

配置包含机制

文件: src/config/includes.ts

// 支持 $include 指令:
{
  "$include": "./base.json5"        // 单文件
  "$include": ["./a.json5", "./b.json5"] // 多文件合并
}

// 特性:
// - 深度合并 (数组连接, 对象递归合并)
// - 循环引用检测 (CircularIncludeError)
// - 最大深度限制 (MAX_INCLUDE_DEPTH = 10)
// - 支持兄弟键合并

环境变量替换

文件: src/config/env/env-substitution.ts

// 语法: ${VAR_NAME}
{
  apiKey: "${OPENAI_API_KEY}",
  token: "${CLAWDBOT_GATEWAY_TOKEN}"
}

// 特性:
// - 仅匹配大写环境变量: [A-Z_][A-Z0-9_]*
// - 转义: $${VAR} -> ${VAR}
// - 缺失时抛出 MissingEnvVarError
// - config.env 中的变量可被 ${VAR} 引用

配置管理

配置保存

文件: src/config/io.ts

async function writeConfigFile(cfg: MoltbotConfig) {
  // 1. 验证配置
  const validated = validateConfigObjectWithPlugins(cfg);
  if (!validated.ok) throw new Error(...);

  // 2. 创建目录 (权限 0o700)
  await deps.fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });

  // 3. 添加版本元数据
  const json = JSON.stringify(stampConfigVersion(cfg), null, 2);

  // 4. 写入临时文件
  const tmp = {path}.{pid}.{uuid}.tmp;
  await deps.fs.promises.writeFile(tmp, json, { mode: 0o600 });

  // 5. 旋转备份 (保留 5 个备份)
  await rotateConfigBackups(configPath);

  // 6. 原子替换
  await deps.fs.promises.rename(tmp, configPath);
}

配置快照和哈希

export type ConfigFileSnapshot = {
  path: string;
  exists: boolean;
  raw: string | null;           // 原始内容
  parsed: unknown;               // 解析后内容
  valid: boolean;                // 是否验证通过
  config: MoltbotConfig;         // 最终配置
  hash: string;                  // SHA256 哈希
  issues: ConfigValidationIssue[];
  warnings: ConfigIssue[];
  legacyIssues: LegacyConfigIssue[];
};

function hashConfigRaw(raw: string | null): string {
  return crypto.createHash("sha256").update(raw ?? "").digest("hex");
}

配置缓存

// 内存缓存 (默认 200ms)
const DEFAULT_CONFIG_CACHE_MS = 200;
let configCache: {
  configPath: string;
  expiresAt: number;
  config: MoltbotConfig;
} | null = null;

// 可通过环境变量禁用:
// CLAWDBOT_DISABLE_CONFIG_CACHE=1
// CLAWDBOT_CONFIG_CACHE_MS=0

验证和迁移

验证流程

文件: src/config/validation.ts

export function validateConfigObjectWithPlugins(raw: unknown) {
  // 1. 基础 Zod 验证
  const validated = MoltbotSchema.safeParse(raw);
  if (!validated.success) return { ok: false, issues: ... };

  // 2. 查找重复代理目录
  const duplicates = findDuplicateAgentDirs(config);
  if (duplicates.length > 0) return { ok: false, issues: ... };

  // 3. 验证头像路径
  const avatarIssues = validateIdentityAvatar(config);
  if (avatarIssues.length > 0) return { ok: false, issues: ... };

  // 4. 插件验证
  // - 检查插件是否存在
  // - 验证插件配置 Schema
  // - 验证内存槽位
  // - 验证通道 ID

  // 5. 通道验证
  // - 验证心跳目标

  return { ok: true, config, warnings: [] };
}

遗留配置迁移

文件: src/config/legacy.migrations.ts

// 迁移分为 3 部分
export const LEGACY_CONFIG_MIGRATIONS = [
  ...LEGACY_CONFIG_MIGRATIONS_PART_1,  // 基础重命名
  ...LEGACY_CONFIG_MIGRATIONS_PART_2,  // 复杂重构
  ...LEGACY_CONFIG_MIGRATIONS_PART_3,  // 细节清理
];

// 示例迁移:
{
  id: "providers->channels",
  describe: "Move provider config sections to channels.*",
  apply: (raw, changes) => {
    // 将 whatsapp, telegram, discord 等移到 channels.* 下
    const legacyKeys = ["whatsapp", "telegram", "discord", ...];
    for (const key of legacyKeys) {
      if (raw[key]) {
        raw.channels[key] = raw[key];
        delete raw[key];
        changes.push(`Moved ${key} → channels.${key}.`);
      }
    }
  }
}

遗留配置规则

文件: src/config/legacy.rules.ts

// 用于检测未迁移的遗留配置
export const LEGACY_CONFIG_RULES = [
  {
    path: ["whatsapp"],
    message: "whatsapp config moved to channels.whatsapp (auto-migrated on load).",
  },
  {
    path: ["routing", "allowFrom"],
    message: "routing.allowFrom was removed; use channels.whatsapp.allowFrom instead...",
  },
  // ...
];

热重载机制

重载模式

文件: src/gateway/config-reload.ts

type GatewayReloadMode = "off" | "restart" | "hot" | "hybrid";

const DEFAULT_RELOAD_SETTINGS = {
  mode: "hybrid",     // 推荐
  debounceMs: 300,     // 防抖延迟
};

重载决策流程

重载规则

// 基础规则
const BASE_RELOAD_RULES = [
  { prefix: "hooks.gmail", kind: "hot", actions: ["restart-gmail-watcher"] },
  { prefix: "hooks", kind: "hot", actions: ["reload-hooks"] },
  { prefix: "agents.defaults.heartbeat", kind: "hot", actions: ["restart-heartbeat"] },
  { prefix: "cron", kind: "hot", actions: ["restart-cron"] },
  { prefix: "browser", kind: "hot", actions: ["restart-browser-control"] },

  // 忽略的配置
  { prefix: "gateway.remote", kind: "none" },
  { prefix: "gateway.reload", kind: "none" },
];

// 重启触发
const BASE_RELOAD_RULES_TAIL = [
  { prefix: "plugins", kind: "restart" },
  { prefix: "gateway", kind: "restart" },
  { prefix: "discovery", kind: "restart" },
  { prefix: "canvasHost", kind: "restart" },
];

运行时覆盖

覆盖机制

文件: src/config/runtime-overrides.ts

let overrides: { [key: string]: unknown } = {};

// 设置覆盖
export function setConfigOverride(pathRaw: string, value: unknown) {
  const parsed = parseConfigPath(pathRaw);
  setConfigValueAtPath(overrides, parsed.path, value);
}

// 应用覆盖 (在所有加载流程最后)
export function applyConfigOverrides(cfg: MoltbotConfig): MoltbotConfig {
  if (!overrides || Object.keys(overrides).length === 0) return cfg;
  return mergeOverrides(cfg, overrides);
}

覆盖优先级

优先级顺序 (从低到高):

  1. 配置文件 (moltbot.json)
  2. $include 包含的文件
  3. config.env 定义的变量 (添加到 process.env)
  4. 环境变量替换 (${VAR})
  5. 运行时覆盖 (setConfigOverride)

配置差异检测

// 深度对比配置
export function diffConfigPaths(prev: unknown, next: unknown, prefix = ""): string[] {
  if (prev === next) return [];

  if (isPlainObject(prev) && isPlainObject(next)) {
    const keys = new Set([...Object.keys(prev), ...Object.keys(next)]);
    for (const key of keys) {
      const childPaths = diffConfigPaths(prev[key], next[key], `${prefix}.${key}`);
      paths.push(...childPaths);
    }
    return paths;
  }

  return [prefix || "<root>"];
}

代码路径引用

功能文件路径
主类型定义src/config/types.ts
配置加载src/config/io.ts
配置验证src/config/validation.ts
Zod Schemasrc/config/zod-schema.ts
路径解析src/config/paths.ts
包含机制src/config/includes.ts
环境变量替换src/config/env/env-substitutional.ts
遗留迁移src/config/legacy.migrations.ts
遗留规则src/config/legacy.rules.ts
运行时覆盖src/config/runtime-overrides.ts
热重载src/gateway/config-reload.ts