miclaw 0.3.5 → 0.5.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.
@@ -34,6 +34,7 @@ var DOCTOR_HOME = APP_HOME;
34
34
  var CONFIG_PATH = join2(APP_HOME, "config.json");
35
35
  var DOCTOR_LOG_DIR = join2(APP_HOME, "logs");
36
36
  var PID_FILE = join2(APP_HOME, "daemon.pid");
37
+ var STOP_FLAG_FILE = join2(APP_HOME, "gateway.stopped");
37
38
  var defaults = {
38
39
  checkInterval: 30,
39
40
  failThreshold: 5,
@@ -234,11 +235,12 @@ function getRestartCommand(info) {
234
235
  }
235
236
  function getStopCommand(info) {
236
237
  const uid = process.getuid?.() ?? 501;
237
- return `launchctl kill SIGTERM gui/${uid}/${info.launchdLabel}`;
238
+ return `launchctl bootout gui/${uid}/${info.launchdLabel} 2>/dev/null || launchctl kill SIGTERM gui/${uid}/${info.launchdLabel} 2>/dev/null || true`;
238
239
  }
239
240
  function getStartCommand(info) {
240
241
  const uid = process.getuid?.() ?? 501;
241
- return `launchctl kickstart gui/${uid}/${info.launchdLabel}`;
242
+ const plistDir = `${process.env.HOME}/Library/LaunchAgents`;
243
+ return `(launchctl bootstrap gui/${uid} ${plistDir}/${info.launchdLabel}.plist 2>/dev/null || true) && launchctl kickstart gui/${uid}/${info.launchdLabel}`;
242
244
  }
243
245
 
244
246
  // src/core/logger.ts
@@ -514,7 +516,7 @@ function scanCosts(agents) {
514
516
  }
515
517
 
516
518
  // src/dashboard/server.ts
517
- var _PKG_VER = true ? "0.3.5" : "0.2.1";
519
+ var _PKG_VER = true ? "0.5.0" : "0.2.1";
518
520
  var pkgVersion = _PKG_VER;
519
521
  function readDoctorLogs(maxLines = 50) {
520
522
  if (!existsSync6(DOCTOR_LOG_DIR)) return [];
@@ -614,6 +616,11 @@ function renderShell() {
614
616
  <span class="status-dot" :style="'background:' + statusColor"></span>
615
617
  <span class="status-label" :style="'color:' + statusColor" x-text="statusText"></span>
616
618
  </div>
619
+ <div class="nav-actions" style="display:flex;gap:0.5rem;align-items:center;">
620
+ <button class="btn" style="background:#00A67E;color:#fff;padding:0.35rem 0.75rem;font-size:0.75rem;" :disabled="actionLoading" @click="doStart()">\u25B6 Start</button>
621
+ <button class="btn btn-blue" style="padding:0.35rem 0.75rem;font-size:0.75rem;" :disabled="actionLoading" @click="doRestart()">\u21BA Restart</button>
622
+ <button class="btn" style="background:#ef4444;color:#fff;padding:0.35rem 0.75rem;font-size:0.75rem;" :disabled="actionLoading" @click="doStop()">\u25A0 Stop</button>
623
+ </div>
617
624
  <div class="nav-right" x-text="lastCheck ? 'Updated ' + lastCheck : 'Loading...'"></div>
618
625
  </div>
619
626
 
@@ -941,6 +948,34 @@ function renderShell() {
941
948
  this.actionLoading = false;
942
949
  },
943
950
 
951
+ async doStart() {
952
+ this.actionLoading = true;
953
+ this.actionResult = '';
954
+ try {
955
+ const res = await fetch('/api/gateway/start', { method: 'POST' });
956
+ const d = await res.json();
957
+ this.actionResult = d.success ? 'Gateway started.' : ('Start failed: ' + (d.message ?? 'unknown'));
958
+ await this.refresh();
959
+ } catch (e) {
960
+ this.actionResult = 'Request failed: ' + e.message;
961
+ }
962
+ this.actionLoading = false;
963
+ },
964
+
965
+ async doStop() {
966
+ this.actionLoading = true;
967
+ this.actionResult = '';
968
+ try {
969
+ const res = await fetch('/api/gateway/stop', { method: 'POST' });
970
+ const d = await res.json();
971
+ this.actionResult = d.success ? 'Gateway stopped.' : ('Stop failed: ' + (d.message ?? 'unknown'));
972
+ await this.refresh();
973
+ } catch (e) {
974
+ this.actionResult = 'Request failed: ' + e.message;
975
+ }
976
+ this.actionLoading = false;
977
+ },
978
+
944
979
  async doDoctor() {
945
980
  this.actionLoading = true;
946
981
  this.actionResult = '';
@@ -1011,6 +1046,26 @@ async function handleApiRestart(info, res) {
1011
1046
  res.end(JSON.stringify({ success: false, message: String(err) }));
1012
1047
  }
1013
1048
  }
1049
+ async function handleApiGatewayStart(info, res) {
1050
+ try {
1051
+ const result = await startGateway(info);
1052
+ res.writeHead(200, { "Content-Type": "application/json" });
1053
+ res.end(JSON.stringify({ success: result.success, message: result.output ?? result.error ?? "" }));
1054
+ } catch (err) {
1055
+ res.writeHead(500, { "Content-Type": "application/json" });
1056
+ res.end(JSON.stringify({ success: false, message: String(err) }));
1057
+ }
1058
+ }
1059
+ async function handleApiGatewayStop(info, res) {
1060
+ try {
1061
+ const result = await stopGateway(info);
1062
+ res.writeHead(200, { "Content-Type": "application/json" });
1063
+ res.end(JSON.stringify({ success: result.success, message: result.output ?? result.error ?? "" }));
1064
+ } catch (err) {
1065
+ res.writeHead(500, { "Content-Type": "application/json" });
1066
+ res.end(JSON.stringify({ success: false, message: String(err) }));
1067
+ }
1068
+ }
1014
1069
  async function handleApiDoctor(info, res) {
1015
1070
  try {
1016
1071
  const output = await runOpenClawCmd(info, "doctor --non-interactive");
@@ -1055,6 +1110,10 @@ function startDashboard(options) {
1055
1110
  handleApiLogs(res);
1056
1111
  } else if (method === "POST" && url === "/api/restart") {
1057
1112
  await handleApiRestart(info, res);
1113
+ } else if (method === "POST" && url === "/api/gateway/start") {
1114
+ await handleApiGatewayStart(info, res);
1115
+ } else if (method === "POST" && url === "/api/gateway/stop") {
1116
+ await handleApiGatewayStop(info, res);
1058
1117
  } else if (method === "POST" && url === "/api/doctor") {
1059
1118
  await handleApiDoctor(info, res);
1060
1119
  } else {
@@ -1073,9 +1132,11 @@ function startDashboard(options) {
1073
1132
  export {
1074
1133
  __require,
1075
1134
  BINARY_NAME,
1135
+ APP_HOME,
1076
1136
  DISPLAY_NAME,
1077
1137
  DOCTOR_LOG_DIR,
1078
1138
  PID_FILE,
1139
+ STOP_FLAG_FILE,
1079
1140
  ensureDoctorHome,
1080
1141
  loadConfig,
1081
1142
  initLogger,
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
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,
6
7
  PID_FILE,
7
8
  RestartThrottle,
9
+ STOP_FLAG_FILE,
8
10
  __require,
9
11
  checkHealth,
10
12
  detectOpenClaw,
@@ -17,7 +19,7 @@ import {
17
19
  startDashboard,
18
20
  startGateway,
19
21
  stopGateway
20
- } from "./chunk-LJXPQKQB.js";
22
+ } from "./chunk-XSRH4RHR.js";
21
23
 
22
24
  // src/index.ts
23
25
  import { spawnSync } from "child_process";
@@ -25,9 +27,137 @@ import { Command } from "commander";
25
27
 
26
28
  // src/commands/watch.ts
27
29
  import { spawn } from "child_process";
28
- import { writeFileSync, readFileSync, existsSync, unlinkSync, openSync } from "fs";
30
+ import { writeFileSync as writeFileSync2, readFileSync as readFileSync2, existsSync as existsSync2, unlinkSync, openSync } from "fs";
29
31
  import chalk from "chalk";
32
+ import { join as join2 } from "path";
33
+
34
+ // src/telemetry.ts
35
+ import { createHash, randomUUID } from "crypto";
36
+ import { execSync } from "child_process";
37
+ import { readFileSync, writeFileSync, existsSync } from "fs";
30
38
  import { join } from "path";
39
+ var MEASUREMENT_ID = "G-B46J8RT804";
40
+ var API_SECRET = "qkqms1nURj2S02Q3WqO7GQ";
41
+ var ENDPOINT = `https://www.google-analytics.com/mp/collect?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`;
42
+ var TELEMETRY_FILE = join(APP_HOME, "telemetry.json");
43
+ function loadState() {
44
+ if (existsSync(TELEMETRY_FILE)) {
45
+ try {
46
+ return JSON.parse(readFileSync(TELEMETRY_FILE, "utf-8"));
47
+ } catch {
48
+ }
49
+ }
50
+ const state = {
51
+ client_id: resolveClientId(),
52
+ opt_out: false,
53
+ first_run_notified: false
54
+ };
55
+ saveState(state);
56
+ return state;
57
+ }
58
+ function saveState(state) {
59
+ try {
60
+ writeFileSync(TELEMETRY_FILE, JSON.stringify(state, null, 2) + "\n");
61
+ } catch {
62
+ }
63
+ }
64
+ function sha256(input) {
65
+ return createHash("sha256").update(input).digest("hex");
66
+ }
67
+ function tryExec(cmd) {
68
+ try {
69
+ return execSync(cmd, { stdio: ["ignore", "pipe", "ignore"], timeout: 2e3 }).toString().trim();
70
+ } catch {
71
+ return null;
72
+ }
73
+ }
74
+ function resolveClientId() {
75
+ const gitEmail = tryExec("git config --global user.email");
76
+ if (gitEmail && gitEmail.includes("@")) return "git:" + sha256(gitEmail);
77
+ const npmEmail = tryExec("npm config get email");
78
+ if (npmEmail && npmEmail.includes("@")) return "npm:" + sha256(npmEmail);
79
+ return "anon:" + randomUUID();
80
+ }
81
+ function isOptedOut() {
82
+ if (process.env.OPENCLAW_NO_TELEMETRY === "1") return true;
83
+ if (process.env.DO_NOT_TRACK === "1") return true;
84
+ if (process.env.CI) return true;
85
+ try {
86
+ const state = loadState();
87
+ return state.opt_out;
88
+ } catch {
89
+ return false;
90
+ }
91
+ }
92
+ function setOptOut(value) {
93
+ const state = loadState();
94
+ state.opt_out = value;
95
+ saveState(state);
96
+ }
97
+ function getTelemetryStatus() {
98
+ const state = loadState();
99
+ return { optOut: state.opt_out, clientId: state.client_id };
100
+ }
101
+ function printFirstRunNotice() {
102
+ const state = loadState();
103
+ if (state.first_run_notified || state.opt_out) return;
104
+ state.first_run_notified = true;
105
+ saveState(state);
106
+ process.stderr.write(
107
+ "\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"
108
+ );
109
+ }
110
+ async function track(eventName, opts) {
111
+ if (isOptedOut()) return;
112
+ let state;
113
+ try {
114
+ state = loadState();
115
+ } catch {
116
+ return;
117
+ }
118
+ const params = {
119
+ platform: opts.platform,
120
+ engagement_time_msec: 1,
121
+ ...opts.command !== void 0 && { command: opts.command },
122
+ ...opts.success !== void 0 && { success: opts.success ? 1 : 0 },
123
+ ...opts.version !== void 0 && { app_version: opts.version },
124
+ ...opts.os !== void 0 && { os_type: opts.os },
125
+ ...opts.extra
126
+ };
127
+ const payload = {
128
+ client_id: state.client_id,
129
+ non_personalized_ads: true,
130
+ events: [
131
+ {
132
+ name: eventName,
133
+ params
134
+ }
135
+ ]
136
+ };
137
+ try {
138
+ const controller = new AbortController();
139
+ const timer = setTimeout(() => controller.abort(), 3e3);
140
+ await fetch(ENDPOINT, {
141
+ method: "POST",
142
+ headers: { "Content-Type": "application/json" },
143
+ body: JSON.stringify(payload),
144
+ signal: controller.signal
145
+ });
146
+ clearTimeout(timer);
147
+ } catch {
148
+ }
149
+ }
150
+ async function trackCommand(command, success, version2) {
151
+ await track("cli_command", {
152
+ platform: "cli",
153
+ command,
154
+ success,
155
+ version: version2,
156
+ os: process.platform
157
+ });
158
+ }
159
+
160
+ // src/commands/watch.ts
31
161
  async function watchDaemon(options) {
32
162
  if (options.daemon) {
33
163
  return daemonize(options);
@@ -36,14 +166,16 @@ async function watchDaemon(options) {
36
166
  initLogger();
37
167
  ensureDoctorHome();
38
168
  const info = detectOpenClaw(options.profile ?? config.openclawProfile);
39
- writeFileSync(PID_FILE, String(process.pid));
169
+ writeFileSync2(PID_FILE, String(process.pid));
170
+ trackCommand("watch start", true).catch(() => {
171
+ });
40
172
  log("info", "OpenClaw Doctor started (foreground)");
41
173
  log("info", `Gateway port: ${info.gatewayPort}`);
42
174
  log("info", `Channels: ${info.channels.join(", ") || "none detected"}`);
43
175
  log("info", `Check interval: ${config.checkInterval}s`);
44
176
  log("info", `PID: ${process.pid}`);
45
177
  if (options.dashboard) {
46
- const { startDashboard: startDashboard2 } = await import("./server-BR52HPJB.js");
178
+ const { startDashboard: startDashboard2 } = await import("./server-3JMOADVR.js");
47
179
  startDashboard2({ config: options.config });
48
180
  }
49
181
  const throttle = new RestartThrottle(config.maxRestartsPerHour);
@@ -51,6 +183,10 @@ async function watchDaemon(options) {
51
183
  let isRestarting = false;
52
184
  async function tick() {
53
185
  if (isRestarting) return;
186
+ if (existsSync2(STOP_FLAG_FILE)) {
187
+ log("info", "Gateway is manually stopped \u2014 skipping auto-restart");
188
+ return;
189
+ }
54
190
  const result = await checkHealth(info);
55
191
  if (result.healthy) {
56
192
  consecutiveFailures = 0;
@@ -73,7 +209,7 @@ async function watchDaemon(options) {
73
209
  consecutiveFailures = 0;
74
210
  throttle.record();
75
211
  await restartGateway(info);
76
- log("info", "Waiting 30s for gateway to start...");
212
+ log("info", "Waiting 60s for gateway to start...");
77
213
  await new Promise((r) => setTimeout(r, 6e4));
78
214
  isRestarting = false;
79
215
  }
@@ -105,8 +241,8 @@ function daemonize(options) {
105
241
  (a) => a !== "-d" && a !== "--daemon"
106
242
  );
107
243
  const fullArgs = [...execArgv, ...scriptArgs];
108
- const outLog = join(DOCTOR_LOG_DIR, "daemon.out.log");
109
- const errLog = join(DOCTOR_LOG_DIR, "daemon.err.log");
244
+ const outLog = join2(DOCTOR_LOG_DIR, "daemon.out.log");
245
+ const errLog = join2(DOCTOR_LOG_DIR, "daemon.err.log");
110
246
  const out = openSync(outLog, "a");
111
247
  const err = openSync(errLog, "a");
112
248
  const child = spawn(process.execPath, fullArgs, {
@@ -115,7 +251,7 @@ function daemonize(options) {
115
251
  env: { ...process.env, OPENCLAW_DOCTOR_DAEMON: "1" }
116
252
  });
117
253
  const pid = child.pid;
118
- writeFileSync(PID_FILE, String(pid));
254
+ writeFileSync2(PID_FILE, String(pid));
119
255
  child.unref();
120
256
  console.log(chalk.green(`Doctor started in background (PID ${pid})`));
121
257
  console.log(chalk.gray(` Logs: ${outLog}`));
@@ -138,8 +274,12 @@ async function stopDaemon(options) {
138
274
  try {
139
275
  process.kill(pid, "SIGTERM");
140
276
  console.log(chalk.green(`Doctor stopped (PID ${pid})`));
277
+ trackCommand("watch stop", true).catch(() => {
278
+ });
141
279
  } catch (err) {
142
280
  console.log(chalk.red(`Failed to stop Doctor (PID ${pid}): ${err}`));
281
+ trackCommand("watch stop", false).catch(() => {
282
+ });
143
283
  }
144
284
  await new Promise((r) => setTimeout(r, 1e3));
145
285
  try {
@@ -148,8 +288,8 @@ async function stopDaemon(options) {
148
288
  }
149
289
  }
150
290
  function readDaemonPid() {
151
- if (!existsSync(PID_FILE)) return null;
152
- const raw = readFileSync(PID_FILE, "utf-8").trim();
291
+ if (!existsSync2(PID_FILE)) return null;
292
+ const raw = readFileSync2(PID_FILE, "utf-8").trim();
153
293
  const pid = parseInt(raw, 10);
154
294
  return isNaN(pid) ? null : pid;
155
295
  }
@@ -224,17 +364,19 @@ async function showStatus(options) {
224
364
  console.log(chalk2.gray(` OpenClaw ${info.version ?? "unknown"}`));
225
365
  console.log(chalk2.gray(` Config: ${info.configPath}`));
226
366
  console.log();
367
+ trackCommand("status", true).catch(() => {
368
+ });
227
369
  process.exit(result.healthy ? 0 : 1);
228
370
  }
229
371
 
230
372
  // src/commands/doctor.ts
231
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
373
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
232
374
  import chalk3 from "chalk";
233
375
  function findConfigIssues(configPath) {
234
- if (!existsSync2(configPath)) return [];
376
+ if (!existsSync3(configPath)) return [];
235
377
  let raw;
236
378
  try {
237
- raw = JSON.parse(readFileSync2(configPath, "utf-8"));
379
+ raw = JSON.parse(readFileSync3(configPath, "utf-8"));
238
380
  } catch {
239
381
  return [{ path: "root", message: "Config file is not valid JSON", fix: () => {
240
382
  } }];
@@ -261,7 +403,7 @@ function findConfigIssues(configPath) {
261
403
  const origFix = originalFixes[i];
262
404
  issues[i].fix = () => {
263
405
  origFix();
264
- writeFileSync2(configPath, JSON.stringify(raw, null, 2) + "\n");
406
+ writeFileSync3(configPath, JSON.stringify(raw, null, 2) + "\n");
265
407
  };
266
408
  }
267
409
  }
@@ -346,11 +488,13 @@ async function runDoctor(options) {
346
488
  console.log(chalk3.green(" Gateway healthy \u2014 no repair needed"));
347
489
  }
348
490
  }
491
+ trackCommand("doctor", true).catch(() => {
492
+ });
349
493
  console.log();
350
494
  }
351
495
 
352
496
  // src/commands/logs.ts
353
- import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
497
+ import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
354
498
  import chalk4 from "chalk";
355
499
  function showLogs(options) {
356
500
  const config = loadConfig(options.config);
@@ -361,14 +505,14 @@ function showLogs(options) {
361
505
  }
362
506
  const info = detectOpenClaw(options.profile ?? config.openclawProfile);
363
507
  const logFile = options.error ? `${info.logDir}/gateway.err.log` : `${info.logDir}/gateway.log`;
364
- if (!existsSync3(logFile)) {
508
+ if (!existsSync4(logFile)) {
365
509
  console.log(chalk4.yellow(`Log file not found: ${logFile}`));
366
510
  return;
367
511
  }
368
512
  console.log(chalk4.blue.bold(`
369
513
  ${logFile}
370
514
  `));
371
- const content = readFileSync3(logFile, "utf-8");
515
+ const content = readFileSync4(logFile, "utf-8");
372
516
  const lines = content.trim().split("\n");
373
517
  const tail = lines.slice(-maxLines);
374
518
  for (const line of tail) {
@@ -384,8 +528,8 @@ function showLogs(options) {
384
528
  }
385
529
  function showDoctorLogs(maxLines) {
386
530
  const { readdirSync } = __require("fs");
387
- const { join: join3 } = __require("path");
388
- if (!existsSync3(DOCTOR_LOG_DIR)) {
531
+ const { join: join4 } = __require("path");
532
+ if (!existsSync4(DOCTOR_LOG_DIR)) {
389
533
  console.log(chalk4.yellow("No doctor logs found."));
390
534
  return;
391
535
  }
@@ -396,9 +540,9 @@ function showDoctorLogs(maxLines) {
396
540
  }
397
541
  const latest = files[0];
398
542
  console.log(chalk4.blue.bold(`
399
- ${join3(DOCTOR_LOG_DIR, latest)}
543
+ ${join4(DOCTOR_LOG_DIR, latest)}
400
544
  `));
401
- const content = readFileSync3(join3(DOCTOR_LOG_DIR, latest), "utf-8");
545
+ const content = readFileSync4(join4(DOCTOR_LOG_DIR, latest), "utf-8");
402
546
  const lines = content.trim().split("\n");
403
547
  const tail = lines.slice(-maxLines);
404
548
  for (const line of tail) {
@@ -417,50 +561,77 @@ function showDoctorLogs(maxLines) {
417
561
 
418
562
  // src/commands/gateway.ts
419
563
  import chalk5 from "chalk";
564
+ import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync2 } from "fs";
565
+ var _VER = true ? "0.5.0" : void 0;
420
566
  async function gatewayStart(options) {
421
567
  const config = loadConfig(options.config);
422
568
  initLogger();
569
+ ensureDoctorHome();
423
570
  const info = detectOpenClaw(options.profile ?? config.openclawProfile);
571
+ try {
572
+ unlinkSync2(STOP_FLAG_FILE);
573
+ } catch {
574
+ }
424
575
  const result = await startGateway(info);
425
576
  if (result.success) {
426
- console.log(chalk5.green("Gateway started"));
577
+ console.log(chalk5.green("Gateway started (auto-restart resumed)"));
578
+ trackCommand("gateway start", true, _VER).catch(() => {
579
+ });
427
580
  } else {
428
581
  console.log(chalk5.red(`Failed to start gateway: ${result.error}`));
582
+ trackCommand("gateway start", false, _VER).catch(() => {
583
+ });
429
584
  process.exit(1);
430
585
  }
431
586
  }
432
587
  async function gatewayStop(options) {
433
588
  const config = loadConfig(options.config);
434
589
  initLogger();
590
+ ensureDoctorHome();
435
591
  const info = detectOpenClaw(options.profile ?? config.openclawProfile);
436
592
  const result = await stopGateway(info);
437
593
  if (result.success) {
438
- console.log(chalk5.green("Gateway stopped"));
594
+ writeFileSync4(STOP_FLAG_FILE, (/* @__PURE__ */ new Date()).toISOString());
595
+ console.log(chalk5.green("Gateway stopped (auto-restart paused)"));
596
+ console.log(chalk5.gray(" Run `gateway start` to resume."));
597
+ trackCommand("gateway stop", true, _VER).catch(() => {
598
+ });
439
599
  } else {
440
600
  console.log(chalk5.red(`Failed to stop gateway: ${result.error}`));
601
+ trackCommand("gateway stop", false, _VER).catch(() => {
602
+ });
441
603
  process.exit(1);
442
604
  }
443
605
  }
444
606
  async function gatewayRestart(options) {
445
607
  const config = loadConfig(options.config);
446
608
  initLogger();
609
+ ensureDoctorHome();
447
610
  const info = detectOpenClaw(options.profile ?? config.openclawProfile);
611
+ try {
612
+ unlinkSync2(STOP_FLAG_FILE);
613
+ } catch {
614
+ }
448
615
  const result = await restartGateway(info);
449
616
  if (result.success) {
450
- console.log(chalk5.green("Gateway restarted"));
617
+ console.log(chalk5.green("Gateway restarted (auto-restart resumed)"));
618
+ trackCommand("gateway restart", true, _VER).catch(() => {
619
+ });
451
620
  } else {
452
621
  console.log(chalk5.red(`Failed to restart gateway: ${result.error}`));
622
+ trackCommand("gateway restart", false, _VER).catch(() => {
623
+ });
453
624
  process.exit(1);
454
625
  }
455
626
  }
456
627
 
457
628
  // src/commands/memory.ts
458
629
  import chalk6 from "chalk";
459
- import { existsSync as existsSync4, statSync } from "fs";
460
- import { join as join2 } from "path";
630
+ import { existsSync as existsSync6, statSync } from "fs";
631
+ import { join as join3 } from "path";
461
632
  import { homedir } from "os";
462
633
  function expandHome(p) {
463
- return p.startsWith("~/") ? join2(homedir(), p.slice(2)) : p;
634
+ return p.startsWith("~/") ? join3(homedir(), p.slice(2)) : p;
464
635
  }
465
636
  async function memoryStatus(options) {
466
637
  const config = loadConfig(options.config);
@@ -470,8 +641,8 @@ async function memoryStatus(options) {
470
641
  const ws = agent.workspace;
471
642
  if (!ws) continue;
472
643
  const wsPath = expandHome(ws);
473
- const memPath = join2(wsPath, "MEMORY.md");
474
- const exists = existsSync4(memPath);
644
+ const memPath = join3(wsPath, "MEMORY.md");
645
+ const exists = existsSync6(memPath);
475
646
  const sizeKB = exists ? Math.round(statSync(memPath).size / 1024) : 0;
476
647
  const warn = sizeKB > 50;
477
648
  const indicator = warn ? chalk6.yellow("\u26A0") : chalk6.green("\u2713");
@@ -510,9 +681,30 @@ async function memoryCompact(options) {
510
681
  console.log();
511
682
  }
512
683
 
684
+ // src/commands/telemetry.ts
685
+ import chalk7 from "chalk";
686
+ function telemetryOn() {
687
+ setOptOut(false);
688
+ console.log(chalk7.green("\u2713 Telemetry enabled. Thanks for helping improve OpenClaw!"));
689
+ }
690
+ function telemetryOff() {
691
+ setOptOut(true);
692
+ console.log(chalk7.yellow("\u2713 Telemetry disabled. Set OPENCLAW_NO_TELEMETRY=1 to suppress permanently."));
693
+ }
694
+ function telemetryStatus() {
695
+ const { optOut, clientId } = getTelemetryStatus();
696
+ const status = optOut ? chalk7.red("disabled") : chalk7.green("enabled");
697
+ console.log(`Telemetry: ${status}`);
698
+ console.log(`Client ID: ${chalk7.dim(clientId)}`);
699
+ console.log();
700
+ console.log(chalk7.dim("Toggle: openclaw telemetry on/off"));
701
+ console.log(chalk7.dim("Env: OPENCLAW_NO_TELEMETRY=1"));
702
+ }
703
+
513
704
  // src/index.ts
514
- var _PKG_VER = true ? "0.3.5" : "0.2.1";
705
+ var _PKG_VER = true ? "0.5.0" : "0.2.1";
515
706
  var version = _PKG_VER;
707
+ printFirstRunNotice();
516
708
  var program = new Command();
517
709
  program.name(BINARY_NAME).description(`${DISPLAY_NAME} \u2014 health monitor and management for OpenClaw services`).version(version);
518
710
  var addGlobalOpts = (cmd) => cmd.option("-c, --config <path>", "Path to config file").option("--profile <name>", "OpenClaw profile (default, dev, ...)", "default");
@@ -535,6 +727,10 @@ var gw = program.command("gateway").description("Manage the OpenClaw gateway ser
535
727
  addGlobalOpts(gw.command("start").description("Start the gateway")).action(gatewayStart);
536
728
  addGlobalOpts(gw.command("stop").description("Stop the gateway")).action(gatewayStop);
537
729
  addGlobalOpts(gw.command("restart").description("Restart the gateway")).action(gatewayRestart);
730
+ var tele = program.command("telemetry").description("Manage anonymous usage telemetry");
731
+ tele.command("on").description("Enable telemetry").action(telemetryOn);
732
+ tele.command("off").description("Disable telemetry").action(telemetryOff);
733
+ tele.command("status").description("Show telemetry status").action(telemetryStatus);
538
734
  var mem = program.command("memory").description("Memory file health and management");
539
735
  addGlobalOpts(mem.command("status").description("Show MEMORY.md size and health per agent")).action(memoryStatus);
540
736
  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-XSRH4RHR.js";
5
5
  export {
6
6
  startDashboard
7
7
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "miclaw",
3
- "version": "0.3.5",
4
- "description": "AI assistant gateway watchdog alias for openclaw-cli. Visit https://openclaw-cli.app",
3
+ "version": "0.5.0",
4
+ "description": "Micro OpenClaw CLIlightweight gateway watchdog. Alias of openclaw-cli.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "miclaw": "./dist/index.js"
@@ -15,11 +15,9 @@
15
15
  "keywords": [
16
16
  "openclaw",
17
17
  "openclaw-cli",
18
- "ai",
19
- "daemon",
20
- "gateway",
21
- "watchdog",
22
- "health-check"
18
+ "micro",
19
+ "lightweight",
20
+ "ai-gateway"
23
21
  ],
24
22
  "license": "MIT",
25
23
  "repository": {