@lark-apaas/openclaw-scripts-diagnose-cli 0.1.4-beta.0 → 0.1.4

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 +95 -8
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -53,7 +53,7 @@ let json_diff = require("json-diff");
53
53
  * it terse and parseable.
54
54
  */
55
55
  function getVersion() {
56
- return "0.1.4-beta.0";
56
+ return "0.1.4";
57
57
  }
58
58
  //#endregion
59
59
  //#region src/rule-engine/base.ts
@@ -1420,19 +1420,24 @@ OldMiaodaPluginsCleanupRule = __decorate([Rule({
1420
1420
  //#endregion
1421
1421
  //#region src/rules/lark-plugin-allow.ts
1422
1422
  const LARK_PLUGIN = "openclaw-lark";
1423
- const LARK_PLUGIN_NAMES = [LARK_PLUGIN, "feishu-openclaw-plugin"];
1423
+ const LEGACY_LARK_PLUGIN = "feishu-openclaw-plugin";
1424
+ const LARK_PLUGIN_NAMES = [LARK_PLUGIN, LEGACY_LARK_PLUGIN];
1424
1425
  let LarkPluginAllowRule = class LarkPluginAllowRule extends DiagnoseRule {
1425
1426
  validate(ctx) {
1426
1427
  const allow = getAllow(ctx.config);
1427
1428
  if (LARK_PLUGIN_NAMES.some((name) => allow.includes(name))) return { pass: true };
1429
+ const installed = detectInstalledLarkPlugin(getExtensionsDir(ctx.configPath));
1430
+ if (installed == null) return { pass: true };
1428
1431
  return {
1429
1432
  pass: false,
1430
- message: `plugins.allow 缺少飞书插件 (expected one of: ${LARK_PLUGIN_NAMES.join(", ")})`
1433
+ message: `plugins.allow 缺少飞书插件 ${installed}(已在 extensions/ 下装但未启用)`
1431
1434
  };
1432
1435
  }
1433
1436
  repair(ctx) {
1437
+ const installed = detectInstalledLarkPlugin(getExtensionsDir(ctx.configPath));
1438
+ if (installed == null) return;
1434
1439
  if (ctx.config.plugins == null || typeof ctx.config.plugins !== "object" || Array.isArray(ctx.config.plugins)) {
1435
- ctx.config.plugins = { allow: [LARK_PLUGIN] };
1440
+ ctx.config.plugins = { allow: [installed] };
1436
1441
  return;
1437
1442
  }
1438
1443
  const pluginsMap = ctx.config.plugins;
@@ -1440,7 +1445,7 @@ let LarkPluginAllowRule = class LarkPluginAllowRule extends DiagnoseRule {
1440
1445
  const original = Array.isArray(rawAllow) ? rawAllow : [];
1441
1446
  const stringAllow = original.filter((e) => typeof e === "string");
1442
1447
  if (LARK_PLUGIN_NAMES.some((name) => stringAllow.includes(name))) return;
1443
- original.push(LARK_PLUGIN);
1448
+ original.push(installed);
1444
1449
  pluginsMap.allow = original;
1445
1450
  }
1446
1451
  };
@@ -1456,6 +1461,22 @@ function getAllow(config) {
1456
1461
  if (!Array.isArray(allow)) return [];
1457
1462
  return allow.filter((e) => typeof e === "string");
1458
1463
  }
1464
+ /**
1465
+ * fs-only 检测:`<extDir>/<name>/package.json` 存在即视为已装。
1466
+ * 优先级 openclaw-lark(新版)> feishu-openclaw-plugin(legacy)。
1467
+ * 不读 package.json 内容,只判存在性,避开 JSON 损坏。
1468
+ */
1469
+ function detectInstalledLarkPlugin(extDir) {
1470
+ for (const name of [LARK_PLUGIN, LEGACY_LARK_PLUGIN]) if (pluginPackageJsonExists(extDir, name)) return name;
1471
+ return null;
1472
+ }
1473
+ function pluginPackageJsonExists(extDir, pluginDir) {
1474
+ try {
1475
+ return node_fs.default.existsSync(node_path.default.join(extDir, pluginDir, "package.json"));
1476
+ } catch {
1477
+ return false;
1478
+ }
1479
+ }
1459
1480
  //#endregion
1460
1481
  //#region src/fs-utils.ts
1461
1482
  /**
@@ -2687,22 +2708,88 @@ function startAsyncReset(ctxBase64) {
2687
2708
  }
2688
2709
  //#endregion
2689
2710
  //#region src/oss/fetchWithDiag.ts
2711
+ /** Methods retried automatically on transient transport errors. POST and
2712
+ * PATCH are deliberately excluded: a transient ECONNRESET / ETIMEDOUT
2713
+ * after the request body has been written might mean the server
2714
+ * received and processed it (just couldn't reply) — auto-retrying
2715
+ * would cause duplicate side effects. AWS / GCP / Aliyun SDKs all use
2716
+ * the same allowlist. */
2717
+ const IDEMPOTENT_METHODS = new Set([
2718
+ "GET",
2719
+ "HEAD",
2720
+ "OPTIONS",
2721
+ "DELETE",
2722
+ "PUT"
2723
+ ]);
2724
+ /** Errno codes treated as transient — almost always a stale connection
2725
+ * or momentary network blip that succeeds on retry. The list mirrors
2726
+ * what AWS / GCP / Aliyun SDKs retry by default. Only used for fetch
2727
+ * rejection cause; HTTP 4xx/5xx responses pass through unchanged
2728
+ * (caller decides whether the application-level status is retryable). */
2729
+ const TRANSIENT_CODES = new Set([
2730
+ "ECONNRESET",
2731
+ "ETIMEDOUT",
2732
+ "EPIPE",
2733
+ "ECONNREFUSED",
2734
+ "EHOSTUNREACH",
2735
+ "ENETUNREACH",
2736
+ "EAI_AGAIN"
2737
+ ]);
2690
2738
  async function fetchWithDiag(url, opts = {}) {
2739
+ const maxRetries = opts.maxRetries ?? 2;
2740
+ const method = (opts.init?.method ?? "GET").toUpperCase();
2741
+ const retrySafe = opts.retryNonIdempotent || IDEMPOTENT_METHODS.has(method);
2742
+ let lastErr;
2743
+ for (let attempt = 0; attempt <= maxRetries; attempt++) try {
2744
+ return await fetchOnce(url, opts);
2745
+ } catch (e) {
2746
+ lastErr = e;
2747
+ const code = extractCauseCode(e);
2748
+ if (!(code !== void 0 && TRANSIENT_CODES.has(code)) || attempt === maxRetries) throw e;
2749
+ if (!retrySafe) {
2750
+ console.error(`fetch ${opts.label ?? originOf(url)}: transient ${code} but method=${method} is non-idempotent, NOT retrying`);
2751
+ throw e;
2752
+ }
2753
+ const delay = 200 * Math.pow(2, attempt) + Math.floor(Math.random() * 100);
2754
+ console.error(`fetch ${opts.label ?? originOf(url)}: transient ${code}, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
2755
+ await new Promise((r) => setTimeout(r, delay));
2756
+ }
2757
+ throw lastErr;
2758
+ }
2759
+ /** One fetch attempt with timeout. Throws on transport / abort failures
2760
+ * (caught by the retry loop above) and on its own enriched message form. */
2761
+ async function fetchOnce(url, opts) {
2691
2762
  const label = opts.label ?? originOf(url);
2692
2763
  const timeoutMs = opts.timeoutMs ?? 3e4;
2693
2764
  const start = Date.now();
2694
2765
  const ac = new AbortController();
2695
2766
  const timer = timeoutMs > 0 && Number.isFinite(timeoutMs) ? setTimeout(() => ac.abort(), timeoutMs) : void 0;
2696
2767
  try {
2697
- return await fetch(url, { signal: ac.signal });
2768
+ return await fetch(url, {
2769
+ ...opts.init,
2770
+ signal: ac.signal
2771
+ });
2698
2772
  } catch (e) {
2699
2773
  const durationMs = Date.now() - start;
2700
2774
  const causeStr = e.name === "AbortError" || ac.signal.aborted && timeoutMs > 0 ? `request aborted after ${timeoutMs}ms (timeout)` : describeCause(e.cause) || e.message;
2701
- throw new Error(`fetch ${label} failed: ${causeStr} (url=${redactUrl(url)} durationMs=${durationMs})`);
2775
+ const enriched = /* @__PURE__ */ new Error(`fetch ${label} failed: ${causeStr} (url=${redactUrl(url)} durationMs=${durationMs})`);
2776
+ enriched.cause = e.cause ?? e;
2777
+ throw enriched;
2702
2778
  } finally {
2703
2779
  if (timer) clearTimeout(timer);
2704
2780
  }
2705
2781
  }
2782
+ /** Pull the errno-style code from the deepest cause we can reach. Used by
2783
+ * retry-classification only — error messages still get the human-readable
2784
+ * describeCause() rendering. */
2785
+ function extractCauseCode(e) {
2786
+ let cur = e.cause ?? e;
2787
+ for (let depth = 0; depth < 5 && cur; depth++) {
2788
+ const code = cur.code;
2789
+ if (typeof code === "string") return code;
2790
+ cur = cur.cause;
2791
+ }
2792
+ }
2706
2793
  /** Walk the Error.cause chain and produce a single-line summary like
2707
2794
  * `ENOTFOUND getaddrinfo ENOTFOUND oss.example.com`. */
2708
2795
  function describeCause(c, depth = 0) {
@@ -4210,7 +4297,7 @@ async function reportCliRun(opts) {
4210
4297
  //#region src/help.ts
4211
4298
  const BIN = "mclaw-diagnose";
4212
4299
  function versionBanner() {
4213
- return `v0.1.4-beta.0`;
4300
+ return `v0.1.4`;
4214
4301
  }
4215
4302
  const COMMANDS = [
4216
4303
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/openclaw-scripts-diagnose-cli",
3
- "version": "0.1.4-beta.0",
3
+ "version": "0.1.4",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {