@lark-apaas/openclaw-scripts-diagnose-cli 0.1.1-alpha.1 → 0.1.1-alpha.10
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.cjs +464 -21
- package/package.json +4 -2
- package/template/openclaw.json +522 -0
- package/template/scripts/restart.sh +37 -0
- package/template/scripts/start.sh +6 -0
- package/template/scripts/stop.sh +2 -0
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ node_fs = __toESM(node_fs);
|
|
|
30
30
|
let node_path = require("node:path");
|
|
31
31
|
node_path = __toESM(node_path);
|
|
32
32
|
let node_child_process = require("node:child_process");
|
|
33
|
+
let node_crypto = require("node:crypto");
|
|
33
34
|
//#region src/rule-engine/base.ts
|
|
34
35
|
/** Abstract base class for all diagnose rules */
|
|
35
36
|
var DiagnoseRule = class {
|
|
@@ -191,7 +192,7 @@ function fileExists(filePath) {
|
|
|
191
192
|
return node_fs.default.existsSync(filePath);
|
|
192
193
|
}
|
|
193
194
|
/** Execute a shell command, return stdout. Throws on failure. */
|
|
194
|
-
function shell(cmd, timeoutMs =
|
|
195
|
+
function shell(cmd, timeoutMs = 6e4) {
|
|
195
196
|
return (0, node_child_process.execSync)(cmd, {
|
|
196
197
|
encoding: "utf-8",
|
|
197
198
|
timeout: timeoutMs
|
|
@@ -685,7 +686,9 @@ let AllowedOriginsRule = class AllowedOriginsRule extends DiagnoseRule {
|
|
|
685
686
|
validate(ctx) {
|
|
686
687
|
const expected = getExpectedOrigins(ctx.vars);
|
|
687
688
|
if (expected.length === 0) return { pass: true };
|
|
688
|
-
const
|
|
689
|
+
const current = getCurrentOrigins(ctx.config);
|
|
690
|
+
if (hasWildcard(current)) return { pass: true };
|
|
691
|
+
const missing = findMissing(current, expected);
|
|
689
692
|
if (missing.length === 0) return { pass: true };
|
|
690
693
|
return {
|
|
691
694
|
pass: false,
|
|
@@ -695,6 +698,7 @@ let AllowedOriginsRule = class AllowedOriginsRule extends DiagnoseRule {
|
|
|
695
698
|
repair(ctx) {
|
|
696
699
|
const expected = getExpectedOrigins(ctx.vars);
|
|
697
700
|
const current = getCurrentOrigins(ctx.config);
|
|
701
|
+
if (hasWildcard(current)) return;
|
|
698
702
|
const missing = findMissing(current, expected);
|
|
699
703
|
if (missing.length > 0) {
|
|
700
704
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -721,13 +725,7 @@ AllowedOriginsRule = __decorate([Rule({
|
|
|
721
725
|
repairMode: "standard"
|
|
722
726
|
})], AllowedOriginsRule);
|
|
723
727
|
function getExpectedOrigins(vars) {
|
|
724
|
-
|
|
725
|
-
if (vars.miaodaDomain) origins.push(vars.miaodaDomain);
|
|
726
|
-
if (vars.miaodaOrigin) origins.push(vars.miaodaOrigin);
|
|
727
|
-
if (Array.isArray(vars.miaodaOrigins)) {
|
|
728
|
-
for (const o of vars.miaodaOrigins) if (o) origins.push(o);
|
|
729
|
-
}
|
|
730
|
-
return [...new Set(origins)];
|
|
728
|
+
return Array.isArray(vars.expectedOrigins) ? vars.expectedOrigins : [];
|
|
731
729
|
}
|
|
732
730
|
function getCurrentOrigins(config) {
|
|
733
731
|
const controlUi = getNestedMap(config, "gateway", "controlUi");
|
|
@@ -740,6 +738,10 @@ function findMissing(current, expected) {
|
|
|
740
738
|
const set = new Set(current);
|
|
741
739
|
return expected.filter((o) => !set.has(o));
|
|
742
740
|
}
|
|
741
|
+
/** Exact "*" entry means allow-all; pattern globs like "https://*.example.com" don't count. */
|
|
742
|
+
function hasWildcard(origins) {
|
|
743
|
+
return origins.includes("*");
|
|
744
|
+
}
|
|
743
745
|
//#endregion
|
|
744
746
|
//#region src/rules/jwt-token.ts
|
|
745
747
|
let JwtTokenRule = class JwtTokenRule extends DiagnoseRule {
|
|
@@ -990,19 +992,460 @@ function runRepair(input) {
|
|
|
990
992
|
}
|
|
991
993
|
}
|
|
992
994
|
//#endregion
|
|
995
|
+
//#region src/backup.ts
|
|
996
|
+
const BACKUP_PATH = "/home/gem/workspace/.force/openclaw/core-backup.json";
|
|
997
|
+
function runBackup(input) {
|
|
998
|
+
try {
|
|
999
|
+
const { configPath } = input;
|
|
1000
|
+
try {
|
|
1001
|
+
const validateOutput = shell("openclaw config validate --json");
|
|
1002
|
+
if (!JSON.parse(validateOutput).valid) return {
|
|
1003
|
+
success: false,
|
|
1004
|
+
error: "config validation failed"
|
|
1005
|
+
};
|
|
1006
|
+
} catch (e) {
|
|
1007
|
+
return {
|
|
1008
|
+
success: false,
|
|
1009
|
+
error: "config validate command failed: " + e.message
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
if (!fileExists(configPath)) return {
|
|
1013
|
+
success: false,
|
|
1014
|
+
error: "config file not found: " + configPath
|
|
1015
|
+
};
|
|
1016
|
+
const config = loadJSON5().parse(readFile(configPath));
|
|
1017
|
+
const backup = { _backup_meta: { created_at: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
1018
|
+
if (config.agents) backup.agents = config.agents;
|
|
1019
|
+
if (config.bindings) backup.bindings = config.bindings;
|
|
1020
|
+
const feishu = config.channels?.feishu;
|
|
1021
|
+
if (feishu?.accounts) backup.channels = { feishu: { accounts: feishu.accounts } };
|
|
1022
|
+
const backupDir = node_path.default.dirname(BACKUP_PATH);
|
|
1023
|
+
if (!node_fs.default.existsSync(backupDir)) node_fs.default.mkdirSync(backupDir, { recursive: true });
|
|
1024
|
+
const tmpPath = BACKUP_PATH + ".tmp";
|
|
1025
|
+
node_fs.default.writeFileSync(tmpPath, JSON.stringify(backup, null, 2), "utf-8");
|
|
1026
|
+
node_fs.default.renameSync(tmpPath, BACKUP_PATH);
|
|
1027
|
+
return { success: true };
|
|
1028
|
+
} catch (e) {
|
|
1029
|
+
return {
|
|
1030
|
+
success: false,
|
|
1031
|
+
error: "backup failed: " + e.message
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
//#endregion
|
|
1036
|
+
//#region src/reset-async.ts
|
|
1037
|
+
/**
|
|
1038
|
+
* Start an async reset task: spawn a detached child process and return the taskId.
|
|
1039
|
+
*
|
|
1040
|
+
* The child process runs: node cli.js reset --worker --task-id=xxx --ctx=base64
|
|
1041
|
+
*/
|
|
1042
|
+
function startAsyncReset(ctxBase64) {
|
|
1043
|
+
const taskId = (0, node_crypto.randomUUID)();
|
|
1044
|
+
const resultFile = `/tmp/openclaw-reset-${taskId}.json`;
|
|
1045
|
+
const initial = {
|
|
1046
|
+
status: "running",
|
|
1047
|
+
step: 0,
|
|
1048
|
+
totalSteps: 9,
|
|
1049
|
+
progress: "初始化...",
|
|
1050
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1051
|
+
};
|
|
1052
|
+
const tmpPath = resultFile + ".tmp";
|
|
1053
|
+
const dir = node_path.default.dirname(resultFile);
|
|
1054
|
+
if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
|
|
1055
|
+
node_fs.default.writeFileSync(tmpPath, JSON.stringify(initial), "utf-8");
|
|
1056
|
+
node_fs.default.renameSync(tmpPath, resultFile);
|
|
1057
|
+
const child = (0, node_child_process.spawn)(process.execPath, [
|
|
1058
|
+
process.argv[1],
|
|
1059
|
+
"reset",
|
|
1060
|
+
"--worker",
|
|
1061
|
+
`--task-id=${taskId}`,
|
|
1062
|
+
`--ctx=${ctxBase64}`
|
|
1063
|
+
], {
|
|
1064
|
+
detached: true,
|
|
1065
|
+
stdio: "ignore"
|
|
1066
|
+
});
|
|
1067
|
+
child.on("error", (err) => {
|
|
1068
|
+
const failResult = {
|
|
1069
|
+
status: "failed",
|
|
1070
|
+
step: 0,
|
|
1071
|
+
totalSteps: 9,
|
|
1072
|
+
progress: "Worker process failed to start",
|
|
1073
|
+
error: err.message,
|
|
1074
|
+
startedAt: initial.startedAt,
|
|
1075
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1076
|
+
};
|
|
1077
|
+
const errTmpPath = resultFile + ".tmp";
|
|
1078
|
+
node_fs.default.writeFileSync(errTmpPath, JSON.stringify(failResult));
|
|
1079
|
+
node_fs.default.renameSync(errTmpPath, resultFile);
|
|
1080
|
+
});
|
|
1081
|
+
child.unref();
|
|
1082
|
+
return { taskId };
|
|
1083
|
+
}
|
|
1084
|
+
//#endregion
|
|
1085
|
+
//#region src/reset.ts
|
|
1086
|
+
const STEPS = [
|
|
1087
|
+
"备份当前配置",
|
|
1088
|
+
"生成默认配置",
|
|
1089
|
+
"杀掉 openclaw 进程",
|
|
1090
|
+
"等待沙箱初始化完成",
|
|
1091
|
+
"重装 openclaw",
|
|
1092
|
+
"合并核心备份配置",
|
|
1093
|
+
"复制启动脚本",
|
|
1094
|
+
"重装内置插件",
|
|
1095
|
+
"启动并验证"
|
|
1096
|
+
];
|
|
1097
|
+
const TOTAL_STEPS = STEPS.length;
|
|
1098
|
+
const CORE_BACKUP_PATH = "/home/gem/workspace/.force/openclaw/core-backup.json";
|
|
1099
|
+
/**
|
|
1100
|
+
* Directory holding the bundled openclaw template (openclaw.json + scripts/).
|
|
1101
|
+
* Synced from git@code.byted.org:apaas/miaoda-openclaw-template.git via
|
|
1102
|
+
* scripts/sync-template.sh and published alongside dist/.
|
|
1103
|
+
*
|
|
1104
|
+
* At runtime, __dirname points to dist/, so the template lives one level up.
|
|
1105
|
+
*/
|
|
1106
|
+
const TEMPLATE_DIR = node_path.default.resolve(__dirname, "..", "template");
|
|
1107
|
+
function writeResultFile(resultFile, result) {
|
|
1108
|
+
const dir = node_path.default.dirname(resultFile);
|
|
1109
|
+
if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
|
|
1110
|
+
const tmpPath = resultFile + ".tmp";
|
|
1111
|
+
node_fs.default.writeFileSync(tmpPath, JSON.stringify(result), "utf-8");
|
|
1112
|
+
node_fs.default.renameSync(tmpPath, resultFile);
|
|
1113
|
+
}
|
|
1114
|
+
function updateProgress(resultFile, step, startedAt) {
|
|
1115
|
+
writeResultFile(resultFile, {
|
|
1116
|
+
status: "running",
|
|
1117
|
+
step,
|
|
1118
|
+
totalSteps: TOTAL_STEPS,
|
|
1119
|
+
progress: STEPS[step - 1],
|
|
1120
|
+
startedAt
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
function markDone(resultFile, startedAt) {
|
|
1124
|
+
writeResultFile(resultFile, {
|
|
1125
|
+
status: "done",
|
|
1126
|
+
step: TOTAL_STEPS,
|
|
1127
|
+
totalSteps: TOTAL_STEPS,
|
|
1128
|
+
progress: "重置完成",
|
|
1129
|
+
startedAt,
|
|
1130
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
function markFailed(resultFile, step, error, startedAt) {
|
|
1134
|
+
writeResultFile(resultFile, {
|
|
1135
|
+
status: "failed",
|
|
1136
|
+
step,
|
|
1137
|
+
totalSteps: TOTAL_STEPS,
|
|
1138
|
+
progress: step > 0 ? STEPS[step - 1] : "初始化",
|
|
1139
|
+
error,
|
|
1140
|
+
startedAt,
|
|
1141
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
/** Step 1: Backup current config as openclaw.json.bak.N */
|
|
1145
|
+
function backupCurrentConfig(configPath) {
|
|
1146
|
+
if (!fileExists(configPath)) return;
|
|
1147
|
+
const dir = node_path.default.dirname(configPath);
|
|
1148
|
+
let maxN = 0;
|
|
1149
|
+
try {
|
|
1150
|
+
for (const f of node_fs.default.readdirSync(dir)) {
|
|
1151
|
+
const match = f.match(/^openclaw\.json\.bak\.(\d+)$/);
|
|
1152
|
+
if (match) {
|
|
1153
|
+
const n = parseInt(match[1], 10);
|
|
1154
|
+
if (n > maxN) maxN = n;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
} catch {}
|
|
1158
|
+
node_fs.default.copyFileSync(configPath, configPath + ".bak." + (maxN + 1));
|
|
1159
|
+
}
|
|
1160
|
+
/** Step 2: Replace $$__XXX__ placeholders and write default config. */
|
|
1161
|
+
function generateDefaultConfig(srcDir, configPath, templateVars) {
|
|
1162
|
+
const srcConfigPath = node_path.default.join(srcDir, "openclaw.json");
|
|
1163
|
+
if (!fileExists(srcConfigPath)) throw new Error("template openclaw.json not found at " + srcConfigPath);
|
|
1164
|
+
let content = node_fs.default.readFileSync(srcConfigPath, "utf-8");
|
|
1165
|
+
for (const [placeholder, value] of Object.entries(templateVars)) content = content.split(placeholder).join(value);
|
|
1166
|
+
node_fs.default.writeFileSync(configPath, content, "utf-8");
|
|
1167
|
+
}
|
|
1168
|
+
/** Step 3: Kill all openclaw processes. */
|
|
1169
|
+
function killOpenclawProcesses() {
|
|
1170
|
+
try {
|
|
1171
|
+
shell("pkill -f openclaw-gateway || true", 5e3);
|
|
1172
|
+
} catch {}
|
|
1173
|
+
shell("sleep 2", 5e3);
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Step 4: Wait for the sandbox's own init (init_sandbox.sh / concurrent npm
|
|
1177
|
+
* install) to finish before we start our own npm i -g. Two npm processes
|
|
1178
|
+
* sharing ~/.npm cache + competing for disk/network just makes everything
|
|
1179
|
+
* crawl; letting init finish first is the cleanest way to get exclusive
|
|
1180
|
+
* access. Polls every 10s up to `maxWaitMs`. If the deadline is hit we fall
|
|
1181
|
+
* through anyway — better to try than to fail the reset outright.
|
|
1182
|
+
*/
|
|
1183
|
+
function waitForInitNpm(maxWaitMs) {
|
|
1184
|
+
const deadline = Date.now() + maxWaitMs;
|
|
1185
|
+
const ownPid = String(process.pid);
|
|
1186
|
+
while (Date.now() < deadline) {
|
|
1187
|
+
let running = 0;
|
|
1188
|
+
try {
|
|
1189
|
+
const out = shell(`pgrep -af "init_sandbox.sh|npm install|npm i " | grep -v -- "${ownPid}" | wc -l`, 1e4);
|
|
1190
|
+
running = parseInt(out.trim(), 10) || 0;
|
|
1191
|
+
} catch {
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
if (running === 0) return;
|
|
1195
|
+
try {
|
|
1196
|
+
shell("sleep 10", 12e3);
|
|
1197
|
+
} catch {}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Step 5: Reinstall openclaw to the version specified in template.
|
|
1202
|
+
*
|
|
1203
|
+
* Simple: if already at target version, skip. Otherwise uninstall+install
|
|
1204
|
+
* with a generous wall-clock timeout and trust npm's exit code (0 = success,
|
|
1205
|
+
* anything else = real failure, bubble up and fail the reset). No retries,
|
|
1206
|
+
* no idle-detection heuristics — waitForInitNpm above removes the main
|
|
1207
|
+
* source of contention so this step should run cleanly.
|
|
1208
|
+
*/
|
|
1209
|
+
function reinstallOpenclaw(srcDir) {
|
|
1210
|
+
const targetVersion = loadJSON5().parse(node_fs.default.readFileSync(node_path.default.join(srcDir, "openclaw.json"), "utf-8")).meta?.lastTouchedVersion;
|
|
1211
|
+
if (targetVersion && isOpenclawAtVersion(targetVersion)) {
|
|
1212
|
+
shell("openclaw doctor --fix", 12e4);
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
try {
|
|
1216
|
+
shell("npm uninstall -g openclaw 2>/dev/null || true", 6e4);
|
|
1217
|
+
} catch {}
|
|
1218
|
+
shell(`npm i -g openclaw@${targetVersion || "latest"} --prefer-offline --fetch-timeout=60000 --fetch-retries=2`, 15 * 6e4);
|
|
1219
|
+
shell("openclaw doctor --fix", 12e4);
|
|
1220
|
+
}
|
|
1221
|
+
/** Return true if `openclaw --version` output contains `targetVersion`. */
|
|
1222
|
+
function isOpenclawAtVersion(targetVersion) {
|
|
1223
|
+
try {
|
|
1224
|
+
return shell("openclaw --version 2>&1 || true", 1e4).includes(targetVersion);
|
|
1225
|
+
} catch {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
/** Step 6: Merge core-backup.json into config + ensure allowedOrigins. */
|
|
1230
|
+
function mergeCoreBackupAndOrigins(configPath, vars) {
|
|
1231
|
+
const JSON5 = loadJSON5();
|
|
1232
|
+
if (fileExists(CORE_BACKUP_PATH)) {
|
|
1233
|
+
const backup = JSON.parse(node_fs.default.readFileSync(CORE_BACKUP_PATH, "utf-8"));
|
|
1234
|
+
const config = JSON5.parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
1235
|
+
if (backup.agents) config.agents = backup.agents;
|
|
1236
|
+
if (backup.bindings) config.bindings = backup.bindings;
|
|
1237
|
+
const backupAccounts = backup.channels?.feishu;
|
|
1238
|
+
if (backupAccounts?.accounts) {
|
|
1239
|
+
if (!config.channels) config.channels = {};
|
|
1240
|
+
const ch = config.channels;
|
|
1241
|
+
if (!ch.feishu) ch.feishu = {};
|
|
1242
|
+
ch.feishu.accounts = backupAccounts.accounts;
|
|
1243
|
+
}
|
|
1244
|
+
node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1245
|
+
}
|
|
1246
|
+
const expectedOrigins = Array.isArray(vars.expectedOrigins) ? vars.expectedOrigins : [];
|
|
1247
|
+
if (expectedOrigins.length > 0) {
|
|
1248
|
+
const config = JSON5.parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
1249
|
+
if (!config.gateway) config.gateway = {};
|
|
1250
|
+
const gw = config.gateway;
|
|
1251
|
+
if (!gw.controlUi) gw.controlUi = {};
|
|
1252
|
+
const cui = gw.controlUi;
|
|
1253
|
+
const current = Array.isArray(cui.allowedOrigins) ? cui.allowedOrigins.filter((o) => typeof o === "string") : [];
|
|
1254
|
+
if (current.includes("*")) return;
|
|
1255
|
+
const seen = new Set(current);
|
|
1256
|
+
const merged = [...current];
|
|
1257
|
+
for (const o of expectedOrigins) if (!seen.has(o)) {
|
|
1258
|
+
merged.push(o);
|
|
1259
|
+
seen.add(o);
|
|
1260
|
+
}
|
|
1261
|
+
cui.allowedOrigins = merged;
|
|
1262
|
+
node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
/** Step 7: Copy startup scripts from template to agent dir. */
|
|
1266
|
+
function copyStartupScripts(srcDir, configDir) {
|
|
1267
|
+
const srcScriptsDir = node_path.default.join(srcDir, "scripts");
|
|
1268
|
+
const targetScriptsDir = node_path.default.join(configDir, "scripts");
|
|
1269
|
+
if (!node_fs.default.existsSync(srcScriptsDir)) return;
|
|
1270
|
+
if (!node_fs.default.existsSync(targetScriptsDir)) node_fs.default.mkdirSync(targetScriptsDir, { recursive: true });
|
|
1271
|
+
shell(`cp -r '${srcScriptsDir}'/* '${targetScriptsDir}/'`, 1e4);
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Step 8: Reinstall all plugins via openclaw CLI.
|
|
1275
|
+
*
|
|
1276
|
+
* Simple execSync with a generous wall-clock timeout (15min). By now
|
|
1277
|
+
* init_sandbox's npm should have finished (Step 4 waited for it), so we
|
|
1278
|
+
* have exclusive npm access. Non-fatal — a plugin update failure shouldn't
|
|
1279
|
+
* stop the reset from restarting the gateway.
|
|
1280
|
+
*/
|
|
1281
|
+
function reinstallPlugins() {
|
|
1282
|
+
try {
|
|
1283
|
+
shell("openclaw plugins update --all", 15 * 6e4);
|
|
1284
|
+
} catch {}
|
|
1285
|
+
}
|
|
1286
|
+
/** Step 9: Write secrets/provider key files and restart openclaw. */
|
|
1287
|
+
function writeSecretsAndRestart(vars, resetData, configDir) {
|
|
1288
|
+
if (resetData.secretsContent && vars.secretsFilePath) writeFile(vars.secretsFilePath, resetData.secretsContent);
|
|
1289
|
+
if (resetData.providerKeyContent && vars.providerFilePath) writeFile(vars.providerFilePath, resetData.providerKeyContent);
|
|
1290
|
+
const restartScript = node_path.default.join(configDir, "scripts", "restart.sh");
|
|
1291
|
+
if (fileExists(restartScript)) shell(`bash '${restartScript}'`, 3e4);
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Run the 9-step reset process. Called from the worker entry point.
|
|
1295
|
+
*
|
|
1296
|
+
* Each step is an independent function. The orchestrator handles progress
|
|
1297
|
+
* reporting, error handling, and process-level exception guards.
|
|
1298
|
+
*
|
|
1299
|
+
* The openclaw.json / scripts/*.sh template files are bundled with this CLI
|
|
1300
|
+
* (see TEMPLATE_DIR) and synced from the miaoda-openclaw-template repo via
|
|
1301
|
+
* scripts/sync-template.sh, so no runtime download is required.
|
|
1302
|
+
*/
|
|
1303
|
+
function runReset(input, taskId, resultFile) {
|
|
1304
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1305
|
+
const { configPath, vars, resetData } = input;
|
|
1306
|
+
const configDir = node_path.default.dirname(configPath);
|
|
1307
|
+
const srcDir = TEMPLATE_DIR;
|
|
1308
|
+
let currentStep = 0;
|
|
1309
|
+
if (!node_fs.default.existsSync(node_path.default.join(srcDir, "openclaw.json"))) {
|
|
1310
|
+
markFailed(resultFile, 0, `bundled template not found at ${srcDir}`, startedAt);
|
|
1311
|
+
process.exit(1);
|
|
1312
|
+
}
|
|
1313
|
+
process.on("uncaughtException", (err) => {
|
|
1314
|
+
markFailed(resultFile, currentStep, `uncaught exception: ${err.message}`, startedAt);
|
|
1315
|
+
process.exit(1);
|
|
1316
|
+
});
|
|
1317
|
+
process.on("unhandledRejection", (reason) => {
|
|
1318
|
+
markFailed(resultFile, currentStep, `unhandled rejection: ${reason}`, startedAt);
|
|
1319
|
+
process.exit(1);
|
|
1320
|
+
});
|
|
1321
|
+
/** Advance to the next step, updating the progress file. */
|
|
1322
|
+
const step = (n) => {
|
|
1323
|
+
currentStep = n;
|
|
1324
|
+
updateProgress(resultFile, n, startedAt);
|
|
1325
|
+
};
|
|
1326
|
+
try {
|
|
1327
|
+
step(1);
|
|
1328
|
+
backupCurrentConfig(configPath);
|
|
1329
|
+
step(2);
|
|
1330
|
+
generateDefaultConfig(srcDir, configPath, resetData.templateVars);
|
|
1331
|
+
step(3);
|
|
1332
|
+
killOpenclawProcesses();
|
|
1333
|
+
step(4);
|
|
1334
|
+
waitForInitNpm(10 * 6e4);
|
|
1335
|
+
step(5);
|
|
1336
|
+
reinstallOpenclaw(srcDir);
|
|
1337
|
+
step(6);
|
|
1338
|
+
mergeCoreBackupAndOrigins(configPath, vars);
|
|
1339
|
+
step(7);
|
|
1340
|
+
copyStartupScripts(srcDir, configDir);
|
|
1341
|
+
step(8);
|
|
1342
|
+
reinstallPlugins();
|
|
1343
|
+
step(9);
|
|
1344
|
+
writeSecretsAndRestart(vars, resetData, configDir);
|
|
1345
|
+
markDone(resultFile, startedAt);
|
|
1346
|
+
} catch (e) {
|
|
1347
|
+
markFailed(resultFile, currentStep, e.message, startedAt);
|
|
1348
|
+
process.exit(1);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
//#endregion
|
|
1352
|
+
//#region src/get-reset-task.ts
|
|
1353
|
+
/**
|
|
1354
|
+
* Long-poll for a reset task result.
|
|
1355
|
+
* Reads the result file every 1s for up to 30s.
|
|
1356
|
+
* Returns immediately on terminal states (done/failed).
|
|
1357
|
+
*/
|
|
1358
|
+
function getResetTask(taskId) {
|
|
1359
|
+
const resultFile = `/tmp/openclaw-reset-${taskId}.json`;
|
|
1360
|
+
const deadline = Date.now() + 3e4;
|
|
1361
|
+
while (Date.now() < deadline) {
|
|
1362
|
+
if (!node_fs.default.existsSync(resultFile)) {
|
|
1363
|
+
sleepSync(1e3);
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
try {
|
|
1367
|
+
const content = node_fs.default.readFileSync(resultFile, "utf-8");
|
|
1368
|
+
const result = JSON.parse(content);
|
|
1369
|
+
if (result.status === "done" || result.status === "failed") return result;
|
|
1370
|
+
} catch {}
|
|
1371
|
+
sleepSync(1e3);
|
|
1372
|
+
}
|
|
1373
|
+
if (node_fs.default.existsSync(resultFile)) try {
|
|
1374
|
+
return JSON.parse(node_fs.default.readFileSync(resultFile, "utf-8"));
|
|
1375
|
+
} catch {}
|
|
1376
|
+
return {
|
|
1377
|
+
status: "running",
|
|
1378
|
+
progress: "等待中..."
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* Synchronous sleep using Atomics.wait on a shared buffer.
|
|
1383
|
+
*/
|
|
1384
|
+
function sleepSync(ms) {
|
|
1385
|
+
const buf = new SharedArrayBuffer(4);
|
|
1386
|
+
const arr = new Int32Array(buf);
|
|
1387
|
+
Atomics.wait(arr, 0, 0, ms);
|
|
1388
|
+
}
|
|
1389
|
+
//#endregion
|
|
993
1390
|
//#region src/index.ts
|
|
994
1391
|
const args = node_process.default.argv.slice(2);
|
|
995
1392
|
const mode = args.find((a) => !a.startsWith("--"));
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1393
|
+
switch (mode) {
|
|
1394
|
+
case "check":
|
|
1395
|
+
case "repair": {
|
|
1396
|
+
const ctx = args.find((a) => a.startsWith("--ctx="))?.slice(6);
|
|
1397
|
+
if (!ctx) {
|
|
1398
|
+
console.error("Error: --ctx=<base64> is required");
|
|
1399
|
+
node_process.default.exit(1);
|
|
1400
|
+
}
|
|
1401
|
+
const input = JSON.parse(Buffer.from(ctx, "base64").toString("utf-8"));
|
|
1402
|
+
if (mode === "check") console.log(JSON.stringify(runCheck(input)));
|
|
1403
|
+
else console.log(JSON.stringify(runRepair(input)));
|
|
1404
|
+
break;
|
|
1405
|
+
}
|
|
1406
|
+
case "backup": {
|
|
1407
|
+
const ctx = args.find((a) => a.startsWith("--ctx="))?.slice(6);
|
|
1408
|
+
if (!ctx) {
|
|
1409
|
+
console.error("Error: --ctx=<base64> is required");
|
|
1410
|
+
node_process.default.exit(1);
|
|
1411
|
+
}
|
|
1412
|
+
const input = JSON.parse(Buffer.from(ctx, "base64").toString("utf-8"));
|
|
1413
|
+
console.log(JSON.stringify(runBackup(input)));
|
|
1414
|
+
break;
|
|
1415
|
+
}
|
|
1416
|
+
case "reset":
|
|
1417
|
+
if (args.includes("--async")) {
|
|
1418
|
+
const ctx = args.find((a) => a.startsWith("--ctx="))?.slice(6);
|
|
1419
|
+
if (!ctx) {
|
|
1420
|
+
console.error("Error: --ctx=<base64> is required");
|
|
1421
|
+
node_process.default.exit(1);
|
|
1422
|
+
}
|
|
1423
|
+
console.log(JSON.stringify(startAsyncReset(ctx)));
|
|
1424
|
+
} else if (args.includes("--worker")) {
|
|
1425
|
+
const ctx = args.find((a) => a.startsWith("--ctx="))?.slice(6);
|
|
1426
|
+
const taskId = args.find((a) => a.startsWith("--task-id="))?.slice(10);
|
|
1427
|
+
if (!ctx || !taskId) {
|
|
1428
|
+
console.error("Error: --ctx=<base64> and --task-id=<id> are required for worker");
|
|
1429
|
+
node_process.default.exit(1);
|
|
1430
|
+
}
|
|
1431
|
+
const resultFile = `/tmp/openclaw-reset-${taskId}.json`;
|
|
1432
|
+
runReset(JSON.parse(Buffer.from(ctx, "base64").toString("utf-8")), taskId, resultFile);
|
|
1433
|
+
} else {
|
|
1434
|
+
console.error("Usage: reset --async --ctx=<base64> | reset --worker --task-id=<id> --ctx=<base64>");
|
|
1435
|
+
node_process.default.exit(1);
|
|
1436
|
+
}
|
|
1437
|
+
break;
|
|
1438
|
+
case "get_reset_task": {
|
|
1439
|
+
const taskId = args.find((a) => a.startsWith("--task-id="))?.slice(10);
|
|
1440
|
+
if (!taskId) {
|
|
1441
|
+
console.error("Error: --task-id=<id> is required");
|
|
1442
|
+
node_process.default.exit(1);
|
|
1443
|
+
}
|
|
1444
|
+
console.log(JSON.stringify(getResetTask(taskId)));
|
|
1445
|
+
break;
|
|
1446
|
+
}
|
|
1447
|
+
default:
|
|
1448
|
+
console.error("Usage: mclaw-diagnose <check|repair|backup|reset|get_reset_task> [options]");
|
|
1449
|
+
node_process.default.exit(1);
|
|
1450
|
+
}
|
|
1008
1451
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/openclaw-scripts-diagnose-cli",
|
|
3
|
-
"version": "0.1.1-alpha.
|
|
3
|
+
"version": "0.1.1-alpha.10",
|
|
4
4
|
"description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mclaw-diagnose": "./dist/index.cjs"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist"
|
|
10
|
+
"dist",
|
|
11
|
+
"template"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
14
|
"build": "tsdown",
|
|
14
15
|
"test": "vitest run",
|
|
15
16
|
"test:watch": "vitest",
|
|
16
17
|
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
18
|
+
"sync:template": "bash scripts/sync-template.sh",
|
|
17
19
|
"prepublishOnly": "npm run build"
|
|
18
20
|
},
|
|
19
21
|
"keywords": [
|
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"lastTouchedVersion": "2026.4.9",
|
|
4
|
+
"lastTouchedAt": "2026-04-11T09:30:21.703Z"
|
|
5
|
+
},
|
|
6
|
+
"wizard": {
|
|
7
|
+
"lastRunAt": "2026-03-30T14:54:56.160Z",
|
|
8
|
+
"lastRunVersion": "2026.3.24",
|
|
9
|
+
"lastRunCommand": "doctor",
|
|
10
|
+
"lastRunMode": "local"
|
|
11
|
+
},
|
|
12
|
+
"update": {
|
|
13
|
+
"checkOnStart": false
|
|
14
|
+
},
|
|
15
|
+
"browser": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"color": "#00AA00",
|
|
18
|
+
"executablePath": "/usr/bin/chromium-browser",
|
|
19
|
+
"headless": true,
|
|
20
|
+
"noSandbox": true,
|
|
21
|
+
"defaultProfile": "openclaw",
|
|
22
|
+
"extraArgs": [
|
|
23
|
+
"--window-size=1920,1080",
|
|
24
|
+
"--test-type",
|
|
25
|
+
"--disable-gpu",
|
|
26
|
+
"--no-sandbox"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"secrets": {
|
|
30
|
+
"providers": {
|
|
31
|
+
"miaoda-provider": {
|
|
32
|
+
"source": "file",
|
|
33
|
+
"path": "$$__MIAODA_PROVIDER_FILEPATH__",
|
|
34
|
+
"mode": "singleValue"
|
|
35
|
+
},
|
|
36
|
+
"miaoda-secret-provider": {
|
|
37
|
+
"source": "file",
|
|
38
|
+
"path": "$$__MIAODA_OPENCLAW_SECRETS_FILEPATH__",
|
|
39
|
+
"mode": "json"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"models": {
|
|
44
|
+
"providers": {
|
|
45
|
+
"miaoda": {
|
|
46
|
+
"baseUrl": "$$__MIAODA_PROVIDER_BASE_URL__",
|
|
47
|
+
"apiKey": {
|
|
48
|
+
"source": "file",
|
|
49
|
+
"provider": "miaoda-provider",
|
|
50
|
+
"id": "value"
|
|
51
|
+
},
|
|
52
|
+
"api": "openai-completions",
|
|
53
|
+
"headers": {
|
|
54
|
+
"x-api-key": {
|
|
55
|
+
"source": "file",
|
|
56
|
+
"provider": "miaoda-secret-provider",
|
|
57
|
+
"id": "/models_providers_miaoda_headers_x_api_key"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"models": [
|
|
61
|
+
{
|
|
62
|
+
"id": "miaoda-model-auto",
|
|
63
|
+
"name": "妙搭",
|
|
64
|
+
"reasoning": false,
|
|
65
|
+
"input": [
|
|
66
|
+
"text"
|
|
67
|
+
],
|
|
68
|
+
"cost": {
|
|
69
|
+
"input": 0,
|
|
70
|
+
"output": 0,
|
|
71
|
+
"cacheRead": 0,
|
|
72
|
+
"cacheWrite": 0
|
|
73
|
+
},
|
|
74
|
+
"contextWindow": 200000,
|
|
75
|
+
"maxTokens": 8192
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "miaoda-auto-multimodal",
|
|
79
|
+
"name": "妙搭多模态",
|
|
80
|
+
"reasoning": false,
|
|
81
|
+
"input": [
|
|
82
|
+
"text",
|
|
83
|
+
"image"
|
|
84
|
+
],
|
|
85
|
+
"cost": {
|
|
86
|
+
"input": 0,
|
|
87
|
+
"output": 0,
|
|
88
|
+
"cacheRead": 0,
|
|
89
|
+
"cacheWrite": 0
|
|
90
|
+
},
|
|
91
|
+
"contextWindow": 200000,
|
|
92
|
+
"maxTokens": 8192
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "miaoda-model-flash",
|
|
96
|
+
"name": "妙搭 Flash",
|
|
97
|
+
"reasoning": false,
|
|
98
|
+
"input": [
|
|
99
|
+
"text"
|
|
100
|
+
],
|
|
101
|
+
"cost": {
|
|
102
|
+
"input": 0,
|
|
103
|
+
"output": 0,
|
|
104
|
+
"cacheRead": 0,
|
|
105
|
+
"cacheWrite": 0
|
|
106
|
+
},
|
|
107
|
+
"contextWindow": 200000,
|
|
108
|
+
"maxTokens": 8192
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"agents": {
|
|
115
|
+
"defaults": {
|
|
116
|
+
"model": {
|
|
117
|
+
"primary": "miaoda/miaoda-model-auto"
|
|
118
|
+
},
|
|
119
|
+
"imageModel": "miaoda/miaoda-auto-multimodal",
|
|
120
|
+
"imageGenerationModel": {
|
|
121
|
+
"primary": "miaoda/miaoda-image-gen"
|
|
122
|
+
},
|
|
123
|
+
"models": {
|
|
124
|
+
"miaoda/miaoda-model-auto": {
|
|
125
|
+
"alias": "Miaoda Auto"
|
|
126
|
+
},
|
|
127
|
+
"miaoda/miaoda-auto-multimodal": {
|
|
128
|
+
"alias": "Miaoda Multimodal"
|
|
129
|
+
},
|
|
130
|
+
"miaoda/miaoda-model-flash": {
|
|
131
|
+
"alias": "Miaoda Flash"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"workspace": "$$__MIAODA_WORKSPACE_DIR__/workspace",
|
|
135
|
+
"compaction": {
|
|
136
|
+
"mode": "safeguard",
|
|
137
|
+
"reserveTokensFloor": 50000
|
|
138
|
+
},
|
|
139
|
+
"verboseDefault": "off",
|
|
140
|
+
"blockStreamingCoalesce": {
|
|
141
|
+
"minChars": 20,
|
|
142
|
+
"maxChars": 800,
|
|
143
|
+
"idleMs": 300
|
|
144
|
+
},
|
|
145
|
+
"humanDelay": {
|
|
146
|
+
"mode": "off"
|
|
147
|
+
},
|
|
148
|
+
"heartbeat": {
|
|
149
|
+
"every": "4h",
|
|
150
|
+
"activeHours": {
|
|
151
|
+
"start": "08:00",
|
|
152
|
+
"end": "22:00"
|
|
153
|
+
},
|
|
154
|
+
"model": "miaoda/miaoda-model-flash"
|
|
155
|
+
},
|
|
156
|
+
"maxConcurrent": 4,
|
|
157
|
+
"subagents": {
|
|
158
|
+
"maxConcurrent": 8
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"tools": {
|
|
163
|
+
"profile": "full",
|
|
164
|
+
"alsoAllow": [
|
|
165
|
+
"feishu_bitable_app",
|
|
166
|
+
"feishu_bitable_app_table",
|
|
167
|
+
"feishu_bitable_app_table_field",
|
|
168
|
+
"feishu_bitable_app_table_record",
|
|
169
|
+
"feishu_bitable_app_table_view",
|
|
170
|
+
"feishu_calendar_calendar",
|
|
171
|
+
"feishu_calendar_event",
|
|
172
|
+
"feishu_calendar_event_attendee",
|
|
173
|
+
"feishu_calendar_freebusy",
|
|
174
|
+
"feishu_chat",
|
|
175
|
+
"feishu_chat_members",
|
|
176
|
+
"feishu_create_doc",
|
|
177
|
+
"feishu_doc_comments",
|
|
178
|
+
"feishu_doc_media",
|
|
179
|
+
"feishu_drive_file",
|
|
180
|
+
"feishu_fetch_doc",
|
|
181
|
+
"feishu_get_user",
|
|
182
|
+
"feishu_im_bot_image",
|
|
183
|
+
"feishu_im_user_fetch_resource",
|
|
184
|
+
"feishu_im_user_get_messages",
|
|
185
|
+
"feishu_im_user_get_thread_messages",
|
|
186
|
+
"feishu_im_user_message",
|
|
187
|
+
"feishu_im_user_search_messages",
|
|
188
|
+
"feishu_oauth",
|
|
189
|
+
"feishu_oauth_batch_auth",
|
|
190
|
+
"feishu_search_doc_wiki",
|
|
191
|
+
"feishu_search_user",
|
|
192
|
+
"feishu_sheet",
|
|
193
|
+
"feishu_task_comment",
|
|
194
|
+
"feishu_task_subtask",
|
|
195
|
+
"feishu_task_task",
|
|
196
|
+
"feishu_task_tasklist",
|
|
197
|
+
"feishu_update_doc",
|
|
198
|
+
"feishu_wiki_space",
|
|
199
|
+
"feishu_wiki_space_node"
|
|
200
|
+
],
|
|
201
|
+
"deny": [
|
|
202
|
+
"web_fetch",
|
|
203
|
+
"tts",
|
|
204
|
+
"agents_list",
|
|
205
|
+
"feishu_task_task",
|
|
206
|
+
"feishu_task_tasklist",
|
|
207
|
+
"feishu_task_comment",
|
|
208
|
+
"feishu_task_subtask",
|
|
209
|
+
"feishu_bitable_app_table_view",
|
|
210
|
+
"feishu_doc_comments",
|
|
211
|
+
"feishu_doc_media",
|
|
212
|
+
"feishu_drive_file",
|
|
213
|
+
"feishu_wiki_space",
|
|
214
|
+
"feishu_wiki_space_node",
|
|
215
|
+
"feishu_sheet"
|
|
216
|
+
],
|
|
217
|
+
"web": {
|
|
218
|
+
"search": {
|
|
219
|
+
"provider": "miaoda"
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
"media": {
|
|
223
|
+
"image": {
|
|
224
|
+
"models": [
|
|
225
|
+
{
|
|
226
|
+
"provider": "miaoda"
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
},
|
|
230
|
+
"audio": {
|
|
231
|
+
"models": [
|
|
232
|
+
{
|
|
233
|
+
"provider": "miaoda"
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
"sessions": {
|
|
239
|
+
"visibility": "all"
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
"messages": {
|
|
243
|
+
"ackReactionScope": "group-mentions"
|
|
244
|
+
},
|
|
245
|
+
"commands": {
|
|
246
|
+
"native": "auto",
|
|
247
|
+
"nativeSkills": "auto",
|
|
248
|
+
"restart": true,
|
|
249
|
+
"ownerDisplay": "raw"
|
|
250
|
+
},
|
|
251
|
+
"session": {
|
|
252
|
+
"dmScope": "per-channel-peer"
|
|
253
|
+
},
|
|
254
|
+
"channels": {
|
|
255
|
+
"feishu": {
|
|
256
|
+
"enabled": true,
|
|
257
|
+
"appId": "$$__FEISHU_APP_ID__",
|
|
258
|
+
"appSecret": {
|
|
259
|
+
"source": "file",
|
|
260
|
+
"provider": "miaoda-secret-provider",
|
|
261
|
+
"id": "/channels_feishu_app_secret"
|
|
262
|
+
},
|
|
263
|
+
"domain": "feishu",
|
|
264
|
+
"requireMention": true,
|
|
265
|
+
"dmPolicy": "allowlist",
|
|
266
|
+
"allowFrom": [
|
|
267
|
+
"$$__FEISHU_OPEN_ID__"
|
|
268
|
+
],
|
|
269
|
+
"groupPolicy": "allowlist",
|
|
270
|
+
"groupAllowFrom": [
|
|
271
|
+
"$$__FEISHU_OPEN_ID__"
|
|
272
|
+
],
|
|
273
|
+
"groups": {
|
|
274
|
+
"*": {
|
|
275
|
+
"enabled": true
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
"streaming": true,
|
|
279
|
+
"threadSession": true,
|
|
280
|
+
"footer": {
|
|
281
|
+
"elapsed": false,
|
|
282
|
+
"status": false
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"discovery": {
|
|
287
|
+
"mdns": {
|
|
288
|
+
"mode": "off"
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
"gateway": {
|
|
292
|
+
"port": 18789,
|
|
293
|
+
"mode": "local",
|
|
294
|
+
"bind": "loopback",
|
|
295
|
+
"controlUi": {
|
|
296
|
+
"allowedOrigins": [
|
|
297
|
+
"$$__MIAODA_DOMAIN__",
|
|
298
|
+
"$$__MIAODA_ORIGIN__"
|
|
299
|
+
],
|
|
300
|
+
"dangerouslyDisableDeviceAuth": true
|
|
301
|
+
},
|
|
302
|
+
"auth": {
|
|
303
|
+
"mode": "token",
|
|
304
|
+
"token": "$$__CLAW_TOKEN__"
|
|
305
|
+
},
|
|
306
|
+
"trustedProxies": [
|
|
307
|
+
"::1",
|
|
308
|
+
"127.0.0.1"
|
|
309
|
+
],
|
|
310
|
+
"tailscale": {
|
|
311
|
+
"mode": "off",
|
|
312
|
+
"resetOnExit": false
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
"skills": {
|
|
316
|
+
"install": {
|
|
317
|
+
"nodeManager": "npm"
|
|
318
|
+
},
|
|
319
|
+
"entries": {
|
|
320
|
+
"1password": {
|
|
321
|
+
"enabled": false
|
|
322
|
+
},
|
|
323
|
+
"apple-notes": {
|
|
324
|
+
"enabled": false
|
|
325
|
+
},
|
|
326
|
+
"apple-reminders": {
|
|
327
|
+
"enabled": false
|
|
328
|
+
},
|
|
329
|
+
"bear-notes": {
|
|
330
|
+
"enabled": false
|
|
331
|
+
},
|
|
332
|
+
"bluebubbles": {
|
|
333
|
+
"enabled": false
|
|
334
|
+
},
|
|
335
|
+
"blucli": {
|
|
336
|
+
"enabled": false
|
|
337
|
+
},
|
|
338
|
+
"camsnap": {
|
|
339
|
+
"enabled": false
|
|
340
|
+
},
|
|
341
|
+
"coding-agent": {
|
|
342
|
+
"enabled": false
|
|
343
|
+
},
|
|
344
|
+
"discord": {
|
|
345
|
+
"enabled": false
|
|
346
|
+
},
|
|
347
|
+
"eightctl": {
|
|
348
|
+
"enabled": false
|
|
349
|
+
},
|
|
350
|
+
"gemini": {
|
|
351
|
+
"enabled": false
|
|
352
|
+
},
|
|
353
|
+
"gifgrep": {
|
|
354
|
+
"enabled": false
|
|
355
|
+
},
|
|
356
|
+
"gog": {
|
|
357
|
+
"enabled": false
|
|
358
|
+
},
|
|
359
|
+
"goplaces": {
|
|
360
|
+
"enabled": false
|
|
361
|
+
},
|
|
362
|
+
"imsg": {
|
|
363
|
+
"enabled": false
|
|
364
|
+
},
|
|
365
|
+
"model-usage": {
|
|
366
|
+
"enabled": false
|
|
367
|
+
},
|
|
368
|
+
"notion": {
|
|
369
|
+
"enabled": false
|
|
370
|
+
},
|
|
371
|
+
"openai-image-gen": {
|
|
372
|
+
"enabled": false
|
|
373
|
+
},
|
|
374
|
+
"openai-whisper": {
|
|
375
|
+
"enabled": false
|
|
376
|
+
},
|
|
377
|
+
"openai-whisper-api": {
|
|
378
|
+
"enabled": false
|
|
379
|
+
},
|
|
380
|
+
"openhue": {
|
|
381
|
+
"enabled": false
|
|
382
|
+
},
|
|
383
|
+
"oracle": {
|
|
384
|
+
"enabled": false
|
|
385
|
+
},
|
|
386
|
+
"ordercli": {
|
|
387
|
+
"enabled": false
|
|
388
|
+
},
|
|
389
|
+
"peekaboo": {
|
|
390
|
+
"enabled": false
|
|
391
|
+
},
|
|
392
|
+
"sag": {
|
|
393
|
+
"enabled": false
|
|
394
|
+
},
|
|
395
|
+
"slack": {
|
|
396
|
+
"enabled": false
|
|
397
|
+
},
|
|
398
|
+
"sonoscli": {
|
|
399
|
+
"enabled": false
|
|
400
|
+
},
|
|
401
|
+
"spotify-player": {
|
|
402
|
+
"enabled": false
|
|
403
|
+
},
|
|
404
|
+
"summarize": {
|
|
405
|
+
"enabled": false
|
|
406
|
+
},
|
|
407
|
+
"things-mac": {
|
|
408
|
+
"enabled": false
|
|
409
|
+
},
|
|
410
|
+
"trello": {
|
|
411
|
+
"enabled": false
|
|
412
|
+
},
|
|
413
|
+
"voice-call": {
|
|
414
|
+
"enabled": false
|
|
415
|
+
},
|
|
416
|
+
"wacli": {
|
|
417
|
+
"enabled": false
|
|
418
|
+
},
|
|
419
|
+
"xurl": {
|
|
420
|
+
"enabled": false
|
|
421
|
+
},
|
|
422
|
+
"feishu-task": {
|
|
423
|
+
"enabled": false
|
|
424
|
+
},
|
|
425
|
+
"gh-issues": {
|
|
426
|
+
"enabled": false
|
|
427
|
+
},
|
|
428
|
+
"github": {
|
|
429
|
+
"enabled": false
|
|
430
|
+
},
|
|
431
|
+
"tmux": {
|
|
432
|
+
"enabled": false
|
|
433
|
+
},
|
|
434
|
+
"blogwatcher": {
|
|
435
|
+
"enabled": false
|
|
436
|
+
},
|
|
437
|
+
"himalaya": {
|
|
438
|
+
"enabled": false
|
|
439
|
+
},
|
|
440
|
+
"video-frames": {
|
|
441
|
+
"enabled": false
|
|
442
|
+
},
|
|
443
|
+
"obsidian": {
|
|
444
|
+
"enabled": false
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
"plugins": {
|
|
449
|
+
"allow": [
|
|
450
|
+
"openclaw-lark",
|
|
451
|
+
"openclaw-extension-miaoda",
|
|
452
|
+
"openclaw-extension-miaoda-coding",
|
|
453
|
+
"browser"
|
|
454
|
+
],
|
|
455
|
+
"entries": {
|
|
456
|
+
"feishu": {
|
|
457
|
+
"enabled": false
|
|
458
|
+
},
|
|
459
|
+
"openclaw-extension-miaoda": {
|
|
460
|
+
"enabled": true,
|
|
461
|
+
"config": {
|
|
462
|
+
"greeting": {
|
|
463
|
+
"message": "👋 Hi,我是**$$__MIAODA_CLAW_NAME__**,一只生活在飞书里的 🦞 小龙虾,打个招呼,我们开始认识一下?",
|
|
464
|
+
"targets": [
|
|
465
|
+
"$$__FEISHU_OPEN_ID__"
|
|
466
|
+
]
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
"openclaw-lark": {
|
|
471
|
+
"enabled": true
|
|
472
|
+
},
|
|
473
|
+
"openclaw-extension-miaoda-coding": {
|
|
474
|
+
"enabled": true
|
|
475
|
+
},
|
|
476
|
+
"browser": {
|
|
477
|
+
"enabled": true
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
"installs": {
|
|
481
|
+
"openclaw-lark": {
|
|
482
|
+
"source": "npm",
|
|
483
|
+
"spec": "@lark-apaas/openclaw-lark",
|
|
484
|
+
"installPath": "./extensions/openclaw-lark",
|
|
485
|
+
"version": "2026.4.3",
|
|
486
|
+
"resolvedName": "@lark-apaas/openclaw-lark",
|
|
487
|
+
"resolvedVersion": "2026.4.3",
|
|
488
|
+
"resolvedSpec": "@lark-apaas/openclaw-lark@2026.4.3",
|
|
489
|
+
"integrity": "sha512-1eJ2WCvAYsnM9TPwPbEoD3nJtqNSOP2zOBV/3Vb9YQRg1kJJspEly5yCjiZt1wew4UhyN8Tcrp/XV78Qn747GA==",
|
|
490
|
+
"shasum": "e8d8cf05f8cb96cf5de4bca097c527eb9116e655",
|
|
491
|
+
"resolvedAt": "2026-04-03T06:27:44.706Z",
|
|
492
|
+
"installedAt": "2026-04-03T06:28:10.547Z"
|
|
493
|
+
},
|
|
494
|
+
"openclaw-extension-miaoda": {
|
|
495
|
+
"source": "npm",
|
|
496
|
+
"installPath": "./extensions/openclaw-extension-miaoda",
|
|
497
|
+
"version": "1.0.9",
|
|
498
|
+
"installedAt": "2026-04-11T09:27:06.639Z",
|
|
499
|
+
"spec": "@lark-apaas/openclaw-extension-miaoda@1.0.9",
|
|
500
|
+
"resolvedVersion": "1.0.9",
|
|
501
|
+
"resolvedName": "@lark-apaas/openclaw-extension-miaoda",
|
|
502
|
+
"resolvedSpec": "@lark-apaas/openclaw-extension-miaoda@1.0.9",
|
|
503
|
+
"resolvedAt": "2026-04-11T09:27:06.639Z",
|
|
504
|
+
"integrity": "sha512-McNeuPAUDLrMhT3yZuwk9A7pI262r2CK1N1KNQP6VuzymkDUjx2sTcJPCEBB3bFkdXd0yUU983/OhJiaJo+JWg==",
|
|
505
|
+
"shasum": "a3c75886e40b63f39a33a2660932f8afdae6a514"
|
|
506
|
+
},
|
|
507
|
+
"openclaw-extension-miaoda-coding": {
|
|
508
|
+
"source": "npm",
|
|
509
|
+
"spec": "@lark-apaas/openclaw-extension-miaoda-coding",
|
|
510
|
+
"installPath": "./extensions/openclaw-extension-miaoda-coding",
|
|
511
|
+
"version": "1.0.8",
|
|
512
|
+
"resolvedName": "@lark-apaas/openclaw-extension-miaoda-coding",
|
|
513
|
+
"resolvedVersion": "1.0.8",
|
|
514
|
+
"resolvedSpec": "@lark-apaas/openclaw-extension-miaoda-coding@1.0.8",
|
|
515
|
+
"integrity": "sha512-uxlLtgH2CTwz56UTaZD+n/x1p2a3Q01o3Og7oLUJCm6izWHXFEI1SQhNnCPggrfSam49KFir8xB64tY4T9dt2Q==",
|
|
516
|
+
"shasum": "058eadf5bc71ae87f79b5096b9d96f4afb89a9db",
|
|
517
|
+
"resolvedAt": "2026-04-09T11:33:36.208Z",
|
|
518
|
+
"installedAt": "2026-04-09T11:33:37.171Z"
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
log() { echo "[restart.sh] $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
4
|
+
|
|
5
|
+
gw_alive() {
|
|
6
|
+
pgrep -f "openclaw-gateway|openclaw gateway" | grep -vw "$$" >/dev/null 2>&1
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if [ -d "/run/systemd/system/" ]; then
|
|
10
|
+
log "systemd detected, restarting via systemctl..."
|
|
11
|
+
sudo systemctl restart openclaw
|
|
12
|
+
log "systemctl restart exit code: $?"
|
|
13
|
+
else
|
|
14
|
+
log "killing openclaw-gateway processes..."
|
|
15
|
+
PIDS_GW=$(pgrep -f "openclaw-gateway|openclaw gateway" | grep -vw "$$" || true)
|
|
16
|
+
if [ -n "$PIDS_GW" ]; then
|
|
17
|
+
log "killing gateway pids: $(echo $PIDS_GW | tr '\n' ' ')"
|
|
18
|
+
kill -9 $PIDS_GW 2>&1
|
|
19
|
+
else
|
|
20
|
+
log "no openclaw-gateway processes found"
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
for i in $(seq 1 50); do
|
|
24
|
+
gw_alive || break
|
|
25
|
+
sleep 0.1
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
if gw_alive; then
|
|
29
|
+
log "ERROR: openclaw-gateway still alive after 5s, abort"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
log "openclaw-gateway stopped"
|
|
33
|
+
|
|
34
|
+
log "starting openclaw gateway..."
|
|
35
|
+
nohup openclaw gateway run --port 18789 > /tmp/openclaw-gateway.log 2>&1 &
|
|
36
|
+
log "gateway started (pid=$!)"
|
|
37
|
+
fi
|