@lark-apaas/openclaw-scripts-diagnose-cli 0.1.18-alpha.1 → 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.
- package/dist/index.cjs +276 -164
- 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.
|
|
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
|
|
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/
|
|
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 =
|
|
2938
|
-
|
|
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: "
|
|
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 (!
|
|
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),
|
|
3028
|
-
if (getAllow$1(ctx.config).includes(
|
|
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 缺少 ${
|
|
3158
|
+
message: `plugins.allow 缺少 ${MIAODA_PLUGIN_NAME}(已在 extensions/ 下装但未启用)`
|
|
3032
3159
|
};
|
|
3033
3160
|
}
|
|
3034
3161
|
repair(ctx) {
|
|
3035
|
-
if (!isPluginInstalledOnDisk(getExtensionsDir(ctx.configPath),
|
|
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: [
|
|
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(
|
|
3045
|
-
allow.push(
|
|
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
|
|
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
|
|
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?.[
|
|
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 [...
|
|
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
|
|
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["${
|
|
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
|
-
|
|
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,
|
|
3552
|
-
setEntryEnabled(ctx.config,
|
|
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),
|
|
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")?.[
|
|
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")?.[
|
|
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
|
-
|
|
3579
|
-
|
|
3580
|
-
const arr = asRecord(tools)?.
|
|
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(
|
|
3585
|
-
if (
|
|
3586
|
-
return
|
|
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)?.[
|
|
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(
|
|
3594
|
-
if (asRecord(plugins?.installs)?.[
|
|
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
|
|
3608
|
-
|
|
3609
|
-
|
|
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 &&
|
|
3745
|
+
if (entries && "feishu-openclaw-plugin" in entries) delete entries[LEGACY_LARK_PLUGIN_NAME];
|
|
3616
3746
|
const installs = asRecord(plugins.installs);
|
|
3617
|
-
if (installs &&
|
|
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] ===
|
|
3621
|
-
if (!allow.includes(
|
|
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);
|
|
@@ -3792,6 +3922,27 @@ function effectiveCompatible(pluginVersion, openclawVersion) {
|
|
|
3792
3922
|
return maxInfo.exclusive ? semver.default.lt(oc, max) : !semver.default.gt(oc, max);
|
|
3793
3923
|
}
|
|
3794
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 强耦合,故留在此处)。 */
|
|
3935
|
+
const FORK_LARK_PLUGIN_PINNED_VERSION = "2026.4.1";
|
|
3936
|
+
/**
|
|
3937
|
+
* Resolve the version to evaluate against VERSION_COMPAT_MAP for a given plugin.
|
|
3938
|
+
* The fork is pinned to FORK_LARK_PLUGIN_PINNED_VERSION regardless of its own
|
|
3939
|
+
* version; everything else uses its real version.
|
|
3940
|
+
*/
|
|
3941
|
+
function resolveCompatVersion(packageName, version) {
|
|
3942
|
+
if (packageName === "@lark-apaas/openclaw-lark") return FORK_LARK_PLUGIN_PINNED_VERSION;
|
|
3943
|
+
return version;
|
|
3944
|
+
}
|
|
3945
|
+
/**
|
|
3795
3946
|
* Floor match: find the entry with the largest openclawLarkVersion that is
|
|
3796
3947
|
* ≤ pluginVersion ("closest lower-or-equal version").
|
|
3797
3948
|
*
|
|
@@ -3804,22 +3955,14 @@ function findClosestEntry(pluginVersion) {
|
|
|
3804
3955
|
}
|
|
3805
3956
|
//#endregion
|
|
3806
3957
|
//#region src/rules/feishu-plugin-version-compat.ts
|
|
3807
|
-
const
|
|
3808
|
-
const
|
|
3809
|
-
const FORK_SCOPES = ["@lark-apaas"];
|
|
3810
|
-
/** 特化 fork 版全名:复用官方 effectiveCompatible,按对标版本取完整兼容区间 */
|
|
3811
|
-
const FORK_LARK_PLUGIN_FULL_NAME = "@lark-apaas/openclaw-lark";
|
|
3812
|
-
/**
|
|
3813
|
-
* fork 版对标的官方 openclaw-lark 版本。fork 自身版本不在 VERSION_COMPAT_MAP 内,
|
|
3814
|
-
* 兼容性按此对标版本经 effectiveCompatible 取完整区间(含推断上界)判定。
|
|
3815
|
-
*/
|
|
3816
|
-
const FORK_LARK_PLUGIN_PINNED_VERSION = "2026.4.1";
|
|
3958
|
+
const LEGACY_SHORT_NAMES = [LEGACY_LARK_PLUGIN_NAME];
|
|
3959
|
+
const FORK_SCOPES = [FORK_LARK_PLUGIN_SCOPE];
|
|
3817
3960
|
/**
|
|
3818
3961
|
* fork 版兼容区间的下界,取自 VERSION_COMPAT_MAP 中 openclawLarkVersion=2026.4.1
|
|
3819
3962
|
* 的 minOpenclawVersion,避免与映射表脱钩写死。仅用于区分升级方向
|
|
3820
3963
|
* (oc 低于下界 → 升 openclaw;高于上界 → 升 lark);该条目被移除时回退到已知值。
|
|
3821
3964
|
*/
|
|
3822
|
-
const FORK_LARK_PLUGIN_MIN_OC_VERSION = findEntry(
|
|
3965
|
+
const FORK_LARK_PLUGIN_MIN_OC_VERSION = findEntry("2026.4.1")?.minOpenclawVersion ?? "2026.3.28";
|
|
3823
3966
|
let _ocVersion = void 0;
|
|
3824
3967
|
function getOcVersion() {
|
|
3825
3968
|
if (_ocVersion === void 0) _ocVersion = readOpenclawRuntimeVersion();
|
|
@@ -3896,7 +4039,7 @@ let FeishuPluginLarkUpgradeRule = class FeishuPluginLarkUpgradeRule extends Diag
|
|
|
3896
4039
|
if (!cc) return { pass: true };
|
|
3897
4040
|
const { ocCur, recommendedOc, installed, isLegacy } = cc;
|
|
3898
4041
|
if (isForkPlugin(installed)) {
|
|
3899
|
-
if (installed.fullName !==
|
|
4042
|
+
if (installed.fullName !== "@lark-apaas/openclaw-lark") return { pass: true };
|
|
3900
4043
|
if (resolveForkUpgradeDirection(ocCur) !== "lark") return { pass: true };
|
|
3901
4044
|
return {
|
|
3902
4045
|
pass: false,
|
|
@@ -3983,7 +4126,7 @@ function describeCompatConstraint(entry, pluginVersion) {
|
|
|
3983
4126
|
* 'lark' → oc 高于区间上界(插件相对当前 openclaw 过旧),需升级飞书插件
|
|
3984
4127
|
*/
|
|
3985
4128
|
function resolveForkUpgradeDirection(ocCur) {
|
|
3986
|
-
if (effectiveCompatible(
|
|
4129
|
+
if (effectiveCompatible("2026.4.1", ocCur)) return null;
|
|
3987
4130
|
return compareCalVer(ocCur, FORK_LARK_PLUGIN_MIN_OC_VERSION) < 0 ? "openclaw" : "lark";
|
|
3988
4131
|
}
|
|
3989
4132
|
/**
|
|
@@ -3993,7 +4136,7 @@ function resolveForkUpgradeDirection(ocCur) {
|
|
|
3993
4136
|
* recommendedOc 可为 undefined(doctor 模式),此时不指定目标升级版本。
|
|
3994
4137
|
*/
|
|
3995
4138
|
function validateForkPlugin(installed, ocCur, recommendedOc) {
|
|
3996
|
-
if (installed.fullName !==
|
|
4139
|
+
if (installed.fullName !== "@lark-apaas/openclaw-lark") return { pass: true };
|
|
3997
4140
|
if (resolveForkUpgradeDirection(ocCur) !== "openclaw") return { pass: true };
|
|
3998
4141
|
const recommendation = recommendedOc ? `;将 openclaw 升级到 ${recommendedOc} 即可满足` : `;请升级 openclaw 至 ${FORK_LARK_PLUGIN_MIN_OC_VERSION} 或更高版本`;
|
|
3999
4142
|
return {
|
|
@@ -4024,7 +4167,7 @@ function detectInstalledPlugin(ctx) {
|
|
|
4024
4167
|
const allow = Array.isArray(allowRaw) ? allowRaw.filter((e) => typeof e === "string") : [];
|
|
4025
4168
|
const extDir = getExtensionsDir(ctx.configPath);
|
|
4026
4169
|
const installs = getNestedMap(ctx.config, "plugins", "installs");
|
|
4027
|
-
for (const name of [
|
|
4170
|
+
for (const name of [LARK_PLUGIN_NAME, ...LEGACY_SHORT_NAMES]) {
|
|
4028
4171
|
if (!allow.includes(name)) continue;
|
|
4029
4172
|
const pkgPath = node_path.default.join(extDir, name, "package.json");
|
|
4030
4173
|
if (!node_fs.default.existsSync(pkgPath)) continue;
|
|
@@ -4061,7 +4204,7 @@ function needsLarkUpgrade(ctx) {
|
|
|
4061
4204
|
if (!cc) return false;
|
|
4062
4205
|
const { ocCur, installed } = cc;
|
|
4063
4206
|
if (isForkPlugin(installed)) {
|
|
4064
|
-
if (installed.fullName ===
|
|
4207
|
+
if (installed.fullName === "@lark-apaas/openclaw-lark") return !effectiveCompatible(FORK_LARK_PLUGIN_PINNED_VERSION, ocCur);
|
|
4065
4208
|
return false;
|
|
4066
4209
|
}
|
|
4067
4210
|
return isLarkUpgradeNeededFromCC(cc);
|
|
@@ -4117,12 +4260,10 @@ CleanupInstallBackupDirsRule = __decorate([Rule({
|
|
|
4117
4260
|
})], CleanupInstallBackupDirsRule);
|
|
4118
4261
|
//#endregion
|
|
4119
4262
|
//#region src/rules/lark-cli-missing-for-installed-lark-plugin.ts
|
|
4120
|
-
|
|
4121
|
-
const FORK_PACKAGE_NAME = "@lark-apaas/openclaw-lark";
|
|
4263
|
+
/** 触发本规则自动安装 lark-cli 的特定 fork 版本(与对标的官方兼容版本无关)。 */
|
|
4122
4264
|
const TARGET_VERSION = "2026.4.4";
|
|
4123
|
-
const LARK_CLI_NAME$1 = "lark-cli";
|
|
4124
4265
|
function readInstalledLarkPlugin(ctx) {
|
|
4125
|
-
const pkgPath = node_path.default.join(getExtensionsDir(ctx.configPath),
|
|
4266
|
+
const pkgPath = node_path.default.join(getExtensionsDir(ctx.configPath), LARK_PLUGIN_NAME, "package.json");
|
|
4126
4267
|
if (!node_fs.default.existsSync(pkgPath)) return null;
|
|
4127
4268
|
let pkg = {};
|
|
4128
4269
|
try {
|
|
@@ -4135,14 +4276,14 @@ function readInstalledLarkPlugin(ctx) {
|
|
|
4135
4276
|
pkg = {};
|
|
4136
4277
|
}
|
|
4137
4278
|
const installs = getNestedMap(ctx.config, "plugins", "installs");
|
|
4138
|
-
const installEntry = installs ? asRecord(installs[
|
|
4279
|
+
const installEntry = installs ? asRecord(installs[LARK_PLUGIN_NAME]) : void 0;
|
|
4139
4280
|
return {
|
|
4140
4281
|
name: pkg.name ?? extractScopedNameFromSpec(installEntry?.spec),
|
|
4141
4282
|
version: pkg.version ?? (typeof installEntry?.version === "string" ? installEntry.version : void 0)
|
|
4142
4283
|
};
|
|
4143
4284
|
}
|
|
4144
4285
|
function isTargetForkPlugin(plugin) {
|
|
4145
|
-
return plugin?.name ===
|
|
4286
|
+
return plugin?.name === "@lark-apaas/openclaw-lark" && plugin.version === TARGET_VERSION;
|
|
4146
4287
|
}
|
|
4147
4288
|
function extractScopedNameFromSpec(spec) {
|
|
4148
4289
|
if (typeof spec !== "string") return void 0;
|
|
@@ -4151,7 +4292,7 @@ function extractScopedNameFromSpec(spec) {
|
|
|
4151
4292
|
}
|
|
4152
4293
|
function isLarkCliAvailable() {
|
|
4153
4294
|
try {
|
|
4154
|
-
return (0, node_child_process.spawnSync)(LARK_CLI_NAME
|
|
4295
|
+
return (0, node_child_process.spawnSync)(LARK_CLI_NAME, ["--version"], {
|
|
4155
4296
|
encoding: "utf-8",
|
|
4156
4297
|
timeout: 5e3,
|
|
4157
4298
|
stdio: [
|
|
@@ -4193,7 +4334,7 @@ let LarkCliMissingForInstalledLarkPluginRule = class LarkCliMissingForInstalledL
|
|
|
4193
4334
|
if (isLarkCliAvailable()) return { pass: true };
|
|
4194
4335
|
return {
|
|
4195
4336
|
pass: false,
|
|
4196
|
-
message: `${
|
|
4337
|
+
message: `${FORK_LARK_PLUGIN_FULL_NAME}@${TARGET_VERSION} 已安装,但 lark-cli 不可用;将执行一次 lark-cli 安装`
|
|
4197
4338
|
};
|
|
4198
4339
|
}
|
|
4199
4340
|
repair(ctx) {
|
|
@@ -5207,7 +5348,7 @@ async function installOpenclaw(openclawTag, ossFileMap, opts = {}) {
|
|
|
5207
5348
|
console.error(`[install-openclaw] done in ${Date.now() - t0}ms`);
|
|
5208
5349
|
}
|
|
5209
5350
|
//#endregion
|
|
5210
|
-
//#region src/lark-tools-update.ts
|
|
5351
|
+
//#region src/utils/lark-tools-update.ts
|
|
5211
5352
|
/**
|
|
5212
5353
|
* 共享的飞书插件备份 / 恢复 / `openclaw-lark-tools update` 调用。
|
|
5213
5354
|
*
|
|
@@ -5218,8 +5359,8 @@ async function installOpenclaw(openclawTag, ossFileMap, opts = {}) {
|
|
|
5218
5359
|
* `console.error`,独立调用方不传则静默。
|
|
5219
5360
|
*/
|
|
5220
5361
|
const NOOP_LOG = () => {};
|
|
5221
|
-
/** 升级前需备份的 extensions/
|
|
5222
|
-
const FEISHU_PLUGIN_DIRS =
|
|
5362
|
+
/** 升级前需备份的 extensions/ 下的插件目录(官方短名 + legacy)。 */
|
|
5363
|
+
const FEISHU_PLUGIN_DIRS = FEISHU_PLUGIN_DIR_NAMES;
|
|
5223
5364
|
/** 读取 package.json 的 version 字段,失败返回 null(仅用于日志) */
|
|
5224
5365
|
function readPkgVersion(pkgPath) {
|
|
5225
5366
|
try {
|
|
@@ -5339,6 +5480,8 @@ function runLarkToolsUpdate(opts) {
|
|
|
5339
5480
|
spawnError: r.error?.message
|
|
5340
5481
|
};
|
|
5341
5482
|
}
|
|
5483
|
+
//#endregion
|
|
5484
|
+
//#region src/install-extension.ts
|
|
5342
5485
|
async function installExtension(tag, ossFileMap, opts = {}) {
|
|
5343
5486
|
const homeBase = resolveHomeBase(opts.homeBase);
|
|
5344
5487
|
const hasAll = !!opts.all;
|
|
@@ -5361,18 +5504,18 @@ async function installExtension(tag, ossFileMap, opts = {}) {
|
|
|
5361
5504
|
}
|
|
5362
5505
|
console.error(`[install-extension] tag=${tag} targets=${targets.length}`);
|
|
5363
5506
|
const t0 = Date.now();
|
|
5364
|
-
const larkTarget = opts.skipConfigUpdate ? void 0 : targets.find((p) => p.name === LARK_PLUGIN_NAME
|
|
5507
|
+
const larkTarget = opts.skipConfigUpdate ? void 0 : targets.find((p) => p.name === LARK_PLUGIN_NAME);
|
|
5365
5508
|
if (larkTarget) {
|
|
5366
5509
|
if (await installLarkPluginWithCompatCheck({
|
|
5367
5510
|
tag,
|
|
5368
5511
|
pkg: larkTarget,
|
|
5369
5512
|
homeBase,
|
|
5370
|
-
configPath: opts.configPath ?? node_path.default.join(homeBase,
|
|
5513
|
+
configPath: opts.configPath ?? node_path.default.join(homeBase, DEFAULT_CONFIG_REL),
|
|
5371
5514
|
workspaceDir: node_path.default.join(homeBase, "workspace", "agent"),
|
|
5372
5515
|
ossFileMap,
|
|
5373
5516
|
downloadOpts: opts
|
|
5374
5517
|
})) {
|
|
5375
|
-
targets = targets.filter((p) => p.name !== LARK_PLUGIN_NAME
|
|
5518
|
+
targets = targets.filter((p) => p.name !== LARK_PLUGIN_NAME);
|
|
5376
5519
|
if (targets.length === 0) {
|
|
5377
5520
|
console.error(`[install-extension] done in ${Date.now() - t0}ms`);
|
|
5378
5521
|
return;
|
|
@@ -5391,13 +5534,11 @@ async function installExtension(tag, ossFileMap, opts = {}) {
|
|
|
5391
5534
|
installOne$1(pkg, tarball, homeBase);
|
|
5392
5535
|
console.error(`[install-extension] ${pkg.name}: installed`);
|
|
5393
5536
|
}
|
|
5394
|
-
if (!opts.skipConfigUpdate) updatePluginInstalls(opts.configPath ?? node_path.default.join(homeBase,
|
|
5537
|
+
if (!opts.skipConfigUpdate) updatePluginInstalls(opts.configPath ?? node_path.default.join(homeBase, DEFAULT_CONFIG_REL), targets);
|
|
5395
5538
|
else console.error(`[install-extension] skipConfigUpdate=true — not touching openclaw.json`);
|
|
5396
5539
|
console.error(`[install-extension] done ${targets.length}/${targets.length} in ${Date.now() - t0}ms`);
|
|
5397
5540
|
}
|
|
5398
|
-
const
|
|
5399
|
-
const MEM0_PLUGIN_NAME = "openclaw-mem0-plugin";
|
|
5400
|
-
const PLUGINS_TO_AUTO_ENABLE = ["openclaw-lark", "openclaw-extension-miaoda"];
|
|
5541
|
+
const PLUGINS_TO_AUTO_ENABLE = [LARK_PLUGIN_NAME, MIAODA_PLUGIN_NAME];
|
|
5401
5542
|
/**
|
|
5402
5543
|
* Merge each installed extension's installMetadata into openclaw.json's
|
|
5403
5544
|
* plugins.installs[<pkg.name>]. Atomic write via tmp + rename.
|
|
@@ -5429,14 +5570,14 @@ function updatePluginInstalls(configPath, installedPkgs) {
|
|
|
5429
5570
|
installs[pkg.name] = pkg.installMetadata;
|
|
5430
5571
|
updated++;
|
|
5431
5572
|
} else skipped++;
|
|
5432
|
-
if (installedPkgs.some((p) => p.name ===
|
|
5573
|
+
if (installedPkgs.some((p) => p.name === "openclaw-mem0-plugin")) {
|
|
5433
5574
|
const allowRaw = plugins.allow;
|
|
5434
5575
|
const allow = Array.isArray(allowRaw) ? allowRaw : [];
|
|
5435
|
-
if (!allow.filter((e) => typeof e === "string").includes(
|
|
5576
|
+
if (!allow.filter((e) => typeof e === "string").includes("openclaw-mem0-plugin")) allow.push(MEM0_PLUGIN_NAME);
|
|
5436
5577
|
plugins.allow = allow;
|
|
5437
5578
|
if (!plugins.entries || typeof plugins.entries !== "object" || Array.isArray(plugins.entries)) plugins.entries = {};
|
|
5438
5579
|
const entries = plugins.entries;
|
|
5439
|
-
if (!(
|
|
5580
|
+
if (!("openclaw-mem0-plugin" in entries)) entries[MEM0_PLUGIN_NAME] = { enabled: false };
|
|
5440
5581
|
}
|
|
5441
5582
|
for (const pkg of installedPkgs) {
|
|
5442
5583
|
if (!PLUGINS_TO_AUTO_ENABLE.includes(pkg.name)) continue;
|
|
@@ -5503,8 +5644,9 @@ async function installLarkPluginWithCompatCheck(opts) {
|
|
|
5503
5644
|
console.error("[install-extension] WARN: cannot read openclaw version — falling back to direct tarball install for openclaw-lark");
|
|
5504
5645
|
return false;
|
|
5505
5646
|
}
|
|
5506
|
-
const
|
|
5507
|
-
|
|
5647
|
+
const compatVersion = resolveCompatVersion(pkg.packageName, pkg.version);
|
|
5648
|
+
const compatible = compatVersion ? effectiveCompatible(compatVersion, ocCur) : true;
|
|
5649
|
+
console.error(`[install-extension] ${pkg.packageName ?? pkg.name}@${pkg.version ?? "unknown"} (compat-as ${compatVersion ?? "unknown"}) vs openclaw@${ocCur}: ${compatible}`);
|
|
5508
5650
|
if (compatible) return false;
|
|
5509
5651
|
console.error(`[install-extension] openclaw-lark@${pkg.version} incompatible with openclaw@${ocCur} — using openclaw-lark-tools update`);
|
|
5510
5652
|
const log = (msg) => console.error(`[install-extension] ${msg}`);
|
|
@@ -5553,7 +5695,9 @@ async function installLarkPluginWithCompatCheck(opts) {
|
|
|
5553
5695
|
recursive: true,
|
|
5554
5696
|
force: true
|
|
5555
5697
|
});
|
|
5556
|
-
} catch {
|
|
5698
|
+
} catch (e) {
|
|
5699
|
+
console.error(`[install-extension] WARN: failed to clean up backup dir ${backupDir}: ${e.message}`);
|
|
5700
|
+
}
|
|
5557
5701
|
}
|
|
5558
5702
|
}
|
|
5559
5703
|
/**
|
|
@@ -5562,7 +5706,7 @@ async function installLarkPluginWithCompatCheck(opts) {
|
|
|
5562
5706
|
*/
|
|
5563
5707
|
function validateLarkPluginInstall(opts) {
|
|
5564
5708
|
const { homeBase, configPath } = opts;
|
|
5565
|
-
const pkgJson = node_path.default.join(homeBase, "workspace", "agent", "extensions", LARK_PLUGIN_NAME
|
|
5709
|
+
const pkgJson = node_path.default.join(homeBase, "workspace", "agent", "extensions", LARK_PLUGIN_NAME, "package.json");
|
|
5566
5710
|
if (!node_fs.default.existsSync(pkgJson)) return {
|
|
5567
5711
|
ok: false,
|
|
5568
5712
|
error: `plugin directory missing: ${pkgJson}`
|
|
@@ -5572,9 +5716,9 @@ function validateLarkPluginInstall(opts) {
|
|
|
5572
5716
|
error: `openclaw.json not found at ${configPath}`
|
|
5573
5717
|
};
|
|
5574
5718
|
try {
|
|
5575
|
-
if (asRecord(getNestedMap(loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8")), "plugins", "entries")?.[
|
|
5719
|
+
if (asRecord(getNestedMap(loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8")), "plugins", "entries")?.["openclaw-lark"])?.enabled !== true) return {
|
|
5576
5720
|
ok: false,
|
|
5577
|
-
error: `plugins.entries["${LARK_PLUGIN_NAME
|
|
5721
|
+
error: `plugins.entries["${LARK_PLUGIN_NAME}"].enabled is not true`
|
|
5578
5722
|
};
|
|
5579
5723
|
} catch (e) {
|
|
5580
5724
|
return {
|
|
@@ -6864,34 +7008,6 @@ function reportError(params) {
|
|
|
6864
7008
|
}
|
|
6865
7009
|
//#endregion
|
|
6866
7010
|
//#region src/install-cli.ts
|
|
6867
|
-
const LARK_CLI_NAME = "lark-cli";
|
|
6868
|
-
const AGENT_SKILLS_NAME = "agent-skills";
|
|
6869
|
-
const WORKSPACE_AGENT_REL = "workspace/agent";
|
|
6870
|
-
const LARK_PLUGIN_NAME = "openclaw-lark";
|
|
6871
|
-
/**
|
|
6872
|
-
* openclaw-lark tools that overlap with lark-cli functionality.
|
|
6873
|
-
* Written to channels.feishu.tools.deny after lark-cli is installed so that
|
|
6874
|
-
* openclaw-lark's shouldRegisterTool() skips them, avoiding duplicate tools.
|
|
6875
|
-
* Supports trailing-* wildcards (handled by openclaw-lark's matchesAnyPattern).
|
|
6876
|
-
*
|
|
6877
|
-
* Kept: feishu_chat*, feishu_get_user, feishu_search_user, feishu_im_*,
|
|
6878
|
-
* feishu_oauth*, feishu_auth, feishu_diagnose, feishu_doctor
|
|
6879
|
-
*/
|
|
6880
|
-
const LARK_CLI_OVERLAP_TOOL_DENY = Object.freeze([
|
|
6881
|
-
"feishu_create_doc",
|
|
6882
|
-
"feishu_fetch_doc",
|
|
6883
|
-
"feishu_update_doc",
|
|
6884
|
-
"feishu_doc_comments",
|
|
6885
|
-
"feishu_doc_media",
|
|
6886
|
-
"feishu_drive_file",
|
|
6887
|
-
"feishu_wiki_space",
|
|
6888
|
-
"feishu_wiki_space_node",
|
|
6889
|
-
"feishu_search_doc_wiki",
|
|
6890
|
-
"feishu_bitable_*",
|
|
6891
|
-
"feishu_calendar_*",
|
|
6892
|
-
"feishu_task_*",
|
|
6893
|
-
"feishu_sheet"
|
|
6894
|
-
]);
|
|
6895
7011
|
async function installClis(tag, ossFileMap, opts) {
|
|
6896
7012
|
const homeBase = resolveHomeBase(opts.homeBase);
|
|
6897
7013
|
if (opts.names.length === 0) throw new Error("install-cli: must provide at least one --cli=<name>");
|
|
@@ -6931,7 +7047,7 @@ async function installClis(tag, ossFileMap, opts) {
|
|
|
6931
7047
|
linkBins(pkg, homeBase);
|
|
6932
7048
|
console.error(`[install-cli] ${pkg.name}: installed`);
|
|
6933
7049
|
}
|
|
6934
|
-
if (targets.some((p) => p.name ===
|
|
7050
|
+
if (targets.some((p) => p.name === "lark-cli")) {
|
|
6935
7051
|
const skillsInstallPath = await installAgentSkills(manifest, ossFileMap, opts, homeBase, opts.tmpRoot);
|
|
6936
7052
|
if (skillsInstallPath) linkAgentSkills(homeBase, skillsInstallPath);
|
|
6937
7053
|
const appIds = collectFeishuAppIds();
|
|
@@ -6993,7 +7109,7 @@ function linkBins(pkg, homeBase) {
|
|
|
6993
7109
|
}
|
|
6994
7110
|
}
|
|
6995
7111
|
async function installAgentSkills(manifest, ossFileMap, downloadOpts, homeBase, tmpRoot) {
|
|
6996
|
-
const pkg = manifest.packages.find((p) => p.role === "template" && p.name ===
|
|
7112
|
+
const pkg = manifest.packages.find((p) => p.role === "template" && p.name === "agent-skills");
|
|
6997
7113
|
if (!pkg) {
|
|
6998
7114
|
console.error(`[install-cli] installAgentSkills: ${AGENT_SKILLS_NAME} not found in manifest (tag may not bundle it) — skipping skill install`);
|
|
6999
7115
|
return null;
|
|
@@ -7151,18 +7267,14 @@ function disableLarkCliOverlapTools(configPath, homeBase) {
|
|
|
7151
7267
|
}
|
|
7152
7268
|
try {
|
|
7153
7269
|
const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
7154
|
-
if (!config
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
if (!feishu.tools || typeof feishu.tools !== "object") feishu.tools = {};
|
|
7159
|
-
const tools = feishu.tools;
|
|
7160
|
-
const existing = Array.isArray(tools.deny) ? tools.deny : [];
|
|
7161
|
-
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
|
+
}
|
|
7162
7274
|
const tmp = configPath + ".lark-cli-deny-tmp";
|
|
7163
7275
|
node_fs.default.writeFileSync(tmp, JSON.stringify(config, null, 2), "utf-8");
|
|
7164
7276
|
moveSafe(tmp, configPath);
|
|
7165
|
-
console.error(
|
|
7277
|
+
console.error("[install-cli] disableLarkCliOverlapTools: channels.feishu.tools.deny updated");
|
|
7166
7278
|
} catch (e) {
|
|
7167
7279
|
console.error(`[install-cli] WARN: disableLarkCliOverlapTools failed: ${e.message}`);
|
|
7168
7280
|
}
|
|
@@ -11102,7 +11214,7 @@ async function reportCliRun(opts) {
|
|
|
11102
11214
|
//#region src/help.ts
|
|
11103
11215
|
const BIN = "mclaw-diagnose";
|
|
11104
11216
|
function versionBanner() {
|
|
11105
|
-
return `v0.1.18-alpha.
|
|
11217
|
+
return `v0.1.18-alpha.3`;
|
|
11106
11218
|
}
|
|
11107
11219
|
const COMMANDS = [
|
|
11108
11220
|
{
|
|
@@ -11801,8 +11913,8 @@ function snapshotVersions(cwd, log) {
|
|
|
11801
11913
|
});
|
|
11802
11914
|
const ocRaw = (ocResult.stdout ?? "").trim() || (ocResult.stderr ?? "").trim();
|
|
11803
11915
|
const extDir = node_path.default.join(cwd, "extensions");
|
|
11804
|
-
const larkPkg = node_path.default.join(extDir,
|
|
11805
|
-
const feishuPkg = node_path.default.join(extDir,
|
|
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");
|
|
11806
11918
|
log(` version-check paths: ${larkPkg} [${node_fs.default.existsSync(larkPkg) ? "exists" : "missing"}]`);
|
|
11807
11919
|
log(` version-check paths: ${feishuPkg} [${node_fs.default.existsSync(feishuPkg) ? "exists" : "missing"}]`);
|
|
11808
11920
|
return {
|
package/package.json
CHANGED