@lark-apaas/openclaw-scripts-diagnose-cli 0.1.17 → 0.1.18-alpha.1
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 +404 -115
- 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.
|
|
55
|
+
return "0.1.18-alpha.1";
|
|
56
56
|
}
|
|
57
57
|
//#endregion
|
|
58
58
|
//#region src/rule-engine/base.ts
|
|
@@ -3743,12 +3743,53 @@ function coerceCalVer(v) {
|
|
|
3743
3743
|
function compareCalVer(a, b) {
|
|
3744
3744
|
return semver.default.compare(coerceCalVer(a), coerceCalVer(b));
|
|
3745
3745
|
}
|
|
3746
|
-
/**
|
|
3747
|
-
function
|
|
3746
|
+
/** Look up an entry by exact plugin version; undefined if not in the table. */
|
|
3747
|
+
function findEntry(pluginVersion) {
|
|
3748
|
+
return VERSION_COMPAT_MAP.find((e) => e.openclawLarkVersion === pluginVersion);
|
|
3749
|
+
}
|
|
3750
|
+
/**
|
|
3751
|
+
* Infer the effective upper bound for a compat entry that has no explicit maxOpenclawVersion.
|
|
3752
|
+
*
|
|
3753
|
+
* Scans toward higher plugin versions (lower indices in the descending table) and returns
|
|
3754
|
+
* the first entry whose minOpenclawVersion differs from the current entry's.
|
|
3755
|
+
* That minOpenclawVersion becomes an EXCLUSIVE upper bound — the openclaw version that
|
|
3756
|
+
* starts a new incompatible range.
|
|
3757
|
+
*
|
|
3758
|
+
* Returns undefined for the topmost entry (no upper bound).
|
|
3759
|
+
*/
|
|
3760
|
+
function inferEffectiveMax(index) {
|
|
3761
|
+
const cur = VERSION_COMPAT_MAP[index];
|
|
3762
|
+
if (cur.maxOpenclawVersion != null) return {
|
|
3763
|
+
max: cur.maxOpenclawVersion,
|
|
3764
|
+
exclusive: false
|
|
3765
|
+
};
|
|
3766
|
+
for (let i = index - 1; i >= 0; i--) if (compareCalVer(VERSION_COMPAT_MAP[i].minOpenclawVersion, cur.minOpenclawVersion) > 0) return {
|
|
3767
|
+
max: VERSION_COMPAT_MAP[i].minOpenclawVersion,
|
|
3768
|
+
exclusive: true
|
|
3769
|
+
};
|
|
3770
|
+
}
|
|
3771
|
+
/**
|
|
3772
|
+
* Full version compatibility check that infers the effective maxOpenclawVersion for
|
|
3773
|
+
* entries that only define minOpenclawVersion.
|
|
3774
|
+
*
|
|
3775
|
+
* For entries with an explicit maxOpenclawVersion the upper bound is INCLUSIVE (existing
|
|
3776
|
+
* semantics). For entries without one the upper bound is EXCLUSIVE — it is derived from
|
|
3777
|
+
* the minOpenclawVersion of the next entry group that requires a higher openclaw version.
|
|
3778
|
+
*
|
|
3779
|
+
* Example: openclaw-lark@2026.4.10 (min=2026.4.27, no explicit max) gets an inferred
|
|
3780
|
+
* exclusive max of '2026.5.6', so openclaw@2026.5.7 is correctly detected as incompatible
|
|
3781
|
+
* whereas the old compatible() call would have returned true.
|
|
3782
|
+
*/
|
|
3783
|
+
function effectiveCompatible(pluginVersion, openclawVersion) {
|
|
3784
|
+
const index = VERSION_COMPAT_MAP.findIndex((e) => compareCalVer(e.openclawLarkVersion, pluginVersion) <= 0);
|
|
3785
|
+
if (index === -1) return false;
|
|
3786
|
+
const entry = VERSION_COMPAT_MAP[index];
|
|
3748
3787
|
const oc = coerceCalVer(openclawVersion);
|
|
3749
3788
|
if (semver.default.lt(oc, coerceCalVer(entry.minOpenclawVersion))) return false;
|
|
3750
|
-
|
|
3751
|
-
return true;
|
|
3789
|
+
const maxInfo = inferEffectiveMax(index);
|
|
3790
|
+
if (!maxInfo) return true;
|
|
3791
|
+
const max = coerceCalVer(maxInfo.max);
|
|
3792
|
+
return maxInfo.exclusive ? semver.default.lt(oc, max) : !semver.default.gt(oc, max);
|
|
3752
3793
|
}
|
|
3753
3794
|
/**
|
|
3754
3795
|
* Floor match: find the entry with the largest openclawLarkVersion that is
|
|
@@ -3766,10 +3807,19 @@ function findClosestEntry(pluginVersion) {
|
|
|
3766
3807
|
const PLUGIN_NAME$1 = "openclaw-lark";
|
|
3767
3808
|
const LEGACY_SHORT_NAMES = ["feishu-openclaw-plugin"];
|
|
3768
3809
|
const FORK_SCOPES = ["@lark-apaas"];
|
|
3769
|
-
/** 特化 fork
|
|
3810
|
+
/** 特化 fork 版全名:复用官方 effectiveCompatible,按对标版本取完整兼容区间 */
|
|
3770
3811
|
const FORK_LARK_PLUGIN_FULL_NAME = "@lark-apaas/openclaw-lark";
|
|
3771
|
-
/**
|
|
3772
|
-
|
|
3812
|
+
/**
|
|
3813
|
+
* fork 版对标的官方 openclaw-lark 版本。fork 自身版本不在 VERSION_COMPAT_MAP 内,
|
|
3814
|
+
* 兼容性按此对标版本经 effectiveCompatible 取完整区间(含推断上界)判定。
|
|
3815
|
+
*/
|
|
3816
|
+
const FORK_LARK_PLUGIN_PINNED_VERSION = "2026.4.1";
|
|
3817
|
+
/**
|
|
3818
|
+
* fork 版兼容区间的下界,取自 VERSION_COMPAT_MAP 中 openclawLarkVersion=2026.4.1
|
|
3819
|
+
* 的 minOpenclawVersion,避免与映射表脱钩写死。仅用于区分升级方向
|
|
3820
|
+
* (oc 低于下界 → 升 openclaw;高于上界 → 升 lark);该条目被移除时回退到已知值。
|
|
3821
|
+
*/
|
|
3822
|
+
const FORK_LARK_PLUGIN_MIN_OC_VERSION = findEntry(FORK_LARK_PLUGIN_PINNED_VERSION)?.minOpenclawVersion ?? "2026.3.28";
|
|
3773
3823
|
let _ocVersion = void 0;
|
|
3774
3824
|
function getOcVersion() {
|
|
3775
3825
|
if (_ocVersion === void 0) _ocVersion = readOpenclawRuntimeVersion();
|
|
@@ -3845,7 +3895,15 @@ let FeishuPluginLarkUpgradeRule = class FeishuPluginLarkUpgradeRule extends Diag
|
|
|
3845
3895
|
const cc = resolveCompatContext(ctx);
|
|
3846
3896
|
if (!cc) return { pass: true };
|
|
3847
3897
|
const { ocCur, recommendedOc, installed, isLegacy } = cc;
|
|
3848
|
-
if (isForkPlugin(installed))
|
|
3898
|
+
if (isForkPlugin(installed)) {
|
|
3899
|
+
if (installed.fullName !== FORK_LARK_PLUGIN_FULL_NAME) return { pass: true };
|
|
3900
|
+
if (resolveForkUpgradeDirection(ocCur) !== "lark") return { pass: true };
|
|
3901
|
+
return {
|
|
3902
|
+
pass: false,
|
|
3903
|
+
action: "upgrade_lark",
|
|
3904
|
+
message: `飞书插件 ${describePlugin(installed)}(fork 版,对标 openclaw-lark@${FORK_LARK_PLUGIN_PINNED_VERSION})与当前 openclaw@${ocCur} 不兼容;建议升级飞书插件至兼容版本`
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
3849
3907
|
if (!isLarkUpgradeNeededFromCC(cc)) return { pass: true };
|
|
3850
3908
|
const prefix = buildCompatPrefix(installed, ocCur, isLegacy);
|
|
3851
3909
|
if (!recommendedOc) return {
|
|
@@ -3890,13 +3948,12 @@ function isLegacyPlugin(p) {
|
|
|
3890
3948
|
return LEGACY_SHORT_NAMES.includes(p.allowName);
|
|
3891
3949
|
}
|
|
3892
3950
|
/**
|
|
3893
|
-
*
|
|
3894
|
-
*
|
|
3951
|
+
* 检查已装插件版本与当前 openclaw 版本是否兼容。
|
|
3952
|
+
* 使用 effectiveCompatible() 以正确推断无显式 maxOpenclawVersion 条目的上界。
|
|
3895
3953
|
*/
|
|
3896
3954
|
function isVersionCompatible(p, ocCur) {
|
|
3897
3955
|
if (!p.version) return false;
|
|
3898
|
-
|
|
3899
|
-
return entry != null && compatible(entry, ocCur);
|
|
3956
|
+
return effectiveCompatible(p.version, ocCur);
|
|
3900
3957
|
}
|
|
3901
3958
|
/**
|
|
3902
3959
|
* 豁免判断:若推荐的 openclaw 版本本身低于最近条目要求的最低版本,
|
|
@@ -3919,13 +3976,25 @@ function describeCompatConstraint(entry, pluginVersion) {
|
|
|
3919
3976
|
return `该插件版本要求 openclaw ≥ ${entry.minOpenclawVersion}`;
|
|
3920
3977
|
}
|
|
3921
3978
|
/**
|
|
3922
|
-
* @lark-apaas/openclaw-lark
|
|
3979
|
+
* fork @lark-apaas/openclaw-lark 的升级方向:复用官方 effectiveCompatible,按对标版本
|
|
3980
|
+
* FORK_LARK_PLUGIN_PINNED_VERSION (2026.4.1) 取完整兼容区间。
|
|
3981
|
+
* null → 兼容
|
|
3982
|
+
* 'openclaw' → oc 低于区间下界,需升级 openclaw
|
|
3983
|
+
* 'lark' → oc 高于区间上界(插件相对当前 openclaw 过旧),需升级飞书插件
|
|
3984
|
+
*/
|
|
3985
|
+
function resolveForkUpgradeDirection(ocCur) {
|
|
3986
|
+
if (effectiveCompatible(FORK_LARK_PLUGIN_PINNED_VERSION, ocCur)) return null;
|
|
3987
|
+
return compareCalVer(ocCur, FORK_LARK_PLUGIN_MIN_OC_VERSION) < 0 ? "openclaw" : "lark";
|
|
3988
|
+
}
|
|
3989
|
+
/**
|
|
3990
|
+
* @lark-apaas/openclaw-lark 复用官方 effectiveCompatible(按对标版本 2026.4.1 完整区间)。
|
|
3991
|
+
* 本函数(Rule 1)只负责 openclaw 方向(oc 低于下界);oc 高于上界由 Rule 2 报 upgrade_lark。
|
|
3923
3992
|
* 其他 @lark-apaas scope 的 fork 插件继续无条件 pass。
|
|
3924
|
-
* recommendedOc 可为 undefined(doctor
|
|
3993
|
+
* recommendedOc 可为 undefined(doctor 模式),此时不指定目标升级版本。
|
|
3925
3994
|
*/
|
|
3926
3995
|
function validateForkPlugin(installed, ocCur, recommendedOc) {
|
|
3927
3996
|
if (installed.fullName !== FORK_LARK_PLUGIN_FULL_NAME) return { pass: true };
|
|
3928
|
-
if (
|
|
3997
|
+
if (resolveForkUpgradeDirection(ocCur) !== "openclaw") return { pass: true };
|
|
3929
3998
|
const recommendation = recommendedOc ? `;将 openclaw 升级到 ${recommendedOc} 即可满足` : `;请升级 openclaw 至 ${FORK_LARK_PLUGIN_MIN_OC_VERSION} 或更高版本`;
|
|
3930
3999
|
return {
|
|
3931
4000
|
pass: false,
|
|
@@ -3992,7 +4061,7 @@ function needsLarkUpgrade(ctx) {
|
|
|
3992
4061
|
if (!cc) return false;
|
|
3993
4062
|
const { ocCur, installed } = cc;
|
|
3994
4063
|
if (isForkPlugin(installed)) {
|
|
3995
|
-
if (installed.fullName === FORK_LARK_PLUGIN_FULL_NAME) return
|
|
4064
|
+
if (installed.fullName === FORK_LARK_PLUGIN_FULL_NAME) return !effectiveCompatible(FORK_LARK_PLUGIN_PINNED_VERSION, ocCur);
|
|
3996
4065
|
return false;
|
|
3997
4066
|
}
|
|
3998
4067
|
return isLarkUpgradeNeededFromCC(cc);
|
|
@@ -5137,6 +5206,139 @@ async function installOpenclaw(openclawTag, ossFileMap, opts = {}) {
|
|
|
5137
5206
|
}
|
|
5138
5207
|
console.error(`[install-openclaw] done in ${Date.now() - t0}ms`);
|
|
5139
5208
|
}
|
|
5209
|
+
//#endregion
|
|
5210
|
+
//#region src/lark-tools-update.ts
|
|
5211
|
+
/**
|
|
5212
|
+
* 共享的飞书插件备份 / 恢复 / `openclaw-lark-tools update` 调用。
|
|
5213
|
+
*
|
|
5214
|
+
* upgrade-lark(场景修复流)与 install-extension(升级触发的兼容性预判路径)都需要
|
|
5215
|
+
* 「备份 → npx 适配升级 → 失败回滚」的同一套处理,此处统一封装,避免两份实现漂移。
|
|
5216
|
+
*
|
|
5217
|
+
* 日志通过可选 `log` 注入:upgrade-lark 传写日志文件的 Logger,install-extension 传
|
|
5218
|
+
* `console.error`,独立调用方不传则静默。
|
|
5219
|
+
*/
|
|
5220
|
+
const NOOP_LOG = () => {};
|
|
5221
|
+
/** 升级前需备份的 extensions/ 下的插件目录(含 legacy) */
|
|
5222
|
+
const FEISHU_PLUGIN_DIRS = ["openclaw-lark", "feishu-openclaw-plugin"];
|
|
5223
|
+
/** 读取 package.json 的 version 字段,失败返回 null(仅用于日志) */
|
|
5224
|
+
function readPkgVersion(pkgPath) {
|
|
5225
|
+
try {
|
|
5226
|
+
const pkg = JSON.parse(node_fs.default.readFileSync(pkgPath, "utf-8"));
|
|
5227
|
+
return typeof pkg.version === "string" ? pkg.version : null;
|
|
5228
|
+
} catch {
|
|
5229
|
+
return null;
|
|
5230
|
+
}
|
|
5231
|
+
}
|
|
5232
|
+
/**
|
|
5233
|
+
* 备份 openclaw.json + FEISHU_PLUGIN_DIRS 下存在的插件目录到 backupDir。
|
|
5234
|
+
* 只备份当前存在的文件;不存在的记日志后跳过(恢复时据此判断「升级前是否存在」)。
|
|
5235
|
+
*/
|
|
5236
|
+
function backupFeishuPlugins(opts) {
|
|
5237
|
+
const { workspaceDir, configPath, backupDir } = opts;
|
|
5238
|
+
const log = opts.log ?? NOOP_LOG;
|
|
5239
|
+
try {
|
|
5240
|
+
node_fs.default.mkdirSync(backupDir, { recursive: true });
|
|
5241
|
+
log(`backup dir: ${backupDir}`);
|
|
5242
|
+
if (node_fs.default.existsSync(configPath)) {
|
|
5243
|
+
const stat = node_fs.default.statSync(configPath);
|
|
5244
|
+
node_fs.default.copyFileSync(configPath, node_path.default.join(backupDir, "openclaw.json"));
|
|
5245
|
+
log(` backed up: openclaw.json (${stat.size} bytes)`);
|
|
5246
|
+
} else log(` skipped: openclaw.json (not found)`);
|
|
5247
|
+
node_fs.default.mkdirSync(node_path.default.join(backupDir, "extensions"), { recursive: true });
|
|
5248
|
+
const extSrc = node_path.default.join(workspaceDir, "extensions");
|
|
5249
|
+
for (const pluginDir of FEISHU_PLUGIN_DIRS) {
|
|
5250
|
+
const src = node_path.default.join(extSrc, pluginDir);
|
|
5251
|
+
if (node_fs.default.existsSync(src)) {
|
|
5252
|
+
const dst = node_path.default.join(backupDir, "extensions", pluginDir);
|
|
5253
|
+
node_fs.default.cpSync(src, dst, { recursive: true });
|
|
5254
|
+
const version = readPkgVersion(node_path.default.join(src, "package.json"));
|
|
5255
|
+
log(` backed up: extensions/${pluginDir}${version ? ` (version: ${version})` : ""}`);
|
|
5256
|
+
} else log(` skipped: extensions/${pluginDir} (not found)`);
|
|
5257
|
+
}
|
|
5258
|
+
return { ok: true };
|
|
5259
|
+
} catch (e) {
|
|
5260
|
+
const msg = `backup failed: ${e.message}`;
|
|
5261
|
+
log(`ERROR: ${msg}`);
|
|
5262
|
+
return {
|
|
5263
|
+
ok: false,
|
|
5264
|
+
error: msg
|
|
5265
|
+
};
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
/**
|
|
5269
|
+
* 回滚到备份快照:先删除当前 openclaw.json + 插件目录,再从备份恢复(仅恢复备份中存在的,
|
|
5270
|
+
* 即升级前确实存在的文件)。这样升级过程中新建的文件会被清理,真正回到 pre-state。
|
|
5271
|
+
*/
|
|
5272
|
+
function restoreFeishuPlugins(opts) {
|
|
5273
|
+
const { workspaceDir, configPath, backupDir } = opts;
|
|
5274
|
+
const log = opts.log ?? NOOP_LOG;
|
|
5275
|
+
try {
|
|
5276
|
+
if (node_fs.default.existsSync(configPath)) {
|
|
5277
|
+
node_fs.default.rmSync(configPath, { force: true });
|
|
5278
|
+
log(` deleted: openclaw.json`);
|
|
5279
|
+
}
|
|
5280
|
+
const extDst = node_path.default.join(workspaceDir, "extensions");
|
|
5281
|
+
for (const pluginDir of FEISHU_PLUGIN_DIRS) {
|
|
5282
|
+
const dst = node_path.default.join(extDst, pluginDir);
|
|
5283
|
+
if (node_fs.default.existsSync(dst)) {
|
|
5284
|
+
node_fs.default.rmSync(dst, {
|
|
5285
|
+
recursive: true,
|
|
5286
|
+
force: true
|
|
5287
|
+
});
|
|
5288
|
+
log(` deleted: extensions/${pluginDir}`);
|
|
5289
|
+
}
|
|
5290
|
+
}
|
|
5291
|
+
const configBackup = node_path.default.join(backupDir, "openclaw.json");
|
|
5292
|
+
if (node_fs.default.existsSync(configBackup)) {
|
|
5293
|
+
node_fs.default.copyFileSync(configBackup, configPath);
|
|
5294
|
+
log(` restored: openclaw.json`);
|
|
5295
|
+
} else log(` skipped restore: openclaw.json (not in backup — was not present before upgrade)`);
|
|
5296
|
+
for (const pluginDir of FEISHU_PLUGIN_DIRS) {
|
|
5297
|
+
const backupSrc = node_path.default.join(backupDir, "extensions", pluginDir);
|
|
5298
|
+
if (node_fs.default.existsSync(backupSrc)) {
|
|
5299
|
+
node_fs.default.cpSync(backupSrc, node_path.default.join(extDst, pluginDir), { recursive: true });
|
|
5300
|
+
log(` restored: extensions/${pluginDir}`);
|
|
5301
|
+
} else log(` skipped restore: extensions/${pluginDir} (not in backup — was not present before upgrade)`);
|
|
5302
|
+
}
|
|
5303
|
+
return true;
|
|
5304
|
+
} catch (e) {
|
|
5305
|
+
log(` restore error: ${e.message}`);
|
|
5306
|
+
return false;
|
|
5307
|
+
}
|
|
5308
|
+
}
|
|
5309
|
+
/**
|
|
5310
|
+
* 执行 `npx -y @larksuite/openclaw-lark-tools update`,自动按当前 openclaw 内核选择
|
|
5311
|
+
* 适配的 openclaw-lark 版本。stdout/stderr 已 trim;exitCode 缺失时归一为 1。
|
|
5312
|
+
*/
|
|
5313
|
+
function runLarkToolsUpdate(opts) {
|
|
5314
|
+
const log = opts.log ?? NOOP_LOG;
|
|
5315
|
+
const r = (0, node_child_process.spawnSync)("npx", [
|
|
5316
|
+
"-y",
|
|
5317
|
+
"@larksuite/openclaw-lark-tools",
|
|
5318
|
+
"update"
|
|
5319
|
+
], {
|
|
5320
|
+
cwd: opts.cwd,
|
|
5321
|
+
encoding: "utf-8",
|
|
5322
|
+
stdio: [
|
|
5323
|
+
"ignore",
|
|
5324
|
+
"pipe",
|
|
5325
|
+
"pipe"
|
|
5326
|
+
],
|
|
5327
|
+
timeout: opts.timeoutMs ?? 6e5
|
|
5328
|
+
});
|
|
5329
|
+
const stdout = r.stdout?.trim() ?? "";
|
|
5330
|
+
const stderr = r.stderr?.trim() ?? "";
|
|
5331
|
+
const exitCode = r.status ?? 1;
|
|
5332
|
+
if (stdout) log(`npx stdout:\n${stdout}`);
|
|
5333
|
+
if (stderr) log(`npx stderr:\n${stderr}`);
|
|
5334
|
+
log(`npx exit: ${exitCode}${r.error ? ` error: ${r.error.message}` : ""}`);
|
|
5335
|
+
return {
|
|
5336
|
+
exitCode,
|
|
5337
|
+
stdout,
|
|
5338
|
+
stderr,
|
|
5339
|
+
spawnError: r.error?.message
|
|
5340
|
+
};
|
|
5341
|
+
}
|
|
5140
5342
|
async function installExtension(tag, ossFileMap, opts = {}) {
|
|
5141
5343
|
const homeBase = resolveHomeBase(opts.homeBase);
|
|
5142
5344
|
const hasAll = !!opts.all;
|
|
@@ -5159,6 +5361,24 @@ async function installExtension(tag, ossFileMap, opts = {}) {
|
|
|
5159
5361
|
}
|
|
5160
5362
|
console.error(`[install-extension] tag=${tag} targets=${targets.length}`);
|
|
5161
5363
|
const t0 = Date.now();
|
|
5364
|
+
const larkTarget = opts.skipConfigUpdate ? void 0 : targets.find((p) => p.name === LARK_PLUGIN_NAME$1);
|
|
5365
|
+
if (larkTarget) {
|
|
5366
|
+
if (await installLarkPluginWithCompatCheck({
|
|
5367
|
+
tag,
|
|
5368
|
+
pkg: larkTarget,
|
|
5369
|
+
homeBase,
|
|
5370
|
+
configPath: opts.configPath ?? node_path.default.join(homeBase, "workspace/agent/openclaw.json"),
|
|
5371
|
+
workspaceDir: node_path.default.join(homeBase, "workspace", "agent"),
|
|
5372
|
+
ossFileMap,
|
|
5373
|
+
downloadOpts: opts
|
|
5374
|
+
})) {
|
|
5375
|
+
targets = targets.filter((p) => p.name !== LARK_PLUGIN_NAME$1);
|
|
5376
|
+
if (targets.length === 0) {
|
|
5377
|
+
console.error(`[install-extension] done in ${Date.now() - t0}ms`);
|
|
5378
|
+
return;
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
}
|
|
5162
5382
|
const tarballs = await Promise.all(targets.map(async (p) => {
|
|
5163
5383
|
const tb = await downloadWithCache(p, ossFileMap, opts);
|
|
5164
5384
|
console.error(`[install-extension] ${p.name}: downloaded`);
|
|
@@ -5175,6 +5395,7 @@ async function installExtension(tag, ossFileMap, opts = {}) {
|
|
|
5175
5395
|
else console.error(`[install-extension] skipConfigUpdate=true — not touching openclaw.json`);
|
|
5176
5396
|
console.error(`[install-extension] done ${targets.length}/${targets.length} in ${Date.now() - t0}ms`);
|
|
5177
5397
|
}
|
|
5398
|
+
const LARK_PLUGIN_NAME$1 = "openclaw-lark";
|
|
5178
5399
|
const MEM0_PLUGIN_NAME = "openclaw-mem0-plugin";
|
|
5179
5400
|
const PLUGINS_TO_AUTO_ENABLE = ["openclaw-lark", "openclaw-extension-miaoda"];
|
|
5180
5401
|
/**
|
|
@@ -5264,6 +5485,105 @@ function installOne$1(pkg, tarball, homeBase) {
|
|
|
5264
5485
|
force: true
|
|
5265
5486
|
});
|
|
5266
5487
|
}
|
|
5488
|
+
/**
|
|
5489
|
+
* Install openclaw-lark with a pre-flight compatibility check.
|
|
5490
|
+
*
|
|
5491
|
+
* When the recommended version in the manifest is incompatible with the currently
|
|
5492
|
+
* running openclaw, falls back to `npx @larksuite/openclaw-lark-tools update` which
|
|
5493
|
+
* selects the correct plugin version automatically.
|
|
5494
|
+
*
|
|
5495
|
+
* Returns true when the lark plugin was handled by this function (either path),
|
|
5496
|
+
* false when it should fall through to the standard tarball install (e.g. when the
|
|
5497
|
+
* current openclaw version cannot be read).
|
|
5498
|
+
*/
|
|
5499
|
+
async function installLarkPluginWithCompatCheck(opts) {
|
|
5500
|
+
const { tag, pkg, homeBase, configPath, workspaceDir } = opts;
|
|
5501
|
+
const ocCur = readOpenclawRuntimeVersion();
|
|
5502
|
+
if (!ocCur) {
|
|
5503
|
+
console.error("[install-extension] WARN: cannot read openclaw version — falling back to direct tarball install for openclaw-lark");
|
|
5504
|
+
return false;
|
|
5505
|
+
}
|
|
5506
|
+
const compatible = pkg.version ? effectiveCompatible(pkg.version, ocCur) : true;
|
|
5507
|
+
console.error(`[install-extension] openclaw-lark@${pkg.version ?? "unknown"} compat with openclaw@${ocCur}: ${compatible}`);
|
|
5508
|
+
if (compatible) return false;
|
|
5509
|
+
console.error(`[install-extension] openclaw-lark@${pkg.version} incompatible with openclaw@${ocCur} — using openclaw-lark-tools update`);
|
|
5510
|
+
const log = (msg) => console.error(`[install-extension] ${msg}`);
|
|
5511
|
+
const backupDir = node_path.default.join(homeBase, `.openclaw-lark-backup-${tag}`);
|
|
5512
|
+
const backupOk = backupFeishuPlugins({
|
|
5513
|
+
workspaceDir,
|
|
5514
|
+
configPath,
|
|
5515
|
+
backupDir,
|
|
5516
|
+
log
|
|
5517
|
+
}).ok;
|
|
5518
|
+
if (!backupOk) console.error("[install-extension] WARN: backup failed — proceeding without backup for openclaw-lark-tools path");
|
|
5519
|
+
try {
|
|
5520
|
+
const { exitCode } = runLarkToolsUpdate({
|
|
5521
|
+
cwd: workspaceDir,
|
|
5522
|
+
log
|
|
5523
|
+
});
|
|
5524
|
+
if (exitCode !== 0) {
|
|
5525
|
+
console.error("[install-extension] openclaw-lark-tools failed — attempting rollback");
|
|
5526
|
+
if (backupOk) restoreFeishuPlugins({
|
|
5527
|
+
workspaceDir,
|
|
5528
|
+
configPath,
|
|
5529
|
+
backupDir,
|
|
5530
|
+
log
|
|
5531
|
+
});
|
|
5532
|
+
throw new Error(`openclaw-lark-tools update exited with code ${exitCode}`);
|
|
5533
|
+
}
|
|
5534
|
+
const validation = validateLarkPluginInstall({
|
|
5535
|
+
homeBase,
|
|
5536
|
+
configPath
|
|
5537
|
+
});
|
|
5538
|
+
if (!validation.ok) {
|
|
5539
|
+
console.error(`[install-extension] post-install validation failed: ${validation.error} — rolling back`);
|
|
5540
|
+
if (backupOk) restoreFeishuPlugins({
|
|
5541
|
+
workspaceDir,
|
|
5542
|
+
configPath,
|
|
5543
|
+
backupDir,
|
|
5544
|
+
log
|
|
5545
|
+
});
|
|
5546
|
+
throw new Error(`openclaw-lark post-install validation failed: ${validation.error}`);
|
|
5547
|
+
}
|
|
5548
|
+
console.error("[install-extension] openclaw-lark-tools update succeeded");
|
|
5549
|
+
return true;
|
|
5550
|
+
} finally {
|
|
5551
|
+
if (backupOk && node_fs.default.existsSync(backupDir)) try {
|
|
5552
|
+
node_fs.default.rmSync(backupDir, {
|
|
5553
|
+
recursive: true,
|
|
5554
|
+
force: true
|
|
5555
|
+
});
|
|
5556
|
+
} catch {}
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5559
|
+
/**
|
|
5560
|
+
* Post-install validation for the tools-based upgrade path.
|
|
5561
|
+
* Checks: plugin directory exists + plugin is enabled in openclaw.json.
|
|
5562
|
+
*/
|
|
5563
|
+
function validateLarkPluginInstall(opts) {
|
|
5564
|
+
const { homeBase, configPath } = opts;
|
|
5565
|
+
const pkgJson = node_path.default.join(homeBase, "workspace", "agent", "extensions", LARK_PLUGIN_NAME$1, "package.json");
|
|
5566
|
+
if (!node_fs.default.existsSync(pkgJson)) return {
|
|
5567
|
+
ok: false,
|
|
5568
|
+
error: `plugin directory missing: ${pkgJson}`
|
|
5569
|
+
};
|
|
5570
|
+
if (!node_fs.default.existsSync(configPath)) return {
|
|
5571
|
+
ok: false,
|
|
5572
|
+
error: `openclaw.json not found at ${configPath}`
|
|
5573
|
+
};
|
|
5574
|
+
try {
|
|
5575
|
+
if (asRecord(getNestedMap(loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8")), "plugins", "entries")?.[LARK_PLUGIN_NAME$1])?.enabled !== true) return {
|
|
5576
|
+
ok: false,
|
|
5577
|
+
error: `plugins.entries["${LARK_PLUGIN_NAME$1}"].enabled is not true`
|
|
5578
|
+
};
|
|
5579
|
+
} catch (e) {
|
|
5580
|
+
return {
|
|
5581
|
+
ok: false,
|
|
5582
|
+
error: `config parse error: ${e.message}`
|
|
5583
|
+
};
|
|
5584
|
+
}
|
|
5585
|
+
return { ok: true };
|
|
5586
|
+
}
|
|
5267
5587
|
//#endregion
|
|
5268
5588
|
//#region ../../openclaw-slardar/lib/client.js
|
|
5269
5589
|
var import_index_cjs = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
@@ -6547,6 +6867,31 @@ function reportError(params) {
|
|
|
6547
6867
|
const LARK_CLI_NAME = "lark-cli";
|
|
6548
6868
|
const AGENT_SKILLS_NAME = "agent-skills";
|
|
6549
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
|
+
]);
|
|
6550
6895
|
async function installClis(tag, ossFileMap, opts) {
|
|
6551
6896
|
const homeBase = resolveHomeBase(opts.homeBase);
|
|
6552
6897
|
if (opts.names.length === 0) throw new Error("install-cli: must provide at least one --cli=<name>");
|
|
@@ -6611,6 +6956,7 @@ async function installClis(tag, ossFileMap, opts) {
|
|
|
6611
6956
|
}
|
|
6612
6957
|
});
|
|
6613
6958
|
}
|
|
6959
|
+
disableLarkCliOverlapTools(node_path.default.join(homeBase, WORKSPACE_AGENT_REL, "openclaw.json"), homeBase);
|
|
6614
6960
|
}
|
|
6615
6961
|
console.error(`[install-cli] done ${targets.length}/${targets.length} in ${Date.now() - t0}ms`);
|
|
6616
6962
|
}
|
|
@@ -6785,6 +7131,42 @@ function installOne(pkg, tarball, homeBase, tmpRoot) {
|
|
|
6785
7131
|
} catch {}
|
|
6786
7132
|
}
|
|
6787
7133
|
}
|
|
7134
|
+
/**
|
|
7135
|
+
* Write overlapping tool names to channels.feishu.tools.deny in openclaw.json so that
|
|
7136
|
+
* openclaw-lark's shouldRegisterTool() skips them when lark-cli is present.
|
|
7137
|
+
*
|
|
7138
|
+
* Only runs when openclaw-lark is installed (extensions/openclaw-lark/ exists).
|
|
7139
|
+
* Idempotent: merges with any existing deny entries.
|
|
7140
|
+
* Failures are non-fatal (logged as warnings).
|
|
7141
|
+
*/
|
|
7142
|
+
function disableLarkCliOverlapTools(configPath, homeBase) {
|
|
7143
|
+
const larkPluginDir = node_path.default.join(homeBase, WORKSPACE_AGENT_REL, "extensions", LARK_PLUGIN_NAME);
|
|
7144
|
+
if (!node_fs.default.existsSync(larkPluginDir)) {
|
|
7145
|
+
console.error(`[install-cli] disableLarkCliOverlapTools: ${LARK_PLUGIN_NAME} not installed — skipping`);
|
|
7146
|
+
return;
|
|
7147
|
+
}
|
|
7148
|
+
if (!node_fs.default.existsSync(configPath)) {
|
|
7149
|
+
console.error(`[install-cli] disableLarkCliOverlapTools: config not found at ${configPath} — skipping`);
|
|
7150
|
+
return;
|
|
7151
|
+
}
|
|
7152
|
+
try {
|
|
7153
|
+
const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
7154
|
+
if (!config.channels || typeof config.channels !== "object") config.channels = {};
|
|
7155
|
+
const channels = config.channels;
|
|
7156
|
+
if (!channels.feishu || typeof channels.feishu !== "object") channels.feishu = {};
|
|
7157
|
+
const feishu = channels.feishu;
|
|
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])];
|
|
7162
|
+
const tmp = configPath + ".lark-cli-deny-tmp";
|
|
7163
|
+
node_fs.default.writeFileSync(tmp, JSON.stringify(config, null, 2), "utf-8");
|
|
7164
|
+
moveSafe(tmp, configPath);
|
|
7165
|
+
console.error(`[install-cli] disableLarkCliOverlapTools: channels.feishu.tools.deny updated (${LARK_CLI_OVERLAP_TOOL_DENY.length} patterns)`);
|
|
7166
|
+
} catch (e) {
|
|
7167
|
+
console.error(`[install-cli] WARN: disableLarkCliOverlapTools failed: ${e.message}`);
|
|
7168
|
+
}
|
|
7169
|
+
}
|
|
6788
7170
|
//#endregion
|
|
6789
7171
|
//#region src/download-resource.ts
|
|
6790
7172
|
/**
|
|
@@ -10720,7 +11102,7 @@ async function reportCliRun(opts) {
|
|
|
10720
11102
|
//#region src/help.ts
|
|
10721
11103
|
const BIN = "mclaw-diagnose";
|
|
10722
11104
|
function versionBanner() {
|
|
10723
|
-
return `v0.1.
|
|
11105
|
+
return `v0.1.18-alpha.1`;
|
|
10724
11106
|
}
|
|
10725
11107
|
const COMMANDS = [
|
|
10726
11108
|
{
|
|
@@ -11406,83 +11788,6 @@ function buildUpgradeLarkResultSummary(result, checkOnly) {
|
|
|
11406
11788
|
}
|
|
11407
11789
|
//#endregion
|
|
11408
11790
|
//#region src/upgrade-lark.ts
|
|
11409
|
-
/** 升级前需备份的 extensions/ 下的插件目录 */
|
|
11410
|
-
const FEISHU_PLUGIN_DIRS = ["openclaw-lark", "feishu-openclaw-plugin"];
|
|
11411
|
-
function backupFiles(opts) {
|
|
11412
|
-
const { workspaceDir, configPath, backupDir, log } = opts;
|
|
11413
|
-
try {
|
|
11414
|
-
node_fs.default.mkdirSync(backupDir, { recursive: true });
|
|
11415
|
-
log(`backup dir: ${backupDir}`);
|
|
11416
|
-
if (node_fs.default.existsSync(configPath)) {
|
|
11417
|
-
const stat = node_fs.default.statSync(configPath);
|
|
11418
|
-
node_fs.default.copyFileSync(configPath, node_path.default.join(backupDir, "openclaw.json"));
|
|
11419
|
-
log(` backed up: openclaw.json (${stat.size} bytes)`);
|
|
11420
|
-
} else log(` skipped: openclaw.json (not found)`);
|
|
11421
|
-
node_fs.default.mkdirSync(node_path.default.join(backupDir, "extensions"), { recursive: true });
|
|
11422
|
-
const extSrc = node_path.default.join(workspaceDir, "extensions");
|
|
11423
|
-
for (const pluginDir of FEISHU_PLUGIN_DIRS) {
|
|
11424
|
-
const src = node_path.default.join(extSrc, pluginDir);
|
|
11425
|
-
if (node_fs.default.existsSync(src)) {
|
|
11426
|
-
const dst = node_path.default.join(backupDir, "extensions", pluginDir);
|
|
11427
|
-
node_fs.default.cpSync(src, dst, { recursive: true });
|
|
11428
|
-
const version = readPkgVersion(node_path.default.join(src, "package.json"));
|
|
11429
|
-
log(` backed up: extensions/${pluginDir}${version ? ` (version: ${version})` : ""}`);
|
|
11430
|
-
} else log(` skipped: extensions/${pluginDir} (not found)`);
|
|
11431
|
-
}
|
|
11432
|
-
return { ok: true };
|
|
11433
|
-
} catch (e) {
|
|
11434
|
-
const msg = `backup failed: ${e.message}`;
|
|
11435
|
-
log(`ERROR: ${msg}`);
|
|
11436
|
-
return {
|
|
11437
|
-
ok: false,
|
|
11438
|
-
error: msg
|
|
11439
|
-
};
|
|
11440
|
-
}
|
|
11441
|
-
}
|
|
11442
|
-
function restoreFiles(opts) {
|
|
11443
|
-
const { workspaceDir, configPath, backupDir, log } = opts;
|
|
11444
|
-
try {
|
|
11445
|
-
if (node_fs.default.existsSync(configPath)) {
|
|
11446
|
-
node_fs.default.rmSync(configPath, { force: true });
|
|
11447
|
-
log(` deleted: openclaw.json`);
|
|
11448
|
-
}
|
|
11449
|
-
const extDst = node_path.default.join(workspaceDir, "extensions");
|
|
11450
|
-
for (const pluginDir of FEISHU_PLUGIN_DIRS) {
|
|
11451
|
-
const dst = node_path.default.join(extDst, pluginDir);
|
|
11452
|
-
if (node_fs.default.existsSync(dst)) {
|
|
11453
|
-
node_fs.default.rmSync(dst, {
|
|
11454
|
-
recursive: true,
|
|
11455
|
-
force: true
|
|
11456
|
-
});
|
|
11457
|
-
log(` deleted: extensions/${pluginDir}`);
|
|
11458
|
-
}
|
|
11459
|
-
}
|
|
11460
|
-
const configBackup = node_path.default.join(backupDir, "openclaw.json");
|
|
11461
|
-
if (node_fs.default.existsSync(configBackup)) {
|
|
11462
|
-
node_fs.default.copyFileSync(configBackup, configPath);
|
|
11463
|
-
log(` restored: openclaw.json`);
|
|
11464
|
-
} else log(` skipped restore: openclaw.json (not in backup — was not present before upgrade)`);
|
|
11465
|
-
for (const pluginDir of FEISHU_PLUGIN_DIRS) {
|
|
11466
|
-
const backupSrc = node_path.default.join(backupDir, "extensions", pluginDir);
|
|
11467
|
-
if (node_fs.default.existsSync(backupSrc)) {
|
|
11468
|
-
node_fs.default.cpSync(backupSrc, node_path.default.join(extDst, pluginDir), { recursive: true });
|
|
11469
|
-
log(` restored: extensions/${pluginDir}`);
|
|
11470
|
-
} else log(` skipped restore: extensions/${pluginDir} (not in backup — was not present before upgrade)`);
|
|
11471
|
-
}
|
|
11472
|
-
return true;
|
|
11473
|
-
} catch (e) {
|
|
11474
|
-
log(` restore error: ${e.message}`);
|
|
11475
|
-
return false;
|
|
11476
|
-
}
|
|
11477
|
-
}
|
|
11478
|
-
function readPkgVersion(pkgPath) {
|
|
11479
|
-
try {
|
|
11480
|
-
const pkg = JSON.parse(node_fs.default.readFileSync(pkgPath, "utf-8"));
|
|
11481
|
-
return typeof pkg.version === "string" ? pkg.version : null;
|
|
11482
|
-
} catch {
|
|
11483
|
-
return null;
|
|
11484
|
-
}
|
|
11485
|
-
}
|
|
11486
11791
|
function snapshotVersions(cwd, log) {
|
|
11487
11792
|
const ocResult = (0, node_child_process.spawnSync)("openclaw", ["--version"], {
|
|
11488
11793
|
cwd,
|
|
@@ -11738,7 +12043,7 @@ function runUpgradeLark(opts) {
|
|
|
11738
12043
|
log("── [1/6] 文件备份 ────────────────────────────────────────");
|
|
11739
12044
|
log(`before-state: botCount=${countFeishuBots(configPath)}`);
|
|
11740
12045
|
const t_backupStart = Date.now();
|
|
11741
|
-
const backup =
|
|
12046
|
+
const backup = backupFeishuPlugins(fsOpts);
|
|
11742
12047
|
timing.backupMs = Date.now() - t_backupStart;
|
|
11743
12048
|
if (!backup.ok) {
|
|
11744
12049
|
log(`ERROR: ${backup.error}`);
|
|
@@ -11765,27 +12070,11 @@ function runUpgradeLark(opts) {
|
|
|
11765
12070
|
log("");
|
|
11766
12071
|
log("── [3/6] npx install (@larksuite/openclaw-lark-tools update) ──");
|
|
11767
12072
|
const t_npxStart = Date.now();
|
|
11768
|
-
const
|
|
11769
|
-
"-y",
|
|
11770
|
-
"@larksuite/openclaw-lark-tools",
|
|
11771
|
-
"update"
|
|
11772
|
-
], {
|
|
12073
|
+
const { exitCode: npxExitCode, stdout: npxStdout, stderr: npxStderr } = runLarkToolsUpdate({
|
|
11773
12074
|
cwd,
|
|
11774
|
-
|
|
11775
|
-
stdio: [
|
|
11776
|
-
"ignore",
|
|
11777
|
-
"pipe",
|
|
11778
|
-
"pipe"
|
|
11779
|
-
],
|
|
11780
|
-
timeout: 6e5
|
|
12075
|
+
log
|
|
11781
12076
|
});
|
|
11782
12077
|
timing.npxInstallMs = Date.now() - t_npxStart;
|
|
11783
|
-
const npxStdout = npxResult.stdout?.trim() ?? "";
|
|
11784
|
-
const npxStderr = npxResult.stderr?.trim() ?? "";
|
|
11785
|
-
const npxExitCode = npxResult.status ?? 1;
|
|
11786
|
-
if (npxStdout) log(`npx stdout:\n${npxStdout}`);
|
|
11787
|
-
if (npxStderr) log(`npx stderr:\n${npxStderr}`);
|
|
11788
|
-
log(`npx exit: ${npxExitCode}${npxResult.error ? ` error: ${npxResult.error.message}` : ""}`);
|
|
11789
12078
|
if (statusCheckDelayMs > 0) {
|
|
11790
12079
|
log("");
|
|
11791
12080
|
log(`── 等待 ${statusCheckDelayMs / 1e3}s(让 openclaw 服务完成重启) ─────────────`);
|
|
@@ -11794,7 +12083,7 @@ function runUpgradeLark(opts) {
|
|
|
11794
12083
|
}
|
|
11795
12084
|
const doRollback = (reason) => {
|
|
11796
12085
|
log(`ERROR: ${reason}`);
|
|
11797
|
-
const rollbackOk =
|
|
12086
|
+
const rollbackOk = restoreFeishuPlugins(fsOpts);
|
|
11798
12087
|
log(`rollback: ${rollbackOk ? "ok" : "FAILED"}`);
|
|
11799
12088
|
return finalReturn({
|
|
11800
12089
|
status: "failed",
|