openclaw-service 0.6.2 → 0.7.2
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.
- package/dist/index.js +684 -67
- package/dist/index.pkg.cjs +709 -92
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -149,7 +149,7 @@ var init_logger = __esm({
|
|
|
149
149
|
// src/core/openclaw.ts
|
|
150
150
|
import { readFileSync as readFileSync2, existsSync as existsSync3, readdirSync } from "fs";
|
|
151
151
|
import { join as join4 } from "path";
|
|
152
|
-
import { homedir as homedir2 } from "os";
|
|
152
|
+
import { homedir as homedir2, platform } from "os";
|
|
153
153
|
import { execSync } from "child_process";
|
|
154
154
|
import { exec } from "child_process";
|
|
155
155
|
import { promisify } from "util";
|
|
@@ -160,21 +160,23 @@ function getOpenClawHome(profile) {
|
|
|
160
160
|
return join4(homedir2(), ".openclaw");
|
|
161
161
|
}
|
|
162
162
|
function findOpenClawBin() {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
for (const plist of plists) {
|
|
169
|
-
const content = readFileSync2(join4(plistDir, plist), "utf-8");
|
|
170
|
-
const nodeMatch = content.match(
|
|
171
|
-
/<string>(\/[^<]*\/bin\/node)<\/string>/
|
|
163
|
+
if (PLATFORM === "darwin") {
|
|
164
|
+
const plistDir = join4(homedir2(), "Library", "LaunchAgents");
|
|
165
|
+
if (existsSync3(plistDir)) {
|
|
166
|
+
const plists = readdirSync(plistDir).filter(
|
|
167
|
+
(f) => f.includes("openclaw") && f.endsWith(".plist")
|
|
172
168
|
);
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
169
|
+
for (const plist of plists) {
|
|
170
|
+
const content = readFileSync2(join4(plistDir, plist), "utf-8");
|
|
171
|
+
const nodeMatch = content.match(
|
|
172
|
+
/<string>(\/[^<]*\/bin\/node)<\/string>/
|
|
173
|
+
);
|
|
174
|
+
const cliMatch = content.match(
|
|
175
|
+
/<string>(\/[^<]*openclaw[^<]*\.(?:js|mjs))<\/string>/
|
|
176
|
+
);
|
|
177
|
+
if (nodeMatch && cliMatch) {
|
|
178
|
+
return { nodePath: nodeMatch[1], cliBinPath: cliMatch[1] };
|
|
179
|
+
}
|
|
178
180
|
}
|
|
179
181
|
}
|
|
180
182
|
}
|
|
@@ -186,6 +188,7 @@ function findOpenClawBin() {
|
|
|
186
188
|
return null;
|
|
187
189
|
}
|
|
188
190
|
function findLaunchdLabel() {
|
|
191
|
+
if (PLATFORM !== "darwin") return "ai.openclaw.gateway";
|
|
189
192
|
const plistDir = join4(homedir2(), "Library", "LaunchAgents");
|
|
190
193
|
if (!existsSync3(plistDir)) return "ai.openclaw.gateway";
|
|
191
194
|
const plists = readdirSync(plistDir).filter(
|
|
@@ -196,6 +199,18 @@ function findLaunchdLabel() {
|
|
|
196
199
|
}
|
|
197
200
|
return "ai.openclaw.gateway";
|
|
198
201
|
}
|
|
202
|
+
function hasSystemdService() {
|
|
203
|
+
try {
|
|
204
|
+
const { status } = __require("child_process").spawnSync(
|
|
205
|
+
"systemctl",
|
|
206
|
+
["--user", "status", "openclaw"],
|
|
207
|
+
{ stdio: "ignore" }
|
|
208
|
+
);
|
|
209
|
+
return status === 0 || status === 3;
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
199
214
|
function detectOpenClaw(profile = "default") {
|
|
200
215
|
const home = getOpenClawHome(profile);
|
|
201
216
|
const configPath = join4(home, "openclaw.json");
|
|
@@ -291,23 +306,51 @@ async function getGatewayHealth(info) {
|
|
|
291
306
|
}
|
|
292
307
|
}
|
|
293
308
|
function getRestartCommand(info) {
|
|
309
|
+
if (PLATFORM === "win32") {
|
|
310
|
+
throw new Error("Windows native is not supported. Please use WSL 2.");
|
|
311
|
+
}
|
|
312
|
+
if (PLATFORM === "linux") {
|
|
313
|
+
if (hasSystemdService()) {
|
|
314
|
+
return "systemctl --user restart openclaw";
|
|
315
|
+
}
|
|
316
|
+
return `(pgrep -f "openclaw.*gateway" | xargs kill 2>/dev/null || true) && nohup openclaw gateway > /dev/null 2>&1 &`;
|
|
317
|
+
}
|
|
294
318
|
const uid = process.getuid?.() ?? 501;
|
|
295
319
|
return `launchctl kickstart -k gui/${uid}/${info.launchdLabel}`;
|
|
296
320
|
}
|
|
297
321
|
function getStopCommand(info) {
|
|
322
|
+
if (PLATFORM === "win32") {
|
|
323
|
+
throw new Error("Windows native is not supported. Please use WSL 2.");
|
|
324
|
+
}
|
|
325
|
+
if (PLATFORM === "linux") {
|
|
326
|
+
if (hasSystemdService()) {
|
|
327
|
+
return "systemctl --user stop openclaw";
|
|
328
|
+
}
|
|
329
|
+
return `pgrep -f "openclaw.*gateway" | xargs kill 2>/dev/null || true`;
|
|
330
|
+
}
|
|
298
331
|
const uid = process.getuid?.() ?? 501;
|
|
299
332
|
return `launchctl bootout gui/${uid}/${info.launchdLabel} 2>/dev/null || launchctl kill SIGTERM gui/${uid}/${info.launchdLabel} 2>/dev/null || true`;
|
|
300
333
|
}
|
|
301
334
|
function getStartCommand(info) {
|
|
335
|
+
if (PLATFORM === "win32") {
|
|
336
|
+
throw new Error("Windows native is not supported. Please use WSL 2.");
|
|
337
|
+
}
|
|
338
|
+
if (PLATFORM === "linux") {
|
|
339
|
+
if (hasSystemdService()) {
|
|
340
|
+
return "systemctl --user start openclaw";
|
|
341
|
+
}
|
|
342
|
+
return `nohup openclaw gateway > /dev/null 2>&1 &`;
|
|
343
|
+
}
|
|
302
344
|
const uid = process.getuid?.() ?? 501;
|
|
303
345
|
const plistDir = `${process.env.HOME}/Library/LaunchAgents`;
|
|
304
346
|
return `(launchctl bootstrap gui/${uid} ${plistDir}/${info.launchdLabel}.plist 2>/dev/null || true) && launchctl kickstart gui/${uid}/${info.launchdLabel}`;
|
|
305
347
|
}
|
|
306
|
-
var execAsync;
|
|
348
|
+
var execAsync, PLATFORM;
|
|
307
349
|
var init_openclaw = __esm({
|
|
308
350
|
"src/core/openclaw.ts"() {
|
|
309
351
|
"use strict";
|
|
310
352
|
execAsync = promisify(exec);
|
|
353
|
+
PLATFORM = platform();
|
|
311
354
|
}
|
|
312
355
|
});
|
|
313
356
|
|
|
@@ -1339,7 +1382,7 @@ var init_server = __esm({
|
|
|
1339
1382
|
init_process_manager();
|
|
1340
1383
|
init_workspace_scanner();
|
|
1341
1384
|
init_cost_scanner();
|
|
1342
|
-
_PKG_VER = true ? "0.
|
|
1385
|
+
_PKG_VER = true ? "0.7.2" : "0.2.1";
|
|
1343
1386
|
pkgVersion = _PKG_VER;
|
|
1344
1387
|
}
|
|
1345
1388
|
});
|
|
@@ -1370,6 +1413,11 @@ var MEASUREMENT_ID = "G-B46J8RT804";
|
|
|
1370
1413
|
var API_SECRET = "qkqms1nURj2S02Q3WqO7GQ";
|
|
1371
1414
|
var ENDPOINT = `https://www.google-analytics.com/mp/collect?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`;
|
|
1372
1415
|
var TELEMETRY_FILE = join5(APP_HOME, "telemetry.json");
|
|
1416
|
+
var SESSION_ID = randomUUID();
|
|
1417
|
+
var _version = true ? "0.7.2" : "0.0.0";
|
|
1418
|
+
function getVersion() {
|
|
1419
|
+
return _version;
|
|
1420
|
+
}
|
|
1373
1421
|
function loadState() {
|
|
1374
1422
|
if (existsSync4(TELEMETRY_FILE)) {
|
|
1375
1423
|
try {
|
|
@@ -1436,6 +1484,11 @@ function printFirstRunNotice() {
|
|
|
1436
1484
|
process.stderr.write(
|
|
1437
1485
|
"\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"
|
|
1438
1486
|
);
|
|
1487
|
+
track("app_first_run", {
|
|
1488
|
+
platform: "cli",
|
|
1489
|
+
version: getVersion(),
|
|
1490
|
+
os: process.platform
|
|
1491
|
+
});
|
|
1439
1492
|
}
|
|
1440
1493
|
async function track(eventName, opts) {
|
|
1441
1494
|
if (isOptedOut()) return;
|
|
@@ -1448,14 +1501,18 @@ async function track(eventName, opts) {
|
|
|
1448
1501
|
const params = {
|
|
1449
1502
|
platform: opts.platform,
|
|
1450
1503
|
engagement_time_msec: 1,
|
|
1504
|
+
session_id: SESSION_ID,
|
|
1505
|
+
app_version: opts.version ?? _version,
|
|
1506
|
+
node_version: process.version,
|
|
1507
|
+
os_type: opts.os ?? process.platform,
|
|
1508
|
+
arch: process.arch,
|
|
1451
1509
|
...opts.command !== void 0 && { command: opts.command },
|
|
1452
1510
|
...opts.success !== void 0 && { success: opts.success ? 1 : 0 },
|
|
1453
|
-
...opts.version !== void 0 && { app_version: opts.version },
|
|
1454
|
-
...opts.os !== void 0 && { os_type: opts.os },
|
|
1455
1511
|
...opts.extra
|
|
1456
1512
|
};
|
|
1457
1513
|
const payload = {
|
|
1458
1514
|
client_id: state.client_id,
|
|
1515
|
+
user_id: state.client_id,
|
|
1459
1516
|
non_personalized_ads: true,
|
|
1460
1517
|
events: [
|
|
1461
1518
|
{
|
|
@@ -1866,22 +1923,22 @@ function showLogs(options) {
|
|
|
1866
1923
|
console.log();
|
|
1867
1924
|
}
|
|
1868
1925
|
function showDoctorLogs(maxLines) {
|
|
1869
|
-
const { readdirSync:
|
|
1870
|
-
const { join:
|
|
1926
|
+
const { readdirSync: readdirSync7 } = __require("fs");
|
|
1927
|
+
const { join: join14 } = __require("path");
|
|
1871
1928
|
if (!existsSync10(DOCTOR_LOG_DIR)) {
|
|
1872
1929
|
console.log(chalk6.yellow("No doctor logs found."));
|
|
1873
1930
|
return;
|
|
1874
1931
|
}
|
|
1875
|
-
const files =
|
|
1932
|
+
const files = readdirSync7(DOCTOR_LOG_DIR).filter((f) => f.endsWith(".log")).sort().reverse();
|
|
1876
1933
|
if (files.length === 0) {
|
|
1877
1934
|
console.log(chalk6.yellow("No doctor log files found."));
|
|
1878
1935
|
return;
|
|
1879
1936
|
}
|
|
1880
1937
|
const latest = files[0];
|
|
1881
1938
|
console.log(chalk6.blue.bold(`
|
|
1882
|
-
${
|
|
1939
|
+
${join14(DOCTOR_LOG_DIR, latest)}
|
|
1883
1940
|
`));
|
|
1884
|
-
const content = readFileSync8(
|
|
1941
|
+
const content = readFileSync8(join14(DOCTOR_LOG_DIR, latest), "utf-8");
|
|
1885
1942
|
const lines = content.trim().split("\n");
|
|
1886
1943
|
const tail = lines.slice(-maxLines);
|
|
1887
1944
|
for (const line of tail) {
|
|
@@ -1906,8 +1963,15 @@ init_process_manager();
|
|
|
1906
1963
|
init_logger();
|
|
1907
1964
|
import chalk7 from "chalk";
|
|
1908
1965
|
import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync2 } from "fs";
|
|
1909
|
-
|
|
1966
|
+
import { platform as platform2 } from "os";
|
|
1967
|
+
function showPlatformNote() {
|
|
1968
|
+
if (platform2() === "linux") {
|
|
1969
|
+
console.log(chalk7.cyan("Running on Linux/WSL. Using systemd or direct process management (launchctl not available)."));
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
var _VER = true ? "0.7.2" : void 0;
|
|
1910
1973
|
async function gatewayStart(options) {
|
|
1974
|
+
showPlatformNote();
|
|
1911
1975
|
const config = loadConfig(options.config);
|
|
1912
1976
|
initLogger();
|
|
1913
1977
|
ensureDoctorHome();
|
|
@@ -1929,6 +1993,7 @@ async function gatewayStart(options) {
|
|
|
1929
1993
|
}
|
|
1930
1994
|
}
|
|
1931
1995
|
async function gatewayStop(options) {
|
|
1996
|
+
showPlatformNote();
|
|
1932
1997
|
const config = loadConfig(options.config);
|
|
1933
1998
|
initLogger();
|
|
1934
1999
|
ensureDoctorHome();
|
|
@@ -1948,6 +2013,7 @@ async function gatewayStop(options) {
|
|
|
1948
2013
|
}
|
|
1949
2014
|
}
|
|
1950
2015
|
async function gatewayRestart(options) {
|
|
2016
|
+
showPlatformNote();
|
|
1951
2017
|
const config = loadConfig(options.config);
|
|
1952
2018
|
initLogger();
|
|
1953
2019
|
ensureDoctorHome();
|
|
@@ -1973,12 +2039,25 @@ async function gatewayRestart(options) {
|
|
|
1973
2039
|
init_config();
|
|
1974
2040
|
init_openclaw();
|
|
1975
2041
|
import chalk8 from "chalk";
|
|
1976
|
-
import { existsSync as existsSync12, statSync as statSync3 } from "fs";
|
|
1977
|
-
import { join as join10 } from "path";
|
|
2042
|
+
import { existsSync as existsSync12, statSync as statSync3, readFileSync as readFileSync9, readdirSync as readdirSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2043
|
+
import { join as join10, basename as basename2 } from "path";
|
|
1978
2044
|
import { homedir as homedir5 } from "os";
|
|
2045
|
+
import { createInterface } from "readline";
|
|
1979
2046
|
function expandHome2(p) {
|
|
1980
2047
|
return p.startsWith("~/") ? join10(homedir5(), p.slice(2)) : p;
|
|
1981
2048
|
}
|
|
2049
|
+
function getAgentMemoryPaths(agents) {
|
|
2050
|
+
const paths = [];
|
|
2051
|
+
for (const agent of agents) {
|
|
2052
|
+
const ws = agent.workspace;
|
|
2053
|
+
if (!ws) continue;
|
|
2054
|
+
const wsPath = expandHome2(ws);
|
|
2055
|
+
if (existsSync12(wsPath)) {
|
|
2056
|
+
paths.push({ agent: agent.name, dir: wsPath });
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
return paths;
|
|
2060
|
+
}
|
|
1982
2061
|
async function memoryStatus(options) {
|
|
1983
2062
|
const config = loadConfig(options.config);
|
|
1984
2063
|
const info = detectOpenClaw(options.profile ?? config.openclawProfile);
|
|
@@ -2003,11 +2082,54 @@ async function memorySearch(query, options) {
|
|
|
2003
2082
|
console.log(chalk8.bold(`
|
|
2004
2083
|
Searching memory: "${query}"
|
|
2005
2084
|
`));
|
|
2006
|
-
const
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2085
|
+
const agentPaths = getAgentMemoryPaths(info.agents);
|
|
2086
|
+
let found = false;
|
|
2087
|
+
for (const { agent, dir } of agentPaths) {
|
|
2088
|
+
const filesToSearch = [];
|
|
2089
|
+
const memPath = join10(dir, "MEMORY.md");
|
|
2090
|
+
if (existsSync12(memPath)) filesToSearch.push(memPath);
|
|
2091
|
+
const memDir = join10(dir, "memory");
|
|
2092
|
+
if (existsSync12(memDir)) {
|
|
2093
|
+
try {
|
|
2094
|
+
const files = readdirSync5(memDir).filter((f) => f.endsWith(".md"));
|
|
2095
|
+
for (const f of files) {
|
|
2096
|
+
filesToSearch.push(join10(memDir, f));
|
|
2097
|
+
}
|
|
2098
|
+
} catch {
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
for (const filePath of filesToSearch) {
|
|
2102
|
+
try {
|
|
2103
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
2104
|
+
const lines = content.split("\n");
|
|
2105
|
+
const lowerQuery = query.toLowerCase();
|
|
2106
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2107
|
+
if (lines[i].toLowerCase().includes(lowerQuery)) {
|
|
2108
|
+
if (!found) found = true;
|
|
2109
|
+
const relPath = basename2(filePath);
|
|
2110
|
+
const contextStart = Math.max(0, i - 1);
|
|
2111
|
+
const contextEnd = Math.min(lines.length, i + 2);
|
|
2112
|
+
const context = lines.slice(contextStart, contextEnd).map((l, idx) => {
|
|
2113
|
+
const lineNum = contextStart + idx + 1;
|
|
2114
|
+
const prefix = lineNum === i + 1 ? chalk8.yellow("\u2192") : " ";
|
|
2115
|
+
return ` ${prefix} ${chalk8.gray(`${lineNum}:`)} ${l}`;
|
|
2116
|
+
}).join("\n");
|
|
2117
|
+
console.log(` ${chalk8.cyan(agent)} ${chalk8.gray("/")} ${chalk8.white(relPath)}${chalk8.gray(`:${i + 1}`)}`);
|
|
2118
|
+
console.log(context);
|
|
2119
|
+
console.log();
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
} catch {
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
if (!found) {
|
|
2127
|
+
const output = await runOpenClawCmd(info, `memory search "${query}"`);
|
|
2128
|
+
if (output) {
|
|
2129
|
+
console.log(output);
|
|
2130
|
+
} else {
|
|
2131
|
+
console.log(chalk8.yellow(" No results found."));
|
|
2132
|
+
}
|
|
2011
2133
|
}
|
|
2012
2134
|
console.log();
|
|
2013
2135
|
}
|
|
@@ -2026,36 +2148,512 @@ async function memoryCompact(options) {
|
|
|
2026
2148
|
}
|
|
2027
2149
|
console.log();
|
|
2028
2150
|
}
|
|
2151
|
+
async function memoryGc(options) {
|
|
2152
|
+
const config = loadConfig(options.config);
|
|
2153
|
+
const info = detectOpenClaw(options.profile ?? config.openclawProfile);
|
|
2154
|
+
const maxAgeDays = parseInt(options.days ?? "30", 10);
|
|
2155
|
+
const cutoff = Date.now() - maxAgeDays * 24 * 3600 * 1e3;
|
|
2156
|
+
console.log(chalk8.bold(`
|
|
2157
|
+
Memory GC \u2014 removing daily files older than ${maxAgeDays} days
|
|
2158
|
+
`));
|
|
2159
|
+
const agentPaths = getAgentMemoryPaths(info.agents);
|
|
2160
|
+
const filesToDelete = [];
|
|
2161
|
+
const dailyPattern = /^\d{4}-\d{2}-\d{2}\.md$/;
|
|
2162
|
+
for (const { agent, dir } of agentPaths) {
|
|
2163
|
+
const memDir = join10(dir, "memory");
|
|
2164
|
+
if (!existsSync12(memDir)) continue;
|
|
2165
|
+
try {
|
|
2166
|
+
const files = readdirSync5(memDir).filter((f) => dailyPattern.test(f));
|
|
2167
|
+
for (const f of files) {
|
|
2168
|
+
const fpath = join10(memDir, f);
|
|
2169
|
+
try {
|
|
2170
|
+
const stat = statSync3(fpath);
|
|
2171
|
+
if (stat.mtimeMs < cutoff) {
|
|
2172
|
+
filesToDelete.push({ path: fpath, agent, name: f, size: stat.size });
|
|
2173
|
+
}
|
|
2174
|
+
} catch {
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
} catch {
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
if (filesToDelete.length === 0) {
|
|
2181
|
+
console.log(chalk8.green(" No old daily memory files found. Nothing to clean up.\n"));
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
2184
|
+
console.log(` Found ${chalk8.yellow(String(filesToDelete.length))} files to remove:
|
|
2185
|
+
`);
|
|
2186
|
+
for (const f of filesToDelete) {
|
|
2187
|
+
const sizeKB = Math.round(f.size / 1024);
|
|
2188
|
+
console.log(` ${chalk8.gray("\u2022")} ${f.agent} / ${f.name} ${chalk8.gray(`(${sizeKB}KB)`)}`);
|
|
2189
|
+
}
|
|
2190
|
+
console.log();
|
|
2191
|
+
const totalKB = Math.round(filesToDelete.reduce((s, f) => s + f.size, 0) / 1024);
|
|
2192
|
+
console.log(` Total: ${chalk8.yellow(`${totalKB}KB`)} across ${filesToDelete.length} files
|
|
2193
|
+
`);
|
|
2194
|
+
if (!options.force) {
|
|
2195
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
2196
|
+
const answer = await new Promise((resolve3) => {
|
|
2197
|
+
rl.question(" Delete these files? [y/N] ", resolve3);
|
|
2198
|
+
});
|
|
2199
|
+
rl.close();
|
|
2200
|
+
if (answer.toLowerCase() !== "y") {
|
|
2201
|
+
console.log(chalk8.gray(" Cancelled.\n"));
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
let deleted = 0;
|
|
2206
|
+
for (const f of filesToDelete) {
|
|
2207
|
+
try {
|
|
2208
|
+
unlinkSync3(f.path);
|
|
2209
|
+
deleted++;
|
|
2210
|
+
} catch (err) {
|
|
2211
|
+
console.log(chalk8.red(` Failed to delete ${f.name}: ${err}`));
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
console.log(chalk8.green(`
|
|
2215
|
+
Deleted ${deleted} files.
|
|
2216
|
+
`));
|
|
2217
|
+
}
|
|
2218
|
+
async function memoryExport(options) {
|
|
2219
|
+
const config = loadConfig(options.config);
|
|
2220
|
+
const info = detectOpenClaw(options.profile ?? config.openclawProfile);
|
|
2221
|
+
const agentPaths = getAgentMemoryPaths(info.agents);
|
|
2222
|
+
const parts = [];
|
|
2223
|
+
parts.push(`# OpenClaw Memory Export`);
|
|
2224
|
+
parts.push(`# Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
2225
|
+
`);
|
|
2226
|
+
for (const { agent, dir } of agentPaths) {
|
|
2227
|
+
const agentParts = [];
|
|
2228
|
+
const memPath = join10(dir, "MEMORY.md");
|
|
2229
|
+
if (existsSync12(memPath)) {
|
|
2230
|
+
try {
|
|
2231
|
+
const content = readFileSync9(memPath, "utf-8");
|
|
2232
|
+
agentParts.push(`### MEMORY.md
|
|
2233
|
+
|
|
2234
|
+
${content}`);
|
|
2235
|
+
} catch {
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
const memDir = join10(dir, "memory");
|
|
2239
|
+
if (existsSync12(memDir)) {
|
|
2240
|
+
try {
|
|
2241
|
+
const files = readdirSync5(memDir).filter((f) => f.endsWith(".md")).sort();
|
|
2242
|
+
for (const f of files) {
|
|
2243
|
+
try {
|
|
2244
|
+
const content = readFileSync9(join10(memDir, f), "utf-8");
|
|
2245
|
+
agentParts.push(`### memory/${f}
|
|
2246
|
+
|
|
2247
|
+
${content}`);
|
|
2248
|
+
} catch {
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
} catch {
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
if (agentParts.length > 0) {
|
|
2255
|
+
parts.push(`## Agent: ${agent}
|
|
2256
|
+
`);
|
|
2257
|
+
parts.push(agentParts.join("\n---\n\n"));
|
|
2258
|
+
parts.push("");
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
if (parts.length <= 2) {
|
|
2262
|
+
console.log(chalk8.yellow("\n No memory files found to export.\n"));
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
const result = parts.join("\n");
|
|
2266
|
+
if (options.output) {
|
|
2267
|
+
writeFileSync6(options.output, result, "utf-8");
|
|
2268
|
+
console.log(chalk8.green(`
|
|
2269
|
+
Exported memory to ${options.output}
|
|
2270
|
+
`));
|
|
2271
|
+
} else {
|
|
2272
|
+
console.log(result);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
// src/commands/cost.ts
|
|
2277
|
+
init_config();
|
|
2278
|
+
init_openclaw();
|
|
2279
|
+
import chalk9 from "chalk";
|
|
2280
|
+
import { existsSync as existsSync13, readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
|
|
2281
|
+
import { join as join11 } from "path";
|
|
2282
|
+
import { homedir as homedir6 } from "os";
|
|
2283
|
+
var RATES = {
|
|
2284
|
+
"claude-opus-4": { input: 15, output: 75 },
|
|
2285
|
+
"claude-sonnet-4": { input: 3, output: 15 },
|
|
2286
|
+
"claude-haiku-3-5": { input: 0.8, output: 4 },
|
|
2287
|
+
"gpt-4o": { input: 2.5, output: 10 },
|
|
2288
|
+
"gpt-4o-mini": { input: 0.15, output: 0.6 }
|
|
2289
|
+
};
|
|
2290
|
+
var DEFAULT_RATE = { input: 3, output: 15 };
|
|
2291
|
+
function getRate(model) {
|
|
2292
|
+
if (RATES[model]) return RATES[model];
|
|
2293
|
+
for (const [key, rate] of Object.entries(RATES)) {
|
|
2294
|
+
if (model.startsWith(key)) return rate;
|
|
2295
|
+
}
|
|
2296
|
+
return DEFAULT_RATE;
|
|
2297
|
+
}
|
|
2298
|
+
function calcCost(model, inputTokens, outputTokens) {
|
|
2299
|
+
const rate = getRate(model);
|
|
2300
|
+
return (inputTokens * rate.input + outputTokens * rate.output) / 1e6;
|
|
2301
|
+
}
|
|
2302
|
+
function expandHome3(p) {
|
|
2303
|
+
return p.startsWith("~/") ? join11(homedir6(), p.slice(2)) : p;
|
|
2304
|
+
}
|
|
2305
|
+
function scanSessionFiles(agents, sinceDays) {
|
|
2306
|
+
const entries = [];
|
|
2307
|
+
const cutoff = Date.now() - sinceDays * 24 * 3600 * 1e3;
|
|
2308
|
+
for (const agent of agents) {
|
|
2309
|
+
const searchPaths = [];
|
|
2310
|
+
searchPaths.push(join11(homedir6(), ".openclaw", "agents", agent.id, "sessions"));
|
|
2311
|
+
if (agent.workspace) {
|
|
2312
|
+
const ws = expandHome3(agent.workspace);
|
|
2313
|
+
searchPaths.push(join11(ws, "sessions"));
|
|
2314
|
+
searchPaths.push(join11(ws, "logs"));
|
|
2315
|
+
}
|
|
2316
|
+
for (const sessDir of searchPaths) {
|
|
2317
|
+
if (!existsSync13(sessDir)) continue;
|
|
2318
|
+
const files = readdirSync6(sessDir).filter((f) => f.endsWith(".jsonl") || f.endsWith(".json"));
|
|
2319
|
+
for (const file of files) {
|
|
2320
|
+
const fpath = join11(sessDir, file);
|
|
2321
|
+
try {
|
|
2322
|
+
const mtime = statSync4(fpath).mtimeMs;
|
|
2323
|
+
if (mtime < cutoff) continue;
|
|
2324
|
+
} catch {
|
|
2325
|
+
continue;
|
|
2326
|
+
}
|
|
2327
|
+
try {
|
|
2328
|
+
const lines = readFileSync10(fpath, "utf-8").split("\n").filter(Boolean);
|
|
2329
|
+
for (const line of lines) {
|
|
2330
|
+
try {
|
|
2331
|
+
const msg = JSON.parse(line);
|
|
2332
|
+
if (msg.type !== "message" || !msg.message?.usage) continue;
|
|
2333
|
+
const usage = msg.message.usage;
|
|
2334
|
+
const ts = msg.timestamp ? new Date(msg.timestamp).getTime() : msg.message?.timestamp ?? 0;
|
|
2335
|
+
if (ts < cutoff) continue;
|
|
2336
|
+
const model = msg.message?.model ?? msg.model ?? "unknown";
|
|
2337
|
+
const inputTokens = usage.input_tokens ?? usage.inputTokens ?? 0;
|
|
2338
|
+
const outputTokens = usage.output_tokens ?? usage.outputTokens ?? 0;
|
|
2339
|
+
if (inputTokens === 0 && outputTokens === 0) continue;
|
|
2340
|
+
entries.push({
|
|
2341
|
+
model,
|
|
2342
|
+
inputTokens,
|
|
2343
|
+
outputTokens,
|
|
2344
|
+
timestamp: ts,
|
|
2345
|
+
agentName: agent.name,
|
|
2346
|
+
agentId: agent.id
|
|
2347
|
+
});
|
|
2348
|
+
} catch {
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
} catch {
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
const globalLogDir = join11(homedir6(), ".openclaw", "logs");
|
|
2357
|
+
if (existsSync13(globalLogDir)) {
|
|
2358
|
+
const files = readdirSync6(globalLogDir).filter((f) => f.endsWith(".jsonl"));
|
|
2359
|
+
for (const file of files) {
|
|
2360
|
+
const fpath = join11(globalLogDir, file);
|
|
2361
|
+
try {
|
|
2362
|
+
const mtime = statSync4(fpath).mtimeMs;
|
|
2363
|
+
if (mtime < cutoff) continue;
|
|
2364
|
+
} catch {
|
|
2365
|
+
continue;
|
|
2366
|
+
}
|
|
2367
|
+
try {
|
|
2368
|
+
const lines = readFileSync10(fpath, "utf-8").split("\n").filter(Boolean);
|
|
2369
|
+
for (const line of lines) {
|
|
2370
|
+
try {
|
|
2371
|
+
const msg = JSON.parse(line);
|
|
2372
|
+
if (msg.type !== "message" || !msg.message?.usage) continue;
|
|
2373
|
+
const usage = msg.message.usage;
|
|
2374
|
+
const ts = msg.timestamp ? new Date(msg.timestamp).getTime() : msg.message?.timestamp ?? 0;
|
|
2375
|
+
if (ts < cutoff) continue;
|
|
2376
|
+
const model = msg.message?.model ?? msg.model ?? "unknown";
|
|
2377
|
+
const inputTokens = usage.input_tokens ?? usage.inputTokens ?? 0;
|
|
2378
|
+
const outputTokens = usage.output_tokens ?? usage.outputTokens ?? 0;
|
|
2379
|
+
if (inputTokens === 0 && outputTokens === 0) continue;
|
|
2380
|
+
const agentId = msg.agentId ?? msg.message?.agentId ?? "unknown";
|
|
2381
|
+
const agentName = msg.agentName ?? msg.message?.agentName ?? agentId;
|
|
2382
|
+
entries.push({ model, inputTokens, outputTokens, timestamp: ts, agentName, agentId });
|
|
2383
|
+
} catch {
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
} catch {
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
return entries;
|
|
2391
|
+
}
|
|
2392
|
+
function formatCost(cost) {
|
|
2393
|
+
if (cost < 0.01) return `$${cost.toFixed(4)}`;
|
|
2394
|
+
return `$${cost.toFixed(2)}`;
|
|
2395
|
+
}
|
|
2396
|
+
function formatTokens(n) {
|
|
2397
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
2398
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
2399
|
+
return String(n);
|
|
2400
|
+
}
|
|
2401
|
+
async function showCost(options) {
|
|
2402
|
+
const config = loadConfig(options.config);
|
|
2403
|
+
const info = detectOpenClaw(options.profile ?? config.openclawProfile);
|
|
2404
|
+
const days = parseInt(options.days ?? "30", 10);
|
|
2405
|
+
if (info.agents.length === 0) {
|
|
2406
|
+
console.log(chalk9.yellow("\n No agents found in openclaw config."));
|
|
2407
|
+
console.log(chalk9.gray(" Make sure openclaw is installed and configured.\n"));
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
const entries = scanSessionFiles(
|
|
2411
|
+
info.agents.map((a) => ({ id: a.id, name: a.name, workspace: a.workspace })),
|
|
2412
|
+
days
|
|
2413
|
+
);
|
|
2414
|
+
if (entries.length === 0) {
|
|
2415
|
+
console.log(chalk9.yellow("\n No session logs found."));
|
|
2416
|
+
console.log(chalk9.gray(" OpenClaw stores session logs in:"));
|
|
2417
|
+
console.log(chalk9.gray(` ~/.openclaw/agents/<agent-id>/sessions/`));
|
|
2418
|
+
console.log(chalk9.gray(` ~/.openclaw/logs/
|
|
2419
|
+
`));
|
|
2420
|
+
return;
|
|
2421
|
+
}
|
|
2422
|
+
const now = Date.now();
|
|
2423
|
+
const todayStart = /* @__PURE__ */ new Date();
|
|
2424
|
+
todayStart.setHours(0, 0, 0, 0);
|
|
2425
|
+
const weekStart = now - 7 * 24 * 3600 * 1e3;
|
|
2426
|
+
const monthStart = now - 30 * 24 * 3600 * 1e3;
|
|
2427
|
+
const byAgent = {};
|
|
2428
|
+
const byModel = {};
|
|
2429
|
+
for (const e of entries) {
|
|
2430
|
+
const cost = calcCost(e.model, e.inputTokens, e.outputTokens);
|
|
2431
|
+
if (!byAgent[e.agentName]) byAgent[e.agentName] = { today: 0, week: 0, month: 0, tokens: 0 };
|
|
2432
|
+
const ag = byAgent[e.agentName];
|
|
2433
|
+
ag.tokens += e.inputTokens + e.outputTokens;
|
|
2434
|
+
if (e.timestamp >= monthStart) ag.month += cost;
|
|
2435
|
+
if (e.timestamp >= weekStart) ag.week += cost;
|
|
2436
|
+
if (e.timestamp >= todayStart.getTime()) ag.today += cost;
|
|
2437
|
+
if (!byModel[e.model]) byModel[e.model] = { today: 0, week: 0, month: 0, input: 0, output: 0 };
|
|
2438
|
+
const md = byModel[e.model];
|
|
2439
|
+
md.input += e.inputTokens;
|
|
2440
|
+
md.output += e.outputTokens;
|
|
2441
|
+
if (e.timestamp >= monthStart) md.month += cost;
|
|
2442
|
+
if (e.timestamp >= weekStart) md.week += cost;
|
|
2443
|
+
if (e.timestamp >= todayStart.getTime()) md.today += cost;
|
|
2444
|
+
}
|
|
2445
|
+
const totalToday = Object.values(byAgent).reduce((s, a) => s + a.today, 0);
|
|
2446
|
+
const totalWeek = Object.values(byAgent).reduce((s, a) => s + a.week, 0);
|
|
2447
|
+
const totalMonth = Object.values(byAgent).reduce((s, a) => s + a.month, 0);
|
|
2448
|
+
if (options.json) {
|
|
2449
|
+
console.log(JSON.stringify({
|
|
2450
|
+
period: { days },
|
|
2451
|
+
summary: { today: totalToday, week: totalWeek, month: totalMonth, currency: "USD" },
|
|
2452
|
+
byAgent: Object.entries(byAgent).map(([name, v]) => ({ name, ...v })),
|
|
2453
|
+
byModel: Object.entries(byModel).map(([model, v]) => ({ model, ...v })),
|
|
2454
|
+
entries: entries.length
|
|
2455
|
+
}, null, 2));
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
console.log(chalk9.bold("\n Token Cost Summary\n"));
|
|
2459
|
+
console.log(` ${"Period".padEnd(14)} ${"Cost".padStart(10)}`);
|
|
2460
|
+
console.log(` ${"\u2500".repeat(14)} ${"\u2500".repeat(10)}`);
|
|
2461
|
+
console.log(` ${"Today".padEnd(14)} ${chalk9.green(formatCost(totalToday).padStart(10))}`);
|
|
2462
|
+
console.log(` ${"This week".padEnd(14)} ${chalk9.cyan(formatCost(totalWeek).padStart(10))}`);
|
|
2463
|
+
console.log(` ${"Last 30 days".padEnd(14)} ${chalk9.yellow(formatCost(totalMonth).padStart(10))}`);
|
|
2464
|
+
const agentEntries = Object.entries(byAgent).sort((a, b) => b[1].month - a[1].month);
|
|
2465
|
+
if (agentEntries.length > 0) {
|
|
2466
|
+
console.log(chalk9.bold("\n By Agent\n"));
|
|
2467
|
+
console.log(` ${"Agent".padEnd(20)} ${"Today".padStart(10)} ${"Week".padStart(10)} ${"Month".padStart(10)} ${"Tokens".padStart(10)}`);
|
|
2468
|
+
console.log(` ${"\u2500".repeat(20)} ${"\u2500".repeat(10)} ${"\u2500".repeat(10)} ${"\u2500".repeat(10)} ${"\u2500".repeat(10)}`);
|
|
2469
|
+
for (const [name, v] of agentEntries) {
|
|
2470
|
+
console.log(` ${name.padEnd(20)} ${formatCost(v.today).padStart(10)} ${formatCost(v.week).padStart(10)} ${formatCost(v.month).padStart(10)} ${chalk9.gray(formatTokens(v.tokens).padStart(10))}`);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
const modelEntries = Object.entries(byModel).sort((a, b) => b[1].month - a[1].month);
|
|
2474
|
+
if (modelEntries.length > 0) {
|
|
2475
|
+
console.log(chalk9.bold("\n By Model\n"));
|
|
2476
|
+
console.log(` ${"Model".padEnd(24)} ${"Today".padStart(10)} ${"Week".padStart(10)} ${"Month".padStart(10)} ${"In Tokens".padStart(10)} ${"Out Tokens".padStart(10)}`);
|
|
2477
|
+
console.log(` ${"\u2500".repeat(24)} ${"\u2500".repeat(10)} ${"\u2500".repeat(10)} ${"\u2500".repeat(10)} ${"\u2500".repeat(10)} ${"\u2500".repeat(10)}`);
|
|
2478
|
+
for (const [model, v] of modelEntries) {
|
|
2479
|
+
console.log(` ${model.padEnd(24)} ${formatCost(v.today).padStart(10)} ${formatCost(v.week).padStart(10)} ${formatCost(v.month).padStart(10)} ${chalk9.gray(formatTokens(v.input).padStart(10))} ${chalk9.gray(formatTokens(v.output).padStart(10))}`);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
console.log();
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
// src/commands/mcp.ts
|
|
2486
|
+
import chalk10 from "chalk";
|
|
2487
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11 } from "fs";
|
|
2488
|
+
import { join as join12 } from "path";
|
|
2489
|
+
import { homedir as homedir7 } from "os";
|
|
2490
|
+
import { execSync as execSync3 } from "child_process";
|
|
2491
|
+
var CONFIG_LOCATIONS = [
|
|
2492
|
+
{ path: join12(homedir7(), ".config", "claude", "claude_desktop_config.json"), label: "Claude Desktop" },
|
|
2493
|
+
{ path: join12(homedir7(), "Library", "Application Support", "Claude", "claude_desktop_config.json"), label: "Claude Desktop (macOS)" },
|
|
2494
|
+
{ path: join12(homedir7(), ".config", "cursor", "mcp.json"), label: "Cursor" },
|
|
2495
|
+
{ path: join12(homedir7(), ".openclaw", "config.yaml"), label: "OpenClaw" },
|
|
2496
|
+
{ path: join12(homedir7(), ".openclaw", "mcp.json"), label: "OpenClaw MCP" }
|
|
2497
|
+
];
|
|
2498
|
+
function findMcpConfigs() {
|
|
2499
|
+
const servers = [];
|
|
2500
|
+
for (const loc of CONFIG_LOCATIONS) {
|
|
2501
|
+
if (!existsSync14(loc.path)) continue;
|
|
2502
|
+
try {
|
|
2503
|
+
const raw = readFileSync11(loc.path, "utf-8");
|
|
2504
|
+
if (loc.path.endsWith(".yaml") || loc.path.endsWith(".yml")) {
|
|
2505
|
+
const mcpMatch = raw.match(/mcp_servers:\s*\n([\s\S]*?)(?:\n\w|\n*$)/);
|
|
2506
|
+
if (mcpMatch) {
|
|
2507
|
+
const lines = mcpMatch[1].split("\n");
|
|
2508
|
+
for (const line of lines) {
|
|
2509
|
+
const nameMatch = line.match(/^\s+-\s+name:\s*(.+)/);
|
|
2510
|
+
if (nameMatch) {
|
|
2511
|
+
servers.push({
|
|
2512
|
+
name: nameMatch[1].trim(),
|
|
2513
|
+
type: "unknown",
|
|
2514
|
+
source: loc.label,
|
|
2515
|
+
status: "UNKNOWN"
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
continue;
|
|
2521
|
+
}
|
|
2522
|
+
const config = JSON.parse(raw);
|
|
2523
|
+
const mcpServers = config.mcpServers ?? config.mcp_servers ?? config.servers ?? {};
|
|
2524
|
+
for (const [name, def] of Object.entries(mcpServers)) {
|
|
2525
|
+
const d = def;
|
|
2526
|
+
const server = {
|
|
2527
|
+
name,
|
|
2528
|
+
type: d.command ? "stdio" : d.url ? "http" : "unknown",
|
|
2529
|
+
command: d.command,
|
|
2530
|
+
url: d.url,
|
|
2531
|
+
args: d.args,
|
|
2532
|
+
env: d.env,
|
|
2533
|
+
source: loc.label,
|
|
2534
|
+
status: "UNKNOWN"
|
|
2535
|
+
};
|
|
2536
|
+
if (server.type === "stdio" && server.command) {
|
|
2537
|
+
server.status = checkProcessRunning(server.command) ? "RUNNING" : "STOPPED";
|
|
2538
|
+
} else if (server.type === "http") {
|
|
2539
|
+
server.status = "UNKNOWN";
|
|
2540
|
+
}
|
|
2541
|
+
servers.push(server);
|
|
2542
|
+
}
|
|
2543
|
+
} catch {
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
return servers;
|
|
2547
|
+
}
|
|
2548
|
+
function checkProcessRunning(command) {
|
|
2549
|
+
try {
|
|
2550
|
+
const baseName = command.split("/").pop() ?? command;
|
|
2551
|
+
const result = execSync3(`pgrep -f "${baseName}"`, { encoding: "utf-8", timeout: 3e3 });
|
|
2552
|
+
return result.trim().length > 0;
|
|
2553
|
+
} catch {
|
|
2554
|
+
return false;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
function statusColor(status) {
|
|
2558
|
+
switch (status) {
|
|
2559
|
+
case "RUNNING":
|
|
2560
|
+
return chalk10.green(status);
|
|
2561
|
+
case "STOPPED":
|
|
2562
|
+
return chalk10.red(status);
|
|
2563
|
+
default:
|
|
2564
|
+
return chalk10.gray(status);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
async function mcpList() {
|
|
2568
|
+
const servers = findMcpConfigs();
|
|
2569
|
+
if (servers.length === 0) {
|
|
2570
|
+
console.log(chalk10.yellow("\n No MCP server configurations found."));
|
|
2571
|
+
console.log(chalk10.gray(" Checked:"));
|
|
2572
|
+
for (const loc of CONFIG_LOCATIONS) {
|
|
2573
|
+
console.log(chalk10.gray(` ${loc.path}`));
|
|
2574
|
+
}
|
|
2575
|
+
console.log(chalk10.gray("\n Configure MCP servers in Claude Desktop:"));
|
|
2576
|
+
console.log(chalk10.gray(` ${CONFIG_LOCATIONS[0].path}
|
|
2577
|
+
`));
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
console.log(chalk10.bold("\n MCP Servers\n"));
|
|
2581
|
+
console.log(` ${"Name".padEnd(24)} ${"Type".padEnd(8)} ${"Status".padEnd(12)} ${"Source".padEnd(20)} ${"Command/URL"}`);
|
|
2582
|
+
console.log(` ${"\u2500".repeat(24)} ${"\u2500".repeat(8)} ${"\u2500".repeat(12)} ${"\u2500".repeat(20)} ${"\u2500".repeat(30)}`);
|
|
2583
|
+
for (const s of servers) {
|
|
2584
|
+
const endpoint = s.command ? `${s.command}${s.args?.length ? " " + s.args.join(" ") : ""}` : s.url ?? "";
|
|
2585
|
+
const truncEndpoint = endpoint.length > 50 ? endpoint.slice(0, 47) + "..." : endpoint;
|
|
2586
|
+
console.log(
|
|
2587
|
+
` ${s.name.padEnd(24)} ${s.type.padEnd(8)} ${statusColor(s.status).padEnd(12 + 10)} ${chalk10.gray(s.source.padEnd(20))} ${chalk10.gray(truncEndpoint)}`
|
|
2588
|
+
);
|
|
2589
|
+
}
|
|
2590
|
+
console.log();
|
|
2591
|
+
}
|
|
2592
|
+
async function mcpStatus(name) {
|
|
2593
|
+
const servers = findMcpConfigs();
|
|
2594
|
+
const server = servers.find((s) => s.name === name);
|
|
2595
|
+
if (!server) {
|
|
2596
|
+
console.log(chalk10.red(`
|
|
2597
|
+
MCP server "${name}" not found.`));
|
|
2598
|
+
const available = servers.map((s) => s.name).join(", ");
|
|
2599
|
+
if (available) {
|
|
2600
|
+
console.log(chalk10.gray(` Available: ${available}
|
|
2601
|
+
`));
|
|
2602
|
+
} else {
|
|
2603
|
+
console.log(chalk10.gray(" No MCP servers configured.\n"));
|
|
2604
|
+
}
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2607
|
+
console.log(chalk10.bold(`
|
|
2608
|
+
MCP Server: ${server.name}
|
|
2609
|
+
`));
|
|
2610
|
+
console.log(` Status: ${statusColor(server.status)}`);
|
|
2611
|
+
console.log(` Type: ${server.type}`);
|
|
2612
|
+
console.log(` Source: ${chalk10.gray(server.source)}`);
|
|
2613
|
+
if (server.command) {
|
|
2614
|
+
console.log(` Command: ${server.command}`);
|
|
2615
|
+
if (server.args?.length) {
|
|
2616
|
+
console.log(` Args: ${server.args.join(" ")}`);
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
if (server.url) {
|
|
2620
|
+
console.log(` URL: ${server.url}`);
|
|
2621
|
+
}
|
|
2622
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
2623
|
+
console.log(` Env: ${Object.keys(server.env).join(", ")}`);
|
|
2624
|
+
}
|
|
2625
|
+
console.log();
|
|
2626
|
+
}
|
|
2029
2627
|
|
|
2030
2628
|
// src/index.ts
|
|
2031
2629
|
init_server();
|
|
2032
2630
|
init_openclaw();
|
|
2033
2631
|
|
|
2034
2632
|
// src/commands/telemetry.ts
|
|
2035
|
-
import
|
|
2633
|
+
import chalk11 from "chalk";
|
|
2036
2634
|
function telemetryOn() {
|
|
2037
2635
|
setOptOut(false);
|
|
2038
|
-
console.log(
|
|
2636
|
+
console.log(chalk11.green("\u2713 Telemetry enabled. Thanks for helping improve OpenClaw!"));
|
|
2039
2637
|
}
|
|
2040
2638
|
function telemetryOff() {
|
|
2041
2639
|
setOptOut(true);
|
|
2042
|
-
console.log(
|
|
2640
|
+
console.log(chalk11.yellow("\u2713 Telemetry disabled. Set OPENCLAW_NO_TELEMETRY=1 to suppress permanently."));
|
|
2043
2641
|
}
|
|
2044
2642
|
function telemetryStatus() {
|
|
2045
2643
|
const { optOut, clientId } = getTelemetryStatus();
|
|
2046
|
-
const status = optOut ?
|
|
2644
|
+
const status = optOut ? chalk11.red("disabled") : chalk11.green("enabled");
|
|
2047
2645
|
console.log(`Telemetry: ${status}`);
|
|
2048
|
-
console.log(`Client ID: ${
|
|
2646
|
+
console.log(`Client ID: ${chalk11.dim(clientId)}`);
|
|
2049
2647
|
console.log();
|
|
2050
|
-
console.log(
|
|
2051
|
-
console.log(
|
|
2648
|
+
console.log(chalk11.dim("Toggle: openclaw telemetry on/off"));
|
|
2649
|
+
console.log(chalk11.dim("Env: OPENCLAW_NO_TELEMETRY=1"));
|
|
2052
2650
|
}
|
|
2053
2651
|
|
|
2054
2652
|
// src/commands/remote.ts
|
|
2055
2653
|
init_config();
|
|
2056
|
-
import
|
|
2057
|
-
import { readFileSync as
|
|
2058
|
-
import { join as
|
|
2654
|
+
import chalk12 from "chalk";
|
|
2655
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync15, mkdirSync as mkdirSync3 } from "fs";
|
|
2656
|
+
import { join as join13 } from "path";
|
|
2059
2657
|
import { randomUUID as randomUUID2, randomBytes, createHash as createHash2 } from "crypto";
|
|
2060
2658
|
import { createServer as createServer2 } from "http";
|
|
2061
2659
|
import { exec as exec3 } from "child_process";
|
|
@@ -2073,11 +2671,11 @@ async function proxyFetch(url, init) {
|
|
|
2073
2671
|
return fetch(url, init);
|
|
2074
2672
|
}
|
|
2075
2673
|
var REMOTE_API_URL = "https://api.openclaw-cli.app";
|
|
2076
|
-
var REMOTE_CONFIG_FILE =
|
|
2674
|
+
var REMOTE_CONFIG_FILE = join13(DOCTOR_HOME, "remote.json");
|
|
2077
2675
|
function loadRemoteConfig() {
|
|
2078
2676
|
try {
|
|
2079
|
-
if (
|
|
2080
|
-
return JSON.parse(
|
|
2677
|
+
if (existsSync15(REMOTE_CONFIG_FILE)) {
|
|
2678
|
+
return JSON.parse(readFileSync12(REMOTE_CONFIG_FILE, "utf-8"));
|
|
2081
2679
|
}
|
|
2082
2680
|
} catch {
|
|
2083
2681
|
}
|
|
@@ -2090,9 +2688,9 @@ function loadRemoteConfig() {
|
|
|
2090
2688
|
};
|
|
2091
2689
|
}
|
|
2092
2690
|
function saveRemoteConfig(config) {
|
|
2093
|
-
const dir =
|
|
2094
|
-
if (!
|
|
2095
|
-
|
|
2691
|
+
const dir = join13(REMOTE_CONFIG_FILE, "..");
|
|
2692
|
+
if (!existsSync15(dir)) mkdirSync3(dir, { recursive: true });
|
|
2693
|
+
writeFileSync7(REMOTE_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
2096
2694
|
}
|
|
2097
2695
|
var OAUTH_CLIENT_ID = Buffer.from(
|
|
2098
2696
|
"MjM5NDk1OTI0Nzk4LTJtZWFhaTllcjZybTR1bnN0bW4zZmRldHRqZHM2bGJjLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29t",
|
|
@@ -2182,8 +2780,8 @@ function waitForOAuthCallback(codeVerifier) {
|
|
|
2182
2780
|
}
|
|
2183
2781
|
async function remoteLogin(_options) {
|
|
2184
2782
|
const config = loadRemoteConfig();
|
|
2185
|
-
console.log(
|
|
2186
|
-
console.log(
|
|
2783
|
+
console.log(chalk12.cyan.bold("\n Remote Monitoring Login\n"));
|
|
2784
|
+
console.log(chalk12.gray(" Opening browser for Google sign-in...\n"));
|
|
2187
2785
|
const codeVerifier = generateCodeVerifier();
|
|
2188
2786
|
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
2189
2787
|
const authUrl = OAUTH_AUTH_ENDPOINT + "?" + new URLSearchParams({
|
|
@@ -2203,10 +2801,11 @@ async function remoteLogin(_options) {
|
|
|
2203
2801
|
idToken = result.idToken;
|
|
2204
2802
|
email = result.email;
|
|
2205
2803
|
} catch (err) {
|
|
2206
|
-
console.log(
|
|
2804
|
+
console.log(chalk12.red(` ${String(err)}`));
|
|
2805
|
+
await trackCommand("remote login", false, getVersion());
|
|
2207
2806
|
return;
|
|
2208
2807
|
}
|
|
2209
|
-
console.log(
|
|
2808
|
+
console.log(chalk12.gray(" Registering this machine..."));
|
|
2210
2809
|
try {
|
|
2211
2810
|
const host = hostname();
|
|
2212
2811
|
const os = process.platform + "/" + process.arch;
|
|
@@ -2224,7 +2823,7 @@ async function remoteLogin(_options) {
|
|
|
2224
2823
|
clearTimeout(timer);
|
|
2225
2824
|
if (!res.ok) {
|
|
2226
2825
|
const err = await res.text();
|
|
2227
|
-
console.log(
|
|
2826
|
+
console.log(chalk12.red(` Registration failed: ${res.status} ${err}`));
|
|
2228
2827
|
return;
|
|
2229
2828
|
}
|
|
2230
2829
|
const data = await res.json();
|
|
@@ -2233,57 +2832,63 @@ async function remoteLogin(_options) {
|
|
|
2233
2832
|
config.enabled = true;
|
|
2234
2833
|
config.reportUrl = REMOTE_API_URL + "/v1/report";
|
|
2235
2834
|
saveRemoteConfig(config);
|
|
2236
|
-
console.log(
|
|
2835
|
+
console.log(chalk12.green.bold(`
|
|
2237
2836
|
Logged in as ${email}`));
|
|
2238
|
-
console.log(
|
|
2837
|
+
console.log(chalk12.gray(` Machine registered: ${host}`));
|
|
2239
2838
|
console.log(
|
|
2240
|
-
|
|
2839
|
+
chalk12.gray(" Remote monitoring ready. Run: openclaw-cli remote enable\n")
|
|
2241
2840
|
);
|
|
2841
|
+
await trackCommand("remote login", true, getVersion());
|
|
2242
2842
|
} catch (err) {
|
|
2243
|
-
console.log(
|
|
2843
|
+
console.log(chalk12.red(` Error: ${err}`));
|
|
2844
|
+
await trackCommand("remote login", false, getVersion());
|
|
2244
2845
|
}
|
|
2245
2846
|
}
|
|
2246
2847
|
async function remoteEnable(_options) {
|
|
2247
2848
|
const config = loadRemoteConfig();
|
|
2248
2849
|
if (!config.machineToken) {
|
|
2249
2850
|
console.log(
|
|
2250
|
-
|
|
2851
|
+
chalk12.red(" \u2717 Not logged in. Run: openclaw-cli remote login")
|
|
2251
2852
|
);
|
|
2853
|
+
await trackCommand("remote enable", false, getVersion());
|
|
2252
2854
|
return;
|
|
2253
2855
|
}
|
|
2254
2856
|
config.enabled = true;
|
|
2255
2857
|
saveRemoteConfig(config);
|
|
2256
|
-
console.log(
|
|
2858
|
+
console.log(chalk12.green(" Remote reporting enabled."));
|
|
2859
|
+
await trackCommand("remote enable", true, getVersion());
|
|
2257
2860
|
}
|
|
2258
2861
|
async function remoteDisable(_options) {
|
|
2259
2862
|
const config = loadRemoteConfig();
|
|
2260
2863
|
config.enabled = false;
|
|
2261
2864
|
saveRemoteConfig(config);
|
|
2262
|
-
console.log(
|
|
2865
|
+
console.log(chalk12.yellow(" Remote reporting disabled."));
|
|
2866
|
+
await trackCommand("remote disable", true, getVersion());
|
|
2263
2867
|
}
|
|
2264
2868
|
async function remoteStatus(_options) {
|
|
2869
|
+
await trackCommand("remote status", true, getVersion());
|
|
2265
2870
|
const config = loadRemoteConfig();
|
|
2266
|
-
console.log(
|
|
2871
|
+
console.log(chalk12.cyan.bold("\n Remote Monitoring Status\n"));
|
|
2267
2872
|
console.log(
|
|
2268
|
-
` Enabled: ${config.enabled ?
|
|
2873
|
+
` Enabled: ${config.enabled ? chalk12.green("yes") : chalk12.gray("no")}`
|
|
2269
2874
|
);
|
|
2270
2875
|
console.log(
|
|
2271
|
-
` Machine ID: ${
|
|
2876
|
+
` Machine ID: ${chalk12.white(config.machineId || "(none)")}`
|
|
2272
2877
|
);
|
|
2273
2878
|
console.log(
|
|
2274
|
-
` Token: ${config.machineToken ?
|
|
2879
|
+
` Token: ${config.machineToken ? chalk12.green("configured") : chalk12.red("not set")}`
|
|
2275
2880
|
);
|
|
2276
2881
|
console.log(
|
|
2277
|
-
` Report URL: ${
|
|
2882
|
+
` Report URL: ${chalk12.gray(config.reportUrl)}`
|
|
2278
2883
|
);
|
|
2279
2884
|
console.log(
|
|
2280
|
-
` Last Report: ${config.lastReport ?
|
|
2885
|
+
` Last Report: ${config.lastReport ? chalk12.gray(config.lastReport) : chalk12.gray("never")}`
|
|
2281
2886
|
);
|
|
2282
2887
|
console.log();
|
|
2283
2888
|
}
|
|
2284
2889
|
|
|
2285
2890
|
// src/index.ts
|
|
2286
|
-
var _PKG_VER2 = true ? "0.
|
|
2891
|
+
var _PKG_VER2 = true ? "0.7.2" : "0.2.1";
|
|
2287
2892
|
var version = _PKG_VER2;
|
|
2288
2893
|
printFirstRunNotice();
|
|
2289
2894
|
var program = new Command();
|
|
@@ -2308,6 +2913,12 @@ var gw = program.command("gateway").description("Manage the OpenClaw gateway ser
|
|
|
2308
2913
|
addGlobalOpts(gw.command("start").description("Start the gateway")).action(gatewayStart);
|
|
2309
2914
|
addGlobalOpts(gw.command("stop").description("Stop the gateway")).action(gatewayStop);
|
|
2310
2915
|
addGlobalOpts(gw.command("restart").description("Restart the gateway")).action(gatewayRestart);
|
|
2916
|
+
addGlobalOpts(
|
|
2917
|
+
program.command("cost").description("Show token usage and cost summary per agent/model").option("--json", "Machine-readable JSON output").option("--days <n>", "Limit date range (default: 30)")
|
|
2918
|
+
).action(showCost);
|
|
2919
|
+
var mcp = program.command("mcp").description("Manage local MCP (Model Context Protocol) servers");
|
|
2920
|
+
mcp.command("list").description("List configured MCP servers and their status").action(mcpList);
|
|
2921
|
+
mcp.command("status").description("Show detailed info for an MCP server").argument("<name>", "Server name").action(mcpStatus);
|
|
2311
2922
|
var tele = program.command("telemetry").description("Manage anonymous usage telemetry");
|
|
2312
2923
|
tele.command("on").description("Enable telemetry").action(telemetryOn);
|
|
2313
2924
|
tele.command("off").description("Disable telemetry").action(telemetryOff);
|
|
@@ -2320,6 +2931,12 @@ addGlobalOpts(
|
|
|
2320
2931
|
addGlobalOpts(
|
|
2321
2932
|
mem.command("compact").description("Compact agent memory (proxies to openclaw memory compact)").option("--dry-run", "Preview without applying")
|
|
2322
2933
|
).action(memoryCompact);
|
|
2934
|
+
addGlobalOpts(
|
|
2935
|
+
mem.command("gc").description("Remove old daily memory files").option("--days <n>", "Max age in days (default: 30)").option("--force", "Skip confirmation prompt")
|
|
2936
|
+
).action(memoryGc);
|
|
2937
|
+
addGlobalOpts(
|
|
2938
|
+
mem.command("export").description("Export all memory files to a single markdown file").option("-o, --output <file>", "Output file (default: stdout)")
|
|
2939
|
+
).action(memoryExport);
|
|
2323
2940
|
var remote = program.command("remote").description("Remote monitoring \u2014 report gateway status to openclaw-cli.app");
|
|
2324
2941
|
addGlobalOpts(remote.command("login").description("Authenticate and register this machine")).action(remoteLogin);
|
|
2325
2942
|
addGlobalOpts(remote.command("enable").description("Enable remote reporting")).action(remoteEnable);
|