@lark-apaas/openclaw-scripts-diagnose-cli 0.1.1-alpha.9 → 0.1.1
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 +363 -214
- package/package.json +1 -1
- package/template/openclaw.json +49 -32
package/dist/index.cjs
CHANGED
|
@@ -192,82 +192,14 @@ function fileExists(filePath) {
|
|
|
192
192
|
return node_fs.default.existsSync(filePath);
|
|
193
193
|
}
|
|
194
194
|
/** Execute a shell command, return stdout. Throws on failure. */
|
|
195
|
-
function shell(cmd, timeoutMs =
|
|
195
|
+
function shell(cmd, timeoutMs = 6e4) {
|
|
196
196
|
return (0, node_child_process.execSync)(cmd, {
|
|
197
197
|
encoding: "utf-8",
|
|
198
198
|
timeout: timeoutMs
|
|
199
199
|
}).trim();
|
|
200
200
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Run a long-lived command and kill it only when it goes idle (no stdout/stderr
|
|
203
|
-
* for `idleMs`) or hits the total deadline `maxTotalMs`. Suited to commands like
|
|
204
|
-
* `npm install` that may be slow but keep streaming progress — we don't want to
|
|
205
|
-
* kill them just because they exceeded some fixed wall-clock budget, only when
|
|
206
|
-
* they actually stall (e.g. blocked on a cache / lockfile held by another npm).
|
|
207
|
-
*/
|
|
208
|
-
function shellUntilIdle(cmd, opts = {}) {
|
|
209
|
-
const idleMs = opts.idleMs ?? 9e4;
|
|
210
|
-
const maxTotalMs = opts.maxTotalMs ?? 9e5;
|
|
211
|
-
return new Promise((resolve, reject) => {
|
|
212
|
-
const proc = (0, node_child_process.spawn)("bash", ["-c", cmd], { stdio: [
|
|
213
|
-
"ignore",
|
|
214
|
-
"pipe",
|
|
215
|
-
"pipe"
|
|
216
|
-
] });
|
|
217
|
-
const startedAt = Date.now();
|
|
218
|
-
let lastOutputAt = Date.now();
|
|
219
|
-
let killed = null;
|
|
220
|
-
const bump = () => {
|
|
221
|
-
lastOutputAt = Date.now();
|
|
222
|
-
};
|
|
223
|
-
proc.stdout.on("data", bump);
|
|
224
|
-
proc.stderr.on("data", bump);
|
|
225
|
-
const timer = setInterval(() => {
|
|
226
|
-
const now = Date.now();
|
|
227
|
-
if (now - lastOutputAt > idleMs) {
|
|
228
|
-
killed = `idle > ${idleMs}ms`;
|
|
229
|
-
proc.kill("SIGKILL");
|
|
230
|
-
} else if (now - startedAt > maxTotalMs) {
|
|
231
|
-
killed = `total > ${maxTotalMs}ms`;
|
|
232
|
-
proc.kill("SIGKILL");
|
|
233
|
-
}
|
|
234
|
-
}, 5e3);
|
|
235
|
-
proc.on("exit", (code, signal) => {
|
|
236
|
-
clearInterval(timer);
|
|
237
|
-
if (killed) return reject(/* @__PURE__ */ new Error(`shellUntilIdle killed (${killed}): ${cmd}`));
|
|
238
|
-
if (code === 0) return resolve();
|
|
239
|
-
reject(/* @__PURE__ */ new Error(`shellUntilIdle exit code=${code} signal=${signal}: ${cmd}`));
|
|
240
|
-
});
|
|
241
|
-
proc.on("error", (err) => {
|
|
242
|
-
clearInterval(timer);
|
|
243
|
-
reject(err);
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Retry a Promise-returning runner until it succeeds or the overall deadline
|
|
249
|
-
* passes. Designed to pair with `shellUntilIdle` for npm-style operations that
|
|
250
|
-
* may need several attempts if a concurrent npm is holding a lock.
|
|
251
|
-
*/
|
|
252
|
-
async function retryUntilDeadline(run, opts) {
|
|
253
|
-
const betweenAttemptsMs = opts.betweenAttemptsMs ?? 1e4;
|
|
254
|
-
const deadline = Date.now() + opts.maxTotalMs;
|
|
255
|
-
let lastErr;
|
|
256
|
-
let attempt = 0;
|
|
257
|
-
while (Date.now() < deadline) {
|
|
258
|
-
attempt++;
|
|
259
|
-
try {
|
|
260
|
-
return await run();
|
|
261
|
-
} catch (e) {
|
|
262
|
-
lastErr = e;
|
|
263
|
-
if (Date.now() + betweenAttemptsMs >= deadline) break;
|
|
264
|
-
await new Promise((r) => setTimeout(r, betweenAttemptsMs));
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
throw new Error(`retryUntilDeadline gave up after ${attempt} attempt(s): ${lastErr?.message ?? lastErr}`);
|
|
268
|
-
}
|
|
269
201
|
//#endregion
|
|
270
|
-
//#region \0@oxc-project+runtime@0.
|
|
202
|
+
//#region \0@oxc-project+runtime@0.115.0/helpers/decorate.js
|
|
271
203
|
function __decorate(decorators, target, key, desc) {
|
|
272
204
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
273
205
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -313,11 +245,15 @@ function findBackupFiles(configPath) {
|
|
|
313
245
|
}
|
|
314
246
|
/**
|
|
315
247
|
* Among backup files, find the one with the highest numeric suffix.
|
|
316
|
-
*
|
|
248
|
+
* Supports all three naming styles used by the current backup code and its
|
|
249
|
+
* older variants:
|
|
250
|
+
* `.bak` → n = 0 (legacy single-slot backup)
|
|
251
|
+
* `.bakN` → n = N (older style, dot-less)
|
|
252
|
+
* `.bak.N` → n = N (current style written by reset Step 1)
|
|
317
253
|
*/
|
|
318
254
|
function findHighestBackup(backupFiles) {
|
|
319
255
|
if (backupFiles.length === 0) return null;
|
|
320
|
-
const bakRegex = /\.bak(\d*)$/;
|
|
256
|
+
const bakRegex = /\.bak\.?(\d*)$/;
|
|
321
257
|
let best = null;
|
|
322
258
|
for (const f of backupFiles) {
|
|
323
259
|
const match = bakRegex.exec(f);
|
|
@@ -754,7 +690,9 @@ let AllowedOriginsRule = class AllowedOriginsRule extends DiagnoseRule {
|
|
|
754
690
|
validate(ctx) {
|
|
755
691
|
const expected = getExpectedOrigins(ctx.vars);
|
|
756
692
|
if (expected.length === 0) return { pass: true };
|
|
757
|
-
const
|
|
693
|
+
const current = getCurrentOrigins(ctx.config);
|
|
694
|
+
if (hasWildcard(current)) return { pass: true };
|
|
695
|
+
const missing = findMissing(current, expected);
|
|
758
696
|
if (missing.length === 0) return { pass: true };
|
|
759
697
|
return {
|
|
760
698
|
pass: false,
|
|
@@ -764,6 +702,7 @@ let AllowedOriginsRule = class AllowedOriginsRule extends DiagnoseRule {
|
|
|
764
702
|
repair(ctx) {
|
|
765
703
|
const expected = getExpectedOrigins(ctx.vars);
|
|
766
704
|
const current = getCurrentOrigins(ctx.config);
|
|
705
|
+
if (hasWildcard(current)) return;
|
|
767
706
|
const missing = findMissing(current, expected);
|
|
768
707
|
if (missing.length > 0) {
|
|
769
708
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -803,6 +742,10 @@ function findMissing(current, expected) {
|
|
|
803
742
|
const set = new Set(current);
|
|
804
743
|
return expected.filter((o) => !set.has(o));
|
|
805
744
|
}
|
|
745
|
+
/** Exact "*" entry means allow-all; pattern globs like "https://*.example.com" don't count. */
|
|
746
|
+
function hasWildcard(origins) {
|
|
747
|
+
return origins.includes("*");
|
|
748
|
+
}
|
|
806
749
|
//#endregion
|
|
807
750
|
//#region src/rules/jwt-token.ts
|
|
808
751
|
let JwtTokenRule = class JwtTokenRule extends DiagnoseRule {
|
|
@@ -1053,45 +996,34 @@ function runRepair(input) {
|
|
|
1053
996
|
}
|
|
1054
997
|
}
|
|
1055
998
|
//#endregion
|
|
1056
|
-
//#region src/
|
|
1057
|
-
|
|
1058
|
-
|
|
999
|
+
//#region src/paths.ts
|
|
1000
|
+
/**
|
|
1001
|
+
* Central directory for all ephemeral diagnose/reset artifacts: task status
|
|
1002
|
+
* files (`reset-<taskId>.json`) and human-readable step logs
|
|
1003
|
+
* (`reset-<taskId>.log`). Having everything under one dir makes debugging a
|
|
1004
|
+
* stuck reset much easier — `ls /tmp/openclaw-diagnose/` shows every recent
|
|
1005
|
+
* run, and each run's log is right next to its state.
|
|
1006
|
+
*/
|
|
1007
|
+
const DIAGNOSE_DIR = "/tmp/openclaw-diagnose";
|
|
1008
|
+
function resetResultFile(taskId) {
|
|
1009
|
+
return `${DIAGNOSE_DIR}/reset-${taskId}.json`;
|
|
1010
|
+
}
|
|
1011
|
+
function resetLogFile(taskId) {
|
|
1012
|
+
return `${DIAGNOSE_DIR}/reset-${taskId}.log`;
|
|
1013
|
+
}
|
|
1014
|
+
//#endregion
|
|
1015
|
+
//#region src/logger.ts
|
|
1016
|
+
function makeLogger(logFile) {
|
|
1059
1017
|
try {
|
|
1060
|
-
const
|
|
1018
|
+
const dir = node_path.default.dirname(logFile);
|
|
1019
|
+
if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
|
|
1020
|
+
} catch {}
|
|
1021
|
+
return (msg) => {
|
|
1022
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}\n`;
|
|
1061
1023
|
try {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
error: "config validation failed"
|
|
1066
|
-
};
|
|
1067
|
-
} catch (e) {
|
|
1068
|
-
return {
|
|
1069
|
-
success: false,
|
|
1070
|
-
error: "config validate command failed: " + e.message
|
|
1071
|
-
};
|
|
1072
|
-
}
|
|
1073
|
-
if (!fileExists(configPath)) return {
|
|
1074
|
-
success: false,
|
|
1075
|
-
error: "config file not found: " + configPath
|
|
1076
|
-
};
|
|
1077
|
-
const config = loadJSON5().parse(readFile(configPath));
|
|
1078
|
-
const backup = { _backup_meta: { created_at: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
1079
|
-
if (config.agents) backup.agents = config.agents;
|
|
1080
|
-
if (config.bindings) backup.bindings = config.bindings;
|
|
1081
|
-
const feishu = config.channels?.feishu;
|
|
1082
|
-
if (feishu?.accounts) backup.channels = { feishu: { accounts: feishu.accounts } };
|
|
1083
|
-
const backupDir = node_path.default.dirname(BACKUP_PATH);
|
|
1084
|
-
if (!node_fs.default.existsSync(backupDir)) node_fs.default.mkdirSync(backupDir, { recursive: true });
|
|
1085
|
-
const tmpPath = BACKUP_PATH + ".tmp";
|
|
1086
|
-
node_fs.default.writeFileSync(tmpPath, JSON.stringify(backup, null, 2), "utf-8");
|
|
1087
|
-
node_fs.default.renameSync(tmpPath, BACKUP_PATH);
|
|
1088
|
-
return { success: true };
|
|
1089
|
-
} catch (e) {
|
|
1090
|
-
return {
|
|
1091
|
-
success: false,
|
|
1092
|
-
error: "backup failed: " + e.message
|
|
1093
|
-
};
|
|
1094
|
-
}
|
|
1024
|
+
node_fs.default.appendFileSync(logFile, line);
|
|
1025
|
+
} catch {}
|
|
1026
|
+
};
|
|
1095
1027
|
}
|
|
1096
1028
|
//#endregion
|
|
1097
1029
|
//#region src/reset-async.ts
|
|
@@ -1102,7 +1034,9 @@ function runBackup(input) {
|
|
|
1102
1034
|
*/
|
|
1103
1035
|
function startAsyncReset(ctxBase64) {
|
|
1104
1036
|
const taskId = (0, node_crypto.randomUUID)();
|
|
1105
|
-
const resultFile =
|
|
1037
|
+
const resultFile = resetResultFile(taskId);
|
|
1038
|
+
const log = makeLogger(resetLogFile(taskId));
|
|
1039
|
+
log(`=== startAsyncReset spawning worker for taskId=${taskId} ===`);
|
|
1106
1040
|
const initial = {
|
|
1107
1041
|
status: "running",
|
|
1108
1042
|
step: 0,
|
|
@@ -1126,6 +1060,7 @@ function startAsyncReset(ctxBase64) {
|
|
|
1126
1060
|
stdio: "ignore"
|
|
1127
1061
|
});
|
|
1128
1062
|
child.on("error", (err) => {
|
|
1063
|
+
log(`FATAL worker failed to start: ${err.message}`);
|
|
1129
1064
|
const failResult = {
|
|
1130
1065
|
status: "failed",
|
|
1131
1066
|
step: 0,
|
|
@@ -1140,6 +1075,7 @@ function startAsyncReset(ctxBase64) {
|
|
|
1140
1075
|
node_fs.default.renameSync(errTmpPath, resultFile);
|
|
1141
1076
|
});
|
|
1142
1077
|
child.unref();
|
|
1078
|
+
log(`spawned worker pid=${child.pid}`);
|
|
1143
1079
|
return { taskId };
|
|
1144
1080
|
}
|
|
1145
1081
|
//#endregion
|
|
@@ -1148,14 +1084,16 @@ const STEPS = [
|
|
|
1148
1084
|
"备份当前配置",
|
|
1149
1085
|
"生成默认配置",
|
|
1150
1086
|
"杀掉 openclaw 进程",
|
|
1151
|
-
"
|
|
1087
|
+
"等待沙箱初始化完成",
|
|
1088
|
+
"确认 openclaw 版本",
|
|
1152
1089
|
"合并核心备份配置",
|
|
1153
1090
|
"复制启动脚本",
|
|
1154
|
-
"
|
|
1091
|
+
"安装扩展",
|
|
1155
1092
|
"启动并验证"
|
|
1156
1093
|
];
|
|
1157
1094
|
const TOTAL_STEPS = STEPS.length;
|
|
1158
|
-
|
|
1095
|
+
/** Pre-packed extensions archive on OSS. Update this URL when releasing a new version. */
|
|
1096
|
+
const EXTENSIONS_OSS_URL = "https://miaoda-template-online.oss-cn-beijing.aliyuncs.com/builtin/tool/pkg/openclaw-extensions-2026.4.9.tar.gz";
|
|
1159
1097
|
/**
|
|
1160
1098
|
* Directory holding the bundled openclaw template (openclaw.json + scripts/).
|
|
1161
1099
|
* Synced from git@code.byted.org:apaas/miaoda-openclaw-template.git via
|
|
@@ -1202,8 +1140,11 @@ function markFailed(resultFile, step, error, startedAt) {
|
|
|
1202
1140
|
});
|
|
1203
1141
|
}
|
|
1204
1142
|
/** Step 1: Backup current config as openclaw.json.bak.N */
|
|
1205
|
-
function backupCurrentConfig(configPath) {
|
|
1206
|
-
if (!fileExists(configPath))
|
|
1143
|
+
function backupCurrentConfig(configPath, log) {
|
|
1144
|
+
if (!fileExists(configPath)) {
|
|
1145
|
+
log("no existing config, skip backup");
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1207
1148
|
const dir = node_path.default.dirname(configPath);
|
|
1208
1149
|
let maxN = 0;
|
|
1209
1150
|
try {
|
|
@@ -1215,117 +1156,321 @@ function backupCurrentConfig(configPath) {
|
|
|
1215
1156
|
}
|
|
1216
1157
|
}
|
|
1217
1158
|
} catch {}
|
|
1218
|
-
|
|
1159
|
+
const bakPath = configPath + ".bak." + (maxN + 1);
|
|
1160
|
+
node_fs.default.copyFileSync(configPath, bakPath);
|
|
1161
|
+
log(`backed up to ${bakPath}`);
|
|
1219
1162
|
}
|
|
1220
1163
|
/** Step 2: Replace $$__XXX__ placeholders and write default config. */
|
|
1221
|
-
function generateDefaultConfig(srcDir, configPath, templateVars) {
|
|
1164
|
+
function generateDefaultConfig(srcDir, configPath, templateVars, log) {
|
|
1222
1165
|
const srcConfigPath = node_path.default.join(srcDir, "openclaw.json");
|
|
1223
1166
|
if (!fileExists(srcConfigPath)) throw new Error("template openclaw.json not found at " + srcConfigPath);
|
|
1224
1167
|
let content = node_fs.default.readFileSync(srcConfigPath, "utf-8");
|
|
1225
|
-
|
|
1168
|
+
let replaced = 0;
|
|
1169
|
+
for (const [placeholder, value] of Object.entries(templateVars)) {
|
|
1170
|
+
const parts = content.split(placeholder);
|
|
1171
|
+
if (parts.length > 1) replaced += parts.length - 1;
|
|
1172
|
+
content = parts.join(value);
|
|
1173
|
+
}
|
|
1226
1174
|
node_fs.default.writeFileSync(configPath, content, "utf-8");
|
|
1175
|
+
log(`wrote ${configPath} (${replaced} placeholder(s) replaced, ${Object.keys(templateVars).length} provided)`);
|
|
1227
1176
|
}
|
|
1228
1177
|
/** Step 3: Kill all openclaw processes. */
|
|
1229
|
-
function killOpenclawProcesses() {
|
|
1178
|
+
function killOpenclawProcesses(log) {
|
|
1230
1179
|
try {
|
|
1231
1180
|
shell("pkill -f openclaw-gateway || true", 5e3);
|
|
1232
1181
|
} catch {}
|
|
1233
1182
|
shell("sleep 2", 5e3);
|
|
1183
|
+
log("killed openclaw-gateway processes");
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Step 4: Wait for the sandbox's own init (init_sandbox.sh / concurrent npm
|
|
1187
|
+
* install) to finish before we start our own npm i -g. Two npm processes
|
|
1188
|
+
* sharing ~/.npm cache + competing for disk/network just makes everything
|
|
1189
|
+
* crawl; letting init finish first is the cleanest way to get exclusive
|
|
1190
|
+
* access. Polls every 10s up to `maxWaitMs`. If the deadline is hit we fall
|
|
1191
|
+
* through anyway — better to try than to fail the reset outright.
|
|
1192
|
+
*/
|
|
1193
|
+
function waitForInitNpm(maxWaitMs, log) {
|
|
1194
|
+
const deadline = Date.now() + maxWaitMs;
|
|
1195
|
+
const ownPid = String(process.pid);
|
|
1196
|
+
let polls = 0;
|
|
1197
|
+
while (Date.now() < deadline) {
|
|
1198
|
+
polls++;
|
|
1199
|
+
let running = 0;
|
|
1200
|
+
try {
|
|
1201
|
+
const out = shell(`pgrep -af "init_sandbox.sh|npm install|npm i " | grep -v -- "${ownPid}" | wc -l`, 1e4);
|
|
1202
|
+
running = parseInt(out.trim(), 10) || 0;
|
|
1203
|
+
} catch {
|
|
1204
|
+
log(`poll ${polls}: no concurrent npm, proceeding`);
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
if (running === 0) {
|
|
1208
|
+
log(`poll ${polls}: no concurrent npm, proceeding`);
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
log(`poll ${polls}: ${running} concurrent npm/init process(es) still running, waiting 10s`);
|
|
1212
|
+
try {
|
|
1213
|
+
shell("sleep 10", 12e3);
|
|
1214
|
+
} catch {}
|
|
1215
|
+
}
|
|
1216
|
+
log(`deadline (${maxWaitMs}ms) hit after ${polls} poll(s), proceeding anyway`);
|
|
1234
1217
|
}
|
|
1235
1218
|
/**
|
|
1236
|
-
* Step
|
|
1219
|
+
* Step 5: Ensure openclaw binary is at the template's recommended version.
|
|
1237
1220
|
*
|
|
1238
|
-
*
|
|
1239
|
-
*
|
|
1240
|
-
*
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
1243
|
-
* succeed once that npm completes.
|
|
1221
|
+
* Only checks/installs the binary — does NOT run `doctor --fix` any more.
|
|
1222
|
+
* Extensions are installed separately via OSS tar.gz (Step 8), which means
|
|
1223
|
+
* the openclaw-lark extension schema is already in place when openclaw
|
|
1224
|
+
* starts, avoiding the schema-priority mismatch that caused doctor to
|
|
1225
|
+
* reject valid config fields like threadSession / footer.
|
|
1244
1226
|
*/
|
|
1245
|
-
|
|
1246
|
-
const
|
|
1227
|
+
function ensureOpenclawBinary(srcDir, configPath, log) {
|
|
1228
|
+
const targetVersion = loadJSON5().parse(node_fs.default.readFileSync(node_path.default.join(srcDir, "openclaw.json"), "utf-8")).meta?.lastTouchedVersion;
|
|
1229
|
+
log(`target openclaw version: ${targetVersion ?? "<unset>"}`);
|
|
1230
|
+
if (targetVersion && isOpenclawAtVersion(targetVersion)) {
|
|
1231
|
+
log("openclaw already at target version, nothing to do");
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
if (isOpenclawInstalled()) {
|
|
1235
|
+
const updateCmd = `openclaw update${targetVersion ? " --tag " + targetVersion : ""} --yes --no-restart`;
|
|
1236
|
+
log(`openclaw installed but version mismatched, running: ${updateCmd}`);
|
|
1237
|
+
const timeout = 12 * 6e4;
|
|
1238
|
+
const t = Date.now();
|
|
1239
|
+
const tmpConfig = configPath + ".update-tmp";
|
|
1240
|
+
const hadConfig = node_fs.default.existsSync(configPath);
|
|
1241
|
+
if (hadConfig) {
|
|
1242
|
+
node_fs.default.renameSync(configPath, tmpConfig);
|
|
1243
|
+
log("temporarily hid config to bypass config guard");
|
|
1244
|
+
}
|
|
1245
|
+
try {
|
|
1246
|
+
shell(updateCmd, timeout);
|
|
1247
|
+
log(`openclaw update done in ${Date.now() - t}ms`);
|
|
1248
|
+
} catch (e) {
|
|
1249
|
+
const elapsed = Date.now() - t;
|
|
1250
|
+
if (elapsed >= timeout - 1e3) log(`openclaw update timed out after ${elapsed}ms (non-fatal, continuing)`);
|
|
1251
|
+
else throw e;
|
|
1252
|
+
} finally {
|
|
1253
|
+
if (hadConfig && node_fs.default.existsSync(tmpConfig)) {
|
|
1254
|
+
node_fs.default.renameSync(tmpConfig, configPath);
|
|
1255
|
+
log("restored config after update");
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
} else {
|
|
1259
|
+
log("openclaw binary not found, running full reinstall");
|
|
1260
|
+
fullReinstall(targetVersion, log);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
/** Check if openclaw command exists (regardless of version). */
|
|
1264
|
+
function isOpenclawInstalled() {
|
|
1265
|
+
try {
|
|
1266
|
+
shell("which openclaw 2>/dev/null", 5e3);
|
|
1267
|
+
return true;
|
|
1268
|
+
} catch {
|
|
1269
|
+
return false;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
/** Full uninstall + reinstall from npm (slow path, triggers postinstall). */
|
|
1273
|
+
function fullReinstall(targetVersion, log) {
|
|
1247
1274
|
try {
|
|
1248
|
-
shell("npm uninstall -g openclaw 2>/dev/null || true",
|
|
1275
|
+
shell("npm uninstall -g openclaw 2>/dev/null || true", 6e4);
|
|
1249
1276
|
} catch {}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1277
|
+
try {
|
|
1278
|
+
shell("rm -rf /home/gem/.npm-global/lib/node_modules/openclaw /home/gem/.npm-global/bin/openclaw 2>/dev/null || true", 1e4);
|
|
1279
|
+
log("force-cleaned residual openclaw global dir");
|
|
1280
|
+
} catch {}
|
|
1281
|
+
const installCmd = `npm i -g openclaw@${targetVersion || "latest"}`;
|
|
1282
|
+
log(`running: ${installCmd}`);
|
|
1283
|
+
const t = Date.now();
|
|
1284
|
+
shell(installCmd, 30 * 6e4);
|
|
1285
|
+
log(`npm install done in ${Date.now() - t}ms`);
|
|
1259
1286
|
}
|
|
1260
|
-
/**
|
|
1261
|
-
function
|
|
1287
|
+
/** Return true if `openclaw --version` output contains `targetVersion`. */
|
|
1288
|
+
function isOpenclawAtVersion(targetVersion) {
|
|
1289
|
+
try {
|
|
1290
|
+
return shell("openclaw --version 2>&1 || true", 1e4).includes(targetVersion);
|
|
1291
|
+
} catch {
|
|
1292
|
+
return false;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
/** Step 6: Merge coreBackup from resetData + ensure allowedOrigins. */
|
|
1296
|
+
function mergeCoreBackupAndOrigins(configPath, vars, resetData, log) {
|
|
1262
1297
|
const JSON5 = loadJSON5();
|
|
1263
|
-
|
|
1264
|
-
|
|
1298
|
+
const backup = resetData.coreBackup;
|
|
1299
|
+
if (backup) {
|
|
1265
1300
|
const config = JSON5.parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
1266
|
-
|
|
1267
|
-
if (backup.
|
|
1268
|
-
|
|
1269
|
-
|
|
1301
|
+
const merged = [];
|
|
1302
|
+
if (backup.agents && backup.agents.length > 0) {
|
|
1303
|
+
if (!config.agents) config.agents = {};
|
|
1304
|
+
const agents = config.agents;
|
|
1305
|
+
if (!Array.isArray(agents.list)) agents.list = [];
|
|
1306
|
+
const configDir = node_path.default.dirname(configPath);
|
|
1307
|
+
for (const agent of backup.agents) {
|
|
1308
|
+
const enriched = {
|
|
1309
|
+
id: agent.id,
|
|
1310
|
+
name: agent.id,
|
|
1311
|
+
workspace: agent.workspace,
|
|
1312
|
+
agentDir: configDir + "/agents/" + agent.id + "/agent"
|
|
1313
|
+
};
|
|
1314
|
+
agents.list.push(enriched);
|
|
1315
|
+
}
|
|
1316
|
+
merged.push(`agents(+${backup.agents.length})`);
|
|
1317
|
+
const list = agents.list;
|
|
1318
|
+
let mainIdx = list.findIndex((a) => a.id === "main");
|
|
1319
|
+
if (mainIdx < 0) {
|
|
1320
|
+
list.unshift({ id: "main" });
|
|
1321
|
+
mainIdx = 0;
|
|
1322
|
+
}
|
|
1323
|
+
list[mainIdx].subagents = { allowAgents: ["*"] };
|
|
1324
|
+
list[mainIdx].default = true;
|
|
1325
|
+
merged.push("main-team-mode");
|
|
1326
|
+
const feishu = config.channels?.feishu;
|
|
1327
|
+
if (feishu) {
|
|
1328
|
+
if (!feishu.accounts) feishu.accounts = {};
|
|
1329
|
+
const accounts = feishu.accounts;
|
|
1330
|
+
const defaultAccount = {};
|
|
1331
|
+
for (const key of [
|
|
1332
|
+
"dmPolicy",
|
|
1333
|
+
"allowFrom",
|
|
1334
|
+
"groupPolicy",
|
|
1335
|
+
"groupAllowFrom"
|
|
1336
|
+
]) if (feishu[key] !== void 0) defaultAccount[key] = feishu[key];
|
|
1337
|
+
if (Object.keys(defaultAccount).length > 0) {
|
|
1338
|
+
accounts.default = defaultAccount;
|
|
1339
|
+
merged.push("accounts.default");
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
if (backup.bindings && backup.bindings.length > 0) {
|
|
1344
|
+
config.bindings = backup.bindings;
|
|
1345
|
+
merged.push("bindings");
|
|
1346
|
+
}
|
|
1347
|
+
const backupAccounts = backup.channels?.feishu?.accounts;
|
|
1348
|
+
if (backupAccounts && Object.keys(backupAccounts).length > 0) {
|
|
1270
1349
|
if (!config.channels) config.channels = {};
|
|
1271
1350
|
const ch = config.channels;
|
|
1272
1351
|
if (!ch.feishu) ch.feishu = {};
|
|
1273
|
-
|
|
1352
|
+
const feishu = ch.feishu;
|
|
1353
|
+
if (!feishu.accounts) feishu.accounts = {};
|
|
1354
|
+
Object.assign(feishu.accounts, backupAccounts);
|
|
1355
|
+
merged.push("channels.feishu.accounts");
|
|
1274
1356
|
}
|
|
1275
1357
|
node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1276
|
-
|
|
1358
|
+
log(`merged from coreBackup: [${merged.join(", ") || "nothing"}]`);
|
|
1359
|
+
} else log("no coreBackup in resetData, skip multi-agent merge");
|
|
1277
1360
|
const expectedOrigins = Array.isArray(vars.expectedOrigins) ? vars.expectedOrigins : [];
|
|
1278
|
-
if (expectedOrigins.length
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1361
|
+
if (expectedOrigins.length === 0) {
|
|
1362
|
+
log("no expectedOrigins provided");
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
const config = JSON5.parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
1366
|
+
if (!config.gateway) config.gateway = {};
|
|
1367
|
+
const gw = config.gateway;
|
|
1368
|
+
if (!gw.controlUi) gw.controlUi = {};
|
|
1369
|
+
const cui = gw.controlUi;
|
|
1370
|
+
const current = Array.isArray(cui.allowedOrigins) ? cui.allowedOrigins.filter((o) => typeof o === "string") : [];
|
|
1371
|
+
if (current.includes("*")) {
|
|
1372
|
+
log("allowedOrigins already contains \"*\", skip origin merge");
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
const seen = new Set(current);
|
|
1376
|
+
const added = [];
|
|
1377
|
+
const mergedOrigins = [...current];
|
|
1378
|
+
for (const o of expectedOrigins) if (!seen.has(o)) {
|
|
1379
|
+
mergedOrigins.push(o);
|
|
1380
|
+
seen.add(o);
|
|
1381
|
+
added.push(o);
|
|
1293
1382
|
}
|
|
1383
|
+
cui.allowedOrigins = mergedOrigins;
|
|
1384
|
+
node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1385
|
+
log(`allowedOrigins: added ${added.length} (${JSON.stringify(added)}), total now ${mergedOrigins.length}`);
|
|
1294
1386
|
}
|
|
1295
|
-
/** Step
|
|
1296
|
-
function copyStartupScripts(srcDir, configDir) {
|
|
1387
|
+
/** Step 7: Copy startup scripts from template to agent dir. */
|
|
1388
|
+
function copyStartupScripts(srcDir, configDir, log) {
|
|
1297
1389
|
const srcScriptsDir = node_path.default.join(srcDir, "scripts");
|
|
1298
1390
|
const targetScriptsDir = node_path.default.join(configDir, "scripts");
|
|
1299
|
-
if (!node_fs.default.existsSync(srcScriptsDir))
|
|
1391
|
+
if (!node_fs.default.existsSync(srcScriptsDir)) {
|
|
1392
|
+
log(`no scripts/ in template, skip`);
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1300
1395
|
if (!node_fs.default.existsSync(targetScriptsDir)) node_fs.default.mkdirSync(targetScriptsDir, { recursive: true });
|
|
1301
1396
|
shell(`cp -r '${srcScriptsDir}'/* '${targetScriptsDir}/'`, 1e4);
|
|
1397
|
+
log(`copied scripts/* -> ${targetScriptsDir}`);
|
|
1302
1398
|
}
|
|
1303
1399
|
/**
|
|
1304
|
-
* Step
|
|
1400
|
+
* Step 8: Install extensions from OSS tar.gz or fall back to `openclaw plugins update`.
|
|
1401
|
+
*
|
|
1402
|
+
* 1. Derive the OSS URL from the openclaw version in the template config
|
|
1403
|
+
* 2. Download tar.gz → extract to a staging dir
|
|
1404
|
+
* 3. For each included extension, backup (mv) the user's existing copy
|
|
1405
|
+
* to a temp dir — extensions NOT in the archive are left untouched
|
|
1406
|
+
* 4. Move the fresh extensions from staging to the target dir
|
|
1407
|
+
*
|
|
1408
|
+
* This bypasses npm entirely and avoids the schema-priority mismatch where
|
|
1409
|
+
* `openclaw doctor --fix` would validate config against the bundled feishu
|
|
1410
|
+
* plugin's strict schema before openclaw-lark is installed.
|
|
1305
1411
|
*
|
|
1306
|
-
*
|
|
1307
|
-
* we apply the same idle-timeout + retry strategy.
|
|
1412
|
+
* Falls back to `openclaw plugins update --all` if the OSS download fails.
|
|
1308
1413
|
*/
|
|
1309
|
-
|
|
1414
|
+
function installExtensions(configDir, log) {
|
|
1415
|
+
const ossUrl = EXTENSIONS_OSS_URL;
|
|
1416
|
+
const targetExtDir = node_path.default.join(configDir, "extensions");
|
|
1417
|
+
const tmpDir = node_path.default.join(configDir, `.ext-reset-tmp-${Date.now()}`);
|
|
1418
|
+
const tarPath = node_path.default.join(tmpDir, "extensions.tar.gz");
|
|
1419
|
+
node_fs.default.mkdirSync(tmpDir, { recursive: true });
|
|
1310
1420
|
try {
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1421
|
+
log(`downloading extensions from ${ossUrl}`);
|
|
1422
|
+
const dlStart = Date.now();
|
|
1423
|
+
shell(`curl -fsSL '${ossUrl}' -o '${tarPath}'`, 5 * 6e4);
|
|
1424
|
+
const size = node_fs.default.statSync(tarPath).size;
|
|
1425
|
+
log(`download done in ${Date.now() - dlStart}ms (${(size / 1024 / 1024).toFixed(1)}MB)`);
|
|
1426
|
+
shell(`tar -xzf '${tarPath}' -C '${tmpDir}'`, 6e4);
|
|
1427
|
+
const stagedExtDir = node_path.default.join(tmpDir, "extensions");
|
|
1428
|
+
if (!node_fs.default.existsSync(stagedExtDir)) throw new Error("tar.gz does not contain an extensions/ directory");
|
|
1429
|
+
const extNames = node_fs.default.readdirSync(stagedExtDir).filter((name) => node_fs.default.statSync(node_path.default.join(stagedExtDir, name)).isDirectory());
|
|
1430
|
+
log(`archive contains ${extNames.length} extension(s): ${extNames.join(", ")}`);
|
|
1431
|
+
for (const name of extNames) {
|
|
1432
|
+
const target = node_path.default.join(targetExtDir, name);
|
|
1433
|
+
if (node_fs.default.existsSync(target)) node_fs.default.rmSync(target, {
|
|
1434
|
+
recursive: true,
|
|
1435
|
+
force: true
|
|
1436
|
+
});
|
|
1437
|
+
node_fs.default.renameSync(node_path.default.join(stagedExtDir, name), target);
|
|
1438
|
+
log(` ${name}: installed`);
|
|
1439
|
+
}
|
|
1440
|
+
const packinfo = node_path.default.join(stagedExtDir, ".packinfo.json");
|
|
1441
|
+
if (node_fs.default.existsSync(packinfo)) {
|
|
1442
|
+
node_fs.default.copyFileSync(packinfo, node_path.default.join(targetExtDir, ".packinfo.json"));
|
|
1443
|
+
log(`packinfo: ${node_fs.default.readFileSync(packinfo, "utf-8").trim()}`);
|
|
1444
|
+
}
|
|
1445
|
+
log("extensions installed from OSS successfully");
|
|
1446
|
+
} finally {
|
|
1447
|
+
try {
|
|
1448
|
+
node_fs.default.rmSync(tmpDir, {
|
|
1449
|
+
recursive: true,
|
|
1450
|
+
force: true
|
|
1451
|
+
});
|
|
1452
|
+
} catch {}
|
|
1453
|
+
}
|
|
1319
1454
|
}
|
|
1320
|
-
/** Step
|
|
1321
|
-
function writeSecretsAndRestart(vars, resetData, configDir) {
|
|
1322
|
-
if (resetData.secretsContent && vars.secretsFilePath)
|
|
1323
|
-
|
|
1455
|
+
/** Step 9: Write secrets/provider key files and restart openclaw. */
|
|
1456
|
+
function writeSecretsAndRestart(vars, resetData, configDir, log) {
|
|
1457
|
+
if (resetData.secretsContent && vars.secretsFilePath) {
|
|
1458
|
+
writeFile(vars.secretsFilePath, resetData.secretsContent);
|
|
1459
|
+
log(`wrote secrets to ${vars.secretsFilePath}`);
|
|
1460
|
+
}
|
|
1461
|
+
if (resetData.providerKeyContent && vars.providerFilePath) {
|
|
1462
|
+
writeFile(vars.providerFilePath, resetData.providerKeyContent);
|
|
1463
|
+
log(`wrote provider key to ${vars.providerFilePath}`);
|
|
1464
|
+
}
|
|
1324
1465
|
const restartScript = node_path.default.join(configDir, "scripts", "restart.sh");
|
|
1325
|
-
if (fileExists(restartScript))
|
|
1466
|
+
if (fileExists(restartScript)) {
|
|
1467
|
+
const t = Date.now();
|
|
1468
|
+
shell(`bash '${restartScript}'`, 3e4);
|
|
1469
|
+
log(`restart.sh done in ${Date.now() - t}ms`);
|
|
1470
|
+
} else log(`no restart.sh at ${restartScript}, skip`);
|
|
1326
1471
|
}
|
|
1327
1472
|
/**
|
|
1328
|
-
* Run the
|
|
1473
|
+
* Run the 9-step reset process. Called from the worker entry point.
|
|
1329
1474
|
*
|
|
1330
1475
|
* Each step is an independent function. The orchestrator handles progress
|
|
1331
1476
|
* reporting, error handling, and process-level exception guards.
|
|
@@ -1334,49 +1479,66 @@ function writeSecretsAndRestart(vars, resetData, configDir) {
|
|
|
1334
1479
|
* (see TEMPLATE_DIR) and synced from the miaoda-openclaw-template repo via
|
|
1335
1480
|
* scripts/sync-template.sh, so no runtime download is required.
|
|
1336
1481
|
*/
|
|
1337
|
-
|
|
1482
|
+
function runReset(input, taskId, resultFile) {
|
|
1338
1483
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1339
1484
|
const { configPath, vars, resetData } = input;
|
|
1340
1485
|
const configDir = node_path.default.dirname(configPath);
|
|
1341
1486
|
const srcDir = TEMPLATE_DIR;
|
|
1342
1487
|
let currentStep = 0;
|
|
1488
|
+
let stepStartedAt = Date.now();
|
|
1489
|
+
const log = makeLogger(resetLogFile(taskId));
|
|
1490
|
+
log(`=== reset started, taskId=${taskId}, pid=${process.pid} ===`);
|
|
1491
|
+
log(`configPath=${configPath}, configDir=${configDir}, templateDir=${srcDir}`);
|
|
1343
1492
|
if (!node_fs.default.existsSync(node_path.default.join(srcDir, "openclaw.json"))) {
|
|
1344
|
-
|
|
1493
|
+
const err = `bundled template not found at ${srcDir}`;
|
|
1494
|
+
log(`ERROR: ${err}`);
|
|
1495
|
+
markFailed(resultFile, 0, err, startedAt);
|
|
1345
1496
|
process.exit(1);
|
|
1346
1497
|
}
|
|
1347
1498
|
process.on("uncaughtException", (err) => {
|
|
1499
|
+
log(`FATAL uncaughtException: ${err.message}\n${err.stack ?? ""}`);
|
|
1348
1500
|
markFailed(resultFile, currentStep, `uncaught exception: ${err.message}`, startedAt);
|
|
1349
1501
|
process.exit(1);
|
|
1350
1502
|
});
|
|
1351
1503
|
process.on("unhandledRejection", (reason) => {
|
|
1504
|
+
log(`FATAL unhandledRejection: ${String(reason)}`);
|
|
1352
1505
|
markFailed(resultFile, currentStep, `unhandled rejection: ${reason}`, startedAt);
|
|
1353
1506
|
process.exit(1);
|
|
1354
1507
|
});
|
|
1355
|
-
/** Advance to the next step, updating the progress file. */
|
|
1508
|
+
/** Advance to the next step, updating the progress file and logging a boundary. */
|
|
1356
1509
|
const step = (n) => {
|
|
1510
|
+
if (currentStep > 0) log(`step ${currentStep} "${STEPS[currentStep - 1]}" done in ${Date.now() - stepStartedAt}ms`);
|
|
1357
1511
|
currentStep = n;
|
|
1512
|
+
stepStartedAt = Date.now();
|
|
1513
|
+
log(`--- step ${n}/${TOTAL_STEPS}: ${STEPS[n - 1]} ---`);
|
|
1358
1514
|
updateProgress(resultFile, n, startedAt);
|
|
1359
1515
|
};
|
|
1360
1516
|
try {
|
|
1361
1517
|
step(1);
|
|
1362
|
-
backupCurrentConfig(configPath);
|
|
1518
|
+
backupCurrentConfig(configPath, log);
|
|
1363
1519
|
step(2);
|
|
1364
|
-
generateDefaultConfig(srcDir, configPath, resetData.templateVars);
|
|
1520
|
+
generateDefaultConfig(srcDir, configPath, resetData.templateVars, log);
|
|
1365
1521
|
step(3);
|
|
1366
|
-
killOpenclawProcesses();
|
|
1522
|
+
killOpenclawProcesses(log);
|
|
1367
1523
|
step(4);
|
|
1368
|
-
|
|
1524
|
+
waitForInitNpm(10 * 6e4, log);
|
|
1369
1525
|
step(5);
|
|
1370
|
-
|
|
1526
|
+
ensureOpenclawBinary(srcDir, configPath, log);
|
|
1371
1527
|
step(6);
|
|
1372
|
-
|
|
1528
|
+
mergeCoreBackupAndOrigins(configPath, vars, resetData, log);
|
|
1373
1529
|
step(7);
|
|
1374
|
-
|
|
1530
|
+
copyStartupScripts(srcDir, configDir, log);
|
|
1375
1531
|
step(8);
|
|
1376
|
-
|
|
1532
|
+
installExtensions(configDir, log);
|
|
1533
|
+
step(9);
|
|
1534
|
+
writeSecretsAndRestart(vars, resetData, configDir, log);
|
|
1535
|
+
log(`step 9 "${STEPS[8]}" done in ${Date.now() - stepStartedAt}ms`);
|
|
1536
|
+
log("=== reset completed successfully ===");
|
|
1377
1537
|
markDone(resultFile, startedAt);
|
|
1378
1538
|
} catch (e) {
|
|
1379
|
-
|
|
1539
|
+
const err = e.message;
|
|
1540
|
+
log(`ERROR in step ${currentStep} "${STEPS[currentStep - 1] ?? "init"}" after ${Date.now() - stepStartedAt}ms: ${err}\n${e.stack ?? ""}`);
|
|
1541
|
+
markFailed(resultFile, currentStep, err, startedAt);
|
|
1380
1542
|
process.exit(1);
|
|
1381
1543
|
}
|
|
1382
1544
|
}
|
|
@@ -1388,7 +1550,7 @@ async function runReset(input, taskId, resultFile) {
|
|
|
1388
1550
|
* Returns immediately on terminal states (done/failed).
|
|
1389
1551
|
*/
|
|
1390
1552
|
function getResetTask(taskId) {
|
|
1391
|
-
const resultFile =
|
|
1553
|
+
const resultFile = resetResultFile(taskId);
|
|
1392
1554
|
const deadline = Date.now() + 3e4;
|
|
1393
1555
|
while (Date.now() < deadline) {
|
|
1394
1556
|
if (!node_fs.default.existsSync(resultFile)) {
|
|
@@ -1435,16 +1597,6 @@ switch (mode) {
|
|
|
1435
1597
|
else console.log(JSON.stringify(runRepair(input)));
|
|
1436
1598
|
break;
|
|
1437
1599
|
}
|
|
1438
|
-
case "backup": {
|
|
1439
|
-
const ctx = args.find((a) => a.startsWith("--ctx="))?.slice(6);
|
|
1440
|
-
if (!ctx) {
|
|
1441
|
-
console.error("Error: --ctx=<base64> is required");
|
|
1442
|
-
node_process.default.exit(1);
|
|
1443
|
-
}
|
|
1444
|
-
const input = JSON.parse(Buffer.from(ctx, "base64").toString("utf-8"));
|
|
1445
|
-
console.log(JSON.stringify(runBackup(input)));
|
|
1446
|
-
break;
|
|
1447
|
-
}
|
|
1448
1600
|
case "reset":
|
|
1449
1601
|
if (args.includes("--async")) {
|
|
1450
1602
|
const ctx = args.find((a) => a.startsWith("--ctx="))?.slice(6);
|
|
@@ -1460,11 +1612,8 @@ switch (mode) {
|
|
|
1460
1612
|
console.error("Error: --ctx=<base64> and --task-id=<id> are required for worker");
|
|
1461
1613
|
node_process.default.exit(1);
|
|
1462
1614
|
}
|
|
1463
|
-
const resultFile =
|
|
1464
|
-
runReset(JSON.parse(Buffer.from(ctx, "base64").toString("utf-8")), taskId, resultFile)
|
|
1465
|
-
console.error("runReset failed:", e);
|
|
1466
|
-
node_process.default.exit(1);
|
|
1467
|
-
});
|
|
1615
|
+
const resultFile = resetResultFile(taskId);
|
|
1616
|
+
runReset(JSON.parse(Buffer.from(ctx, "base64").toString("utf-8")), taskId, resultFile);
|
|
1468
1617
|
} else {
|
|
1469
1618
|
console.error("Usage: reset --async --ctx=<base64> | reset --worker --task-id=<id> --ctx=<base64>");
|
|
1470
1619
|
node_process.default.exit(1);
|
|
@@ -1480,7 +1629,7 @@ switch (mode) {
|
|
|
1480
1629
|
break;
|
|
1481
1630
|
}
|
|
1482
1631
|
default:
|
|
1483
|
-
console.error("Usage: mclaw-diagnose <check|repair|
|
|
1632
|
+
console.error("Usage: mclaw-diagnose <check|repair|reset|get_reset_task> [options]");
|
|
1484
1633
|
node_process.default.exit(1);
|
|
1485
1634
|
}
|
|
1486
1635
|
//#endregion
|
package/package.json
CHANGED
package/template/openclaw.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"lastTouchedVersion": "2026.4.9",
|
|
4
|
-
"lastTouchedAt": "2026-04-
|
|
4
|
+
"lastTouchedAt": "2026-04-13T03:14:33.386Z"
|
|
5
5
|
},
|
|
6
6
|
"wizard": {
|
|
7
7
|
"lastRunAt": "2026-03-30T14:54:56.160Z",
|
|
@@ -448,14 +448,27 @@
|
|
|
448
448
|
"plugins": {
|
|
449
449
|
"allow": [
|
|
450
450
|
"openclaw-lark",
|
|
451
|
-
"openclaw-extension-miaoda",
|
|
452
451
|
"openclaw-extension-miaoda-coding",
|
|
453
|
-
"browser"
|
|
452
|
+
"browser",
|
|
453
|
+
"openclaw-mem0-plugin",
|
|
454
|
+
"openclaw-extension-miaoda"
|
|
454
455
|
],
|
|
455
456
|
"entries": {
|
|
456
457
|
"feishu": {
|
|
457
458
|
"enabled": false
|
|
458
459
|
},
|
|
460
|
+
"openclaw-lark": {
|
|
461
|
+
"enabled": true
|
|
462
|
+
},
|
|
463
|
+
"openclaw-extension-miaoda-coding": {
|
|
464
|
+
"enabled": true
|
|
465
|
+
},
|
|
466
|
+
"browser": {
|
|
467
|
+
"enabled": true
|
|
468
|
+
},
|
|
469
|
+
"openclaw-mem0-plugin": {
|
|
470
|
+
"enabled": false
|
|
471
|
+
},
|
|
459
472
|
"openclaw-extension-miaoda": {
|
|
460
473
|
"enabled": true,
|
|
461
474
|
"config": {
|
|
@@ -466,15 +479,6 @@
|
|
|
466
479
|
]
|
|
467
480
|
}
|
|
468
481
|
}
|
|
469
|
-
},
|
|
470
|
-
"openclaw-lark": {
|
|
471
|
-
"enabled": true
|
|
472
|
-
},
|
|
473
|
-
"openclaw-extension-miaoda-coding": {
|
|
474
|
-
"enabled": true
|
|
475
|
-
},
|
|
476
|
-
"browser": {
|
|
477
|
-
"enabled": true
|
|
478
482
|
}
|
|
479
483
|
},
|
|
480
484
|
"installs": {
|
|
@@ -491,31 +495,44 @@
|
|
|
491
495
|
"resolvedAt": "2026-04-03T06:27:44.706Z",
|
|
492
496
|
"installedAt": "2026-04-03T06:28:10.547Z"
|
|
493
497
|
},
|
|
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
498
|
"openclaw-extension-miaoda-coding": {
|
|
508
499
|
"source": "npm",
|
|
509
500
|
"spec": "@lark-apaas/openclaw-extension-miaoda-coding",
|
|
510
501
|
"installPath": "./extensions/openclaw-extension-miaoda-coding",
|
|
511
|
-
"version": "1.0.
|
|
502
|
+
"version": "1.0.11",
|
|
512
503
|
"resolvedName": "@lark-apaas/openclaw-extension-miaoda-coding",
|
|
513
|
-
"resolvedVersion": "1.0.
|
|
514
|
-
"resolvedSpec": "@lark-apaas/openclaw-extension-miaoda-coding@1.0.
|
|
515
|
-
"integrity": "sha512-
|
|
516
|
-
"shasum": "
|
|
517
|
-
"resolvedAt": "2026-04-
|
|
518
|
-
"installedAt": "2026-04-
|
|
504
|
+
"resolvedVersion": "1.0.11",
|
|
505
|
+
"resolvedSpec": "@lark-apaas/openclaw-extension-miaoda-coding@1.0.11",
|
|
506
|
+
"integrity": "sha512-B2i3n367cR37UVJrDCTZSBljxUYzDZ4gWf3dw79XBdaZcq0G88XnkgyFmJoZijQd/HQP13xNBlfcLk4Lz2c6Bg==",
|
|
507
|
+
"shasum": "a6bf5efcec70bbd139ee7a75ea183e4f9751fd39",
|
|
508
|
+
"resolvedAt": "2026-04-13T14:40:18.926Z",
|
|
509
|
+
"installedAt": "2026-04-13T14:40:18.926Z"
|
|
510
|
+
},
|
|
511
|
+
"openclaw-mem0-plugin": {
|
|
512
|
+
"source": "npm",
|
|
513
|
+
"spec": "@shareclz/openclaw-mem0-plugin@1.1.2",
|
|
514
|
+
"installPath": "./extensions/openclaw-mem0-plugin",
|
|
515
|
+
"installedAt": "2026-04-13T03:13:10.564Z",
|
|
516
|
+
"version": "1.1.2",
|
|
517
|
+
"resolvedVersion": "1.1.2",
|
|
518
|
+
"resolvedName": "@shareclz/openclaw-mem0-plugin",
|
|
519
|
+
"resolvedSpec": "@shareclz/openclaw-mem0-plugin@1.1.2",
|
|
520
|
+
"resolvedAt": "2026-04-13T03:13:10.564Z",
|
|
521
|
+
"integrity": "sha512-j6m8oN6ykVc3a1/Bw0o7LmXmNApiGk6wxafrT0zW9m4UiqZe8LM4Y2LzvGEAnKwFdkUIkaSzpl/8Qp7MPy/zcQ==",
|
|
522
|
+
"shasum": "b1bdaa44917ce9535ba1acddb145ea0ed5d26bae"
|
|
523
|
+
},
|
|
524
|
+
"openclaw-extension-miaoda": {
|
|
525
|
+
"source": "npm",
|
|
526
|
+
"installPath": "./extensions/openclaw-extension-miaoda",
|
|
527
|
+
"version": "1.0.10",
|
|
528
|
+
"installedAt": "2026-04-14T12:21:23.090Z",
|
|
529
|
+
"spec": "@lark-apaas/openclaw-extension-miaoda@1.0.10",
|
|
530
|
+
"resolvedVersion": "1.0.10",
|
|
531
|
+
"resolvedName": "@lark-apaas/openclaw-extension-miaoda",
|
|
532
|
+
"resolvedSpec": "@lark-apaas/openclaw-extension-miaoda@1.0.10",
|
|
533
|
+
"resolvedAt": "2026-04-14T12:21:23.090Z",
|
|
534
|
+
"integrity": "sha512-aWXIBdzcjUQAdthiUjLhgGp8G0TMVB3wpAzR3wjscDRX7XAo1zhpmcWkBM0JNdrle1i3e5eB4hxVcLfn1yooNw==",
|
|
535
|
+
"shasum": "5ef5f6ef972d90b43091c79e8a1038a3052cc1f5"
|
|
519
536
|
}
|
|
520
537
|
}
|
|
521
538
|
}
|