@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.
- package/dist/index.cjs +70 -4
- 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.
|
|
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, {
|
|
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
|
-
|
|
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.
|
|
4279
|
+
return `v0.1.4-beta.1`;
|
|
4214
4280
|
}
|
|
4215
4281
|
const COMMANDS = [
|
|
4216
4282
|
{
|