@lark-apaas/openclaw-scripts-diagnose-cli 0.1.1-alpha.8 → 0.1.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +367 -120
- package/package.json +1 -1
- package/template/openclaw.json +49 -32
package/dist/index.cjs
CHANGED
|
@@ -192,14 +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
201
|
//#endregion
|
|
202
|
-
//#region \0@oxc-project+runtime@0.
|
|
202
|
+
//#region \0@oxc-project+runtime@0.115.0/helpers/decorate.js
|
|
203
203
|
function __decorate(decorators, target, key, desc) {
|
|
204
204
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
205
205
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -245,11 +245,15 @@ function findBackupFiles(configPath) {
|
|
|
245
245
|
}
|
|
246
246
|
/**
|
|
247
247
|
* Among backup files, find the one with the highest numeric suffix.
|
|
248
|
-
*
|
|
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)
|
|
249
253
|
*/
|
|
250
254
|
function findHighestBackup(backupFiles) {
|
|
251
255
|
if (backupFiles.length === 0) return null;
|
|
252
|
-
const bakRegex = /\.bak(\d*)$/;
|
|
256
|
+
const bakRegex = /\.bak\.?(\d*)$/;
|
|
253
257
|
let best = null;
|
|
254
258
|
for (const f of backupFiles) {
|
|
255
259
|
const match = bakRegex.exec(f);
|
|
@@ -686,7 +690,9 @@ let AllowedOriginsRule = class AllowedOriginsRule extends DiagnoseRule {
|
|
|
686
690
|
validate(ctx) {
|
|
687
691
|
const expected = getExpectedOrigins(ctx.vars);
|
|
688
692
|
if (expected.length === 0) return { pass: true };
|
|
689
|
-
const
|
|
693
|
+
const current = getCurrentOrigins(ctx.config);
|
|
694
|
+
if (hasWildcard(current)) return { pass: true };
|
|
695
|
+
const missing = findMissing(current, expected);
|
|
690
696
|
if (missing.length === 0) return { pass: true };
|
|
691
697
|
return {
|
|
692
698
|
pass: false,
|
|
@@ -696,6 +702,7 @@ let AllowedOriginsRule = class AllowedOriginsRule extends DiagnoseRule {
|
|
|
696
702
|
repair(ctx) {
|
|
697
703
|
const expected = getExpectedOrigins(ctx.vars);
|
|
698
704
|
const current = getCurrentOrigins(ctx.config);
|
|
705
|
+
if (hasWildcard(current)) return;
|
|
699
706
|
const missing = findMissing(current, expected);
|
|
700
707
|
if (missing.length > 0) {
|
|
701
708
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -735,6 +742,10 @@ function findMissing(current, expected) {
|
|
|
735
742
|
const set = new Set(current);
|
|
736
743
|
return expected.filter((o) => !set.has(o));
|
|
737
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
|
+
}
|
|
738
749
|
//#endregion
|
|
739
750
|
//#region src/rules/jwt-token.ts
|
|
740
751
|
let JwtTokenRule = class JwtTokenRule extends DiagnoseRule {
|
|
@@ -985,45 +996,34 @@ function runRepair(input) {
|
|
|
985
996
|
}
|
|
986
997
|
}
|
|
987
998
|
//#endregion
|
|
988
|
-
//#region src/
|
|
989
|
-
|
|
990
|
-
|
|
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) {
|
|
991
1017
|
try {
|
|
992
|
-
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`;
|
|
993
1023
|
try {
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
error: "config validation failed"
|
|
998
|
-
};
|
|
999
|
-
} catch (e) {
|
|
1000
|
-
return {
|
|
1001
|
-
success: false,
|
|
1002
|
-
error: "config validate command failed: " + e.message
|
|
1003
|
-
};
|
|
1004
|
-
}
|
|
1005
|
-
if (!fileExists(configPath)) return {
|
|
1006
|
-
success: false,
|
|
1007
|
-
error: "config file not found: " + configPath
|
|
1008
|
-
};
|
|
1009
|
-
const config = loadJSON5().parse(readFile(configPath));
|
|
1010
|
-
const backup = { _backup_meta: { created_at: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
1011
|
-
if (config.agents) backup.agents = config.agents;
|
|
1012
|
-
if (config.bindings) backup.bindings = config.bindings;
|
|
1013
|
-
const feishu = config.channels?.feishu;
|
|
1014
|
-
if (feishu?.accounts) backup.channels = { feishu: { accounts: feishu.accounts } };
|
|
1015
|
-
const backupDir = node_path.default.dirname(BACKUP_PATH);
|
|
1016
|
-
if (!node_fs.default.existsSync(backupDir)) node_fs.default.mkdirSync(backupDir, { recursive: true });
|
|
1017
|
-
const tmpPath = BACKUP_PATH + ".tmp";
|
|
1018
|
-
node_fs.default.writeFileSync(tmpPath, JSON.stringify(backup, null, 2), "utf-8");
|
|
1019
|
-
node_fs.default.renameSync(tmpPath, BACKUP_PATH);
|
|
1020
|
-
return { success: true };
|
|
1021
|
-
} catch (e) {
|
|
1022
|
-
return {
|
|
1023
|
-
success: false,
|
|
1024
|
-
error: "backup failed: " + e.message
|
|
1025
|
-
};
|
|
1026
|
-
}
|
|
1024
|
+
node_fs.default.appendFileSync(logFile, line);
|
|
1025
|
+
} catch {}
|
|
1026
|
+
};
|
|
1027
1027
|
}
|
|
1028
1028
|
//#endregion
|
|
1029
1029
|
//#region src/reset-async.ts
|
|
@@ -1034,7 +1034,9 @@ function runBackup(input) {
|
|
|
1034
1034
|
*/
|
|
1035
1035
|
function startAsyncReset(ctxBase64) {
|
|
1036
1036
|
const taskId = (0, node_crypto.randomUUID)();
|
|
1037
|
-
const resultFile =
|
|
1037
|
+
const resultFile = resetResultFile(taskId);
|
|
1038
|
+
const log = makeLogger(resetLogFile(taskId));
|
|
1039
|
+
log(`=== startAsyncReset spawning worker for taskId=${taskId} ===`);
|
|
1038
1040
|
const initial = {
|
|
1039
1041
|
status: "running",
|
|
1040
1042
|
step: 0,
|
|
@@ -1058,6 +1060,7 @@ function startAsyncReset(ctxBase64) {
|
|
|
1058
1060
|
stdio: "ignore"
|
|
1059
1061
|
});
|
|
1060
1062
|
child.on("error", (err) => {
|
|
1063
|
+
log(`FATAL worker failed to start: ${err.message}`);
|
|
1061
1064
|
const failResult = {
|
|
1062
1065
|
status: "failed",
|
|
1063
1066
|
step: 0,
|
|
@@ -1072,6 +1075,7 @@ function startAsyncReset(ctxBase64) {
|
|
|
1072
1075
|
node_fs.default.renameSync(errTmpPath, resultFile);
|
|
1073
1076
|
});
|
|
1074
1077
|
child.unref();
|
|
1078
|
+
log(`spawned worker pid=${child.pid}`);
|
|
1075
1079
|
return { taskId };
|
|
1076
1080
|
}
|
|
1077
1081
|
//#endregion
|
|
@@ -1080,14 +1084,16 @@ const STEPS = [
|
|
|
1080
1084
|
"备份当前配置",
|
|
1081
1085
|
"生成默认配置",
|
|
1082
1086
|
"杀掉 openclaw 进程",
|
|
1083
|
-
"
|
|
1087
|
+
"等待沙箱初始化完成",
|
|
1088
|
+
"确认 openclaw 版本",
|
|
1084
1089
|
"合并核心备份配置",
|
|
1085
1090
|
"复制启动脚本",
|
|
1086
|
-
"
|
|
1091
|
+
"安装扩展",
|
|
1087
1092
|
"启动并验证"
|
|
1088
1093
|
];
|
|
1089
1094
|
const TOTAL_STEPS = STEPS.length;
|
|
1090
|
-
|
|
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";
|
|
1091
1097
|
/**
|
|
1092
1098
|
* Directory holding the bundled openclaw template (openclaw.json + scripts/).
|
|
1093
1099
|
* Synced from git@code.byted.org:apaas/miaoda-openclaw-template.git via
|
|
@@ -1134,8 +1140,11 @@ function markFailed(resultFile, step, error, startedAt) {
|
|
|
1134
1140
|
});
|
|
1135
1141
|
}
|
|
1136
1142
|
/** Step 1: Backup current config as openclaw.json.bak.N */
|
|
1137
|
-
function backupCurrentConfig(configPath) {
|
|
1138
|
-
if (!fileExists(configPath))
|
|
1143
|
+
function backupCurrentConfig(configPath, log) {
|
|
1144
|
+
if (!fileExists(configPath)) {
|
|
1145
|
+
log("no existing config, skip backup");
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1139
1148
|
const dir = node_path.default.dirname(configPath);
|
|
1140
1149
|
let maxN = 0;
|
|
1141
1150
|
try {
|
|
@@ -1147,90 +1156,321 @@ function backupCurrentConfig(configPath) {
|
|
|
1147
1156
|
}
|
|
1148
1157
|
}
|
|
1149
1158
|
} catch {}
|
|
1150
|
-
|
|
1159
|
+
const bakPath = configPath + ".bak." + (maxN + 1);
|
|
1160
|
+
node_fs.default.copyFileSync(configPath, bakPath);
|
|
1161
|
+
log(`backed up to ${bakPath}`);
|
|
1151
1162
|
}
|
|
1152
1163
|
/** Step 2: Replace $$__XXX__ placeholders and write default config. */
|
|
1153
|
-
function generateDefaultConfig(srcDir, configPath, templateVars) {
|
|
1164
|
+
function generateDefaultConfig(srcDir, configPath, templateVars, log) {
|
|
1154
1165
|
const srcConfigPath = node_path.default.join(srcDir, "openclaw.json");
|
|
1155
1166
|
if (!fileExists(srcConfigPath)) throw new Error("template openclaw.json not found at " + srcConfigPath);
|
|
1156
1167
|
let content = node_fs.default.readFileSync(srcConfigPath, "utf-8");
|
|
1157
|
-
|
|
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
|
+
}
|
|
1158
1174
|
node_fs.default.writeFileSync(configPath, content, "utf-8");
|
|
1175
|
+
log(`wrote ${configPath} (${replaced} placeholder(s) replaced, ${Object.keys(templateVars).length} provided)`);
|
|
1159
1176
|
}
|
|
1160
1177
|
/** Step 3: Kill all openclaw processes. */
|
|
1161
|
-
function killOpenclawProcesses() {
|
|
1178
|
+
function killOpenclawProcesses(log) {
|
|
1162
1179
|
try {
|
|
1163
1180
|
shell("pkill -f openclaw-gateway || true", 5e3);
|
|
1164
1181
|
} catch {}
|
|
1165
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`);
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Step 5: Ensure openclaw binary is at the template's recommended version.
|
|
1220
|
+
*
|
|
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.
|
|
1226
|
+
*/
|
|
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
|
+
}
|
|
1166
1262
|
}
|
|
1167
|
-
/**
|
|
1168
|
-
function
|
|
1169
|
-
const version = loadJSON5().parse(node_fs.default.readFileSync(node_path.default.join(srcDir, "openclaw.json"), "utf-8")).meta?.lastTouchedVersion;
|
|
1263
|
+
/** Check if openclaw command exists (regardless of version). */
|
|
1264
|
+
function isOpenclawInstalled() {
|
|
1170
1265
|
try {
|
|
1171
|
-
shell("
|
|
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) {
|
|
1274
|
+
try {
|
|
1275
|
+
shell("npm uninstall -g openclaw 2>/dev/null || true", 6e4);
|
|
1276
|
+
} catch {}
|
|
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");
|
|
1172
1280
|
} catch {}
|
|
1173
|
-
|
|
1174
|
-
|
|
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`);
|
|
1175
1286
|
}
|
|
1176
|
-
/**
|
|
1177
|
-
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) {
|
|
1178
1297
|
const JSON5 = loadJSON5();
|
|
1179
|
-
|
|
1180
|
-
|
|
1298
|
+
const backup = resetData.coreBackup;
|
|
1299
|
+
if (backup) {
|
|
1181
1300
|
const config = JSON5.parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
1182
|
-
|
|
1183
|
-
if (backup.
|
|
1184
|
-
|
|
1185
|
-
|
|
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) {
|
|
1186
1349
|
if (!config.channels) config.channels = {};
|
|
1187
1350
|
const ch = config.channels;
|
|
1188
1351
|
if (!ch.feishu) ch.feishu = {};
|
|
1189
|
-
|
|
1352
|
+
const feishu = ch.feishu;
|
|
1353
|
+
if (!feishu.accounts) feishu.accounts = {};
|
|
1354
|
+
Object.assign(feishu.accounts, backupAccounts);
|
|
1355
|
+
merged.push("channels.feishu.accounts");
|
|
1190
1356
|
}
|
|
1191
1357
|
node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1192
|
-
|
|
1358
|
+
log(`merged from coreBackup: [${merged.join(", ") || "nothing"}]`);
|
|
1359
|
+
} else log("no coreBackup in resetData, skip multi-agent merge");
|
|
1193
1360
|
const expectedOrigins = Array.isArray(vars.expectedOrigins) ? vars.expectedOrigins : [];
|
|
1194
|
-
if (expectedOrigins.length
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
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);
|
|
1209
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}`);
|
|
1210
1386
|
}
|
|
1211
|
-
/** Step
|
|
1212
|
-
function copyStartupScripts(srcDir, configDir) {
|
|
1387
|
+
/** Step 7: Copy startup scripts from template to agent dir. */
|
|
1388
|
+
function copyStartupScripts(srcDir, configDir, log) {
|
|
1213
1389
|
const srcScriptsDir = node_path.default.join(srcDir, "scripts");
|
|
1214
1390
|
const targetScriptsDir = node_path.default.join(configDir, "scripts");
|
|
1215
|
-
if (!node_fs.default.existsSync(srcScriptsDir))
|
|
1391
|
+
if (!node_fs.default.existsSync(srcScriptsDir)) {
|
|
1392
|
+
log(`no scripts/ in template, skip`);
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1216
1395
|
if (!node_fs.default.existsSync(targetScriptsDir)) node_fs.default.mkdirSync(targetScriptsDir, { recursive: true });
|
|
1217
1396
|
shell(`cp -r '${srcScriptsDir}'/* '${targetScriptsDir}/'`, 1e4);
|
|
1397
|
+
log(`copied scripts/* -> ${targetScriptsDir}`);
|
|
1218
1398
|
}
|
|
1219
|
-
/**
|
|
1220
|
-
|
|
1399
|
+
/**
|
|
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.
|
|
1411
|
+
*
|
|
1412
|
+
* Falls back to `openclaw plugins update --all` if the OSS download fails.
|
|
1413
|
+
*/
|
|
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 });
|
|
1221
1420
|
try {
|
|
1222
|
-
|
|
1223
|
-
|
|
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
|
+
}
|
|
1224
1454
|
}
|
|
1225
|
-
/** Step
|
|
1226
|
-
function writeSecretsAndRestart(vars, resetData, configDir) {
|
|
1227
|
-
if (resetData.secretsContent && vars.secretsFilePath)
|
|
1228
|
-
|
|
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
|
+
}
|
|
1229
1465
|
const restartScript = node_path.default.join(configDir, "scripts", "restart.sh");
|
|
1230
|
-
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`);
|
|
1231
1471
|
}
|
|
1232
1472
|
/**
|
|
1233
|
-
* Run the
|
|
1473
|
+
* Run the 9-step reset process. Called from the worker entry point.
|
|
1234
1474
|
*
|
|
1235
1475
|
* Each step is an independent function. The orchestrator handles progress
|
|
1236
1476
|
* reporting, error handling, and process-level exception guards.
|
|
@@ -1245,43 +1485,60 @@ function runReset(input, taskId, resultFile) {
|
|
|
1245
1485
|
const configDir = node_path.default.dirname(configPath);
|
|
1246
1486
|
const srcDir = TEMPLATE_DIR;
|
|
1247
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}`);
|
|
1248
1492
|
if (!node_fs.default.existsSync(node_path.default.join(srcDir, "openclaw.json"))) {
|
|
1249
|
-
|
|
1493
|
+
const err = `bundled template not found at ${srcDir}`;
|
|
1494
|
+
log(`ERROR: ${err}`);
|
|
1495
|
+
markFailed(resultFile, 0, err, startedAt);
|
|
1250
1496
|
process.exit(1);
|
|
1251
1497
|
}
|
|
1252
1498
|
process.on("uncaughtException", (err) => {
|
|
1499
|
+
log(`FATAL uncaughtException: ${err.message}\n${err.stack ?? ""}`);
|
|
1253
1500
|
markFailed(resultFile, currentStep, `uncaught exception: ${err.message}`, startedAt);
|
|
1254
1501
|
process.exit(1);
|
|
1255
1502
|
});
|
|
1256
1503
|
process.on("unhandledRejection", (reason) => {
|
|
1504
|
+
log(`FATAL unhandledRejection: ${String(reason)}`);
|
|
1257
1505
|
markFailed(resultFile, currentStep, `unhandled rejection: ${reason}`, startedAt);
|
|
1258
1506
|
process.exit(1);
|
|
1259
1507
|
});
|
|
1260
|
-
/** Advance to the next step, updating the progress file. */
|
|
1508
|
+
/** Advance to the next step, updating the progress file and logging a boundary. */
|
|
1261
1509
|
const step = (n) => {
|
|
1510
|
+
if (currentStep > 0) log(`step ${currentStep} "${STEPS[currentStep - 1]}" done in ${Date.now() - stepStartedAt}ms`);
|
|
1262
1511
|
currentStep = n;
|
|
1512
|
+
stepStartedAt = Date.now();
|
|
1513
|
+
log(`--- step ${n}/${TOTAL_STEPS}: ${STEPS[n - 1]} ---`);
|
|
1263
1514
|
updateProgress(resultFile, n, startedAt);
|
|
1264
1515
|
};
|
|
1265
1516
|
try {
|
|
1266
1517
|
step(1);
|
|
1267
|
-
backupCurrentConfig(configPath);
|
|
1518
|
+
backupCurrentConfig(configPath, log);
|
|
1268
1519
|
step(2);
|
|
1269
|
-
generateDefaultConfig(srcDir, configPath, resetData.templateVars);
|
|
1520
|
+
generateDefaultConfig(srcDir, configPath, resetData.templateVars, log);
|
|
1270
1521
|
step(3);
|
|
1271
|
-
killOpenclawProcesses();
|
|
1522
|
+
killOpenclawProcesses(log);
|
|
1272
1523
|
step(4);
|
|
1273
|
-
|
|
1524
|
+
waitForInitNpm(10 * 6e4, log);
|
|
1274
1525
|
step(5);
|
|
1275
|
-
|
|
1526
|
+
ensureOpenclawBinary(srcDir, configPath, log);
|
|
1276
1527
|
step(6);
|
|
1277
|
-
|
|
1528
|
+
mergeCoreBackupAndOrigins(configPath, vars, resetData, log);
|
|
1278
1529
|
step(7);
|
|
1279
|
-
|
|
1530
|
+
copyStartupScripts(srcDir, configDir, log);
|
|
1280
1531
|
step(8);
|
|
1281
|
-
|
|
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 ===");
|
|
1282
1537
|
markDone(resultFile, startedAt);
|
|
1283
1538
|
} catch (e) {
|
|
1284
|
-
|
|
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);
|
|
1285
1542
|
process.exit(1);
|
|
1286
1543
|
}
|
|
1287
1544
|
}
|
|
@@ -1293,7 +1550,7 @@ function runReset(input, taskId, resultFile) {
|
|
|
1293
1550
|
* Returns immediately on terminal states (done/failed).
|
|
1294
1551
|
*/
|
|
1295
1552
|
function getResetTask(taskId) {
|
|
1296
|
-
const resultFile =
|
|
1553
|
+
const resultFile = resetResultFile(taskId);
|
|
1297
1554
|
const deadline = Date.now() + 3e4;
|
|
1298
1555
|
while (Date.now() < deadline) {
|
|
1299
1556
|
if (!node_fs.default.existsSync(resultFile)) {
|
|
@@ -1340,16 +1597,6 @@ switch (mode) {
|
|
|
1340
1597
|
else console.log(JSON.stringify(runRepair(input)));
|
|
1341
1598
|
break;
|
|
1342
1599
|
}
|
|
1343
|
-
case "backup": {
|
|
1344
|
-
const ctx = args.find((a) => a.startsWith("--ctx="))?.slice(6);
|
|
1345
|
-
if (!ctx) {
|
|
1346
|
-
console.error("Error: --ctx=<base64> is required");
|
|
1347
|
-
node_process.default.exit(1);
|
|
1348
|
-
}
|
|
1349
|
-
const input = JSON.parse(Buffer.from(ctx, "base64").toString("utf-8"));
|
|
1350
|
-
console.log(JSON.stringify(runBackup(input)));
|
|
1351
|
-
break;
|
|
1352
|
-
}
|
|
1353
1600
|
case "reset":
|
|
1354
1601
|
if (args.includes("--async")) {
|
|
1355
1602
|
const ctx = args.find((a) => a.startsWith("--ctx="))?.slice(6);
|
|
@@ -1365,7 +1612,7 @@ switch (mode) {
|
|
|
1365
1612
|
console.error("Error: --ctx=<base64> and --task-id=<id> are required for worker");
|
|
1366
1613
|
node_process.default.exit(1);
|
|
1367
1614
|
}
|
|
1368
|
-
const resultFile =
|
|
1615
|
+
const resultFile = resetResultFile(taskId);
|
|
1369
1616
|
runReset(JSON.parse(Buffer.from(ctx, "base64").toString("utf-8")), taskId, resultFile);
|
|
1370
1617
|
} else {
|
|
1371
1618
|
console.error("Usage: reset --async --ctx=<base64> | reset --worker --task-id=<id> --ctx=<base64>");
|
|
@@ -1382,7 +1629,7 @@ switch (mode) {
|
|
|
1382
1629
|
break;
|
|
1383
1630
|
}
|
|
1384
1631
|
default:
|
|
1385
|
-
console.error("Usage: mclaw-diagnose <check|repair|
|
|
1632
|
+
console.error("Usage: mclaw-diagnose <check|repair|reset|get_reset_task> [options]");
|
|
1386
1633
|
node_process.default.exit(1);
|
|
1387
1634
|
}
|
|
1388
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
|
}
|