sophhub 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/ai-cs-admin/.config.json +6 -1
- package/agents/ai-cs-qa/.config.json +9 -1
- package/agents/ai-cs-qa/AGENTS.md +43 -15
- package/agents/ai-cs-qa/scripts/setup_links.sh +39 -0
- package/agents/vip-admin/.config.json +51 -0
- package/agents/vip-admin/AGENTS.md +331 -0
- package/agents/vip-admin/BOOTSTRAP.md +21 -0
- package/agents/vip-admin/HEARTBEAT.md +19 -0
- package/agents/vip-admin/IDENTITY.md +6 -0
- package/agents/vip-admin/MEMORY.md +29 -0
- package/agents/vip-admin/SOUL.md +25 -0
- package/agents/vip-admin/TOOLS.md +102 -0
- package/agents/vip-admin/USER.md +17 -0
- package/agents/vip-qa/.config.json +58 -0
- package/agents/vip-qa/AGENTS.md +312 -0
- package/agents/vip-qa/BOOTSTRAP.md +74 -0
- package/agents/vip-qa/HEARTBEAT.md +23 -0
- package/agents/vip-qa/IDENTITY.md +6 -0
- package/agents/vip-qa/MEMORY.md +23 -0
- package/agents/vip-qa/SOUL.md +34 -0
- package/agents/vip-qa/TOOLS.md +41 -0
- package/agents/vip-qa/USER.md +16 -0
- package/agents/vip-qa/scripts/setup_links.sh +39 -0
- package/package.json +1 -1
- package/skills/agent-install/skill.json +27 -0
- package/skills/agent-install/src/SKILL.md +238 -0
- package/skills/agent-install/src/pyproject.toml +6 -0
- package/skills/agent-install/src/scripts/backup_agent.py +120 -0
- package/skills/agent-install/src/scripts/check_installed.py +479 -0
- package/skills/agent-install/src/scripts/common.py +487 -0
- package/skills/agent-install/src/scripts/copy_agent_files.py +59 -0
- package/skills/agent-install/src/scripts/list_agents.py +285 -0
- package/skills/agent-install/src/scripts/resolve_install_params.py +90 -0
- package/skills/agent-install/src/scripts/update_agent_md.py +76 -0
- package/skills/agent-install/src/scripts/update_openclaw.py +183 -0
- package/skills/agent-install/src/scripts/verify_download.py +148 -0
- package/skills/bot-api-status/skill.json +36 -0
- package/skills/bot-api-status/src/SKILL.md +89 -0
- package/skills/bot-api-status/src/pyproject.toml +5 -0
- package/skills/bot-api-status/src/scripts/secret.py +481 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# TOOLS.md - 工具备忘(问答 Agent)
|
|
2
|
+
|
|
3
|
+
## 可用工具
|
|
4
|
+
|
|
5
|
+
| 工具 | 用途 | 权限 |
|
|
6
|
+
|------|------|------|
|
|
7
|
+
| `read` | 读取知识库文档、图片 | knowledge/ 只读 |
|
|
8
|
+
| `image` | 调用 VLM 识别用户发来的图片 | 只读 |
|
|
9
|
+
| `write` | 写入自己的实例记忆(日志、反馈、画像、摘要等) | 仅限本实例 `memory/` 下允许的文件与根目录 `MEMORY.md`(不得写入 `knowledge/`;具体以 `runtime/agent-binding.json` 的 `allowedWritePatterns` 为准) |
|
|
10
|
+
| `grep` | 搜索知识库文档内容 | knowledge/ 只读 |
|
|
11
|
+
|
|
12
|
+
## 禁用工具
|
|
13
|
+
|
|
14
|
+
- `exec` — 已在平台层禁用,无法执行任何 shell 命令
|
|
15
|
+
|
|
16
|
+
## 工作目录
|
|
17
|
+
|
|
18
|
+
**路径基目录:** 下文相对路径均以**本问答实例 workspace 根目录**为根(见 `AGENTS.md` 的「路径基目录」)。
|
|
19
|
+
|
|
20
|
+
- **知识库目录:** `knowledge/`(symlink,指向主 Agent 的知识库,只读)
|
|
21
|
+
- **FAQ 文件:** `knowledge/FAQ.md`(只读)
|
|
22
|
+
- **索引文件:** `knowledge/INDEX.md`(只读)
|
|
23
|
+
- **图片目录:** `knowledge/images/media/`(只读)
|
|
24
|
+
- **短期记忆:** `memory/`(可读写)
|
|
25
|
+
- **会话日志:** `memory/YYYY-MM-DD.md`(可写,按需创建)
|
|
26
|
+
- **画像记忆:** `memory/profile.json`(可写)
|
|
27
|
+
- **偏好备忘:** `memory/preferences.md`(可写)
|
|
28
|
+
- **启动摘要:** `memory/summary.md`(可写;通常由离线脚本生成/更新,但文件仍属于实例记忆区)
|
|
29
|
+
- **长期记忆:** `MEMORY.md`(可写,仅限已确认画像和稳定偏好)
|
|
30
|
+
- **用户反馈:** `memory/feedback-YYYY-MM-DD.md`(可写)
|
|
31
|
+
- **安全日志:** `memory/security-log.md`(可写)
|
|
32
|
+
- **FAQ 建议:** `memory/faq-suggestions.md`(可写)
|
|
33
|
+
|
|
34
|
+
## 注意事项
|
|
35
|
+
|
|
36
|
+
- `read` 工具可直接读取 `.md`、`.txt`、`.pdf`、`.xlsx` 和图片文件
|
|
37
|
+
- `grep` 工具可搜索 `knowledge/` 目录下所有 `.md` 文件的内容
|
|
38
|
+
- 不要尝试对 `knowledge/` 目录执行任何写入操作
|
|
39
|
+
- 不要尝试对 `knowledge/` 或系统配置文件执行任何写入操作
|
|
40
|
+
- 写 `MEMORY.md` 时仅允许记录已确认偏好和稳定服务风格,禁止写入知识库事实与安全策略
|
|
41
|
+
- `runtime/agent-binding.json` 属于运行时约束文件,**不要**通过对话尝试修改;若确需调整权限边界,走运维流程
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 与 AGENTS.md 一致:VIP 问答实例 workspace 根目录下 knowledge/ 为只读挂载(symlink),
|
|
3
|
+
# 指向 vip-admin workspace(与实例目录同父级)下的共享 knowledge/。
|
|
4
|
+
# 实例内 memory/、MEMORY.md 等仍在本 workspace,不由本脚本链接。
|
|
5
|
+
#
|
|
6
|
+
# 用法:由安装器在 post_install 中调用;需传入本实例 workspace 绝对路径,或设置:
|
|
7
|
+
# WORKSPACE=/path/to/workspace_<INSTANCE_ID> ./scripts/setup_links.sh
|
|
8
|
+
# 或 OPENCLAW_WORKSPACE=... ./scripts/setup_links.sh
|
|
9
|
+
# 或 ./scripts/setup_links.sh /path/to/workspace_<INSTANCE_ID>
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
WS="${WORKSPACE:-${OPENCLAW_WORKSPACE:-${1:-}}}"
|
|
14
|
+
if [[ -z "$WS" ]]; then
|
|
15
|
+
echo "setup_links: set WORKSPACE or OPENCLAW_WORKSPACE, or pass the instance workspace root as the first argument." >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
WS="$(cd "$WS" && pwd)"
|
|
20
|
+
parent="$(dirname "$WS")"
|
|
21
|
+
admin_knowledge="${parent}/knowledge"
|
|
22
|
+
link="${WS}/knowledge"
|
|
23
|
+
|
|
24
|
+
if [[ ! -d "$admin_knowledge" ]]; then
|
|
25
|
+
echo "setup_links: admin knowledge directory not found: ${admin_knowledge}" >&2
|
|
26
|
+
echo "setup_links: install vip-admin first so ${admin_knowledge} exists." >&2
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
mkdir -p "$WS"
|
|
31
|
+
|
|
32
|
+
if [[ -e "$link" && ! -L "$link" ]]; then
|
|
33
|
+
echo "setup_links: ${link} exists and is not a symlink; refusing to replace." >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
rm -f "$link"
|
|
38
|
+
ln -sfn "$(cd "$admin_knowledge" && pwd)" "$link"
|
|
39
|
+
echo "setup_links: ${link} -> $(readlink "$link")"
|
package/package.json
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-install",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"types": [
|
|
5
|
+
"store"
|
|
6
|
+
],
|
|
7
|
+
"displayName": "Agent安装",
|
|
8
|
+
"description": "通用 Agent 安装与升级。",
|
|
9
|
+
"changelog": [
|
|
10
|
+
{
|
|
11
|
+
"changes": [
|
|
12
|
+
"修复依赖 Agent 使用自定义 openclaw_id 时的安装检测逻辑"
|
|
13
|
+
],
|
|
14
|
+
"date": "2026-04-21",
|
|
15
|
+
"version": "0.1.4"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"changes": [
|
|
19
|
+
"初次提交"
|
|
20
|
+
],
|
|
21
|
+
"date": "2026-04-21",
|
|
22
|
+
"version": "0.1.3"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"createdAt": "2026-04-21",
|
|
26
|
+
"updatedAt": "2026-04-21"
|
|
27
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agent-install
|
|
3
|
+
description: 安装或升级通用 OpenClaw Agent(含按 .config.json 自动下载 skill)。Use when the user asks to install an agent, upgrade an agent, download agent config, back up an existing agent, update openclaw.json, or install skills listed with auto_install.
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Agent Install
|
|
8
|
+
|
|
9
|
+
安装、升级或重置**单个** OpenClaw Agent。本 skill 只做**顺序编排 + 调用脚本**;细节见脚本与 `AGENT-CONFIG.md`。
|
|
10
|
+
|
|
11
|
+
**串行**:一次只装一个 Agent,禁止并行。
|
|
12
|
+
|
|
13
|
+
## 对话约束
|
|
14
|
+
|
|
15
|
+
勿对用户念内部 **status** 枚举;展示安装状态以 **`user_prompt`** 为主。凡提示已备份,须给出 **`backup_dir` 完整路径**(`backup_agent.py` 输出)。密钥勿在公开渠道展示。
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Step 1:选定 Agent 并获取安装包
|
|
20
|
+
|
|
21
|
+
**A. 解析 `agent_id`**
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv run {baseDir}/scripts/list_agents.py
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
非精确 id 时:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv run {baseDir}/scripts/list_agents.py --query "{user_input}" --limit 5
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
内部:`EXACT_MATCH` → 继续;`NEEDS_CONFIRMATION` → 展示 `confirmation_message`;`NO_MATCH` → 请用户重述。
|
|
34
|
+
|
|
35
|
+
**B. 下载并校验**
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx -y sophhub@latest agent download {agent_id} --path=/home/node/.openclaw/workspace
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uv run {baseDir}/scripts/verify_download.py \
|
|
43
|
+
--agent-id "{agent_id}" \
|
|
44
|
+
--path "/home/node/.openclaw/workspace"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
校验失败则**终止全流程**。成功则带同一 `agent_id` 进入 Step 2。
|
|
48
|
+
|
|
49
|
+
**输入模版**
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
🔹【请提供要安装的 Agent】
|
|
53
|
+
- **agent_id**(例:`ai-cs-qa`)或 **自然语言描述**(例:「智能客服 QA」);不确定可先模糊查再选。
|
|
54
|
+
|
|
55
|
+
(需多选一)
|
|
56
|
+
⚠️ <confirmation_message>;可选:<suggested_replies>
|
|
57
|
+
|
|
58
|
+
(未找到)
|
|
59
|
+
❌ 未找到匹配,请换说法或给精确 agent_id。
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**输出模版**
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
✅ 已确定目标 Agent:<resolved_agent_id>
|
|
66
|
+
📦 下载与校验:<成功 ✅ / 失败 ❌>;失败则 🛑 终止,不继续。
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Step 2:参数、占位替换与安装状态检测
|
|
72
|
+
|
|
73
|
+
依次执行,中间勿插入无关步骤。
|
|
74
|
+
|
|
75
|
+
**1)解析安装参数**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv run {baseDir}/scripts/resolve_install_params.py \
|
|
79
|
+
--agent-id "{agent_id}" \
|
|
80
|
+
--path "/home/node/.openclaw/workspace"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
确认 `openclaw_id`、`workspace`、`agent_name`。`placeholder_catalog` / `placeholders_detail` 见 `AGENT-CONFIG.md`。用户可覆盖默认值。
|
|
84
|
+
|
|
85
|
+
**2)按需替换 `.md` 中 `token`(有 `placeholders` 时)**
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
uv run {baseDir}/scripts/update_agent_md.py \
|
|
89
|
+
--agent-id "{agent_id}" \
|
|
90
|
+
--path "/home/node/.openclaw/workspace" \
|
|
91
|
+
--replace "{{客服助手}}=客服助手"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**3)检测当前安装状态**
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
uv run {baseDir}/scripts/check_installed.py \
|
|
98
|
+
--agent-id "{agent_id}" \
|
|
99
|
+
--source-path "/home/node/.openclaw/workspace" \
|
|
100
|
+
--openclaw-id "{openclaw_id}" \
|
|
101
|
+
--workspace "{workspace}" \
|
|
102
|
+
--name "{agent_name}"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
对用户主状态含义(内部仍用 JSON `status` 分支,**勿对用户念枚举**):
|
|
106
|
+
|
|
107
|
+
- `DEPENDENCY_MISSING`:缺依赖 Agent → 提示先装依赖,**终止**
|
|
108
|
+
- `UPDATABLE`:可升级 → 后续先备份再覆盖;本地若改过配置需自行同步
|
|
109
|
+
- `SAME_VERSION_REINSTALL`:同版本 → 实为**重置**,须确认并先备份
|
|
110
|
+
- `NOT_INSTALLED`:新装 → 可直接进入 Step 3 的拷贝/写配置
|
|
111
|
+
|
|
112
|
+
兜底:`CONFIG_ERROR` → **终止**,人工核对 workspace / ID / 版本
|
|
113
|
+
|
|
114
|
+
说明时优先复述 **`user_prompt`**。
|
|
115
|
+
|
|
116
|
+
**输入模版**
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
🔹【确认或覆盖】openclaw_id / workspace / agent_name(默认见脚本)
|
|
120
|
+
⚠️ 若有占位:按 placeholder_catalog 的 label、description 确认每个 token 的替换文案
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**输出模版**
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
📌 参数已就绪(表格可列 openclaw_id、workspace、agent_name)
|
|
127
|
+
✏️ 若已跑 update_agent_md:<简述>
|
|
128
|
+
🧭 安装状态:用口语复述 user_prompt;CONFIG_ERROR 时 🛑 暂停
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Step 3:写入目标环境(条件备份 → 拷贝 → openclaw)
|
|
134
|
+
|
|
135
|
+
仅在 Step 2 **不是** `DEPENDENCY_MISSING` / `CONFIG_ERROR` 时继续。
|
|
136
|
+
|
|
137
|
+
**1)备份(仅 `UPDATABLE` 或 `SAME_VERSION_REINSTALL`)**
|
|
138
|
+
须先让用户回复「确认更新」或「确认重置」,再执行:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv run {baseDir}/scripts/backup_agent.py \
|
|
142
|
+
--agent-id "{agent_id}" \
|
|
143
|
+
--openclaw-id "{current_openclaw_id}" \
|
|
144
|
+
--workspace "{workspace}" \
|
|
145
|
+
--status "{status}"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
成功后必须把 **`backup_dir` 完整路径**告诉用户。其它 status **不要**调备份。
|
|
149
|
+
|
|
150
|
+
**2)拷贝文件并更新 `openclaw.json`**
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
uv run {baseDir}/scripts/copy_agent_files.py \
|
|
154
|
+
--agent-id "{agent_id}" \
|
|
155
|
+
--path "/home/node/.openclaw/workspace" \
|
|
156
|
+
--workspace "{workspace}"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
uv run {baseDir}/scripts/update_openclaw.py \
|
|
161
|
+
--agent-id "{agent_id}" \
|
|
162
|
+
--source-path "/home/node/.openclaw/workspace" \
|
|
163
|
+
--openclaw-id "{openclaw_id}" \
|
|
164
|
+
--workspace "{workspace}" \
|
|
165
|
+
--name "{agent_name}"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`NOT_INSTALLED` 无备份,直接 2)。`UPDATABLE` / `SAME_VERSION_REINSTALL` 须已完成 1)。
|
|
169
|
+
|
|
170
|
+
**输入模版(仅要备份时)**
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
🔹 确认更新 → 回复:确认更新
|
|
174
|
+
🔹 确认重置 → 回复:确认重置
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**输出模版**
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
✅ 已备份(如有):backup_dir = <完整路径>;copied_md_files 可附
|
|
181
|
+
🛠️ openclaw 已更新:摘要 agent_id、openclaw_id、workspace、version、bot-api、install_state(密钥走安全渠道)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Step 4:Skill(auto_install)与完成表
|
|
187
|
+
|
|
188
|
+
在 Step 3 成功、`openclaw.json` 已写入后执行。
|
|
189
|
+
|
|
190
|
+
**Skill**:读 workspace 内 `.config.json` 的 `skills`,对 **`auto_install: true`** 的条目:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
mkdir -p "{workspace}/skills"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npx -y sophhub@latest download {skill_name} -o "{workspace}/skills"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`{workspace}` 为本 Agent 工作目录;多条**依次**下载。必要时加 `--type builtin` / `--type store`。
|
|
201
|
+
|
|
202
|
+
**输出模版**
|
|
203
|
+
|
|
204
|
+
```text
|
|
205
|
+
📎 Skill:<列出本次 download 成功的 name>;失败须如实说明
|
|
206
|
+
🎉 **Agent 安装流程结束**
|
|
207
|
+
| 项目 | 内容 |
|
|
208
|
+
|------|------|
|
|
209
|
+
| agent_id | <…> |
|
|
210
|
+
| 结果 | <新装 🆕 / 升级 ⬆️ / 重置 🔄> |
|
|
211
|
+
| workspace | <路径> |
|
|
212
|
+
| 备份目录 | <backup_dir 或「本次无备份」> |
|
|
213
|
+
| skills | <已下载 name 列表或「未在本流程下载」> |
|
|
214
|
+
| bot-api | <仅安全渠道> |
|
|
215
|
+
🔐 密钥勿截图发群、勿写入公开工单。
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 错误与中止
|
|
221
|
+
|
|
222
|
+
同版须先确认再备份;本地版本更高勿强退;无 `openclaw.json` 先初始化;`CONFIG_ERROR` 等见 Step 2。
|
|
223
|
+
|
|
224
|
+
**输出模版**
|
|
225
|
+
|
|
226
|
+
```text
|
|
227
|
+
❌ 流程中止:<步骤简述> — <原因> — <建议>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Forbidden
|
|
233
|
+
|
|
234
|
+
- 不要直接手改 `openclaw.json`
|
|
235
|
+
- 不要在未确认时覆盖同名 Agent;未确认参数前不要跑 `check_installed`
|
|
236
|
+
- 不要并行多 Agent;不要删其它 Agent 配置
|
|
237
|
+
- 勿把「下载成功」当成「已安装完成」
|
|
238
|
+
- 勿在本 skill 展开脚本实现细节
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import sys
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from common import (
|
|
13
|
+
default_openclaw_config_path,
|
|
14
|
+
find_agent_entry,
|
|
15
|
+
load_openclaw_config,
|
|
16
|
+
resolve_existing_agent_entry,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
ALLOWED_BACKUP_STATUSES = {"UPDATABLE", "SAME_VERSION_REINSTALL"}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def backup_md_files(workspace_path: Path, backup_dir: Path) -> list[str]:
|
|
23
|
+
copied_files: list[str] = []
|
|
24
|
+
if not workspace_path.is_dir():
|
|
25
|
+
return copied_files
|
|
26
|
+
|
|
27
|
+
backup_dir.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
for file_path in sorted(workspace_path.iterdir()):
|
|
29
|
+
if file_path.is_file() and file_path.suffix == ".md":
|
|
30
|
+
shutil.copy2(file_path, backup_dir / file_path.name)
|
|
31
|
+
copied_files.append(file_path.name)
|
|
32
|
+
return copied_files
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def backup_agent(
|
|
36
|
+
agent_id: str,
|
|
37
|
+
status: str,
|
|
38
|
+
config_path: Path,
|
|
39
|
+
backup_root: Path,
|
|
40
|
+
*,
|
|
41
|
+
openclaw_id: str | None = None,
|
|
42
|
+
workspace: str | None = None,
|
|
43
|
+
) -> dict[str, object]:
|
|
44
|
+
if status not in ALLOWED_BACKUP_STATUSES:
|
|
45
|
+
raise ValueError(
|
|
46
|
+
"当前状态不允许执行备份。"
|
|
47
|
+
f"仅当状态为 {', '.join(sorted(ALLOWED_BACKUP_STATUSES))} 时才能备份,当前状态: {status}"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
openclaw = load_openclaw_config(config_path)
|
|
51
|
+
agent_entry = None
|
|
52
|
+
if isinstance(openclaw_id, str) and openclaw_id.strip():
|
|
53
|
+
agent_entry = find_agent_entry(openclaw, openclaw_id.strip())
|
|
54
|
+
if agent_entry is None and isinstance(workspace, str) and workspace.strip():
|
|
55
|
+
agent_entry = resolve_existing_agent_entry(config_path, openclaw, agent_id, workspace.strip())
|
|
56
|
+
if agent_entry is None:
|
|
57
|
+
agent_entry = find_agent_entry(openclaw, agent_id)
|
|
58
|
+
if agent_entry is None:
|
|
59
|
+
raise ValueError(f"openclaw.json 中不存在 Agent: {agent_id}")
|
|
60
|
+
|
|
61
|
+
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
62
|
+
backup_dir = backup_root / agent_id / timestamp
|
|
63
|
+
backup_dir.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
|
|
65
|
+
workspace_backed_up = False
|
|
66
|
+
workspace_path = agent_entry.get("workspace")
|
|
67
|
+
copied_md_files: list[str] = []
|
|
68
|
+
if isinstance(workspace_path, str) and Path(workspace_path).exists():
|
|
69
|
+
copied_md_files = backup_md_files(Path(workspace_path), backup_dir)
|
|
70
|
+
workspace_backed_up = bool(copied_md_files)
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
"agent_id": agent_id,
|
|
74
|
+
"openclaw_id": agent_entry.get("id"),
|
|
75
|
+
"status": status,
|
|
76
|
+
"backup_dir": str(backup_dir),
|
|
77
|
+
"workspace_backed_up": workspace_backed_up,
|
|
78
|
+
"workspace": workspace_path,
|
|
79
|
+
"copied_md_files": copied_md_files,
|
|
80
|
+
"message": "Agent 配置备份完成,已备份 workspace 下的 .md 文件。",
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def main() -> int:
|
|
85
|
+
parser = argparse.ArgumentParser(description="备份已安装 Agent")
|
|
86
|
+
parser.add_argument("--agent-id", required=True, help="Agent ID")
|
|
87
|
+
parser.add_argument("--openclaw-id", help="当前 openclaw 条目 id(check_installed / openclaw 解析结果)")
|
|
88
|
+
parser.add_argument("--workspace", help="当前 workspace(流程确认结果)")
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
"--status",
|
|
91
|
+
required=True,
|
|
92
|
+
help="check_installed 状态,仅允许 UPDATABLE 或 SAME_VERSION_REINSTALL",
|
|
93
|
+
)
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"--config",
|
|
96
|
+
default=str(default_openclaw_config_path()),
|
|
97
|
+
help="openclaw.json 路径",
|
|
98
|
+
)
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
"--backup-root",
|
|
101
|
+
default=os.path.expanduser("~/.openclaw/backups"),
|
|
102
|
+
help="备份根目录",
|
|
103
|
+
)
|
|
104
|
+
args = parser.parse_args()
|
|
105
|
+
|
|
106
|
+
result = backup_agent(
|
|
107
|
+
args.agent_id,
|
|
108
|
+
args.status,
|
|
109
|
+
Path(args.config).expanduser().resolve(),
|
|
110
|
+
Path(args.backup_root).expanduser().resolve(),
|
|
111
|
+
openclaw_id=args.openclaw_id,
|
|
112
|
+
workspace=args.workspace,
|
|
113
|
+
)
|
|
114
|
+
json.dump(result, sys.stdout, indent=2, ensure_ascii=False)
|
|
115
|
+
sys.stdout.write("\n")
|
|
116
|
+
return 0
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
raise SystemExit(main())
|