soulhubcli 1.0.20 → 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 +627 -119
- package/package.json +1 -1
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",
|
|
@@ -19440,11 +19555,12 @@ var CATEGORY_LABELS = {
|
|
|
19440
19555
|
education: "Education",
|
|
19441
19556
|
dispatcher: "Dispatcher"
|
|
19442
19557
|
};
|
|
19443
|
-
function registerAgentToOpenClaw(agentName, workspaceDir,
|
|
19558
|
+
function registerAgentToOpenClaw(agentName, workspaceDir, clawDir) {
|
|
19444
19559
|
const agentId = agentName.toLowerCase().replace(/[\s_]+/g, "-");
|
|
19445
|
-
|
|
19560
|
+
const brandName = clawDir ? detectClawBrand(clawDir) : "OpenClaw/LightClaw";
|
|
19561
|
+
logger.debug(`Registering agent to ${brandName}`, { agentId, workspaceDir });
|
|
19446
19562
|
try {
|
|
19447
|
-
const clawCmd = detectClawCommand();
|
|
19563
|
+
const clawCmd = detectClawCommand(clawDir);
|
|
19448
19564
|
(0, import_node_child_process.execSync)(
|
|
19449
19565
|
`${clawCmd} agents add "${agentId}" --workspace "${workspaceDir}" --non-interactive --json`,
|
|
19450
19566
|
{ stdio: "pipe", timeout: 15e3 }
|
|
@@ -19458,8 +19574,8 @@ function registerAgentToOpenClaw(agentName, workspaceDir, _clawDir) {
|
|
|
19458
19574
|
if (stderr.includes("already exists")) {
|
|
19459
19575
|
logger.info(`Agent "${agentId}" already exists in CLI, updating config...`);
|
|
19460
19576
|
try {
|
|
19461
|
-
const
|
|
19462
|
-
addAgentToOpenClawConfig(
|
|
19577
|
+
const resolvedClawDir = clawDir || import_node_path11.default.dirname(workspaceDir);
|
|
19578
|
+
addAgentToOpenClawConfig(resolvedClawDir, agentId, agentName, false);
|
|
19463
19579
|
} catch {
|
|
19464
19580
|
logger.warn(`Failed to update config for existing agent "${agentId}", skipping.`);
|
|
19465
19581
|
}
|
|
@@ -19471,8 +19587,8 @@ function registerAgentToOpenClaw(agentName, workspaceDir, _clawDir) {
|
|
|
19471
19587
|
const errMsg = cliError instanceof Error ? cliError.message : String(cliError);
|
|
19472
19588
|
logger.warn(`CLI agents add failed, falling back to config file modification`, { agentId, stderr, error: errMsg });
|
|
19473
19589
|
try {
|
|
19474
|
-
const
|
|
19475
|
-
const configUpdated = addAgentToOpenClawConfig(
|
|
19590
|
+
const resolvedClawDir = clawDir || import_node_path11.default.dirname(workspaceDir);
|
|
19591
|
+
const configUpdated = addAgentToOpenClawConfig(resolvedClawDir, agentId, agentName, false);
|
|
19476
19592
|
if (configUpdated) {
|
|
19477
19593
|
logger.info(`Agent "${agentId}" registered via config file fallback.`);
|
|
19478
19594
|
return {
|
|
@@ -19496,7 +19612,11 @@ function registerAgentToOpenClaw(agentName, workspaceDir, _clawDir) {
|
|
|
19496
19612
|
}
|
|
19497
19613
|
}
|
|
19498
19614
|
}
|
|
19499
|
-
function detectClawCommand() {
|
|
19615
|
+
function detectClawCommand(clawDir) {
|
|
19616
|
+
if (clawDir) {
|
|
19617
|
+
const brand = detectClawBrand(clawDir);
|
|
19618
|
+
return brand === "LightClaw" ? "lightclaw" : "openclaw";
|
|
19619
|
+
}
|
|
19500
19620
|
try {
|
|
19501
19621
|
(0, import_node_child_process.execSync)("which lightclaw 2>/dev/null || where lightclaw 2>nul", { stdio: "pipe" });
|
|
19502
19622
|
return "lightclaw";
|
|
@@ -19509,8 +19629,8 @@ function detectClawCommand() {
|
|
|
19509
19629
|
}
|
|
19510
19630
|
return "openclaw";
|
|
19511
19631
|
}
|
|
19512
|
-
function restartOpenClawGateway() {
|
|
19513
|
-
const clawCmd = detectClawCommand();
|
|
19632
|
+
function restartOpenClawGateway(clawDir) {
|
|
19633
|
+
const clawCmd = detectClawCommand(clawDir);
|
|
19514
19634
|
logger.debug(`Restarting ${clawCmd} Gateway`);
|
|
19515
19635
|
try {
|
|
19516
19636
|
(0, import_node_child_process.execSync)(`${clawCmd} gateway restart`, {
|
|
@@ -19533,7 +19653,7 @@ function restartOpenClawGateway() {
|
|
|
19533
19653
|
}
|
|
19534
19654
|
|
|
19535
19655
|
// src/commands/search.ts
|
|
19536
|
-
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) => {
|
|
19537
19657
|
try {
|
|
19538
19658
|
const index = await fetchIndex();
|
|
19539
19659
|
let agents = index.agents;
|
|
@@ -19551,14 +19671,30 @@ var searchCommand = new Command("search").description("Search for agents in the
|
|
|
19551
19671
|
const limit = parseInt(options.limit, 10);
|
|
19552
19672
|
const shown = agents.slice(0, limit);
|
|
19553
19673
|
if (shown.length === 0) {
|
|
19554
|
-
|
|
19555
|
-
|
|
19556
|
-
|
|
19557
|
-
|
|
19558
|
-
)
|
|
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
|
+
}
|
|
19559
19683
|
}
|
|
19560
19684
|
return;
|
|
19561
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
|
+
}
|
|
19562
19698
|
console.log(
|
|
19563
19699
|
source_default.bold(`
|
|
19564
19700
|
Found ${agents.length} agent(s):
|
|
@@ -19599,7 +19735,7 @@ var searchCommand = new Command("search").description("Search for agents in the
|
|
|
19599
19735
|
});
|
|
19600
19736
|
|
|
19601
19737
|
// src/commands/info.ts
|
|
19602
|
-
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) => {
|
|
19603
19739
|
try {
|
|
19604
19740
|
const index = await fetchIndex();
|
|
19605
19741
|
const agent = index.agents.find((a) => a.name === name);
|
|
@@ -19611,6 +19747,22 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19611
19747
|
process.exit(1);
|
|
19612
19748
|
}
|
|
19613
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
|
+
}
|
|
19614
19766
|
console.log();
|
|
19615
19767
|
console.log(source_default.bold.cyan(` ${agent.displayName}`));
|
|
19616
19768
|
console.log(source_default.dim(` ${agent.name} v${agent.version}`));
|
|
@@ -19639,7 +19791,7 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19639
19791
|
console.log(` ${fileName} ${source_default.dim(`(${sizeStr})`)}`);
|
|
19640
19792
|
}
|
|
19641
19793
|
if (options.identity || options.soul) {
|
|
19642
|
-
const
|
|
19794
|
+
const fs8 = await import("fs");
|
|
19643
19795
|
const pkgDir = await downloadAgentPackage(name, agent.version);
|
|
19644
19796
|
try {
|
|
19645
19797
|
if (options.identity) {
|
|
@@ -19647,8 +19799,8 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19647
19799
|
console.log(source_default.bold(" \u2500\u2500 IDENTITY.md \u2500\u2500"));
|
|
19648
19800
|
console.log();
|
|
19649
19801
|
const identityPath = (await import("path")).default.join(pkgDir, "IDENTITY.md");
|
|
19650
|
-
if (
|
|
19651
|
-
const content =
|
|
19802
|
+
if (fs8.default.existsSync(identityPath)) {
|
|
19803
|
+
const content = fs8.default.readFileSync(identityPath, "utf-8");
|
|
19652
19804
|
console.log(
|
|
19653
19805
|
content.split("\n").map((l) => ` ${l}`).join("\n")
|
|
19654
19806
|
);
|
|
@@ -19661,8 +19813,8 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19661
19813
|
console.log(source_default.bold(" \u2500\u2500 SOUL.md \u2500\u2500"));
|
|
19662
19814
|
console.log();
|
|
19663
19815
|
const soulPath = (await import("path")).default.join(pkgDir, "SOUL.md");
|
|
19664
|
-
if (
|
|
19665
|
-
const content =
|
|
19816
|
+
if (fs8.default.existsSync(soulPath)) {
|
|
19817
|
+
const content = fs8.default.readFileSync(soulPath, "utf-8");
|
|
19666
19818
|
console.log(
|
|
19667
19819
|
content.split("\n").map((l) => ` ${l}`).join("\n")
|
|
19668
19820
|
);
|
|
@@ -19671,7 +19823,7 @@ var infoCommand = new Command("info").description("Show details of an agent (ide
|
|
|
19671
19823
|
}
|
|
19672
19824
|
}
|
|
19673
19825
|
} finally {
|
|
19674
|
-
|
|
19826
|
+
fs8.default.rmSync(pkgDir, { recursive: true, force: true });
|
|
19675
19827
|
}
|
|
19676
19828
|
}
|
|
19677
19829
|
console.log();
|
|
@@ -19773,27 +19925,39 @@ function resolveAllClawDirs(clawDir) {
|
|
|
19773
19925
|
}
|
|
19774
19926
|
return findAllClawDirs();
|
|
19775
19927
|
}
|
|
19776
|
-
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(
|
|
19777
19929
|
"--dir <path>",
|
|
19778
19930
|
"Target directory (defaults to OpenClaw/LightClaw workspace)"
|
|
19779
19931
|
).option(
|
|
19780
|
-
"--
|
|
19932
|
+
"--claw-type <type>",
|
|
19781
19933
|
"Specify claw type: OpenClaw or LightClaw (case-insensitive)"
|
|
19782
|
-
).action(async (name, options) => {
|
|
19934
|
+
).option("-y, --yes", "Skip all confirmation prompts (auto-confirm)").action(async (name, options) => {
|
|
19783
19935
|
try {
|
|
19784
|
-
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
|
+
}
|
|
19785
19943
|
if (options.from) {
|
|
19786
|
-
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);
|
|
19787
19947
|
} else if (name) {
|
|
19788
|
-
|
|
19948
|
+
const resolvedRole = roleExplicit ? options.role.toLowerCase() === "main" : void 0;
|
|
19949
|
+
await installFromRegistry(name, options.dir, options.clawType, resolvedRole, clawExplicit, skipConfirm);
|
|
19789
19950
|
} else {
|
|
19790
19951
|
console.error(source_default.red("Please specify an agent or team name, or use --from to install from a local source."));
|
|
19791
|
-
console.log(
|
|
19792
|
-
console.log(source_default.dim("
|
|
19793
|
-
console.log(source_default.dim(" soulhub install
|
|
19794
|
-
console.log(source_default.dim(" soulhub install
|
|
19795
|
-
console.log(source_default.dim(" soulhub install --
|
|
19796
|
-
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"));
|
|
19797
19961
|
process.exit(1);
|
|
19798
19962
|
}
|
|
19799
19963
|
} catch (error) {
|
|
@@ -19805,15 +19969,60 @@ var installCommand = new Command("install").description("Install an agent or tea
|
|
|
19805
19969
|
process.exit(1);
|
|
19806
19970
|
}
|
|
19807
19971
|
});
|
|
19808
|
-
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) {
|
|
19809
19990
|
const spinner = createSpinner(`Checking registry for ${source_default.cyan(name)}...`).start();
|
|
19810
19991
|
const index = await fetchIndex();
|
|
19811
19992
|
const agent = index.agents.find((a) => a.name === name);
|
|
19812
19993
|
const recipe = index.recipes.find((r) => r.name === name);
|
|
19813
19994
|
if (agent && !recipe) {
|
|
19814
19995
|
spinner.stop();
|
|
19815
|
-
|
|
19816
|
-
|
|
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);
|
|
19817
20026
|
} else if (recipe) {
|
|
19818
20027
|
spinner.stop();
|
|
19819
20028
|
logger.info(`Installing team recipe from registry: ${name}`);
|
|
@@ -19824,12 +20033,25 @@ async function installFromRegistry(name, targetDir, clawDir, asMain) {
|
|
|
19824
20033
|
console.log(source_default.dim(" Use 'soulhub search' to find available agents and teams."));
|
|
19825
20034
|
}
|
|
19826
20035
|
}
|
|
19827
|
-
|
|
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) {
|
|
19828
20050
|
if (targetDir) {
|
|
19829
20051
|
await installSingleAgentToClaw(name, null, targetDir, asMain);
|
|
19830
20052
|
return;
|
|
19831
20053
|
}
|
|
19832
|
-
const allClawDirs = resolveAllClawDirs(clawDir);
|
|
20054
|
+
const allClawDirs = preResolvedClawDirs || resolveAllClawDirs(clawDir);
|
|
19833
20055
|
if (allClawDirs.length === 0) {
|
|
19834
20056
|
console.log(source_default.red("OpenClaw/LightClaw workspace directory not found."));
|
|
19835
20057
|
printOpenClawInstallHelp();
|
|
@@ -19963,7 +20185,7 @@ async function installSingleAgentToClaw(name, selectedClawDir, targetDir, asMain
|
|
|
19963
20185
|
console.log(` ${source_default.dim("Version:")} ${agent.version}`);
|
|
19964
20186
|
console.log(` ${source_default.dim("Type:")} ${typeLabel}`);
|
|
19965
20187
|
if (!targetDir) {
|
|
19966
|
-
await tryRestartGateway();
|
|
20188
|
+
await tryRestartGateway(selectedClawDir || void 0);
|
|
19967
20189
|
}
|
|
19968
20190
|
console.log();
|
|
19969
20191
|
}
|
|
@@ -20024,7 +20246,7 @@ async function installRecipeFromRegistry(name, recipe, targetDir, clawDir) {
|
|
|
20024
20246
|
const agentId = worker.name;
|
|
20025
20247
|
const workerDir = targetDir ? import_node_path12.default.join(resolvedClawDir, `workspace-${agentId}`) : getWorkspaceDir(resolvedClawDir, agentId);
|
|
20026
20248
|
if (!targetDir) {
|
|
20027
|
-
const regResult = registerAgentToOpenClaw(agentId, workerDir,
|
|
20249
|
+
const regResult = registerAgentToOpenClaw(agentId, workerDir, resolvedClawDir);
|
|
20028
20250
|
if (!regResult.success) {
|
|
20029
20251
|
console.log(source_default.yellow(` \u26A0 Failed to register ${agentId}: ${regResult.message}`));
|
|
20030
20252
|
continue;
|
|
@@ -20056,10 +20278,11 @@ async function installRecipeFromRegistry(name, recipe, targetDir, clawDir) {
|
|
|
20056
20278
|
);
|
|
20057
20279
|
printTeamSummary(pkg, workerIds);
|
|
20058
20280
|
if (!targetDir) {
|
|
20059
|
-
await tryRestartGateway();
|
|
20281
|
+
await tryRestartGateway(resolvedClawDir);
|
|
20060
20282
|
}
|
|
20061
20283
|
}
|
|
20062
|
-
async function installFromSource(source, targetDir, clawDir, asMain) {
|
|
20284
|
+
async function installFromSource(source, targetDir, clawDir, asMain, clawExplicit, skipConfirm = false) {
|
|
20285
|
+
if (asMain === null) return;
|
|
20063
20286
|
const spinner = createSpinner("Analyzing package...").start();
|
|
20064
20287
|
let packageDir;
|
|
20065
20288
|
let tempDir = null;
|
|
@@ -20113,7 +20336,7 @@ async function installFromSource(source, targetDir, clawDir, asMain) {
|
|
|
20113
20336
|
spinner.text = `Detected package type: ${source_default.blue(kind)}`;
|
|
20114
20337
|
if (kind === "agent") {
|
|
20115
20338
|
spinner.stop();
|
|
20116
|
-
await installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain);
|
|
20339
|
+
await installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain, clawExplicit, skipConfirm);
|
|
20117
20340
|
} else if (kind === "team") {
|
|
20118
20341
|
spinner.stop();
|
|
20119
20342
|
await installTeamFromDir(packageDir, targetDir, clawDir);
|
|
@@ -20125,14 +20348,30 @@ async function installFromSource(source, targetDir, clawDir, asMain) {
|
|
|
20125
20348
|
import_node_fs9.default.rmSync(tempDir, { recursive: true, force: true });
|
|
20126
20349
|
}
|
|
20127
20350
|
}
|
|
20128
|
-
async function installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain = false) {
|
|
20351
|
+
async function installSingleAgentFromDir(packageDir, targetDir, clawDir, asMain = false, clawExplicit, skipConfirm = false) {
|
|
20129
20352
|
const pkg = readSoulHubPackage(packageDir);
|
|
20130
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
|
+
}
|
|
20131
20370
|
if (targetDir) {
|
|
20132
20371
|
await installSingleAgentFromDirToClaw(packageDir, agentName, pkg, null, targetDir, asMain);
|
|
20133
20372
|
return;
|
|
20134
20373
|
}
|
|
20135
|
-
const allClawDirs =
|
|
20374
|
+
const allClawDirs = await resolveClawDirsInteractive(clawDir, clawExplicit);
|
|
20136
20375
|
if (allClawDirs.length === 0) {
|
|
20137
20376
|
console.log(source_default.red("OpenClaw/LightClaw workspace directory not found."));
|
|
20138
20377
|
printOpenClawInstallHelp();
|
|
@@ -20229,7 +20468,7 @@ async function installSingleAgentFromDirToClaw(packageDir, agentName, pkg, selec
|
|
|
20229
20468
|
console.log(` ${source_default.dim("Source:")} ${packageDir}`);
|
|
20230
20469
|
console.log(` ${source_default.dim("Type:")} ${typeLabel}`);
|
|
20231
20470
|
if (!targetDir) {
|
|
20232
|
-
await tryRestartGateway();
|
|
20471
|
+
await tryRestartGateway(selectedClawDir || void 0);
|
|
20233
20472
|
}
|
|
20234
20473
|
console.log();
|
|
20235
20474
|
}
|
|
@@ -20315,7 +20554,7 @@ async function installTeamFromDir(packageDir, targetDir, clawDir) {
|
|
|
20315
20554
|
spinner.text = `Installing worker ${source_default.cyan(agentId)}...`;
|
|
20316
20555
|
const workerWorkspace = targetDir ? import_node_path12.default.join(resolvedClawDir, `workspace-${agentId}`) : getWorkspaceDir(resolvedClawDir, agentId);
|
|
20317
20556
|
if (!targetDir) {
|
|
20318
|
-
const regResult = registerAgentToOpenClaw(agentId, workerWorkspace,
|
|
20557
|
+
const regResult = registerAgentToOpenClaw(agentId, workerWorkspace, resolvedClawDir);
|
|
20319
20558
|
if (!regResult.success) {
|
|
20320
20559
|
console.log(source_default.yellow(` \u26A0 Failed to register ${agentId}: ${regResult.message}`));
|
|
20321
20560
|
continue;
|
|
@@ -20345,7 +20584,7 @@ async function installTeamFromDir(packageDir, targetDir, clawDir) {
|
|
|
20345
20584
|
);
|
|
20346
20585
|
printTeamSummary(pkg, workerIds);
|
|
20347
20586
|
if (!targetDir) {
|
|
20348
|
-
await tryRestartGateway();
|
|
20587
|
+
await tryRestartGateway(resolvedClawDir);
|
|
20349
20588
|
}
|
|
20350
20589
|
}
|
|
20351
20590
|
function copyAgentFilesFromDir(sourceDir, targetDir) {
|
|
@@ -20413,7 +20652,7 @@ async function extractZipToDir(zip, targetDir) {
|
|
|
20413
20652
|
}
|
|
20414
20653
|
function printOpenClawInstallHelp() {
|
|
20415
20654
|
console.log(source_default.dim(" Please install OpenClaw or LightClaw first, or use one of the following options:"));
|
|
20416
|
-
console.log(source_default.dim(" --
|
|
20655
|
+
console.log(source_default.dim(" --claw-type <type> Specify claw type: OpenClaw or LightClaw"));
|
|
20417
20656
|
console.log(source_default.dim(" --dir <path> Specify agent target directory directly"));
|
|
20418
20657
|
console.log(source_default.dim(" OPENCLAW_HOME=<path> Set environment variable (for OpenClaw)"));
|
|
20419
20658
|
console.log(source_default.dim(" LIGHTCLAW_HOME=<path> Set environment variable (for LightClaw)"));
|
|
@@ -20433,11 +20672,11 @@ function printTeamSummary(pkg, workerIds) {
|
|
|
20433
20672
|
}
|
|
20434
20673
|
console.log();
|
|
20435
20674
|
}
|
|
20436
|
-
async function tryRestartGateway() {
|
|
20437
|
-
const clawCmd = detectClawCommand();
|
|
20675
|
+
async function tryRestartGateway(clawDir) {
|
|
20676
|
+
const clawCmd = detectClawCommand(clawDir);
|
|
20438
20677
|
const brandName = clawCmd === "lightclaw" ? "LightClaw" : "OpenClaw";
|
|
20439
20678
|
const restartSpinner = createSpinner(`Restarting ${brandName} Gateway...`).start();
|
|
20440
|
-
const result = restartOpenClawGateway();
|
|
20679
|
+
const result = restartOpenClawGateway(clawDir);
|
|
20441
20680
|
if (result.success) {
|
|
20442
20681
|
restartSpinner.succeed(`${brandName} Gateway restarted successfully.`);
|
|
20443
20682
|
} else {
|
|
@@ -20446,17 +20685,31 @@ async function tryRestartGateway() {
|
|
|
20446
20685
|
}
|
|
20447
20686
|
|
|
20448
20687
|
// src/commands/list.ts
|
|
20449
|
-
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) => {
|
|
20450
20689
|
try {
|
|
20451
20690
|
const config = loadConfig();
|
|
20452
20691
|
if (config.installed.length === 0) {
|
|
20453
|
-
|
|
20454
|
-
|
|
20455
|
-
|
|
20456
|
-
|
|
20457
|
-
|
|
20458
|
-
|
|
20459
|
-
|
|
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));
|
|
20460
20713
|
return;
|
|
20461
20714
|
}
|
|
20462
20715
|
console.log(
|
|
@@ -20514,6 +20767,19 @@ var updateCommand = new Command("update").description("Update installed agents t
|
|
|
20514
20767
|
}
|
|
20515
20768
|
spinner.text = `Updating ${source_default.cyan(installed.name)} (${installed.version} \u2192 ${remote.version})...`;
|
|
20516
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
|
+
}
|
|
20517
20783
|
if (!import_node_fs10.default.existsSync(workspaceDir)) {
|
|
20518
20784
|
import_node_fs10.default.mkdirSync(workspaceDir, { recursive: true });
|
|
20519
20785
|
}
|
|
@@ -20542,20 +20808,109 @@ var updateCommand = new Command("update").description("Update installed agents t
|
|
|
20542
20808
|
}
|
|
20543
20809
|
});
|
|
20544
20810
|
|
|
20545
|
-
// src/commands/
|
|
20811
|
+
// src/commands/uninstall.ts
|
|
20546
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);
|
|
20547
20898
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
20548
|
-
var
|
|
20549
|
-
|
|
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>",
|
|
20550
20902
|
"Specify claw type: OpenClaw or LightClaw (case-insensitive)"
|
|
20551
|
-
).action(async (options) => {
|
|
20903
|
+
).option("-y, --yes", "Skip all confirmation prompts (auto-confirm)").action(async (options) => {
|
|
20552
20904
|
try {
|
|
20905
|
+
const skipConfirm = !!options.yes;
|
|
20553
20906
|
if (options.list) {
|
|
20554
20907
|
listBackupRecords();
|
|
20555
20908
|
} else if (options.id) {
|
|
20556
|
-
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);
|
|
20557
20912
|
} else {
|
|
20558
|
-
await
|
|
20913
|
+
await interactiveRollback(options.clawType);
|
|
20559
20914
|
}
|
|
20560
20915
|
} catch (error) {
|
|
20561
20916
|
logger.errorObj("Rollback command failed", error);
|
|
@@ -20574,53 +20929,192 @@ function listBackupRecords() {
|
|
|
20574
20929
|
return;
|
|
20575
20930
|
}
|
|
20576
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) {
|
|
20577
20942
|
console.log(
|
|
20578
20943
|
source_default.dim(
|
|
20579
|
-
` ${"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`
|
|
20580
20945
|
)
|
|
20581
20946
|
);
|
|
20582
|
-
console.log(source_default.dim(" " + "\u2500".repeat(
|
|
20583
|
-
|
|
20947
|
+
console.log(source_default.dim(" " + "\u2500".repeat(108)));
|
|
20948
|
+
records.forEach((record, index) => {
|
|
20584
20949
|
const date = new Date(record.createdAt).toLocaleString();
|
|
20585
20950
|
const typeLabel = formatInstallType(record.installType);
|
|
20586
20951
|
const itemCount = record.items.length;
|
|
20952
|
+
const clawBrand = detectClawBrandFromDir(record.clawDir);
|
|
20587
20953
|
console.log(
|
|
20588
|
-
` ${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)`
|
|
20589
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;
|
|
20590
20964
|
}
|
|
20965
|
+
console.log(source_default.bold("\n Select a record to rollback:\n"));
|
|
20966
|
+
printRecordTable(manifest.records);
|
|
20591
20967
|
console.log();
|
|
20592
|
-
|
|
20593
|
-
|
|
20594
|
-
|
|
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];
|
|
20595
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);
|
|
20596
21002
|
}
|
|
20597
|
-
async function
|
|
21003
|
+
async function performRollbackByIndex(n, clawDir, skipConfirm = false) {
|
|
20598
21004
|
const manifest = loadBackupManifest();
|
|
20599
21005
|
if (manifest.records.length === 0) {
|
|
20600
21006
|
console.log(source_default.yellow("No backup records found. Nothing to rollback."));
|
|
20601
21007
|
return;
|
|
20602
21008
|
}
|
|
20603
|
-
|
|
20604
|
-
|
|
20605
|
-
|
|
20606
|
-
|
|
20607
|
-
|
|
20608
|
-
|
|
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."));
|
|
20609
21036
|
return;
|
|
20610
21037
|
}
|
|
20611
21038
|
} else {
|
|
20612
|
-
|
|
20613
|
-
|
|
20614
|
-
|
|
20615
|
-
|
|
20616
|
-
|
|
20617
|
-
|
|
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;
|
|
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}`));
|
|
20618
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) {
|
|
20619
21113
|
const spinner = createSpinner(
|
|
20620
21114
|
`Rolling back ${source_default.cyan(record.packageName)}...`
|
|
20621
21115
|
).start();
|
|
20622
|
-
const resolvedClawDir = clawDir ? findOpenClawDir(clawDir) || record.clawDir :
|
|
20623
|
-
if (!resolvedClawDir || !
|
|
21116
|
+
const resolvedClawDir = clawDir ? findOpenClawDir(clawDir) || record.clawDir : record.clawDir;
|
|
21117
|
+
if (!resolvedClawDir || !import_node_fs12.default.existsSync(resolvedClawDir)) {
|
|
20624
21118
|
spinner.fail(`OpenClaw/LightClaw directory not found: ${record.clawDir}`);
|
|
20625
21119
|
return;
|
|
20626
21120
|
}
|
|
@@ -20640,66 +21134,72 @@ async function performRollback(recordId, clawDir) {
|
|
|
20640
21134
|
spinner.text = "Removing installed workers...";
|
|
20641
21135
|
for (const workerId of record.installedWorkerIds) {
|
|
20642
21136
|
const workerDir = getWorkspaceDir(resolvedClawDir, workerId);
|
|
20643
|
-
if (
|
|
20644
|
-
|
|
21137
|
+
if (import_node_fs12.default.existsSync(workerDir)) {
|
|
21138
|
+
import_node_fs12.default.rmSync(workerDir, { recursive: true, force: true });
|
|
20645
21139
|
logger.info(`Removed installed worker directory`, { workerId, dir: workerDir });
|
|
20646
21140
|
}
|
|
20647
21141
|
const agentConfigDir = import_node_path13.default.join(resolvedClawDir, "agents", workerId);
|
|
20648
|
-
if (
|
|
20649
|
-
|
|
21142
|
+
if (import_node_fs12.default.existsSync(agentConfigDir)) {
|
|
21143
|
+
import_node_fs12.default.rmSync(agentConfigDir, { recursive: true, force: true });
|
|
20650
21144
|
}
|
|
20651
21145
|
}
|
|
20652
21146
|
}
|
|
20653
21147
|
if (record.installedMainAgent) {
|
|
20654
21148
|
const mainWorkspace = getMainWorkspaceDir(resolvedClawDir);
|
|
20655
21149
|
const hasMainBackup = record.items.some((item) => item.role === "main");
|
|
20656
|
-
if (hasMainBackup &&
|
|
21150
|
+
if (hasMainBackup && import_node_fs12.default.existsSync(mainWorkspace)) {
|
|
20657
21151
|
spinner.text = "Cleaning current main workspace...";
|
|
20658
|
-
const entries =
|
|
21152
|
+
const entries = import_node_fs12.default.readdirSync(mainWorkspace);
|
|
20659
21153
|
for (const entry of entries) {
|
|
20660
|
-
|
|
21154
|
+
import_node_fs12.default.rmSync(import_node_path13.default.join(mainWorkspace, entry), { recursive: true, force: true });
|
|
20661
21155
|
}
|
|
20662
21156
|
}
|
|
20663
21157
|
}
|
|
20664
21158
|
let restoredCount = 0;
|
|
20665
21159
|
for (const item of record.items) {
|
|
20666
|
-
if (!
|
|
21160
|
+
if (!import_node_fs12.default.existsSync(item.backupPath)) {
|
|
20667
21161
|
logger.warn(`Backup path not found, skipping`, { backupPath: item.backupPath });
|
|
20668
21162
|
console.log(source_default.yellow(` \u26A0 Backup not found: ${item.backupPath}, skipping...`));
|
|
20669
21163
|
continue;
|
|
20670
21164
|
}
|
|
20671
21165
|
spinner.text = `Restoring ${source_default.cyan(item.agentId)} (${item.role})...`;
|
|
20672
21166
|
if (item.method === "mv") {
|
|
20673
|
-
if (
|
|
20674
|
-
|
|
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 });
|
|
20675
21176
|
}
|
|
20676
|
-
import_node_fs11.default.mkdirSync(import_node_path13.default.dirname(item.originalPath), { recursive: true });
|
|
20677
|
-
import_node_fs11.default.renameSync(item.backupPath, item.originalPath);
|
|
20678
21177
|
logger.info(`Restored (mv back)`, { from: item.backupPath, to: item.originalPath });
|
|
20679
21178
|
} else {
|
|
20680
|
-
if (
|
|
20681
|
-
const entries =
|
|
21179
|
+
if (import_node_fs12.default.existsSync(item.originalPath)) {
|
|
21180
|
+
const entries = import_node_fs12.default.readdirSync(item.originalPath);
|
|
20682
21181
|
for (const entry of entries) {
|
|
20683
|
-
|
|
21182
|
+
import_node_fs12.default.rmSync(import_node_path13.default.join(item.originalPath, entry), { recursive: true, force: true });
|
|
20684
21183
|
}
|
|
20685
21184
|
} else {
|
|
20686
|
-
|
|
21185
|
+
import_node_fs12.default.mkdirSync(item.originalPath, { recursive: true });
|
|
20687
21186
|
}
|
|
20688
|
-
|
|
20689
|
-
|
|
21187
|
+
import_node_fs12.default.cpSync(item.backupPath, item.originalPath, { recursive: true });
|
|
21188
|
+
import_node_fs12.default.rmSync(item.backupPath, { recursive: true, force: true });
|
|
20690
21189
|
logger.info(`Restored (cp back)`, { from: item.backupPath, to: item.originalPath });
|
|
20691
21190
|
}
|
|
20692
21191
|
restoredCount++;
|
|
20693
21192
|
}
|
|
21193
|
+
const manifest = loadBackupManifest();
|
|
20694
21194
|
manifest.records = manifest.records.filter((r) => r.id !== record.id);
|
|
20695
21195
|
saveBackupManifest(manifest);
|
|
20696
21196
|
spinner.succeed(
|
|
20697
21197
|
`Rolled back ${source_default.cyan.bold(record.packageName)} successfully! (${restoredCount} item(s) restored)`
|
|
20698
21198
|
);
|
|
20699
|
-
const clawCmd = detectClawCommand();
|
|
21199
|
+
const clawCmd = detectClawCommand(resolvedClawDir);
|
|
20700
21200
|
const brandName = clawCmd === "lightclaw" ? "LightClaw" : "OpenClaw";
|
|
20701
21201
|
const restartSpinner = createSpinner(`Restarting ${brandName} Gateway...`).start();
|
|
20702
|
-
const result = restartOpenClawGateway();
|
|
21202
|
+
const result = restartOpenClawGateway(resolvedClawDir);
|
|
20703
21203
|
if (result.success) {
|
|
20704
21204
|
restartSpinner.succeed(`${brandName} Gateway restarted successfully.`);
|
|
20705
21205
|
} else {
|
|
@@ -20724,16 +21224,23 @@ function formatInstallType(type2) {
|
|
|
20724
21224
|
return type2;
|
|
20725
21225
|
}
|
|
20726
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
|
+
}
|
|
20727
21234
|
|
|
20728
21235
|
// src/index.ts
|
|
20729
21236
|
var program2 = new Command();
|
|
20730
|
-
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", () => {
|
|
20731
21238
|
const opts = program2.opts();
|
|
20732
21239
|
const verbose = opts.verbose || process.env.SOULHUB_DEBUG === "1";
|
|
20733
21240
|
logger.init(verbose);
|
|
20734
21241
|
logger.info("CLI started", {
|
|
20735
21242
|
args: process.argv.slice(2),
|
|
20736
|
-
version: "1.0.
|
|
21243
|
+
version: "1.0.22",
|
|
20737
21244
|
node: process.version
|
|
20738
21245
|
});
|
|
20739
21246
|
});
|
|
@@ -20742,6 +21249,7 @@ program2.addCommand(infoCommand);
|
|
|
20742
21249
|
program2.addCommand(installCommand);
|
|
20743
21250
|
program2.addCommand(listCommand);
|
|
20744
21251
|
program2.addCommand(updateCommand);
|
|
21252
|
+
program2.addCommand(uninstallCommand);
|
|
20745
21253
|
program2.addCommand(rollbackCommand);
|
|
20746
21254
|
program2.parse();
|
|
20747
21255
|
/*! Bundled license information:
|