@lark-apaas/openclaw-scripts-diagnose-cli 0.1.1-alpha.8 → 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.
Files changed (2) hide show
  1. package/dist/index.cjs +108 -10
  2. package/package.json +1 -1
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) {
@@ -1164,13 +1232,29 @@ function killOpenclawProcesses() {
1164
1232
  } catch {}
1165
1233
  shell("sleep 2", 5e3);
1166
1234
  }
1167
- /** Step 4: Reinstall openclaw to the version specified in template. */
1168
- 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) {
1169
1246
  const version = loadJSON5().parse(node_fs.default.readFileSync(node_path.default.join(srcDir, "openclaw.json"), "utf-8")).meta?.lastTouchedVersion;
1170
1247
  try {
1171
1248
  shell("npm uninstall -g openclaw 2>/dev/null || true", 3e4);
1172
1249
  } catch {}
1173
- 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
+ });
1174
1258
  shell("openclaw doctor --fix", 12e4);
1175
1259
  }
1176
1260
  /** Step 5: Merge core-backup.json into config + ensure allowedOrigins. */
@@ -1216,10 +1300,21 @@ function copyStartupScripts(srcDir, configDir) {
1216
1300
  if (!node_fs.default.existsSync(targetScriptsDir)) node_fs.default.mkdirSync(targetScriptsDir, { recursive: true });
1217
1301
  shell(`cp -r '${srcScriptsDir}'/* '${targetScriptsDir}/'`, 1e4);
1218
1302
  }
1219
- /** Step 7: Reinstall all plugins via openclaw CLI. */
1220
- 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() {
1221
1310
  try {
1222
- 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
+ });
1223
1318
  } catch {}
1224
1319
  }
1225
1320
  /** Step 8: Write secrets/provider key files and restart openclaw. */
@@ -1239,7 +1334,7 @@ function writeSecretsAndRestart(vars, resetData, configDir) {
1239
1334
  * (see TEMPLATE_DIR) and synced from the miaoda-openclaw-template repo via
1240
1335
  * scripts/sync-template.sh, so no runtime download is required.
1241
1336
  */
1242
- function runReset(input, taskId, resultFile) {
1337
+ async function runReset(input, taskId, resultFile) {
1243
1338
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1244
1339
  const { configPath, vars, resetData } = input;
1245
1340
  const configDir = node_path.default.dirname(configPath);
@@ -1270,13 +1365,13 @@ function runReset(input, taskId, resultFile) {
1270
1365
  step(3);
1271
1366
  killOpenclawProcesses();
1272
1367
  step(4);
1273
- reinstallOpenclaw(srcDir);
1368
+ await reinstallOpenclaw(srcDir);
1274
1369
  step(5);
1275
1370
  mergeCoreBackupAndOrigins(configPath, vars);
1276
1371
  step(6);
1277
1372
  copyStartupScripts(srcDir, configDir);
1278
1373
  step(7);
1279
- reinstallPlugins();
1374
+ await reinstallPlugins();
1280
1375
  step(8);
1281
1376
  writeSecretsAndRestart(vars, resetData, configDir);
1282
1377
  markDone(resultFile, startedAt);
@@ -1366,7 +1461,10 @@ switch (mode) {
1366
1461
  node_process.default.exit(1);
1367
1462
  }
1368
1463
  const resultFile = `/tmp/openclaw-reset-${taskId}.json`;
1369
- 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
+ });
1370
1468
  } else {
1371
1469
  console.error("Usage: reset --async --ctx=<base64> | reset --worker --task-id=<id> --ctx=<base64>");
1372
1470
  node_process.default.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/openclaw-scripts-diagnose-cli",
3
- "version": "0.1.1-alpha.8",
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": {