volclaw 0.3.1

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.
Files changed (80) hide show
  1. package/CONTRIBUTING.md +105 -0
  2. package/LICENSE +21 -0
  3. package/README.aiclaw.md +22 -0
  4. package/README.aliclaw.md +22 -0
  5. package/README.autoopenclaw.md +22 -0
  6. package/README.claw-open.md +22 -0
  7. package/README.clawjs.md +22 -0
  8. package/README.fastclaw.md +22 -0
  9. package/README.md +22 -0
  10. package/README.md.bak +242 -0
  11. package/README.megaclaw.md +22 -0
  12. package/README.open-claw.md +22 -0
  13. package/README.openclaw-cli.md +239 -0
  14. package/README.openclaw-daemon.md +239 -0
  15. package/README.openclaw-gateway.md +239 -0
  16. package/README.openclaw-health.md +239 -0
  17. package/README.openclaw-helper.md +239 -0
  18. package/README.openclaw-install.md +239 -0
  19. package/README.openclaw-manage.md +239 -0
  20. package/README.openclaw-monitor.md +239 -0
  21. package/README.openclaw-run.md +239 -0
  22. package/README.openclaw-service.md +239 -0
  23. package/README.openclaw-setup.md +239 -0
  24. package/README.openclaw-start.md +239 -0
  25. package/README.openclaw-tools.md +239 -0
  26. package/README.openclaw-upgrade.md +13 -0
  27. package/README.openclaw-utils.md +239 -0
  28. package/README.openclaw-watch.md +239 -0
  29. package/README.qclaw-cli.md +22 -0
  30. package/README.qclaw.md +22 -0
  31. package/README.smartclaw.md +22 -0
  32. package/README.volclaw.md +22 -0
  33. package/README.zh-CN.md +213 -0
  34. package/app-dist/main.js +300 -0
  35. package/app-dist/package.json +3 -0
  36. package/app-dist/server-process.js +95 -0
  37. package/assets/demo.gif +0 -0
  38. package/assets/welcome.png +0 -0
  39. package/demo.tape +28 -0
  40. package/dist/chunk-LIZ6XXW3.js +1149 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +582 -0
  43. package/dist/server-ZYSNFLSO.js +7 -0
  44. package/homebrew/README.md +37 -0
  45. package/homebrew/openclaw-cli.rb +22 -0
  46. package/homebrew/openclaw-doctor.rb +26 -0
  47. package/package.aiclaw.json +25 -0
  48. package/package.aliclaw.json +25 -0
  49. package/package.autoopenclaw.json +25 -0
  50. package/package.claw-open.json +25 -0
  51. package/package.clawjs.json +25 -0
  52. package/package.fastclaw.json +25 -0
  53. package/package.json +25 -0
  54. package/package.json.bak +51 -0
  55. package/package.megaclaw.json +25 -0
  56. package/package.open-claw.json +25 -0
  57. package/package.openclaw-cli.json +51 -0
  58. package/package.openclaw-daemon.json +51 -0
  59. package/package.openclaw-gateway.json +51 -0
  60. package/package.openclaw-health.json +51 -0
  61. package/package.openclaw-helper.json +51 -0
  62. package/package.openclaw-install.json +51 -0
  63. package/package.openclaw-manage.json +51 -0
  64. package/package.openclaw-monitor.json +51 -0
  65. package/package.openclaw-run.json +51 -0
  66. package/package.openclaw-service.json +51 -0
  67. package/package.openclaw-setup.json +51 -0
  68. package/package.openclaw-start.json +51 -0
  69. package/package.openclaw-tools.json +51 -0
  70. package/package.openclaw-upgrade.json +50 -0
  71. package/package.openclaw-utils.json +51 -0
  72. package/package.openclaw-watch.json +51 -0
  73. package/package.qclaw-cli.json +25 -0
  74. package/package.qclaw.json +25 -0
  75. package/package.smartclaw.json +25 -0
  76. package/package.volclaw.json +25 -0
  77. package/scripts/post-app-compile.cjs +3 -0
  78. package/scripts/post-app-compile.js +7 -0
  79. package/scripts/publish.sh +120 -0
  80. package/scripts/publish.sh.bak +63 -0
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,582 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ BINARY_NAME,
4
+ DISPLAY_NAME,
5
+ DOCTOR_LOG_DIR,
6
+ PID_FILE,
7
+ RestartThrottle,
8
+ __require,
9
+ checkHealth,
10
+ detectOpenClaw,
11
+ ensureDoctorHome,
12
+ initLogger,
13
+ loadConfig,
14
+ log,
15
+ restartGateway,
16
+ runOpenClawCmd,
17
+ startDashboard,
18
+ startGateway,
19
+ stopGateway
20
+ } from "./chunk-LIZ6XXW3.js";
21
+
22
+ // src/index.ts
23
+ import { spawnSync } from "child_process";
24
+ import { Command } from "commander";
25
+
26
+ // src/commands/watch.ts
27
+ import { spawn } from "child_process";
28
+ import { writeFileSync, readFileSync, existsSync, unlinkSync, openSync } from "fs";
29
+ import chalk from "chalk";
30
+ import { join } from "path";
31
+ async function watchDaemon(options) {
32
+ if (options.daemon) {
33
+ return daemonize(options);
34
+ }
35
+ const config = loadConfig(options.config);
36
+ initLogger();
37
+ ensureDoctorHome();
38
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
39
+ writeFileSync(PID_FILE, String(process.pid));
40
+ log("info", "OpenClaw Doctor started (foreground)");
41
+ log("info", `Gateway port: ${info.gatewayPort}`);
42
+ log("info", `Channels: ${info.channels.join(", ") || "none detected"}`);
43
+ log("info", `Check interval: ${config.checkInterval}s`);
44
+ log("info", `PID: ${process.pid}`);
45
+ if (options.dashboard) {
46
+ const { startDashboard: startDashboard2 } = await import("./server-ZYSNFLSO.js");
47
+ startDashboard2({ config: options.config });
48
+ }
49
+ const throttle = new RestartThrottle(config.maxRestartsPerHour);
50
+ let consecutiveFailures = 0;
51
+ let isRestarting = false;
52
+ async function tick() {
53
+ if (isRestarting) return;
54
+ const result = await checkHealth(info);
55
+ if (result.healthy) {
56
+ consecutiveFailures = 0;
57
+ return;
58
+ }
59
+ consecutiveFailures++;
60
+ log(
61
+ "warn",
62
+ `Consecutive failures: ${consecutiveFailures}/${config.failThreshold}`
63
+ );
64
+ if (consecutiveFailures >= config.failThreshold) {
65
+ if (!throttle.canRestart()) {
66
+ log(
67
+ "error",
68
+ `Restart throttled: ${throttle.recentCount()} restarts in the last hour (max ${config.maxRestartsPerHour})`
69
+ );
70
+ return;
71
+ }
72
+ isRestarting = true;
73
+ consecutiveFailures = 0;
74
+ throttle.record();
75
+ await restartGateway(info);
76
+ log("info", "Waiting 30s for gateway to start...");
77
+ await new Promise((r) => setTimeout(r, 6e4));
78
+ isRestarting = false;
79
+ }
80
+ }
81
+ await tick();
82
+ setInterval(tick, config.checkInterval * 1e3);
83
+ const cleanup = () => {
84
+ log("info", "OpenClaw Doctor stopped");
85
+ try {
86
+ unlinkSync(PID_FILE);
87
+ } catch {
88
+ }
89
+ process.exit(0);
90
+ };
91
+ process.on("SIGINT", cleanup);
92
+ process.on("SIGTERM", cleanup);
93
+ }
94
+ function daemonize(options) {
95
+ ensureDoctorHome();
96
+ const existingPid = readDaemonPid();
97
+ if (existingPid && isProcessAlive(existingPid)) {
98
+ console.log(chalk.yellow(`Doctor is already running (PID ${existingPid})`));
99
+ return;
100
+ }
101
+ const execArgv = process.execArgv.filter(
102
+ (a) => !a.includes("--eval")
103
+ );
104
+ const scriptArgs = process.argv.slice(1).filter(
105
+ (a) => a !== "-d" && a !== "--daemon"
106
+ );
107
+ const fullArgs = [...execArgv, ...scriptArgs];
108
+ const outLog = join(DOCTOR_LOG_DIR, "daemon.out.log");
109
+ const errLog = join(DOCTOR_LOG_DIR, "daemon.err.log");
110
+ const out = openSync(outLog, "a");
111
+ const err = openSync(errLog, "a");
112
+ const child = spawn(process.execPath, fullArgs, {
113
+ detached: true,
114
+ stdio: ["ignore", out, err],
115
+ env: { ...process.env, OPENCLAW_DOCTOR_DAEMON: "1" }
116
+ });
117
+ const pid = child.pid;
118
+ writeFileSync(PID_FILE, String(pid));
119
+ child.unref();
120
+ console.log(chalk.green(`Doctor started in background (PID ${pid})`));
121
+ console.log(chalk.gray(` Logs: ${outLog}`));
122
+ console.log(chalk.gray(` Stop: openclaw-doctor stop`));
123
+ }
124
+ async function stopDaemon(options) {
125
+ const pid = readDaemonPid();
126
+ if (!pid) {
127
+ console.log(chalk.yellow("Doctor is not running (no PID file)"));
128
+ return;
129
+ }
130
+ if (!isProcessAlive(pid)) {
131
+ console.log(chalk.yellow(`Doctor is not running (PID ${pid} is dead, cleaning up)`));
132
+ try {
133
+ unlinkSync(PID_FILE);
134
+ } catch {
135
+ }
136
+ return;
137
+ }
138
+ try {
139
+ process.kill(pid, "SIGTERM");
140
+ console.log(chalk.green(`Doctor stopped (PID ${pid})`));
141
+ } catch (err) {
142
+ console.log(chalk.red(`Failed to stop Doctor (PID ${pid}): ${err}`));
143
+ }
144
+ await new Promise((r) => setTimeout(r, 1e3));
145
+ try {
146
+ unlinkSync(PID_FILE);
147
+ } catch {
148
+ }
149
+ }
150
+ function readDaemonPid() {
151
+ if (!existsSync(PID_FILE)) return null;
152
+ const raw = readFileSync(PID_FILE, "utf-8").trim();
153
+ const pid = parseInt(raw, 10);
154
+ return isNaN(pid) ? null : pid;
155
+ }
156
+ function isProcessAlive(pid) {
157
+ try {
158
+ process.kill(pid, 0);
159
+ return true;
160
+ } catch {
161
+ return false;
162
+ }
163
+ }
164
+
165
+ // src/commands/status.ts
166
+ import chalk2 from "chalk";
167
+ async function showStatus(options) {
168
+ const config = loadConfig(options.config);
169
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
170
+ const result = await checkHealth(info);
171
+ if (options.json) {
172
+ console.log(
173
+ JSON.stringify(
174
+ {
175
+ gateway: {
176
+ healthy: result.healthy,
177
+ reachable: result.gateway,
178
+ port: info.gatewayPort,
179
+ durationMs: result.durationMs
180
+ },
181
+ channels: result.channels,
182
+ agents: info.agents,
183
+ openclaw: {
184
+ version: info.version,
185
+ profile: info.profile,
186
+ configPath: info.configPath
187
+ },
188
+ doctor: {
189
+ checkInterval: config.checkInterval,
190
+ failThreshold: config.failThreshold
191
+ }
192
+ },
193
+ null,
194
+ 2
195
+ )
196
+ );
197
+ process.exit(result.healthy ? 0 : 1);
198
+ return;
199
+ }
200
+ console.log(chalk2.bold("\n OpenClaw Doctor\n"));
201
+ if (result.healthy) {
202
+ console.log(
203
+ chalk2.green.bold(` Gateway: HEALTHY`) + chalk2.gray(` (port ${info.gatewayPort}, ${result.durationMs}ms)`)
204
+ );
205
+ } else if (result.gateway) {
206
+ console.log(chalk2.yellow.bold(` Gateway: DEGRADED`) + chalk2.gray(` (responded but ok=false)`));
207
+ } else {
208
+ console.log(chalk2.red.bold(` Gateway: UNREACHABLE`));
209
+ if (result.error) console.log(chalk2.red(` ${result.error}`));
210
+ }
211
+ if (result.channels && result.channels.length > 0) {
212
+ console.log();
213
+ for (const ch of result.channels ?? []) {
214
+ const icon = ch.ok ? chalk2.green("ok") : chalk2.red("fail");
215
+ console.log(` ${chalk2.gray("Channel")} ${ch.name}: ${icon}`);
216
+ }
217
+ }
218
+ if (info.agents.length > 0) {
219
+ console.log();
220
+ const agentList = info.agents.map((a) => a.isDefault ? `${a.name} (default)` : a.name).join(", ");
221
+ console.log(chalk2.gray(` Agents: ${agentList}`));
222
+ }
223
+ console.log();
224
+ console.log(chalk2.gray(` OpenClaw ${info.version ?? "unknown"}`));
225
+ console.log(chalk2.gray(` Config: ${info.configPath}`));
226
+ console.log();
227
+ process.exit(result.healthy ? 0 : 1);
228
+ }
229
+
230
+ // src/commands/doctor.ts
231
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
232
+ import chalk3 from "chalk";
233
+ function findConfigIssues(configPath) {
234
+ if (!existsSync2(configPath)) return [];
235
+ let raw;
236
+ try {
237
+ raw = JSON.parse(readFileSync2(configPath, "utf-8"));
238
+ } catch {
239
+ return [{ path: "root", message: "Config file is not valid JSON", fix: () => {
240
+ } }];
241
+ }
242
+ const issues = [];
243
+ if (Array.isArray(raw.agents?.list)) {
244
+ for (let i = 0; i < raw.agents.list.length; i++) {
245
+ const agent = raw.agents.list[i];
246
+ if (Array.isArray(agent.workspace)) {
247
+ const idx = i;
248
+ issues.push({
249
+ path: `agents.list.${i}.workspace`,
250
+ message: `Invalid input: expected string, received array`,
251
+ fix: () => {
252
+ raw.agents.list[idx].workspace = raw.agents.list[idx].workspace[0];
253
+ }
254
+ });
255
+ }
256
+ }
257
+ }
258
+ if (issues.length > 0) {
259
+ const originalFixes = issues.map((issue) => issue.fix);
260
+ for (let i = 0; i < issues.length; i++) {
261
+ const origFix = originalFixes[i];
262
+ issues[i].fix = () => {
263
+ origFix();
264
+ writeFileSync2(configPath, JSON.stringify(raw, null, 2) + "\n");
265
+ };
266
+ }
267
+ }
268
+ return issues;
269
+ }
270
+ async function runDoctor(options) {
271
+ const config = loadConfig(options.config);
272
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
273
+ console.log(chalk3.bold("\n OpenClaw Doctor \u2014 Full Diagnostics\n"));
274
+ console.log(chalk3.gray(" [0/4] Config validation"));
275
+ const issues = findConfigIssues(info.configPath);
276
+ if (issues.length === 0) {
277
+ console.log(chalk3.green(" Config: valid"));
278
+ } else {
279
+ for (const issue of issues) {
280
+ console.log(chalk3.red(` ${issue.path}: ${issue.message}`));
281
+ }
282
+ if (options.fix) {
283
+ for (const issue of issues) {
284
+ issue.fix();
285
+ console.log(chalk3.green(` Fixed: ${issue.path}`));
286
+ }
287
+ console.log(chalk3.green(` Config saved: ${info.configPath}`));
288
+ } else {
289
+ console.log(chalk3.yellow(" Run with --fix to auto-repair"));
290
+ }
291
+ }
292
+ console.log(chalk3.gray("\n [1/4] OpenClaw binary"));
293
+ if (info.cliBinPath) {
294
+ console.log(chalk3.green(` Found: ${info.cliBinPath}`));
295
+ console.log(chalk3.gray(` Node: ${info.nodePath}`));
296
+ if (info.version) console.log(chalk3.gray(` Version: ${info.version}`));
297
+ } else {
298
+ console.log(chalk3.red(" Not found \u2014 openclaw CLI is not installed or not in PATH"));
299
+ }
300
+ console.log(chalk3.gray("\n [2/4] Gateway health"));
301
+ const result = await checkHealth(info);
302
+ if (result.healthy) {
303
+ console.log(chalk3.green(` Gateway: healthy (port ${info.gatewayPort}, ${result.durationMs}ms)`));
304
+ } else if (result.gateway) {
305
+ console.log(chalk3.yellow(` Gateway: responded but degraded`));
306
+ } else {
307
+ console.log(chalk3.red(` Gateway: unreachable`));
308
+ if (result.error) console.log(chalk3.red(` ${result.error}`));
309
+ }
310
+ console.log(chalk3.gray("\n [3/4] Channels"));
311
+ if (result.channels.length > 0) {
312
+ for (const ch of result.channels) {
313
+ const status = ch.ok ? chalk3.green("ok") : chalk3.red("fail");
314
+ console.log(` ${ch.name}: ${status}`);
315
+ }
316
+ } else {
317
+ console.log(chalk3.yellow(" No channel data available"));
318
+ }
319
+ console.log(chalk3.gray("\n [4/4] OpenClaw built-in doctor"));
320
+ const doctorOutput = await runOpenClawCmd(info, "doctor");
321
+ if (doctorOutput) {
322
+ const lines = doctorOutput.split("\n");
323
+ const startIdx = lines.findIndex((l) => l.includes("OpenClaw doctor") || l.includes("Gateway service"));
324
+ const relevant = startIdx >= 0 ? lines.slice(startIdx) : lines;
325
+ for (const line of relevant) {
326
+ console.log(` ${line}`);
327
+ }
328
+ } else {
329
+ console.log(chalk3.yellow(" Could not run openclaw doctor"));
330
+ }
331
+ if (options.fix) {
332
+ console.log(chalk3.gray("\n [5/5] Auto-repair"));
333
+ if (!result.healthy) {
334
+ console.log(chalk3.yellow(" Gateway unhealthy \u2014 running openclaw doctor --repair --non-interactive"));
335
+ const repairOutput = await runOpenClawCmd(info, "doctor --repair --non-interactive");
336
+ if (repairOutput) {
337
+ const lines = repairOutput.split("\n");
338
+ for (const line of lines.slice(0, 30)) {
339
+ if (line.trim()) console.log(` ${line}`);
340
+ }
341
+ console.log(chalk3.green(" Repair completed"));
342
+ } else {
343
+ console.log(chalk3.yellow(" Could not run repair (openclaw CLI unavailable)"));
344
+ }
345
+ } else {
346
+ console.log(chalk3.green(" Gateway healthy \u2014 no repair needed"));
347
+ }
348
+ }
349
+ console.log();
350
+ }
351
+
352
+ // src/commands/logs.ts
353
+ import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
354
+ import chalk4 from "chalk";
355
+ function showLogs(options) {
356
+ const config = loadConfig(options.config);
357
+ const maxLines = parseInt(options.lines ?? "50", 10);
358
+ if (options.doctor) {
359
+ showDoctorLogs(maxLines);
360
+ return;
361
+ }
362
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
363
+ const logFile = options.error ? `${info.logDir}/gateway.err.log` : `${info.logDir}/gateway.log`;
364
+ if (!existsSync3(logFile)) {
365
+ console.log(chalk4.yellow(`Log file not found: ${logFile}`));
366
+ return;
367
+ }
368
+ console.log(chalk4.blue.bold(`
369
+ ${logFile}
370
+ `));
371
+ const content = readFileSync3(logFile, "utf-8");
372
+ const lines = content.trim().split("\n");
373
+ const tail = lines.slice(-maxLines);
374
+ for (const line of tail) {
375
+ if (line.includes("[error]") || line.includes("[ERROR]")) {
376
+ console.log(chalk4.red(line));
377
+ } else if (line.includes("[warn]") || line.includes("[WARN]")) {
378
+ console.log(chalk4.yellow(line));
379
+ } else {
380
+ console.log(chalk4.gray(line));
381
+ }
382
+ }
383
+ console.log();
384
+ }
385
+ function showDoctorLogs(maxLines) {
386
+ const { readdirSync } = __require("fs");
387
+ const { join: join3 } = __require("path");
388
+ if (!existsSync3(DOCTOR_LOG_DIR)) {
389
+ console.log(chalk4.yellow("No doctor logs found."));
390
+ return;
391
+ }
392
+ const files = readdirSync(DOCTOR_LOG_DIR).filter((f) => f.endsWith(".log")).sort().reverse();
393
+ if (files.length === 0) {
394
+ console.log(chalk4.yellow("No doctor log files found."));
395
+ return;
396
+ }
397
+ const latest = files[0];
398
+ console.log(chalk4.blue.bold(`
399
+ ${join3(DOCTOR_LOG_DIR, latest)}
400
+ `));
401
+ const content = readFileSync3(join3(DOCTOR_LOG_DIR, latest), "utf-8");
402
+ const lines = content.trim().split("\n");
403
+ const tail = lines.slice(-maxLines);
404
+ for (const line of tail) {
405
+ if (line.includes("[ERROR]")) {
406
+ console.log(chalk4.red(line));
407
+ } else if (line.includes("[WARN]")) {
408
+ console.log(chalk4.yellow(line));
409
+ } else if (line.includes("[SUCCESS]")) {
410
+ console.log(chalk4.green(line));
411
+ } else {
412
+ console.log(chalk4.gray(line));
413
+ }
414
+ }
415
+ console.log();
416
+ }
417
+
418
+ // src/commands/gateway.ts
419
+ import chalk5 from "chalk";
420
+ async function gatewayStart(options) {
421
+ const config = loadConfig(options.config);
422
+ initLogger();
423
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
424
+ const result = await startGateway(info);
425
+ if (result.success) {
426
+ console.log(chalk5.green("Gateway started"));
427
+ } else {
428
+ console.log(chalk5.red(`Failed to start gateway: ${result.error}`));
429
+ process.exit(1);
430
+ }
431
+ }
432
+ async function gatewayStop(options) {
433
+ const config = loadConfig(options.config);
434
+ initLogger();
435
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
436
+ const result = await stopGateway(info);
437
+ if (result.success) {
438
+ console.log(chalk5.green("Gateway stopped"));
439
+ } else {
440
+ console.log(chalk5.red(`Failed to stop gateway: ${result.error}`));
441
+ process.exit(1);
442
+ }
443
+ }
444
+ async function gatewayRestart(options) {
445
+ const config = loadConfig(options.config);
446
+ initLogger();
447
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
448
+ const result = await restartGateway(info);
449
+ if (result.success) {
450
+ console.log(chalk5.green("Gateway restarted"));
451
+ } else {
452
+ console.log(chalk5.red(`Failed to restart gateway: ${result.error}`));
453
+ process.exit(1);
454
+ }
455
+ }
456
+
457
+ // src/commands/memory.ts
458
+ import chalk6 from "chalk";
459
+ import { existsSync as existsSync4, statSync } from "fs";
460
+ import { join as join2 } from "path";
461
+ import { homedir } from "os";
462
+ function expandHome(p) {
463
+ return p.startsWith("~/") ? join2(homedir(), p.slice(2)) : p;
464
+ }
465
+ async function memoryStatus(options) {
466
+ const config = loadConfig(options.config);
467
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
468
+ console.log(chalk6.bold("\n Memory Status\n"));
469
+ for (const agent of info.agents) {
470
+ const ws = agent.workspace;
471
+ if (!ws) continue;
472
+ const wsPath = expandHome(ws);
473
+ const memPath = join2(wsPath, "MEMORY.md");
474
+ const exists = existsSync4(memPath);
475
+ const sizeKB = exists ? Math.round(statSync(memPath).size / 1024) : 0;
476
+ const warn = sizeKB > 50;
477
+ const indicator = warn ? chalk6.yellow("\u26A0") : chalk6.green("\u2713");
478
+ const sizeStr = warn ? chalk6.yellow(`${sizeKB}KB`) : chalk6.gray(`${sizeKB}KB`);
479
+ console.log(` ${indicator} ${agent.name.padEnd(16)} MEMORY.md: ${sizeStr}${warn ? chalk6.yellow(" \u2014 exceeds 50KB, may waste tokens") : ""}`);
480
+ }
481
+ console.log();
482
+ }
483
+ async function memorySearch(query, options) {
484
+ const config = loadConfig(options.config);
485
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
486
+ console.log(chalk6.bold(`
487
+ Searching memory: "${query}"
488
+ `));
489
+ const output = await runOpenClawCmd(info, `memory search "${query}"`);
490
+ if (output) {
491
+ console.log(output);
492
+ } else {
493
+ console.log(chalk6.yellow(" No results or openclaw memory search unavailable"));
494
+ }
495
+ console.log();
496
+ }
497
+ async function memoryCompact(options) {
498
+ const config = loadConfig(options.config);
499
+ const info = detectOpenClaw(options.profile ?? config.openclawProfile);
500
+ const flag = options.dryRun ? "--dry-run" : "";
501
+ console.log(chalk6.bold(`
502
+ Memory Compact${options.dryRun ? " (dry run)" : ""}
503
+ `));
504
+ const output = await runOpenClawCmd(info, `memory compact ${flag}`);
505
+ if (output) {
506
+ console.log(output);
507
+ } else {
508
+ console.log(chalk6.yellow(" openclaw memory compact not available"));
509
+ }
510
+ console.log();
511
+ }
512
+
513
+ // src/index.ts
514
+ var _PKG_VER = true ? "0.3.1" : "0.2.1";
515
+ var version = _PKG_VER;
516
+ var program = new Command();
517
+ program.name(BINARY_NAME).description(`${DISPLAY_NAME} \u2014 health monitor and management for OpenClaw services`).version(version);
518
+ var addGlobalOpts = (cmd) => cmd.option("-c, --config <path>", "Path to config file").option("--profile <name>", "OpenClaw profile (default, dev, ...)", "default");
519
+ addGlobalOpts(
520
+ program.command("watch").description("Start health monitoring daemon").option("-d, --daemon", "Run in background").option("--dashboard", "Also start web dashboard")
521
+ ).action(watchDaemon);
522
+ addGlobalOpts(
523
+ program.command("unwatch").description("Stop monitoring daemon")
524
+ ).action(stopDaemon);
525
+ addGlobalOpts(
526
+ program.command("status").description("Show gateway and channel health").option("--json", "Machine-readable JSON output")
527
+ ).action(showStatus);
528
+ addGlobalOpts(
529
+ program.command("doctor").description("Run full diagnostics (our checks + openclaw doctor)").option("--fix", "Auto-fix common config issues")
530
+ ).action(runDoctor);
531
+ addGlobalOpts(
532
+ program.command("monitor").description("Start local monitoring web UI (http://localhost:9090)")
533
+ ).action(startDashboard);
534
+ var gw = program.command("gateway").description("Manage the OpenClaw gateway service");
535
+ addGlobalOpts(gw.command("start").description("Start the gateway")).action(gatewayStart);
536
+ addGlobalOpts(gw.command("stop").description("Stop the gateway")).action(gatewayStop);
537
+ addGlobalOpts(gw.command("restart").description("Restart the gateway")).action(gatewayRestart);
538
+ var mem = program.command("memory").description("Memory file health and management");
539
+ addGlobalOpts(mem.command("status").description("Show MEMORY.md size and health per agent")).action(memoryStatus);
540
+ addGlobalOpts(
541
+ mem.command("search").description("Search agent memory files").argument("<query>", "Search query")
542
+ ).action((query, options) => memorySearch(query, options));
543
+ addGlobalOpts(
544
+ mem.command("compact").description("Compact agent memory (proxies to openclaw memory compact)").option("--dry-run", "Preview without applying")
545
+ ).action(memoryCompact);
546
+ addGlobalOpts(
547
+ program.command("logs").description("View logs (proxies to openclaw logs; use --doctor for our own logs)").option("-n, --lines <count>", "Number of lines to show", "50").option("--error", "Show gateway error logs").option("--doctor", "Show our own event logs").option("--tail", "Follow logs in real time").allowUnknownOption()
548
+ ).action((options, cmd) => {
549
+ if (options.doctor) {
550
+ showLogs({ ...options, doctor: true });
551
+ } else {
552
+ proxyToOpenclaw(cmd.args.length ? cmd.args : [], ["logs", ...process.argv.slice(3)]);
553
+ }
554
+ });
555
+ program.command("*", { hidden: true, isDefault: false }).allowUnknownOption().action(() => {
556
+ });
557
+ program.addHelpText("after", `
558
+ Proxy: any unrecognized command is forwarded to the openclaw CLI.
559
+ e.g. "${BINARY_NAME} channels list" \u2192 "openclaw channels list"
560
+ `);
561
+ var knownCommands = program.commands.map((c) => c.name());
562
+ var rawArgs = process.argv.slice(2);
563
+ var firstArg = rawArgs[0];
564
+ if (firstArg && !firstArg.startsWith("-") && !knownCommands.includes(firstArg)) {
565
+ proxyToOpenclaw(rawArgs);
566
+ } else {
567
+ program.parse();
568
+ }
569
+ function proxyToOpenclaw(args, override) {
570
+ const info = detectOpenClaw("default");
571
+ const bin = info.cliBinPath;
572
+ if (!bin) {
573
+ console.error("openclaw CLI not found. Please install openclaw first.");
574
+ process.exit(1);
575
+ }
576
+ const passArgs = override ?? args;
577
+ const result = spawnSync(info.nodePath, [bin, ...passArgs], {
578
+ stdio: "inherit",
579
+ env: process.env
580
+ });
581
+ process.exit(result.status ?? 0);
582
+ }
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ startDashboard
4
+ } from "./chunk-LIZ6XXW3.js";
5
+ export {
6
+ startDashboard
7
+ };
@@ -0,0 +1,37 @@
1
+ # Homebrew Tap — OpenClaw
2
+
3
+ This directory contains Homebrew formulas for OpenClaw tools.
4
+
5
+ ## Setup
6
+
7
+ 1. Create a new GitHub repo: `Sobranier/homebrew-openclaw`
8
+ 2. Copy the `.rb` files from this directory into `Formula/` in that repo
9
+ 3. After creating a GitHub Release (e.g. `v0.3.0`), update the `sha256` in each formula:
10
+
11
+ ```bash
12
+ curl -sL https://github.com/Sobranier/openclaw-doctor/archive/refs/tags/v0.3.0.tar.gz | shasum -a 256
13
+ ```
14
+
15
+ ## Installation (once published)
16
+
17
+ ```bash
18
+ brew tap Sobranier/openclaw
19
+ brew install openclaw-cli
20
+
21
+ # Or the doctor alias (backward compat)
22
+ brew install openclaw-doctor
23
+ ```
24
+
25
+ ## Update formula on new release
26
+
27
+ When you publish a new version:
28
+
29
+ 1. Update `url` version tag in both `.rb` files
30
+ 2. Recalculate `sha256`:
31
+ ```bash
32
+ curl -sL https://github.com/Sobranier/openclaw-doctor/archive/refs/tags/vX.X.X.tar.gz | shasum -a 256
33
+ ```
34
+ 3. Update `sha256` in both `.rb` files
35
+ 4. Commit and push to `homebrew-openclaw` repo
36
+
37
+ > Tip: Add this to `scripts/publish.sh` to automate the sha256 update.
@@ -0,0 +1,22 @@
1
+ class OpenclasCli < Formula
2
+ desc "CLI and health-watch daemon for OpenClaw AI assistant gateway"
3
+ homepage "https://github.com/Sobranier/openclaw-doctor"
4
+ url "https://github.com/Sobranier/openclaw-doctor/archive/refs/tags/v0.3.0.tar.gz"
5
+ sha256 "REPLACE_WITH_SHA256_AFTER_RELEASE"
6
+ license "MIT"
7
+ head "https://github.com/Sobranier/openclaw-doctor.git", branch: "main"
8
+
9
+ depends_on "node"
10
+
11
+ def install
12
+ system "npm", "install", *std_npm_args
13
+ # Install the openclaw-cli binary
14
+ bin.install_symlink Dir["#{libexec}/bin/openclaw-cli"]
15
+ # Also provide openclaw-doctor alias for backward compatibility
16
+ bin.install_symlink Dir["#{libexec}/bin/openclaw-cli"] => "openclaw-doctor"
17
+ end
18
+
19
+ test do
20
+ assert_match version.to_s, shell_output("#{bin}/openclaw-cli --version")
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # openclaw-doctor is an alias for openclaw-cli.
2
+ # The main formula is openclaw-cli — install that instead:
3
+ #
4
+ # brew install Sobranier/openclaw/openclaw-cli
5
+ #
6
+ class OpenclawDoctor < Formula
7
+ desc "Alias for openclaw-cli — health-watch daemon for OpenClaw gateway"
8
+ homepage "https://github.com/Sobranier/openclaw-doctor"
9
+ url "https://github.com/Sobranier/openclaw-doctor/archive/refs/tags/v0.3.0.tar.gz"
10
+ sha256 "REPLACE_WITH_SHA256_AFTER_RELEASE"
11
+ license "MIT"
12
+
13
+ depends_on "node"
14
+ # Recommend the primary formula
15
+ deprecated_unless "openclaw-cli is now the primary package; this formula is kept for backward compatibility."
16
+
17
+ def install
18
+ system "npm", "install", *std_npm_args
19
+ bin.install_symlink Dir["#{libexec}/bin/openclaw-cli"] => "openclaw-doctor"
20
+ bin.install_symlink Dir["#{libexec}/bin/openclaw-cli"]
21
+ end
22
+
23
+ test do
24
+ assert_match version.to_s, shell_output("#{bin}/openclaw-doctor --version")
25
+ end
26
+ end