@lark-apaas/openclaw-scripts-diagnose-cli 0.1.1-alpha.7 → 0.1.1-alpha.9
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 +135 -53
- 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
|
@@ -198,6 +198,74 @@ function shell(cmd, timeoutMs = 1e4) {
|
|
|
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
|
+
}
|
|
201
269
|
//#endregion
|
|
202
270
|
//#region \0@oxc-project+runtime@0.121.0/helpers/decorate.js
|
|
203
271
|
function __decorate(decorators, target, key, desc) {
|
|
@@ -1078,7 +1146,6 @@ function startAsyncReset(ctxBase64) {
|
|
|
1078
1146
|
//#region src/reset.ts
|
|
1079
1147
|
const STEPS = [
|
|
1080
1148
|
"备份当前配置",
|
|
1081
|
-
"下载技术栈模板",
|
|
1082
1149
|
"生成默认配置",
|
|
1083
1150
|
"杀掉 openclaw 进程",
|
|
1084
1151
|
"重装 openclaw",
|
|
@@ -1089,7 +1156,14 @@ const STEPS = [
|
|
|
1089
1156
|
];
|
|
1090
1157
|
const TOTAL_STEPS = STEPS.length;
|
|
1091
1158
|
const CORE_BACKUP_PATH = "/home/gem/workspace/.force/openclaw/core-backup.json";
|
|
1092
|
-
|
|
1159
|
+
/**
|
|
1160
|
+
* Directory holding the bundled openclaw template (openclaw.json + scripts/).
|
|
1161
|
+
* Synced from git@code.byted.org:apaas/miaoda-openclaw-template.git via
|
|
1162
|
+
* scripts/sync-template.sh and published alongside dist/.
|
|
1163
|
+
*
|
|
1164
|
+
* At runtime, __dirname points to dist/, so the template lives one level up.
|
|
1165
|
+
*/
|
|
1166
|
+
const TEMPLATE_DIR = node_path.default.resolve(__dirname, "..", "template");
|
|
1093
1167
|
function writeResultFile(resultFile, result) {
|
|
1094
1168
|
const dir = node_path.default.dirname(resultFile);
|
|
1095
1169
|
if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
|
|
@@ -1143,16 +1217,7 @@ function backupCurrentConfig(configPath) {
|
|
|
1143
1217
|
} catch {}
|
|
1144
1218
|
node_fs.default.copyFileSync(configPath, configPath + ".bak." + (maxN + 1));
|
|
1145
1219
|
}
|
|
1146
|
-
/** Step 2:
|
|
1147
|
-
function downloadAndExtractTemplate(zipDownloadURL) {
|
|
1148
|
-
if (!node_fs.default.existsSync(TMP_RESET_DIR)) node_fs.default.mkdirSync(TMP_RESET_DIR, { recursive: true });
|
|
1149
|
-
const zipPath = node_path.default.join(TMP_RESET_DIR, "template.zip");
|
|
1150
|
-
if (zipDownloadURL.startsWith("http://") || zipDownloadURL.startsWith("https://")) shell(`curl -sSL -o '${zipPath}' '${zipDownloadURL}'`, 12e4);
|
|
1151
|
-
else shell(`cp "${zipDownloadURL}" '${zipPath}'`, 1e4);
|
|
1152
|
-
shell(`unzip -o '${zipPath}' -d '${TMP_RESET_DIR}'`, 6e4);
|
|
1153
|
-
return findSrcDir(TMP_RESET_DIR);
|
|
1154
|
-
}
|
|
1155
|
-
/** Step 3: Replace $$__XXX__ placeholders and write default config. */
|
|
1220
|
+
/** Step 2: Replace $$__XXX__ placeholders and write default config. */
|
|
1156
1221
|
function generateDefaultConfig(srcDir, configPath, templateVars) {
|
|
1157
1222
|
const srcConfigPath = node_path.default.join(srcDir, "openclaw.json");
|
|
1158
1223
|
if (!fileExists(srcConfigPath)) throw new Error("template openclaw.json not found at " + srcConfigPath);
|
|
@@ -1160,23 +1225,39 @@ function generateDefaultConfig(srcDir, configPath, templateVars) {
|
|
|
1160
1225
|
for (const [placeholder, value] of Object.entries(templateVars)) content = content.split(placeholder).join(value);
|
|
1161
1226
|
node_fs.default.writeFileSync(configPath, content, "utf-8");
|
|
1162
1227
|
}
|
|
1163
|
-
/** Step
|
|
1228
|
+
/** Step 3: Kill all openclaw processes. */
|
|
1164
1229
|
function killOpenclawProcesses() {
|
|
1165
1230
|
try {
|
|
1166
1231
|
shell("pkill -f openclaw-gateway || true", 5e3);
|
|
1167
1232
|
} catch {}
|
|
1168
1233
|
shell("sleep 2", 5e3);
|
|
1169
1234
|
}
|
|
1170
|
-
/**
|
|
1171
|
-
|
|
1235
|
+
/**
|
|
1236
|
+
* Step 4: Reinstall openclaw to the version specified in template.
|
|
1237
|
+
*
|
|
1238
|
+
* `npm i -g` contends on ~/.npm cache/lock with any concurrent npm (e.g. the
|
|
1239
|
+
* sandbox's own init_sandbox.sh busy installing plugin deps). Using
|
|
1240
|
+
* `shellUntilIdle` lets slow-but-progressing installs run freely (we only kill
|
|
1241
|
+
* on genuine idleness), and `retryUntilDeadline` recovers when we *are* killed
|
|
1242
|
+
* because another npm was holding the lock — the next attempt is likely to
|
|
1243
|
+
* succeed once that npm completes.
|
|
1244
|
+
*/
|
|
1245
|
+
async function reinstallOpenclaw(srcDir) {
|
|
1172
1246
|
const version = loadJSON5().parse(node_fs.default.readFileSync(node_path.default.join(srcDir, "openclaw.json"), "utf-8")).meta?.lastTouchedVersion;
|
|
1173
1247
|
try {
|
|
1174
1248
|
shell("npm uninstall -g openclaw 2>/dev/null || true", 3e4);
|
|
1175
1249
|
} catch {}
|
|
1176
|
-
|
|
1250
|
+
const installCmd = `npm i -g openclaw@${version || "latest"} --loglevel=http --prefer-offline`;
|
|
1251
|
+
await retryUntilDeadline(() => shellUntilIdle(installCmd, {
|
|
1252
|
+
idleMs: 9e4,
|
|
1253
|
+
maxTotalMs: 6e5
|
|
1254
|
+
}), {
|
|
1255
|
+
maxTotalMs: 12e5,
|
|
1256
|
+
betweenAttemptsMs: 1e4
|
|
1257
|
+
});
|
|
1177
1258
|
shell("openclaw doctor --fix", 12e4);
|
|
1178
1259
|
}
|
|
1179
|
-
/** Step
|
|
1260
|
+
/** Step 5: Merge core-backup.json into config + ensure allowedOrigins. */
|
|
1180
1261
|
function mergeCoreBackupAndOrigins(configPath, vars) {
|
|
1181
1262
|
const JSON5 = loadJSON5();
|
|
1182
1263
|
if (fileExists(CORE_BACKUP_PATH)) {
|
|
@@ -1211,7 +1292,7 @@ function mergeCoreBackupAndOrigins(configPath, vars) {
|
|
|
1211
1292
|
node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1212
1293
|
}
|
|
1213
1294
|
}
|
|
1214
|
-
/** Step
|
|
1295
|
+
/** Step 6: Copy startup scripts from template to agent dir. */
|
|
1215
1296
|
function copyStartupScripts(srcDir, configDir) {
|
|
1216
1297
|
const srcScriptsDir = node_path.default.join(srcDir, "scripts");
|
|
1217
1298
|
const targetScriptsDir = node_path.default.join(configDir, "scripts");
|
|
@@ -1219,39 +1300,50 @@ function copyStartupScripts(srcDir, configDir) {
|
|
|
1219
1300
|
if (!node_fs.default.existsSync(targetScriptsDir)) node_fs.default.mkdirSync(targetScriptsDir, { recursive: true });
|
|
1220
1301
|
shell(`cp -r '${srcScriptsDir}'/* '${targetScriptsDir}/'`, 1e4);
|
|
1221
1302
|
}
|
|
1222
|
-
/**
|
|
1223
|
-
|
|
1303
|
+
/**
|
|
1304
|
+
* Step 7: Reinstall all plugins via openclaw CLI.
|
|
1305
|
+
*
|
|
1306
|
+
* Same contention story as Step 4 — under the hood this shells out to npm, so
|
|
1307
|
+
* we apply the same idle-timeout + retry strategy.
|
|
1308
|
+
*/
|
|
1309
|
+
async function reinstallPlugins() {
|
|
1224
1310
|
try {
|
|
1225
|
-
|
|
1311
|
+
await retryUntilDeadline(() => shellUntilIdle("openclaw plugins update --all", {
|
|
1312
|
+
idleMs: 12e4,
|
|
1313
|
+
maxTotalMs: 6e5
|
|
1314
|
+
}), {
|
|
1315
|
+
maxTotalMs: 9e5,
|
|
1316
|
+
betweenAttemptsMs: 1e4
|
|
1317
|
+
});
|
|
1226
1318
|
} catch {}
|
|
1227
1319
|
}
|
|
1228
|
-
/** Step
|
|
1320
|
+
/** Step 8: Write secrets/provider key files and restart openclaw. */
|
|
1229
1321
|
function writeSecretsAndRestart(vars, resetData, configDir) {
|
|
1230
1322
|
if (resetData.secretsContent && vars.secretsFilePath) writeFile(vars.secretsFilePath, resetData.secretsContent);
|
|
1231
1323
|
if (resetData.providerKeyContent && vars.providerFilePath) writeFile(vars.providerFilePath, resetData.providerKeyContent);
|
|
1232
1324
|
const restartScript = node_path.default.join(configDir, "scripts", "restart.sh");
|
|
1233
1325
|
if (fileExists(restartScript)) shell(`bash '${restartScript}'`, 3e4);
|
|
1234
1326
|
}
|
|
1235
|
-
/** Clean up temporary reset directory. */
|
|
1236
|
-
function cleanup() {
|
|
1237
|
-
try {
|
|
1238
|
-
node_fs.default.rmSync(TMP_RESET_DIR, {
|
|
1239
|
-
recursive: true,
|
|
1240
|
-
force: true
|
|
1241
|
-
});
|
|
1242
|
-
} catch {}
|
|
1243
|
-
}
|
|
1244
1327
|
/**
|
|
1245
|
-
* Run the
|
|
1328
|
+
* Run the 8-step reset process. Called from the worker entry point.
|
|
1246
1329
|
*
|
|
1247
1330
|
* Each step is an independent function. The orchestrator handles progress
|
|
1248
1331
|
* reporting, error handling, and process-level exception guards.
|
|
1332
|
+
*
|
|
1333
|
+
* The openclaw.json / scripts/*.sh template files are bundled with this CLI
|
|
1334
|
+
* (see TEMPLATE_DIR) and synced from the miaoda-openclaw-template repo via
|
|
1335
|
+
* scripts/sync-template.sh, so no runtime download is required.
|
|
1249
1336
|
*/
|
|
1250
|
-
function runReset(input, taskId, resultFile) {
|
|
1337
|
+
async function runReset(input, taskId, resultFile) {
|
|
1251
1338
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1252
1339
|
const { configPath, vars, resetData } = input;
|
|
1253
1340
|
const configDir = node_path.default.dirname(configPath);
|
|
1341
|
+
const srcDir = TEMPLATE_DIR;
|
|
1254
1342
|
let currentStep = 0;
|
|
1343
|
+
if (!node_fs.default.existsSync(node_path.default.join(srcDir, "openclaw.json"))) {
|
|
1344
|
+
markFailed(resultFile, 0, `bundled template not found at ${srcDir}`, startedAt);
|
|
1345
|
+
process.exit(1);
|
|
1346
|
+
}
|
|
1255
1347
|
process.on("uncaughtException", (err) => {
|
|
1256
1348
|
markFailed(resultFile, currentStep, `uncaught exception: ${err.message}`, startedAt);
|
|
1257
1349
|
process.exit(1);
|
|
@@ -1269,38 +1361,25 @@ function runReset(input, taskId, resultFile) {
|
|
|
1269
1361
|
step(1);
|
|
1270
1362
|
backupCurrentConfig(configPath);
|
|
1271
1363
|
step(2);
|
|
1272
|
-
const srcDir = downloadAndExtractTemplate(resetData.zipDownloadURL);
|
|
1273
|
-
step(3);
|
|
1274
1364
|
generateDefaultConfig(srcDir, configPath, resetData.templateVars);
|
|
1275
|
-
step(
|
|
1365
|
+
step(3);
|
|
1276
1366
|
killOpenclawProcesses();
|
|
1367
|
+
step(4);
|
|
1368
|
+
await reinstallOpenclaw(srcDir);
|
|
1277
1369
|
step(5);
|
|
1278
|
-
reinstallOpenclaw(srcDir);
|
|
1279
|
-
step(6);
|
|
1280
1370
|
mergeCoreBackupAndOrigins(configPath, vars);
|
|
1281
|
-
step(
|
|
1371
|
+
step(6);
|
|
1282
1372
|
copyStartupScripts(srcDir, configDir);
|
|
1373
|
+
step(7);
|
|
1374
|
+
await reinstallPlugins();
|
|
1283
1375
|
step(8);
|
|
1284
|
-
reinstallPlugins();
|
|
1285
|
-
step(9);
|
|
1286
1376
|
writeSecretsAndRestart(vars, resetData, configDir);
|
|
1287
|
-
cleanup();
|
|
1288
1377
|
markDone(resultFile, startedAt);
|
|
1289
1378
|
} catch (e) {
|
|
1290
1379
|
markFailed(resultFile, currentStep, e.message, startedAt);
|
|
1291
1380
|
process.exit(1);
|
|
1292
1381
|
}
|
|
1293
1382
|
}
|
|
1294
|
-
/** Find the source directory within the extracted zip (openclaw.json location). */
|
|
1295
|
-
function findSrcDir(baseDir) {
|
|
1296
|
-
if (node_fs.default.existsSync(node_path.default.join(baseDir, "openclaw.json"))) return baseDir;
|
|
1297
|
-
const entries = node_fs.default.readdirSync(baseDir, { withFileTypes: true });
|
|
1298
|
-
for (const entry of entries) if (entry.isDirectory()) {
|
|
1299
|
-
const candidate = node_path.default.join(baseDir, entry.name);
|
|
1300
|
-
if (node_fs.default.existsSync(node_path.default.join(candidate, "openclaw.json"))) return candidate;
|
|
1301
|
-
}
|
|
1302
|
-
return baseDir;
|
|
1303
|
-
}
|
|
1304
1383
|
//#endregion
|
|
1305
1384
|
//#region src/get-reset-task.ts
|
|
1306
1385
|
/**
|
|
@@ -1382,7 +1461,10 @@ switch (mode) {
|
|
|
1382
1461
|
node_process.default.exit(1);
|
|
1383
1462
|
}
|
|
1384
1463
|
const resultFile = `/tmp/openclaw-reset-${taskId}.json`;
|
|
1385
|
-
runReset(JSON.parse(Buffer.from(ctx, "base64").toString("utf-8")), taskId, resultFile)
|
|
1464
|
+
runReset(JSON.parse(Buffer.from(ctx, "base64").toString("utf-8")), taskId, resultFile).catch((e) => {
|
|
1465
|
+
console.error("runReset failed:", e);
|
|
1466
|
+
node_process.default.exit(1);
|
|
1467
|
+
});
|
|
1386
1468
|
} else {
|
|
1387
1469
|
console.error("Usage: reset --async --ctx=<base64> | reset --worker --task-id=<id> --ctx=<base64>");
|
|
1388
1470
|
node_process.default.exit(1);
|
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.9",
|
|
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
|