@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 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
- const TMP_RESET_DIR = "/tmp/openclaw-reset";
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: Download/copy template zip and extract to TMP_RESET_DIR. Returns srcDir. */
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 4: Kill all openclaw processes. */
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
- /** Step 5: Reinstall openclaw to the version specified in template. */
1171
- function reinstallOpenclaw(srcDir) {
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
- shell(`npm i -g openclaw@${version || "latest"}`, 6e5);
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 6: Merge core-backup.json into config + ensure allowedOrigins. */
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 7: Copy startup scripts from template to agent dir. */
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
- /** Step 8: Reinstall all plugins via openclaw CLI. */
1223
- function reinstallPlugins() {
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
- shell("openclaw plugins update --all", 3e5);
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 9: Write secrets/provider key files and restart openclaw. */
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 9-step reset process. Called from the worker entry point.
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(4);
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(7);
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.7",
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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ if [ -d "/run/systemd/system/" ]; then
3
+ sudo systemctl start openclaw
4
+ else
5
+ nohup openclaw gateway run --port 18789 > /tmp/openclaw-gateway.log 2>&1 &
6
+ fi
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bash
2
+ lsof -nP -iTCP:18789 -sTCP:LISTEN -t | xargs -r kill -9