txclaw 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
|
-
|
|
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.
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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
|
|
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 =
|
|
109
|
-
const errLog =
|
|
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
|
-
|
|
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 (!
|
|
152
|
-
const raw =
|
|
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
|
|
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 (!
|
|
376
|
+
if (!existsSync3(configPath)) return [];
|
|
235
377
|
let raw;
|
|
236
378
|
try {
|
|
237
|
-
raw = JSON.parse(
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 =
|
|
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:
|
|
388
|
-
if (!
|
|
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
|
-
${
|
|
543
|
+
${join4(DOCTOR_LOG_DIR, latest)}
|
|
400
544
|
`));
|
|
401
|
-
const content =
|
|
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
|
-
|
|
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
|
|
460
|
-
import { join as
|
|
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("~/") ?
|
|
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 =
|
|
474
|
-
const exists =
|
|
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.
|
|
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(
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "txclaw",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "AI
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "OpenClaw CLI for Tencent Cloud AI users — gateway watchdog for Hunyuan. Alias of openclaw-cli.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"txclaw": "./dist/index.js"
|
|
@@ -15,11 +15,10 @@
|
|
|
15
15
|
"keywords": [
|
|
16
16
|
"openclaw",
|
|
17
17
|
"openclaw-cli",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"health-check"
|
|
18
|
+
"tencent",
|
|
19
|
+
"tencentcloud",
|
|
20
|
+
"hunyuan",
|
|
21
|
+
"ai-gateway"
|
|
23
22
|
],
|
|
24
23
|
"license": "MIT",
|
|
25
24
|
"repository": {
|