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

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 +70 -4
  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-beta.1";
57
57
  }
58
58
  //#endregion
59
59
  //#region src/rule-engine/base.ts
@@ -2687,22 +2687,88 @@ function startAsyncReset(ctxBase64) {
2687
2687
  }
2688
2688
  //#endregion
2689
2689
  //#region src/oss/fetchWithDiag.ts
2690
+ /** Methods retried automatically on transient transport errors. POST and
2691
+ * PATCH are deliberately excluded: a transient ECONNRESET / ETIMEDOUT
2692
+ * after the request body has been written might mean the server
2693
+ * received and processed it (just couldn't reply) — auto-retrying
2694
+ * would cause duplicate side effects. AWS / GCP / Aliyun SDKs all use
2695
+ * the same allowlist. */
2696
+ const IDEMPOTENT_METHODS = new Set([
2697
+ "GET",
2698
+ "HEAD",
2699
+ "OPTIONS",
2700
+ "DELETE",
2701
+ "PUT"
2702
+ ]);
2703
+ /** Errno codes treated as transient — almost always a stale connection
2704
+ * or momentary network blip that succeeds on retry. The list mirrors
2705
+ * what AWS / GCP / Aliyun SDKs retry by default. Only used for fetch
2706
+ * rejection cause; HTTP 4xx/5xx responses pass through unchanged
2707
+ * (caller decides whether the application-level status is retryable). */
2708
+ const TRANSIENT_CODES = new Set([
2709
+ "ECONNRESET",
2710
+ "ETIMEDOUT",
2711
+ "EPIPE",
2712
+ "ECONNREFUSED",
2713
+ "EHOSTUNREACH",
2714
+ "ENETUNREACH",
2715
+ "EAI_AGAIN"
2716
+ ]);
2690
2717
  async function fetchWithDiag(url, opts = {}) {
2718
+ const maxRetries = opts.maxRetries ?? 2;
2719
+ const method = (opts.init?.method ?? "GET").toUpperCase();
2720
+ const retrySafe = opts.retryNonIdempotent || IDEMPOTENT_METHODS.has(method);
2721
+ let lastErr;
2722
+ for (let attempt = 0; attempt <= maxRetries; attempt++) try {
2723
+ return await fetchOnce(url, opts);
2724
+ } catch (e) {
2725
+ lastErr = e;
2726
+ const code = extractCauseCode(e);
2727
+ if (!(code !== void 0 && TRANSIENT_CODES.has(code)) || attempt === maxRetries) throw e;
2728
+ if (!retrySafe) {
2729
+ console.error(`fetch ${opts.label ?? originOf(url)}: transient ${code} but method=${method} is non-idempotent, NOT retrying`);
2730
+ throw e;
2731
+ }
2732
+ const delay = 200 * Math.pow(2, attempt) + Math.floor(Math.random() * 100);
2733
+ console.error(`fetch ${opts.label ?? originOf(url)}: transient ${code}, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
2734
+ await new Promise((r) => setTimeout(r, delay));
2735
+ }
2736
+ throw lastErr;
2737
+ }
2738
+ /** One fetch attempt with timeout. Throws on transport / abort failures
2739
+ * (caught by the retry loop above) and on its own enriched message form. */
2740
+ async function fetchOnce(url, opts) {
2691
2741
  const label = opts.label ?? originOf(url);
2692
2742
  const timeoutMs = opts.timeoutMs ?? 3e4;
2693
2743
  const start = Date.now();
2694
2744
  const ac = new AbortController();
2695
2745
  const timer = timeoutMs > 0 && Number.isFinite(timeoutMs) ? setTimeout(() => ac.abort(), timeoutMs) : void 0;
2696
2746
  try {
2697
- return await fetch(url, { signal: ac.signal });
2747
+ return await fetch(url, {
2748
+ ...opts.init,
2749
+ signal: ac.signal
2750
+ });
2698
2751
  } catch (e) {
2699
2752
  const durationMs = Date.now() - start;
2700
2753
  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})`);
2754
+ const enriched = /* @__PURE__ */ new Error(`fetch ${label} failed: ${causeStr} (url=${redactUrl(url)} durationMs=${durationMs})`);
2755
+ enriched.cause = e.cause ?? e;
2756
+ throw enriched;
2702
2757
  } finally {
2703
2758
  if (timer) clearTimeout(timer);
2704
2759
  }
2705
2760
  }
2761
+ /** Pull the errno-style code from the deepest cause we can reach. Used by
2762
+ * retry-classification only — error messages still get the human-readable
2763
+ * describeCause() rendering. */
2764
+ function extractCauseCode(e) {
2765
+ let cur = e.cause ?? e;
2766
+ for (let depth = 0; depth < 5 && cur; depth++) {
2767
+ const code = cur.code;
2768
+ if (typeof code === "string") return code;
2769
+ cur = cur.cause;
2770
+ }
2771
+ }
2706
2772
  /** Walk the Error.cause chain and produce a single-line summary like
2707
2773
  * `ENOTFOUND getaddrinfo ENOTFOUND oss.example.com`. */
2708
2774
  function describeCause(c, depth = 0) {
@@ -4210,7 +4276,7 @@ async function reportCliRun(opts) {
4210
4276
  //#region src/help.ts
4211
4277
  const BIN = "mclaw-diagnose";
4212
4278
  function versionBanner() {
4213
- return `v0.1.4-beta.0`;
4279
+ return `v0.1.4-beta.1`;
4214
4280
  }
4215
4281
  const COMMANDS = [
4216
4282
  {
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-beta.1",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {