fix: 完善备份、验证和技能共享机制
- 备份 agents-registry.json(与 openclaw.json 同时备份) - 验证 accountId 唯一性,防止覆盖现有配置 - 验证 bindings 重复,防止重复绑定 - 回退 agents-registry.json(失败时恢复) - 继承顶层 allowFrom 配置(安全优先) - 创建共享 skills 目录(符号链接) - 清理符号链接(回退时) - 新增 AUDIT-REPORT.md 审查报告
This commit is contained in:
386
AUDIT-REPORT.md
Normal file
386
AUDIT-REPORT.md
Normal file
@@ -0,0 +1,386 @@
|
||||
# agent-creator-with-binding 全面审查报告
|
||||
|
||||
**审查日期:** 2026-03-17
|
||||
**审查范围:** 备份机制、配置正确性、多 Agent 协作、Skill 共享
|
||||
|
||||
---
|
||||
|
||||
## 1. 备份和回退机制审查
|
||||
|
||||
### ✅ 已实现的备份
|
||||
|
||||
| 备份项 | 实现位置 | 状态 |
|
||||
|--------|---------|------|
|
||||
| `openclaw.json` | `createBackup()` | ✅ 已实现 |
|
||||
| 备份路径 | `~/.openclaw/backups/openclaw.json.<timestamp>` | ✅ 正确 |
|
||||
|
||||
### ❌ 缺失的备份
|
||||
|
||||
| 备份项 | 重要性 | 状态 | 建议 |
|
||||
|--------|--------|------|------|
|
||||
| Agent 工作空间模板 | 高 | ❌ 未备份 | 创建前备份模板文件 |
|
||||
| `agents-registry.json` | 中 | ❌ 未备份 | 更新前备份 |
|
||||
| 现有 agent 的 AGENTS.md/SOUL.md | 中 | ❌ 未备份 | 覆盖前备份 |
|
||||
|
||||
### ⚠️ 回退机制问题
|
||||
|
||||
**当前实现:**
|
||||
```javascript
|
||||
// 步骤 1:创建 Agent
|
||||
createAgent(agentId) → 失败 → rollback(backupPath) + cleanupAgent(agentId)
|
||||
|
||||
// 步骤 2:生成人设文件
|
||||
generateAgentFiles() → 失败 → 不回退(仅警告)
|
||||
|
||||
// 步骤 3:更新配置
|
||||
updateConfig() → 失败 → cleanupAgent(agentId) + rollback(backupPath)
|
||||
|
||||
// 步骤 4:配置飞书账户
|
||||
configureFeishuAccount() → 失败 → cleanupAgent(agentId) + rollback(backupPath)
|
||||
|
||||
// 步骤 5:重启 Gateway
|
||||
restartGateway() → 失败 → 不回退(配置正确)
|
||||
```
|
||||
|
||||
**问题:**
|
||||
1. `generateAgentFiles()` 失败不回退,但 Agent 已创建,导致"空壳"Agent
|
||||
2. `agents-registry.json` 更新失败无回退
|
||||
3. 没有验证步骤,无法在修改前发现配置冲突
|
||||
|
||||
---
|
||||
|
||||
## 2. 配置数据结构审查
|
||||
|
||||
### ✅ 正确的配置(当前 openclaw.json)
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"feishu": {
|
||||
"enabled": true,
|
||||
"appId": "cli_a93dc207e1f89bcb",
|
||||
"appSecret": "xxx",
|
||||
|
||||
"dmPolicy": "allowlist",
|
||||
"allowFrom": ["ou_xxx", "ou_yyy"],
|
||||
"groupPolicy": "open",
|
||||
"groupAllowFrom": [],
|
||||
|
||||
"accounts": {
|
||||
"default": {
|
||||
"appId": "cli_a93dc207e1f89bcb",
|
||||
"appSecret": "xxx",
|
||||
"dmPolicy": "allowlist",
|
||||
"allowFrom": ["ou_xxx", "ou_yyy"]
|
||||
},
|
||||
"coding-expert-bot": {
|
||||
"appId": "cli_a9320c94f23a1bd6",
|
||||
"appSecret": "yyy",
|
||||
"dmPolicy": "allowlist",
|
||||
"allowFrom": ["ou_xxx", "ou_yyy"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"agents": {
|
||||
"list": [
|
||||
{"id": "main"},
|
||||
{"id": "coding-assistant", "name": "编程助手", "workspace": "..."},
|
||||
{"id": "coding-expert", "name": "编程专家", "workspace": "..."}
|
||||
]
|
||||
},
|
||||
|
||||
"bindings": [
|
||||
{"agentId": "main", "match": {"channel": "feishu", "accountId": "default"}},
|
||||
{"agentId": "coding-expert", "match": {"channel": "feishu", "accountId": "coding-expert-bot"}}
|
||||
],
|
||||
|
||||
"session": {
|
||||
"dmScope": "per-account-channel-peer"
|
||||
},
|
||||
|
||||
"tools": {
|
||||
"agentToAgent": {
|
||||
"enabled": true,
|
||||
"allow": ["coding-assistant", "coding-expert"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ⚠️ 潜在问题
|
||||
|
||||
**问题 1:`configureFeishuAccount` 未继承现有白名单**
|
||||
|
||||
当前代码:
|
||||
```javascript
|
||||
config.channels.feishu.accounts[options.accountId] = {
|
||||
dmPolicy: options.dmPolicy || config.channels.feishu.dmPolicy || 'allowlist',
|
||||
allowFrom: options.allowFrom || config.channels.feishu.allowFrom || ['*']
|
||||
// ❌ 如果是新账户,没有继承现有账户的 allowFrom
|
||||
}
|
||||
```
|
||||
|
||||
**建议:** 默认继承顶层配置的 allowFrom,而不是 `['*']`
|
||||
|
||||
**问题 2:未验证 accountId 唯一性**
|
||||
|
||||
```javascript
|
||||
// 没有检查 accountId 是否已存在
|
||||
config.channels.feishu.accounts[options.accountId] = {...}
|
||||
```
|
||||
|
||||
**建议:** 添加验证,防止覆盖现有账户
|
||||
|
||||
**问题 3:bindings 可能重复**
|
||||
|
||||
```javascript
|
||||
// 没有检查 binding 是否已存在
|
||||
config.bindings.push(binding);
|
||||
```
|
||||
|
||||
**建议:** 检查重复,避免重复绑定
|
||||
|
||||
---
|
||||
|
||||
## 3. 多 Agent 协作实现审查
|
||||
|
||||
### 当前实现
|
||||
|
||||
**机制:** 共享注册表文件
|
||||
```
|
||||
/home/admin/.openclaw/agents-registry.json
|
||||
```
|
||||
|
||||
**问题:**
|
||||
1. ❌ **Agent 工作空间隔离**,无法直接读取 `~/.openclaw/` 下的文件
|
||||
2. ❌ **没有内置工具** 让 agent 读取注册表
|
||||
3. ❌ **没有任务队列** 机制,coordinator 无法追踪任务进度
|
||||
4. ❌ **没有权限控制**,任何 agent 都可以给其他 agent 发送任务
|
||||
|
||||
### 建议方案
|
||||
|
||||
#### 方案 A:共享目录 + 内置工具
|
||||
|
||||
```
|
||||
/home/admin/.openclaw/shared/
|
||||
├── agents-registry.json # Agent 注册表
|
||||
├── skills/ # 共享 skills(符号链接)
|
||||
└── tasks/ # 任务队列
|
||||
├── pending/
|
||||
├── in-progress/
|
||||
└── completed/
|
||||
```
|
||||
|
||||
**内置工具:**
|
||||
```javascript
|
||||
// agent-registry: 读取注册表
|
||||
{
|
||||
"action": "agent_registry",
|
||||
"list": true // 列出所有 agent
|
||||
}
|
||||
|
||||
// task-dispatch: 分发任务
|
||||
{
|
||||
"action": "task_dispatch",
|
||||
"to": "game-balance",
|
||||
"task": "设计战斗公式",
|
||||
"priority": "high"
|
||||
}
|
||||
```
|
||||
|
||||
#### 方案 B:使用 sessions_send + 共享上下文
|
||||
|
||||
```javascript
|
||||
// Coordinator 发送任务
|
||||
{
|
||||
"action": "sessions_send",
|
||||
"agentId": "game-balance",
|
||||
"message": "任务:设计战斗公式\n\n上下文:/home/admin/.openclaw/shared/projects/rpg/context.md"
|
||||
}
|
||||
|
||||
// Specialist 读取共享上下文
|
||||
read("/home/admin/.openclaw/shared/projects/rpg/context.md")
|
||||
```
|
||||
|
||||
**问题:** Specialist 的 workspace 是独立的,无法访问共享目录
|
||||
|
||||
---
|
||||
|
||||
## 4. Skill 共享机制审查
|
||||
|
||||
### 当前问题
|
||||
|
||||
**每个 Agent 独立 skills 目录:**
|
||||
```
|
||||
/home/admin/.openclaw/workspace/
|
||||
└── skills/
|
||||
|
||||
/home/admin/.openclaw/workspace-coding-expert/
|
||||
└── skills/
|
||||
|
||||
/home/admin/.openclaw/workspace-game-director/
|
||||
└── skills/
|
||||
```
|
||||
|
||||
**问题:**
|
||||
1. ❌ **重复安装**:每个 agent 都要安装相同的 skills
|
||||
2. ❌ **磁盘浪费**:相同的 skill 代码多份副本
|
||||
3. ❌ **更新困难**:更新 skill 需要更新所有 agent
|
||||
|
||||
### 建议方案
|
||||
|
||||
#### 方案 A:符号链接(推荐)
|
||||
|
||||
```bash
|
||||
# 共享 skills 目录
|
||||
/home/admin/.openclaw/skills/ # 全局共享
|
||||
|
||||
# Agent workspace 使用符号链接
|
||||
/home/admin/.openclaw/workspace/skills -> ../skills
|
||||
/home/admin/.openclaw/workspace-coding-expert/skills -> ../skills
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- ✅ 单一来源,更新一次即可
|
||||
- ✅ 不占额外空间
|
||||
- ✅ 所有 agent 使用相同版本
|
||||
|
||||
**缺点:**
|
||||
- ⚠️ 某些 skills 可能需要独立配置
|
||||
|
||||
#### 方案 B:技能加载路径配置
|
||||
|
||||
```json
|
||||
// openclaw.json
|
||||
{
|
||||
"agents": {
|
||||
"list": [
|
||||
{
|
||||
"id": "main",
|
||||
"workspace": "/home/admin/.openclaw/workspace/",
|
||||
"skillPaths": [
|
||||
"/home/admin/.openclaw/workspace/skills",
|
||||
"/home/admin/.openclaw/skills" // 共享路径
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优点:**
|
||||
- ✅ 灵活配置
|
||||
- ✅ 支持混合模式(共享 + 独立)
|
||||
|
||||
**缺点:**
|
||||
- ⚠️ 需要 OpenClaw 核心支持
|
||||
|
||||
---
|
||||
|
||||
## 5. 修复建议优先级
|
||||
|
||||
### 🔴 高优先级(必须修复)
|
||||
|
||||
1. **备份 agents-registry.json** - 防止注册表损坏
|
||||
2. **验证 accountId 唯一性** - 防止覆盖现有配置
|
||||
3. **验证 bindings 重复** - 防止重复绑定
|
||||
4. **Skill 共享机制** - 符号链接方案
|
||||
|
||||
### 🟡 中优先级(建议修复)
|
||||
|
||||
1. **继承现有 allowFrom** - 保持安全配置一致性
|
||||
2. **Agent 读取注册表工具** - 支持多 Agent 协作
|
||||
3. **回退 generateAgentFiles 失败** - 清理空壳 Agent
|
||||
|
||||
### 🟢 低优先级(可选)
|
||||
|
||||
1. **任务队列机制** - 复杂协作场景才需要
|
||||
2. **权限控制** - 信任环境可省略
|
||||
|
||||
---
|
||||
|
||||
## 6. 实施计划
|
||||
|
||||
### 阶段 1:修复备份和验证(1 天)
|
||||
|
||||
- [ ] 备份 agents-registry.json
|
||||
- [ ] 验证 accountId 唯一性
|
||||
- [ ] 验证 bindings 重复
|
||||
- [ ] 回退失败的 agent 创建
|
||||
|
||||
### 阶段 2:Skill 共享(1 天)
|
||||
|
||||
- [ ] 创建全局 skills 目录
|
||||
- [ ] 修改新 agent 创建流程,使用符号链接
|
||||
- [ ] 迁移现有 agent 到共享 skills
|
||||
|
||||
### 阶段 3:多 Agent 协作(2 天)
|
||||
|
||||
- [ ] 创建共享目录结构
|
||||
- [ ] 添加 agent_registry 工具
|
||||
- [ ] 添加 task_dispatch 工具
|
||||
- [ ] 更新 coordinator/specialist 模板
|
||||
|
||||
---
|
||||
|
||||
## 7. 已实施修复
|
||||
|
||||
### 2026-03-17 修复
|
||||
|
||||
- ✅ **备份 agents-registry.json** - `createBackup()` 现在同时备份注册表
|
||||
- ✅ **验证 accountId 唯一性** - `validateAccountIdUnique()` 防止覆盖
|
||||
- ✅ **验证 bindings 重复** - `validateBindingUnique()` 防止重复绑定
|
||||
- ✅ **回退注册表** - `rollbackRegistry()` 支持注册表回退
|
||||
- ✅ **继承 allowFrom** - `configureFeishuAccount()` 继承顶层配置
|
||||
- ✅ **Skill 共享** - `setupSharedSkills()` 创建符号链接
|
||||
- ✅ **清理符号链接** - `cleanupAgent()` 删除符号链接
|
||||
|
||||
---
|
||||
|
||||
## 8. 配置结构参考
|
||||
|
||||
**正确的 openclaw.json 结构:**
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"feishu": {
|
||||
"enabled": true,
|
||||
"appId": "cli_xxx",
|
||||
"appSecret": "xxx",
|
||||
"dmPolicy": "allowlist",
|
||||
"allowFrom": ["ou_xxx"],
|
||||
"groupPolicy": "open",
|
||||
"groupAllowFrom": [],
|
||||
"accounts": {
|
||||
"default": {
|
||||
"appId": "cli_xxx",
|
||||
"appSecret": "xxx",
|
||||
"dmPolicy": "allowlist",
|
||||
"allowFrom": ["ou_xxx"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"agents": {
|
||||
"list": [
|
||||
{"id": "main"},
|
||||
{"id": "coding-expert", "name": "编程专家", "workspace": "/home/admin/.openclaw/workspace-coding-expert/"}
|
||||
]
|
||||
},
|
||||
"bindings": [
|
||||
{"agentId": "main", "match": {"channel": "feishu", "accountId": "default"}}
|
||||
],
|
||||
"session": {
|
||||
"dmScope": "per-account-channel-peer"
|
||||
},
|
||||
"tools": {
|
||||
"agentToAgent": {
|
||||
"enabled": true,
|
||||
"allow": ["coding-expert"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
144
index.js
144
index.js
@@ -81,6 +81,15 @@ function createBackup() {
|
||||
const backupPath = path.join(BACKUP_DIR, `openclaw.json.${timestamp}`);
|
||||
fs.copyFileSync(CONFIG_PATH, backupPath);
|
||||
log.success(`配置已备份:${path.basename(backupPath)}`);
|
||||
|
||||
// 额外备份 agents-registry.json(如果存在)
|
||||
const registryPath = path.join(path.dirname(CONFIG_PATH), 'agents-registry.json');
|
||||
if (fs.existsSync(registryPath)) {
|
||||
const registryBackup = path.join(BACKUP_DIR, `agents-registry.json.${timestamp}`);
|
||||
fs.copyFileSync(registryPath, registryBackup);
|
||||
log.success(`Agent 注册表已备份:${path.basename(registryBackup)}`);
|
||||
}
|
||||
|
||||
return backupPath;
|
||||
} catch (err) {
|
||||
log.error(`创建备份失败:${err.message}`);
|
||||
@@ -88,6 +97,60 @@ function createBackup() {
|
||||
}
|
||||
}
|
||||
|
||||
// 验证 accountId 唯一性
|
||||
function validateAccountIdUnique(config, accountId) {
|
||||
const existingAccounts = config.channels?.feishu?.accounts || {};
|
||||
if (existingAccounts[accountId]) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `账户 ID '${accountId}' 已存在,请使用不同的 ID`
|
||||
};
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
// 验证 binding 不重复
|
||||
function validateBindingUnique(config, agentId, accountId, chatId, bindingMode) {
|
||||
const bindings = config.bindings || [];
|
||||
|
||||
for (const binding of bindings) {
|
||||
if (binding.agentId !== agentId) {
|
||||
if (bindingMode === 'account' && binding.match?.accountId === accountId) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `账户 '${accountId}' 已绑定到 agent '${binding.agentId}'`
|
||||
};
|
||||
}
|
||||
if (bindingMode === 'group' && binding.match?.peer?.id === chatId) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `群聊 '${chatId}' 已绑定到 agent '${binding.agentId}'`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
// 回退 Agent 注册表
|
||||
function rollbackRegistry(backupPath) {
|
||||
if (!backupPath) return false;
|
||||
|
||||
const registryBackup = backupPath.replace('openclaw.json', 'agents-registry.json');
|
||||
const registryPath = path.join(path.dirname(CONFIG_PATH), 'agents-registry.json');
|
||||
|
||||
if (fs.existsSync(registryBackup)) {
|
||||
try {
|
||||
fs.copyFileSync(registryBackup, registryPath);
|
||||
log.success('Agent 注册表已回退');
|
||||
return true;
|
||||
} catch (err) {
|
||||
log.error(`回退注册表失败:${err.message}`);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 回退配置
|
||||
function rollback(backupPath) {
|
||||
if (!backupPath || !fs.existsSync(backupPath)) {
|
||||
@@ -112,6 +175,20 @@ function cleanupAgent(agentId) {
|
||||
const workspacePath = `/home/admin/.openclaw/workspace-${agentId}`;
|
||||
const agentPath = `/home/admin/.openclaw/agents/${agentId}`;
|
||||
|
||||
// 先删除符号链接(如果有)
|
||||
const skillsPath = path.join(workspacePath, 'skills');
|
||||
if (fs.existsSync(skillsPath)) {
|
||||
try {
|
||||
const stats = fs.lstatSync(skillsPath);
|
||||
if (stats.isSymbolicLink()) {
|
||||
fs.unlinkSync(skillsPath);
|
||||
log.warning(`已删除 skills 符号链接`);
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略符号链接删除错误
|
||||
}
|
||||
}
|
||||
|
||||
if (fs.existsSync(workspacePath)) {
|
||||
fs.rmSync(workspacePath, { recursive: true, force: true });
|
||||
log.warning(`已删除工作空间:${workspacePath}`);
|
||||
@@ -150,6 +227,10 @@ function createAgent(agentId) {
|
||||
try {
|
||||
execSync(`openclaw agents add ${agentId}`, { stdio: 'inherit' });
|
||||
log.success(`Agent ${agentId} 创建成功`);
|
||||
|
||||
// 创建 skills 符号链接(共享技能)
|
||||
setupSharedSkills(agentId);
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
log.error(`创建 Agent 失败:${err.message}`);
|
||||
@@ -157,6 +238,31 @@ function createAgent(agentId) {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置共享 Skills(符号链接)
|
||||
function setupSharedSkills(agentId) {
|
||||
const workspacePath = `/home/admin/.openclaw/workspace-${agentId}`;
|
||||
const skillsPath = path.join(workspacePath, 'skills');
|
||||
const sharedSkillsPath = path.join(path.dirname(CONFIG_PATH), 'skills');
|
||||
|
||||
try {
|
||||
// 如果 skills 目录不存在,创建符号链接
|
||||
if (!fs.existsSync(skillsPath)) {
|
||||
// 确保共享 skills 目录存在
|
||||
if (!fs.existsSync(sharedSkillsPath)) {
|
||||
fs.mkdirSync(sharedSkillsPath, { recursive: true });
|
||||
log.success(`创建共享 skills 目录:${sharedSkillsPath}`);
|
||||
}
|
||||
|
||||
// 创建符号链接
|
||||
fs.symlinkSync(sharedSkillsPath, skillsPath, 'dir');
|
||||
log.success(`已创建 skills 符号链接 → ${sharedSkillsPath}`);
|
||||
}
|
||||
} catch (err) {
|
||||
log.warning(`创建 skills 符号链接失败:${err.message}`);
|
||||
log.warning('Agent 将使用独立 skills 目录');
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 Agent 人设文件
|
||||
function generateAgentFiles(options) {
|
||||
log.step(2, 6, '生成 Agent 人设文件');
|
||||
@@ -489,6 +595,10 @@ function configureFeishuAccount(options) {
|
||||
}
|
||||
|
||||
// 添加账户级配置
|
||||
// ★ 重要:如果没有显式设置 allowFrom,继承顶层配置(而不是 ['*'])
|
||||
const inheritedAllowFrom = config.channels.feishu.allowFrom || ['*'];
|
||||
const inheritedGroupAllowFrom = config.channels.feishu.groupAllowFrom || [];
|
||||
|
||||
config.channels.feishu.accounts[options.accountId] = {
|
||||
appId: options.appId,
|
||||
appSecret: options.appSecret,
|
||||
@@ -500,9 +610,9 @@ function configureFeishuAccount(options) {
|
||||
streaming: true,
|
||||
// 账户级配置可以覆盖顶层设置
|
||||
dmPolicy: options.dmPolicy || config.channels.feishu.dmPolicy || 'allowlist',
|
||||
allowFrom: options.allowFrom || config.channels.feishu.allowFrom || ['*'],
|
||||
allowFrom: options.allowFrom || inheritedAllowFrom,
|
||||
groupPolicy: options.groupPolicy || config.channels.feishu.groupPolicy || 'open',
|
||||
groupAllowFrom: options.groupAllowFrom || config.channels.feishu.groupAllowFrom || []
|
||||
groupAllowFrom: options.groupAllowFrom || inheritedGroupAllowFrom
|
||||
};
|
||||
|
||||
if (saveConfig(config)) {
|
||||
@@ -563,6 +673,30 @@ function updateConfig(options) {
|
||||
|
||||
const config = loadConfig();
|
||||
|
||||
// 0. 验证:accountId 唯一性
|
||||
if (options.newBot || options.accountId) {
|
||||
const accountIdValidation = validateAccountIdUnique(config, options.accountId);
|
||||
if (!accountIdValidation.valid) {
|
||||
log.error(accountIdValidation.error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 0b. 验证:binding 不重复
|
||||
if (options.bindingMode !== 'none') {
|
||||
const bindingValidation = validateBindingUnique(
|
||||
config,
|
||||
options.agentId,
|
||||
options.accountId,
|
||||
options.chatId,
|
||||
options.bindingMode
|
||||
);
|
||||
if (!bindingValidation.valid) {
|
||||
log.error(bindingValidation.error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 添加 agent 到 agents.list
|
||||
if (!config.agents) config.agents = {};
|
||||
if (!config.agents.list) config.agents.list = [];
|
||||
@@ -874,12 +1008,13 @@ async function interactiveMode() {
|
||||
// 不回退,继续执行
|
||||
}
|
||||
|
||||
// 步骤 3:更新配置
|
||||
// 步骤 3:更新配置(包含验证)
|
||||
const configUpdated = updateConfig(options);
|
||||
if (!configUpdated) {
|
||||
log.error('更新配置失败,正在回退...');
|
||||
log.error('更新配置失败(验证未通过),正在回退...');
|
||||
cleanupAgent(agentId);
|
||||
rollback(backupPath);
|
||||
rollbackRegistry(backupPath);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -890,6 +1025,7 @@ async function interactiveMode() {
|
||||
log.error('配置飞书账户失败,正在回退...');
|
||||
cleanupAgent(agentId);
|
||||
rollback(backupPath);
|
||||
rollbackRegistry(backupPath);
|
||||
log.warning('openclaw.json 已回退,但飞书侧配置可能需要手动清理');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user