@lark-apaas/openclaw-scripts-diagnose-cli 0.1.14-alpha.8 → 0.1.14-beta.0

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 +579 -1053
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -52,7 +52,7 @@ node_assert = __toESM(node_assert);
52
52
  * it terse and parseable.
53
53
  */
54
54
  function getVersion() {
55
- return "0.1.14-alpha.8";
55
+ return "0.1.14-beta.0";
56
56
  }
57
57
  //#endregion
58
58
  //#region src/rule-engine/base.ts
@@ -2520,6 +2520,337 @@ function upsertResourceConstrainedToolsBlock(content) {
2520
2520
  return `${content}${content.length > 0 && !content.endsWith("\n") ? "\n\n" : "\n"}${RESOURCE_CONSTRAINED_TOOLS_BLOCK}\n`;
2521
2521
  }
2522
2522
  //#endregion
2523
+ //#region src/paths.ts
2524
+ /**
2525
+ * Central directory for all ephemeral diagnose/reset artifacts: task status
2526
+ * files (`reset-<taskId>.json`) and human-readable step logs
2527
+ * (`reset-<taskId>.log`). Having everything under one dir makes debugging a
2528
+ * stuck reset much easier — `ls /tmp/openclaw-diagnose/` shows every recent
2529
+ * run, and each run's log is right next to its state.
2530
+ */
2531
+ const DIAGNOSE_DIR = "/tmp/openclaw-diagnose";
2532
+ function resetResultFile(taskId) {
2533
+ return `${DIAGNOSE_DIR}/reset-${taskId}.json`;
2534
+ }
2535
+ function resetLogFile(taskId) {
2536
+ return `${DIAGNOSE_DIR}/reset-${taskId}.log`;
2537
+ }
2538
+ /** Sandbox workspace root where openclaw config + agent state lives. */
2539
+ const WORKSPACE_DIR = "/home/gem/workspace/agent";
2540
+ /** File containing the provider key used by the openclaw miaoda provider. */
2541
+ const PROVIDER_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-provider-key";
2542
+ /** File containing the miaoda openclaw secrets JSON. */
2543
+ const SECRETS_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-openclaw-secrets.json";
2544
+ /** Absolute path to the openclaw config JSON. */
2545
+ const CONFIG_PATH = `${WORKSPACE_DIR}/openclaw.json`;
2546
+ //#endregion
2547
+ //#region src/lark-cli-init.ts
2548
+ const LARK_PLUGIN_NAMES$1 = ["openclaw-lark", "feishu-openclaw-plugin"];
2549
+ const PE_XML_TAG = "lark-cli-pe";
2550
+ const PE_PLACEHOLDER = `
2551
+ <${PE_XML_TAG}>
2552
+ **【强制要求 - 无例外】飞书所有操作必须先读 feishu-lark-cli skill。未读skill不得调用任何飞书工具,违者视为操作失误。**
2553
+ </${PE_XML_TAG}>
2554
+ `;
2555
+ function isLarkPluginInstalled(configPath) {
2556
+ const extDir = getExtensionsDir(configPath);
2557
+ return LARK_PLUGIN_NAMES$1.some((name) => {
2558
+ try {
2559
+ return node_fs.default.existsSync(node_path.default.join(extDir, name, "package.json"));
2560
+ } catch {
2561
+ return false;
2562
+ }
2563
+ });
2564
+ }
2565
+ function isLarkCliAvailable$2() {
2566
+ try {
2567
+ return (0, node_child_process.spawnSync)("lark-cli", ["--version"], {
2568
+ encoding: "utf-8",
2569
+ timeout: 5e3,
2570
+ stdio: [
2571
+ "ignore",
2572
+ "pipe",
2573
+ "ignore"
2574
+ ]
2575
+ }).status === 0;
2576
+ } catch {
2577
+ return false;
2578
+ }
2579
+ }
2580
+ function readConfig(configPath) {
2581
+ try {
2582
+ const raw = node_fs.default.readFileSync(configPath, "utf-8");
2583
+ const parsed = loadJSON5().parse(raw);
2584
+ return parsed != null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
2585
+ } catch {
2586
+ return null;
2587
+ }
2588
+ }
2589
+ /**
2590
+ * Resolve the feishu app secret for the given appId.
2591
+ *
2592
+ * Lookup order:
2593
+ * 1. channels.feishu.appSecret (single-agent: feishu.appId === appId)
2594
+ * 2. channels.feishu.accounts[key].appSecret (multi-agent: account.appId === appId)
2595
+ *
2596
+ * Value interpretation:
2597
+ * - string → use directly
2598
+ * - object → secret is managed by a provider; use `feishuAppSecret` param instead
2599
+ *
2600
+ * Returns null when the secret cannot be determined.
2601
+ */
2602
+ function resolveAppSecret(appId, config, feishuAppSecret) {
2603
+ const feishu = getNestedMap(config, "channels", "feishu");
2604
+ if (!feishu) return null;
2605
+ let rawSecret;
2606
+ if (typeof feishu.appId === "string" && feishu.appId === appId) rawSecret = feishu.appSecret;
2607
+ else {
2608
+ const accounts = asRecord(feishu.accounts);
2609
+ if (accounts) for (const [, val] of Object.entries(accounts)) {
2610
+ const account = asRecord(val);
2611
+ if (account?.appId === appId) {
2612
+ rawSecret = account.appSecret ?? feishu.appSecret;
2613
+ break;
2614
+ }
2615
+ }
2616
+ }
2617
+ if (typeof rawSecret === "string" && rawSecret) return rawSecret;
2618
+ if (rawSecret != null && typeof rawSecret === "object") return feishuAppSecret ?? null;
2619
+ return null;
2620
+ }
2621
+ /**
2622
+ * Resolve the agents.md path for the given appId from the openclaw config.
2623
+ *
2624
+ * Case 1: appId matches channels.feishu.appId (single-agent path)
2625
+ * → WORKSPACE_DIR/AGENTS.md
2626
+ *
2627
+ * Case 2: appId found in channels.feishu.accounts (multi-agent path)
2628
+ * → find account key where account.appId === appId
2629
+ * → find binding where match.channel=feishu && match.accountId=that key
2630
+ * → if agentId === 'main' → WORKSPACE_DIR/agents.md
2631
+ * → else find agent in agents.list by id → agent.workspace/agents.md
2632
+ *
2633
+ * Returns null when the path cannot be determined.
2634
+ */
2635
+ function resolveAgentsMdPath(appId, config) {
2636
+ const feishu = getNestedMap(config, "channels", "feishu");
2637
+ if (!feishu) {
2638
+ console.error("resolveAgentsMdPath: channels.feishu not found");
2639
+ return null;
2640
+ }
2641
+ if (typeof feishu.appId === "string" && feishu.appId === appId) {
2642
+ console.error(`resolveAgentsMdPath: case=single-agent feishu.appId=${feishu.appId}`);
2643
+ return node_path.default.join(WORKSPACE_DIR, "workspace", "AGENTS.md");
2644
+ }
2645
+ const accounts = asRecord(feishu.accounts);
2646
+ if (!accounts) {
2647
+ console.error("resolveAgentsMdPath: feishu.accounts not found");
2648
+ return null;
2649
+ }
2650
+ let accountId;
2651
+ for (const [key, val] of Object.entries(accounts)) if (asRecord(val)?.appId === appId) {
2652
+ accountId = key;
2653
+ break;
2654
+ }
2655
+ if (!accountId) {
2656
+ console.error(`resolveAgentsMdPath: no account found with appId=${appId} in feishu.accounts keys=[${Object.keys(accounts).join(",")}]`);
2657
+ return null;
2658
+ }
2659
+ console.error(`resolveAgentsMdPath: found accountId=${accountId}`);
2660
+ const bindings = Array.isArray(config.bindings) ? config.bindings : [];
2661
+ let agentId;
2662
+ for (const b of bindings) {
2663
+ const binding = asRecord(b);
2664
+ if (!binding) continue;
2665
+ const match = asRecord(binding.match);
2666
+ if (match?.channel === "feishu" && match?.accountId === accountId) {
2667
+ if (typeof binding.agentId === "string") {
2668
+ agentId = binding.agentId;
2669
+ break;
2670
+ }
2671
+ }
2672
+ }
2673
+ if (!agentId) {
2674
+ console.error(`resolveAgentsMdPath: no binding found for accountId=${accountId} in bindings(count=${bindings.length})`);
2675
+ return null;
2676
+ }
2677
+ console.error(`resolveAgentsMdPath: found agentId=${agentId}`);
2678
+ if (agentId === "main") {
2679
+ console.error("resolveAgentsMdPath: case=multi-agent-main");
2680
+ return node_path.default.join(WORKSPACE_DIR, "workspace", "AGENTS.md");
2681
+ }
2682
+ const agentsObj = asRecord(config.agents);
2683
+ const list = Array.isArray(agentsObj?.list) ? agentsObj.list : [];
2684
+ for (const a of list) {
2685
+ const agent = asRecord(a);
2686
+ if (agent?.id === agentId) {
2687
+ const ws = typeof agent.workspace === "string" ? agent.workspace : node_path.default.join(WORKSPACE_DIR, "workspace");
2688
+ console.error(`resolveAgentsMdPath: case=multi-agent-custom agentId=${agentId} workspace=${ws}`);
2689
+ return node_path.default.join(ws, "AGENTS.md");
2690
+ }
2691
+ }
2692
+ console.error(`resolveAgentsMdPath: agentId=${agentId} not found in agents.list(count=${list.length})`);
2693
+ return null;
2694
+ }
2695
+ function appendPeToAgentsMd(agentsMdPath) {
2696
+ const dir = node_path.default.dirname(agentsMdPath);
2697
+ if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
2698
+ const existing = node_fs.default.existsSync(agentsMdPath) ? node_fs.default.readFileSync(agentsMdPath, "utf-8") : "";
2699
+ if (existing.includes(`<lark-cli-pe>`)) {
2700
+ console.error(`lark-cli-init: <${PE_XML_TAG}> already present in ${agentsMdPath}, skipping`);
2701
+ return;
2702
+ }
2703
+ const sep = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
2704
+ node_fs.default.appendFileSync(agentsMdPath, `${sep}${PE_PLACEHOLDER}`, "utf-8");
2705
+ console.error(`lark-cli-init: appended PE placeholder to ${agentsMdPath}`);
2706
+ }
2707
+ /**
2708
+ * Collect every Feishu bot appId declared in the openclaw config.
2709
+ * Covers both single-agent (channels.feishu.appId) and multi-agent
2710
+ * (channels.feishu.accounts[*].appId) layouts. Returns a deduplicated list.
2711
+ */
2712
+ function collectFeishuAppIds(configPath) {
2713
+ const config = readConfig(configPath ?? CONFIG_PATH);
2714
+ if (!config) return [];
2715
+ const feishu = getNestedMap(config, "channels", "feishu");
2716
+ if (!feishu) return [];
2717
+ const appIds = /* @__PURE__ */ new Set();
2718
+ const topAppId = feishu.appId;
2719
+ if (typeof topAppId === "string" && topAppId.trim()) appIds.add(topAppId.trim());
2720
+ const accounts = asRecord(feishu.accounts);
2721
+ if (accounts) for (const val of Object.values(accounts)) {
2722
+ const appId = asRecord(val)?.appId;
2723
+ if (typeof appId === "string" && appId.trim()) appIds.add(appId.trim());
2724
+ }
2725
+ return [...appIds];
2726
+ }
2727
+ function runLarkCliInit(opts) {
2728
+ const configPath = opts.configPath ?? CONFIG_PATH;
2729
+ if (!isLarkPluginInstalled(configPath)) {
2730
+ console.error("lark-cli-init: skipping — openclaw-lark plugin not installed");
2731
+ return {
2732
+ ok: true,
2733
+ skipped: true,
2734
+ skipReason: "openclaw-lark plugin not installed"
2735
+ };
2736
+ }
2737
+ if (!isLarkCliAvailable$2()) {
2738
+ console.error("lark-cli-init: skipping — lark-cli command not found");
2739
+ return {
2740
+ ok: true,
2741
+ skipped: true,
2742
+ skipReason: "lark-cli command not found"
2743
+ };
2744
+ }
2745
+ const config = readConfig(configPath);
2746
+ if (!config) return {
2747
+ ok: false,
2748
+ error: `could not read config at ${configPath}`
2749
+ };
2750
+ const agentsMdPath = resolveAgentsMdPath(opts.appId, config);
2751
+ console.error(`lark-cli-init: resolved agents.md path=${agentsMdPath ?? "(null)"}`);
2752
+ if (!agentsMdPath) return {
2753
+ ok: false,
2754
+ error: `could not resolve agents.md path for appId=${opts.appId}`
2755
+ };
2756
+ const appSecret = resolveAppSecret(opts.appId, config, opts.feishuAppSecret);
2757
+ if (!appSecret) return {
2758
+ ok: false,
2759
+ error: `could not resolve appSecret for appId=${opts.appId}`
2760
+ };
2761
+ console.error(`lark-cli-init: running lark-cli config init --name ${opts.appId} --app-id ${opts.appId} --brand feishu --app-secret-stdin --force-init`);
2762
+ const initRes = (0, node_child_process.spawnSync)("lark-cli", [
2763
+ "config",
2764
+ "init",
2765
+ "--name",
2766
+ opts.appId,
2767
+ "--app-id",
2768
+ opts.appId,
2769
+ "--brand",
2770
+ "feishu",
2771
+ "--app-secret-stdin",
2772
+ "--force-init"
2773
+ ], {
2774
+ stdio: [
2775
+ "pipe",
2776
+ "pipe",
2777
+ "pipe"
2778
+ ],
2779
+ encoding: "utf-8",
2780
+ input: appSecret
2781
+ });
2782
+ const configInitStdout = initRes.stdout?.trim() || void 0;
2783
+ const configInitStderr = initRes.stderr?.trim() || void 0;
2784
+ if (configInitStdout) console.error(`lark-cli config init stdout: ${configInitStdout}`);
2785
+ if (configInitStderr) console.error(`lark-cli config init stderr: ${configInitStderr}`);
2786
+ if (initRes.error) return {
2787
+ ok: false,
2788
+ configInitStdout,
2789
+ configInitStderr,
2790
+ error: `lark-cli config init spawn error: ${initRes.error.message}`
2791
+ };
2792
+ if (initRes.status !== 0) return {
2793
+ ok: false,
2794
+ configInitExitCode: initRes.status ?? void 0,
2795
+ configInitStdout,
2796
+ configInitStderr,
2797
+ error: `lark-cli config init exited with code ${initRes.status}`
2798
+ };
2799
+ appendPeToAgentsMd(agentsMdPath);
2800
+ return {
2801
+ ok: true,
2802
+ configInitExitCode: 0,
2803
+ agentsMdPath
2804
+ };
2805
+ }
2806
+ //#endregion
2807
+ //#region src/rules/agents-md-lark-cli-pe.ts
2808
+ function isLarkCliAvailable$1() {
2809
+ try {
2810
+ return (0, node_child_process.spawnSync)("lark-cli", ["--version"], {
2811
+ encoding: "utf-8",
2812
+ timeout: 5e3,
2813
+ stdio: [
2814
+ "ignore",
2815
+ "pipe",
2816
+ "ignore"
2817
+ ]
2818
+ }).status === 0;
2819
+ } catch {
2820
+ return false;
2821
+ }
2822
+ }
2823
+ let AgentsMdLarkCliPeRule = class AgentsMdLarkCliPeRule extends DiagnoseRule {
2824
+ validate(ctx) {
2825
+ if (!isLarkCliAvailable$1()) return { pass: true };
2826
+ const missingPath = collectExistingAgentsMdPaths(ctx).find((filePath) => {
2827
+ return !node_fs.default.readFileSync(filePath, "utf-8").includes(`<${PE_XML_TAG}>`);
2828
+ });
2829
+ if (!missingPath) return { pass: true };
2830
+ return {
2831
+ pass: false,
2832
+ message: `${missingPath} 中缺少 lark-cli-pe PE 内容,需要追加`
2833
+ };
2834
+ }
2835
+ repair(ctx) {
2836
+ if (!isLarkCliAvailable$1()) return;
2837
+ for (const filePath of collectExistingAgentsMdPaths(ctx)) {
2838
+ const content = node_fs.default.readFileSync(filePath, "utf-8");
2839
+ if (content.includes(`<lark-cli-pe>`)) continue;
2840
+ const sep = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
2841
+ node_fs.default.appendFileSync(filePath, `${sep}${PE_PLACEHOLDER}`, "utf-8");
2842
+ console.error(`agents-md-lark-cli-pe: appended PE to ${filePath}`);
2843
+ }
2844
+ }
2845
+ };
2846
+ AgentsMdLarkCliPeRule = __decorate([Rule({
2847
+ key: "agents_md_lark_cli_pe",
2848
+ description: "检测各智能体 AGENTS.md 中是否缺失 lark-cli-pe PE 内容,lark-cli 存在时自动追加",
2849
+ dependsOn: ["config_syntax_check"],
2850
+ repairMode: "standard",
2851
+ level: "silent"
2852
+ })], AgentsMdLarkCliPeRule);
2853
+ //#endregion
2523
2854
  //#region src/rules/miaoda-official-plugins-install-spec-unlock.ts
2524
2855
  /**
2525
2856
  * Official miaoda-side plugins that must track manifest — version-locked specs
@@ -2623,11 +2954,11 @@ function getAllow$1(config) {
2623
2954
  //#region src/rules/lark-plugin-allow.ts
2624
2955
  const LARK_PLUGIN = "openclaw-lark";
2625
2956
  const LEGACY_LARK_PLUGIN = "feishu-openclaw-plugin";
2626
- const LARK_PLUGIN_NAMES$1 = [LARK_PLUGIN, LEGACY_LARK_PLUGIN];
2957
+ const LARK_PLUGIN_NAMES = [LARK_PLUGIN, LEGACY_LARK_PLUGIN];
2627
2958
  let LarkPluginAllowRule = class LarkPluginAllowRule extends DiagnoseRule {
2628
2959
  validate(ctx) {
2629
2960
  const allow = getAllow(ctx.config);
2630
- if (LARK_PLUGIN_NAMES$1.some((name) => allow.includes(name))) return { pass: true };
2961
+ if (LARK_PLUGIN_NAMES.some((name) => allow.includes(name))) return { pass: true };
2631
2962
  const installed = detectInstalledLarkPlugin(getExtensionsDir(ctx.configPath));
2632
2963
  if (installed == null) return { pass: true };
2633
2964
  return {
@@ -2646,7 +2977,7 @@ let LarkPluginAllowRule = class LarkPluginAllowRule extends DiagnoseRule {
2646
2977
  const rawAllow = pluginsMap.allow;
2647
2978
  const original = Array.isArray(rawAllow) ? rawAllow : [];
2648
2979
  const stringAllow = original.filter((e) => typeof e === "string");
2649
- if (LARK_PLUGIN_NAMES$1.some((name) => stringAllow.includes(name))) return;
2980
+ if (LARK_PLUGIN_NAMES.some((name) => stringAllow.includes(name))) return;
2650
2981
  original.push(installed);
2651
2982
  pluginsMap.allow = original;
2652
2983
  }
@@ -3347,6 +3678,7 @@ function resolveUpgradeDirection(installed, ocCur, recommendedOc, isLegacy) {
3347
3678
  /** 提取公共前置上下文;任何前置条件不满足时返回 null(规则 pass)。 */
3348
3679
  function resolveCompatContext(ctx) {
3349
3680
  const recommendedOc = ctx.vars.recommendedOpenclawTag;
3681
+ if (!recommendedOc) return null;
3350
3682
  const ocCur = getOcVersion();
3351
3683
  if (!ocCur) return null;
3352
3684
  const installed = getInstalledPlugin(ctx);
@@ -3369,7 +3701,6 @@ let FeishuPluginOpenclawUpgradeRule = class FeishuPluginOpenclawUpgradeRule exte
3369
3701
  if (!cc) return { pass: true };
3370
3702
  const { ocCur, recommendedOc, installed, isLegacy } = cc;
3371
3703
  if (isForkPlugin(installed)) return validateForkPlugin(installed, ocCur, recommendedOc);
3372
- if (!recommendedOc) return { pass: true };
3373
3704
  if (resolveUpgradeDirection(installed, ocCur, recommendedOc, isLegacy) !== "openclaw") return { pass: true };
3374
3705
  return {
3375
3706
  pass: false,
@@ -3397,14 +3728,6 @@ let FeishuPluginLarkUpgradeRule = class FeishuPluginLarkUpgradeRule extends Diag
3397
3728
  if (!cc) return { pass: true };
3398
3729
  const { ocCur, recommendedOc, installed, isLegacy } = cc;
3399
3730
  if (isForkPlugin(installed)) return { pass: true };
3400
- if (!recommendedOc) {
3401
- if (isLegacy || !isVersionCompatible(installed, ocCur)) return {
3402
- pass: false,
3403
- action: "upgrade_lark",
3404
- message: `${buildCompatPrefix(installed, ocCur, isLegacy)};建议升级飞书插件至兼容版本`
3405
- };
3406
- return { pass: true };
3407
- }
3408
3731
  if (resolveUpgradeDirection(installed, ocCur, recommendedOc, isLegacy) !== "lark") return { pass: true };
3409
3732
  return {
3410
3733
  pass: false,
@@ -3459,16 +3782,14 @@ function describeCompatConstraint(entry, pluginVersion) {
3459
3782
  /**
3460
3783
  * @lark-apaas/openclaw-lark 豁免 VERSION_COMPAT_MAP,但仍要求 openclaw ≥ FORK_LARK_PLUGIN_MIN_OC_VERSION。
3461
3784
  * 其他 @lark-apaas scope 的 fork 插件继续无条件 pass。
3462
- * recommendedOc 可为 undefined(doctor 模式),此时只检测最低版本要求,不指定目标升级版本。
3463
3785
  */
3464
3786
  function validateForkPlugin(installed, ocCur, recommendedOc) {
3465
3787
  if (installed.fullName !== FORK_LARK_PLUGIN_FULL_NAME) return { pass: true };
3466
3788
  if (compareCalVer(ocCur, FORK_LARK_PLUGIN_MIN_OC_VERSION) >= 0) return { pass: true };
3467
- const recommendation = recommendedOc ? `;将 openclaw 升级到 ${recommendedOc} 即可满足` : `;请升级 openclaw 至 ${FORK_LARK_PLUGIN_MIN_OC_VERSION} 或更高版本`;
3468
3789
  return {
3469
3790
  pass: false,
3470
3791
  action: "upgrade_openclaw",
3471
- message: `飞书插件 ${describePlugin(installed)}(fork 版)要求 openclaw ≥ ${FORK_LARK_PLUGIN_MIN_OC_VERSION},当前 openclaw@${ocCur} 低于此要求${recommendation}`
3792
+ message: `飞书插件 ${describePlugin(installed)}(fork 版)要求 openclaw ≥ ${FORK_LARK_PLUGIN_MIN_OC_VERSION},当前 openclaw@${ocCur} 低于此要求;将 openclaw 升级到 ${recommendedOc} 即可满足`
3472
3793
  };
3473
3794
  }
3474
3795
  function describePlugin(p) {
@@ -3515,181 +3836,6 @@ function extractScopedNameFromSpec$1(spec) {
3515
3836
  const at = spec.indexOf("@", 1);
3516
3837
  return at === -1 ? spec : spec.slice(0, at);
3517
3838
  }
3518
- /**
3519
- * Returns true if the installed feishu plugin is version-incompatible with
3520
- * the current openclaw (or is a legacy plugin that must be replaced).
3521
- * Used by the upgrade_lark_needed rule and the upgrade-lark pre-check gate.
3522
- */
3523
- function needsLarkUpgrade(ctx) {
3524
- const cc = resolveCompatContext(ctx);
3525
- if (!cc) return false;
3526
- const { ocCur, recommendedOc, installed, isLegacy } = cc;
3527
- if (isForkPlugin(installed)) {
3528
- if (recommendedOc) return false;
3529
- if (installed.fullName === FORK_LARK_PLUGIN_FULL_NAME) return compareCalVer(ocCur, FORK_LARK_PLUGIN_MIN_OC_VERSION) < 0;
3530
- return false;
3531
- }
3532
- if (recommendedOc) return resolveUpgradeDirection(installed, ocCur, recommendedOc, isLegacy) === "lark";
3533
- return isLegacy || !isVersionCompatible(installed, ocCur);
3534
- }
3535
- //#endregion
3536
- //#region src/channels-probe.ts
3537
- const FEISHU_INVALID_CONFIG_MSG = "channels.feishu: invalid config: must NOT have additional properties";
3538
- const CHANNEL_LINE_RE = /^-\s+Feishu\s+([^:]+):\s+(.+)$/;
3539
- /**
3540
- * Port of Python `_account_is_working` from the feishu-channel-success-rate skill.
3541
- *
3542
- * Strips colon-prefixed key:value bits (dm:, bot:, in:, out:, token:, allow:,
3543
- * intents:, groups:, health:) and evaluates the canonical health formula.
3544
- */
3545
- function accountIsWorking(bits) {
3546
- const bitTokens = /* @__PURE__ */ new Set();
3547
- let hasError = false;
3548
- let hasProbeFailed = false;
3549
- for (const raw of bits) {
3550
- const b = raw.trim();
3551
- if (!b) continue;
3552
- if (b.startsWith("error:")) {
3553
- hasError = true;
3554
- continue;
3555
- }
3556
- if (b === "probe failed") {
3557
- hasProbeFailed = true;
3558
- continue;
3559
- }
3560
- bitTokens.add(b.split(":")[0]);
3561
- }
3562
- if (!bitTokens.has("enabled") || !bitTokens.has("configured")) return false;
3563
- if (bitTokens.has("works")) return true;
3564
- if (bitTokens.has("running") && !hasError && !hasProbeFailed) return true;
3565
- return false;
3566
- }
3567
- /**
3568
- * Parse the raw stdout of `openclaw channels status --probe`.
3569
- * Port of Python `extract_channels_probe` from the feishu-channel-success-rate skill.
3570
- */
3571
- function parseChannelsProbeOutput(text) {
3572
- const gatewayReachable = text.includes("Gateway reachable");
3573
- const feishuConfigInvalid = text.includes(FEISHU_INVALID_CONFIG_MSG);
3574
- const accounts = [];
3575
- let anyAccountWorking = false;
3576
- for (const line of text.split("\n")) {
3577
- const m = CHANNEL_LINE_RE.exec(line.trim());
3578
- if (!m) continue;
3579
- const [, acct, rest] = m;
3580
- const bits = rest.split(",").map((b) => b.trim());
3581
- const isWorking = accountIsWorking(bits);
3582
- if (isWorking) anyAccountWorking = true;
3583
- accounts.push({
3584
- id: acct.trim(),
3585
- bits,
3586
- isWorking,
3587
- raw: line.trim()
3588
- });
3589
- }
3590
- return {
3591
- gatewayReachable,
3592
- feishuConfigInvalid,
3593
- accounts,
3594
- anyAccountWorking
3595
- };
3596
- }
3597
- /**
3598
- * Run `openclaw channels status --probe` and return a structured result.
3599
- *
3600
- * The command may exit non-zero when some bot accounts fail their probe — that
3601
- * is still useful output. We therefore try to parse stdout even when the
3602
- * process exits with a non-zero code, falling back to an unavailable result
3603
- * only when there is genuinely no output to parse.
3604
- *
3605
- * @param timeoutMs Maximum wait time. Default is 60 s because v2026.4.x
3606
- * lacks a per-request HTTP timeout and can block indefinitely.
3607
- */
3608
- function runChannelsProbe(timeoutMs = 6e4) {
3609
- let stdout = "";
3610
- let execError;
3611
- try {
3612
- stdout = (0, node_child_process.execSync)("openclaw channels status --probe", {
3613
- encoding: "utf-8",
3614
- timeout: timeoutMs,
3615
- stdio: [
3616
- "ignore",
3617
- "pipe",
3618
- "pipe"
3619
- ]
3620
- });
3621
- } catch (e) {
3622
- const err = e;
3623
- const stdoutRaw = err.stdout;
3624
- stdout = typeof stdoutRaw === "string" ? stdoutRaw : stdoutRaw?.toString("utf-8") ?? "";
3625
- execError = err.message;
3626
- const stderrRaw = err.stderr;
3627
- const stderr = (typeof stderrRaw === "string" ? stderrRaw : stderrRaw?.toString("utf-8") ?? "").trim();
3628
- if (stderr) console.error(`channels-probe: stderr from CLI: ${stderr}`);
3629
- }
3630
- if (stdout.trim()) return {
3631
- available: true,
3632
- ...parseChannelsProbeOutput(stdout)
3633
- };
3634
- return {
3635
- available: false,
3636
- gatewayReachable: false,
3637
- feishuConfigInvalid: false,
3638
- accounts: [],
3639
- anyAccountWorking: false,
3640
- error: execError ?? "no output from openclaw channels status --probe"
3641
- };
3642
- }
3643
- //#endregion
3644
- //#region src/rules/upgrade-lark-needed.ts
3645
- /**
3646
- * Detects the condition that warrants running `upgrade-lark`:
3647
- * - feishu plugin version incompatible with current openclaw, OR
3648
- * - openclaw channels status --probe reports feishu channel config invalid; AND
3649
- * - channels are not working.
3650
- *
3651
- * Both conditions must be true simultaneously. If version is compatible and
3652
- * feishu config is valid, or channels are working, the rule passes (no action needed).
3653
- *
3654
- * feishuConfigInvalid is read from the channels probe output rather than running a
3655
- * separate `openclaw status` call, since only `openclaw channels status --probe`
3656
- * reliably surfaces the schema validation error.
3657
- *
3658
- * profile: experimental — runs only in full sweep mode, not in standard doctor.
3659
- * level: silent — telemetry/sweep-only, does not trigger page-level repair UI.
3660
- */
3661
- let UpgradeLarkNeededRule = class UpgradeLarkNeededRule extends DiagnoseRule {
3662
- validate(ctx) {
3663
- let versionIncompatible = false;
3664
- try {
3665
- versionIncompatible = needsLarkUpgrade(ctx);
3666
- } catch {
3667
- versionIncompatible = true;
3668
- }
3669
- let probeResult;
3670
- try {
3671
- probeResult = runChannelsProbe(3e4);
3672
- } catch {
3673
- return { pass: true };
3674
- }
3675
- const feishuConfigInvalid = probeResult.feishuConfigInvalid;
3676
- if (!(versionIncompatible || feishuConfigInvalid)) return { pass: true };
3677
- if (probeResult.anyAccountWorking) return { pass: true };
3678
- return {
3679
- pass: false,
3680
- action: "upgrade_lark",
3681
- message: `飞书插件需要升级且 channels 不可用(版本不兼容=${versionIncompatible}, feishu配置无效=${feishuConfigInvalid}),建议执行 upgrade-lark 命令升级飞书插件`
3682
- };
3683
- }
3684
- };
3685
- UpgradeLarkNeededRule = __decorate([Rule({
3686
- key: "upgrade_lark_needed",
3687
- description: "检测飞书插件版本不兼容且 channels 不可用,判断是否需要执行 upgrade-lark 升级",
3688
- repairMode: "check-only",
3689
- level: "silent",
3690
- profile: "experimental",
3691
- usesVars: ["recommendedOpenclawTag"]
3692
- })], UpgradeLarkNeededRule);
3693
3839
  //#endregion
3694
3840
  //#region src/rules/cleanup-install-backup-dirs.ts
3695
3841
  const DIR_PREFIX = ".openclaw-install-";
@@ -3773,7 +3919,7 @@ function extractScopedNameFromSpec(spec) {
3773
3919
  const at = spec.indexOf("@", 1);
3774
3920
  return at === -1 ? spec : spec.slice(0, at);
3775
3921
  }
3776
- function isLarkCliAvailable$1() {
3922
+ function isLarkCliAvailable() {
3777
3923
  try {
3778
3924
  return (0, node_child_process.spawnSync)(LARK_CLI_NAME$1, ["--version"], {
3779
3925
  encoding: "utf-8",
@@ -3814,26 +3960,137 @@ function installLarkCliOnce(tag) {
3814
3960
  let LarkCliMissingForInstalledLarkPluginRule = class LarkCliMissingForInstalledLarkPluginRule extends DiagnoseRule {
3815
3961
  validate(ctx) {
3816
3962
  if (!isTargetForkPlugin(readInstalledLarkPlugin(ctx))) return { pass: true };
3817
- if (isLarkCliAvailable$1()) return { pass: true };
3963
+ if (isLarkCliAvailable()) return { pass: true };
3964
+ return {
3965
+ pass: false,
3966
+ message: `${FORK_PACKAGE_NAME}@${TARGET_VERSION} 已安装,但 lark-cli 不可用;将执行一次 lark-cli 安装`
3967
+ };
3968
+ }
3969
+ repair(ctx) {
3970
+ if (!isTargetForkPlugin(readInstalledLarkPlugin(ctx))) return;
3971
+ if (isLarkCliAvailable()) return;
3972
+ installLarkCliOnce(ctx.vars.recommendedOpenclawTag ?? TARGET_VERSION);
3973
+ }
3974
+ };
3975
+ LarkCliMissingForInstalledLarkPluginRule = __decorate([Rule({
3976
+ key: "lark_cli_missing_for_installed_lark_plugin",
3977
+ description: "检测特定飞书插件版本已安装但 lark-cli 缺失的环境,并自动安装 lark-cli 一次",
3978
+ dependsOn: ["config_syntax_check"],
3979
+ repairMode: "standard",
3980
+ level: "critical",
3981
+ usesVars: ["recommendedOpenclawTag"]
3982
+ })], LarkCliMissingForInstalledLarkPluginRule);
3983
+ //#endregion
3984
+ //#region src/rules/feishu-bot-channel-config.ts
3985
+ /**
3986
+ * Ensures each bot account's channel config is correct:
3987
+ * 1. `allowFrom` contains its own `creatorOpenID` from larkApps
3988
+ * 2. `appSecret` is either the canonical provider-ref or matches larkApps plaintext
3989
+ *
3990
+ * Covers both multi-account (channels.feishu.accounts) and single-account
3991
+ * (channels.feishu.appId + allowFrom at top level) layouts.
3992
+ */
3993
+ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends DiagnoseRule {
3994
+ validate(ctx) {
3995
+ const larkApps = ctx.vars.larkApps;
3996
+ if (!larkApps || larkApps.length === 0) return { pass: true };
3997
+ const feishu = asRecord(getNestedMap(ctx.config, "channels", "feishu"));
3998
+ if (!feishu) return { pass: true };
3999
+ const issues = [];
4000
+ const accounts = asRecord(feishu.accounts);
4001
+ if (accounts) for (const [accountId, account] of Object.entries(accounts)) {
4002
+ const bot = asRecord(account);
4003
+ if (!bot) continue;
4004
+ const appId = bot.appId;
4005
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
4006
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
4007
+ if (!larkApp) continue;
4008
+ this.checkBot(accountId, bot, larkApp, issues);
4009
+ }
4010
+ const singleAppId = feishu.appId;
4011
+ if (typeof singleAppId === "string" && singleAppId.startsWith("cli_") && !accounts) {
4012
+ const larkApp = larkApps.find((e) => e.larkAppID === singleAppId);
4013
+ if (larkApp) this.checkBot("feishu", feishu, larkApp, issues);
4014
+ }
4015
+ if (issues.length === 0) return { pass: true };
3818
4016
  return {
3819
4017
  pass: false,
3820
- message: `${FORK_PACKAGE_NAME}@${TARGET_VERSION} 已安装,但 lark-cli 不可用;将执行一次 lark-cli 安装`
4018
+ message: issues.join("; ")
3821
4019
  };
3822
4020
  }
4021
+ /** Check a single bot entry (either an account object or the feishu channel itself).
4022
+ * appSecret is validated based on its current type:
4023
+ * - object → must match canonical provider-ref
4024
+ * - string → must match larkApps plaintext
4025
+ */
4026
+ checkBot(label, bot, larkApp, issues) {
4027
+ const creatorOpenID = larkApp.creatorOpenID;
4028
+ const allowFrom = Array.isArray(bot.allowFrom) ? bot.allowFrom : [];
4029
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
4030
+ if (!allowFrom.includes(creatorOpenID)) issues.push(`${label} allowFrom missing creatorOpenID ${creatorOpenID.length > 8 ? creatorOpenID.slice(0, 4) + "***" + creatorOpenID.slice(-4) : "***"}`);
4031
+ } else if (allowFrom.length === 0) issues.push(`${label} allowFrom is empty (creatorOpenID unavailable, cannot auto-fix)`);
4032
+ const secret = bot.appSecret;
4033
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
4034
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) issues.push(`${label} appSecret is a provider-ref but not the canonical one`);
4035
+ } else if (typeof secret === "string") {
4036
+ if (secret !== larkApp.appSecret) issues.push(`${label} appSecret plaintext mismatch`);
4037
+ } else issues.push(`${label} appSecret has unexpected type ${typeof secret}`);
4038
+ }
3823
4039
  repair(ctx) {
3824
- if (!isTargetForkPlugin(readInstalledLarkPlugin(ctx))) return;
3825
- if (isLarkCliAvailable$1()) return;
3826
- installLarkCliOnce(ctx.vars.recommendedOpenclawTag ?? TARGET_VERSION);
4040
+ const larkApps = ctx.vars.larkApps;
4041
+ if (!larkApps || larkApps.length === 0) return;
4042
+ const feishu = asRecord(getNestedMap(ctx.config, "channels", "feishu"));
4043
+ if (!feishu) return;
4044
+ const accounts = asRecord(feishu.accounts);
4045
+ if (accounts) for (const [, account] of Object.entries(accounts)) {
4046
+ const bot = asRecord(account);
4047
+ if (!bot) continue;
4048
+ const appId = bot.appId;
4049
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
4050
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
4051
+ if (!larkApp) continue;
4052
+ this.fixBot(bot, larkApp);
4053
+ }
4054
+ const singleAppId = feishu.appId;
4055
+ if (typeof singleAppId === "string" && singleAppId.startsWith("cli_") && !accounts) {
4056
+ const larkApp = larkApps.find((e) => e.larkAppID === singleAppId);
4057
+ if (larkApp) this.fixBot(feishu, larkApp);
4058
+ }
4059
+ }
4060
+ /** Fix a single bot entry in-place.
4061
+ * appSecret is repaired based on its current type:
4062
+ * - object → fix to canonical provider-ref
4063
+ * - string → fix to larkApps plaintext
4064
+ */
4065
+ fixBot(bot, larkApp) {
4066
+ const creatorOpenID = larkApp.creatorOpenID;
4067
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
4068
+ const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
4069
+ if (!allowFrom.includes(creatorOpenID)) {
4070
+ allowFrom.push(creatorOpenID);
4071
+ bot.allowFrom = allowFrom;
4072
+ }
4073
+ }
4074
+ const secret = bot.appSecret;
4075
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
4076
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
4077
+ } else if (typeof secret === "string") {
4078
+ if (secret !== larkApp.appSecret) bot.appSecret = larkApp.appSecret;
4079
+ }
3827
4080
  }
3828
4081
  };
3829
- LarkCliMissingForInstalledLarkPluginRule = __decorate([Rule({
3830
- key: "lark_cli_missing_for_installed_lark_plugin",
3831
- description: "检测特定飞书插件版本已安装但 lark-cli 缺失的环境,并自动安装 lark-cli 一次",
3832
- dependsOn: ["config_syntax_check"],
4082
+ FeishuBotChannelConfigRule = __decorate([Rule({
4083
+ key: "feishu_bot_channel_config",
4084
+ description: "确保飞书配置中 bot 账号的 allowFrom 包含其创建者 openID 且 appSecret 值正确",
4085
+ dependsOn: [
4086
+ "config_syntax_check",
4087
+ "feishu_default_account",
4088
+ "feishu_bot_id"
4089
+ ],
3833
4090
  repairMode: "standard",
3834
- level: "critical",
3835
- usesVars: ["recommendedOpenclawTag"]
3836
- })], LarkCliMissingForInstalledLarkPluginRule);
4091
+ usesVars: ["larkApps"],
4092
+ level: "critical"
4093
+ })], FeishuBotChannelConfigRule);
3837
4094
  //#endregion
3838
4095
  //#region src/check.ts
3839
4096
  /** Telemetry-aware entry: returns both the legacy CheckResult (for stdout)
@@ -4275,33 +4532,6 @@ function finalize$1(results, aborted) {
4275
4532
  };
4276
4533
  }
4277
4534
  //#endregion
4278
- //#region src/paths.ts
4279
- /**
4280
- * Central directory for all ephemeral diagnose/reset artifacts: task status
4281
- * files (`reset-<taskId>.json`) and human-readable step logs
4282
- * (`reset-<taskId>.log`). Having everything under one dir makes debugging a
4283
- * stuck reset much easier — `ls /tmp/openclaw-diagnose/` shows every recent
4284
- * run, and each run's log is right next to its state.
4285
- */
4286
- const DIAGNOSE_DIR = "/tmp/openclaw-diagnose";
4287
- function resetResultFile(taskId) {
4288
- return `${DIAGNOSE_DIR}/reset-${taskId}.json`;
4289
- }
4290
- function resetLogFile(taskId) {
4291
- return `${DIAGNOSE_DIR}/reset-${taskId}.log`;
4292
- }
4293
- /** Sandbox workspace root where openclaw config + agent state lives. */
4294
- const WORKSPACE_DIR = "/home/gem/workspace/agent";
4295
- /** File containing the provider key used by the openclaw miaoda provider. */
4296
- const PROVIDER_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-provider-key";
4297
- /** File containing the miaoda openclaw secrets JSON. */
4298
- const SECRETS_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-openclaw-secrets.json";
4299
- /** Absolute path to the openclaw config JSON. */
4300
- const CONFIG_PATH = `${WORKSPACE_DIR}/openclaw.json`;
4301
- function upgradeLarkLogFile(runId) {
4302
- return `${DIAGNOSE_DIR}/upgrade-lark-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-")}-${runId.slice(0, 8)}.log`;
4303
- }
4304
- //#endregion
4305
4535
  //#region src/run-log.ts
4306
4536
  let currentRunContext;
4307
4537
  /**
@@ -4437,9 +4667,10 @@ function makeLogger(logFile) {
4437
4667
  /**
4438
4668
  * Start an async reset task: spawn a detached child process and return the taskId.
4439
4669
  *
4440
- * The child process runs: node cli.js reset --worker --task-id=xxx --ctx=base64
4670
+ * The child process runs: node cli.js reset --worker --task-id=xxx
4671
+ * The worker fetches ctx from innerApi itself — no --ctx passthrough.
4441
4672
  */
4442
- function startAsyncReset(ctxBase64) {
4673
+ function startAsyncReset() {
4443
4674
  const taskId = (0, node_crypto.randomUUID)();
4444
4675
  const resultFile = resetResultFile(taskId);
4445
4676
  const log = makeLogger(resetLogFile(taskId));
@@ -4463,8 +4694,7 @@ function startAsyncReset(ctxBase64) {
4463
4694
  process.argv[1],
4464
4695
  "reset",
4465
4696
  "--worker",
4466
- `--task-id=${taskId}`,
4467
- `--ctx=${ctxBase64}`
4697
+ `--task-id=${taskId}`
4468
4698
  ], {
4469
4699
  detached: true,
4470
4700
  stdio: "ignore",
@@ -4866,273 +5096,13 @@ function installOne$1(pkg, tarball, homeBase) {
4866
5096
  } catch {}
4867
5097
  throw e;
4868
5098
  }
4869
- const hadOld = node_fs.default.existsSync(destDir);
4870
- if (hadOld) moveSafe(destDir, oldDir);
4871
- moveSafe(stagingDir, destDir);
4872
- if (hadOld && node_fs.default.existsSync(oldDir)) node_fs.default.rmSync(oldDir, {
4873
- recursive: true,
4874
- force: true
4875
- });
4876
- }
4877
- //#endregion
4878
- //#region src/lark-cli-init.ts
4879
- const LARK_PLUGIN_NAMES = ["openclaw-lark", "feishu-openclaw-plugin"];
4880
- const PE_XML_TAG = "lark-cli-pe";
4881
- const PE_PLACEHOLDER = `
4882
- <${PE_XML_TAG}>
4883
- **【强制要求 - 无例外】飞书所有操作必须先读 feishu-lark-cli skill。未读skill不得调用任何飞书工具,违者视为操作失误。**
4884
- </${PE_XML_TAG}>
4885
- `;
4886
- function isLarkPluginInstalled(configPath) {
4887
- const extDir = getExtensionsDir(configPath);
4888
- return LARK_PLUGIN_NAMES.some((name) => {
4889
- try {
4890
- return node_fs.default.existsSync(node_path.default.join(extDir, name, "package.json"));
4891
- } catch {
4892
- return false;
4893
- }
4894
- });
4895
- }
4896
- function isLarkCliAvailable() {
4897
- try {
4898
- return (0, node_child_process.spawnSync)("lark-cli", ["--version"], {
4899
- encoding: "utf-8",
4900
- timeout: 5e3,
4901
- stdio: [
4902
- "ignore",
4903
- "pipe",
4904
- "ignore"
4905
- ]
4906
- }).status === 0;
4907
- } catch {
4908
- return false;
4909
- }
4910
- }
4911
- function readConfig(configPath) {
4912
- try {
4913
- const raw = node_fs.default.readFileSync(configPath, "utf-8");
4914
- const parsed = loadJSON5().parse(raw);
4915
- return parsed != null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
4916
- } catch {
4917
- return null;
4918
- }
4919
- }
4920
- /**
4921
- * Resolve the feishu app secret for the given appId.
4922
- *
4923
- * Lookup order:
4924
- * 1. channels.feishu.appSecret (single-agent: feishu.appId === appId)
4925
- * 2. channels.feishu.accounts[key].appSecret (multi-agent: account.appId === appId)
4926
- *
4927
- * Value interpretation:
4928
- * - string → use directly
4929
- * - object → secret is managed by a provider; use `feishuAppSecret` param instead
4930
- *
4931
- * Returns null when the secret cannot be determined.
4932
- */
4933
- function resolveAppSecret(appId, config, feishuAppSecret) {
4934
- const feishu = getNestedMap(config, "channels", "feishu");
4935
- if (!feishu) return null;
4936
- let rawSecret;
4937
- if (typeof feishu.appId === "string" && feishu.appId === appId) rawSecret = feishu.appSecret;
4938
- else {
4939
- const accounts = asRecord(feishu.accounts);
4940
- if (accounts) for (const [, val] of Object.entries(accounts)) {
4941
- const account = asRecord(val);
4942
- if (account?.appId === appId) {
4943
- rawSecret = account.appSecret ?? feishu.appSecret;
4944
- break;
4945
- }
4946
- }
4947
- }
4948
- if (typeof rawSecret === "string" && rawSecret) return rawSecret;
4949
- if (rawSecret != null && typeof rawSecret === "object") return feishuAppSecret ?? null;
4950
- return null;
4951
- }
4952
- /**
4953
- * Resolve the agents.md path for the given appId from the openclaw config.
4954
- *
4955
- * Case 1: appId matches channels.feishu.appId (single-agent path)
4956
- * → WORKSPACE_DIR/AGENTS.md
4957
- *
4958
- * Case 2: appId found in channels.feishu.accounts (multi-agent path)
4959
- * → find account key where account.appId === appId
4960
- * → find binding where match.channel=feishu && match.accountId=that key
4961
- * → if agentId === 'main' → WORKSPACE_DIR/agents.md
4962
- * → else find agent in agents.list by id → agent.workspace/agents.md
4963
- *
4964
- * Returns null when the path cannot be determined.
4965
- */
4966
- function resolveAgentsMdPath(appId, config) {
4967
- const feishu = getNestedMap(config, "channels", "feishu");
4968
- if (!feishu) {
4969
- console.error("resolveAgentsMdPath: channels.feishu not found");
4970
- return null;
4971
- }
4972
- if (typeof feishu.appId === "string" && feishu.appId === appId) {
4973
- console.error(`resolveAgentsMdPath: case=single-agent feishu.appId=${feishu.appId}`);
4974
- return node_path.default.join(WORKSPACE_DIR, "workspace", "AGENTS.md");
4975
- }
4976
- const accounts = asRecord(feishu.accounts);
4977
- if (!accounts) {
4978
- console.error("resolveAgentsMdPath: feishu.accounts not found");
4979
- return null;
4980
- }
4981
- let accountId;
4982
- for (const [key, val] of Object.entries(accounts)) if (asRecord(val)?.appId === appId) {
4983
- accountId = key;
4984
- break;
4985
- }
4986
- if (!accountId) {
4987
- console.error(`resolveAgentsMdPath: no account found with appId=${appId} in feishu.accounts keys=[${Object.keys(accounts).join(",")}]`);
4988
- return null;
4989
- }
4990
- console.error(`resolveAgentsMdPath: found accountId=${accountId}`);
4991
- const bindings = Array.isArray(config.bindings) ? config.bindings : [];
4992
- let agentId;
4993
- for (const b of bindings) {
4994
- const binding = asRecord(b);
4995
- if (!binding) continue;
4996
- const match = asRecord(binding.match);
4997
- if (match?.channel === "feishu" && match?.accountId === accountId) {
4998
- if (typeof binding.agentId === "string") {
4999
- agentId = binding.agentId;
5000
- break;
5001
- }
5002
- }
5003
- }
5004
- if (!agentId) {
5005
- console.error(`resolveAgentsMdPath: no binding found for accountId=${accountId} in bindings(count=${bindings.length})`);
5006
- return null;
5007
- }
5008
- console.error(`resolveAgentsMdPath: found agentId=${agentId}`);
5009
- if (agentId === "main") {
5010
- console.error("resolveAgentsMdPath: case=multi-agent-main");
5011
- return node_path.default.join(WORKSPACE_DIR, "workspace", "AGENTS.md");
5012
- }
5013
- const agentsObj = asRecord(config.agents);
5014
- const list = Array.isArray(agentsObj?.list) ? agentsObj.list : [];
5015
- for (const a of list) {
5016
- const agent = asRecord(a);
5017
- if (agent?.id === agentId) {
5018
- const ws = typeof agent.workspace === "string" ? agent.workspace : node_path.default.join(WORKSPACE_DIR, "workspace");
5019
- console.error(`resolveAgentsMdPath: case=multi-agent-custom agentId=${agentId} workspace=${ws}`);
5020
- return node_path.default.join(ws, "AGENTS.md");
5021
- }
5022
- }
5023
- console.error(`resolveAgentsMdPath: agentId=${agentId} not found in agents.list(count=${list.length})`);
5024
- return null;
5025
- }
5026
- function appendPeToAgentsMd(agentsMdPath) {
5027
- const dir = node_path.default.dirname(agentsMdPath);
5028
- if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
5029
- const existing = node_fs.default.existsSync(agentsMdPath) ? node_fs.default.readFileSync(agentsMdPath, "utf-8") : "";
5030
- if (existing.includes(`<${PE_XML_TAG}>`)) {
5031
- console.error(`lark-cli-init: <${PE_XML_TAG}> already present in ${agentsMdPath}, skipping`);
5032
- return;
5033
- }
5034
- const sep = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
5035
- node_fs.default.appendFileSync(agentsMdPath, `${sep}${PE_PLACEHOLDER}`, "utf-8");
5036
- console.error(`lark-cli-init: appended PE placeholder to ${agentsMdPath}`);
5037
- }
5038
- /**
5039
- * Collect every Feishu bot appId declared in the openclaw config.
5040
- * Covers both single-agent (channels.feishu.appId) and multi-agent
5041
- * (channels.feishu.accounts[*].appId) layouts. Returns a deduplicated list.
5042
- */
5043
- function collectFeishuAppIds(configPath) {
5044
- const config = readConfig(configPath ?? CONFIG_PATH);
5045
- if (!config) return [];
5046
- const feishu = getNestedMap(config, "channels", "feishu");
5047
- if (!feishu) return [];
5048
- const appIds = /* @__PURE__ */ new Set();
5049
- const topAppId = feishu.appId;
5050
- if (typeof topAppId === "string" && topAppId.trim()) appIds.add(topAppId.trim());
5051
- const accounts = asRecord(feishu.accounts);
5052
- if (accounts) for (const val of Object.values(accounts)) {
5053
- const appId = asRecord(val)?.appId;
5054
- if (typeof appId === "string" && appId.trim()) appIds.add(appId.trim());
5055
- }
5056
- return [...appIds];
5057
- }
5058
- function runLarkCliInit(opts) {
5059
- const configPath = opts.configPath ?? CONFIG_PATH;
5060
- if (!isLarkPluginInstalled(configPath)) {
5061
- console.error("lark-cli-init: skipping — openclaw-lark plugin not installed");
5062
- return {
5063
- ok: true,
5064
- skipped: true,
5065
- skipReason: "openclaw-lark plugin not installed"
5066
- };
5067
- }
5068
- if (!isLarkCliAvailable()) {
5069
- console.error("lark-cli-init: skipping — lark-cli command not found");
5070
- return {
5071
- ok: true,
5072
- skipped: true,
5073
- skipReason: "lark-cli command not found"
5074
- };
5075
- }
5076
- const config = readConfig(configPath);
5077
- if (!config) return {
5078
- ok: false,
5079
- error: `could not read config at ${configPath}`
5080
- };
5081
- const agentsMdPath = resolveAgentsMdPath(opts.appId, config);
5082
- console.error(`lark-cli-init: resolved agents.md path=${agentsMdPath ?? "(null)"}`);
5083
- if (!agentsMdPath) return {
5084
- ok: false,
5085
- error: `could not resolve agents.md path for appId=${opts.appId}`
5086
- };
5087
- const appSecret = resolveAppSecret(opts.appId, config, opts.feishuAppSecret);
5088
- if (!appSecret) return {
5089
- ok: false,
5090
- error: `could not resolve appSecret for appId=${opts.appId}`
5091
- };
5092
- console.error(`lark-cli-init: running lark-cli config init --name ${opts.appId} --app-id ${opts.appId} --brand feishu --app-secret-stdin --force-init`);
5093
- const initRes = (0, node_child_process.spawnSync)("lark-cli", [
5094
- "config",
5095
- "init",
5096
- "--name",
5097
- opts.appId,
5098
- "--app-id",
5099
- opts.appId,
5100
- "--brand",
5101
- "feishu",
5102
- "--app-secret-stdin",
5103
- "--force-init"
5104
- ], {
5105
- stdio: [
5106
- "pipe",
5107
- "pipe",
5108
- "pipe"
5109
- ],
5110
- encoding: "utf-8",
5111
- input: appSecret
5099
+ const hadOld = node_fs.default.existsSync(destDir);
5100
+ if (hadOld) moveSafe(destDir, oldDir);
5101
+ moveSafe(stagingDir, destDir);
5102
+ if (hadOld && node_fs.default.existsSync(oldDir)) node_fs.default.rmSync(oldDir, {
5103
+ recursive: true,
5104
+ force: true
5112
5105
  });
5113
- const configInitStdout = initRes.stdout?.trim() || void 0;
5114
- const configInitStderr = initRes.stderr?.trim() || void 0;
5115
- if (configInitStdout) console.error(`lark-cli config init stdout: ${configInitStdout}`);
5116
- if (configInitStderr) console.error(`lark-cli config init stderr: ${configInitStderr}`);
5117
- if (initRes.error) return {
5118
- ok: false,
5119
- configInitStdout,
5120
- configInitStderr,
5121
- error: `lark-cli config init spawn error: ${initRes.error.message}`
5122
- };
5123
- if (initRes.status !== 0) return {
5124
- ok: false,
5125
- configInitExitCode: initRes.status ?? void 0,
5126
- configInitStdout,
5127
- configInitStderr,
5128
- error: `lark-cli config init exited with code ${initRes.status}`
5129
- };
5130
- appendPeToAgentsMd(agentsMdPath);
5131
- return {
5132
- ok: true,
5133
- configInitExitCode: 0,
5134
- agentsMdPath
5135
- };
5136
5106
  }
5137
5107
  //#endregion
5138
5108
  //#region ../../openclaw-slardar/lib/client.js
@@ -6978,6 +6948,60 @@ function mergeCoreBackupAndOrigins(configPath, vars, resetData, log) {
6978
6948
  log(`allowedOrigins: added ${added.length} (${JSON.stringify(added)}), total now ${mergedOrigins.length}`);
6979
6949
  }
6980
6950
  /**
6951
+ * Fix bot account allowFrom and appSecret using larkApps from innerApi.
6952
+ *
6953
+ * For each bot account (key starts with `bot-cli_`):
6954
+ * - allowFrom must contain the bot's own creatorOpenID from larkApps
6955
+ * - appSecret must be either the canonical provider-ref or match larkApps plaintext
6956
+ *
6957
+ * Runs after mergeCoreBackupAndOrigins so it operates on the final config state.
6958
+ */
6959
+ function fixBotChannelConfig(configPath, larkApps, log) {
6960
+ if (!larkApps || larkApps.length === 0) {
6961
+ log("no larkApps data, skip bot channel config fix");
6962
+ return;
6963
+ }
6964
+ const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
6965
+ const accounts = asRecord(getNestedMap(config, "channels", "feishu")?.accounts);
6966
+ if (!accounts) {
6967
+ log("no feishu accounts in config, skip bot channel config fix");
6968
+ return;
6969
+ }
6970
+ let fixCount = 0;
6971
+ for (const [, account] of Object.entries(accounts)) {
6972
+ const bot = asRecord(account);
6973
+ if (!bot) continue;
6974
+ const appId = bot.appId;
6975
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
6976
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
6977
+ if (!larkApp) continue;
6978
+ const creatorOpenID = larkApp.creatorOpenID;
6979
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
6980
+ const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
6981
+ if (!allowFrom.includes(creatorOpenID)) {
6982
+ allowFrom.push(creatorOpenID);
6983
+ bot.allowFrom = allowFrom;
6984
+ fixCount++;
6985
+ }
6986
+ }
6987
+ const secret = bot.appSecret;
6988
+ let needsFix = false;
6989
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
6990
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
6991
+ } else if (typeof secret === "string") {
6992
+ if (secret !== larkApp.appSecret) needsFix = true;
6993
+ } else needsFix = true;
6994
+ if (needsFix) {
6995
+ bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
6996
+ fixCount++;
6997
+ }
6998
+ }
6999
+ if (fixCount > 0) {
7000
+ node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
7001
+ log(`fixed ${fixCount} bot channel config issue(s) (allowFrom/appSecret)`);
7002
+ } else log("bot channel config ok, no fixes needed");
7003
+ }
7004
+ /**
6981
7005
  * Step 7: Verify startup scripts landed in configDir/scripts/.
6982
7006
  *
6983
7007
  * Scripts are extracted directly to configDir/scripts/ during stageTemplate —
@@ -7122,6 +7146,7 @@ async function runReset(input, taskId, resultFile) {
7122
7146
  await step5InstallOpenclaw(openclawTag, ossFileMap, log);
7123
7147
  step(6);
7124
7148
  mergeCoreBackupAndOrigins(configPath, vars, resetData, log);
7149
+ fixBotChannelConfig(configPath, vars.larkApps, log);
7125
7150
  step(7);
7126
7151
  verifyStartupScripts(configDir, log);
7127
7152
  step(8);
@@ -7920,7 +7945,8 @@ function normalizeCtx(raw) {
7920
7945
  reset: {
7921
7946
  templateVars: r.reset.templateVars ?? {},
7922
7947
  coreBackup: r.reset.coreBackup
7923
- }
7948
+ },
7949
+ larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7924
7950
  };
7925
7951
  }
7926
7952
  const vars = r.vars ?? {};
@@ -7945,7 +7971,8 @@ function normalizeCtx(raw) {
7945
7971
  reset: {
7946
7972
  templateVars: resetData.templateVars ?? {},
7947
7973
  coreBackup: resetData.coreBackup
7948
- }
7974
+ },
7975
+ larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7949
7976
  };
7950
7977
  }
7951
7978
  function fillApp(src) {
@@ -8010,7 +8037,8 @@ function buildCheckInput(raw, configPathOverride) {
8010
8037
  providerFilePath: PROVIDER_FILE_PATH,
8011
8038
  secretsFilePath: SECRETS_FILE_PATH,
8012
8039
  templateVars: ctx.app.templateVars,
8013
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
8040
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
8041
+ larkApps: ctx.larkApps
8014
8042
  },
8015
8043
  templateVars: ctx.app.templateVars
8016
8044
  };
@@ -8042,7 +8070,8 @@ function buildRepairInput(raw, configPathOverride) {
8042
8070
  providerFilePath: PROVIDER_FILE_PATH,
8043
8071
  secretsFilePath: SECRETS_FILE_PATH,
8044
8072
  templateVars: ctx.app.templateVars,
8045
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
8073
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
8074
+ larkApps: ctx.larkApps
8046
8075
  },
8047
8076
  repairData: {
8048
8077
  secretsContent: ctx.secrets.secretsContent,
@@ -8078,7 +8107,8 @@ function buildResetInput(raw, configPathOverride) {
8078
8107
  providerFilePath: PROVIDER_FILE_PATH,
8079
8108
  secretsFilePath: SECRETS_FILE_PATH,
8080
8109
  templateVars: ctx.app.templateVars,
8081
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
8110
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
8111
+ larkApps: ctx.larkApps
8082
8112
  },
8083
8113
  resetData: {
8084
8114
  templateVars: ctx.reset.templateVars,
@@ -10388,7 +10418,7 @@ async function reportCliRun(opts) {
10388
10418
  //#region src/help.ts
10389
10419
  const BIN = "mclaw-diagnose";
10390
10420
  function versionBanner() {
10391
- return `v0.1.14-alpha.8`;
10421
+ return `v0.1.14-beta.0`;
10392
10422
  }
10393
10423
  const COMMANDS = [
10394
10424
  {
@@ -10492,16 +10522,12 @@ EXIT CODES
10492
10522
  hidden: true,
10493
10523
  summary: "Run rule-engine check only",
10494
10524
  help: `USAGE
10495
- ${BIN} check [--ctx=<base64>]
10525
+ ${BIN} check
10496
10526
 
10497
10527
  DESCRIPTION
10498
10528
  Runs the rule engine against the sandbox's current openclaw config and
10499
- returns { failedRules }. Used by sandbox_console's push-style callers
10500
- that already own the ctx — end-users should prefer \`doctor\`.
10501
-
10502
- OPTIONS
10503
- --ctx=<base64> Opaque ctx JSON (base64). When absent, fetched from
10504
- innerapi (same path as doctor).
10529
+ returns { failedRules }. Ctx is fetched from innerapi automatically.
10530
+ End-users should prefer \`doctor\`.
10505
10531
  `
10506
10532
  },
10507
10533
  {
@@ -10509,16 +10535,11 @@ OPTIONS
10509
10535
  hidden: true,
10510
10536
  summary: "Apply standard-mode repairs",
10511
10537
  help: `USAGE
10512
- ${BIN} repair [--ctx=<base64>]
10538
+ ${BIN} repair
10513
10539
 
10514
10540
  DESCRIPTION
10515
- Runs repair for the failing rules listed inside the ctx's repairData.
10516
- Intended for sandbox_console's push path — end-users should use
10517
- \`doctor --fix\` instead.
10518
-
10519
- OPTIONS
10520
- --ctx=<base64> Opaque ctx JSON (base64). When absent, fetched from
10521
- innerapi.
10541
+ Runs repair for the failing rules. Ctx is fetched from innerapi
10542
+ automatically. End-users should use \`doctor --fix\` instead.
10522
10543
  `
10523
10544
  },
10524
10545
  {
@@ -10526,14 +10547,15 @@ OPTIONS
10526
10547
  hidden: true,
10527
10548
  summary: "Re-initialize sandbox via the 9-step reset pipeline",
10528
10549
  help: `USAGE
10529
- ${BIN} reset --async [--ctx=<base64>]
10530
- ${BIN} reset --worker --task-id=<id> [--ctx=<base64>]
10550
+ ${BIN} reset --async
10551
+ ${BIN} reset --worker --task-id=<id>
10531
10552
 
10532
10553
  DESCRIPTION
10533
10554
  Two-phase pipeline driven asynchronously: the --async invocation spawns
10534
10555
  a detached worker and returns { taskId } immediately; the --worker
10535
10556
  invocation (spawned by --async) runs the actual 9 steps and writes
10536
10557
  progress to /tmp/openclaw-diagnose/reset-<taskId>.json.
10558
+ Ctx is fetched from innerapi automatically.
10537
10559
 
10538
10560
  Poll progress with \`${BIN} get_reset_task --task-id=<id>\`.
10539
10561
 
@@ -10541,7 +10563,6 @@ OPTIONS
10541
10563
  --async Start a detached worker and return taskId on stdout.
10542
10564
  --worker Internal — run the 9-step pipeline (launched by --async).
10543
10565
  --task-id=<id> Required with --worker; identifies the progress file.
10544
- --ctx=<base64> Opaque ctx JSON; fetched from innerapi when absent.
10545
10566
  `
10546
10567
  },
10547
10568
  {
@@ -10564,7 +10585,7 @@ OPTIONS
10564
10585
  hidden: true,
10565
10586
  summary: "Download + install the openclaw tarball",
10566
10587
  help: `USAGE
10567
- ${BIN} install-openclaw <tag> [--ctx=<base64> | --oss_file_map=<base64>]
10588
+ ${BIN} install-openclaw <tag> [--oss_file_map=<base64>]
10568
10589
 
10569
10590
  DESCRIPTION
10570
10591
  Downloads the openclaw@<tag> tgz via the signed OSS URL found in the
@@ -10576,9 +10597,9 @@ ARGUMENTS
10576
10597
  <tag> Openclaw version tag, e.g. 2026.4.11.
10577
10598
 
10578
10599
  OPTIONS
10579
- --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10580
10600
  --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi
10581
- entirely. Wins over --ctx when both provided.
10601
+ entirely. When absent, ossFileMap is fetched from
10602
+ innerapi automatically.
10582
10603
  `
10583
10604
  },
10584
10605
  {
@@ -10604,8 +10625,7 @@ OPTIONS
10604
10625
  --home_base=<dir> Override the /home/gem base (tests).
10605
10626
  --config_path=<p> Override the openclaw.json path (tests).
10606
10627
  --skip-config-update Leave plugins.installs in openclaw.json untouched.
10607
- --ctx=<base64> Opaque ctx; see install-openclaw for semantics.
10608
- --oss_file_map=... Pre-built OSS URL map (base64 JSON).
10628
+ --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10609
10629
  `
10610
10630
  },
10611
10631
  {
@@ -10632,7 +10652,6 @@ OPTIONS
10632
10652
  --cli=<name> CLI package to install by short name or scoped
10633
10653
  packageName (repeatable, at least one required).
10634
10654
  --home_base=<dir> Override the /home/gem base (tests).
10635
- --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10636
10655
  --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10637
10656
 
10638
10657
  EXAMPLES
@@ -10686,46 +10705,6 @@ OPTIONS
10686
10705
  EXIT CODES
10687
10706
  0 Success or skipped (prerequisites not met).
10688
10707
  1 Secret/path unresolvable, lark-cli failed, or config unreadable.
10689
- `
10690
- },
10691
- {
10692
- name: "upgrade-lark",
10693
- hidden: false,
10694
- summary: "Upgrade the Feishu/Lark plugin via @larksuite/openclaw-lark-tools",
10695
- help: `USAGE
10696
- ${BIN} upgrade-lark [--scene=<scene>] [--caller=<n>] [--trace-id=<id>]
10697
-
10698
- DESCRIPTION
10699
- Upgrades the Feishu/Lark plugin by running:
10700
- npx -y @larksuite/openclaw-lark-tools update --use-existing
10701
-
10702
- Before the upgrade, the following files are backed up:
10703
- - openclaw.json
10704
- - extensions/openclaw-lark/ (if present)
10705
- - extensions/feishu-openclaw-plugin/ (if present)
10706
- After the upgrade, the result is validated:
10707
- - feishu.accounts bot count must not decrease
10708
- - gateway config structure must remain valid (port/mode/bind/auth/trustedProxies)
10709
- If the upgrade command fails, or validation fails, the backed-up files are
10710
- restored to roll back the changes.
10711
-
10712
- Execution is logged to /tmp/openclaw-diagnose/upgrade-lark-<runId>.log.
10713
-
10714
- Output is a single JSON object on stdout:
10715
- { "ok": true, "stdout": "...", "stderr": "...", "logFile": "..." }
10716
- { "ok": false, "error": "...", "stderr": "...", "exitCode": 1,
10717
- "rollbackOk": true, "validationError": "...", "logFile": "..." }
10718
-
10719
- OPTIONS
10720
- --scene=<scene> Telemetry label forwarded to Slardar only.
10721
- Known values: PageUpgradeLark, etc. Custom strings accepted.
10722
- --caller=<name> Optional metadata passed to innerapi.
10723
- --trace-id=<id> Optional log-correlation id.
10724
-
10725
- EXIT CODES
10726
- 0 Success: upgrade ran and all validations passed.
10727
- 1 Failure: npx error, validation failed, or git commit failed.
10728
- File rollback was attempted (see rollbackOk in the JSON output).
10729
10708
  `
10730
10709
  },
10731
10710
  {
@@ -10759,41 +10738,6 @@ EXAMPLES
10759
10738
  ${BIN} rules # all rules
10760
10739
  ${BIN} rules --rule=gateway # single rule
10761
10740
  ${BIN} rules --rule=gateway --rule=feishu_channel # multiple rules
10762
- `
10763
- },
10764
- {
10765
- name: "channels-probe",
10766
- hidden: true,
10767
- summary: "Check feishu channel health via openclaw channels status --probe",
10768
- help: `USAGE
10769
- ${BIN} channels-probe [--timeout=<ms>]
10770
-
10771
- DESCRIPTION
10772
- Runs \`openclaw channels status --probe\` and returns a structured JSON
10773
- summary of whether the current environment's feishu channels are
10774
- configured and working correctly.
10775
-
10776
- Output:
10777
- {
10778
- "available": true,
10779
- "gatewayReachable": true,
10780
- "accounts": [
10781
- { "id": "default", "bits": ["enabled","configured","running","works"],
10782
- "isWorking": true, "raw": "- Feishu default: ..." }
10783
- ],
10784
- "anyAccountWorking": true
10785
- }
10786
-
10787
- An account is considered working when:
10788
- enabled ∧ configured ∧ ( works ∨ ( running ∧ no error: ∧ no probe failed ) )
10789
-
10790
- "available": false means the CLI invocation itself failed (openclaw not
10791
- found, gateway unreachable, or no parseable output returned).
10792
-
10793
- OPTIONS
10794
- --timeout=<ms> Max wait in milliseconds (default: 60000). The probe
10795
- can hang indefinitely on openclaw v2026.4.x due to a
10796
- missing per-request HTTP timeout — set this accordingly.
10797
10741
  `
10798
10742
  },
10799
10743
  {
@@ -10814,8 +10758,7 @@ OPTIONS
10814
10758
  --role=<role> Package role (e.g. template, config).
10815
10759
  --name=<name> Package name within the role.
10816
10760
  --dir=<dir> Target dir (defaults to dirname(pkg.installPath)).
10817
- --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10818
- --oss_file_map=... Pre-built OSS URL map (base64 JSON).
10761
+ --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10819
10762
  `
10820
10763
  }
10821
10764
  ];
@@ -10891,31 +10834,31 @@ function planVarsFields(opts = {}) {
10891
10834
  *
10892
10835
  * Per-command group needs:
10893
10836
  *
10894
- * doctor / check app (rule-driven)
10895
- * repair app + secrets (writes secretsContent / providerKeyContent)
10896
- * reset app + secrets + install + reset (the works)
10837
+ * doctor / check app + larkApps
10838
+ * repair app + secrets + larkApps
10839
+ * reset app + secrets + install + reset + larkApps
10897
10840
  * install-* install only
10898
10841
  *
10899
10842
  * Empty result (`{}`) means "no group needed" — the CLI can skip the
10900
10843
  * `fetchCtxViaInnerApi` call entirely and run with a synthetic empty ctx.
10901
- * Happens e.g. when the user pinned `--rule=<key>` to a vars-free rule on
10902
- * `doctor`.
10903
10844
  */
10904
10845
  function planCtxPopulate(opts) {
10905
10846
  if (opts.command === "install") return { install: true };
10906
10847
  const populate = {};
10907
- const appFields = planVarsFields({
10848
+ if (planVarsFields({
10908
10849
  disabled: opts.disabled,
10909
10850
  onlyRules: opts.onlyRules,
10910
10851
  profile: opts.profile
10911
- });
10912
- if (appFields.length > 0) populate.app = appFields;
10913
- if (opts.command === "repair") populate.secrets = true;
10914
- else if (opts.command === "reset") {
10852
+ }).length > 0) populate.app = true;
10853
+ if (opts.command === "repair") {
10854
+ populate.secrets = true;
10855
+ populate.larkApps = true;
10856
+ } else if (opts.command === "reset") {
10915
10857
  populate.secrets = true;
10916
10858
  populate.install = true;
10917
10859
  populate.reset = true;
10918
- }
10860
+ populate.larkApps = true;
10861
+ } else if (opts.command === "doctor" || opts.command === "check") populate.larkApps = true;
10919
10862
  return populate;
10920
10863
  }
10921
10864
  //#endregion
@@ -10969,372 +10912,11 @@ function reportDoctorRunToSlardar(opts) {
10969
10912
  }
10970
10913
  });
10971
10914
  }
10972
- function readLogFile(filePath) {
10973
- try {
10974
- return node_fs.default.readFileSync(filePath, "utf-8");
10975
- } catch {
10976
- return "";
10977
- }
10978
- }
10979
- function reportUpgradeLarkToSlardar(opts) {
10980
- console.error(`[slardar] upgrade_lark_run scene=${opts.scene ?? ""} success=${opts.success} exitCode=${opts.exitCode ?? ""} rollbackOk=${opts.rollbackOk ?? ""}`);
10981
- const logContent = readLogFile(opts.logFile);
10982
- reportTask({
10983
- eventName: "upgrade_lark_run",
10984
- durationMs: opts.durationMs,
10985
- status: opts.success ? "success" : "failed",
10986
- extraCategories: {
10987
- scene: opts.scene ?? "",
10988
- exit_code: String(opts.exitCode ?? ""),
10989
- rollback_ok: opts.rollbackOk != null ? String(opts.rollbackOk) : "",
10990
- validation_error: opts.validationError ?? "",
10991
- error_msg: opts.error ?? "",
10992
- log_content: logContent
10993
- }
10994
- });
10995
- }
10996
- //#endregion
10997
- //#region src/upgrade-lark.ts
10998
- /** Plugin directories under extensions/ that are backed up before upgrade */
10999
- const FEISHU_PLUGIN_DIRS = ["openclaw-lark", "feishu-openclaw-plugin"];
11000
- function backupFiles(opts) {
11001
- const { workspaceDir, configPath, backupDir, log } = opts;
11002
- try {
11003
- node_fs.default.mkdirSync(backupDir, { recursive: true });
11004
- log(`backup dir: ${backupDir}`);
11005
- if (node_fs.default.existsSync(configPath)) {
11006
- const stat = node_fs.default.statSync(configPath);
11007
- node_fs.default.copyFileSync(configPath, node_path.default.join(backupDir, "openclaw.json"));
11008
- log(` backed up: openclaw.json (${stat.size} bytes)`);
11009
- } else log(` skipped: openclaw.json (not found)`);
11010
- const extSrc = node_path.default.join(workspaceDir, "extensions");
11011
- for (const pluginDir of FEISHU_PLUGIN_DIRS) {
11012
- const src = node_path.default.join(extSrc, pluginDir);
11013
- if (node_fs.default.existsSync(src)) {
11014
- const dst = node_path.default.join(backupDir, "extensions", pluginDir);
11015
- node_fs.default.cpSync(src, dst, { recursive: true });
11016
- const version = readPkgVersion(node_path.default.join(src, "package.json"));
11017
- log(` backed up: extensions/${pluginDir}${version ? ` (version: ${version})` : ""}`);
11018
- } else log(` skipped: extensions/${pluginDir} (not found)`);
11019
- }
11020
- return { ok: true };
11021
- } catch (e) {
11022
- return {
11023
- ok: false,
11024
- error: `backup failed: ${e.message}`
11025
- };
11026
- }
11027
- }
11028
- function restoreFiles(opts) {
11029
- const { workspaceDir, configPath, backupDir, log } = opts;
11030
- try {
11031
- const configBackup = node_path.default.join(backupDir, "openclaw.json");
11032
- if (node_fs.default.existsSync(configBackup)) {
11033
- node_fs.default.copyFileSync(configBackup, configPath);
11034
- log(` restored: openclaw.json`);
11035
- }
11036
- const extDst = node_path.default.join(workspaceDir, "extensions");
11037
- for (const pluginDir of FEISHU_PLUGIN_DIRS) {
11038
- const backupSrc = node_path.default.join(backupDir, "extensions", pluginDir);
11039
- if (node_fs.default.existsSync(backupSrc)) {
11040
- const dst = node_path.default.join(extDst, pluginDir);
11041
- if (node_fs.default.existsSync(dst)) node_fs.default.rmSync(dst, {
11042
- recursive: true,
11043
- force: true
11044
- });
11045
- node_fs.default.cpSync(backupSrc, dst, { recursive: true });
11046
- log(` restored: extensions/${pluginDir}`);
11047
- }
11048
- }
11049
- return true;
11050
- } catch (e) {
11051
- log(` restore error: ${e.message}`);
11052
- return false;
11053
- }
11054
- }
11055
- function readPkgVersion(pkgPath) {
11056
- try {
11057
- const pkg = JSON.parse(node_fs.default.readFileSync(pkgPath, "utf-8"));
11058
- return typeof pkg.version === "string" ? pkg.version : null;
11059
- } catch {
11060
- return null;
11061
- }
11062
- }
11063
- function snapshotVersions(cwd, log) {
11064
- const ocResult = (0, node_child_process.spawnSync)("openclaw", ["--version"], {
11065
- cwd,
11066
- encoding: "utf-8",
11067
- stdio: [
11068
- "ignore",
11069
- "pipe",
11070
- "pipe"
11071
- ],
11072
- timeout: 5e3
11073
- });
11074
- const ocRaw = (ocResult.stdout ?? "").trim() || (ocResult.stderr ?? "").trim();
11075
- const extDir = node_path.default.join(cwd, "extensions");
11076
- const larkPkg = node_path.default.join(extDir, "openclaw-lark", "package.json");
11077
- const feishuPkg = node_path.default.join(extDir, "feishu-openclaw-plugin", "package.json");
11078
- log(` version-check paths: ${larkPkg} [${node_fs.default.existsSync(larkPkg) ? "exists" : "missing"}]`);
11079
- log(` version-check paths: ${feishuPkg} [${node_fs.default.existsSync(feishuPkg) ? "exists" : "missing"}]`);
11080
- return {
11081
- openclaw: ocRaw || null,
11082
- openclawLark: readPkgVersion(larkPkg),
11083
- feishuOpenclawPlugin: readPkgVersion(feishuPkg)
11084
- };
11085
- }
11086
- function logVersionSnapshot(label, v, log) {
11087
- log(`${label}: openclaw=${v.openclaw ?? "n/a"} openclaw-lark=${v.openclawLark ?? "n/a"} feishu-openclaw-plugin=${v.feishuOpenclawPlugin ?? "n/a"}`);
11088
- }
11089
- function countFeishuBots(configPath) {
11090
- try {
11091
- const raw = node_fs.default.readFileSync(configPath, "utf-8");
11092
- const config = loadJSON5().parse(raw);
11093
- const accounts = getNestedMap(config, "channels", "feishu", "accounts");
11094
- if (accounts) return Object.keys(accounts).length;
11095
- const feishu = getNestedMap(config, "channels", "feishu");
11096
- return typeof feishu?.appId === "string" && feishu.appId ? 1 : 0;
11097
- } catch {
11098
- return 0;
11099
- }
11100
- }
11101
- /** Run channels probe, log results, and return the result. Never throws. */
11102
- function probeChannels(label, log, timeoutMs) {
11103
- try {
11104
- const r = runChannelsProbe(timeoutMs);
11105
- log(` ${label} available=${r.available} anyAccountWorking=${r.anyAccountWorking}`);
11106
- if (r.error) log(` ${label} error: ${r.error}`);
11107
- if (r.gatewayReachable != null) log(` ${label} gatewayReachable: ${r.gatewayReachable}`);
11108
- for (const acct of r.accounts ?? []) log(` ${label} account ${acct.id}: isWorking=${acct.isWorking} bits=[${acct.bits.join(",")}]`);
11109
- return r;
11110
- } catch (e) {
11111
- log(` ${label} channels probe threw: ${e.message}`);
11112
- return {
11113
- available: false,
11114
- gatewayReachable: false,
11115
- feishuConfigInvalid: false,
11116
- accounts: [],
11117
- anyAccountWorking: false
11118
- };
11119
- }
11120
- }
11121
- function runUpgradeLark(opts) {
11122
- const cwd = opts.cwd ?? "/home/gem/workspace/agent";
11123
- const configPath = opts.configPath ?? CONFIG_PATH;
11124
- const logFile = upgradeLarkLogFile(opts.runId);
11125
- const log = makeLogger(logFile);
11126
- const fsOpts = {
11127
- workspaceDir: cwd,
11128
- configPath,
11129
- backupDir: node_path.default.join(opts.backupBaseDir ?? "/tmp/openclaw-diagnose", `upgrade-lark-backup-${opts.runId}`),
11130
- log
11131
- };
11132
- const cliScript = opts.cliScript ?? process.argv[1];
11133
- const statusCheckDelayMs = opts.statusCheckDelayMs ?? 5e3;
11134
- log(`${"=".repeat(60)}`);
11135
- log(`upgrade-lark started runId=${opts.runId}`);
11136
- log(` cwd : ${cwd}`);
11137
- log(` configPath : ${configPath}`);
11138
- log(`${"=".repeat(60)}`);
11139
- log("");
11140
- log("── [Pre-check A] channels probe(升级前)────────────────");
11141
- const beforeChannels = probeChannels("before", log, 3e4);
11142
- log("");
11143
- log("── [Pre-check B] 版本兼容预检 ───────────────────────────");
11144
- let versionIncompatible = false;
11145
- try {
11146
- const rawConfig = node_fs.default.readFileSync(configPath, "utf-8");
11147
- versionIncompatible = needsLarkUpgrade({
11148
- config: loadJSON5().parse(rawConfig),
11149
- configPath,
11150
- vars: {},
11151
- providerDeps: {
11152
- usesMiaodaProvider: false,
11153
- usesMiaodaSecretProvider: false
11154
- }
11155
- });
11156
- log(` version-compat pre-check: ${versionIncompatible ? "NEEDS_UPGRADE" : "ok"}`);
11157
- } catch (e) {
11158
- log(` version-compat pre-check error: ${e.message} — treating as needs-upgrade`);
11159
- versionIncompatible = true;
11160
- }
11161
- const feishuConfigInvalid = beforeChannels.feishuConfigInvalid;
11162
- log(` feishu config invalid : ${feishuConfigInvalid}`);
11163
- log("");
11164
- log("── [Gate] 升级前置条件检查 ───────────────────────────────");
11165
- log(` versionIncompatible : ${versionIncompatible}`);
11166
- log(` feishuConfigInvalid : ${feishuConfigInvalid}`);
11167
- log(` channels working before: ${beforeChannels.anyAccountWorking}`);
11168
- if (!(versionIncompatible || feishuConfigInvalid)) {
11169
- const reason = "version compatible and feishu channel config valid — upgrade not needed";
11170
- log(` SKIP: ${reason}`);
11171
- log(`${"=".repeat(60)}`);
11172
- log("upgrade-lark skipped (pre-check gate)");
11173
- log(`${"=".repeat(60)}`);
11174
- return {
11175
- ok: true,
11176
- skipped: true,
11177
- skipReason: reason,
11178
- logFile
11179
- };
11180
- }
11181
- if (beforeChannels.anyAccountWorking) {
11182
- const reason = "channels are working — upgrade not needed (issue detected but system is functional)";
11183
- log(` SKIP: ${reason}`);
11184
- log(`${"=".repeat(60)}`);
11185
- log("upgrade-lark skipped (pre-check gate)");
11186
- log(`${"=".repeat(60)}`);
11187
- return {
11188
- ok: true,
11189
- skipped: true,
11190
- skipReason: reason,
11191
- logFile
11192
- };
11193
- }
11194
- log(` PROCEED: requiresLarkUpgrade=true (version=${versionIncompatible}, feishuConfig=${feishuConfigInvalid}) AND channels not working → running upgrade`);
11195
- log("");
11196
- log("── [1/6] 文件备份 ────────────────────────────────────────");
11197
- log(`before-state: botCount=${countFeishuBots(configPath)}`);
11198
- const backup = backupFiles(fsOpts);
11199
- if (!backup.ok) {
11200
- log(`ERROR: ${backup.error}`);
11201
- return {
11202
- ok: false,
11203
- error: backup.error,
11204
- logFile
11205
- };
11206
- }
11207
- log("backup: ok");
11208
- logVersionSnapshot("before-versions", snapshotVersions(cwd, log), log);
11209
- log("");
11210
- log("── [2/6] 清理本地 openclaw shim ─────────────────────────");
11211
- const localOpenclawBin = node_path.default.join(cwd, "node_modules", ".bin", "openclaw");
11212
- if (node_fs.default.existsSync(localOpenclawBin)) try {
11213
- node_fs.default.rmSync(localOpenclawBin);
11214
- log(` removed: ${localOpenclawBin}`);
11215
- } catch (e) {
11216
- log(` WARN: failed to remove ${localOpenclawBin}: ${e.message}`);
11217
- }
11218
- else log(` skipped: ${localOpenclawBin} (not found)`);
11219
- log("");
11220
- log("── [3/6] npx install (@larksuite/openclaw-lark-tools update) ──");
11221
- const npxResult = (0, node_child_process.spawnSync)("npx", [
11222
- "-y",
11223
- "@larksuite/openclaw-lark-tools",
11224
- "update"
11225
- ], {
11226
- cwd,
11227
- encoding: "utf-8",
11228
- stdio: [
11229
- "ignore",
11230
- "pipe",
11231
- "pipe"
11232
- ],
11233
- timeout: 12e4
11234
- });
11235
- const npxStdout = npxResult.stdout?.trim() ?? "";
11236
- const npxStderr = npxResult.stderr?.trim() ?? "";
11237
- const npxExitCode = npxResult.status ?? 1;
11238
- if (npxStdout) log(`npx stdout:\n${npxStdout}`);
11239
- if (npxStderr) log(`npx stderr:\n${npxStderr}`);
11240
- log(`npx exit: ${npxExitCode}${npxResult.error ? ` error: ${npxResult.error.message}` : ""}`);
11241
- if (statusCheckDelayMs > 0) {
11242
- log("");
11243
- log(`── 等待 ${statusCheckDelayMs / 1e3}s(让 openclaw 服务完成重启) ─────────────`);
11244
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, statusCheckDelayMs);
11245
- log("wait done");
11246
- }
11247
- const doRollback = (reason) => {
11248
- log(`ERROR: ${reason}`);
11249
- const rollbackOk = restoreFiles(fsOpts);
11250
- log(`rollback: ${rollbackOk ? "ok" : "FAILED"}`);
11251
- return {
11252
- ok: false,
11253
- error: reason,
11254
- validationError: reason,
11255
- stdout: npxStdout,
11256
- stderr: npxStderr,
11257
- exitCode: npxExitCode,
11258
- rollbackOk,
11259
- logFile
11260
- };
11261
- };
11262
- log("");
11263
- log("── [4/5] 安装后诊断校验 ─────────────────────────────────");
11264
- logVersionSnapshot("after-versions", snapshotVersions(cwd, log), log);
11265
- let afterVersionIncompatible = false;
11266
- try {
11267
- const rawConfig = node_fs.default.readFileSync(configPath, "utf-8");
11268
- afterVersionIncompatible = needsLarkUpgrade({
11269
- config: loadJSON5().parse(rawConfig),
11270
- configPath,
11271
- vars: {},
11272
- providerDeps: {
11273
- usesMiaodaProvider: false,
11274
- usesMiaodaSecretProvider: false
11275
- }
11276
- });
11277
- log(` version-compat post-check: ${afterVersionIncompatible ? "STILL_INCOMPATIBLE" : "ok"}`);
11278
- } catch (e) {
11279
- log(` version-compat post-check error: ${e.message} — treating as still-incompatible`);
11280
- afterVersionIncompatible = true;
11281
- }
11282
- const afterChannels = probeChannels("after", log, 3e4);
11283
- log(` feishu config invalid after: ${afterChannels.feishuConfigInvalid}`);
11284
- const stillNeedsUpgrade = (afterVersionIncompatible || afterChannels.feishuConfigInvalid) && !afterChannels.anyAccountWorking;
11285
- log(` post-check: stillNeedsUpgrade=${stillNeedsUpgrade} (version=${afterVersionIncompatible}, feishuConfig=${afterChannels.feishuConfigInvalid}, channelsWorking=${afterChannels.anyAccountWorking})`);
11286
- if (stillNeedsUpgrade) return doRollback(`post-install diagnosis still shows anomaly: versionIncompatible=${afterVersionIncompatible}, feishuConfigInvalid=${afterChannels.feishuConfigInvalid}, anyAccountWorking=${afterChannels.anyAccountWorking}`);
11287
- log(" post-install diagnosis: ok (upgrade conditions resolved)");
11288
- log("");
11289
- log("── [6/6] doctor --fix ────────────────────────────────────");
11290
- const fixArgs = ["doctor", "--fix"];
11291
- if (opts.scene) fixArgs.push(`--scene=${opts.scene}`);
11292
- const fixResult = (0, node_child_process.spawnSync)(process.execPath, [cliScript, ...fixArgs], {
11293
- cwd,
11294
- encoding: "utf-8",
11295
- stdio: [
11296
- "ignore",
11297
- "pipe",
11298
- "pipe"
11299
- ],
11300
- timeout: 6e4,
11301
- env: process.env
11302
- });
11303
- if (fixResult.stdout?.trim()) log(`doctor(fix) stdout:\n${fixResult.stdout.trim()}`);
11304
- if (fixResult.stderr?.trim()) log(`doctor(fix) stderr:\n${fixResult.stderr.trim()}`);
11305
- log(`doctor(fix) exit: ${fixResult.status ?? "null"}${fixResult.error ? ` error: ${fixResult.error.message}` : ""}`);
11306
- log("");
11307
- log(`${"=".repeat(60)}`);
11308
- log("upgrade-lark completed successfully");
11309
- log(`${"=".repeat(60)}`);
11310
- return {
11311
- ok: true,
11312
- stdout: npxStdout,
11313
- stderr: npxStderr,
11314
- exitCode: npxExitCode,
11315
- logFile
11316
- };
11317
- }
11318
10915
  //#endregion
11319
10916
  //#region src/index.ts
11320
10917
  const args = node_process.default.argv.slice(2);
11321
10918
  const mode = args.find((a) => !a.startsWith("-"));
11322
10919
  /**
11323
- * Decode `--ctx=<base64>` into an opaque JSON object. Returns undefined when
11324
- * the flag isn't present — the caller decides whether to fall back to the
11325
- * innerapi or to error out.
11326
- *
11327
- * The object's shape is not enforced here; downstream code consumes it via
11328
- * either `normalizeCtx()` (new path) or direct field access for the legacy
11329
- * check/repair/reset contract still used by sandbox_console push.
11330
- */
11331
- function parseCtxFlag(args) {
11332
- const ctxArg = args.find((a) => a.startsWith("--ctx="));
11333
- if (!ctxArg) return void 0;
11334
- const b64 = ctxArg.slice(6);
11335
- return JSON.parse(Buffer.from(b64, "base64").toString("utf-8"));
11336
- }
11337
- /**
11338
10920
  * Pull the first non-flag positional after the mode name.
11339
10921
  * (The mode itself is args[0] in the filtered set, so we skip index 0.)
11340
10922
  */
@@ -11362,8 +10944,8 @@ function getMultiFlag(args, name) {
11362
10944
  * case but is no longer consulted.
11363
10945
  */
11364
10946
  async function reportRun(command, rc, _raw, invocation, durationMs, outcome, slardar = {
11365
- scene,
11366
- profile,
10947
+ scene: void 0,
10948
+ profile: "standard",
11367
10949
  fix: false
11368
10950
  }) {
11369
10951
  console.error(`${command}: telemetry calling report_cli_run`);
@@ -11427,7 +11009,7 @@ async function main() {
11427
11009
  console.error(`${mode}: begin argv=[${args.join(" ")}] version=${getVersion()} traceId=${traceId ?? "-"} caller=${caller ?? "-"} runIdGenerated=${rc.generated}`);
11428
11010
  switch (mode) {
11429
11011
  case "check": {
11430
- const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11012
+ const raw = await fetchCtxViaInnerApi({
11431
11013
  populate: planCtxPopulate({
11432
11014
  command: "check",
11433
11015
  profile
@@ -11452,7 +11034,7 @@ async function main() {
11452
11034
  break;
11453
11035
  }
11454
11036
  case "repair": {
11455
- const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11037
+ const raw = await fetchCtxViaInnerApi({
11456
11038
  populate: planCtxPopulate({
11457
11039
  command: "repair",
11458
11040
  profile
@@ -11523,27 +11105,15 @@ async function main() {
11523
11105
  break;
11524
11106
  }
11525
11107
  case "reset":
11526
- if (args.includes("--async")) {
11527
- const ctxArg = args.find((a) => a.startsWith("--ctx="));
11528
- let ctxBase64;
11529
- if (ctxArg) ctxBase64 = ctxArg.slice(6);
11530
- else {
11531
- const fetched = await fetchCtxViaInnerApi({
11532
- populate: planCtxPopulate({ command: "reset" }),
11533
- caller,
11534
- traceId
11535
- });
11536
- ctxBase64 = Buffer.from(JSON.stringify(fetched), "utf-8").toString("base64");
11537
- }
11538
- console.log(JSON.stringify(startAsyncReset(ctxBase64)));
11539
- } else if (args.includes("--worker")) {
11108
+ if (args.includes("--async")) console.log(JSON.stringify(startAsyncReset()));
11109
+ else if (args.includes("--worker")) {
11540
11110
  const taskId = args.find((a) => a.startsWith("--task-id="))?.slice(10);
11541
11111
  if (!taskId) {
11542
11112
  console.error("Error: --task-id=<id> is required for worker");
11543
11113
  node_process.default.exit(1);
11544
11114
  }
11545
11115
  const resultFile = resetResultFile(taskId);
11546
- const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11116
+ const raw = await fetchCtxViaInnerApi({
11547
11117
  populate: planCtxPopulate({ command: "reset" }),
11548
11118
  caller,
11549
11119
  traceId
@@ -11567,7 +11137,7 @@ async function main() {
11567
11137
  return;
11568
11138
  }
11569
11139
  } else {
11570
- console.error("Usage: reset --async [--ctx=<base64>] | reset --worker --task-id=<id> [--ctx=<base64>]");
11140
+ console.error("Usage: reset --async | reset --worker --task-id=<id>");
11571
11141
  node_process.default.exit(1);
11572
11142
  }
11573
11143
  break;
@@ -11583,14 +11153,14 @@ async function main() {
11583
11153
  case "install-openclaw": {
11584
11154
  const tag = getPositionalTag(args, "install-openclaw");
11585
11155
  if (!tag) {
11586
- console.error("Usage: install-openclaw <tag> [--ctx=<base64> | --oss_file_map=<base64>]");
11156
+ console.error("Usage: install-openclaw <tag> [--oss_file_map=<base64>]");
11587
11157
  node_process.default.exit(1);
11588
11158
  }
11589
11159
  const ossFileMapFlag = getFlag(args, "oss_file_map");
11590
11160
  let installOssFileMap;
11591
11161
  let rawForTelemetry;
11592
11162
  if (!ossFileMapFlag) {
11593
- rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11163
+ rawForTelemetry = await fetchCtxViaInnerApi({
11594
11164
  populate: planCtxPopulate({ command: "install" }),
11595
11165
  caller,
11596
11166
  traceId
@@ -11625,7 +11195,7 @@ async function main() {
11625
11195
  case "install-extension": {
11626
11196
  const tag = getPositionalTag(args, "install-extension");
11627
11197
  if (!tag) {
11628
- console.error("Usage: install-extension <tag> (--all | --extension=<name>...) [--home_base=<dir>] [--config_path=<path>] [--skip-config-update] [--ctx=<base64> | --oss_file_map=<base64>]");
11198
+ console.error("Usage: install-extension <tag> (--all | --extension=<name>...) [--home_base=<dir>] [--config_path=<path>] [--skip-config-update] [--oss_file_map=<base64>]");
11629
11199
  node_process.default.exit(1);
11630
11200
  }
11631
11201
  const all = args.includes("--all");
@@ -11637,7 +11207,7 @@ async function main() {
11637
11207
  let installOssFileMap;
11638
11208
  let rawForTelemetry;
11639
11209
  if (!ossFileMapFlag) {
11640
- rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11210
+ rawForTelemetry = await fetchCtxViaInnerApi({
11641
11211
  populate: planCtxPopulate({ command: "install" }),
11642
11212
  caller,
11643
11213
  traceId
@@ -11683,12 +11253,12 @@ async function main() {
11683
11253
  case "install-cli": {
11684
11254
  const tag = getPositionalTag(args, "install-cli");
11685
11255
  if (!tag) {
11686
- console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--ctx=<base64> | --oss_file_map=<base64>]");
11256
+ console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
11687
11257
  node_process.default.exit(1);
11688
11258
  }
11689
11259
  const names = getMultiFlag(args, "cli");
11690
11260
  if (names.length === 0) {
11691
- console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--ctx=<base64> | --oss_file_map=<base64>]");
11261
+ console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
11692
11262
  node_process.default.exit(1);
11693
11263
  }
11694
11264
  const homeBase = getFlag(args, "home_base");
@@ -11696,7 +11266,7 @@ async function main() {
11696
11266
  let installOssFileMap;
11697
11267
  let rawForTelemetry;
11698
11268
  if (!ossFileMapFlag) {
11699
- rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11269
+ rawForTelemetry = await fetchCtxViaInnerApi({
11700
11270
  populate: planCtxPopulate({ command: "install" }),
11701
11271
  caller,
11702
11272
  traceId
@@ -11744,7 +11314,7 @@ async function main() {
11744
11314
  case "download-resource": {
11745
11315
  const tag = getPositionalTag(args, "download-resource");
11746
11316
  if (!tag) {
11747
- console.error("Usage: download-resource <tag> --role=<role> --name=<name> [--dir=<dir>] [--ctx=<base64> | --oss_file_map=<base64>]");
11317
+ console.error("Usage: download-resource <tag> --role=<role> --name=<name> [--dir=<dir>] [--oss_file_map=<base64>]");
11748
11318
  node_process.default.exit(1);
11749
11319
  }
11750
11320
  const role = getFlag(args, "role");
@@ -11758,7 +11328,7 @@ async function main() {
11758
11328
  let installOssFileMap;
11759
11329
  let rawForTelemetry;
11760
11330
  if (!ossFileMapFlag) {
11761
- rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11331
+ rawForTelemetry = await fetchCtxViaInnerApi({
11762
11332
  populate: planCtxPopulate({ command: "install" }),
11763
11333
  caller,
11764
11334
  traceId
@@ -11832,50 +11402,6 @@ async function main() {
11832
11402
  if (!result.ok) node_process.default.exit(1);
11833
11403
  break;
11834
11404
  }
11835
- case "upgrade-lark": {
11836
- const result = runUpgradeLark({
11837
- runId: rc.runId,
11838
- scene
11839
- });
11840
- const upgradeDurationMs = Date.now() - t0;
11841
- console.log(JSON.stringify(result));
11842
- reportUpgradeLarkToSlardar({
11843
- scene,
11844
- durationMs: upgradeDurationMs,
11845
- success: result.ok,
11846
- logFile: result.logFile,
11847
- exitCode: result.exitCode,
11848
- rollbackOk: result.rollbackOk,
11849
- validationError: result.validationError,
11850
- error: result.error
11851
- });
11852
- try {
11853
- await reportCliRun({
11854
- command: "upgrade-lark",
11855
- runId: rc.runId,
11856
- version: getVersion(),
11857
- invocation: args.join(" "),
11858
- durationMs: upgradeDurationMs,
11859
- caller: rc.caller,
11860
- traceId: rc.traceId,
11861
- success: result.ok,
11862
- result,
11863
- error: result.ok ? void 0 : { message: result.error ?? "upgrade-lark failed" }
11864
- });
11865
- } catch (e) {
11866
- console.error(`[telemetry] reportCliRun failed: ${e.message}`);
11867
- }
11868
- if (!result.ok) {
11869
- node_process.default.exitCode = 1;
11870
- return;
11871
- }
11872
- break;
11873
- }
11874
- case "channels-probe": {
11875
- const result = runChannelsProbe(getFlag(args, "timeout") ? Number(getFlag(args, "timeout")) : void 0);
11876
- console.log(JSON.stringify(result));
11877
- break;
11878
- }
11879
11405
  default:
11880
11406
  node_process.default.stderr.write(`Unknown command: ${mode}\n\n`);
11881
11407
  node_process.default.stderr.write(formatTopLevelHelp(helpFlags.expert));