@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.
- package/dist/index.cjs +258 -150
- 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);
|
|
@@ -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
|
|
3818
|
-
const
|
|
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 [
|
|
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
|
-
|
|
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),
|
|
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[
|
|
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 ===
|
|
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
|
|
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: `${
|
|
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/
|
|
5225
|
-
const FEISHU_PLUGIN_DIRS =
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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 ===
|
|
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(
|
|
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 (!(
|
|
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
|
|
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")?.[
|
|
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
|
|
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 ===
|
|
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 ===
|
|
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
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
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(
|
|
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.
|
|
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,
|
|
11809
|
-
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");
|
|
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