soulhubcli 1.0.21 → 1.0.22
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/README.md +133 -21
- package/dist/index.cjs +601 -98
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,14 +34,41 @@ npx soulhubcli <command>
|
|
|
34
34
|
|
|
35
35
|
| 命令 | 说明 |
|
|
36
36
|
|------|------|
|
|
37
|
-
| `soulhub search
|
|
37
|
+
| `soulhub search [query]` | 搜索 Agent 模板(匹配名称、描述、标签) |
|
|
38
|
+
| `soulhub search -c <category>` | 按分类筛选搜索结果 |
|
|
39
|
+
| `soulhub search -n <number>` | 限制搜索结果数量(默认 20) |
|
|
40
|
+
| `soulhub search --json` | 以 JSON 格式输出搜索结果 |
|
|
38
41
|
| `soulhub info <name>` | 查看 Agent 详细信息(identity、soul、skills 等) |
|
|
39
|
-
| `soulhub
|
|
40
|
-
| `soulhub
|
|
42
|
+
| `soulhub info <name> --identity` | 显示 IDENTITY.md 内容 |
|
|
43
|
+
| `soulhub info <name> --soul` | 显示 SOUL.md 内容 |
|
|
44
|
+
| `soulhub info <name> --json` | 以 JSON 格式输出 Agent 详情 |
|
|
45
|
+
| `soulhub install <name>` | 从 Registry 安装 Agent 或团队(交互式:选择角色和目标 claw) |
|
|
46
|
+
| `soulhub install <name> --role main` | 安装为主 Agent(跳过角色选择) |
|
|
47
|
+
| `soulhub install <name> --role worker` | 安装为 Worker Agent(跳过角色选择) |
|
|
48
|
+
| `soulhub install <name> --claw-type <type>` | 指定 claw 类型(跳过 claw 选择) |
|
|
49
|
+
| `soulhub install <name> --dir <path>` | 安装到指定目录(不依赖 claw 环境) |
|
|
50
|
+
| `soulhub install <name> -y` | 跳过所有确认提示(自动确认) |
|
|
41
51
|
| `soulhub install --from <source>` | 从本地目录、ZIP 文件或 URL 安装 |
|
|
42
52
|
| `soulhub list` | 列出已安装的 Agent |
|
|
43
|
-
| `soulhub
|
|
44
|
-
| `soulhub
|
|
53
|
+
| `soulhub list --json` | 以 JSON 格式输出已安装的 Agent |
|
|
54
|
+
| `soulhub update [name]` | 更新已安装的 Agent(不传名称则更新全部) |
|
|
55
|
+
| `soulhub uninstall <name>` | 卸载 Agent(同时删除相关备份) |
|
|
56
|
+
| `soulhub uninstall <name> --keep-files` | 卸载但保留 workspace 文件 |
|
|
57
|
+
| `soulhub uninstall <name> -y` | 卸载 Agent(跳过确认提示) |
|
|
58
|
+
| `soulhub rollback` | 交互式选择回滚到某次安装前的状态 |
|
|
59
|
+
| `soulhub rollback --list` | 列出所有可用的回滚记录 |
|
|
60
|
+
| `soulhub rollback --last <n>` | 回滚倒数第 n 次安装(1 = 最近一次) |
|
|
61
|
+
| `soulhub rollback --id <id>` | 按 ID 回滚到指定的备份记录 |
|
|
62
|
+
| `soulhub rollback --claw-type <type>` | 指定回滚的目标 claw 类型 |
|
|
63
|
+
| `soulhub rollback --last <n> -y` | 回滚并跳过确认提示 |
|
|
64
|
+
|
|
65
|
+
### 全局选项
|
|
66
|
+
|
|
67
|
+
| 选项 | 说明 |
|
|
68
|
+
|------|------|
|
|
69
|
+
| `--verbose` | 启用详细调试日志 |
|
|
70
|
+
| `--version` | 显示版本号 |
|
|
71
|
+
| `--help` | 显示帮助信息 |
|
|
45
72
|
|
|
46
73
|
## 使用方法
|
|
47
74
|
|
|
@@ -50,22 +77,41 @@ npx soulhubcli <command>
|
|
|
50
77
|
```bash
|
|
51
78
|
soulhub search python
|
|
52
79
|
soulhub search "content writing"
|
|
80
|
+
|
|
81
|
+
# 按分类筛选
|
|
82
|
+
soulhub search -c development
|
|
83
|
+
|
|
84
|
+
# 限制结果数量
|
|
85
|
+
soulhub search writer -n 5
|
|
86
|
+
|
|
87
|
+
# JSON 格式输出(适合 CI/管道集成)
|
|
88
|
+
soulhub search writer --json
|
|
53
89
|
```
|
|
54
90
|
|
|
55
91
|
### 安装 Agent
|
|
56
92
|
|
|
57
93
|
CLI 会自动识别目标是单 Agent 还是多 Agent 团队,无需手动区分。
|
|
58
94
|
|
|
59
|
-
|
|
95
|
+
**默认行为:交互式安装。** CLI 会提示用户选择安装角色(主 Agent / Worker Agent)以及目标 claw 目录(支持多选)。通过命令行参数可跳过交互,实现完全非交互式安装。
|
|
60
96
|
|
|
61
97
|
**从 Registry 安装:**
|
|
62
98
|
|
|
63
99
|
```bash
|
|
64
|
-
#
|
|
100
|
+
# 交互式安装(会提示选择角色和 claw 目录)
|
|
65
101
|
soulhub install writer-wechat
|
|
66
102
|
|
|
67
|
-
#
|
|
68
|
-
soulhub install writer-wechat --main
|
|
103
|
+
# 指定角色,仍交互选择 claw 目录
|
|
104
|
+
soulhub install writer-wechat --role main
|
|
105
|
+
soulhub install writer-wechat --role worker
|
|
106
|
+
|
|
107
|
+
# 指定 claw 类型,仍交互选择角色
|
|
108
|
+
soulhub install writer-wechat --claw-type LightClaw
|
|
109
|
+
|
|
110
|
+
# 完全非交互式安装
|
|
111
|
+
soulhub install writer-wechat --role worker --claw-type OpenClaw
|
|
112
|
+
|
|
113
|
+
# 完全非交互式安装(主 Agent,-y 跳过确认)
|
|
114
|
+
soulhub install writer-wechat --role main --claw-type OpenClaw -y
|
|
69
115
|
|
|
70
116
|
# 安装多 Agent 团队(调度 Agent + 工作 Agent)
|
|
71
117
|
soulhub install dev-squad
|
|
@@ -89,15 +135,27 @@ soulhub install --from https://example.com/agent-team.zip
|
|
|
89
135
|
```bash
|
|
90
136
|
# 安装到自定义目录(不依赖 OpenClaw/LightClaw 环境)
|
|
91
137
|
soulhub install writer-wechat --dir ./my-agents
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 查看 Agent 详情
|
|
92
141
|
|
|
93
|
-
|
|
94
|
-
soulhub
|
|
142
|
+
```bash
|
|
143
|
+
soulhub info writer-xiaohongshu
|
|
144
|
+
|
|
145
|
+
# 查看 IDENTITY.md 和 SOUL.md 内容
|
|
146
|
+
soulhub info writer-xiaohongshu --identity --soul
|
|
147
|
+
|
|
148
|
+
# JSON 格式输出
|
|
149
|
+
soulhub info writer-xiaohongshu --json
|
|
95
150
|
```
|
|
96
151
|
|
|
97
152
|
### 列出已安装的 Agent
|
|
98
153
|
|
|
99
154
|
```bash
|
|
100
155
|
soulhub list
|
|
156
|
+
|
|
157
|
+
# JSON 格式输出
|
|
158
|
+
soulhub list --json
|
|
101
159
|
```
|
|
102
160
|
|
|
103
161
|
### 更新 Agent
|
|
@@ -109,28 +167,81 @@ soulhub update ops-assistant # 更新指定 Agent
|
|
|
109
167
|
|
|
110
168
|
### 卸载 Agent
|
|
111
169
|
|
|
170
|
+
卸载时,如果存在相关备份记录,CLI 会提示用户确认,因为卸载操作会同时删除该 Agent 的所有备份文件,删除后将无法回滚。使用 `-y` 参数可跳过确认。
|
|
171
|
+
|
|
112
172
|
```bash
|
|
113
173
|
soulhub uninstall ops-assistant
|
|
174
|
+
|
|
175
|
+
# 跳过确认提示
|
|
176
|
+
soulhub uninstall ops-assistant -y
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
使用 `--keep-files` 参数可保留 workspace 文件,仅从安装记录中移除:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
soulhub uninstall ops-assistant --keep-files
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 回滚安装
|
|
186
|
+
|
|
187
|
+
每次安装操作都会自动创建备份记录,支持回滚到安装前的状态。
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# 交互式选择:展示所有备份记录,选择要回滚的项
|
|
191
|
+
soulhub rollback
|
|
192
|
+
|
|
193
|
+
# 查看所有可用的回滚记录
|
|
194
|
+
soulhub rollback --list
|
|
195
|
+
|
|
196
|
+
# 非交互式:回滚最近一次安装
|
|
197
|
+
soulhub rollback --last 1
|
|
198
|
+
|
|
199
|
+
# 非交互式:回滚最近一次安装(跳过确认)
|
|
200
|
+
soulhub rollback --last 1 -y
|
|
201
|
+
|
|
202
|
+
# 非交互式:回滚倒数第 2 次安装
|
|
203
|
+
soulhub rollback --last 2
|
|
204
|
+
|
|
205
|
+
# 按 ID 回滚到指定记录
|
|
206
|
+
soulhub rollback --id <record-id>
|
|
114
207
|
```
|
|
115
208
|
|
|
116
209
|
## 安装行为说明
|
|
117
210
|
|
|
211
|
+
### 交互式安装流程
|
|
212
|
+
|
|
213
|
+
未指定 `--role` 和 `--claw-type` 参数时,CLI 进入交互式安装:
|
|
214
|
+
|
|
215
|
+
1. 展示 Agent 基本信息(名称、版本、描述、分类、标签)
|
|
216
|
+
2. 提示选择安装角色:**Main Agent** 或 **Worker Agent**
|
|
217
|
+
3. 安装为 Main Agent 时,警告将覆盖当前 workspace 内容(人格文件会被替换,记忆不受影响),需用户确认(或使用 `-y` 跳过)
|
|
218
|
+
4. 提示多选目标 claw 目录(OpenClaw / LightClaw),可同时安装到多个 claw
|
|
219
|
+
5. 执行安装、注册、重启
|
|
220
|
+
|
|
118
221
|
### 单 Agent 安装
|
|
119
222
|
|
|
120
|
-
-
|
|
121
|
-
-
|
|
122
|
-
-
|
|
123
|
-
- 如果目标目录已存在,CLI 会**自动备份**(复制到 `agentbackup/`)
|
|
223
|
+
- 安装为 **Worker Agent** 时,部署到 `workspace-<agentId>/` 目录
|
|
224
|
+
- 安装为 **Main Agent** 时,部署到 `workspace/` 目录,会覆盖已有人格文件
|
|
225
|
+
- 安装前自动备份已有内容到 `~/.soulhub/backups/<claw>/`(按 claw 类型分目录存储)
|
|
124
226
|
- 仅覆盖 `IDENTITY.md`、`SOUL.md` 等灵魂文件,不影响 workspace 中的其他运行时文件
|
|
125
|
-
- 安装完成后自动重启 OpenClaw Gateway;若重启失败会提示手动重启
|
|
227
|
+
- 安装完成后自动重启 OpenClaw/LightClaw Gateway;若重启失败会提示手动重启
|
|
126
228
|
|
|
127
229
|
### 多 Agent 团队安装
|
|
128
230
|
|
|
129
231
|
- **调度 Agent(Dispatcher)** 作为主 Agent 安装到 `workspace/` 目录
|
|
130
232
|
- **工作 Agent(Worker)** 安装到各自的 `workspace-<agentId>/` 目录
|
|
233
|
+
- 安装前自动备份存量子 Agent(mv 方式移走已有 worker 目录)
|
|
131
234
|
- 自动配置多 Agent 之间的通信
|
|
132
235
|
- Worker Agent 自动注册到 claw 配置中
|
|
133
|
-
- 安装完成后自动重启 OpenClaw Gateway
|
|
236
|
+
- 安装完成后自动重启 OpenClaw/LightClaw Gateway
|
|
237
|
+
|
|
238
|
+
### 备份与回滚
|
|
239
|
+
|
|
240
|
+
- 备份文件存储在 `~/.soulhub/backups/<claw>/` 目录下,按 claw 类型(如 `openclaw`、`lightclaw`)分目录管理
|
|
241
|
+
- 备份清单记录在 `~/.soulhub/backup-manifest.json` 中
|
|
242
|
+
- 每次安装都会自动创建备份记录,包含 agent 文件和 claw 配置快照
|
|
243
|
+
- 回滚时自动恢复备份文件和 claw 配置,并重启 Gateway
|
|
244
|
+
- 卸载 Agent 时会同时清理相关备份记录和备份文件
|
|
134
245
|
|
|
135
246
|
## 配置
|
|
136
247
|
|
|
@@ -148,11 +259,12 @@ export SOULHUB_REGISTRY_URL=https://your-registry.example.com
|
|
|
148
259
|
|
|
149
260
|
CLI 按以下优先级查找 claw 安装目录:
|
|
150
261
|
|
|
151
|
-
1. `--
|
|
152
|
-
2. `
|
|
153
|
-
3.
|
|
262
|
+
1. `--claw-type` 命令行参数(指定时只安装到该 claw)
|
|
263
|
+
2. `--dir` 命令行参数(直接指定目标目录,不依赖 claw 环境)
|
|
264
|
+
3. `OPENCLAW_HOME` / `LIGHTCLAW_HOME` 环境变量
|
|
265
|
+
4. 默认路径 `~/.openclaw`、`~/.lightclaw`
|
|
154
266
|
|
|
155
|
-
未指定 `--
|
|
267
|
+
未指定 `--claw-type` 或 `--dir` 时,CLI 会检测所有可用的 claw 目录,多个时交互式多选。
|
|
156
268
|
|
|
157
269
|
## 环境要求
|
|
158
270
|
|
package/dist/index.cjs
CHANGED
|
@@ -966,7 +966,7 @@ var require_command = __commonJS({
|
|
|
966
966
|
var EventEmitter = require("events").EventEmitter;
|
|
967
967
|
var childProcess = require("child_process");
|
|
968
968
|
var path5 = require("path");
|
|
969
|
-
var
|
|
969
|
+
var fs8 = require("fs");
|
|
970
970
|
var process3 = require("process");
|
|
971
971
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
972
972
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1899,10 +1899,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1899
1899
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1900
1900
|
function findFile(baseDir, baseName) {
|
|
1901
1901
|
const localBin = path5.resolve(baseDir, baseName);
|
|
1902
|
-
if (
|
|
1902
|
+
if (fs8.existsSync(localBin)) return localBin;
|
|
1903
1903
|
if (sourceExt.includes(path5.extname(baseName))) return void 0;
|
|
1904
1904
|
const foundExt = sourceExt.find(
|
|
1905
|
-
(ext) =>
|
|
1905
|
+
(ext) => fs8.existsSync(`${localBin}${ext}`)
|
|
1906
1906
|
);
|
|
1907
1907
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1908
1908
|
return void 0;
|
|
@@ -1914,7 +1914,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1914
1914
|
if (this._scriptPath) {
|
|
1915
1915
|
let resolvedScriptPath;
|
|
1916
1916
|
try {
|
|
1917
|
-
resolvedScriptPath =
|
|
1917
|
+
resolvedScriptPath = fs8.realpathSync(this._scriptPath);
|
|
1918
1918
|
} catch (err) {
|
|
1919
1919
|
resolvedScriptPath = this._scriptPath;
|
|
1920
1920
|
}
|
|
@@ -19165,6 +19165,11 @@ function recordInstall(name, version, workspace) {
|
|
|
19165
19165
|
saveConfig(config);
|
|
19166
19166
|
logger.debug(`Recorded install`, { name, version, workspace });
|
|
19167
19167
|
}
|
|
19168
|
+
function removeInstallRecord(name) {
|
|
19169
|
+
const config = loadConfig();
|
|
19170
|
+
config.installed = config.installed.filter((a) => a.name !== name);
|
|
19171
|
+
saveConfig(config);
|
|
19172
|
+
}
|
|
19168
19173
|
function getWorkspaceDir(clawDir, agentName) {
|
|
19169
19174
|
return import_node_path11.default.join(clawDir, `workspace-${agentName}`);
|
|
19170
19175
|
}
|
|
@@ -19301,6 +19306,11 @@ function detectPackageKind(dir) {
|
|
|
19301
19306
|
}
|
|
19302
19307
|
return "unknown";
|
|
19303
19308
|
}
|
|
19309
|
+
function getBackupBaseDir(clawDir) {
|
|
19310
|
+
const home = process.env.HOME || "~";
|
|
19311
|
+
const brand = detectClawBrand(clawDir).toLowerCase();
|
|
19312
|
+
return import_node_path11.default.join(home, ".soulhub", "backups", brand);
|
|
19313
|
+
}
|
|
19304
19314
|
function backupAgentWorkspace(workspaceDir) {
|
|
19305
19315
|
if (!import_node_fs8.default.existsSync(workspaceDir)) {
|
|
19306
19316
|
return null;
|
|
@@ -19310,7 +19320,7 @@ function backupAgentWorkspace(workspaceDir) {
|
|
|
19310
19320
|
return null;
|
|
19311
19321
|
}
|
|
19312
19322
|
const clawDir = import_node_path11.default.dirname(workspaceDir);
|
|
19313
|
-
const backupBaseDir =
|
|
19323
|
+
const backupBaseDir = getBackupBaseDir(clawDir);
|
|
19314
19324
|
if (!import_node_fs8.default.existsSync(backupBaseDir)) {
|
|
19315
19325
|
import_node_fs8.default.mkdirSync(backupBaseDir, { recursive: true });
|
|
19316
19326
|
}
|
|
@@ -19333,7 +19343,7 @@ function moveBackupAgentWorkspace(workspaceDir) {
|
|
|
19333
19343
|
return null;
|
|
19334
19344
|
}
|
|
19335
19345
|
const clawDir = import_node_path11.default.dirname(workspaceDir);
|
|
19336
|
-
const backupBaseDir =
|
|
19346
|
+
const backupBaseDir = getBackupBaseDir(clawDir);
|
|
19337
19347
|
if (!import_node_fs8.default.existsSync(backupBaseDir)) {
|
|
19338
19348
|
import_node_fs8.default.mkdirSync(backupBaseDir, { recursive: true });
|
|
19339
19349
|
}
|
|
@@ -19343,7 +19353,12 @@ function moveBackupAgentWorkspace(workspaceDir) {
|
|
|
19343
19353
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
19344
19354
|
backupDir = import_node_path11.default.join(backupBaseDir, `${dirName}-${timestamp2}`);
|
|
19345
19355
|
}
|
|
19346
|
-
|
|
19356
|
+
try {
|
|
19357
|
+
import_node_fs8.default.renameSync(workspaceDir, backupDir);
|
|
19358
|
+
} catch {
|
|
19359
|
+
import_node_fs8.default.cpSync(workspaceDir, backupDir, { recursive: true });
|
|
19360
|
+
import_node_fs8.default.rmSync(workspaceDir, { recursive: true, force: true });
|
|
19361
|
+
}
|
|
19347
19362
|
logger.info(`Workspace moved to backup`, { from: workspaceDir, to: backupDir });
|
|
19348
19363
|
return backupDir;
|
|
19349
19364
|
}
|
|
@@ -19432,6 +19447,106 @@ function commitBackupRecord(record) {
|
|
|
19432
19447
|
saveBackupManifest(manifest);
|
|
19433
19448
|
logger.info(`Backup record saved`, { id: record.id, items: record.items.length, workers: record.installedWorkerIds.length });
|
|
19434
19449
|
}
|
|
19450
|
+
async function promptSelectRole() {
|
|
19451
|
+
console.log();
|
|
19452
|
+
console.log(" ? Install as:");
|
|
19453
|
+
console.log();
|
|
19454
|
+
console.log(" 1) \u{1F477} Worker agent (\u5B50Agent\uFF0C\u5B89\u88C5\u5230 workspace-<name>/ \u76EE\u5F55)");
|
|
19455
|
+
console.log(" 2) \u{1F451} Main agent (\u4E3BAgent\uFF0C\u5B89\u88C5\u5230 workspace/ \u76EE\u5F55)");
|
|
19456
|
+
console.log();
|
|
19457
|
+
const rl = import_node_readline.default.createInterface({
|
|
19458
|
+
input: process.stdin,
|
|
19459
|
+
output: process.stdout
|
|
19460
|
+
});
|
|
19461
|
+
return new Promise((resolve) => {
|
|
19462
|
+
rl.question(" Please select (1-2) [1]: ", (answer) => {
|
|
19463
|
+
rl.close();
|
|
19464
|
+
const trimmed = answer.trim();
|
|
19465
|
+
if (trimmed === "" || trimmed === "1") {
|
|
19466
|
+
resolve("worker");
|
|
19467
|
+
} else if (trimmed === "2") {
|
|
19468
|
+
resolve("main");
|
|
19469
|
+
} else {
|
|
19470
|
+
console.log(" Invalid selection, operation cancelled.");
|
|
19471
|
+
resolve(null);
|
|
19472
|
+
}
|
|
19473
|
+
});
|
|
19474
|
+
});
|
|
19475
|
+
}
|
|
19476
|
+
async function promptMultiSelectClawDirs() {
|
|
19477
|
+
const dirs = findAllClawDirs();
|
|
19478
|
+
if (dirs.length === 0) {
|
|
19479
|
+
return [];
|
|
19480
|
+
}
|
|
19481
|
+
if (dirs.length === 1) {
|
|
19482
|
+
const brand = detectClawBrand(dirs[0]);
|
|
19483
|
+
console.log();
|
|
19484
|
+
console.log(` \u2714 Detected ${brand}: ${dirs[0]}`);
|
|
19485
|
+
return dirs;
|
|
19486
|
+
}
|
|
19487
|
+
console.log();
|
|
19488
|
+
console.log(" ? Select target Claw installations (multiple allowed):");
|
|
19489
|
+
console.log();
|
|
19490
|
+
dirs.forEach((dir, index) => {
|
|
19491
|
+
const brand = detectClawBrand(dir);
|
|
19492
|
+
console.log(` ${index + 1}) ${brand} ${dir}`);
|
|
19493
|
+
});
|
|
19494
|
+
console.log();
|
|
19495
|
+
const rl = import_node_readline.default.createInterface({
|
|
19496
|
+
input: process.stdin,
|
|
19497
|
+
output: process.stdout
|
|
19498
|
+
});
|
|
19499
|
+
return new Promise((resolve) => {
|
|
19500
|
+
const allNums = dirs.map((_2, i) => String(i + 1)).join(",");
|
|
19501
|
+
rl.question(` Enter numbers separated by comma (e.g. 1,2) [${allNums}]: `, (answer) => {
|
|
19502
|
+
rl.close();
|
|
19503
|
+
const trimmed = answer.trim();
|
|
19504
|
+
if (trimmed === "") {
|
|
19505
|
+
resolve(dirs);
|
|
19506
|
+
return;
|
|
19507
|
+
}
|
|
19508
|
+
const parts = trimmed.split(",").map((s3) => s3.trim());
|
|
19509
|
+
const selected = [];
|
|
19510
|
+
for (const part of parts) {
|
|
19511
|
+
const idx = parseInt(part, 10);
|
|
19512
|
+
if (idx >= 1 && idx <= dirs.length) {
|
|
19513
|
+
const dir = dirs[idx - 1];
|
|
19514
|
+
if (!selected.includes(dir)) {
|
|
19515
|
+
selected.push(dir);
|
|
19516
|
+
}
|
|
19517
|
+
} else {
|
|
19518
|
+
console.log(` Invalid selection: ${part}, operation cancelled.`);
|
|
19519
|
+
resolve([]);
|
|
19520
|
+
return;
|
|
19521
|
+
}
|
|
19522
|
+
}
|
|
19523
|
+
if (selected.length === 0) {
|
|
19524
|
+
console.log(" No claw selected, operation cancelled.");
|
|
19525
|
+
}
|
|
19526
|
+
resolve(selected);
|
|
19527
|
+
});
|
|
19528
|
+
});
|
|
19529
|
+
}
|
|
19530
|
+
async function promptConfirm(message, defaultYes = true) {
|
|
19531
|
+
const rl = import_node_readline.default.createInterface({
|
|
19532
|
+
input: process.stdin,
|
|
19533
|
+
output: process.stdout
|
|
19534
|
+
});
|
|
19535
|
+
const hint = defaultYes ? "Y/n" : "y/N";
|
|
19536
|
+
return new Promise((resolve) => {
|
|
19537
|
+
rl.question(` ${message} (${hint}) `, (answer) => {
|
|
19538
|
+
rl.close();
|
|
19539
|
+
const trimmed = answer.trim().toLowerCase();
|
|
19540
|
+
if (trimmed === "") {
|
|
19541
|
+
resolve(defaultYes);
|
|
19542
|
+
} else if (trimmed === "y" || trimmed === "yes") {
|
|
19543
|
+
resolve(true);
|
|
19544
|
+
} else {
|
|
19545
|
+
resolve(false);
|
|
19546
|
+
}
|
|
19547
|
+
});
|
|
19548
|
+
});
|
|
19549
|
+
}
|
|
19435
19550
|
var CATEGORY_LABELS = {
|
|
19436
19551
|
"self-media": "Self Media",
|
|
19437
19552
|
development: "Development",
|
|
@@ -19538,7 +19653,7 @@ function restartOpenClawGateway(clawDir) {
|
|
|
19538
19653
|
}
|
|
19539
19654
|
|
|
19540
19655
|
// src/commands/search.ts
|
|
19541
|
-
var searchCommand = new Command("search").description("Search for agents in the SoulHub registry").argument("[query]", "Search query (matches name, description, tags)").option("-c, --category <category>", "Filter by category").option("-
|
|
19656
|
+
var searchCommand = new Command("search").description("Search for agents in the SoulHub registry").argument("[query]", "Search query (matches name, description, tags)").option("-c, --category <category>", "Filter by category").option("-n, --limit <number>", "Max results to show", "20").option("--json", "Output results in JSON format").action(async (query, options) => {
|
|
19542
19657
|
try {
|
|
19543
19658
|
const index = await fetchIndex();
|
|
19544
19659
|
let agents = index.agents;
|
|
@@ -19556,14 +19671,30 @@ var searchCommand = new Command("search").description("Search for agents in the
|
|
|
19556
19671
|
const limit = parseInt(options.limit, 10);
|
|
19557
19672
|
const shown = agents.slice(0, limit);
|
|
19558
19673
|
if (shown.length === 0) {
|
|
19559
|
-
|
|
19560
|
-
|
|
19561
|
-
|
|
19562
|
-
|
|
19563
|
-
)
|
|
19674
|
+
if (options.json) {
|
|
19675
|
+
console.log(JSON.stringify([], null, 2));
|
|
19676
|
+
} else {
|
|
19677
|
+
console.log(source_default.yellow("No agents found matching your query."));
|
|
19678
|
+
if (query) {
|
|
19679
|
+
console.log(
|
|
19680
|
+
source_default.dim(` Try: soulhub search (without query to list all)`)
|
|
19681
|
+
);
|
|
19682
|
+
}
|
|
19564
19683
|
}
|
|
19565
19684
|
return;
|
|
19566
19685
|
}
|
|
19686
|
+
if (options.json) {
|
|
19687
|
+
const jsonOutput = shown.map((a) => ({
|
|
19688
|
+
name: a.name,
|
|
19689
|
+
displayName: a.displayName,
|
|
19690
|
+
version: a.version,
|
|
19691
|
+
description: a.description,
|
|
19692
|
+
category: a.category,
|
|
19693
|
+
tags: a.tags
|
|
19694
|
+
}));
|
|
19695
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
19696
|
+
return;
|
|
19697
|
+
}
|
|
19567
19698
|
console.log(
|
|
19568
19699
|
source_default.bold(`
|
|
19569
19700
|
Found ${agents.length} agent(s):
|
|
@@ -19604,7 +19735,7 @@ var searchCommand = new Command("search").description("Search for agents in the
|
|
|
19604
19735
|
});
|
|
19605
19736
|
|
|
19606
19737
|
// src/commands/info.ts
|
|
19607
|
-
var infoCommand = new Command("info").description("Show details of an agent (identity, soul, skills, etc.)").argument("<name>", "Agent name").option("--identity", "Show IDENTITY.md content").option("--soul", "Show SOUL.md content").action(async (name, options) => {
|
|
19738
|
+
var infoCommand = new Command("info").description("Show details of an agent (identity, soul, skills, etc.)").argument("<name>", "Agent name").option("--identity", "Show IDENTITY.md content").option("--soul", "Show SOUL.md content").option("--json", "Output results in JSON format").action(async (name, options) => {
|
|
19608
19739
|
try {
|
|
19609
19740
|
const index = await fetchIndex();
|
|
19610
19741
|
const agent = index.agents.find((a) => a.name === name);
|
|
@@ -19616,6 +19747,22 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19616
19747
|
process.exit(1);
|
|
19617
19748
|
}
|
|
19618
19749
|
const category = CATEGORY_LABELS[agent.category] || agent.category;
|
|
19750
|
+
if (options.json) {
|
|
19751
|
+
const jsonOutput = {
|
|
19752
|
+
name: agent.name,
|
|
19753
|
+
displayName: agent.displayName,
|
|
19754
|
+
version: agent.version,
|
|
19755
|
+
description: agent.description,
|
|
19756
|
+
category: agent.category,
|
|
19757
|
+
author: agent.author,
|
|
19758
|
+
tags: agent.tags,
|
|
19759
|
+
minClawVersion: agent.minClawVersion,
|
|
19760
|
+
downloads: agent.downloads,
|
|
19761
|
+
files: agent.files
|
|
19762
|
+
};
|
|
19763
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
19764
|
+
return;
|
|
19765
|
+
}
|
|
19619
19766
|
console.log();
|
|
19620
19767
|
console.log(source_default.bold.cyan(` ${agent.displayName}`));
|
|
19621
19768
|
console.log(source_default.dim(` ${agent.name} v${agent.version}`));
|
|
@@ -19644,7 +19791,7 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19644
19791
|
console.log(` ${fileName} ${source_default.dim(`(${sizeStr})`)}`);
|
|
19645
19792
|
}
|
|
19646
19793
|
if (options.identity || options.soul) {
|
|
19647
|
-
const
|
|
19794
|
+
const fs8 = await import("fs");
|
|
19648
19795
|
const pkgDir = await downloadAgentPackage(name, agent.version);
|
|
19649
19796
|
try {
|
|
19650
19797
|
if (options.identity) {
|
|
@@ -19652,8 +19799,8 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19652
19799
|
console.log(source_default.bold(" \u2500\u2500 IDENTITY.md \u2500\u2500"));
|
|
19653
19800
|
console.log();
|
|
19654
19801
|
const identityPath = (await import("path")).default.join(pkgDir, "IDENTITY.md");
|
|
19655
|
-
if (
|
|
19656
|
-
const content =
|
|
19802
|
+
if (fs8.default.existsSync(identityPath)) {
|
|
19803
|
+
const content = fs8.default.readFileSync(identityPath, "utf-8");
|
|
19657
19804
|
console.log(
|
|
19658
19805
|
content.split("\n").map((l) => ` ${l}`).join("\n")
|
|
19659
19806
|
);
|
|
@@ -19666,8 +19813,8 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19666
19813
|
console.log(source_default.bold(" \u2500\u2500 SOUL.md \u2500\u2500"));
|
|
19667
19814
|
console.log();
|
|
19668
19815
|
const soulPath = (await import("path")).default.join(pkgDir, "SOUL.md");
|
|
19669
|
-
if (
|
|
19670
|
-
const content =
|
|
19816
|
+
if (fs8.default.existsSync(soulPath)) {
|
|
19817
|
+
const content = fs8.default.readFileSync(soulPath, "utf-8");
|
|
19671
19818
|
console.log(
|
|
19672
19819
|
content.split("\n").map((l) => ` ${l}`).join("\n")
|
|
19673
19820
|
);
|
|
@@ -19676,7 +19823,7 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19676
19823
|
}
|
|
19677
19824
|
}
|
|
19678
19825
|
} finally {
|
|
19679
|
-
|
|
19826
|
+
fs8.default.rmSync(pkgDir, { recursive: true, force: true });
|
|
19680
19827
|
}
|
|
19681
19828
|
}
|
|
19682
19829
|
console.log();
|
|
@@ -19778,27 +19925,39 @@ function resolveAllClawDirs(clawDir) {
|
|
|
19778
19925
|
}
|
|
19779
19926
|
return findAllClawDirs();
|
|
19780
19927
|
}
|
|
19781
|
-
var installCommand = new Command("install").description("Install an agent or team from the SoulHub registry
|
|
19928
|
+
var installCommand = new Command("install").description("Install an agent or team from the SoulHub registry").argument("[name]", "Agent or team name to install").option("--from <source>", "Install from a local directory, ZIP file, or URL").option("-r, --role <role>", "Install role: main or worker (skip role selection prompt)").option(
|
|
19782
19929
|
"--dir <path>",
|
|
19783
19930
|
"Target directory (defaults to OpenClaw/LightClaw workspace)"
|
|
19784
19931
|
).option(
|
|
19785
|
-
"--
|
|
19932
|
+
"--claw-type <type>",
|
|
19786
19933
|
"Specify claw type: OpenClaw or LightClaw (case-insensitive)"
|
|
19787
|
-
).action(async (name, options) => {
|
|
19934
|
+
).option("-y, --yes", "Skip all confirmation prompts (auto-confirm)").action(async (name, options) => {
|
|
19788
19935
|
try {
|
|
19789
|
-
const
|
|
19936
|
+
const roleExplicit = !!options.role;
|
|
19937
|
+
const clawExplicit = !!options.clawType || !!options.dir;
|
|
19938
|
+
const skipConfirm = !!options.yes;
|
|
19939
|
+
if (options.role && !["main", "worker"].includes(options.role.toLowerCase())) {
|
|
19940
|
+
console.error(source_default.red(`Invalid role: "${options.role}". Must be "main" or "worker".`));
|
|
19941
|
+
process.exit(1);
|
|
19942
|
+
}
|
|
19790
19943
|
if (options.from) {
|
|
19791
|
-
await
|
|
19944
|
+
const asMain = await resolveRole(roleExplicit ? options.role.toLowerCase() === "main" : void 0);
|
|
19945
|
+
if (asMain === null) return;
|
|
19946
|
+
await installFromSource(options.from, options.dir, options.clawType, asMain, clawExplicit, skipConfirm);
|
|
19792
19947
|
} else if (name) {
|
|
19793
|
-
|
|
19948
|
+
const resolvedRole = roleExplicit ? options.role.toLowerCase() === "main" : void 0;
|
|
19949
|
+
await installFromRegistry(name, options.dir, options.clawType, resolvedRole, clawExplicit, skipConfirm);
|
|
19794
19950
|
} else {
|
|
19795
19951
|
console.error(source_default.red("Please specify an agent or team name, or use --from to install from a local source."));
|
|
19796
|
-
console.log(
|
|
19797
|
-
console.log(source_default.dim("
|
|
19798
|
-
console.log(source_default.dim(" soulhub install
|
|
19799
|
-
console.log(source_default.dim(" soulhub install
|
|
19800
|
-
console.log(source_default.dim(" soulhub install --
|
|
19801
|
-
console.log(source_default.dim(" soulhub install
|
|
19952
|
+
console.log();
|
|
19953
|
+
console.log(source_default.dim(" Usage:"));
|
|
19954
|
+
console.log(source_default.dim(" soulhub install <name> # Interactive: select role & claw"));
|
|
19955
|
+
console.log(source_default.dim(" soulhub install <name> --role main # As main agent, interactive claw selection"));
|
|
19956
|
+
console.log(source_default.dim(" soulhub install <name> --role worker # As worker agent, interactive claw selection"));
|
|
19957
|
+
console.log(source_default.dim(" soulhub install <name> --claw-type LightClaw # Interactive role, install to specific claw"));
|
|
19958
|
+
console.log(source_default.dim(" soulhub install <name> --role worker --claw-type OpenClaw # Fully non-interactive"));
|
|
19959
|
+
console.log(source_default.dim(" soulhub install <name> --role main --claw-type OpenClaw -y # Non-interactive, skip confirmation"));
|
|
19960
|
+
console.log(source_default.dim(" soulhub install --from ./agent-dir/ # Install from local directory"));
|
|
19802
19961
|
process.exit(1);
|
|
19803
19962
|
}
|
|
19804
19963
|
} catch (error) {
|
|
@@ -19810,15 +19969,60 @@ var installCommand = new Command("install").description("Install an agent or tea
|
|
|
19810
19969
|
process.exit(1);
|
|
19811
19970
|
}
|
|
19812
19971
|
});
|
|
19813
|
-
async function
|
|
19972
|
+
async function resolveRole(explicitMain) {
|
|
19973
|
+
if (explicitMain !== void 0) {
|
|
19974
|
+
return explicitMain;
|
|
19975
|
+
}
|
|
19976
|
+
const role = await promptSelectRole();
|
|
19977
|
+
if (role === null) return null;
|
|
19978
|
+
return role === "main";
|
|
19979
|
+
}
|
|
19980
|
+
async function resolveClawDirsInteractive(clawDir, clawExplicit) {
|
|
19981
|
+
if (clawDir) {
|
|
19982
|
+
return resolveAllClawDirs(clawDir);
|
|
19983
|
+
}
|
|
19984
|
+
if (clawExplicit) {
|
|
19985
|
+
return [];
|
|
19986
|
+
}
|
|
19987
|
+
return promptMultiSelectClawDirs();
|
|
19988
|
+
}
|
|
19989
|
+
async function installFromRegistry(name, targetDir, clawDir, asMain, clawExplicit, skipConfirm = false) {
|
|
19814
19990
|
const spinner = createSpinner(`Checking registry for ${source_default.cyan(name)}...`).start();
|
|
19815
19991
|
const index = await fetchIndex();
|
|
19816
19992
|
const agent = index.agents.find((a) => a.name === name);
|
|
19817
19993
|
const recipe = index.recipes.find((r) => r.name === name);
|
|
19818
19994
|
if (agent && !recipe) {
|
|
19819
19995
|
spinner.stop();
|
|
19820
|
-
|
|
19821
|
-
|
|
19996
|
+
printAgentInfo(agent);
|
|
19997
|
+
const resolvedMain = await resolveRole(asMain);
|
|
19998
|
+
if (resolvedMain === null) return;
|
|
19999
|
+
if (resolvedMain) {
|
|
20000
|
+
console.log();
|
|
20001
|
+
console.log(source_default.yellow(" \u26A0 Installing as main agent will overwrite the current workspace/ content."));
|
|
20002
|
+
console.log(source_default.yellow(" The existing persona (IDENTITY.md, SOUL.md, etc.) will be replaced."));
|
|
20003
|
+
console.log(source_default.yellow(" Memory and conversation history will NOT be affected."));
|
|
20004
|
+
console.log();
|
|
20005
|
+
if (!skipConfirm) {
|
|
20006
|
+
const confirmed = await promptConfirm("Continue?", true);
|
|
20007
|
+
if (!confirmed) {
|
|
20008
|
+
console.log(source_default.dim(" Installation cancelled."));
|
|
20009
|
+
return;
|
|
20010
|
+
}
|
|
20011
|
+
} else {
|
|
20012
|
+
console.log(source_default.dim(" Auto-confirmed with --yes flag."));
|
|
20013
|
+
}
|
|
20014
|
+
}
|
|
20015
|
+
let resolvedClawDirs;
|
|
20016
|
+
if (!targetDir) {
|
|
20017
|
+
resolvedClawDirs = await resolveClawDirsInteractive(clawDir, clawExplicit);
|
|
20018
|
+
if (resolvedClawDirs.length === 0) {
|
|
20019
|
+
console.log(source_default.red("\n OpenClaw/LightClaw workspace directory not found."));
|
|
20020
|
+
printOpenClawInstallHelp();
|
|
20021
|
+
return;
|
|
20022
|
+
}
|
|
20023
|
+
}
|
|
20024
|
+
logger.info(`Installing single agent from registry: ${name}, asMain=${resolvedMain}`);
|
|
20025
|
+
await installSingleAgent(name, targetDir, clawDir, resolvedMain, resolvedClawDirs);
|
|
19822
20026
|
} else if (recipe) {
|
|
19823
20027
|
spinner.stop();
|
|
19824
20028
|
logger.info(`Installing team recipe from registry: ${name}`);
|
|
@@ -19829,12 +20033,25 @@ async function installFromRegistry(name, targetDir, clawDir, asMain) {
|
|
|
19829
20033
|
console.log(source_default.dim(" Use 'soulhub search' to find available agents and teams."));
|
|
19830
20034
|
}
|
|
19831
20035
|
}
|
|
19832
|
-
|
|
20036
|
+
function printAgentInfo(agent) {
|
|
20037
|
+
console.log();
|
|
20038
|
+
console.log(source_default.bold(` \u{1F4E6} ${agent.displayName}`), source_default.dim(`v${agent.version}`));
|
|
20039
|
+
if (agent.description) {
|
|
20040
|
+
console.log(source_default.dim(` ${agent.description}`));
|
|
20041
|
+
}
|
|
20042
|
+
if (agent.category) {
|
|
20043
|
+
console.log(source_default.dim(` Category: ${agent.category}`));
|
|
20044
|
+
}
|
|
20045
|
+
if (agent.tags && agent.tags.length > 0) {
|
|
20046
|
+
console.log(source_default.dim(` Tags: ${agent.tags.join(", ")}`));
|
|
20047
|
+
}
|
|
20048
|
+
}
|
|
20049
|
+
async function installSingleAgent(name, targetDir, clawDir, asMain = false, preResolvedClawDirs) {
|
|
19833
20050
|
if (targetDir) {
|
|
19834
20051
|
await installSingleAgentToClaw(name, null, targetDir, asMain);
|
|
19835
20052
|
return;
|
|
19836
20053
|
}
|
|
19837
|
-
const allClawDirs = resolveAllClawDirs(clawDir);
|
|
20054
|
+
const allClawDirs = preResolvedClawDirs || resolveAllClawDirs(clawDir);
|
|
19838
20055
|
if (allClawDirs.length === 0) {
|
|
19839
20056
|
console.log(source_default.red("OpenClaw/LightClaw workspace directory not found."));
|
|
19840
20057
|
printOpenClawInstallHelp();
|
|
@@ -20064,7 +20281,8 @@ async function installRecipeFromRegistry(name, recipe, targetDir, clawDir) {
|
|
|
20064
20281
|
await tryRestartGateway(resolvedClawDir);
|
|
20065
20282
|
}
|
|
20066
20283
|
}
|
|
20067
|
-
async function installFromSource(source, targetDir, clawDir, asMain) {
|
|
20284
|
+
async function installFromSource(source, targetDir, clawDir, asMain, clawExplicit, skipConfirm = false) {
|
|
20285
|
+
if (asMain === null) return;
|
|
20068
20286
|
const spinner = createSpinner("Analyzing package...").start();
|
|
20069
20287
|
let packageDir;
|
|
20070
20288
|
let tempDir = null;
|
|
@@ -20118,7 +20336,7 @@ async function installFromSource(source, targetDir, clawDir, asMain) {
|
|
|
20118
20336
|
spinner.text = `Detected package type: ${source_default.blue(kind)}`;
|
|
20119
20337
|
if (kind === "agent") {
|
|
20120
20338
|
spinner.stop();
|
|
20121
|
-
await installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain);
|
|
20339
|
+
await installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain, clawExplicit, skipConfirm);
|
|
20122
20340
|
} else if (kind === "team") {
|
|
20123
20341
|
spinner.stop();
|
|
20124
20342
|
await installTeamFromDir(packageDir, targetDir, clawDir);
|
|
@@ -20130,14 +20348,30 @@ async function installFromSource(source, targetDir, clawDir, asMain) {
|
|
|
20130
20348
|
import_node_fs9.default.rmSync(tempDir, { recursive: true, force: true });
|
|
20131
20349
|
}
|
|
20132
20350
|
}
|
|
20133
|
-
async function installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain = false) {
|
|
20351
|
+
async function installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain = false, clawExplicit, skipConfirm = false) {
|
|
20134
20352
|
const pkg = readSoulHubPackage(packageDir);
|
|
20135
20353
|
const agentName = pkg?.name || import_node_path12.default.basename(packageDir);
|
|
20354
|
+
if (asMain) {
|
|
20355
|
+
console.log();
|
|
20356
|
+
console.log(source_default.yellow(" \u26A0 Installing as main agent will overwrite the current workspace/ content."));
|
|
20357
|
+
console.log(source_default.yellow(" The existing persona (IDENTITY.md, SOUL.md, etc.) will be replaced."));
|
|
20358
|
+
console.log(source_default.yellow(" Memory and conversation history will NOT be affected."));
|
|
20359
|
+
console.log();
|
|
20360
|
+
if (!skipConfirm) {
|
|
20361
|
+
const confirmed = await promptConfirm("Continue?", true);
|
|
20362
|
+
if (!confirmed) {
|
|
20363
|
+
console.log(source_default.dim(" Installation cancelled."));
|
|
20364
|
+
return;
|
|
20365
|
+
}
|
|
20366
|
+
} else {
|
|
20367
|
+
console.log(source_default.dim(" Auto-confirmed with --yes flag."));
|
|
20368
|
+
}
|
|
20369
|
+
}
|
|
20136
20370
|
if (targetDir) {
|
|
20137
20371
|
await installSingleAgentFromDirToClaw(packageDir, agentName, pkg, null, targetDir, asMain);
|
|
20138
20372
|
return;
|
|
20139
20373
|
}
|
|
20140
|
-
const allClawDirs =
|
|
20374
|
+
const allClawDirs = await resolveClawDirsInteractive(clawDir, clawExplicit);
|
|
20141
20375
|
if (allClawDirs.length === 0) {
|
|
20142
20376
|
console.log(source_default.red("OpenClaw/LightClaw workspace directory not found."));
|
|
20143
20377
|
printOpenClawInstallHelp();
|
|
@@ -20418,7 +20652,7 @@ async function extractZipToDir(zip, targetDir) {
|
|
|
20418
20652
|
}
|
|
20419
20653
|
function printOpenClawInstallHelp() {
|
|
20420
20654
|
console.log(source_default.dim(" Please install OpenClaw or LightClaw first, or use one of the following options:"));
|
|
20421
|
-
console.log(source_default.dim(" --
|
|
20655
|
+
console.log(source_default.dim(" --claw-type <type> Specify claw type: OpenClaw or LightClaw"));
|
|
20422
20656
|
console.log(source_default.dim(" --dir <path> Specify agent target directory directly"));
|
|
20423
20657
|
console.log(source_default.dim(" OPENCLAW_HOME=<path> Set environment variable (for OpenClaw)"));
|
|
20424
20658
|
console.log(source_default.dim(" LIGHTCLAW_HOME=<path> Set environment variable (for LightClaw)"));
|
|
@@ -20451,17 +20685,31 @@ async function tryRestartGateway(clawDir) {
|
|
|
20451
20685
|
}
|
|
20452
20686
|
|
|
20453
20687
|
// src/commands/list.ts
|
|
20454
|
-
var listCommand = new Command("list").description("List installed agents").alias("ls").action(async () => {
|
|
20688
|
+
var listCommand = new Command("list").description("List installed agents").alias("ls").option("--json", "Output results in JSON format").action(async (options) => {
|
|
20455
20689
|
try {
|
|
20456
20690
|
const config = loadConfig();
|
|
20457
20691
|
if (config.installed.length === 0) {
|
|
20458
|
-
|
|
20459
|
-
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
|
|
20463
|
-
|
|
20464
|
-
|
|
20692
|
+
if (options.json) {
|
|
20693
|
+
console.log(JSON.stringify([], null, 2));
|
|
20694
|
+
} else {
|
|
20695
|
+
console.log(source_default.yellow("\n No agents installed yet.\n"));
|
|
20696
|
+
console.log(
|
|
20697
|
+
source_default.dim(" Install one: soulhub install <name>")
|
|
20698
|
+
);
|
|
20699
|
+
console.log(
|
|
20700
|
+
source_default.dim(" Browse all: soulhub search\n")
|
|
20701
|
+
);
|
|
20702
|
+
}
|
|
20703
|
+
return;
|
|
20704
|
+
}
|
|
20705
|
+
if (options.json) {
|
|
20706
|
+
const jsonOutput = config.installed.map((a) => ({
|
|
20707
|
+
name: a.name,
|
|
20708
|
+
version: a.version,
|
|
20709
|
+
installedAt: a.installedAt,
|
|
20710
|
+
workspace: a.workspace
|
|
20711
|
+
}));
|
|
20712
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
20465
20713
|
return;
|
|
20466
20714
|
}
|
|
20467
20715
|
console.log(
|
|
@@ -20519,6 +20767,19 @@ var updateCommand = new Command("update").description("Update installed agents t
|
|
|
20519
20767
|
}
|
|
20520
20768
|
spinner.text = `Updating ${source_default.cyan(installed.name)} (${installed.version} \u2192 ${remote.version})...`;
|
|
20521
20769
|
const workspaceDir = installed.workspace;
|
|
20770
|
+
const backupDir = backupAgentWorkspace(workspaceDir);
|
|
20771
|
+
if (backupDir) {
|
|
20772
|
+
const backupRecord = createBackupRecord("single-agent", installed.name, workspaceDir);
|
|
20773
|
+
addBackupItem(backupRecord, {
|
|
20774
|
+
originalPath: workspaceDir,
|
|
20775
|
+
backupPath: backupDir,
|
|
20776
|
+
method: "cp",
|
|
20777
|
+
role: "worker",
|
|
20778
|
+
agentId: installed.name
|
|
20779
|
+
});
|
|
20780
|
+
commitBackupRecord(backupRecord);
|
|
20781
|
+
logger.info(`Update backup created for ${installed.name}`, { backupDir });
|
|
20782
|
+
}
|
|
20522
20783
|
if (!import_node_fs10.default.existsSync(workspaceDir)) {
|
|
20523
20784
|
import_node_fs10.default.mkdirSync(workspaceDir, { recursive: true });
|
|
20524
20785
|
}
|
|
@@ -20547,20 +20808,109 @@ var updateCommand = new Command("update").description("Update installed agents t
|
|
|
20547
20808
|
}
|
|
20548
20809
|
});
|
|
20549
20810
|
|
|
20550
|
-
// src/commands/
|
|
20811
|
+
// src/commands/uninstall.ts
|
|
20551
20812
|
var import_node_fs11 = __toESM(require("fs"), 1);
|
|
20813
|
+
var uninstallCommand = new Command("uninstall").description("Uninstall an agent template").alias("rm").argument("<name>", "Agent name to uninstall").option("--keep-files", "Remove from registry but keep workspace files").option("-y, --yes", "Skip all confirmation prompts (auto-confirm)").action(async (name, options) => {
|
|
20814
|
+
try {
|
|
20815
|
+
const config = loadConfig();
|
|
20816
|
+
const installed = config.installed.find((a) => a.name === name);
|
|
20817
|
+
if (!installed) {
|
|
20818
|
+
console.error(
|
|
20819
|
+
source_default.red(`
|
|
20820
|
+
Agent "${name}" is not installed.
|
|
20821
|
+
`)
|
|
20822
|
+
);
|
|
20823
|
+
console.log(
|
|
20824
|
+
source_default.dim(" Use 'soulhub list' to see installed agents.")
|
|
20825
|
+
);
|
|
20826
|
+
process.exit(1);
|
|
20827
|
+
}
|
|
20828
|
+
const spinner = createSpinner(
|
|
20829
|
+
`Uninstalling ${source_default.cyan(name)}...`
|
|
20830
|
+
).start();
|
|
20831
|
+
const manifest = loadBackupManifest();
|
|
20832
|
+
const relatedRecords = manifest.records.filter((r) => r.packageName === name);
|
|
20833
|
+
if (relatedRecords.length > 0) {
|
|
20834
|
+
spinner.stop();
|
|
20835
|
+
console.log(
|
|
20836
|
+
source_default.yellow(`
|
|
20837
|
+
\u26A0 Found ${relatedRecords.length} backup record(s) for "${name}".`)
|
|
20838
|
+
);
|
|
20839
|
+
console.log(
|
|
20840
|
+
source_default.yellow(` Uninstalling will also delete all related backup files.`)
|
|
20841
|
+
);
|
|
20842
|
+
console.log(
|
|
20843
|
+
source_default.yellow(` After deletion, you will NOT be able to rollback this agent.
|
|
20844
|
+
`)
|
|
20845
|
+
);
|
|
20846
|
+
if (!options.yes) {
|
|
20847
|
+
const confirmed = await promptConfirm("Proceed with uninstall?");
|
|
20848
|
+
if (!confirmed) {
|
|
20849
|
+
console.log(source_default.dim("\n Uninstall cancelled.\n"));
|
|
20850
|
+
return;
|
|
20851
|
+
}
|
|
20852
|
+
} else {
|
|
20853
|
+
console.log(source_default.dim(" Auto-confirmed with --yes flag."));
|
|
20854
|
+
}
|
|
20855
|
+
spinner.start(`Uninstalling ${source_default.cyan(name)}...`);
|
|
20856
|
+
}
|
|
20857
|
+
if (!options.keepFiles && import_node_fs11.default.existsSync(installed.workspace)) {
|
|
20858
|
+
import_node_fs11.default.rmSync(installed.workspace, { recursive: true, force: true });
|
|
20859
|
+
spinner.text = `Removed workspace: ${installed.workspace}`;
|
|
20860
|
+
}
|
|
20861
|
+
removeInstallRecord(name);
|
|
20862
|
+
if (relatedRecords.length > 0) {
|
|
20863
|
+
for (const record of relatedRecords) {
|
|
20864
|
+
for (const item of record.items) {
|
|
20865
|
+
if (import_node_fs11.default.existsSync(item.backupPath)) {
|
|
20866
|
+
import_node_fs11.default.rmSync(item.backupPath, { recursive: true, force: true });
|
|
20867
|
+
logger.info(`Cleaned backup file for uninstalled agent`, { backupPath: item.backupPath });
|
|
20868
|
+
}
|
|
20869
|
+
}
|
|
20870
|
+
}
|
|
20871
|
+
manifest.records = manifest.records.filter((r) => r.packageName !== name);
|
|
20872
|
+
saveBackupManifest(manifest);
|
|
20873
|
+
spinner.text = `Cleaned ${relatedRecords.length} backup record(s)`;
|
|
20874
|
+
logger.info(`Cleaned ${relatedRecords.length} backup record(s) for ${name}`);
|
|
20875
|
+
}
|
|
20876
|
+
logger.info(`Agent uninstalled: ${name}`, { workspace: installed.workspace, keepFiles: !!options.keepFiles });
|
|
20877
|
+
spinner.succeed(
|
|
20878
|
+
`${source_default.cyan.bold(name)} uninstalled.`
|
|
20879
|
+
);
|
|
20880
|
+
if (options.keepFiles) {
|
|
20881
|
+
console.log(
|
|
20882
|
+
source_default.dim(` Files kept at: ${installed.workspace}`)
|
|
20883
|
+
);
|
|
20884
|
+
}
|
|
20885
|
+
console.log();
|
|
20886
|
+
} catch (error) {
|
|
20887
|
+
logger.errorObj("Uninstall command failed", error);
|
|
20888
|
+
console.error(
|
|
20889
|
+
source_default.red(`Error: ${error instanceof Error ? error.message : error}`)
|
|
20890
|
+
);
|
|
20891
|
+
console.error(source_default.dim(` See logs: ${logger.getTodayLogFile()}`));
|
|
20892
|
+
process.exit(1);
|
|
20893
|
+
}
|
|
20894
|
+
});
|
|
20895
|
+
|
|
20896
|
+
// src/commands/rollback.ts
|
|
20897
|
+
var import_node_fs12 = __toESM(require("fs"), 1);
|
|
20552
20898
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
20553
|
-
var
|
|
20554
|
-
|
|
20899
|
+
var import_node_readline2 = __toESM(require("readline"), 1);
|
|
20900
|
+
var rollbackCommand = new Command("rollback").description("Rollback to a previous agent installation state").option("--list", "List available rollback records").option("--id <id>", "Rollback to a specific backup record by ID").option("--last <n>", "Rollback the Nth most recent installation (1 = latest, 2 = second latest, etc.)", parseInt).option(
|
|
20901
|
+
"--claw-type <type>",
|
|
20555
20902
|
"Specify claw type: OpenClaw or LightClaw (case-insensitive)"
|
|
20556
|
-
).action(async (options) => {
|
|
20903
|
+
).option("-y, --yes", "Skip all confirmation prompts (auto-confirm)").action(async (options) => {
|
|
20557
20904
|
try {
|
|
20905
|
+
const skipConfirm = !!options.yes;
|
|
20558
20906
|
if (options.list) {
|
|
20559
20907
|
listBackupRecords();
|
|
20560
20908
|
} else if (options.id) {
|
|
20561
|
-
await performRollback(options.id, options.
|
|
20909
|
+
await performRollback(options.id, options.clawType, skipConfirm);
|
|
20910
|
+
} else if (options.last) {
|
|
20911
|
+
await performRollbackByIndex(options.last, options.clawType, skipConfirm);
|
|
20562
20912
|
} else {
|
|
20563
|
-
await
|
|
20913
|
+
await interactiveRollback(options.clawType);
|
|
20564
20914
|
}
|
|
20565
20915
|
} catch (error) {
|
|
20566
20916
|
logger.errorObj("Rollback command failed", error);
|
|
@@ -20579,53 +20929,192 @@ function listBackupRecords() {
|
|
|
20579
20929
|
return;
|
|
20580
20930
|
}
|
|
20581
20931
|
console.log(source_default.bold("\nAvailable rollback records:\n"));
|
|
20932
|
+
printRecordTable(manifest.records);
|
|
20933
|
+
console.log();
|
|
20934
|
+
console.log(source_default.dim(" Usage:"));
|
|
20935
|
+
console.log(source_default.dim(" soulhub rollback # Interactive: select a record to rollback"));
|
|
20936
|
+
console.log(source_default.dim(" soulhub rollback --last 1 # Rollback the latest installation"));
|
|
20937
|
+
console.log(source_default.dim(" soulhub rollback --last 2 # Rollback the 2nd latest installation"));
|
|
20938
|
+
console.log(source_default.dim(" soulhub rollback --id <id> # Rollback to a specific record by ID"));
|
|
20939
|
+
console.log();
|
|
20940
|
+
}
|
|
20941
|
+
function printRecordTable(records) {
|
|
20582
20942
|
console.log(
|
|
20583
20943
|
source_default.dim(
|
|
20584
|
-
` ${"ID".padEnd(20)} ${"Type".padEnd(20)} ${"Package".padEnd(20)} ${"Date".padEnd(22)} Items`
|
|
20944
|
+
` ${"#".padEnd(4)} ${"ID".padEnd(20)} ${"Type".padEnd(20)} ${"Package".padEnd(20)} ${"Claw".padEnd(14)} ${"Date".padEnd(22)} Items`
|
|
20585
20945
|
)
|
|
20586
20946
|
);
|
|
20587
|
-
console.log(source_default.dim(" " + "\u2500".repeat(
|
|
20588
|
-
|
|
20947
|
+
console.log(source_default.dim(" " + "\u2500".repeat(108)));
|
|
20948
|
+
records.forEach((record, index) => {
|
|
20589
20949
|
const date = new Date(record.createdAt).toLocaleString();
|
|
20590
20950
|
const typeLabel = formatInstallType(record.installType);
|
|
20591
20951
|
const itemCount = record.items.length;
|
|
20952
|
+
const clawBrand = detectClawBrandFromDir(record.clawDir);
|
|
20592
20953
|
console.log(
|
|
20593
|
-
` ${source_default.cyan(record.id.padEnd(20))} ${typeLabel.padEnd(20)} ${source_default.white(record.packageName.padEnd(20))} ${source_default.dim(date.padEnd(22))} ${itemCount} backup(s)`
|
|
20954
|
+
` ${source_default.yellow(String(index + 1).padEnd(4))} ${source_default.cyan(record.id.padEnd(20))} ${typeLabel.padEnd(20)} ${source_default.white(record.packageName.padEnd(20))} ${source_default.dim(clawBrand.padEnd(14))} ${source_default.dim(date.padEnd(22))} ${itemCount} backup(s)`
|
|
20594
20955
|
);
|
|
20956
|
+
});
|
|
20957
|
+
}
|
|
20958
|
+
async function interactiveRollback(clawDir) {
|
|
20959
|
+
const manifest = loadBackupManifest();
|
|
20960
|
+
if (manifest.records.length === 0) {
|
|
20961
|
+
console.log(source_default.yellow("No backup records found. Nothing to rollback."));
|
|
20962
|
+
console.log(source_default.dim(" Backup records are created automatically when you install agents."));
|
|
20963
|
+
return;
|
|
20595
20964
|
}
|
|
20965
|
+
console.log(source_default.bold("\n Select a record to rollback:\n"));
|
|
20966
|
+
printRecordTable(manifest.records);
|
|
20596
20967
|
console.log();
|
|
20597
|
-
|
|
20598
|
-
|
|
20599
|
-
|
|
20968
|
+
const rl = import_node_readline2.default.createInterface({
|
|
20969
|
+
input: process.stdin,
|
|
20970
|
+
output: process.stdout
|
|
20971
|
+
});
|
|
20972
|
+
const selected = await new Promise((resolve) => {
|
|
20973
|
+
rl.question(` Enter number to rollback (1-${manifest.records.length}), or 'q' to cancel: `, (answer) => {
|
|
20974
|
+
rl.close();
|
|
20975
|
+
const trimmed = answer.trim().toLowerCase();
|
|
20976
|
+
if (trimmed === "q" || trimmed === "quit" || trimmed === "") {
|
|
20977
|
+
resolve(null);
|
|
20978
|
+
return;
|
|
20979
|
+
}
|
|
20980
|
+
const idx = parseInt(trimmed, 10);
|
|
20981
|
+
if (idx >= 1 && idx <= manifest.records.length) {
|
|
20982
|
+
resolve(idx);
|
|
20983
|
+
} else {
|
|
20984
|
+
resolve(null);
|
|
20985
|
+
}
|
|
20986
|
+
});
|
|
20987
|
+
});
|
|
20988
|
+
if (selected === null) {
|
|
20989
|
+
console.log(source_default.dim(" Rollback cancelled."));
|
|
20990
|
+
return;
|
|
20991
|
+
}
|
|
20992
|
+
const record = manifest.records[selected - 1];
|
|
20600
20993
|
console.log();
|
|
20994
|
+
console.log(source_default.dim(` Selected: ${source_default.cyan(record.id)} (${record.packageName})`));
|
|
20995
|
+
printRollbackDetails(record);
|
|
20996
|
+
const confirmed = await promptConfirmRollback();
|
|
20997
|
+
if (!confirmed) {
|
|
20998
|
+
console.log(source_default.dim(" Rollback cancelled."));
|
|
20999
|
+
return;
|
|
21000
|
+
}
|
|
21001
|
+
await executeRollback(record, clawDir);
|
|
20601
21002
|
}
|
|
20602
|
-
async function
|
|
21003
|
+
async function performRollbackByIndex(n, clawDir, skipConfirm = false) {
|
|
20603
21004
|
const manifest = loadBackupManifest();
|
|
20604
21005
|
if (manifest.records.length === 0) {
|
|
20605
21006
|
console.log(source_default.yellow("No backup records found. Nothing to rollback."));
|
|
20606
21007
|
return;
|
|
20607
21008
|
}
|
|
20608
|
-
|
|
20609
|
-
|
|
20610
|
-
|
|
20611
|
-
|
|
20612
|
-
|
|
20613
|
-
|
|
21009
|
+
if (n < 1 || n > manifest.records.length) {
|
|
21010
|
+
console.error(source_default.red(`Invalid index: ${n}. Available range: 1-${manifest.records.length}`));
|
|
21011
|
+
console.log(source_default.dim(" Use 'soulhub rollback --list' to see all available records."));
|
|
21012
|
+
return;
|
|
21013
|
+
}
|
|
21014
|
+
const record = manifest.records[n - 1];
|
|
21015
|
+
console.log(
|
|
21016
|
+
source_default.dim(
|
|
21017
|
+
`
|
|
21018
|
+
Rolling back #${n}: ${source_default.cyan(record.id)} (${record.packageName})`
|
|
21019
|
+
)
|
|
21020
|
+
);
|
|
21021
|
+
printRollbackDetails(record);
|
|
21022
|
+
if (!skipConfirm) {
|
|
21023
|
+
const rl2 = import_node_readline2.default.createInterface({
|
|
21024
|
+
input: process.stdin,
|
|
21025
|
+
output: process.stdout
|
|
21026
|
+
});
|
|
21027
|
+
const confirmed = await new Promise((resolve) => {
|
|
21028
|
+
rl2.question(` ${source_default.yellow("\u26A0")} Proceed with rollback? (Y/n) `, (answer) => {
|
|
21029
|
+
rl2.close();
|
|
21030
|
+
const trimmed = answer.trim().toLowerCase();
|
|
21031
|
+
resolve(trimmed === "" || trimmed === "y" || trimmed === "yes");
|
|
21032
|
+
});
|
|
21033
|
+
});
|
|
21034
|
+
if (!confirmed) {
|
|
21035
|
+
console.log(source_default.dim(" Rollback cancelled."));
|
|
20614
21036
|
return;
|
|
20615
21037
|
}
|
|
20616
21038
|
} else {
|
|
20617
|
-
|
|
20618
|
-
|
|
20619
|
-
|
|
20620
|
-
|
|
20621
|
-
|
|
20622
|
-
|
|
21039
|
+
console.log(source_default.dim(" Auto-confirmed with --yes flag."));
|
|
21040
|
+
}
|
|
21041
|
+
await executeRollback(record, clawDir);
|
|
21042
|
+
}
|
|
21043
|
+
async function performRollback(recordId, clawDir, skipConfirm = false) {
|
|
21044
|
+
const manifest = loadBackupManifest();
|
|
21045
|
+
if (manifest.records.length === 0) {
|
|
21046
|
+
console.log(source_default.yellow("No backup records found. Nothing to rollback."));
|
|
21047
|
+
return;
|
|
20623
21048
|
}
|
|
21049
|
+
const record = manifest.records.find((r) => r.id === recordId);
|
|
21050
|
+
if (!record) {
|
|
21051
|
+
console.error(source_default.red(`Backup record "${recordId}" not found.`));
|
|
21052
|
+
console.log(source_default.dim(" Use 'soulhub rollback --list' to see available records."));
|
|
21053
|
+
return;
|
|
21054
|
+
}
|
|
21055
|
+
console.log(
|
|
21056
|
+
source_default.dim(
|
|
21057
|
+
`
|
|
21058
|
+
Rolling back: ${source_default.cyan(record.id)} (${record.packageName})`
|
|
21059
|
+
)
|
|
21060
|
+
);
|
|
21061
|
+
printRollbackDetails(record);
|
|
21062
|
+
if (!skipConfirm) {
|
|
21063
|
+
const confirmed = await promptConfirmRollback();
|
|
21064
|
+
if (!confirmed) {
|
|
21065
|
+
console.log(source_default.dim(" Rollback cancelled."));
|
|
21066
|
+
return;
|
|
21067
|
+
}
|
|
21068
|
+
} else {
|
|
21069
|
+
console.log(source_default.dim(" Auto-confirmed with --yes flag."));
|
|
21070
|
+
}
|
|
21071
|
+
await executeRollback(record, clawDir);
|
|
21072
|
+
}
|
|
21073
|
+
function printRollbackDetails(record) {
|
|
21074
|
+
console.log();
|
|
21075
|
+
console.log(source_default.dim(" Rollback details:"));
|
|
21076
|
+
console.log(source_default.dim(` Package: ${record.packageName}`));
|
|
21077
|
+
console.log(source_default.dim(` Type: ${formatInstallType(record.installType)}`));
|
|
21078
|
+
console.log(source_default.dim(` Claw dir: ${record.clawDir}`));
|
|
21079
|
+
console.log(source_default.dim(` Date: ${new Date(record.createdAt).toLocaleString()}`));
|
|
21080
|
+
if (record.items.length > 0) {
|
|
21081
|
+
console.log(source_default.dim(` Backups to restore:`));
|
|
21082
|
+
for (const item of record.items) {
|
|
21083
|
+
const methodLabel = item.method === "mv" ? "move back" : "copy back";
|
|
21084
|
+
console.log(source_default.dim(` - ${item.agentId} (${item.role}, ${methodLabel})`));
|
|
21085
|
+
}
|
|
21086
|
+
}
|
|
21087
|
+
if (record.installedWorkerIds.length > 0) {
|
|
21088
|
+
console.log(source_default.dim(` Workers to remove: ${record.installedWorkerIds.join(", ")}`));
|
|
21089
|
+
}
|
|
21090
|
+
if (record.installedMainAgent) {
|
|
21091
|
+
console.log(source_default.dim(` Main agent to revert: ${record.installedMainAgent}`));
|
|
21092
|
+
}
|
|
21093
|
+
console.log();
|
|
21094
|
+
}
|
|
21095
|
+
async function promptConfirmRollback() {
|
|
21096
|
+
const rl = import_node_readline2.default.createInterface({
|
|
21097
|
+
input: process.stdin,
|
|
21098
|
+
output: process.stdout
|
|
21099
|
+
});
|
|
21100
|
+
return new Promise((resolve) => {
|
|
21101
|
+
rl.question(` ${source_default.yellow("\u26A0")} Proceed with rollback? (Y/n) `, (answer) => {
|
|
21102
|
+
rl.close();
|
|
21103
|
+
const trimmed = answer.trim().toLowerCase();
|
|
21104
|
+
if (trimmed === "" || trimmed === "y" || trimmed === "yes") {
|
|
21105
|
+
resolve(true);
|
|
21106
|
+
} else {
|
|
21107
|
+
resolve(false);
|
|
21108
|
+
}
|
|
21109
|
+
});
|
|
21110
|
+
});
|
|
21111
|
+
}
|
|
21112
|
+
async function executeRollback(record, clawDir) {
|
|
20624
21113
|
const spinner = createSpinner(
|
|
20625
21114
|
`Rolling back ${source_default.cyan(record.packageName)}...`
|
|
20626
21115
|
).start();
|
|
20627
|
-
const resolvedClawDir = clawDir ? findOpenClawDir(clawDir) || record.clawDir :
|
|
20628
|
-
if (!resolvedClawDir || !
|
|
21116
|
+
const resolvedClawDir = clawDir ? findOpenClawDir(clawDir) || record.clawDir : record.clawDir;
|
|
21117
|
+
if (!resolvedClawDir || !import_node_fs12.default.existsSync(resolvedClawDir)) {
|
|
20629
21118
|
spinner.fail(`OpenClaw/LightClaw directory not found: ${record.clawDir}`);
|
|
20630
21119
|
return;
|
|
20631
21120
|
}
|
|
@@ -20645,57 +21134,63 @@ async function performRollback(recordId, clawDir) {
|
|
|
20645
21134
|
spinner.text = "Removing installed workers...";
|
|
20646
21135
|
for (const workerId of record.installedWorkerIds) {
|
|
20647
21136
|
const workerDir = getWorkspaceDir(resolvedClawDir, workerId);
|
|
20648
|
-
if (
|
|
20649
|
-
|
|
21137
|
+
if (import_node_fs12.default.existsSync(workerDir)) {
|
|
21138
|
+
import_node_fs12.default.rmSync(workerDir, { recursive: true, force: true });
|
|
20650
21139
|
logger.info(`Removed installed worker directory`, { workerId, dir: workerDir });
|
|
20651
21140
|
}
|
|
20652
21141
|
const agentConfigDir = import_node_path13.default.join(resolvedClawDir, "agents", workerId);
|
|
20653
|
-
if (
|
|
20654
|
-
|
|
21142
|
+
if (import_node_fs12.default.existsSync(agentConfigDir)) {
|
|
21143
|
+
import_node_fs12.default.rmSync(agentConfigDir, { recursive: true, force: true });
|
|
20655
21144
|
}
|
|
20656
21145
|
}
|
|
20657
21146
|
}
|
|
20658
21147
|
if (record.installedMainAgent) {
|
|
20659
21148
|
const mainWorkspace = getMainWorkspaceDir(resolvedClawDir);
|
|
20660
21149
|
const hasMainBackup = record.items.some((item) => item.role === "main");
|
|
20661
|
-
if (hasMainBackup &&
|
|
21150
|
+
if (hasMainBackup && import_node_fs12.default.existsSync(mainWorkspace)) {
|
|
20662
21151
|
spinner.text = "Cleaning current main workspace...";
|
|
20663
|
-
const entries =
|
|
21152
|
+
const entries = import_node_fs12.default.readdirSync(mainWorkspace);
|
|
20664
21153
|
for (const entry of entries) {
|
|
20665
|
-
|
|
21154
|
+
import_node_fs12.default.rmSync(import_node_path13.default.join(mainWorkspace, entry), { recursive: true, force: true });
|
|
20666
21155
|
}
|
|
20667
21156
|
}
|
|
20668
21157
|
}
|
|
20669
21158
|
let restoredCount = 0;
|
|
20670
21159
|
for (const item of record.items) {
|
|
20671
|
-
if (!
|
|
21160
|
+
if (!import_node_fs12.default.existsSync(item.backupPath)) {
|
|
20672
21161
|
logger.warn(`Backup path not found, skipping`, { backupPath: item.backupPath });
|
|
20673
21162
|
console.log(source_default.yellow(` \u26A0 Backup not found: ${item.backupPath}, skipping...`));
|
|
20674
21163
|
continue;
|
|
20675
21164
|
}
|
|
20676
21165
|
spinner.text = `Restoring ${source_default.cyan(item.agentId)} (${item.role})...`;
|
|
20677
21166
|
if (item.method === "mv") {
|
|
20678
|
-
if (
|
|
20679
|
-
|
|
21167
|
+
if (import_node_fs12.default.existsSync(item.originalPath)) {
|
|
21168
|
+
import_node_fs12.default.rmSync(item.originalPath, { recursive: true, force: true });
|
|
21169
|
+
}
|
|
21170
|
+
import_node_fs12.default.mkdirSync(import_node_path13.default.dirname(item.originalPath), { recursive: true });
|
|
21171
|
+
try {
|
|
21172
|
+
import_node_fs12.default.renameSync(item.backupPath, item.originalPath);
|
|
21173
|
+
} catch {
|
|
21174
|
+
import_node_fs12.default.cpSync(item.backupPath, item.originalPath, { recursive: true });
|
|
21175
|
+
import_node_fs12.default.rmSync(item.backupPath, { recursive: true, force: true });
|
|
20680
21176
|
}
|
|
20681
|
-
import_node_fs11.default.mkdirSync(import_node_path13.default.dirname(item.originalPath), { recursive: true });
|
|
20682
|
-
import_node_fs11.default.renameSync(item.backupPath, item.originalPath);
|
|
20683
21177
|
logger.info(`Restored (mv back)`, { from: item.backupPath, to: item.originalPath });
|
|
20684
21178
|
} else {
|
|
20685
|
-
if (
|
|
20686
|
-
const entries =
|
|
21179
|
+
if (import_node_fs12.default.existsSync(item.originalPath)) {
|
|
21180
|
+
const entries = import_node_fs12.default.readdirSync(item.originalPath);
|
|
20687
21181
|
for (const entry of entries) {
|
|
20688
|
-
|
|
21182
|
+
import_node_fs12.default.rmSync(import_node_path13.default.join(item.originalPath, entry), { recursive: true, force: true });
|
|
20689
21183
|
}
|
|
20690
21184
|
} else {
|
|
20691
|
-
|
|
21185
|
+
import_node_fs12.default.mkdirSync(item.originalPath, { recursive: true });
|
|
20692
21186
|
}
|
|
20693
|
-
|
|
20694
|
-
|
|
21187
|
+
import_node_fs12.default.cpSync(item.backupPath, item.originalPath, { recursive: true });
|
|
21188
|
+
import_node_fs12.default.rmSync(item.backupPath, { recursive: true, force: true });
|
|
20695
21189
|
logger.info(`Restored (cp back)`, { from: item.backupPath, to: item.originalPath });
|
|
20696
21190
|
}
|
|
20697
21191
|
restoredCount++;
|
|
20698
21192
|
}
|
|
21193
|
+
const manifest = loadBackupManifest();
|
|
20699
21194
|
manifest.records = manifest.records.filter((r) => r.id !== record.id);
|
|
20700
21195
|
saveBackupManifest(manifest);
|
|
20701
21196
|
spinner.succeed(
|
|
@@ -20729,16 +21224,23 @@ function formatInstallType(type2) {
|
|
|
20729
21224
|
return type2;
|
|
20730
21225
|
}
|
|
20731
21226
|
}
|
|
21227
|
+
function detectClawBrandFromDir(clawDir) {
|
|
21228
|
+
const dirName = import_node_path13.default.basename(clawDir).toLowerCase();
|
|
21229
|
+
if (dirName.includes("lightclaw")) {
|
|
21230
|
+
return "LightClaw";
|
|
21231
|
+
}
|
|
21232
|
+
return "OpenClaw";
|
|
21233
|
+
}
|
|
20732
21234
|
|
|
20733
21235
|
// src/index.ts
|
|
20734
21236
|
var program2 = new Command();
|
|
20735
|
-
program2.name("soulhub").description("SoulHub CLI - Discover, install and manage AI agent souls").version("1.0.
|
|
21237
|
+
program2.name("soulhub").description("SoulHub CLI - Discover, install and manage AI agent souls").version("1.0.22").option("--verbose", "Enable verbose debug logging").hook("preAction", () => {
|
|
20736
21238
|
const opts = program2.opts();
|
|
20737
21239
|
const verbose = opts.verbose || process.env.SOULHUB_DEBUG === "1";
|
|
20738
21240
|
logger.init(verbose);
|
|
20739
21241
|
logger.info("CLI started", {
|
|
20740
21242
|
args: process.argv.slice(2),
|
|
20741
|
-
version: "1.0.
|
|
21243
|
+
version: "1.0.22",
|
|
20742
21244
|
node: process.version
|
|
20743
21245
|
});
|
|
20744
21246
|
});
|
|
@@ -20747,6 +21249,7 @@ program2.addCommand(infoCommand);
|
|
|
20747
21249
|
program2.addCommand(installCommand);
|
|
20748
21250
|
program2.addCommand(listCommand);
|
|
20749
21251
|
program2.addCommand(updateCommand);
|
|
21252
|
+
program2.addCommand(uninstallCommand);
|
|
20750
21253
|
program2.addCommand(rollbackCommand);
|
|
20751
21254
|
program2.parse();
|
|
20752
21255
|
/*! Bundled license information:
|