@lark-apaas/openclaw-scripts-diagnose-cli 0.1.1-alpha.27 → 0.1.1-alpha.29
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 +297 -25
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,8 @@ node_path = __toESM(node_path);
|
|
|
32
32
|
let node_child_process = require("node:child_process");
|
|
33
33
|
let node_crypto = require("node:crypto");
|
|
34
34
|
node_crypto = __toESM(node_crypto);
|
|
35
|
+
let node_os = require("node:os");
|
|
36
|
+
node_os = __toESM(node_os);
|
|
35
37
|
let node_stream = require("node:stream");
|
|
36
38
|
let node_stream_promises = require("node:stream/promises");
|
|
37
39
|
let node_assert = require("node:assert");
|
|
@@ -684,18 +686,10 @@ FeishuChannelRule = _FeishuChannelRule = __decorate([Rule({
|
|
|
684
686
|
//#endregion
|
|
685
687
|
//#region src/rules/feishu-default-account.ts
|
|
686
688
|
let FeishuDefaultAccountRule = class FeishuDefaultAccountRule extends DiagnoseRule {
|
|
687
|
-
validate(
|
|
688
|
-
const feishu = getNestedMap(ctx.config, "channels", "feishu");
|
|
689
|
-
if (feishu && "defaultAccount" in feishu) return {
|
|
690
|
-
pass: false,
|
|
691
|
-
message: "channels.feishu.defaultAccount should be removed"
|
|
692
|
-
};
|
|
689
|
+
validate(_ctx) {
|
|
693
690
|
return { pass: true };
|
|
694
691
|
}
|
|
695
|
-
repair(
|
|
696
|
-
const feishu = getNestedMap(ctx.config, "channels", "feishu");
|
|
697
|
-
if (feishu && "defaultAccount" in feishu) delete feishu.defaultAccount;
|
|
698
|
-
}
|
|
692
|
+
repair(_ctx) {}
|
|
699
693
|
};
|
|
700
694
|
FeishuDefaultAccountRule = __decorate([Rule({
|
|
701
695
|
key: "feishu_default_account",
|
|
@@ -1582,32 +1576,51 @@ async function installOpenclaw(openclawTag, ossFileMap, opts = {}) {
|
|
|
1582
1576
|
if (!pkg) throw new Error("install-openclaw: role=cli,name=openclaw not found in manifest");
|
|
1583
1577
|
const targetDir = opts.targetDir ?? node_path.default.join(homeBase, pkg.installPath);
|
|
1584
1578
|
const bakDir = targetDir + ".bak";
|
|
1579
|
+
const newDir = targetDir + ".new";
|
|
1585
1580
|
const tarball = await downloadWithCache(pkg, ossFileMap, opts);
|
|
1586
1581
|
console.error(`[install-openclaw] tag=${openclawTag} shasum=${pkg.shasum.slice(0, 12)}...`);
|
|
1582
|
+
if (node_fs.default.existsSync(newDir)) node_fs.default.rmSync(newDir, {
|
|
1583
|
+
recursive: true,
|
|
1584
|
+
force: true
|
|
1585
|
+
});
|
|
1587
1586
|
if (node_fs.default.existsSync(bakDir)) node_fs.default.rmSync(bakDir, {
|
|
1588
1587
|
recursive: true,
|
|
1589
1588
|
force: true
|
|
1590
1589
|
});
|
|
1591
|
-
|
|
1592
|
-
|
|
1590
|
+
node_fs.default.mkdirSync(node_path.default.dirname(targetDir), { recursive: true });
|
|
1591
|
+
const tmpStage = node_fs.default.mkdtempSync(node_path.default.join(opts.tmpRoot ?? node_os.default.tmpdir(), "openclaw-install-"));
|
|
1593
1592
|
try {
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1593
|
+
execCaptureErr(`tar -xzf '${tarball}' -C '${tmpStage}' --strip-components=1`);
|
|
1594
|
+
if (!node_fs.default.existsSync(node_path.default.join(tmpStage, "package.json"))) throw new Error("extracted tarball missing package.json");
|
|
1595
|
+
moveSafe(tmpStage, newDir);
|
|
1596
|
+
const hadExisting = node_fs.default.existsSync(targetDir);
|
|
1598
1597
|
try {
|
|
1599
|
-
|
|
1598
|
+
if (hadExisting) moveSafe(targetDir, bakDir);
|
|
1599
|
+
moveSafe(newDir, targetDir);
|
|
1600
|
+
} catch (e) {
|
|
1601
|
+
if (hadExisting && !node_fs.default.existsSync(targetDir) && node_fs.default.existsSync(bakDir)) try {
|
|
1602
|
+
moveSafe(bakDir, targetDir);
|
|
1603
|
+
} catch {}
|
|
1604
|
+
try {
|
|
1605
|
+
node_fs.default.rmSync(newDir, {
|
|
1606
|
+
recursive: true,
|
|
1607
|
+
force: true
|
|
1608
|
+
});
|
|
1609
|
+
} catch {}
|
|
1610
|
+
throw e;
|
|
1611
|
+
}
|
|
1612
|
+
if (hadExisting && node_fs.default.existsSync(bakDir)) node_fs.default.rmSync(bakDir, {
|
|
1613
|
+
recursive: true,
|
|
1614
|
+
force: true
|
|
1615
|
+
});
|
|
1616
|
+
} finally {
|
|
1617
|
+
if (node_fs.default.existsSync(tmpStage)) try {
|
|
1618
|
+
node_fs.default.rmSync(tmpStage, {
|
|
1600
1619
|
recursive: true,
|
|
1601
1620
|
force: true
|
|
1602
1621
|
});
|
|
1603
1622
|
} catch {}
|
|
1604
|
-
if (hadExisting && node_fs.default.existsSync(bakDir)) moveSafe(bakDir, targetDir);
|
|
1605
|
-
throw e;
|
|
1606
1623
|
}
|
|
1607
|
-
if (node_fs.default.existsSync(bakDir)) node_fs.default.rmSync(bakDir, {
|
|
1608
|
-
recursive: true,
|
|
1609
|
-
force: true
|
|
1610
|
-
});
|
|
1611
1624
|
console.error(`[install-openclaw] done in ${Date.now() - t0}ms`);
|
|
1612
1625
|
}
|
|
1613
1626
|
async function installExtension(tag, ossFileMap, opts = {}) {
|
|
@@ -2504,9 +2517,248 @@ async function runDoctor(rawCtx, opts) {
|
|
|
2504
2517
|
};
|
|
2505
2518
|
}
|
|
2506
2519
|
//#endregion
|
|
2520
|
+
//#region src/help.ts
|
|
2521
|
+
const BIN = "mclaw-diagnose";
|
|
2522
|
+
function versionBanner() {
|
|
2523
|
+
return `v0.1.1-alpha.29`;
|
|
2524
|
+
}
|
|
2525
|
+
const COMMANDS = [
|
|
2526
|
+
{
|
|
2527
|
+
name: "doctor",
|
|
2528
|
+
hidden: false,
|
|
2529
|
+
summary: "Diagnose openclaw config; apply repairs with --fix",
|
|
2530
|
+
help: `USAGE
|
|
2531
|
+
${BIN} doctor [--fix] [--rule=<key>]...
|
|
2532
|
+
|
|
2533
|
+
DESCRIPTION
|
|
2534
|
+
Fetches DoctorCtx via innerapi, then runs one of three modes depending
|
|
2535
|
+
on the flags. Output is a single JSON object on stdout.
|
|
2536
|
+
|
|
2537
|
+
MODES
|
|
2538
|
+
(no flags) Check-only. Runs the rule engine against the
|
|
2539
|
+
sandbox's current openclaw config and returns
|
|
2540
|
+
{ failedRules: { standard, ai, reset } }
|
|
2541
|
+
No files are mutated. Use this when you just
|
|
2542
|
+
want to know what's wrong.
|
|
2543
|
+
|
|
2544
|
+
--fix Check + repair-all. First runs the rule engine,
|
|
2545
|
+
then repairs every failing standard-mode rule.
|
|
2546
|
+
Returns
|
|
2547
|
+
{ check: {...}, repair: {...} }
|
|
2548
|
+
Use this as the default "fix everything" action.
|
|
2549
|
+
|
|
2550
|
+
--fix --rule=<key>... Targeted repair. Skips the check pass entirely
|
|
2551
|
+
and runs repair against the listed rule keys
|
|
2552
|
+
only. Unknown keys are silently ignored.
|
|
2553
|
+
Returns { repair: {...} } with only those
|
|
2554
|
+
rules' outcomes. Use this when you already
|
|
2555
|
+
know which rules need fixing.
|
|
2556
|
+
|
|
2557
|
+
OPTIONS
|
|
2558
|
+
--fix Enable repair. See MODES above.
|
|
2559
|
+
--rule=<key> Repair only this rule key. Repeatable. Only
|
|
2560
|
+
meaningful together with --fix.
|
|
2561
|
+
|
|
2562
|
+
EXAMPLES
|
|
2563
|
+
${BIN} doctor # check only
|
|
2564
|
+
${BIN} doctor --fix # check then repair all
|
|
2565
|
+
${BIN} doctor --fix --rule=gateway # repair 'gateway' only
|
|
2566
|
+
${BIN} doctor --fix --rule=gateway --rule=jwt_token # repair multiple
|
|
2567
|
+
|
|
2568
|
+
EXIT CODES
|
|
2569
|
+
0 success
|
|
2570
|
+
1 generic error
|
|
2571
|
+
77 innerapi authentication failed (sandbox JWT expired/invalid)
|
|
2572
|
+
`
|
|
2573
|
+
},
|
|
2574
|
+
{
|
|
2575
|
+
name: "check",
|
|
2576
|
+
hidden: true,
|
|
2577
|
+
summary: "Run rule-engine check only",
|
|
2578
|
+
help: `USAGE
|
|
2579
|
+
${BIN} check [--ctx=<base64>]
|
|
2580
|
+
|
|
2581
|
+
DESCRIPTION
|
|
2582
|
+
Runs the rule engine against the sandbox's current openclaw config and
|
|
2583
|
+
returns { failedRules }. Used by sandbox_console's push-style callers
|
|
2584
|
+
that already own the ctx — end-users should prefer \`doctor\`.
|
|
2585
|
+
|
|
2586
|
+
OPTIONS
|
|
2587
|
+
--ctx=<base64> Opaque ctx JSON (base64). When absent, fetched from
|
|
2588
|
+
innerapi (same path as doctor).
|
|
2589
|
+
`
|
|
2590
|
+
},
|
|
2591
|
+
{
|
|
2592
|
+
name: "repair",
|
|
2593
|
+
hidden: true,
|
|
2594
|
+
summary: "Apply standard-mode repairs",
|
|
2595
|
+
help: `USAGE
|
|
2596
|
+
${BIN} repair [--ctx=<base64>]
|
|
2597
|
+
|
|
2598
|
+
DESCRIPTION
|
|
2599
|
+
Runs repair for the failing rules listed inside the ctx's repairData.
|
|
2600
|
+
Intended for sandbox_console's push path — end-users should use
|
|
2601
|
+
\`doctor --fix\` instead.
|
|
2602
|
+
|
|
2603
|
+
OPTIONS
|
|
2604
|
+
--ctx=<base64> Opaque ctx JSON (base64). When absent, fetched from
|
|
2605
|
+
innerapi.
|
|
2606
|
+
`
|
|
2607
|
+
},
|
|
2608
|
+
{
|
|
2609
|
+
name: "reset",
|
|
2610
|
+
hidden: true,
|
|
2611
|
+
summary: "Re-initialize sandbox via the 9-step reset pipeline",
|
|
2612
|
+
help: `USAGE
|
|
2613
|
+
${BIN} reset --async [--ctx=<base64>]
|
|
2614
|
+
${BIN} reset --worker --task-id=<id> [--ctx=<base64>]
|
|
2615
|
+
|
|
2616
|
+
DESCRIPTION
|
|
2617
|
+
Two-phase pipeline driven asynchronously: the --async invocation spawns
|
|
2618
|
+
a detached worker and returns { taskId } immediately; the --worker
|
|
2619
|
+
invocation (spawned by --async) runs the actual 9 steps and writes
|
|
2620
|
+
progress to /tmp/openclaw-diagnose/reset-<taskId>.json.
|
|
2621
|
+
|
|
2622
|
+
Poll progress with \`${BIN} get_reset_task --task-id=<id>\`.
|
|
2623
|
+
|
|
2624
|
+
OPTIONS
|
|
2625
|
+
--async Start a detached worker and return taskId on stdout.
|
|
2626
|
+
--worker Internal — run the 9-step pipeline (launched by --async).
|
|
2627
|
+
--task-id=<id> Required with --worker; identifies the progress file.
|
|
2628
|
+
--ctx=<base64> Opaque ctx JSON; fetched from innerapi when absent.
|
|
2629
|
+
`
|
|
2630
|
+
},
|
|
2631
|
+
{
|
|
2632
|
+
name: "get_reset_task",
|
|
2633
|
+
hidden: true,
|
|
2634
|
+
summary: "Poll progress of an async reset task",
|
|
2635
|
+
help: `USAGE
|
|
2636
|
+
${BIN} get_reset_task --task-id=<id>
|
|
2637
|
+
|
|
2638
|
+
DESCRIPTION
|
|
2639
|
+
Reads /tmp/openclaw-diagnose/reset-<taskId>.json and prints its content
|
|
2640
|
+
as JSON on stdout. Safe to call repeatedly while reset is in progress.
|
|
2641
|
+
|
|
2642
|
+
OPTIONS
|
|
2643
|
+
--task-id=<id> Required. Matches the id returned by \`reset --async\`.
|
|
2644
|
+
`
|
|
2645
|
+
},
|
|
2646
|
+
{
|
|
2647
|
+
name: "install-openclaw",
|
|
2648
|
+
hidden: true,
|
|
2649
|
+
summary: "Download + install the openclaw tarball",
|
|
2650
|
+
help: `USAGE
|
|
2651
|
+
${BIN} install-openclaw <tag> [--ctx=<base64> | --oss_file_map=<base64>]
|
|
2652
|
+
|
|
2653
|
+
DESCRIPTION
|
|
2654
|
+
Downloads the openclaw@<tag> tgz via the signed OSS URL found in the
|
|
2655
|
+
ctx's install.ossFileMap, extracts it into a tmpfs staging dir, and
|
|
2656
|
+
atomically swaps it into /home/gem/.npm-global/lib/node_modules/openclaw.
|
|
2657
|
+
Used by step 5 of reset.
|
|
2658
|
+
|
|
2659
|
+
ARGUMENTS
|
|
2660
|
+
<tag> Openclaw version tag, e.g. 2026.4.11.
|
|
2661
|
+
|
|
2662
|
+
OPTIONS
|
|
2663
|
+
--ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
|
|
2664
|
+
--oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi
|
|
2665
|
+
entirely. Wins over --ctx when both provided.
|
|
2666
|
+
`
|
|
2667
|
+
},
|
|
2668
|
+
{
|
|
2669
|
+
name: "install-extension",
|
|
2670
|
+
hidden: true,
|
|
2671
|
+
summary: "Install openclaw extension package(s)",
|
|
2672
|
+
help: `USAGE
|
|
2673
|
+
${BIN} install-extension <tag> (--all | --extension=<name>...) [options]
|
|
2674
|
+
|
|
2675
|
+
DESCRIPTION
|
|
2676
|
+
Downloads + installs one or more openclaw extension tarballs
|
|
2677
|
+
(feishu, miaoda, etc.) into <home_base>/workspace/agent/extensions/,
|
|
2678
|
+
then splices installMetadata into openclaw.json's plugins.installs
|
|
2679
|
+
unless --skip-config-update is passed.
|
|
2680
|
+
|
|
2681
|
+
ARGUMENTS
|
|
2682
|
+
<tag> Openclaw version tag; extension versions resolved
|
|
2683
|
+
against the matching manifest.
|
|
2684
|
+
|
|
2685
|
+
OPTIONS
|
|
2686
|
+
--all Install every extension in the manifest.
|
|
2687
|
+
--extension=<name> Install a specific extension (repeatable).
|
|
2688
|
+
--home_base=<dir> Override the /home/gem base (tests).
|
|
2689
|
+
--config_path=<p> Override the openclaw.json path (tests).
|
|
2690
|
+
--skip-config-update Leave plugins.installs in openclaw.json untouched.
|
|
2691
|
+
--ctx=<base64> Opaque ctx; see install-openclaw for semantics.
|
|
2692
|
+
--oss_file_map=... Pre-built OSS URL map (base64 JSON).
|
|
2693
|
+
`
|
|
2694
|
+
},
|
|
2695
|
+
{
|
|
2696
|
+
name: "download-resource",
|
|
2697
|
+
hidden: true,
|
|
2698
|
+
summary: "Download + extract a single OSS resource",
|
|
2699
|
+
help: `USAGE
|
|
2700
|
+
${BIN} download-resource <tag> --role=<role> --name=<name> [--dir=<dir>] [options]
|
|
2701
|
+
|
|
2702
|
+
DESCRIPTION
|
|
2703
|
+
Downloads one resource (template, config asset, etc.) identified by
|
|
2704
|
+
(role, name) from the manifest and extracts/copies it to <dir>.
|
|
2705
|
+
|
|
2706
|
+
ARGUMENTS
|
|
2707
|
+
<tag> Openclaw version tag.
|
|
2708
|
+
|
|
2709
|
+
OPTIONS
|
|
2710
|
+
--role=<role> Package role (e.g. template, config).
|
|
2711
|
+
--name=<name> Package name within the role.
|
|
2712
|
+
--dir=<dir> Target dir (defaults to dirname(pkg.installPath)).
|
|
2713
|
+
--ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
|
|
2714
|
+
--oss_file_map=... Pre-built OSS URL map (base64 JSON).
|
|
2715
|
+
`
|
|
2716
|
+
}
|
|
2717
|
+
];
|
|
2718
|
+
function parseHelpFlags(args) {
|
|
2719
|
+
return {
|
|
2720
|
+
help: args.includes("--help") || args.includes("-h"),
|
|
2721
|
+
expert: args.includes("-x") || args.includes("--expert")
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
/**
|
|
2725
|
+
* Render the top-level help to the given stream. When `expert` is true,
|
|
2726
|
+
* hidden commands are listed alongside the user-facing ones.
|
|
2727
|
+
*/
|
|
2728
|
+
function formatTopLevelHelp(expert) {
|
|
2729
|
+
const visible = COMMANDS.filter((c) => !c.hidden);
|
|
2730
|
+
const hidden = COMMANDS.filter((c) => c.hidden);
|
|
2731
|
+
const pad = (s, w) => s + " ".repeat(Math.max(0, w - s.length));
|
|
2732
|
+
const w = Math.max(...COMMANDS.map((c) => c.name.length)) + 2;
|
|
2733
|
+
const lines = [];
|
|
2734
|
+
lines.push(`${BIN} — OpenClaw config diagnose / repair CLI`);
|
|
2735
|
+
lines.push(versionBanner());
|
|
2736
|
+
lines.push("");
|
|
2737
|
+
lines.push("USAGE");
|
|
2738
|
+
lines.push(` ${BIN} <command> [options]`);
|
|
2739
|
+
lines.push(` ${BIN} <command> --help per-command help`);
|
|
2740
|
+
lines.push(` ${BIN} --help this message`);
|
|
2741
|
+
lines.push("");
|
|
2742
|
+
lines.push("COMMANDS");
|
|
2743
|
+
for (const c of visible) lines.push(` ${pad(c.name, w)}${c.summary}`);
|
|
2744
|
+
if (expert && hidden.length > 0) {
|
|
2745
|
+
lines.push("");
|
|
2746
|
+
lines.push("INTERNAL COMMANDS (revealed by -x)");
|
|
2747
|
+
for (const c of hidden) lines.push(` ${pad(c.name, w)}${c.summary}`);
|
|
2748
|
+
}
|
|
2749
|
+
lines.push("");
|
|
2750
|
+
return lines.join("\n");
|
|
2751
|
+
}
|
|
2752
|
+
/** Render per-command help. Returns undefined when the name is unknown. */
|
|
2753
|
+
function formatCommandHelp(name) {
|
|
2754
|
+
const cmd = COMMANDS.find((c) => c.name === name);
|
|
2755
|
+
if (!cmd) return void 0;
|
|
2756
|
+
return cmd.help;
|
|
2757
|
+
}
|
|
2758
|
+
//#endregion
|
|
2507
2759
|
//#region src/index.ts
|
|
2508
2760
|
const args = node_process.default.argv.slice(2);
|
|
2509
|
-
const mode = args.find((a) => !a.startsWith("
|
|
2761
|
+
const mode = args.find((a) => !a.startsWith("-"));
|
|
2510
2762
|
/**
|
|
2511
2763
|
* Decode `--ctx=<base64>` into an opaque JSON object. Returns undefined when
|
|
2512
2764
|
* the flag isn't present — the caller decides whether to fall back to the
|
|
@@ -2539,6 +2791,25 @@ function getMultiFlag(args, name) {
|
|
|
2539
2791
|
}
|
|
2540
2792
|
async function main() {
|
|
2541
2793
|
installStderrMirror();
|
|
2794
|
+
const helpFlags = parseHelpFlags(args);
|
|
2795
|
+
if (mode && helpFlags.help) {
|
|
2796
|
+
const body = formatCommandHelp(mode);
|
|
2797
|
+
if (body) {
|
|
2798
|
+
node_process.default.stdout.write(body);
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
node_process.default.stderr.write(`Unknown command: ${mode}\n\n`);
|
|
2802
|
+
node_process.default.stderr.write(formatTopLevelHelp(helpFlags.expert));
|
|
2803
|
+
node_process.default.exit(1);
|
|
2804
|
+
}
|
|
2805
|
+
if (!mode) {
|
|
2806
|
+
if (helpFlags.help) {
|
|
2807
|
+
node_process.default.stdout.write(formatTopLevelHelp(helpFlags.expert));
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2810
|
+
node_process.default.stderr.write(formatTopLevelHelp(helpFlags.expert));
|
|
2811
|
+
node_process.default.exit(1);
|
|
2812
|
+
}
|
|
2542
2813
|
switch (mode) {
|
|
2543
2814
|
case "check":
|
|
2544
2815
|
case "repair": {
|
|
@@ -2660,7 +2931,8 @@ async function main() {
|
|
|
2660
2931
|
break;
|
|
2661
2932
|
}
|
|
2662
2933
|
default:
|
|
2663
|
-
|
|
2934
|
+
node_process.default.stderr.write(`Unknown command: ${mode}\n\n`);
|
|
2935
|
+
node_process.default.stderr.write(formatTopLevelHelp(helpFlags.expert));
|
|
2664
2936
|
node_process.default.exit(1);
|
|
2665
2937
|
}
|
|
2666
2938
|
}
|
package/package.json
CHANGED