openclaw-manager 0.1.3 → 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 +19 -0
- package/bin/commands/stop-all.js +2 -19
- package/bin/lib/args.js +18 -5
- package/bin/lib/banner.js +4 -9
- package/bin/lib/color.js +16 -0
- package/bin/lib/help.js +2 -2
- package/bin/lib/reset-shared.js +137 -0
- package/bin/lib/sandbox.js +29 -0
- package/bin/openclaw-manager.js +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { stopAll } from "./stop-all.js";
|
|
2
|
+
import { listSandboxDirs } from "../lib/sandbox.js";
|
|
3
|
+
import { resetEnvironmentShared } from "../lib/reset-shared.js";
|
|
4
|
+
export function resetManager(flags) {
|
|
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
|
+
});
|
|
19
|
+
}
|
package/bin/commands/stop-all.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { listGatewayProcesses } from "../lib/system.js";
|
|
5
4
|
import { stopManager } from "./stop.js";
|
|
6
5
|
import { readPid } from "../lib/pids.js";
|
|
6
|
+
import { listSandboxDirs } from "../lib/sandbox.js";
|
|
7
7
|
export function stopAll(flags) {
|
|
8
8
|
const messages = [];
|
|
9
9
|
const errors = [];
|
|
@@ -11,7 +11,7 @@ export function stopAll(flags) {
|
|
|
11
11
|
messages.push(...managerResult.messages);
|
|
12
12
|
if (!managerResult.ok && managerResult.error)
|
|
13
13
|
errors.push(managerResult.error);
|
|
14
|
-
const sandboxes =
|
|
14
|
+
const sandboxes = listSandboxDirs();
|
|
15
15
|
if (!sandboxes.length) {
|
|
16
16
|
messages.push("sandbox: none");
|
|
17
17
|
}
|
|
@@ -46,23 +46,6 @@ export function stopAll(flags) {
|
|
|
46
46
|
}
|
|
47
47
|
return { ok: true, messages };
|
|
48
48
|
}
|
|
49
|
-
function listSandboxInstances() {
|
|
50
|
-
const dir = os.tmpdir();
|
|
51
|
-
let entries = [];
|
|
52
|
-
try {
|
|
53
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
return entries
|
|
59
|
-
.filter((entry) => {
|
|
60
|
-
return (entry.isDirectory() &&
|
|
61
|
-
(entry.name.startsWith("openclaw-manager-sandbox-") ||
|
|
62
|
-
entry.name.startsWith("clawdbot-manager-sandbox-")));
|
|
63
|
-
})
|
|
64
|
-
.map((entry) => path.join(dir, entry.name));
|
|
65
|
-
}
|
|
66
49
|
function stopSandboxDir(rootDir) {
|
|
67
50
|
const pidFile = path.join(rootDir, "manager-api.pid");
|
|
68
51
|
if (!fs.existsSync(pidFile)) {
|
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",
|
|
@@ -19,6 +25,7 @@ const shortKeyMap = {
|
|
|
19
25
|
u: "user",
|
|
20
26
|
p: "pass"
|
|
21
27
|
};
|
|
28
|
+
const validKeys = new Set(Object.values(longKeyMap));
|
|
22
29
|
export function parseArgs(argv) {
|
|
23
30
|
const flags = {};
|
|
24
31
|
const positionals = [];
|
|
@@ -31,7 +38,13 @@ export function parseArgs(argv) {
|
|
|
31
38
|
if (arg.startsWith("--")) {
|
|
32
39
|
const [rawKey, inlineValue] = arg.slice(2).split("=");
|
|
33
40
|
const key = longKeyMap[rawKey] ?? rawKey;
|
|
34
|
-
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") {
|
|
35
48
|
flags[key] = true;
|
|
36
49
|
}
|
|
37
50
|
else if (inlineValue !== undefined) {
|
|
@@ -74,6 +87,8 @@ export function parseArgs(argv) {
|
|
|
74
87
|
return { command, flags };
|
|
75
88
|
}
|
|
76
89
|
function setFlag(flags, key, value) {
|
|
90
|
+
if (!validKeys.has(key))
|
|
91
|
+
return;
|
|
77
92
|
if (key === "apiPort") {
|
|
78
93
|
const num = Number(value);
|
|
79
94
|
if (Number.isFinite(num)) {
|
|
@@ -81,7 +96,5 @@ function setFlag(flags, key, value) {
|
|
|
81
96
|
return;
|
|
82
97
|
}
|
|
83
98
|
}
|
|
84
|
-
|
|
85
|
-
flags[key] = value;
|
|
86
|
-
}
|
|
99
|
+
flags[key] = value;
|
|
87
100
|
}
|
package/bin/lib/banner.js
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
+
import { cyan, dim } from "./color.js";
|
|
1
2
|
import { readPackageVersion } from "./version.js";
|
|
2
|
-
const BANNER_LINES = [
|
|
3
|
-
" ____ __ __ __ ___ ",
|
|
4
|
-
" / __ \\____ ___ ____/ /___ _/ /___ _____/ |/ /___ _____ ____ _____ _____ ",
|
|
5
|
-
" / / / / __ \\/ _ \\/ __ / __ `/ / __ \\___/ /|_/ / __ `/ __ \\/ __ `/ __ \\/ ___/ ",
|
|
6
|
-
"/ /_/ / /_/ / __/ /_/ / /_/ / / / / / /__/ / / / /_/ / / / / /_/ / /_/ / / ",
|
|
7
|
-
"\\____/ .___/\\___/\\__,_/\\__,_/_/_/ /_/\\___/_/ /_/\\__,_/_/ /_/\\__,_/\\____/_/ ",
|
|
8
|
-
" /_/ "
|
|
9
|
-
];
|
|
10
3
|
export function printBanner() {
|
|
11
4
|
const version = readPackageVersion();
|
|
12
|
-
|
|
5
|
+
const title = cyan("OpenClaw Manager");
|
|
6
|
+
const ver = dim(`v${version}`);
|
|
7
|
+
console.log(`${title} ${ver}`);
|
|
13
8
|
}
|
package/bin/lib/color.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function colorize(text, code) {
|
|
2
|
+
if (!shouldColor())
|
|
3
|
+
return text;
|
|
4
|
+
return `\u001b[${code}m${text}\u001b[0m`;
|
|
5
|
+
}
|
|
6
|
+
export function cyan(text) {
|
|
7
|
+
return colorize(text, 36);
|
|
8
|
+
}
|
|
9
|
+
export function dim(text) {
|
|
10
|
+
return colorize(text, 90);
|
|
11
|
+
}
|
|
12
|
+
function shouldColor() {
|
|
13
|
+
if (process.env.NO_COLOR)
|
|
14
|
+
return false;
|
|
15
|
+
return Boolean(process.stdout.isTTY);
|
|
16
|
+
}
|
package/bin/lib/help.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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\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();
|
|
8
|
-
console.log(`\nQuick start:\n openclaw-manager start\n\nCommon commands:\n openclaw-manager stop\n openclaw-manager stop-all\n\nTip: First start will ask for admin username/password.\nDocs: https://openclaw-manager.com\n`);
|
|
8
|
+
console.log(`\nQuick start:\n openclaw-manager start\n\nCommon commands:\n openclaw-manager stop\n openclaw-manager stop-all\n openclaw-manager reset\n\nTip: First start will ask for admin username/password.\nDocs: https://openclaw-manager.com\n`);
|
|
9
9
|
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export function listSandboxDirs() {
|
|
5
|
+
const dir = os.tmpdir();
|
|
6
|
+
let entries = [];
|
|
7
|
+
try {
|
|
8
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
return entries
|
|
14
|
+
.filter((entry) => {
|
|
15
|
+
return (entry.isDirectory() &&
|
|
16
|
+
(entry.name.startsWith("openclaw-manager-sandbox-") ||
|
|
17
|
+
entry.name.startsWith("clawdbot-manager-sandbox-")));
|
|
18
|
+
})
|
|
19
|
+
.map((entry) => path.join(dir, entry.name));
|
|
20
|
+
}
|
|
21
|
+
export function removeSandboxDir(rootDir) {
|
|
22
|
+
try {
|
|
23
|
+
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
24
|
+
return { ok: true, message: `removed (${rootDir})` };
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return { ok: false, error: `failed to remove ${rootDir}: ${String(err)}` };
|
|
28
|
+
}
|
|
29
|
+
}
|
package/bin/openclaw-manager.js
CHANGED
|
@@ -6,6 +6,7 @@ import { readPackageVersion } from "./lib/version.js";
|
|
|
6
6
|
import { startManager } from "./commands/start.js";
|
|
7
7
|
import { stopManager } from "./commands/stop.js";
|
|
8
8
|
import { stopAll } from "./commands/stop-all.js";
|
|
9
|
+
import { resetManager } from "./commands/reset.js";
|
|
9
10
|
const args = process.argv.slice(2);
|
|
10
11
|
const parsed = parseArgs(args);
|
|
11
12
|
const cmd = parsed.command;
|
|
@@ -39,6 +40,13 @@ try {
|
|
|
39
40
|
if (!result.ok)
|
|
40
41
|
process.exit(1);
|
|
41
42
|
}
|
|
43
|
+
else if (cmd === "reset") {
|
|
44
|
+
const result = resetManager(parsed.flags);
|
|
45
|
+
for (const line of result.messages)
|
|
46
|
+
console.log(line);
|
|
47
|
+
if (!result.ok)
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
42
50
|
else {
|
|
43
51
|
console.error(`[manager] Unknown command: ${cmd}`);
|
|
44
52
|
printHelp();
|