openclaw-manager 0.1.1 → 0.1.2
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/openclaw-manager.js +371 -58
- package/dist/lib/commands.js +2 -2
- package/dist/services/jobs.service.js +2 -2
- package/package.json +3 -2
- package/web-dist/assets/{index-BabnD_ew.js → index-C_m7eOq1.js} +2 -2
- package/web-dist/docker.sh +7 -7
- package/web-dist/index.html +2 -2
- package/web-dist/install.ps1 +1 -1
- package/web-dist/install.sh +2 -2
package/bin/openclaw-manager.js
CHANGED
|
@@ -1,43 +1,61 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { randomBytes, scryptSync } from "node:crypto";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
3
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import process from "node:process";
|
|
8
|
-
import readline from "node:readline";
|
|
9
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
import prompts from "prompts";
|
|
10
10
|
|
|
11
11
|
const args = process.argv.slice(2);
|
|
12
|
-
const
|
|
12
|
+
const parsed = parseArgs(args);
|
|
13
|
+
const cmd = parsed.command;
|
|
13
14
|
|
|
14
|
-
if (
|
|
15
|
+
if (parsed.flags.help || cmd === "help") {
|
|
15
16
|
printHelp();
|
|
16
17
|
process.exit(0);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
if (
|
|
20
|
+
if (parsed.flags.version) {
|
|
20
21
|
console.log("openclaw-manager 0.1.0");
|
|
21
22
|
process.exit(0);
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
if (cmd
|
|
25
|
+
if (!cmd) {
|
|
26
|
+
printWelcome();
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (cmd === "start") {
|
|
31
|
+
void start(parsed.flags);
|
|
32
|
+
} else if (cmd === "stop") {
|
|
33
|
+
void stop(parsed.flags);
|
|
34
|
+
} else if (cmd === "stop-all") {
|
|
35
|
+
void stopAll(parsed.flags);
|
|
36
|
+
} else {
|
|
25
37
|
console.error(`[manager] Unknown command: ${cmd}`);
|
|
26
38
|
printHelp();
|
|
27
39
|
process.exit(1);
|
|
28
40
|
}
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
async function start(flags) {
|
|
43
|
+
const apiPort = String(flags.apiPort ?? process.env.MANAGER_API_PORT ?? "17321");
|
|
44
|
+
const apiHost = flags.apiHost ?? process.env.MANAGER_API_HOST ?? "0.0.0.0";
|
|
45
|
+
const configDir =
|
|
46
|
+
flags.configDir ??
|
|
47
|
+
process.env.MANAGER_CONFIG_DIR ??
|
|
48
|
+
path.join(os.homedir(), ".openclaw-manager");
|
|
36
49
|
const configPath =
|
|
37
|
-
|
|
50
|
+
flags.configPath ??
|
|
51
|
+
process.env.MANAGER_CONFIG_PATH ??
|
|
52
|
+
path.join(configDir, "config.json");
|
|
38
53
|
const logPath =
|
|
39
|
-
|
|
54
|
+
flags.logPath ??
|
|
55
|
+
process.env.MANAGER_LOG_PATH ??
|
|
56
|
+
path.join(configDir, "openclaw-manager.log");
|
|
40
57
|
const errorLogPath =
|
|
58
|
+
flags.errorLogPath ??
|
|
41
59
|
process.env.MANAGER_ERROR_LOG_PATH ??
|
|
42
60
|
path.join(configDir, "openclaw-manager.error.log");
|
|
43
61
|
const pidPath = path.join(configDir, "manager.pid");
|
|
@@ -52,15 +70,53 @@ async function start() {
|
|
|
52
70
|
return;
|
|
53
71
|
}
|
|
54
72
|
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
const explicitUser = normalizeString(
|
|
74
|
+
flags.user ??
|
|
75
|
+
flags.username ??
|
|
57
76
|
process.env.MANAGER_ADMIN_USER ??
|
|
58
|
-
process.env.OPENCLAW_MANAGER_ADMIN_USER
|
|
59
|
-
|
|
60
|
-
|
|
77
|
+
process.env.OPENCLAW_MANAGER_ADMIN_USER
|
|
78
|
+
);
|
|
79
|
+
const explicitPass = normalizeString(
|
|
80
|
+
flags.pass ??
|
|
81
|
+
flags.password ??
|
|
61
82
|
process.env.MANAGER_ADMIN_PASS ??
|
|
62
|
-
process.env.OPENCLAW_MANAGER_ADMIN_PASS
|
|
63
|
-
|
|
83
|
+
process.env.OPENCLAW_MANAGER_ADMIN_PASS
|
|
84
|
+
);
|
|
85
|
+
const hasConfig = hasAdminConfig(configPath);
|
|
86
|
+
if (explicitUser || explicitPass) {
|
|
87
|
+
if (!explicitUser || !explicitPass) {
|
|
88
|
+
console.error("[manager] Both --user and --password are required when overriding admin config.");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
writeAdminConfig(configPath, explicitUser, explicitPass);
|
|
92
|
+
} else if (!hasConfig) {
|
|
93
|
+
if (flags.nonInteractive || !process.stdin.isTTY) {
|
|
94
|
+
console.error("[manager] Admin username/password is required. Use --user/--password.");
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
const response = await prompts(
|
|
98
|
+
[
|
|
99
|
+
{
|
|
100
|
+
type: "text",
|
|
101
|
+
name: "username",
|
|
102
|
+
message: "Admin username",
|
|
103
|
+
validate: (value) => (value ? true : "Username is required")
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: "password",
|
|
107
|
+
name: "password",
|
|
108
|
+
message: "Admin password",
|
|
109
|
+
validate: (value) => (value ? true : "Password is required")
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
{
|
|
113
|
+
onCancel: () => {
|
|
114
|
+
throw new Error("Prompt cancelled");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
const username = String(response.username ?? "").trim();
|
|
119
|
+
const password = String(response.password ?? "").trim();
|
|
64
120
|
if (!username || !password) {
|
|
65
121
|
console.error("[manager] Admin username/password is required.");
|
|
66
122
|
process.exit(1);
|
|
@@ -106,6 +162,22 @@ async function start() {
|
|
|
106
162
|
}
|
|
107
163
|
}
|
|
108
164
|
|
|
165
|
+
async function stop(flags) {
|
|
166
|
+
const results = stopManagerProcess({ flags });
|
|
167
|
+
for (const line of results.messages) {
|
|
168
|
+
console.log(line);
|
|
169
|
+
}
|
|
170
|
+
if (!results.ok) process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function stopAll(flags) {
|
|
174
|
+
const results = stopAllProcesses({ flags });
|
|
175
|
+
for (const line of results.messages) {
|
|
176
|
+
console.log(line);
|
|
177
|
+
}
|
|
178
|
+
if (!results.ok) process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
|
|
109
181
|
function ensureDir(dir) {
|
|
110
182
|
if (!dir) return;
|
|
111
183
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -140,42 +212,6 @@ function writeAdminConfig(configPath, username, password) {
|
|
|
140
212
|
console.log(`[manager] Admin config saved to ${configPath}`);
|
|
141
213
|
}
|
|
142
214
|
|
|
143
|
-
async function promptLine(prompt) {
|
|
144
|
-
if (!process.stdin.isTTY) return "";
|
|
145
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
146
|
-
const answer = await new Promise((resolve) => rl.question(prompt, resolve));
|
|
147
|
-
rl.close();
|
|
148
|
-
return String(answer).trim();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async function promptSecret(prompt) {
|
|
152
|
-
if (!process.stdin.isTTY) return "";
|
|
153
|
-
return new Promise((resolve) => {
|
|
154
|
-
const stdin = process.stdin;
|
|
155
|
-
const stdout = process.stdout;
|
|
156
|
-
let value = "";
|
|
157
|
-
stdout.write(prompt);
|
|
158
|
-
stdin.setRawMode(true);
|
|
159
|
-
stdin.resume();
|
|
160
|
-
const onData = (data) => {
|
|
161
|
-
const char = data.toString();
|
|
162
|
-
if (char === "\n" || char === "\r") {
|
|
163
|
-
stdout.write("\n");
|
|
164
|
-
stdin.setRawMode(false);
|
|
165
|
-
stdin.pause();
|
|
166
|
-
stdin.removeListener("data", onData);
|
|
167
|
-
resolve(value.trim());
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
if (char === "\u0003") {
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
value += char;
|
|
174
|
-
};
|
|
175
|
-
stdin.on("data", onData);
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
215
|
function resolveLanIp() {
|
|
180
216
|
const nets = os.networkInterfaces();
|
|
181
217
|
for (const name of Object.keys(nets)) {
|
|
@@ -193,6 +229,283 @@ function resolvePackageRoot() {
|
|
|
193
229
|
return path.resolve(path.dirname(filePath), "..");
|
|
194
230
|
}
|
|
195
231
|
|
|
232
|
+
function hasAdminConfig(configPath) {
|
|
233
|
+
if (!fs.existsSync(configPath)) return false;
|
|
234
|
+
try {
|
|
235
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
236
|
+
const parsed = JSON.parse(raw);
|
|
237
|
+
return Boolean(
|
|
238
|
+
parsed &&
|
|
239
|
+
parsed.auth &&
|
|
240
|
+
typeof parsed.auth.username === "string" &&
|
|
241
|
+
typeof parsed.auth.salt === "string" &&
|
|
242
|
+
typeof parsed.auth.hash === "string"
|
|
243
|
+
);
|
|
244
|
+
} catch {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function stopManagerProcess({ flags }) {
|
|
250
|
+
const messages = [];
|
|
251
|
+
const errors = [];
|
|
252
|
+
const candidates = resolveConfigDirCandidates(flags);
|
|
253
|
+
let stopped = false;
|
|
254
|
+
|
|
255
|
+
if (process.platform !== "win32" && commandExists("systemctl")) {
|
|
256
|
+
const serviceName = "clawdbot-manager";
|
|
257
|
+
const servicePath = `/etc/systemd/system/${serviceName}.service`;
|
|
258
|
+
if (fs.existsSync(servicePath)) {
|
|
259
|
+
const result = spawnSync("systemctl", ["stop", serviceName], { encoding: "utf-8" });
|
|
260
|
+
if (result.status === 0) {
|
|
261
|
+
messages.push("manager: stopped systemd service");
|
|
262
|
+
stopped = true;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
for (const configDir of candidates) {
|
|
268
|
+
const pidPath = path.join(configDir, "manager.pid");
|
|
269
|
+
if (!fs.existsSync(pidPath)) continue;
|
|
270
|
+
const pid = readPid(pidPath);
|
|
271
|
+
if (!pid) continue;
|
|
272
|
+
try {
|
|
273
|
+
process.kill(pid, "SIGTERM");
|
|
274
|
+
fs.rmSync(pidPath, { force: true });
|
|
275
|
+
messages.push(`manager: stopped pid ${pid}`);
|
|
276
|
+
stopped = true;
|
|
277
|
+
} catch (err) {
|
|
278
|
+
errors.push(`manager: failed to stop pid ${pid}: ${String(err)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!stopped) {
|
|
283
|
+
const port = Number(flags.apiPort ?? process.env.MANAGER_API_PORT ?? 17321);
|
|
284
|
+
const pids = findListeningPids(port);
|
|
285
|
+
if (pids.length) {
|
|
286
|
+
for (const pid of pids) {
|
|
287
|
+
try {
|
|
288
|
+
process.kill(pid, "SIGTERM");
|
|
289
|
+
} catch (err) {
|
|
290
|
+
errors.push(`manager: failed to stop pid ${pid}: ${String(err)}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
messages.push(`manager: stopped port ${port} (pids: ${pids.join(", ")})`);
|
|
294
|
+
stopped = true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!stopped && !errors.length) {
|
|
299
|
+
messages.push("manager: not running");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (errors.length) {
|
|
303
|
+
return { ok: false, messages, error: errors.join("; ") };
|
|
304
|
+
}
|
|
305
|
+
return { ok: true, messages };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function stopAllProcesses({ flags }) {
|
|
309
|
+
const messages = [];
|
|
310
|
+
const errors = [];
|
|
311
|
+
|
|
312
|
+
const managerResult = stopManagerProcess({ flags });
|
|
313
|
+
messages.push(...managerResult.messages);
|
|
314
|
+
if (!managerResult.ok) errors.push(managerResult.error ?? "manager stop failed");
|
|
315
|
+
|
|
316
|
+
const sandboxes = listSandboxInstances();
|
|
317
|
+
if (!sandboxes.length) {
|
|
318
|
+
messages.push("sandbox: none");
|
|
319
|
+
} else {
|
|
320
|
+
for (const sandbox of sandboxes) {
|
|
321
|
+
const result = stopSandboxDir(sandbox);
|
|
322
|
+
if (result.ok) {
|
|
323
|
+
messages.push(`sandbox: ${result.message}`);
|
|
324
|
+
} else {
|
|
325
|
+
errors.push(`sandbox: ${result.error ?? "stop failed"}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const gatewayResult = stopGatewayProcesses();
|
|
331
|
+
messages.push(gatewayResult.message);
|
|
332
|
+
if (!gatewayResult.ok) errors.push(gatewayResult.error ?? "gateway stop failed");
|
|
333
|
+
|
|
334
|
+
if (errors.length) {
|
|
335
|
+
return { ok: false, messages, error: errors.join("; ") };
|
|
336
|
+
}
|
|
337
|
+
return { ok: true, messages };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function resolveConfigDirCandidates(flags) {
|
|
341
|
+
const explicit = flags.configDir ?? process.env.MANAGER_CONFIG_DIR;
|
|
342
|
+
if (explicit) return [explicit];
|
|
343
|
+
return [
|
|
344
|
+
path.join(os.homedir(), ".openclaw-manager"),
|
|
345
|
+
path.join(os.homedir(), ".clawdbot-manager")
|
|
346
|
+
];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function listSandboxInstances() {
|
|
350
|
+
const dir = os.tmpdir();
|
|
351
|
+
let entries = [];
|
|
352
|
+
try {
|
|
353
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
354
|
+
} catch {
|
|
355
|
+
return [];
|
|
356
|
+
}
|
|
357
|
+
return entries
|
|
358
|
+
.filter((entry) => {
|
|
359
|
+
return (
|
|
360
|
+
entry.isDirectory() &&
|
|
361
|
+
(entry.name.startsWith("openclaw-manager-sandbox-") ||
|
|
362
|
+
entry.name.startsWith("clawdbot-manager-sandbox-"))
|
|
363
|
+
);
|
|
364
|
+
})
|
|
365
|
+
.map((entry) => path.join(dir, entry.name));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function stopSandboxDir(rootDir) {
|
|
369
|
+
const pidFile = path.join(rootDir, "manager-api.pid");
|
|
370
|
+
if (!fs.existsSync(pidFile)) {
|
|
371
|
+
return { ok: true, message: `already stopped (${rootDir})` };
|
|
372
|
+
}
|
|
373
|
+
const pid = readPid(pidFile);
|
|
374
|
+
if (!pid) {
|
|
375
|
+
return { ok: true, message: `pid invalid (${rootDir})` };
|
|
376
|
+
}
|
|
377
|
+
try {
|
|
378
|
+
process.kill(pid, "SIGTERM");
|
|
379
|
+
return { ok: true, message: `stopped pid ${pid}` };
|
|
380
|
+
} catch (err) {
|
|
381
|
+
return { ok: false, error: `failed to stop pid ${pid}: ${String(err)}` };
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function stopGatewayProcesses() {
|
|
386
|
+
if (process.platform === "win32" || !commandExists("pgrep")) {
|
|
387
|
+
return { ok: true, message: "gateway: skipped" };
|
|
388
|
+
}
|
|
389
|
+
const result = spawnSync("pgrep", ["-fl", "clawdbot-gateway"], { encoding: "utf-8" });
|
|
390
|
+
if (result.error || result.status !== 0) {
|
|
391
|
+
return { ok: true, message: "gateway: none" };
|
|
392
|
+
}
|
|
393
|
+
const lines = String(result.stdout)
|
|
394
|
+
.split(/\n/)
|
|
395
|
+
.map((line) => line.trim())
|
|
396
|
+
.filter(Boolean);
|
|
397
|
+
const pids = lines
|
|
398
|
+
.map((line) => Number(line.split(/\s+/)[0]))
|
|
399
|
+
.filter((pid) => Number.isFinite(pid) && pid > 0);
|
|
400
|
+
if (!pids.length) {
|
|
401
|
+
return { ok: true, message: "gateway: none" };
|
|
402
|
+
}
|
|
403
|
+
for (const pid of pids) {
|
|
404
|
+
try {
|
|
405
|
+
process.kill(pid, "SIGTERM");
|
|
406
|
+
} catch {
|
|
407
|
+
// ignore individual failures; report below
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return { ok: true, message: `gateway: stopped (${pids.join(", ")})` };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function findListeningPids(port) {
|
|
414
|
+
if (process.platform === "win32" || !commandExists("lsof")) return [];
|
|
415
|
+
const result = spawnSync("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], {
|
|
416
|
+
encoding: "utf-8"
|
|
417
|
+
});
|
|
418
|
+
if (result.error || result.status !== 0) return [];
|
|
419
|
+
return String(result.stdout)
|
|
420
|
+
.split(/\s+/)
|
|
421
|
+
.map((value) => Number(value.trim()))
|
|
422
|
+
.filter((pid) => Number.isFinite(pid) && pid > 0);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function readPid(pidPath) {
|
|
426
|
+
try {
|
|
427
|
+
const raw = fs.readFileSync(pidPath, "utf-8").trim();
|
|
428
|
+
const pid = Number(raw);
|
|
429
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
430
|
+
return pid;
|
|
431
|
+
} catch {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function normalizeString(value) {
|
|
437
|
+
if (typeof value !== "string") return "";
|
|
438
|
+
return value.trim();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function commandExists(cmd) {
|
|
442
|
+
const result = spawnSync("command", ["-v", cmd], { encoding: "utf-8", shell: true });
|
|
443
|
+
return result.status === 0;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function parseArgs(argv) {
|
|
447
|
+
const flags = {};
|
|
448
|
+
const positionals = [];
|
|
449
|
+
|
|
450
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
451
|
+
const arg = argv[i];
|
|
452
|
+
if (arg === "--") {
|
|
453
|
+
positionals.push(...argv.slice(i + 1));
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
if (arg.startsWith("--")) {
|
|
457
|
+
const [keyRaw, inlineValue] = arg.slice(2).split("=");
|
|
458
|
+
const key = normalizeFlagKey(keyRaw);
|
|
459
|
+
if (key === "help") flags.help = true;
|
|
460
|
+
else if (key === "version") flags.version = true;
|
|
461
|
+
else if (inlineValue !== undefined) flags[key] = inlineValue;
|
|
462
|
+
else if (i + 1 < argv.length && !argv[i + 1].startsWith("-")) {
|
|
463
|
+
flags[key] = argv[i + 1];
|
|
464
|
+
i += 1;
|
|
465
|
+
} else {
|
|
466
|
+
flags[key] = true;
|
|
467
|
+
}
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if (arg.startsWith("-") && arg.length > 1) {
|
|
471
|
+
const shorts = arg.slice(1).split("");
|
|
472
|
+
for (const short of shorts) {
|
|
473
|
+
if (short === "h") flags.help = true;
|
|
474
|
+
else if (short === "v") flags.version = true;
|
|
475
|
+
else if (short === "u") flags.user = argv[i + 1] && !argv[i + 1].startsWith("-") ? argv[++i] : true;
|
|
476
|
+
else if (short === "p") flags.pass = argv[i + 1] && !argv[i + 1].startsWith("-") ? argv[++i] : true;
|
|
477
|
+
else flags[short] = true;
|
|
478
|
+
}
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
positionals.push(arg);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return { command: positionals[0] ?? "", flags };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function normalizeFlagKey(key) {
|
|
488
|
+
if (!key) return "";
|
|
489
|
+
if (key === "config-dir") return "configDir";
|
|
490
|
+
if (key === "config-path") return "configPath";
|
|
491
|
+
if (key === "log-path") return "logPath";
|
|
492
|
+
if (key === "error-log-path") return "errorLogPath";
|
|
493
|
+
if (key === "api-port") return "apiPort";
|
|
494
|
+
if (key === "api-host") return "apiHost";
|
|
495
|
+
if (key === "non-interactive") return "nonInteractive";
|
|
496
|
+
if (key === "user" || key === "username") return "user";
|
|
497
|
+
if (key === "pass" || key === "password") return "pass";
|
|
498
|
+
return key;
|
|
499
|
+
}
|
|
500
|
+
|
|
196
501
|
function printHelp() {
|
|
197
|
-
console.log(
|
|
502
|
+
console.log(
|
|
503
|
+
`openclaw-manager\n\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`
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function printWelcome() {
|
|
508
|
+
console.log(
|
|
509
|
+
`OpenClaw Manager\n\n最快开始:\n openclaw-manager start\n\n常用命令:\n openclaw-manager stop\n openclaw-manager stop-all\n\n提示:首次启动会要求设置管理员账号密码。\n文档: https://openclaw-manager.com\n`
|
|
510
|
+
);
|
|
198
511
|
}
|
package/dist/lib/commands.js
CHANGED
|
@@ -3,8 +3,8 @@ export function buildCommandRegistry(root) {
|
|
|
3
3
|
return [
|
|
4
4
|
{
|
|
5
5
|
id: "install-cli",
|
|
6
|
-
title: "Install
|
|
7
|
-
description: "Install the latest
|
|
6
|
+
title: "Install OpenClaw CLI",
|
|
7
|
+
description: "Install the latest OpenClaw CLI (may require sudo)",
|
|
8
8
|
command: "npm",
|
|
9
9
|
args: ["i", "-g", "clawdbot@latest"],
|
|
10
10
|
cwd: root,
|
|
@@ -8,9 +8,9 @@ const DEFAULT_PAIRING_WAIT_TIMEOUT_MS = 180_000;
|
|
|
8
8
|
const DEFAULT_PAIRING_POLL_MS = 3000;
|
|
9
9
|
const DEFAULT_PAIRING_APPROVE_TIMEOUT_MS = 8000;
|
|
10
10
|
export function createCliInstallJob(deps) {
|
|
11
|
-
const job = deps.jobStore.createJob("Install
|
|
11
|
+
const job = deps.jobStore.createJob("Install OpenClaw CLI");
|
|
12
12
|
deps.jobStore.startJob(job.id);
|
|
13
|
-
deps.jobStore.appendLog(job.id, "开始安装
|
|
13
|
+
deps.jobStore.appendLog(job.id, "开始安装 OpenClaw CLI...");
|
|
14
14
|
const timeoutMs = parsePositiveInt(process.env.MANAGER_CLI_INSTALL_TIMEOUT_MS) ?? 600_000;
|
|
15
15
|
void (async () => {
|
|
16
16
|
const current = await getCliStatus(deps.runCommand);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-manager",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"openclaw-manager": "bin/openclaw-manager.js"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@hono/node-server": "1.13.1",
|
|
16
|
-
"hono": "4.11.4"
|
|
16
|
+
"hono": "4.11.4",
|
|
17
|
+
"prompts": "^2.4.2"
|
|
17
18
|
}
|
|
18
19
|
}
|