openclaw-manager 0.1.1 → 0.1.3

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.
@@ -1,198 +1,52 @@
1
1
  #!/usr/bin/env node
2
- import { randomBytes, scryptSync } from "node:crypto";
3
- import { spawn } from "node:child_process";
4
- import fs from "node:fs";
5
- import os from "node:os";
6
- import path from "node:path";
7
2
  import process from "node:process";
8
- import readline from "node:readline";
9
- import { fileURLToPath } from "node:url";
10
-
3
+ import { parseArgs } from "./lib/args.js";
4
+ import { printHelp, printWelcome } from "./lib/help.js";
5
+ import { readPackageVersion } from "./lib/version.js";
6
+ import { startManager } from "./commands/start.js";
7
+ import { stopManager } from "./commands/stop.js";
8
+ import { stopAll } from "./commands/stop-all.js";
11
9
  const args = process.argv.slice(2);
12
- const cmd = args.find((arg) => !arg.startsWith("-")) ?? "start";
13
-
14
- if (args.includes("-h") || args.includes("--help") || cmd === "help") {
15
- printHelp();
16
- process.exit(0);
17
- }
18
-
19
- if (args.includes("-v") || args.includes("--version")) {
20
- console.log("openclaw-manager 0.1.0");
21
- process.exit(0);
22
- }
23
-
24
- if (cmd !== "start") {
25
- console.error(`[manager] Unknown command: ${cmd}`);
26
- printHelp();
27
- process.exit(1);
28
- }
29
-
30
- void start();
31
-
32
- async function start() {
33
- const apiPort = process.env.MANAGER_API_PORT ?? "17321";
34
- const apiHost = process.env.MANAGER_API_HOST ?? "0.0.0.0";
35
- const configDir = process.env.MANAGER_CONFIG_DIR ?? path.join(os.homedir(), ".openclaw-manager");
36
- const configPath =
37
- process.env.MANAGER_CONFIG_PATH ?? path.join(configDir, "config.json");
38
- const logPath =
39
- process.env.MANAGER_LOG_PATH ?? path.join(configDir, "openclaw-manager.log");
40
- const errorLogPath =
41
- process.env.MANAGER_ERROR_LOG_PATH ??
42
- path.join(configDir, "openclaw-manager.error.log");
43
- const pidPath = path.join(configDir, "manager.pid");
44
-
45
- ensureDir(configDir);
46
- ensureDir(path.dirname(logPath));
47
- ensureDir(path.dirname(errorLogPath));
48
-
49
- if (isRunning(pidPath)) {
50
- const pid = fs.readFileSync(pidPath, "utf-8").trim();
51
- console.log(`[manager] Already running (pid: ${pid}).`);
52
- return;
53
- }
54
-
55
- if (!fs.existsSync(configPath)) {
56
- const username =
57
- process.env.MANAGER_ADMIN_USER ??
58
- process.env.OPENCLAW_MANAGER_ADMIN_USER ??
59
- (await promptLine("Admin username: "));
60
- const password =
61
- process.env.MANAGER_ADMIN_PASS ??
62
- process.env.OPENCLAW_MANAGER_ADMIN_PASS ??
63
- (await promptSecret("Admin password: "));
64
- if (!username || !password) {
65
- console.error("[manager] Admin username/password is required.");
66
- process.exit(1);
10
+ const parsed = parseArgs(args);
11
+ const cmd = parsed.command;
12
+ if (parsed.flags.help || cmd === "help") {
13
+ printHelp();
14
+ process.exit(0);
15
+ }
16
+ if (parsed.flags.version) {
17
+ console.log(`openclaw-manager ${readPackageVersion()}`);
18
+ process.exit(0);
19
+ }
20
+ if (!cmd) {
21
+ printWelcome();
22
+ process.exit(0);
23
+ }
24
+ try {
25
+ if (cmd === "start") {
26
+ await startManager(parsed.flags);
67
27
  }
68
- writeAdminConfig(configPath, username, password);
69
- }
70
-
71
- const pkgRoot = resolvePackageRoot();
72
- const apiEntry = path.join(pkgRoot, "dist", "index.js");
73
- const webDist = path.join(pkgRoot, "web-dist");
74
-
75
- if (!fs.existsSync(apiEntry) || !fs.existsSync(webDist)) {
76
- console.error("[manager] Package is missing build artifacts.");
77
- console.error("[manager] Please reinstall or use a release that includes dist assets.");
78
- process.exit(1);
79
- }
80
-
81
- const out = fs.openSync(logPath, "a");
82
- const err = fs.openSync(errorLogPath, "a");
83
- const child = spawn(process.execPath, [apiEntry], {
84
- env: {
85
- ...process.env,
86
- MANAGER_API_HOST: apiHost,
87
- MANAGER_API_PORT: apiPort,
88
- MANAGER_WEB_DIST: webDist,
89
- MANAGER_CONFIG_PATH: configPath
90
- },
91
- detached: true,
92
- stdio: ["ignore", out, err]
93
- });
94
- child.unref();
95
-
96
- fs.writeFileSync(pidPath, String(child.pid), "utf-8");
97
-
98
- const lanIp = resolveLanIp();
99
- console.log(`[manager] Started (pid: ${child.pid}).`);
100
- console.log(`[manager] Log: ${logPath}`);
101
- console.log(`[manager] Error log: ${errorLogPath}`);
102
- console.log(`[manager] Open (local): http://localhost:${apiPort}`);
103
- console.log(`[manager] Open (local): http://127.0.0.1:${apiPort}`);
104
- if (lanIp) {
105
- console.log(`[manager] Open (LAN): http://${lanIp}:${apiPort}`);
106
- }
107
- }
108
-
109
- function ensureDir(dir) {
110
- if (!dir) return;
111
- fs.mkdirSync(dir, { recursive: true });
112
- }
113
-
114
- function isRunning(pidPath) {
115
- if (!fs.existsSync(pidPath)) return false;
116
- const raw = fs.readFileSync(pidPath, "utf-8").trim();
117
- const pid = Number(raw);
118
- if (!pid) return false;
119
- try {
120
- process.kill(pid, 0);
121
- return true;
122
- } catch {
123
- return false;
124
- }
125
- }
126
-
127
- function writeAdminConfig(configPath, username, password) {
128
- const salt = randomBytes(16).toString("base64");
129
- const hash = scryptSync(password, salt, 64).toString("base64");
130
- const payload = {
131
- auth: {
132
- username,
133
- salt,
134
- hash
135
- },
136
- createdAt: new Date().toISOString()
137
- };
138
- ensureDir(path.dirname(configPath));
139
- fs.writeFileSync(configPath, JSON.stringify(payload, null, 2));
140
- console.log(`[manager] Admin config saved to ${configPath}`);
141
- }
142
-
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") {
28
+ else if (cmd === "stop") {
29
+ const result = stopManager(parsed.flags);
30
+ for (const line of result.messages)
31
+ console.log(line);
32
+ if (!result.ok)
33
+ process.exit(1);
34
+ }
35
+ else if (cmd === "stop-all") {
36
+ const result = stopAll(parsed.flags);
37
+ for (const line of result.messages)
38
+ console.log(line);
39
+ if (!result.ok)
40
+ process.exit(1);
41
+ }
42
+ else {
43
+ console.error(`[manager] Unknown command: ${cmd}`);
44
+ printHelp();
171
45
  process.exit(1);
172
- }
173
- value += char;
174
- };
175
- stdin.on("data", onData);
176
- });
177
- }
178
-
179
- function resolveLanIp() {
180
- const nets = os.networkInterfaces();
181
- for (const name of Object.keys(nets)) {
182
- for (const net of nets[name] ?? []) {
183
- if (net.family === "IPv4" && !net.internal) {
184
- return net.address;
185
- }
186
46
  }
187
- }
188
- return null;
189
- }
190
-
191
- function resolvePackageRoot() {
192
- const filePath = fileURLToPath(import.meta.url);
193
- return path.resolve(path.dirname(filePath), "..");
194
47
  }
195
-
196
- function printHelp() {
197
- console.log(`openclaw-manager\n\nUsage:\n openclaw-manager start\n\nOptions:\n -h, --help Show help\n -v, --version Show version\n`);
48
+ catch (err) {
49
+ const message = err instanceof Error ? err.message : String(err);
50
+ console.error(message);
51
+ process.exit(1);
198
52
  }
@@ -3,8 +3,8 @@ export function buildCommandRegistry(root) {
3
3
  return [
4
4
  {
5
5
  id: "install-cli",
6
- title: "Install Clawdbot CLI",
7
- description: "Install the latest Clawdbot CLI (may require sudo)",
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 Clawdbot CLI");
11
+ const job = deps.jobStore.createJob("Install OpenClaw CLI");
12
12
  deps.jobStore.startJob(job.id);
13
- deps.jobStore.appendLog(job.id, "开始安装 Clawdbot CLI...");
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.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "openclaw-manager": "bin/openclaw-manager.js"
@@ -13,6 +13,16 @@
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"
18
+ },
19
+ "devDependencies": {
20
+ "@types/prompts": "2.4.9",
21
+ "@types/node": "25.0.10",
22
+ "typescript": "5.9.3"
23
+ },
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.json",
26
+ "lint": "tsc -p tsconfig.json --noEmit"
17
27
  }
18
28
  }