openclaw-manager 0.1.4 → 0.1.5
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/bin/commands/reset.js +16 -63
- package/bin/lib/args.js +14 -2
- package/bin/lib/help.js +1 -1
- package/bin/lib/reset-shared.js +137 -0
- package/package.json +1 -1
package/bin/commands/reset.js
CHANGED
|
@@ -1,66 +1,19 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import { resolveConfigPaths } from "../lib/config.js";
|
|
5
1
|
import { stopAll } from "./stop-all.js";
|
|
6
|
-
import { listSandboxDirs
|
|
2
|
+
import { listSandboxDirs } from "../lib/sandbox.js";
|
|
3
|
+
import { resetEnvironmentShared } from "../lib/reset-shared.js";
|
|
7
4
|
export function resetManager(flags) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
else {
|
|
23
|
-
messages.push(`config: not found (${configPath.configDir})`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
catch (err) {
|
|
27
|
-
errors.push(`config: failed to remove (${configPath.configDir}): ${String(err)}`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
errors.push(`config: refuse remove unsafe path (${configPath.configDir})`);
|
|
32
|
-
}
|
|
33
|
-
const sandboxes = listSandboxDirs();
|
|
34
|
-
if (!sandboxes.length) {
|
|
35
|
-
messages.push("sandbox: none");
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
for (const dir of sandboxes) {
|
|
39
|
-
const result = removeSandboxDir(dir);
|
|
40
|
-
if (result.ok) {
|
|
41
|
-
messages.push(`sandbox: ${result.message}`);
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
errors.push(`sandbox: ${result.error ?? "remove failed"}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (errors.length) {
|
|
49
|
-
return { ok: false, messages, error: errors.join("; ") };
|
|
50
|
-
}
|
|
51
|
-
return { ok: true, messages };
|
|
52
|
-
}
|
|
53
|
-
function isSafeConfigDir(target) {
|
|
54
|
-
const resolved = path.resolve(target);
|
|
55
|
-
const home = os.homedir();
|
|
56
|
-
if (!resolved.startsWith(home))
|
|
57
|
-
return false;
|
|
58
|
-
const base = path.basename(resolved);
|
|
59
|
-
if (base.includes("openclaw-manager") || base.includes("clawdbot-manager")) {
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
return (resolved.endsWith(".openclaw-manager") ||
|
|
63
|
-
resolved.endsWith(".clawdbot-manager") ||
|
|
64
|
-
resolved.includes(`${path.sep}.openclaw-manager${path.sep}`) ||
|
|
65
|
-
resolved.includes(`${path.sep}.clawdbot-manager${path.sep}`));
|
|
5
|
+
return resetEnvironmentShared({
|
|
6
|
+
flags: {
|
|
7
|
+
dryRun: flags.dryRun,
|
|
8
|
+
keepClawdbot: flags.keepClawdbot,
|
|
9
|
+
noStop: flags.noStop,
|
|
10
|
+
force: flags.force,
|
|
11
|
+
configDir: flags.configDir,
|
|
12
|
+
configPath: flags.configPath,
|
|
13
|
+
installDir: flags.installDir,
|
|
14
|
+
clawdbotDir: flags.clawdbotDir
|
|
15
|
+
},
|
|
16
|
+
stopAll: () => stopAll(flags),
|
|
17
|
+
sandboxDirs: listSandboxDirs()
|
|
18
|
+
});
|
|
66
19
|
}
|
package/bin/lib/args.js
CHANGED
|
@@ -11,7 +11,13 @@ const longKeyMap = {
|
|
|
11
11
|
"config-path": "configPath",
|
|
12
12
|
"log-path": "logPath",
|
|
13
13
|
"error-log-path": "errorLogPath",
|
|
14
|
-
"non-interactive": "nonInteractive"
|
|
14
|
+
"non-interactive": "nonInteractive",
|
|
15
|
+
"dry-run": "dryRun",
|
|
16
|
+
"keep-clawdbot": "keepClawdbot",
|
|
17
|
+
"no-stop": "noStop",
|
|
18
|
+
force: "force",
|
|
19
|
+
"install-dir": "installDir",
|
|
20
|
+
"clawdbot-dir": "clawdbotDir"
|
|
15
21
|
};
|
|
16
22
|
const shortKeyMap = {
|
|
17
23
|
h: "help",
|
|
@@ -32,7 +38,13 @@ export function parseArgs(argv) {
|
|
|
32
38
|
if (arg.startsWith("--")) {
|
|
33
39
|
const [rawKey, inlineValue] = arg.slice(2).split("=");
|
|
34
40
|
const key = longKeyMap[rawKey] ?? rawKey;
|
|
35
|
-
if (key === "help" ||
|
|
41
|
+
if (key === "help" ||
|
|
42
|
+
key === "version" ||
|
|
43
|
+
key === "nonInteractive" ||
|
|
44
|
+
key === "dryRun" ||
|
|
45
|
+
key === "keepClawdbot" ||
|
|
46
|
+
key === "noStop" ||
|
|
47
|
+
key === "force") {
|
|
36
48
|
flags[key] = true;
|
|
37
49
|
}
|
|
38
50
|
else if (inlineValue !== undefined) {
|
package/bin/lib/help.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { printBanner } from "./banner.js";
|
|
2
2
|
export function printHelp() {
|
|
3
3
|
printBanner();
|
|
4
|
-
console.log(`\nUsage:\n openclaw-manager <command> [options]\n\nCommands:\n start Start OpenClaw Manager\n stop Stop the running Manager process\n stop-all Stop Manager, sandboxes, and gateway processes\n reset Stop all and remove local manager data\n\nOptions:\n -h, --help Show help\n -v, --version Show version\n -u, --user <name> Admin username (start)\n -p, --pass <value> Admin password (start)\n --non-interactive Fail instead of prompting for credentials\n --api-port <port> API port (default: 17321)\n --api-host <host> API host (default: 0.0.0.0)\n --config-dir <dir> Config directory\n --config-path <path> Config file path\n`);
|
|
4
|
+
console.log(`\nUsage:\n openclaw-manager <command> [options]\n\nCommands:\n start Start OpenClaw Manager\n stop Stop the running Manager process\n stop-all Stop Manager, sandboxes, and gateway processes\n reset Stop all and remove local manager data\n\nOptions:\n -h, --help Show help\n -v, --version Show version\n -u, --user <name> Admin username (start)\n -p, --pass <value> Admin password (start)\n --non-interactive Fail instead of prompting for credentials\n --api-port <port> API port (default: 17321)\n --api-host <host> API host (default: 0.0.0.0)\n --config-dir <dir> Config directory\n --config-path <path> Config file path\n --install-dir <dir> Install directory (reset)\n --clawdbot-dir <dir> Clawdbot data directory (reset)\n --dry-run Print removals without deleting (reset)\n --keep-clawdbot Keep ~/.clawdbot (reset)\n --no-stop Skip stopping services (reset)\n --force Allow removing non-default paths (reset)\n`);
|
|
5
5
|
}
|
|
6
6
|
export function printWelcome() {
|
|
7
7
|
printBanner();
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export function resetEnvironmentShared(params) {
|
|
5
|
+
const messages = [];
|
|
6
|
+
const errors = [];
|
|
7
|
+
const flags = params.flags ?? {};
|
|
8
|
+
if (!flags.noStop && params.stopAll) {
|
|
9
|
+
const stopResult = params.stopAll();
|
|
10
|
+
messages.push(...stopResult.messages);
|
|
11
|
+
if (!stopResult.ok && stopResult.error) {
|
|
12
|
+
messages.push(`warn: stop-all failed (${stopResult.error})`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
messages.push("manager: skipped");
|
|
17
|
+
}
|
|
18
|
+
const targets = buildResetTargets(flags, params.sandboxDirs);
|
|
19
|
+
const seen = new Set();
|
|
20
|
+
for (const target of targets) {
|
|
21
|
+
const resolved = path.resolve(target.path);
|
|
22
|
+
if (!resolved || seen.has(resolved))
|
|
23
|
+
continue;
|
|
24
|
+
seen.add(resolved);
|
|
25
|
+
if (!isSafeResetPath(resolved)) {
|
|
26
|
+
errors.push(`refuse remove unsafe path (${resolved})`);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!flags.force && !isExpectedResetPath(resolved)) {
|
|
30
|
+
errors.push(`refuse remove ${resolved} (use --force)`);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (flags.dryRun) {
|
|
34
|
+
messages.push(`[dry-run] ${target.label}: remove ${resolved}`);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (!fs.existsSync(resolved)) {
|
|
38
|
+
messages.push(`${target.label}: not found (${resolved})`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
fs.rmSync(resolved, { recursive: true, force: true });
|
|
43
|
+
messages.push(`${target.label}: removed (${resolved})`);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
errors.push(`${target.label}: failed to remove (${resolved}): ${String(err)}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (errors.length) {
|
|
50
|
+
return { ok: false, error: errors.join("; "), messages };
|
|
51
|
+
}
|
|
52
|
+
return { ok: true, messages };
|
|
53
|
+
}
|
|
54
|
+
function buildResetTargets(flags, sandboxDirs) {
|
|
55
|
+
const configDirs = resolveConfigDirs(flags);
|
|
56
|
+
const installDirs = resolveInstallDirs(flags);
|
|
57
|
+
const clawdbotDir = flags.keepClawdbot ? "" : resolveClawdbotDir(flags);
|
|
58
|
+
const sandboxes = sandboxDirs && sandboxDirs.length ? sandboxDirs : listSandboxDirs();
|
|
59
|
+
const targets = [
|
|
60
|
+
...configDirs.map((dir) => ({ label: "config", path: dir })),
|
|
61
|
+
...installDirs.map((dir) => ({ label: "install", path: dir })),
|
|
62
|
+
...sandboxes.map((dir) => ({ label: "sandbox", path: dir }))
|
|
63
|
+
];
|
|
64
|
+
if (clawdbotDir) {
|
|
65
|
+
targets.push({ label: "clawdbot", path: clawdbotDir });
|
|
66
|
+
}
|
|
67
|
+
return targets.filter((entry) => Boolean(entry.path));
|
|
68
|
+
}
|
|
69
|
+
function resolveConfigDirs(flags) {
|
|
70
|
+
const explicitDir = normalizePath(flags.configDir) ?? normalizePath(process.env.MANAGER_CONFIG_DIR);
|
|
71
|
+
const explicitPath = normalizePath(flags.configPath) ?? normalizePath(process.env.MANAGER_CONFIG_PATH);
|
|
72
|
+
if (explicitDir)
|
|
73
|
+
return [explicitDir];
|
|
74
|
+
if (explicitPath)
|
|
75
|
+
return [path.dirname(explicitPath)];
|
|
76
|
+
const home = os.homedir();
|
|
77
|
+
if (isRootUser()) {
|
|
78
|
+
return ["/etc/openclaw-manager", "/etc/clawdbot-manager"];
|
|
79
|
+
}
|
|
80
|
+
return [path.join(home, ".openclaw-manager"), path.join(home, ".clawdbot-manager")];
|
|
81
|
+
}
|
|
82
|
+
function resolveInstallDirs(flags) {
|
|
83
|
+
const explicit = normalizePath(flags.installDir) ?? normalizePath(process.env.MANAGER_INSTALL_DIR);
|
|
84
|
+
if (explicit)
|
|
85
|
+
return [explicit];
|
|
86
|
+
const home = os.homedir();
|
|
87
|
+
if (isRootUser()) {
|
|
88
|
+
return ["/opt/openclaw-manager", "/opt/clawdbot-manager"];
|
|
89
|
+
}
|
|
90
|
+
return [path.join(home, "openclaw-manager"), path.join(home, "clawdbot-manager")];
|
|
91
|
+
}
|
|
92
|
+
function resolveClawdbotDir(flags) {
|
|
93
|
+
const explicit = normalizePath(flags.clawdbotDir) ?? normalizePath(process.env.CLAWDBOT_DIR);
|
|
94
|
+
if (explicit)
|
|
95
|
+
return explicit;
|
|
96
|
+
return path.join(os.homedir(), ".clawdbot");
|
|
97
|
+
}
|
|
98
|
+
function listSandboxDirs() {
|
|
99
|
+
const dir = os.tmpdir();
|
|
100
|
+
let entries = [];
|
|
101
|
+
try {
|
|
102
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return entries
|
|
108
|
+
.filter((entry) => {
|
|
109
|
+
return (entry.isDirectory() &&
|
|
110
|
+
(entry.name.startsWith("openclaw-manager-sandbox-") ||
|
|
111
|
+
entry.name.startsWith("clawdbot-manager-sandbox-")));
|
|
112
|
+
})
|
|
113
|
+
.map((entry) => path.join(dir, entry.name));
|
|
114
|
+
}
|
|
115
|
+
function isSafeResetPath(resolved) {
|
|
116
|
+
if (!resolved)
|
|
117
|
+
return false;
|
|
118
|
+
const blocked = new Set([path.resolve("/"), path.resolve(os.homedir()), path.resolve(os.tmpdir())]);
|
|
119
|
+
return !blocked.has(path.resolve(resolved));
|
|
120
|
+
}
|
|
121
|
+
function isExpectedResetPath(resolved) {
|
|
122
|
+
const normalized = resolved.replace(/\\/g, "/");
|
|
123
|
+
return (normalized.includes("/openclaw-manager") ||
|
|
124
|
+
normalized.includes("/.openclaw-manager") ||
|
|
125
|
+
normalized.includes("/clawdbot-manager") ||
|
|
126
|
+
normalized.includes("/.clawdbot-manager") ||
|
|
127
|
+
normalized.includes("/.clawdbot"));
|
|
128
|
+
}
|
|
129
|
+
function normalizePath(value) {
|
|
130
|
+
if (typeof value !== "string")
|
|
131
|
+
return null;
|
|
132
|
+
const trimmed = value.trim();
|
|
133
|
+
return trimmed ? trimmed : null;
|
|
134
|
+
}
|
|
135
|
+
function isRootUser() {
|
|
136
|
+
return typeof process.getuid === "function" && process.getuid() === 0;
|
|
137
|
+
}
|