@lark-apaas/openclaw-scripts-diagnose-cli 0.1.18-alpha.2 → 0.1.18-alpha.3

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 +258 -150
  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.18-alpha.2";
55
+ return "0.1.18-alpha.3";
56
56
  }
57
57
  //#endregion
58
58
  //#region src/rule-engine/base.ts
@@ -2655,17 +2655,71 @@ function upgradeLarkLogFile(runId, checkOnly = false) {
2655
2655
  return `${DIAGNOSE_DIR}/upgrade-lark${checkOnly ? "-check" : ""}-${ts}-${runId.slice(0, 8)}.log`;
2656
2656
  }
2657
2657
  //#endregion
2658
+ //#region src/constants.ts
2659
+ /**
2660
+ * 全局共享常量(插件名 / CLI 名 / 路径 / 插件集合)。
2661
+ *
2662
+ * 历史上这些字面量散落在 install-*、upgrade-lark、各 rule 等十余个文件里重复定义,
2663
+ * 易漂移。统一收口到此处,按用途分组,单一来源。
2664
+ */
2665
+ /**
2666
+ * 官方插件:`openclaw-lark`(plugins.allow 中的短名;npm 发布名 `@larksuite/openclaw-lark`)。
2667
+ * 走 VERSION_COMPAT_MAP 按版本判定与 openclaw 的兼容性。
2668
+ */
2669
+ const LARK_PLUGIN_NAME = "openclaw-lark";
2670
+ /**
2671
+ * fork 插件:`@lark-apaas/openclaw-lark`(@lark-apaas 内部 fork,全名带 scope)。
2672
+ * 版本号自成体系、不在 VERSION_COMPAT_MAP 内,按对标的官方版本判定兼容性。
2673
+ */
2674
+ const FORK_LARK_PLUGIN_FULL_NAME = "@lark-apaas/openclaw-lark";
2675
+ /** fork 插件的 scope 前缀。 */
2676
+ const FORK_LARK_PLUGIN_SCOPE = "@lark-apaas";
2677
+ /**
2678
+ * 社区插件:openclaw **内置** 的 `feishu` 插件(非独立扩展)。
2679
+ * 与官方/ fork openclaw-lark 互斥,规范化时应禁用以让位 openclaw-lark。
2680
+ */
2681
+ const BUILTIN_FEISHU_PLUGIN_NAME = "feishu";
2682
+ /**
2683
+ * 旧版插件:已弃用的 `feishu-openclaw-plugin`,始终不兼容,需替换为官方 openclaw-lark。
2684
+ */
2685
+ const LEGACY_LARK_PLUGIN_NAME = "feishu-openclaw-plugin";
2686
+ const MIAODA_PLUGIN_NAME = "openclaw-extension-miaoda";
2687
+ const MIAODA_CODING_PLUGIN_NAME = "openclaw-extension-miaoda-coding";
2688
+ const GUARDIAN_PLUGIN_NAME = "openclaw-guardian-plugin";
2689
+ const MEM0_PLUGIN_NAME = "openclaw-mem0-plugin";
2690
+ /** 飞书 lark-cli 命令名(独立 CLI,与 openclaw-lark 插件配套)。 */
2691
+ const LARK_CLI_NAME = "lark-cli";
2692
+ /** agent-skills 模板包名(manifest role=template,随 lark-cli 一起安装)。 */
2693
+ const AGENT_SKILLS_NAME = "agent-skills";
2694
+ /** 飞书插件目录集合(官方短名 + 旧版名):备份 / 清理 / 安装时迭代用。 */
2695
+ const FEISHU_PLUGIN_DIR_NAMES = [LARK_PLUGIN_NAME, LEGACY_LARK_PLUGIN_NAME];
2696
+ /**
2697
+ * install-extension --all 安装的官方扩展插件集合(manifest role=extension)。
2698
+ * builtin-plugin-missing 与 miaoda-official-plugins-install-spec-unlock 共用此单一来源,
2699
+ * 二者此前各维护一份且注释明示"必须保持一致"。
2700
+ */
2701
+ const OFFICIAL_EXTENSION_PLUGIN_NAMES = new Set([
2702
+ LARK_PLUGIN_NAME,
2703
+ MIAODA_PLUGIN_NAME,
2704
+ MIAODA_CODING_PLUGIN_NAME,
2705
+ GUARDIAN_PLUGIN_NAME,
2706
+ MEM0_PLUGIN_NAME
2707
+ ]);
2708
+ /** agent 工作区相对路径。 */
2709
+ const WORKSPACE_AGENT_REL = "workspace/agent";
2710
+ /** openclaw.json 默认相对路径。 */
2711
+ const DEFAULT_CONFIG_REL = `${WORKSPACE_AGENT_REL}/openclaw.json`;
2712
+ //#endregion
2658
2713
  //#region src/lark-cli-init.ts
2659
- const LARK_PLUGIN_NAMES$1 = ["openclaw-lark", "feishu-openclaw-plugin"];
2660
2714
  const PE_XML_TAG = "lark-cli-pe";
2661
2715
  const PE_PLACEHOLDER = `
2662
2716
  <${PE_XML_TAG}>
2663
2717
  **【强制要求 - 无例外】飞书所有操作必须先读 feishu-lark-cli skill。未读skill不得调用任何飞书工具,违者视为操作失误。**
2664
2718
  </${PE_XML_TAG}>
2665
2719
  `;
2666
- function isLarkPluginInstalled(configPath) {
2720
+ function isLarkPluginInstalled$1(configPath) {
2667
2721
  const extDir = getExtensionsDir(configPath);
2668
- return LARK_PLUGIN_NAMES$1.some((name) => {
2722
+ return FEISHU_PLUGIN_DIR_NAMES.some((name) => {
2669
2723
  try {
2670
2724
  return node_fs.default.existsSync(node_path.default.join(extDir, name, "package.json"));
2671
2725
  } catch {
@@ -2837,7 +2891,7 @@ function collectFeishuAppIds(configPath) {
2837
2891
  }
2838
2892
  function runLarkCliInit(opts) {
2839
2893
  const configPath = opts.configPath ?? CONFIG_PATH;
2840
- if (!isLarkPluginInstalled(configPath)) {
2894
+ if (!isLarkPluginInstalled$1(configPath)) {
2841
2895
  console.error("lark-cli-init: skipping — openclaw-lark plugin not installed");
2842
2896
  return {
2843
2897
  ok: true,
@@ -2915,7 +2969,66 @@ function runLarkCliInit(opts) {
2915
2969
  };
2916
2970
  }
2917
2971
  //#endregion
2918
- //#region src/rules/agents-md-lark-cli-pe.ts
2972
+ //#region src/utils/feishu-tools-deny.ts
2973
+ /**
2974
+ * openclaw-lark 中与 lark-cli 功能重叠的工具,写入 channels.feishu.tools.deny 后
2975
+ * 由 openclaw-lark 插件自身的 shouldRegisterTool() 跳过注册,避免与 lark-cli 重复。
2976
+ * 支持尾部 `*` 通配(openclaw-lark matchesAnyPattern 识别)。
2977
+ *
2978
+ * 保留(IM / 鉴权 / 诊断类,与 lark-cli 无重叠):feishu_chat*、feishu_get_user、
2979
+ * feishu_search_user、feishu_im_*、feishu_oauth*、feishu_auth、feishu_diagnose、feishu_doctor。
2980
+ *
2981
+ * install-cli(安装 lark-cli 后即时写)与 agents_md_lark_cli_pe 规则(doctor --fix
2982
+ * 自愈)共用本常量与合并逻辑,单一来源。
2983
+ */
2984
+ const LARK_CLI_OVERLAP_TOOL_DENY = Object.freeze([
2985
+ "feishu_create_doc",
2986
+ "feishu_fetch_doc",
2987
+ "feishu_update_doc",
2988
+ "feishu_doc_comments",
2989
+ "feishu_doc_media",
2990
+ "feishu_drive_file",
2991
+ "feishu_wiki_space",
2992
+ "feishu_wiki_space_node",
2993
+ "feishu_search_doc_wiki",
2994
+ "feishu_bitable_*",
2995
+ "feishu_calendar_*",
2996
+ "feishu_task_*",
2997
+ "feishu_sheet"
2998
+ ]);
2999
+ /** 读取 channels.feishu.tools.deny 的字符串数组(缺失/非法时返回空数组)。 */
3000
+ function readDenyList(config) {
3001
+ const deny = asRecord(asRecord(asRecord(config.channels)?.feishu)?.tools)?.deny;
3002
+ return Array.isArray(deny) ? deny.filter((e) => typeof e === "string") : [];
3003
+ }
3004
+ /** 返回 deny 列表中尚缺的重叠工具(用于 validate 判定)。 */
3005
+ function larkCliToolDenyMissing(config) {
3006
+ const existing = new Set(readDenyList(config));
3007
+ return LARK_CLI_OVERLAP_TOOL_DENY.filter((t) => !existing.has(t));
3008
+ }
3009
+ /**
3010
+ * 确保 channels.feishu.tools.deny 含全部重叠工具(只增不删,幂等合并)。
3011
+ * 直接 mutate 传入的 config 对象;返回是否发生了变更。
3012
+ */
3013
+ function ensureLarkCliToolDeny(config) {
3014
+ if (larkCliToolDenyMissing(config).length === 0) return false;
3015
+ if (!config.channels || typeof config.channels !== "object" || Array.isArray(config.channels)) config.channels = {};
3016
+ const channels = config.channels;
3017
+ if (!channels.feishu || typeof channels.feishu !== "object" || Array.isArray(channels.feishu)) channels.feishu = {};
3018
+ const feishu = channels.feishu;
3019
+ if (!feishu.tools || typeof feishu.tools !== "object" || Array.isArray(feishu.tools)) feishu.tools = {};
3020
+ const tools = feishu.tools;
3021
+ const existing = readDenyList(config);
3022
+ tools.deny = [...new Set([...existing, ...LARK_CLI_OVERLAP_TOOL_DENY])];
3023
+ return true;
3024
+ }
3025
+ //#endregion
3026
+ //#region src/utils/lark-cli-detect.ts
3027
+ /**
3028
+ * 探测 lark-cli 是否可用(`lark-cli --version` 退出码为 0)。
3029
+ * 多条规则共用同一判定:feishu_plugin_state_normalize(是否补 feishu_* 授权)、
3030
+ * agents_md_lark_cli_pe(是否补 PE / deny)等。
3031
+ */
2919
3032
  function isLarkCliAvailable$1() {
2920
3033
  try {
2921
3034
  return (0, node_child_process.spawnSync)("lark-cli", ["--version"], {
@@ -2931,17 +3044,38 @@ function isLarkCliAvailable$1() {
2931
3044
  return false;
2932
3045
  }
2933
3046
  }
3047
+ //#endregion
3048
+ //#region src/rules/agents-md-lark-cli-pe.ts
3049
+ /** openclaw-lark 插件是否已落盘安装(deny 仅在插件存在时有意义)。 */
3050
+ function isLarkPluginInstalled(ctx) {
3051
+ return node_fs.default.existsSync(node_path.default.join(getExtensionsDir(ctx.configPath), LARK_PLUGIN_NAME));
3052
+ }
3053
+ /** 第一个缺失 PE 内容的 AGENTS.md 路径;无则 undefined。 */
3054
+ function findAgentsMdMissingPe(ctx) {
3055
+ return collectExistingAgentsMdPaths(ctx).find((filePath) => {
3056
+ return !node_fs.default.readFileSync(filePath, "utf-8").includes(`<${PE_XML_TAG}>`);
3057
+ });
3058
+ }
3059
+ /**
3060
+ * lark-cli 存在时统一规范化环境:
3061
+ * 1. 给各智能体 AGENTS.md 追加 lark-cli-pe PE 内容
3062
+ * 2. openclaw-lark 已装时,把与 lark-cli 重叠的工具写入 channels.feishu.tools.deny
3063
+ * (只增不删,避免与 lark-cli 重复注册)
3064
+ * 两者门禁同为「lark-cli 可用」,故合并为一条规则。
3065
+ */
2934
3066
  let AgentsMdLarkCliPeRule = class AgentsMdLarkCliPeRule extends DiagnoseRule {
2935
3067
  validate(ctx) {
2936
3068
  if (!isLarkCliAvailable$1()) return { pass: true };
2937
- const missingPath = collectExistingAgentsMdPaths(ctx).find((filePath) => {
2938
- return !node_fs.default.readFileSync(filePath, "utf-8").includes(`<${PE_XML_TAG}>`);
2939
- });
2940
- if (!missingPath) return { pass: true };
2941
- return {
3069
+ const missingPath = findAgentsMdMissingPe(ctx);
3070
+ if (missingPath) return {
2942
3071
  pass: false,
2943
3072
  message: `${missingPath} 中缺少 lark-cli-pe PE 内容,需要追加`
2944
3073
  };
3074
+ if (isLarkPluginInstalled(ctx) && larkCliToolDenyMissing(ctx.config).length > 0) return {
3075
+ pass: false,
3076
+ message: "channels.feishu.tools.deny 缺少与 lark-cli 重叠的 openclaw-lark 工具,需要补充"
3077
+ };
3078
+ return { pass: true };
2945
3079
  }
2946
3080
  repair(ctx) {
2947
3081
  if (!isLarkCliAvailable$1()) return;
@@ -2952,11 +3086,12 @@ let AgentsMdLarkCliPeRule = class AgentsMdLarkCliPeRule extends DiagnoseRule {
2952
3086
  node_fs.default.appendFileSync(filePath, `${sep}${PE_PLACEHOLDER}`, "utf-8");
2953
3087
  console.error(`agents-md-lark-cli-pe: appended PE to ${filePath}`);
2954
3088
  }
3089
+ if (isLarkPluginInstalled(ctx) && ensureLarkCliToolDeny(ctx.config)) console.error("agents-md-lark-cli-pe: merged lark-cli overlap tools into channels.feishu.tools.deny");
2955
3090
  }
2956
3091
  };
2957
3092
  AgentsMdLarkCliPeRule = __decorate([Rule({
2958
3093
  key: "agents_md_lark_cli_pe",
2959
- description: "检测各智能体 AGENTS.md 中是否缺失 lark-cli-pe PE 内容,lark-cli 存在时自动追加",
3094
+ description: "lark-cli 存在时规范化环境:AGENTS.md 追加 lark-cli-pe PE + openclaw-lark 重叠工具写入 channels.feishu.tools.deny",
2960
3095
  dependsOn: ["config_syntax_check"],
2961
3096
  repairMode: "standard",
2962
3097
  level: "silent"
@@ -2966,15 +3101,8 @@ AgentsMdLarkCliPeRule = __decorate([Rule({
2966
3101
  /**
2967
3102
  * Official miaoda-side plugins that must track manifest — version-locked specs
2968
3103
  * here block upgrades. Third-party / user-installed plugins are intentionally
2969
- * out of scope (users may pin them deliberately).
3104
+ * out of scope (users may pin them deliberately). 集合见 constants.OFFICIAL_EXTENSION_PLUGIN_NAMES。
2970
3105
  */
2971
- const OFFICIAL_PLUGIN_NAMES = new Set([
2972
- "openclaw-extension-miaoda",
2973
- "openclaw-extension-miaoda-coding",
2974
- "openclaw-guardian-plugin",
2975
- "openclaw-mem0-plugin",
2976
- "openclaw-lark"
2977
- ]);
2978
3106
  const LOCKED_NPM_SPEC = /^(@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*@[^@/:#\s]+$/i;
2979
3107
  function isLockedNpmSpec(spec) {
2980
3108
  return typeof spec === "string" && LOCKED_NPM_SPEC.test(spec);
@@ -2989,7 +3117,7 @@ function* iterLockedOfficialInstalls(config) {
2989
3117
  const installs = getNestedMap(config, "plugins", "installs");
2990
3118
  if (!installs) return;
2991
3119
  for (const [key, entry] of Object.entries(installs)) {
2992
- if (!OFFICIAL_PLUGIN_NAMES.has(key)) continue;
3120
+ if (!OFFICIAL_EXTENSION_PLUGIN_NAMES.has(key)) continue;
2993
3121
  const spec = asRecord(entry)?.spec;
2994
3122
  if (isLockedNpmSpec(spec)) yield [key, spec];
2995
3123
  }
@@ -3021,28 +3149,27 @@ MiaodaOfficialPluginsInstallSpecUnlockRule = __decorate([Rule({
3021
3149
  })], MiaodaOfficialPluginsInstallSpecUnlockRule);
3022
3150
  //#endregion
3023
3151
  //#region src/rules/miaoda-plugin-allow.ts
3024
- const MIAODA_PLUGIN = "openclaw-extension-miaoda";
3025
3152
  let MiaodaPluginAllowRule = class MiaodaPluginAllowRule extends DiagnoseRule {
3026
3153
  validate(ctx) {
3027
- if (!isPluginInstalledOnDisk(getExtensionsDir(ctx.configPath), MIAODA_PLUGIN)) return { pass: true };
3028
- if (getAllow$1(ctx.config).includes(MIAODA_PLUGIN)) return { pass: true };
3154
+ if (!isPluginInstalledOnDisk(getExtensionsDir(ctx.configPath), "openclaw-extension-miaoda")) return { pass: true };
3155
+ if (getAllow$1(ctx.config).includes("openclaw-extension-miaoda")) return { pass: true };
3029
3156
  return {
3030
3157
  pass: false,
3031
- message: `plugins.allow 缺少 ${MIAODA_PLUGIN}(已在 extensions/ 下装但未启用)`
3158
+ message: `plugins.allow 缺少 ${MIAODA_PLUGIN_NAME}(已在 extensions/ 下装但未启用)`
3032
3159
  };
3033
3160
  }
3034
3161
  repair(ctx) {
3035
- if (!isPluginInstalledOnDisk(getExtensionsDir(ctx.configPath), MIAODA_PLUGIN)) return;
3162
+ if (!isPluginInstalledOnDisk(getExtensionsDir(ctx.configPath), "openclaw-extension-miaoda")) return;
3036
3163
  const plugins = ctx.config.plugins;
3037
3164
  if (plugins == null || typeof plugins !== "object" || Array.isArray(plugins)) {
3038
- ctx.config.plugins = { allow: [MIAODA_PLUGIN] };
3165
+ ctx.config.plugins = { allow: [MIAODA_PLUGIN_NAME] };
3039
3166
  return;
3040
3167
  }
3041
3168
  const pluginsMap = plugins;
3042
3169
  const rawAllow = pluginsMap.allow;
3043
3170
  const allow = Array.isArray(rawAllow) ? rawAllow : [];
3044
- if (allow.includes(MIAODA_PLUGIN)) return;
3045
- allow.push(MIAODA_PLUGIN);
3171
+ if (allow.includes("openclaw-extension-miaoda")) return;
3172
+ allow.push(MIAODA_PLUGIN_NAME);
3046
3173
  pluginsMap.allow = allow;
3047
3174
  }
3048
3175
  };
@@ -3063,9 +3190,7 @@ function getAllow$1(config) {
3063
3190
  }
3064
3191
  //#endregion
3065
3192
  //#region src/rules/lark-plugin-allow.ts
3066
- const LARK_PLUGIN = "openclaw-lark";
3067
- const LEGACY_LARK_PLUGIN = "feishu-openclaw-plugin";
3068
- const LARK_PLUGIN_NAMES = [LARK_PLUGIN, LEGACY_LARK_PLUGIN];
3193
+ const LARK_PLUGIN_NAMES = FEISHU_PLUGIN_DIR_NAMES;
3069
3194
  let LarkPluginAllowRule = class LarkPluginAllowRule extends DiagnoseRule {
3070
3195
  validate(ctx) {
3071
3196
  const allow = getAllow(ctx.config);
@@ -3113,7 +3238,7 @@ function getAllow(config) {
3113
3238
  * 不读 package.json 内容,只判存在性,避开 JSON 损坏。
3114
3239
  */
3115
3240
  function detectInstalledLarkPlugin(extDir) {
3116
- for (const name of [LARK_PLUGIN, LEGACY_LARK_PLUGIN]) if (pluginPackageJsonExists(extDir, name)) return name;
3241
+ for (const name of LARK_PLUGIN_NAMES) if (pluginPackageJsonExists(extDir, name)) return name;
3117
3242
  return null;
3118
3243
  }
3119
3244
  function pluginPackageJsonExists(extDir, pluginDir) {
@@ -3125,7 +3250,6 @@ function pluginPackageJsonExists(extDir, pluginDir) {
3125
3250
  }
3126
3251
  //#endregion
3127
3252
  //#region src/rules/old-miaoda-plugins-cleanup.ts
3128
- const NEW_MIAODA = "openclaw-extension-miaoda";
3129
3253
  const OLD_PLUGIN_NAMES = Object.freeze([
3130
3254
  "openclaw-feishu-greeting",
3131
3255
  "openclaw-miaoda-keepalive",
@@ -3141,7 +3265,7 @@ function getPluginMaps(config) {
3141
3265
  };
3142
3266
  }
3143
3267
  function hasNewMiaoda({ entries, installs, allow }) {
3144
- return asRecord(entries?.[NEW_MIAODA]) != null || asRecord(installs?.[NEW_MIAODA]) != null || (allow?.includes(NEW_MIAODA) ?? false);
3268
+ return asRecord(entries?.["openclaw-extension-miaoda"]) != null || asRecord(installs?.["openclaw-extension-miaoda"]) != null || (allow?.includes("openclaw-extension-miaoda") ?? false);
3145
3269
  }
3146
3270
  function findResiduals({ entries, installs, allow }, extensionsDir) {
3147
3271
  return OLD_PLUGIN_NAMES.filter((name) => entries?.[name] != null || installs?.[name] != null || (allow?.includes(name) ?? false) || node_fs.default.existsSync(node_path.default.join(extensionsDir, name)));
@@ -3193,20 +3317,9 @@ OldMiaodaPluginsCleanupRule = __decorate([Rule({
3193
3317
  })], OldMiaodaPluginsCleanupRule);
3194
3318
  //#endregion
3195
3319
  //#region src/rules/builtin-plugin-missing.ts
3196
- /**
3197
- * install-extension --all 安装的内置扩展插件列表(manifest role=extension)。
3198
- * 与 miaoda-official-plugins-install-spec-unlock.ts 中的 OFFICIAL_PLUGIN_NAMES 保持一致。
3199
- */
3200
- const BUILTIN_EXTENSION_PLUGINS = new Set([
3201
- "openclaw-lark",
3202
- "openclaw-extension-miaoda",
3203
- "openclaw-extension-miaoda-coding",
3204
- "openclaw-guardian-plugin",
3205
- "openclaw-mem0-plugin"
3206
- ]);
3207
3320
  function findMissingBuiltinPlugins(ctx) {
3208
3321
  const extDir = getExtensionsDir(ctx.configPath);
3209
- return [...BUILTIN_EXTENSION_PLUGINS].filter((name) => !isPluginInstalledOnDisk(extDir, name)).sort();
3322
+ return [...OFFICIAL_EXTENSION_PLUGIN_NAMES].filter((name) => !isPluginInstalledOnDisk(extDir, name)).sort();
3210
3323
  }
3211
3324
  let BuiltinPluginMissingRule = class BuiltinPluginMissingRule extends DiagnoseRule {
3212
3325
  validate(ctx) {
@@ -3487,10 +3600,7 @@ function extractTarballTolerant(tarball, destDir, opts = {}) {
3487
3600
  }
3488
3601
  //#endregion
3489
3602
  //#region src/rules/feishu-plugin-state-normalize.ts
3490
- const PLUGIN_NAME$2 = "openclaw-lark";
3491
- const BUILTIN_FEISHU = "feishu";
3492
- const LEGACY_PLUGIN_NAME = "feishu-openclaw-plugin";
3493
- const LEGACY_DIRS_TO_REMOVE = [LEGACY_PLUGIN_NAME, BUILTIN_FEISHU];
3603
+ const LEGACY_DIRS_TO_REMOVE = [LEGACY_LARK_PLUGIN_NAME, BUILTIN_FEISHU_PLUGIN_NAME];
3494
3604
  const FEISHU_TOOLS = Object.freeze([
3495
3605
  "feishu_bitable_app",
3496
3606
  "feishu_bitable_app_table",
@@ -3536,9 +3646,10 @@ let FeishuPluginStateNormalizeRule = class FeishuPluginStateNormalizeRule extend
3536
3646
  validate(ctx) {
3537
3647
  if (!isPluginInstalled(ctx)) return { pass: true };
3538
3648
  const fails = [];
3539
- if (!isNewPluginEnabled(ctx.config)) fails.push(`plugins.entries["${PLUGIN_NAME$2}"].enabled !== true(应启用)`);
3649
+ if (!isNewPluginEnabled(ctx.config)) fails.push(`plugins.entries["${LARK_PLUGIN_NAME}"].enabled !== true(应启用)`);
3540
3650
  if (isBuiltinFeishuEnabled(ctx.config)) fails.push("plugins.entries.feishu.enabled === true(应禁用)");
3541
- if (isTopLevelMissingFeishuTools(ctx.config)) fails.push("tools.alsoAllow 缺 feishu_* tools");
3651
+ const grantGap = missingFeishuToolsGrant(ctx.config);
3652
+ if (grantGap) fails.push(`tools.${grantGap} 缺 feishu_* tools`);
3542
3653
  const legacyResiduals = findLegacyResiduals(ctx);
3543
3654
  if (legacyResiduals.length > 0) fails.push(`legacy 飞书插件残留:${legacyResiduals.join(", ")}`);
3544
3655
  if (fails.length === 0) return { pass: true };
@@ -3548,8 +3659,8 @@ let FeishuPluginStateNormalizeRule = class FeishuPluginStateNormalizeRule extend
3548
3659
  };
3549
3660
  }
3550
3661
  repair(ctx) {
3551
- setEntryEnabled(ctx.config, PLUGIN_NAME$2, true);
3552
- setEntryEnabled(ctx.config, BUILTIN_FEISHU, false);
3662
+ setEntryEnabled(ctx.config, LARK_PLUGIN_NAME, true);
3663
+ setEntryEnabled(ctx.config, BUILTIN_FEISHU_PLUGIN_NAME, false);
3553
3664
  ensureFeishuTools(ctx.config);
3554
3665
  cleanupLegacyResiduals(ctx);
3555
3666
  }
@@ -3562,36 +3673,54 @@ FeishuPluginStateNormalizeRule = __decorate([Rule({
3562
3673
  level: "critical"
3563
3674
  })], FeishuPluginStateNormalizeRule);
3564
3675
  function isPluginInstalled(ctx) {
3565
- return node_fs.default.existsSync(node_path.default.join(getExtensionsDir(ctx.configPath), PLUGIN_NAME$2));
3676
+ return node_fs.default.existsSync(node_path.default.join(getExtensionsDir(ctx.configPath), LARK_PLUGIN_NAME));
3566
3677
  }
3567
3678
  function isNewPluginEnabled(config) {
3568
- return asRecord(getNestedMap(config, "plugins", "entries")?.[PLUGIN_NAME$2])?.enabled === true;
3679
+ return asRecord(getNestedMap(config, "plugins", "entries")?.[LARK_PLUGIN_NAME])?.enabled === true;
3569
3680
  }
3570
3681
  function isBuiltinFeishuEnabled(config) {
3571
- return asRecord(getNestedMap(config, "plugins", "entries")?.[BUILTIN_FEISHU])?.enabled === true;
3572
- }
3573
- /** 仅看顶层 tools.alsoAllow——agent 级 alsoAllow 是用户对单 agent 的精细化授权,doctor 不动。
3574
- * 含 "*" 或任一 feishu_* 即视为已配。 */
3575
- function isTopLevelMissingFeishuTools(config) {
3576
- return !hasFeishuTool(readAlsoAllow(config));
3682
+ return asRecord(getNestedMap(config, "plugins", "entries")?.[BUILTIN_FEISHU_PLUGIN_NAME])?.enabled === true;
3577
3683
  }
3578
- function readAlsoAllow(host) {
3579
- const tools = asRecord(host)?.tools;
3580
- const arr = asRecord(tools)?.alsoAllow;
3684
+ /** 读取顶层 tools.<key>(allow / alsoAllow)的字符串数组。 */
3685
+ function readTopLevelToolsList(config, key) {
3686
+ const arr = asRecord(asRecord(config)?.tools)?.[key];
3581
3687
  if (!Array.isArray(arr)) return [];
3582
3688
  return arr.filter((e) => typeof e === "string");
3583
3689
  }
3584
- function hasFeishuTool(alsoAllow) {
3585
- if (alsoAllow.includes("*")) return true;
3586
- return alsoAllow.some((t) => FEISHU_TOOLS.includes(t));
3690
+ function hasFeishuTool(list) {
3691
+ if (list.includes("*")) return true;
3692
+ return list.some((t) => FEISHU_TOOLS.includes(t));
3693
+ }
3694
+ /**
3695
+ * 决定 feishu_* 应补充到哪个授权键:
3696
+ * - 已有非空 tools.allow(限制式白名单)→ 'allow'(合并进 allow,避免与 alsoAllow 冲突)
3697
+ * - 否则 → 'alsoAllow'(追加式,叠加在 profile 基线上)
3698
+ * openclaw schema 禁止同一 scope 同时设 allow + alsoAllow,故有 allow 时必须并入 allow,
3699
+ * 否则会触发 tools_allow_also_allow_conflict 规则反复来回改写。
3700
+ */
3701
+ function feishuGrantTarget(config) {
3702
+ return readTopLevelToolsList(config, "allow").length > 0 ? "allow" : "alsoAllow";
3703
+ }
3704
+ /**
3705
+ * 仅看顶层 tools——agent 级授权是用户对单 agent 的精细化配置,doctor 不动。
3706
+ * 返回缺失的目标键名('allow' / 'alsoAllow'),不缺时返回 null。
3707
+ *
3708
+ * 不关心 lark-cli:tools.alsoAllow(授权层)与 channels.feishu.tools.deny(插件注册层)
3709
+ * 是两个正交的 config key,可并存——授权一个被 deny 的工具是无害空操作。因此本规则始终
3710
+ * 补全 FEISHU_TOOLS(让 openclaw-lark 实际注册的工具都可用),重叠工具的"不可用"由独立的
3711
+ * deny 规则在注册层处理,互不干扰。
3712
+ */
3713
+ function missingFeishuToolsGrant(config) {
3714
+ const target = feishuGrantTarget(config);
3715
+ return hasFeishuTool(readTopLevelToolsList(config, target)) ? null : target;
3587
3716
  }
3588
3717
  function findLegacyResiduals(ctx) {
3589
3718
  const found = [];
3590
3719
  const plugins = asRecord(ctx.config.plugins);
3591
- if (asRecord(plugins?.entries)?.[LEGACY_PLUGIN_NAME] != null) found.push("entries[legacy]");
3720
+ if (asRecord(plugins?.entries)?.["feishu-openclaw-plugin"] != null) found.push("entries[legacy]");
3592
3721
  const allow = plugins?.allow;
3593
- if (Array.isArray(allow) && allow.includes(LEGACY_PLUGIN_NAME)) found.push("allow[legacy]");
3594
- if (asRecord(plugins?.installs)?.[LEGACY_PLUGIN_NAME] != null) found.push("installs[legacy]");
3722
+ if (Array.isArray(allow) && allow.includes("feishu-openclaw-plugin")) found.push("allow[legacy]");
3723
+ if (asRecord(plugins?.installs)?.["feishu-openclaw-plugin"] != null) found.push("installs[legacy]");
3595
3724
  const extDir = getExtensionsDir(ctx.configPath);
3596
3725
  for (const name of LEGACY_DIRS_TO_REMOVE) if (node_fs.default.existsSync(node_path.default.join(extDir, name))) found.push(`fs/${name}`);
3597
3726
  return found;
@@ -3604,21 +3733,22 @@ function setEntryEnabled(config, key, enabled) {
3604
3733
  };
3605
3734
  }
3606
3735
  function ensureFeishuTools(config) {
3607
- const alsoAllow = readAlsoAllow(config);
3608
- if (hasFeishuTool(alsoAllow)) return;
3609
- ensureRecord(config, "tools").alsoAllow = [...new Set([...alsoAllow, ...FEISHU_TOOLS])];
3736
+ const target = feishuGrantTarget(config);
3737
+ const current = readTopLevelToolsList(config, target);
3738
+ if (hasFeishuTool(current)) return;
3739
+ ensureRecord(config, "tools")[target] = [...new Set([...current, ...FEISHU_TOOLS])];
3610
3740
  }
3611
3741
  function cleanupLegacyResiduals(ctx) {
3612
3742
  const plugins = asRecord(ctx.config.plugins);
3613
3743
  if (plugins) {
3614
3744
  const entries = asRecord(plugins.entries);
3615
- if (entries && LEGACY_PLUGIN_NAME in entries) delete entries[LEGACY_PLUGIN_NAME];
3745
+ if (entries && "feishu-openclaw-plugin" in entries) delete entries[LEGACY_LARK_PLUGIN_NAME];
3616
3746
  const installs = asRecord(plugins.installs);
3617
- if (installs && LEGACY_PLUGIN_NAME in installs) delete installs[LEGACY_PLUGIN_NAME];
3747
+ if (installs && "feishu-openclaw-plugin" in installs) delete installs[LEGACY_LARK_PLUGIN_NAME];
3618
3748
  const allow = plugins.allow;
3619
3749
  if (Array.isArray(allow)) {
3620
- for (let i = allow.length - 1; i >= 0; i--) if (allow[i] === LEGACY_PLUGIN_NAME) allow.splice(i, 1);
3621
- if (!allow.includes(PLUGIN_NAME$2)) allow.push(PLUGIN_NAME$2);
3750
+ for (let i = allow.length - 1; i >= 0; i--) if (allow[i] === "feishu-openclaw-plugin") allow.splice(i, 1);
3751
+ if (!allow.includes("openclaw-lark")) allow.push(LARK_PLUGIN_NAME);
3622
3752
  }
3623
3753
  }
3624
3754
  const extDir = getExtensionsDir(ctx.configPath);
@@ -3791,6 +3921,17 @@ function effectiveCompatible(pluginVersion, openclawVersion) {
3791
3921
  const max = coerceCalVer(maxInfo.max);
3792
3922
  return maxInfo.exclusive ? semver.default.lt(oc, max) : !semver.default.gt(oc, max);
3793
3923
  }
3924
+ /**
3925
+ * Fork plugin handling.
3926
+ *
3927
+ * `@lark-apaas/openclaw-lark` is an internal fork whose own version numbers
3928
+ * (e.g. 2026.4.3, 2026.4.4) are NOT keys in VERSION_COMPAT_MAP. Floor-matching
3929
+ * a fork version against the official-keyed map only works by coincidence of
3930
+ * numbering and breaks once the fork version drifts past an official entry.
3931
+ * Both the install-time compat check and the diagnose rule must therefore pin
3932
+ * the fork to its official-equivalent version before consulting the map.
3933
+ */
3934
+ /** fork 对标的官方 openclaw-lark 版本(与 VERSION_COMPAT_MAP 强耦合,故留在此处)。 */
3794
3935
  const FORK_LARK_PLUGIN_PINNED_VERSION = "2026.4.1";
3795
3936
  /**
3796
3937
  * Resolve the version to evaluate against VERSION_COMPAT_MAP for a given plugin.
@@ -3814,9 +3955,8 @@ function findClosestEntry(pluginVersion) {
3814
3955
  }
3815
3956
  //#endregion
3816
3957
  //#region src/rules/feishu-plugin-version-compat.ts
3817
- const PLUGIN_NAME$1 = "openclaw-lark";
3818
- const LEGACY_SHORT_NAMES = ["feishu-openclaw-plugin"];
3819
- const FORK_SCOPES = ["@lark-apaas"];
3958
+ const LEGACY_SHORT_NAMES = [LEGACY_LARK_PLUGIN_NAME];
3959
+ const FORK_SCOPES = [FORK_LARK_PLUGIN_SCOPE];
3820
3960
  /**
3821
3961
  * fork 版兼容区间的下界,取自 VERSION_COMPAT_MAP 中 openclawLarkVersion=2026.4.1
3822
3962
  * 的 minOpenclawVersion,避免与映射表脱钩写死。仅用于区分升级方向
@@ -4027,7 +4167,7 @@ function detectInstalledPlugin(ctx) {
4027
4167
  const allow = Array.isArray(allowRaw) ? allowRaw.filter((e) => typeof e === "string") : [];
4028
4168
  const extDir = getExtensionsDir(ctx.configPath);
4029
4169
  const installs = getNestedMap(ctx.config, "plugins", "installs");
4030
- for (const name of [PLUGIN_NAME$1, ...LEGACY_SHORT_NAMES]) {
4170
+ for (const name of [LARK_PLUGIN_NAME, ...LEGACY_SHORT_NAMES]) {
4031
4171
  if (!allow.includes(name)) continue;
4032
4172
  const pkgPath = node_path.default.join(extDir, name, "package.json");
4033
4173
  if (!node_fs.default.existsSync(pkgPath)) continue;
@@ -4120,12 +4260,10 @@ CleanupInstallBackupDirsRule = __decorate([Rule({
4120
4260
  })], CleanupInstallBackupDirsRule);
4121
4261
  //#endregion
4122
4262
  //#region src/rules/lark-cli-missing-for-installed-lark-plugin.ts
4123
- const PLUGIN_NAME = "openclaw-lark";
4124
- const FORK_PACKAGE_NAME = "@lark-apaas/openclaw-lark";
4263
+ /** 触发本规则自动安装 lark-cli 的特定 fork 版本(与对标的官方兼容版本无关)。 */
4125
4264
  const TARGET_VERSION = "2026.4.4";
4126
- const LARK_CLI_NAME$1 = "lark-cli";
4127
4265
  function readInstalledLarkPlugin(ctx) {
4128
- const pkgPath = node_path.default.join(getExtensionsDir(ctx.configPath), PLUGIN_NAME, "package.json");
4266
+ const pkgPath = node_path.default.join(getExtensionsDir(ctx.configPath), LARK_PLUGIN_NAME, "package.json");
4129
4267
  if (!node_fs.default.existsSync(pkgPath)) return null;
4130
4268
  let pkg = {};
4131
4269
  try {
@@ -4138,14 +4276,14 @@ function readInstalledLarkPlugin(ctx) {
4138
4276
  pkg = {};
4139
4277
  }
4140
4278
  const installs = getNestedMap(ctx.config, "plugins", "installs");
4141
- const installEntry = installs ? asRecord(installs[PLUGIN_NAME]) : void 0;
4279
+ const installEntry = installs ? asRecord(installs[LARK_PLUGIN_NAME]) : void 0;
4142
4280
  return {
4143
4281
  name: pkg.name ?? extractScopedNameFromSpec(installEntry?.spec),
4144
4282
  version: pkg.version ?? (typeof installEntry?.version === "string" ? installEntry.version : void 0)
4145
4283
  };
4146
4284
  }
4147
4285
  function isTargetForkPlugin(plugin) {
4148
- return plugin?.name === FORK_PACKAGE_NAME && plugin.version === TARGET_VERSION;
4286
+ return plugin?.name === "@lark-apaas/openclaw-lark" && plugin.version === TARGET_VERSION;
4149
4287
  }
4150
4288
  function extractScopedNameFromSpec(spec) {
4151
4289
  if (typeof spec !== "string") return void 0;
@@ -4154,7 +4292,7 @@ function extractScopedNameFromSpec(spec) {
4154
4292
  }
4155
4293
  function isLarkCliAvailable() {
4156
4294
  try {
4157
- return (0, node_child_process.spawnSync)(LARK_CLI_NAME$1, ["--version"], {
4295
+ return (0, node_child_process.spawnSync)(LARK_CLI_NAME, ["--version"], {
4158
4296
  encoding: "utf-8",
4159
4297
  timeout: 5e3,
4160
4298
  stdio: [
@@ -4196,7 +4334,7 @@ let LarkCliMissingForInstalledLarkPluginRule = class LarkCliMissingForInstalledL
4196
4334
  if (isLarkCliAvailable()) return { pass: true };
4197
4335
  return {
4198
4336
  pass: false,
4199
- message: `${FORK_PACKAGE_NAME}@${TARGET_VERSION} 已安装,但 lark-cli 不可用;将执行一次 lark-cli 安装`
4337
+ message: `${FORK_LARK_PLUGIN_FULL_NAME}@${TARGET_VERSION} 已安装,但 lark-cli 不可用;将执行一次 lark-cli 安装`
4200
4338
  };
4201
4339
  }
4202
4340
  repair(ctx) {
@@ -5210,7 +5348,7 @@ async function installOpenclaw(openclawTag, ossFileMap, opts = {}) {
5210
5348
  console.error(`[install-openclaw] done in ${Date.now() - t0}ms`);
5211
5349
  }
5212
5350
  //#endregion
5213
- //#region src/lark-tools-update.ts
5351
+ //#region src/utils/lark-tools-update.ts
5214
5352
  /**
5215
5353
  * 共享的飞书插件备份 / 恢复 / `openclaw-lark-tools update` 调用。
5216
5354
  *
@@ -5221,8 +5359,8 @@ async function installOpenclaw(openclawTag, ossFileMap, opts = {}) {
5221
5359
  * `console.error`,独立调用方不传则静默。
5222
5360
  */
5223
5361
  const NOOP_LOG = () => {};
5224
- /** 升级前需备份的 extensions/ 下的插件目录(含 legacy */
5225
- const FEISHU_PLUGIN_DIRS = ["openclaw-lark", "feishu-openclaw-plugin"];
5362
+ /** 升级前需备份的 extensions/ 下的插件目录(官方短名 + legacy)。 */
5363
+ const FEISHU_PLUGIN_DIRS = FEISHU_PLUGIN_DIR_NAMES;
5226
5364
  /** 读取 package.json 的 version 字段,失败返回 null(仅用于日志) */
5227
5365
  function readPkgVersion(pkgPath) {
5228
5366
  try {
@@ -5342,6 +5480,8 @@ function runLarkToolsUpdate(opts) {
5342
5480
  spawnError: r.error?.message
5343
5481
  };
5344
5482
  }
5483
+ //#endregion
5484
+ //#region src/install-extension.ts
5345
5485
  async function installExtension(tag, ossFileMap, opts = {}) {
5346
5486
  const homeBase = resolveHomeBase(opts.homeBase);
5347
5487
  const hasAll = !!opts.all;
@@ -5364,18 +5504,18 @@ async function installExtension(tag, ossFileMap, opts = {}) {
5364
5504
  }
5365
5505
  console.error(`[install-extension] tag=${tag} targets=${targets.length}`);
5366
5506
  const t0 = Date.now();
5367
- const larkTarget = opts.skipConfigUpdate ? void 0 : targets.find((p) => p.name === LARK_PLUGIN_NAME$1);
5507
+ const larkTarget = opts.skipConfigUpdate ? void 0 : targets.find((p) => p.name === LARK_PLUGIN_NAME);
5368
5508
  if (larkTarget) {
5369
5509
  if (await installLarkPluginWithCompatCheck({
5370
5510
  tag,
5371
5511
  pkg: larkTarget,
5372
5512
  homeBase,
5373
- configPath: opts.configPath ?? node_path.default.join(homeBase, "workspace/agent/openclaw.json"),
5513
+ configPath: opts.configPath ?? node_path.default.join(homeBase, DEFAULT_CONFIG_REL),
5374
5514
  workspaceDir: node_path.default.join(homeBase, "workspace", "agent"),
5375
5515
  ossFileMap,
5376
5516
  downloadOpts: opts
5377
5517
  })) {
5378
- targets = targets.filter((p) => p.name !== LARK_PLUGIN_NAME$1);
5518
+ targets = targets.filter((p) => p.name !== LARK_PLUGIN_NAME);
5379
5519
  if (targets.length === 0) {
5380
5520
  console.error(`[install-extension] done in ${Date.now() - t0}ms`);
5381
5521
  return;
@@ -5394,13 +5534,11 @@ async function installExtension(tag, ossFileMap, opts = {}) {
5394
5534
  installOne$1(pkg, tarball, homeBase);
5395
5535
  console.error(`[install-extension] ${pkg.name}: installed`);
5396
5536
  }
5397
- if (!opts.skipConfigUpdate) updatePluginInstalls(opts.configPath ?? node_path.default.join(homeBase, "workspace/agent/openclaw.json"), targets);
5537
+ if (!opts.skipConfigUpdate) updatePluginInstalls(opts.configPath ?? node_path.default.join(homeBase, DEFAULT_CONFIG_REL), targets);
5398
5538
  else console.error(`[install-extension] skipConfigUpdate=true — not touching openclaw.json`);
5399
5539
  console.error(`[install-extension] done ${targets.length}/${targets.length} in ${Date.now() - t0}ms`);
5400
5540
  }
5401
- const LARK_PLUGIN_NAME$1 = "openclaw-lark";
5402
- const MEM0_PLUGIN_NAME = "openclaw-mem0-plugin";
5403
- const PLUGINS_TO_AUTO_ENABLE = ["openclaw-lark", "openclaw-extension-miaoda"];
5541
+ const PLUGINS_TO_AUTO_ENABLE = [LARK_PLUGIN_NAME, MIAODA_PLUGIN_NAME];
5404
5542
  /**
5405
5543
  * Merge each installed extension's installMetadata into openclaw.json's
5406
5544
  * plugins.installs[<pkg.name>]. Atomic write via tmp + rename.
@@ -5432,14 +5570,14 @@ function updatePluginInstalls(configPath, installedPkgs) {
5432
5570
  installs[pkg.name] = pkg.installMetadata;
5433
5571
  updated++;
5434
5572
  } else skipped++;
5435
- if (installedPkgs.some((p) => p.name === MEM0_PLUGIN_NAME)) {
5573
+ if (installedPkgs.some((p) => p.name === "openclaw-mem0-plugin")) {
5436
5574
  const allowRaw = plugins.allow;
5437
5575
  const allow = Array.isArray(allowRaw) ? allowRaw : [];
5438
- if (!allow.filter((e) => typeof e === "string").includes(MEM0_PLUGIN_NAME)) allow.push(MEM0_PLUGIN_NAME);
5576
+ if (!allow.filter((e) => typeof e === "string").includes("openclaw-mem0-plugin")) allow.push(MEM0_PLUGIN_NAME);
5439
5577
  plugins.allow = allow;
5440
5578
  if (!plugins.entries || typeof plugins.entries !== "object" || Array.isArray(plugins.entries)) plugins.entries = {};
5441
5579
  const entries = plugins.entries;
5442
- if (!(MEM0_PLUGIN_NAME in entries)) entries[MEM0_PLUGIN_NAME] = { enabled: false };
5580
+ if (!("openclaw-mem0-plugin" in entries)) entries[MEM0_PLUGIN_NAME] = { enabled: false };
5443
5581
  }
5444
5582
  for (const pkg of installedPkgs) {
5445
5583
  if (!PLUGINS_TO_AUTO_ENABLE.includes(pkg.name)) continue;
@@ -5557,7 +5695,9 @@ async function installLarkPluginWithCompatCheck(opts) {
5557
5695
  recursive: true,
5558
5696
  force: true
5559
5697
  });
5560
- } catch {}
5698
+ } catch (e) {
5699
+ console.error(`[install-extension] WARN: failed to clean up backup dir ${backupDir}: ${e.message}`);
5700
+ }
5561
5701
  }
5562
5702
  }
5563
5703
  /**
@@ -5566,7 +5706,7 @@ async function installLarkPluginWithCompatCheck(opts) {
5566
5706
  */
5567
5707
  function validateLarkPluginInstall(opts) {
5568
5708
  const { homeBase, configPath } = opts;
5569
- const pkgJson = node_path.default.join(homeBase, "workspace", "agent", "extensions", LARK_PLUGIN_NAME$1, "package.json");
5709
+ const pkgJson = node_path.default.join(homeBase, "workspace", "agent", "extensions", LARK_PLUGIN_NAME, "package.json");
5570
5710
  if (!node_fs.default.existsSync(pkgJson)) return {
5571
5711
  ok: false,
5572
5712
  error: `plugin directory missing: ${pkgJson}`
@@ -5576,9 +5716,9 @@ function validateLarkPluginInstall(opts) {
5576
5716
  error: `openclaw.json not found at ${configPath}`
5577
5717
  };
5578
5718
  try {
5579
- if (asRecord(getNestedMap(loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8")), "plugins", "entries")?.[LARK_PLUGIN_NAME$1])?.enabled !== true) return {
5719
+ if (asRecord(getNestedMap(loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8")), "plugins", "entries")?.["openclaw-lark"])?.enabled !== true) return {
5580
5720
  ok: false,
5581
- error: `plugins.entries["${LARK_PLUGIN_NAME$1}"].enabled is not true`
5721
+ error: `plugins.entries["${LARK_PLUGIN_NAME}"].enabled is not true`
5582
5722
  };
5583
5723
  } catch (e) {
5584
5724
  return {
@@ -6868,34 +7008,6 @@ function reportError(params) {
6868
7008
  }
6869
7009
  //#endregion
6870
7010
  //#region src/install-cli.ts
6871
- const LARK_CLI_NAME = "lark-cli";
6872
- const AGENT_SKILLS_NAME = "agent-skills";
6873
- const WORKSPACE_AGENT_REL = "workspace/agent";
6874
- const LARK_PLUGIN_NAME = "openclaw-lark";
6875
- /**
6876
- * openclaw-lark tools that overlap with lark-cli functionality.
6877
- * Written to channels.feishu.tools.deny after lark-cli is installed so that
6878
- * openclaw-lark's shouldRegisterTool() skips them, avoiding duplicate tools.
6879
- * Supports trailing-* wildcards (handled by openclaw-lark's matchesAnyPattern).
6880
- *
6881
- * Kept: feishu_chat*, feishu_get_user, feishu_search_user, feishu_im_*,
6882
- * feishu_oauth*, feishu_auth, feishu_diagnose, feishu_doctor
6883
- */
6884
- const LARK_CLI_OVERLAP_TOOL_DENY = Object.freeze([
6885
- "feishu_create_doc",
6886
- "feishu_fetch_doc",
6887
- "feishu_update_doc",
6888
- "feishu_doc_comments",
6889
- "feishu_doc_media",
6890
- "feishu_drive_file",
6891
- "feishu_wiki_space",
6892
- "feishu_wiki_space_node",
6893
- "feishu_search_doc_wiki",
6894
- "feishu_bitable_*",
6895
- "feishu_calendar_*",
6896
- "feishu_task_*",
6897
- "feishu_sheet"
6898
- ]);
6899
7011
  async function installClis(tag, ossFileMap, opts) {
6900
7012
  const homeBase = resolveHomeBase(opts.homeBase);
6901
7013
  if (opts.names.length === 0) throw new Error("install-cli: must provide at least one --cli=<name>");
@@ -6935,7 +7047,7 @@ async function installClis(tag, ossFileMap, opts) {
6935
7047
  linkBins(pkg, homeBase);
6936
7048
  console.error(`[install-cli] ${pkg.name}: installed`);
6937
7049
  }
6938
- if (targets.some((p) => p.name === LARK_CLI_NAME)) {
7050
+ if (targets.some((p) => p.name === "lark-cli")) {
6939
7051
  const skillsInstallPath = await installAgentSkills(manifest, ossFileMap, opts, homeBase, opts.tmpRoot);
6940
7052
  if (skillsInstallPath) linkAgentSkills(homeBase, skillsInstallPath);
6941
7053
  const appIds = collectFeishuAppIds();
@@ -6997,7 +7109,7 @@ function linkBins(pkg, homeBase) {
6997
7109
  }
6998
7110
  }
6999
7111
  async function installAgentSkills(manifest, ossFileMap, downloadOpts, homeBase, tmpRoot) {
7000
- const pkg = manifest.packages.find((p) => p.role === "template" && p.name === AGENT_SKILLS_NAME);
7112
+ const pkg = manifest.packages.find((p) => p.role === "template" && p.name === "agent-skills");
7001
7113
  if (!pkg) {
7002
7114
  console.error(`[install-cli] installAgentSkills: ${AGENT_SKILLS_NAME} not found in manifest (tag may not bundle it) — skipping skill install`);
7003
7115
  return null;
@@ -7155,18 +7267,14 @@ function disableLarkCliOverlapTools(configPath, homeBase) {
7155
7267
  }
7156
7268
  try {
7157
7269
  const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
7158
- if (!config.channels || typeof config.channels !== "object") config.channels = {};
7159
- const channels = config.channels;
7160
- if (!channels.feishu || typeof channels.feishu !== "object") channels.feishu = {};
7161
- const feishu = channels.feishu;
7162
- if (!feishu.tools || typeof feishu.tools !== "object") feishu.tools = {};
7163
- const tools = feishu.tools;
7164
- const existing = Array.isArray(tools.deny) ? tools.deny : [];
7165
- tools.deny = [...new Set([...existing, ...LARK_CLI_OVERLAP_TOOL_DENY])];
7270
+ if (!ensureLarkCliToolDeny(config)) {
7271
+ console.error("[install-cli] disableLarkCliOverlapTools: deny already up to date");
7272
+ return;
7273
+ }
7166
7274
  const tmp = configPath + ".lark-cli-deny-tmp";
7167
7275
  node_fs.default.writeFileSync(tmp, JSON.stringify(config, null, 2), "utf-8");
7168
7276
  moveSafe(tmp, configPath);
7169
- console.error(`[install-cli] disableLarkCliOverlapTools: channels.feishu.tools.deny updated (${LARK_CLI_OVERLAP_TOOL_DENY.length} patterns)`);
7277
+ console.error("[install-cli] disableLarkCliOverlapTools: channels.feishu.tools.deny updated");
7170
7278
  } catch (e) {
7171
7279
  console.error(`[install-cli] WARN: disableLarkCliOverlapTools failed: ${e.message}`);
7172
7280
  }
@@ -11106,7 +11214,7 @@ async function reportCliRun(opts) {
11106
11214
  //#region src/help.ts
11107
11215
  const BIN = "mclaw-diagnose";
11108
11216
  function versionBanner() {
11109
- return `v0.1.18-alpha.2`;
11217
+ return `v0.1.18-alpha.3`;
11110
11218
  }
11111
11219
  const COMMANDS = [
11112
11220
  {
@@ -11805,8 +11913,8 @@ function snapshotVersions(cwd, log) {
11805
11913
  });
11806
11914
  const ocRaw = (ocResult.stdout ?? "").trim() || (ocResult.stderr ?? "").trim();
11807
11915
  const extDir = node_path.default.join(cwd, "extensions");
11808
- const larkPkg = node_path.default.join(extDir, "openclaw-lark", "package.json");
11809
- const feishuPkg = node_path.default.join(extDir, "feishu-openclaw-plugin", "package.json");
11916
+ const larkPkg = node_path.default.join(extDir, LARK_PLUGIN_NAME, "package.json");
11917
+ const feishuPkg = node_path.default.join(extDir, LEGACY_LARK_PLUGIN_NAME, "package.json");
11810
11918
  log(` version-check paths: ${larkPkg} [${node_fs.default.existsSync(larkPkg) ? "exists" : "missing"}]`);
11811
11919
  log(` version-check paths: ${feishuPkg} [${node_fs.default.existsSync(feishuPkg) ? "exists" : "missing"}]`);
11812
11920
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/openclaw-scripts-diagnose-cli",
3
- "version": "0.1.18-alpha.2",
3
+ "version": "0.1.18-alpha.3",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {