openclaw-helper 0.3.5 → 0.4.0

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.
@@ -514,7 +514,7 @@ function scanCosts(agents) {
514
514
  }
515
515
 
516
516
  // src/dashboard/server.ts
517
- var _PKG_VER = true ? "0.3.5" : "0.2.1";
517
+ var _PKG_VER = true ? "0.4.0" : "0.2.1";
518
518
  var pkgVersion = _PKG_VER;
519
519
  function readDoctorLogs(maxLines = 50) {
520
520
  if (!existsSync6(DOCTOR_LOG_DIR)) return [];
@@ -614,6 +614,11 @@ function renderShell() {
614
614
  <span class="status-dot" :style="'background:' + statusColor"></span>
615
615
  <span class="status-label" :style="'color:' + statusColor" x-text="statusText"></span>
616
616
  </div>
617
+ <div class="nav-actions" style="display:flex;gap:0.5rem;align-items:center;">
618
+ <button class="btn" style="background:#00A67E;color:#fff;padding:0.35rem 0.75rem;font-size:0.75rem;" :disabled="actionLoading" @click="doStart()">\u25B6 Start</button>
619
+ <button class="btn btn-blue" style="padding:0.35rem 0.75rem;font-size:0.75rem;" :disabled="actionLoading" @click="doRestart()">\u21BA Restart</button>
620
+ <button class="btn" style="background:#ef4444;color:#fff;padding:0.35rem 0.75rem;font-size:0.75rem;" :disabled="actionLoading" @click="doStop()">\u25A0 Stop</button>
621
+ </div>
617
622
  <div class="nav-right" x-text="lastCheck ? 'Updated ' + lastCheck : 'Loading...'"></div>
618
623
  </div>
619
624
 
@@ -941,6 +946,34 @@ function renderShell() {
941
946
  this.actionLoading = false;
942
947
  },
943
948
 
949
+ async doStart() {
950
+ this.actionLoading = true;
951
+ this.actionResult = '';
952
+ try {
953
+ const res = await fetch('/api/gateway/start', { method: 'POST' });
954
+ const d = await res.json();
955
+ this.actionResult = d.success ? 'Gateway started.' : ('Start failed: ' + (d.message ?? 'unknown'));
956
+ await this.refresh();
957
+ } catch (e) {
958
+ this.actionResult = 'Request failed: ' + e.message;
959
+ }
960
+ this.actionLoading = false;
961
+ },
962
+
963
+ async doStop() {
964
+ this.actionLoading = true;
965
+ this.actionResult = '';
966
+ try {
967
+ const res = await fetch('/api/gateway/stop', { method: 'POST' });
968
+ const d = await res.json();
969
+ this.actionResult = d.success ? 'Gateway stopped.' : ('Stop failed: ' + (d.message ?? 'unknown'));
970
+ await this.refresh();
971
+ } catch (e) {
972
+ this.actionResult = 'Request failed: ' + e.message;
973
+ }
974
+ this.actionLoading = false;
975
+ },
976
+
944
977
  async doDoctor() {
945
978
  this.actionLoading = true;
946
979
  this.actionResult = '';
@@ -1011,6 +1044,26 @@ async function handleApiRestart(info, res) {
1011
1044
  res.end(JSON.stringify({ success: false, message: String(err) }));
1012
1045
  }
1013
1046
  }
1047
+ async function handleApiGatewayStart(info, res) {
1048
+ try {
1049
+ const result = await startGateway(info);
1050
+ res.writeHead(200, { "Content-Type": "application/json" });
1051
+ res.end(JSON.stringify({ success: result.success, message: result.output ?? result.error ?? "" }));
1052
+ } catch (err) {
1053
+ res.writeHead(500, { "Content-Type": "application/json" });
1054
+ res.end(JSON.stringify({ success: false, message: String(err) }));
1055
+ }
1056
+ }
1057
+ async function handleApiGatewayStop(info, res) {
1058
+ try {
1059
+ const result = await stopGateway(info);
1060
+ res.writeHead(200, { "Content-Type": "application/json" });
1061
+ res.end(JSON.stringify({ success: result.success, message: result.output ?? result.error ?? "" }));
1062
+ } catch (err) {
1063
+ res.writeHead(500, { "Content-Type": "application/json" });
1064
+ res.end(JSON.stringify({ success: false, message: String(err) }));
1065
+ }
1066
+ }
1014
1067
  async function handleApiDoctor(info, res) {
1015
1068
  try {
1016
1069
  const output = await runOpenClawCmd(info, "doctor --non-interactive");
@@ -1055,6 +1108,10 @@ function startDashboard(options) {
1055
1108
  handleApiLogs(res);
1056
1109
  } else if (method === "POST" && url === "/api/restart") {
1057
1110
  await handleApiRestart(info, res);
1111
+ } else if (method === "POST" && url === "/api/gateway/start") {
1112
+ await handleApiGatewayStart(info, res);
1113
+ } else if (method === "POST" && url === "/api/gateway/stop") {
1114
+ await handleApiGatewayStop(info, res);
1058
1115
  } else if (method === "POST" && url === "/api/doctor") {
1059
1116
  await handleApiDoctor(info, res);
1060
1117
  } else {
@@ -1073,6 +1130,7 @@ function startDashboard(options) {
1073
1130
  export {
1074
1131
  __require,
1075
1132
  BINARY_NAME,
1133
+ APP_HOME,
1076
1134
  DISPLAY_NAME,
1077
1135
  DOCTOR_LOG_DIR,
1078
1136
  PID_FILE,
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ APP_HOME,
3
4
  BINARY_NAME,
4
5
  DISPLAY_NAME,
5
6
  DOCTOR_LOG_DIR,
@@ -17,7 +18,7 @@ import {
17
18
  startDashboard,
18
19
  startGateway,
19
20
  stopGateway
20
- } from "./chunk-LJXPQKQB.js";
21
+ } from "./chunk-UXL57WT4.js";
21
22
 
22
23
  // src/index.ts
23
24
  import { spawnSync } from "child_process";
@@ -25,9 +26,137 @@ import { Command } from "commander";
25
26
 
26
27
  // src/commands/watch.ts
27
28
  import { spawn } from "child_process";
28
- import { writeFileSync, readFileSync, existsSync, unlinkSync, openSync } from "fs";
29
+ import { writeFileSync as writeFileSync2, readFileSync as readFileSync2, existsSync as existsSync2, unlinkSync, openSync } from "fs";
29
30
  import chalk from "chalk";
31
+ import { join as join2 } from "path";
32
+
33
+ // src/telemetry.ts
34
+ import { createHash, randomUUID } from "crypto";
35
+ import { execSync } from "child_process";
36
+ import { readFileSync, writeFileSync, existsSync } from "fs";
30
37
  import { join } from "path";
38
+ var MEASUREMENT_ID = "G-B46J8RT804";
39
+ var API_SECRET = "qkqms1nURj2S02Q3WqO7GQ";
40
+ var ENDPOINT = `https://www.google-analytics.com/mp/collect?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`;
41
+ var TELEMETRY_FILE = join(APP_HOME, "telemetry.json");
42
+ function loadState() {
43
+ if (existsSync(TELEMETRY_FILE)) {
44
+ try {
45
+ return JSON.parse(readFileSync(TELEMETRY_FILE, "utf-8"));
46
+ } catch {
47
+ }
48
+ }
49
+ const state = {
50
+ client_id: resolveClientId(),
51
+ opt_out: false,
52
+ first_run_notified: false
53
+ };
54
+ saveState(state);
55
+ return state;
56
+ }
57
+ function saveState(state) {
58
+ try {
59
+ writeFileSync(TELEMETRY_FILE, JSON.stringify(state, null, 2) + "\n");
60
+ } catch {
61
+ }
62
+ }
63
+ function sha256(input) {
64
+ return createHash("sha256").update(input).digest("hex");
65
+ }
66
+ function tryExec(cmd) {
67
+ try {
68
+ return execSync(cmd, { stdio: ["ignore", "pipe", "ignore"], timeout: 2e3 }).toString().trim();
69
+ } catch {
70
+ return null;
71
+ }
72
+ }
73
+ function resolveClientId() {
74
+ const gitEmail = tryExec("git config --global user.email");
75
+ if (gitEmail && gitEmail.includes("@")) return "git:" + sha256(gitEmail);
76
+ const npmEmail = tryExec("npm config get email");
77
+ if (npmEmail && npmEmail.includes("@")) return "npm:" + sha256(npmEmail);
78
+ return "anon:" + randomUUID();
79
+ }
80
+ function isOptedOut() {
81
+ if (process.env.OPENCLAW_NO_TELEMETRY === "1") return true;
82
+ if (process.env.DO_NOT_TRACK === "1") return true;
83
+ if (process.env.CI) return true;
84
+ try {
85
+ const state = loadState();
86
+ return state.opt_out;
87
+ } catch {
88
+ return false;
89
+ }
90
+ }
91
+ function setOptOut(value) {
92
+ const state = loadState();
93
+ state.opt_out = value;
94
+ saveState(state);
95
+ }
96
+ function getTelemetryStatus() {
97
+ const state = loadState();
98
+ return { optOut: state.opt_out, clientId: state.client_id };
99
+ }
100
+ function printFirstRunNotice() {
101
+ const state = loadState();
102
+ if (state.first_run_notified || state.opt_out) return;
103
+ state.first_run_notified = true;
104
+ saveState(state);
105
+ process.stderr.write(
106
+ "\n \u{1F4CA} OpenClaw collects anonymous usage data to improve the product.\n To opt out: openclaw telemetry off (or set OPENCLAW_NO_TELEMETRY=1)\n\n"
107
+ );
108
+ }
109
+ async function track(eventName, opts) {
110
+ if (isOptedOut()) return;
111
+ let state;
112
+ try {
113
+ state = loadState();
114
+ } catch {
115
+ return;
116
+ }
117
+ const params = {
118
+ platform: opts.platform,
119
+ engagement_time_msec: 1,
120
+ ...opts.command !== void 0 && { command: opts.command },
121
+ ...opts.success !== void 0 && { success: opts.success ? 1 : 0 },
122
+ ...opts.version !== void 0 && { app_version: opts.version },
123
+ ...opts.os !== void 0 && { os_type: opts.os },
124
+ ...opts.extra
125
+ };
126
+ const payload = {
127
+ client_id: state.client_id,
128
+ non_personalized_ads: true,
129
+ events: [
130
+ {
131
+ name: eventName,
132
+ params
133
+ }
134
+ ]
135
+ };
136
+ try {
137
+ const controller = new AbortController();
138
+ const timer = setTimeout(() => controller.abort(), 3e3);
139
+ await fetch(ENDPOINT, {
140
+ method: "POST",
141
+ headers: { "Content-Type": "application/json" },
142
+ body: JSON.stringify(payload),
143
+ signal: controller.signal
144
+ });
145
+ clearTimeout(timer);
146
+ } catch {
147
+ }
148
+ }
149
+ async function trackCommand(command, success, version2) {
150
+ await track("cli_command", {
151
+ platform: "cli",
152
+ command,
153
+ success,
154
+ version: version2,
155
+ os: process.platform
156
+ });
157
+ }
158
+
159
+ // src/commands/watch.ts
31
160
  async function watchDaemon(options) {
32
161
  if (options.daemon) {
33
162
  return daemonize(options);
@@ -36,14 +165,16 @@ async function watchDaemon(options) {
36
165
  initLogger();
37
166
  ensureDoctorHome();
38
167
  const info = detectOpenClaw(options.profile ?? config.openclawProfile);
39
- writeFileSync(PID_FILE, String(process.pid));
168
+ writeFileSync2(PID_FILE, String(process.pid));
169
+ trackCommand("watch start", true).catch(() => {
170
+ });
40
171
  log("info", "OpenClaw Doctor started (foreground)");
41
172
  log("info", `Gateway port: ${info.gatewayPort}`);
42
173
  log("info", `Channels: ${info.channels.join(", ") || "none detected"}`);
43
174
  log("info", `Check interval: ${config.checkInterval}s`);
44
175
  log("info", `PID: ${process.pid}`);
45
176
  if (options.dashboard) {
46
- const { startDashboard: startDashboard2 } = await import("./server-BR52HPJB.js");
177
+ const { startDashboard: startDashboard2 } = await import("./server-GZ3MD6AH.js");
47
178
  startDashboard2({ config: options.config });
48
179
  }
49
180
  const throttle = new RestartThrottle(config.maxRestartsPerHour);
@@ -105,8 +236,8 @@ function daemonize(options) {
105
236
  (a) => a !== "-d" && a !== "--daemon"
106
237
  );
107
238
  const fullArgs = [...execArgv, ...scriptArgs];
108
- const outLog = join(DOCTOR_LOG_DIR, "daemon.out.log");
109
- const errLog = join(DOCTOR_LOG_DIR, "daemon.err.log");
239
+ const outLog = join2(DOCTOR_LOG_DIR, "daemon.out.log");
240
+ const errLog = join2(DOCTOR_LOG_DIR, "daemon.err.log");
110
241
  const out = openSync(outLog, "a");
111
242
  const err = openSync(errLog, "a");
112
243
  const child = spawn(process.execPath, fullArgs, {
@@ -115,7 +246,7 @@ function daemonize(options) {
115
246
  env: { ...process.env, OPENCLAW_DOCTOR_DAEMON: "1" }
116
247
  });
117
248
  const pid = child.pid;
118
- writeFileSync(PID_FILE, String(pid));
249
+ writeFileSync2(PID_FILE, String(pid));
119
250
  child.unref();
120
251
  console.log(chalk.green(`Doctor started in background (PID ${pid})`));
121
252
  console.log(chalk.gray(` Logs: ${outLog}`));
@@ -138,8 +269,12 @@ async function stopDaemon(options) {
138
269
  try {
139
270
  process.kill(pid, "SIGTERM");
140
271
  console.log(chalk.green(`Doctor stopped (PID ${pid})`));
272
+ trackCommand("watch stop", true).catch(() => {
273
+ });
141
274
  } catch (err) {
142
275
  console.log(chalk.red(`Failed to stop Doctor (PID ${pid}): ${err}`));
276
+ trackCommand("watch stop", false).catch(() => {
277
+ });
143
278
  }
144
279
  await new Promise((r) => setTimeout(r, 1e3));
145
280
  try {
@@ -148,8 +283,8 @@ async function stopDaemon(options) {
148
283
  }
149
284
  }
150
285
  function readDaemonPid() {
151
- if (!existsSync(PID_FILE)) return null;
152
- const raw = readFileSync(PID_FILE, "utf-8").trim();
286
+ if (!existsSync2(PID_FILE)) return null;
287
+ const raw = readFileSync2(PID_FILE, "utf-8").trim();
153
288
  const pid = parseInt(raw, 10);
154
289
  return isNaN(pid) ? null : pid;
155
290
  }
@@ -224,17 +359,19 @@ async function showStatus(options) {
224
359
  console.log(chalk2.gray(` OpenClaw ${info.version ?? "unknown"}`));
225
360
  console.log(chalk2.gray(` Config: ${info.configPath}`));
226
361
  console.log();
362
+ trackCommand("status", true).catch(() => {
363
+ });
227
364
  process.exit(result.healthy ? 0 : 1);
228
365
  }
229
366
 
230
367
  // src/commands/doctor.ts
231
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
368
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
232
369
  import chalk3 from "chalk";
233
370
  function findConfigIssues(configPath) {
234
- if (!existsSync2(configPath)) return [];
371
+ if (!existsSync3(configPath)) return [];
235
372
  let raw;
236
373
  try {
237
- raw = JSON.parse(readFileSync2(configPath, "utf-8"));
374
+ raw = JSON.parse(readFileSync3(configPath, "utf-8"));
238
375
  } catch {
239
376
  return [{ path: "root", message: "Config file is not valid JSON", fix: () => {
240
377
  } }];
@@ -261,7 +398,7 @@ function findConfigIssues(configPath) {
261
398
  const origFix = originalFixes[i];
262
399
  issues[i].fix = () => {
263
400
  origFix();
264
- writeFileSync2(configPath, JSON.stringify(raw, null, 2) + "\n");
401
+ writeFileSync3(configPath, JSON.stringify(raw, null, 2) + "\n");
265
402
  };
266
403
  }
267
404
  }
@@ -346,11 +483,13 @@ async function runDoctor(options) {
346
483
  console.log(chalk3.green(" Gateway healthy \u2014 no repair needed"));
347
484
  }
348
485
  }
486
+ trackCommand("doctor", true).catch(() => {
487
+ });
349
488
  console.log();
350
489
  }
351
490
 
352
491
  // src/commands/logs.ts
353
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
492
+ import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
354
493
  import chalk4 from "chalk";
355
494
  function showLogs(options) {
356
495
  const config = loadConfig(options.config);
@@ -361,14 +500,14 @@ function showLogs(options) {
361
500
  }
362
501
  const info = detectOpenClaw(options.profile ?? config.openclawProfile);
363
502
  const logFile = options.error ? `${info.logDir}/gateway.err.log` : `${info.logDir}/gateway.log`;
364
- if (!existsSync3(logFile)) {
503
+ if (!existsSync4(logFile)) {
365
504
  console.log(chalk4.yellow(`Log file not found: ${logFile}`));
366
505
  return;
367
506
  }
368
507
  console.log(chalk4.blue.bold(`
369
508
  ${logFile}
370
509
  `));
371
- const content = readFileSync3(logFile, "utf-8");
510
+ const content = readFileSync4(logFile, "utf-8");
372
511
  const lines = content.trim().split("\n");
373
512
  const tail = lines.slice(-maxLines);
374
513
  for (const line of tail) {
@@ -384,8 +523,8 @@ function showLogs(options) {
384
523
  }
385
524
  function showDoctorLogs(maxLines) {
386
525
  const { readdirSync } = __require("fs");
387
- const { join: join3 } = __require("path");
388
- if (!existsSync3(DOCTOR_LOG_DIR)) {
526
+ const { join: join4 } = __require("path");
527
+ if (!existsSync4(DOCTOR_LOG_DIR)) {
389
528
  console.log(chalk4.yellow("No doctor logs found."));
390
529
  return;
391
530
  }
@@ -396,9 +535,9 @@ function showDoctorLogs(maxLines) {
396
535
  }
397
536
  const latest = files[0];
398
537
  console.log(chalk4.blue.bold(`
399
- ${join3(DOCTOR_LOG_DIR, latest)}
538
+ ${join4(DOCTOR_LOG_DIR, latest)}
400
539
  `));
401
- const content = readFileSync3(join3(DOCTOR_LOG_DIR, latest), "utf-8");
540
+ const content = readFileSync4(join4(DOCTOR_LOG_DIR, latest), "utf-8");
402
541
  const lines = content.trim().split("\n");
403
542
  const tail = lines.slice(-maxLines);
404
543
  for (const line of tail) {
@@ -417,6 +556,7 @@ function showDoctorLogs(maxLines) {
417
556
 
418
557
  // src/commands/gateway.ts
419
558
  import chalk5 from "chalk";
559
+ var _VER = true ? "0.4.0" : void 0;
420
560
  async function gatewayStart(options) {
421
561
  const config = loadConfig(options.config);
422
562
  initLogger();
@@ -424,8 +564,12 @@ async function gatewayStart(options) {
424
564
  const result = await startGateway(info);
425
565
  if (result.success) {
426
566
  console.log(chalk5.green("Gateway started"));
567
+ trackCommand("gateway start", true, _VER).catch(() => {
568
+ });
427
569
  } else {
428
570
  console.log(chalk5.red(`Failed to start gateway: ${result.error}`));
571
+ trackCommand("gateway start", false, _VER).catch(() => {
572
+ });
429
573
  process.exit(1);
430
574
  }
431
575
  }
@@ -436,8 +580,12 @@ async function gatewayStop(options) {
436
580
  const result = await stopGateway(info);
437
581
  if (result.success) {
438
582
  console.log(chalk5.green("Gateway stopped"));
583
+ trackCommand("gateway stop", true, _VER).catch(() => {
584
+ });
439
585
  } else {
440
586
  console.log(chalk5.red(`Failed to stop gateway: ${result.error}`));
587
+ trackCommand("gateway stop", false, _VER).catch(() => {
588
+ });
441
589
  process.exit(1);
442
590
  }
443
591
  }
@@ -448,19 +596,23 @@ async function gatewayRestart(options) {
448
596
  const result = await restartGateway(info);
449
597
  if (result.success) {
450
598
  console.log(chalk5.green("Gateway restarted"));
599
+ trackCommand("gateway restart", true, _VER).catch(() => {
600
+ });
451
601
  } else {
452
602
  console.log(chalk5.red(`Failed to restart gateway: ${result.error}`));
603
+ trackCommand("gateway restart", false, _VER).catch(() => {
604
+ });
453
605
  process.exit(1);
454
606
  }
455
607
  }
456
608
 
457
609
  // src/commands/memory.ts
458
610
  import chalk6 from "chalk";
459
- import { existsSync as existsSync4, statSync } from "fs";
460
- import { join as join2 } from "path";
611
+ import { existsSync as existsSync5, statSync } from "fs";
612
+ import { join as join3 } from "path";
461
613
  import { homedir } from "os";
462
614
  function expandHome(p) {
463
- return p.startsWith("~/") ? join2(homedir(), p.slice(2)) : p;
615
+ return p.startsWith("~/") ? join3(homedir(), p.slice(2)) : p;
464
616
  }
465
617
  async function memoryStatus(options) {
466
618
  const config = loadConfig(options.config);
@@ -470,8 +622,8 @@ async function memoryStatus(options) {
470
622
  const ws = agent.workspace;
471
623
  if (!ws) continue;
472
624
  const wsPath = expandHome(ws);
473
- const memPath = join2(wsPath, "MEMORY.md");
474
- const exists = existsSync4(memPath);
625
+ const memPath = join3(wsPath, "MEMORY.md");
626
+ const exists = existsSync5(memPath);
475
627
  const sizeKB = exists ? Math.round(statSync(memPath).size / 1024) : 0;
476
628
  const warn = sizeKB > 50;
477
629
  const indicator = warn ? chalk6.yellow("\u26A0") : chalk6.green("\u2713");
@@ -510,9 +662,30 @@ async function memoryCompact(options) {
510
662
  console.log();
511
663
  }
512
664
 
665
+ // src/commands/telemetry.ts
666
+ import chalk7 from "chalk";
667
+ function telemetryOn() {
668
+ setOptOut(false);
669
+ console.log(chalk7.green("\u2713 Telemetry enabled. Thanks for helping improve OpenClaw!"));
670
+ }
671
+ function telemetryOff() {
672
+ setOptOut(true);
673
+ console.log(chalk7.yellow("\u2713 Telemetry disabled. Set OPENCLAW_NO_TELEMETRY=1 to suppress permanently."));
674
+ }
675
+ function telemetryStatus() {
676
+ const { optOut, clientId } = getTelemetryStatus();
677
+ const status = optOut ? chalk7.red("disabled") : chalk7.green("enabled");
678
+ console.log(`Telemetry: ${status}`);
679
+ console.log(`Client ID: ${chalk7.dim(clientId)}`);
680
+ console.log();
681
+ console.log(chalk7.dim("Toggle: openclaw telemetry on/off"));
682
+ console.log(chalk7.dim("Env: OPENCLAW_NO_TELEMETRY=1"));
683
+ }
684
+
513
685
  // src/index.ts
514
- var _PKG_VER = true ? "0.3.5" : "0.2.1";
686
+ var _PKG_VER = true ? "0.4.0" : "0.2.1";
515
687
  var version = _PKG_VER;
688
+ printFirstRunNotice();
516
689
  var program = new Command();
517
690
  program.name(BINARY_NAME).description(`${DISPLAY_NAME} \u2014 health monitor and management for OpenClaw services`).version(version);
518
691
  var addGlobalOpts = (cmd) => cmd.option("-c, --config <path>", "Path to config file").option("--profile <name>", "OpenClaw profile (default, dev, ...)", "default");
@@ -535,6 +708,10 @@ var gw = program.command("gateway").description("Manage the OpenClaw gateway ser
535
708
  addGlobalOpts(gw.command("start").description("Start the gateway")).action(gatewayStart);
536
709
  addGlobalOpts(gw.command("stop").description("Stop the gateway")).action(gatewayStop);
537
710
  addGlobalOpts(gw.command("restart").description("Restart the gateway")).action(gatewayRestart);
711
+ var tele = program.command("telemetry").description("Manage anonymous usage telemetry");
712
+ tele.command("on").description("Enable telemetry").action(telemetryOn);
713
+ tele.command("off").description("Disable telemetry").action(telemetryOff);
714
+ tele.command("status").description("Show telemetry status").action(telemetryStatus);
538
715
  var mem = program.command("memory").description("Memory file health and management");
539
716
  addGlobalOpts(mem.command("status").description("Show MEMORY.md size and health per agent")).action(memoryStatus);
540
717
  addGlobalOpts(
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startDashboard
4
- } from "./chunk-LJXPQKQB.js";
4
+ } from "./chunk-UXL57WT4.js";
5
5
  export {
6
6
  startDashboard
7
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-helper",
3
- "version": "0.3.5",
3
+ "version": "0.4.0",
4
4
  "description": "AI assistant gateway watchdog — alias for openclaw-cli. Visit https://openclaw-cli.app",
5
5
  "type": "module",
6
6
  "bin": {