@lark-apaas/openclaw-scripts-diagnose-cli 0.1.14-alpha.1 → 0.1.14-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 +227 -695
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -52,7 +52,7 @@ node_assert = __toESM(node_assert);
|
|
|
52
52
|
* it terse and parseable.
|
|
53
53
|
*/
|
|
54
54
|
function getVersion() {
|
|
55
|
-
return "0.1.14-alpha.
|
|
55
|
+
return "0.1.14-alpha.3";
|
|
56
56
|
}
|
|
57
57
|
//#endregion
|
|
58
58
|
//#region src/rule-engine/base.ts
|
|
@@ -3347,6 +3347,7 @@ function resolveUpgradeDirection(installed, ocCur, recommendedOc, isLegacy) {
|
|
|
3347
3347
|
/** 提取公共前置上下文;任何前置条件不满足时返回 null(规则 pass)。 */
|
|
3348
3348
|
function resolveCompatContext(ctx) {
|
|
3349
3349
|
const recommendedOc = ctx.vars.recommendedOpenclawTag;
|
|
3350
|
+
if (!recommendedOc) return null;
|
|
3350
3351
|
const ocCur = getOcVersion();
|
|
3351
3352
|
if (!ocCur) return null;
|
|
3352
3353
|
const installed = getInstalledPlugin(ctx);
|
|
@@ -3367,7 +3368,6 @@ let FeishuPluginOpenclawUpgradeRule = class FeishuPluginOpenclawUpgradeRule exte
|
|
|
3367
3368
|
validate(ctx) {
|
|
3368
3369
|
const cc = resolveCompatContext(ctx);
|
|
3369
3370
|
if (!cc) return { pass: true };
|
|
3370
|
-
if (!cc.recommendedOc) return { pass: true };
|
|
3371
3371
|
const { ocCur, recommendedOc, installed, isLegacy } = cc;
|
|
3372
3372
|
if (isForkPlugin(installed)) return validateForkPlugin(installed, ocCur, recommendedOc);
|
|
3373
3373
|
if (resolveUpgradeDirection(installed, ocCur, recommendedOc, isLegacy) !== "openclaw") return { pass: true };
|
|
@@ -3397,14 +3397,6 @@ let FeishuPluginLarkUpgradeRule = class FeishuPluginLarkUpgradeRule extends Diag
|
|
|
3397
3397
|
if (!cc) return { pass: true };
|
|
3398
3398
|
const { ocCur, recommendedOc, installed, isLegacy } = cc;
|
|
3399
3399
|
if (isForkPlugin(installed)) return { pass: true };
|
|
3400
|
-
if (!recommendedOc) {
|
|
3401
|
-
if (isLegacy || !isVersionCompatible(installed, ocCur)) return {
|
|
3402
|
-
pass: false,
|
|
3403
|
-
action: "upgrade_lark",
|
|
3404
|
-
message: `${buildCompatPrefix(installed, ocCur, isLegacy)};建议升级飞书插件至兼容版本`
|
|
3405
|
-
};
|
|
3406
|
-
return { pass: true };
|
|
3407
|
-
}
|
|
3408
3400
|
if (resolveUpgradeDirection(installed, ocCur, recommendedOc, isLegacy) !== "lark") return { pass: true };
|
|
3409
3401
|
return {
|
|
3410
3402
|
pass: false,
|
|
@@ -3658,6 +3650,119 @@ LarkCliMissingForInstalledLarkPluginRule = __decorate([Rule({
|
|
|
3658
3650
|
usesVars: ["recommendedOpenclawTag"]
|
|
3659
3651
|
})], LarkCliMissingForInstalledLarkPluginRule);
|
|
3660
3652
|
//#endregion
|
|
3653
|
+
//#region src/rules/feishu-bot-channel-config.ts
|
|
3654
|
+
/**
|
|
3655
|
+
* Ensures each bot account's channel config is correct:
|
|
3656
|
+
* 1. `allowFrom` contains its own `creatorOpenID` from larkApps
|
|
3657
|
+
* 2. `appSecret` is either the canonical provider-ref or matches larkApps plaintext
|
|
3658
|
+
*
|
|
3659
|
+
* Covers both multi-account (channels.feishu.accounts) and single-account
|
|
3660
|
+
* (channels.feishu.appId + allowFrom at top level) layouts.
|
|
3661
|
+
*/
|
|
3662
|
+
let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends DiagnoseRule {
|
|
3663
|
+
validate(ctx) {
|
|
3664
|
+
const larkApps = ctx.vars.larkApps;
|
|
3665
|
+
if (!larkApps || larkApps.length === 0) return { pass: true };
|
|
3666
|
+
const feishu = asRecord(getNestedMap(ctx.config, "channels", "feishu"));
|
|
3667
|
+
if (!feishu) return { pass: true };
|
|
3668
|
+
const issues = [];
|
|
3669
|
+
const accounts = asRecord(feishu.accounts);
|
|
3670
|
+
if (accounts) for (const [accountId, account] of Object.entries(accounts)) {
|
|
3671
|
+
const bot = asRecord(account);
|
|
3672
|
+
if (!bot) continue;
|
|
3673
|
+
const appId = bot.appId;
|
|
3674
|
+
if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
|
|
3675
|
+
const larkApp = larkApps.find((e) => e.larkAppID === appId);
|
|
3676
|
+
if (!larkApp) continue;
|
|
3677
|
+
this.checkBot(accountId, bot, larkApp, issues, false);
|
|
3678
|
+
}
|
|
3679
|
+
const singleAppId = feishu.appId;
|
|
3680
|
+
if (typeof singleAppId === "string" && singleAppId.startsWith("cli_") && !accounts) {
|
|
3681
|
+
const larkApp = larkApps.find((e) => e.larkAppID === singleAppId);
|
|
3682
|
+
if (larkApp) this.checkBot("feishu", feishu, larkApp, issues, true);
|
|
3683
|
+
}
|
|
3684
|
+
if (issues.length === 0) return { pass: true };
|
|
3685
|
+
return {
|
|
3686
|
+
pass: false,
|
|
3687
|
+
message: issues.join("; ")
|
|
3688
|
+
};
|
|
3689
|
+
}
|
|
3690
|
+
/** Check a single bot entry (either an account object or the feishu channel itself).
|
|
3691
|
+
* @param isSingleAccount true for single-account layout (expects provider-ref),
|
|
3692
|
+
* false for multi-account (expects plaintext from larkApps).
|
|
3693
|
+
*/
|
|
3694
|
+
checkBot(label, bot, larkApp, issues, isSingleAccount) {
|
|
3695
|
+
const creatorOpenID = larkApp.creatorOpenID;
|
|
3696
|
+
const allowFrom = Array.isArray(bot.allowFrom) ? bot.allowFrom : [];
|
|
3697
|
+
if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
|
|
3698
|
+
if (!allowFrom.includes(creatorOpenID)) issues.push(`${label} allowFrom missing creatorOpenID ${creatorOpenID.length > 8 ? creatorOpenID.slice(0, 4) + "***" + creatorOpenID.slice(-4) : "***"}`);
|
|
3699
|
+
} else if (allowFrom.length === 0) issues.push(`${label} allowFrom is empty (creatorOpenID unavailable, cannot auto-fix)`);
|
|
3700
|
+
const secret = bot.appSecret;
|
|
3701
|
+
if (isSingleAccount) if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
|
|
3702
|
+
if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) issues.push(`${label} appSecret is a provider-ref but not the canonical one`);
|
|
3703
|
+
} else issues.push(`${label} appSecret should be provider-ref, got ${typeof secret}`);
|
|
3704
|
+
else if (typeof secret === "string") {
|
|
3705
|
+
if (secret !== larkApp.appSecret) issues.push(`${label} appSecret plaintext mismatch`);
|
|
3706
|
+
} else issues.push(`${label} appSecret should be plaintext, got ${typeof secret}`);
|
|
3707
|
+
}
|
|
3708
|
+
repair(ctx) {
|
|
3709
|
+
const larkApps = ctx.vars.larkApps;
|
|
3710
|
+
if (!larkApps || larkApps.length === 0) return;
|
|
3711
|
+
const feishu = asRecord(getNestedMap(ctx.config, "channels", "feishu"));
|
|
3712
|
+
if (!feishu) return;
|
|
3713
|
+
const accounts = asRecord(feishu.accounts);
|
|
3714
|
+
if (accounts) for (const [, account] of Object.entries(accounts)) {
|
|
3715
|
+
const bot = asRecord(account);
|
|
3716
|
+
if (!bot) continue;
|
|
3717
|
+
const appId = bot.appId;
|
|
3718
|
+
if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
|
|
3719
|
+
const larkApp = larkApps.find((e) => e.larkAppID === appId);
|
|
3720
|
+
if (!larkApp) continue;
|
|
3721
|
+
this.fixBot(bot, larkApp, false);
|
|
3722
|
+
}
|
|
3723
|
+
const singleAppId = feishu.appId;
|
|
3724
|
+
if (typeof singleAppId === "string" && singleAppId.startsWith("cli_") && !accounts) {
|
|
3725
|
+
const larkApp = larkApps.find((e) => e.larkAppID === singleAppId);
|
|
3726
|
+
if (larkApp) this.fixBot(feishu, larkApp, true);
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
/** Fix a single bot entry in-place.
|
|
3730
|
+
* @param isSingleAccount true for single-account layout (use provider-ref),
|
|
3731
|
+
* false for multi-account (use plaintext from larkApps).
|
|
3732
|
+
*/
|
|
3733
|
+
fixBot(bot, larkApp, isSingleAccount) {
|
|
3734
|
+
const creatorOpenID = larkApp.creatorOpenID;
|
|
3735
|
+
if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
|
|
3736
|
+
const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
|
|
3737
|
+
if (!allowFrom.includes(creatorOpenID)) {
|
|
3738
|
+
allowFrom.push(creatorOpenID);
|
|
3739
|
+
bot.allowFrom = allowFrom;
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
const secret = bot.appSecret;
|
|
3743
|
+
let needsFix = false;
|
|
3744
|
+
if (isSingleAccount) if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
|
|
3745
|
+
if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
|
|
3746
|
+
} else needsFix = true;
|
|
3747
|
+
else if (typeof secret === "string") {
|
|
3748
|
+
if (secret !== larkApp.appSecret) needsFix = true;
|
|
3749
|
+
} else needsFix = true;
|
|
3750
|
+
if (needsFix) bot.appSecret = isSingleAccount ? { ...DEFAULT_FEISHU_APP_SECRET } : larkApp.appSecret;
|
|
3751
|
+
}
|
|
3752
|
+
};
|
|
3753
|
+
FeishuBotChannelConfigRule = __decorate([Rule({
|
|
3754
|
+
key: "feishu_bot_channel_config",
|
|
3755
|
+
description: "确保飞书配置中 bot 账号的 allowFrom 包含其创建者 openID 且 appSecret 值正确",
|
|
3756
|
+
dependsOn: [
|
|
3757
|
+
"config_syntax_check",
|
|
3758
|
+
"feishu_default_account",
|
|
3759
|
+
"feishu_bot_id"
|
|
3760
|
+
],
|
|
3761
|
+
repairMode: "standard",
|
|
3762
|
+
usesVars: ["larkApps"],
|
|
3763
|
+
level: "critical"
|
|
3764
|
+
})], FeishuBotChannelConfigRule);
|
|
3765
|
+
//#endregion
|
|
3661
3766
|
//#region src/check.ts
|
|
3662
3767
|
/** Telemetry-aware entry: returns both the legacy CheckResult (for stdout)
|
|
3663
3768
|
* AND a DoctorReport-shape payload (for `openclaw.report_cli_run`). The
|
|
@@ -4121,9 +4226,6 @@ const PROVIDER_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-provider-
|
|
|
4121
4226
|
const SECRETS_FILE_PATH = "/home/gem/workspace/.force/openclaw/miaoda-openclaw-secrets.json";
|
|
4122
4227
|
/** Absolute path to the openclaw config JSON. */
|
|
4123
4228
|
const CONFIG_PATH = `${WORKSPACE_DIR}/openclaw.json`;
|
|
4124
|
-
function upgradeLarkLogFile(runId) {
|
|
4125
|
-
return `${DIAGNOSE_DIR}/upgrade-lark-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-")}-${runId.slice(0, 8)}.log`;
|
|
4126
|
-
}
|
|
4127
4229
|
//#endregion
|
|
4128
4230
|
//#region src/run-log.ts
|
|
4129
4231
|
let currentRunContext;
|
|
@@ -4260,9 +4362,10 @@ function makeLogger(logFile) {
|
|
|
4260
4362
|
/**
|
|
4261
4363
|
* Start an async reset task: spawn a detached child process and return the taskId.
|
|
4262
4364
|
*
|
|
4263
|
-
* The child process runs: node cli.js reset --worker --task-id=xxx
|
|
4365
|
+
* The child process runs: node cli.js reset --worker --task-id=xxx
|
|
4366
|
+
* The worker fetches ctx from innerApi itself — no --ctx passthrough.
|
|
4264
4367
|
*/
|
|
4265
|
-
function startAsyncReset(
|
|
4368
|
+
function startAsyncReset() {
|
|
4266
4369
|
const taskId = (0, node_crypto.randomUUID)();
|
|
4267
4370
|
const resultFile = resetResultFile(taskId);
|
|
4268
4371
|
const log = makeLogger(resetLogFile(taskId));
|
|
@@ -4286,8 +4389,7 @@ function startAsyncReset(ctxBase64) {
|
|
|
4286
4389
|
process.argv[1],
|
|
4287
4390
|
"reset",
|
|
4288
4391
|
"--worker",
|
|
4289
|
-
`--task-id=${taskId}
|
|
4290
|
-
`--ctx=${ctxBase64}`
|
|
4392
|
+
`--task-id=${taskId}`
|
|
4291
4393
|
], {
|
|
4292
4394
|
detached: true,
|
|
4293
4395
|
stdio: "ignore",
|
|
@@ -6236,23 +6338,6 @@ function reportError(params) {
|
|
|
6236
6338
|
} catch {}
|
|
6237
6339
|
}
|
|
6238
6340
|
//#endregion
|
|
6239
|
-
//#region ../../openclaw-slardar/lib/report-log.js
|
|
6240
|
-
function reportLog(params) {
|
|
6241
|
-
try {
|
|
6242
|
-
const extra = mergeLogExtra(buildTelemetryFields(params), {
|
|
6243
|
-
event: params.event,
|
|
6244
|
-
phase: params.phase,
|
|
6245
|
-
status: params.status,
|
|
6246
|
-
...params.extra
|
|
6247
|
-
});
|
|
6248
|
-
Slardar.sendLog?.({
|
|
6249
|
-
content: params.message,
|
|
6250
|
-
level: params.level ?? "info",
|
|
6251
|
-
extra
|
|
6252
|
-
});
|
|
6253
|
-
} catch {}
|
|
6254
|
-
}
|
|
6255
|
-
//#endregion
|
|
6256
6341
|
//#region src/install-cli.ts
|
|
6257
6342
|
const LARK_CLI_NAME = "lark-cli";
|
|
6258
6343
|
const AGENT_SKILLS_NAME = "agent-skills";
|
|
@@ -6818,6 +6903,60 @@ function mergeCoreBackupAndOrigins(configPath, vars, resetData, log) {
|
|
|
6818
6903
|
log(`allowedOrigins: added ${added.length} (${JSON.stringify(added)}), total now ${mergedOrigins.length}`);
|
|
6819
6904
|
}
|
|
6820
6905
|
/**
|
|
6906
|
+
* Fix bot account allowFrom and appSecret using larkApps from innerApi.
|
|
6907
|
+
*
|
|
6908
|
+
* For each bot account (key starts with `bot-cli_`):
|
|
6909
|
+
* - allowFrom must contain the bot's own creatorOpenID from larkApps
|
|
6910
|
+
* - appSecret must be either the canonical provider-ref or match larkApps plaintext
|
|
6911
|
+
*
|
|
6912
|
+
* Runs after mergeCoreBackupAndOrigins so it operates on the final config state.
|
|
6913
|
+
*/
|
|
6914
|
+
function fixBotChannelConfig(configPath, larkApps, log) {
|
|
6915
|
+
if (!larkApps || larkApps.length === 0) {
|
|
6916
|
+
log("no larkApps data, skip bot channel config fix");
|
|
6917
|
+
return;
|
|
6918
|
+
}
|
|
6919
|
+
const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
|
|
6920
|
+
const accounts = asRecord(getNestedMap(config, "channels", "feishu")?.accounts);
|
|
6921
|
+
if (!accounts) {
|
|
6922
|
+
log("no feishu accounts in config, skip bot channel config fix");
|
|
6923
|
+
return;
|
|
6924
|
+
}
|
|
6925
|
+
let fixCount = 0;
|
|
6926
|
+
for (const [, account] of Object.entries(accounts)) {
|
|
6927
|
+
const bot = asRecord(account);
|
|
6928
|
+
if (!bot) continue;
|
|
6929
|
+
const appId = bot.appId;
|
|
6930
|
+
if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
|
|
6931
|
+
const larkApp = larkApps.find((e) => e.larkAppID === appId);
|
|
6932
|
+
if (!larkApp) continue;
|
|
6933
|
+
const creatorOpenID = larkApp.creatorOpenID;
|
|
6934
|
+
if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
|
|
6935
|
+
const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
|
|
6936
|
+
if (!allowFrom.includes(creatorOpenID)) {
|
|
6937
|
+
allowFrom.push(creatorOpenID);
|
|
6938
|
+
bot.allowFrom = allowFrom;
|
|
6939
|
+
fixCount++;
|
|
6940
|
+
}
|
|
6941
|
+
}
|
|
6942
|
+
const secret = bot.appSecret;
|
|
6943
|
+
let needsFix = false;
|
|
6944
|
+
if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
|
|
6945
|
+
if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
|
|
6946
|
+
} else if (typeof secret === "string") {
|
|
6947
|
+
if (secret !== larkApp.appSecret) needsFix = true;
|
|
6948
|
+
} else needsFix = true;
|
|
6949
|
+
if (needsFix) {
|
|
6950
|
+
bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
|
|
6951
|
+
fixCount++;
|
|
6952
|
+
}
|
|
6953
|
+
}
|
|
6954
|
+
if (fixCount > 0) {
|
|
6955
|
+
node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
6956
|
+
log(`fixed ${fixCount} bot channel config issue(s) (allowFrom/appSecret)`);
|
|
6957
|
+
} else log("bot channel config ok, no fixes needed");
|
|
6958
|
+
}
|
|
6959
|
+
/**
|
|
6821
6960
|
* Step 7: Verify startup scripts landed in configDir/scripts/.
|
|
6822
6961
|
*
|
|
6823
6962
|
* Scripts are extracted directly to configDir/scripts/ during stageTemplate —
|
|
@@ -6962,6 +7101,7 @@ async function runReset(input, taskId, resultFile) {
|
|
|
6962
7101
|
await step5InstallOpenclaw(openclawTag, ossFileMap, log);
|
|
6963
7102
|
step(6);
|
|
6964
7103
|
mergeCoreBackupAndOrigins(configPath, vars, resetData, log);
|
|
7104
|
+
fixBotChannelConfig(configPath, vars.larkApps, log);
|
|
6965
7105
|
step(7);
|
|
6966
7106
|
verifyStartupScripts(configDir, log);
|
|
6967
7107
|
step(8);
|
|
@@ -7760,7 +7900,8 @@ function normalizeCtx(raw) {
|
|
|
7760
7900
|
reset: {
|
|
7761
7901
|
templateVars: r.reset.templateVars ?? {},
|
|
7762
7902
|
coreBackup: r.reset.coreBackup
|
|
7763
|
-
}
|
|
7903
|
+
},
|
|
7904
|
+
larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
|
|
7764
7905
|
};
|
|
7765
7906
|
}
|
|
7766
7907
|
const vars = r.vars ?? {};
|
|
@@ -7785,7 +7926,8 @@ function normalizeCtx(raw) {
|
|
|
7785
7926
|
reset: {
|
|
7786
7927
|
templateVars: resetData.templateVars ?? {},
|
|
7787
7928
|
coreBackup: resetData.coreBackup
|
|
7788
|
-
}
|
|
7929
|
+
},
|
|
7930
|
+
larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
|
|
7789
7931
|
};
|
|
7790
7932
|
}
|
|
7791
7933
|
function fillApp(src) {
|
|
@@ -7850,7 +7992,8 @@ function buildCheckInput(raw, configPathOverride) {
|
|
|
7850
7992
|
providerFilePath: PROVIDER_FILE_PATH,
|
|
7851
7993
|
secretsFilePath: SECRETS_FILE_PATH,
|
|
7852
7994
|
templateVars: ctx.app.templateVars,
|
|
7853
|
-
recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
|
|
7995
|
+
recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
|
|
7996
|
+
larkApps: ctx.larkApps
|
|
7854
7997
|
},
|
|
7855
7998
|
templateVars: ctx.app.templateVars
|
|
7856
7999
|
};
|
|
@@ -7882,7 +8025,8 @@ function buildRepairInput(raw, configPathOverride) {
|
|
|
7882
8025
|
providerFilePath: PROVIDER_FILE_PATH,
|
|
7883
8026
|
secretsFilePath: SECRETS_FILE_PATH,
|
|
7884
8027
|
templateVars: ctx.app.templateVars,
|
|
7885
|
-
recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
|
|
8028
|
+
recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
|
|
8029
|
+
larkApps: ctx.larkApps
|
|
7886
8030
|
},
|
|
7887
8031
|
repairData: {
|
|
7888
8032
|
secretsContent: ctx.secrets.secretsContent,
|
|
@@ -7918,7 +8062,8 @@ function buildResetInput(raw, configPathOverride) {
|
|
|
7918
8062
|
providerFilePath: PROVIDER_FILE_PATH,
|
|
7919
8063
|
secretsFilePath: SECRETS_FILE_PATH,
|
|
7920
8064
|
templateVars: ctx.app.templateVars,
|
|
7921
|
-
recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
|
|
8065
|
+
recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
|
|
8066
|
+
larkApps: ctx.larkApps
|
|
7922
8067
|
},
|
|
7923
8068
|
resetData: {
|
|
7924
8069
|
templateVars: ctx.reset.templateVars,
|
|
@@ -10149,109 +10294,6 @@ function finalize(results, aborted) {
|
|
|
10149
10294
|
};
|
|
10150
10295
|
}
|
|
10151
10296
|
//#endregion
|
|
10152
|
-
//#region src/channels-probe.ts
|
|
10153
|
-
const CHANNEL_LINE_RE = /^-\s+Feishu\s+([^:]+):\s+(.+)$/;
|
|
10154
|
-
/**
|
|
10155
|
-
* Port of Python `_account_is_working` from the feishu-channel-success-rate skill.
|
|
10156
|
-
*
|
|
10157
|
-
* Strips colon-prefixed key:value bits (dm:, bot:, in:, out:, token:, allow:,
|
|
10158
|
-
* intents:, groups:, health:) and evaluates the canonical health formula.
|
|
10159
|
-
*/
|
|
10160
|
-
function accountIsWorking(bits) {
|
|
10161
|
-
const bitTokens = /* @__PURE__ */ new Set();
|
|
10162
|
-
let hasError = false;
|
|
10163
|
-
let hasProbeFailed = false;
|
|
10164
|
-
for (const raw of bits) {
|
|
10165
|
-
const b = raw.trim();
|
|
10166
|
-
if (!b) continue;
|
|
10167
|
-
if (b.startsWith("error:")) {
|
|
10168
|
-
hasError = true;
|
|
10169
|
-
continue;
|
|
10170
|
-
}
|
|
10171
|
-
if (b === "probe failed") {
|
|
10172
|
-
hasProbeFailed = true;
|
|
10173
|
-
continue;
|
|
10174
|
-
}
|
|
10175
|
-
bitTokens.add(b.split(":")[0]);
|
|
10176
|
-
}
|
|
10177
|
-
if (!bitTokens.has("enabled") || !bitTokens.has("configured")) return false;
|
|
10178
|
-
if (bitTokens.has("works")) return true;
|
|
10179
|
-
if (bitTokens.has("running") && !hasError && !hasProbeFailed) return true;
|
|
10180
|
-
return false;
|
|
10181
|
-
}
|
|
10182
|
-
/**
|
|
10183
|
-
* Parse the raw stdout of `openclaw channels status --probe`.
|
|
10184
|
-
* Port of Python `extract_channels_probe` from the feishu-channel-success-rate skill.
|
|
10185
|
-
*/
|
|
10186
|
-
function parseChannelsProbeOutput(text) {
|
|
10187
|
-
const gatewayReachable = text.includes("Gateway reachable");
|
|
10188
|
-
const accounts = [];
|
|
10189
|
-
let anyAccountWorking = false;
|
|
10190
|
-
for (const line of text.split("\n")) {
|
|
10191
|
-
const m = CHANNEL_LINE_RE.exec(line.trim());
|
|
10192
|
-
if (!m) continue;
|
|
10193
|
-
const [, acct, rest] = m;
|
|
10194
|
-
const bits = rest.split(",").map((b) => b.trim());
|
|
10195
|
-
const isWorking = accountIsWorking(bits);
|
|
10196
|
-
if (isWorking) anyAccountWorking = true;
|
|
10197
|
-
accounts.push({
|
|
10198
|
-
id: acct.trim(),
|
|
10199
|
-
bits,
|
|
10200
|
-
isWorking,
|
|
10201
|
-
raw: line.trim()
|
|
10202
|
-
});
|
|
10203
|
-
}
|
|
10204
|
-
return {
|
|
10205
|
-
gatewayReachable,
|
|
10206
|
-
accounts,
|
|
10207
|
-
anyAccountWorking
|
|
10208
|
-
};
|
|
10209
|
-
}
|
|
10210
|
-
/**
|
|
10211
|
-
* Run `openclaw channels status --probe` and return a structured result.
|
|
10212
|
-
*
|
|
10213
|
-
* The command may exit non-zero when some bot accounts fail their probe — that
|
|
10214
|
-
* is still useful output. We therefore try to parse stdout even when the
|
|
10215
|
-
* process exits with a non-zero code, falling back to an unavailable result
|
|
10216
|
-
* only when there is genuinely no output to parse.
|
|
10217
|
-
*
|
|
10218
|
-
* @param timeoutMs Maximum wait time. Default is 60 s because v2026.4.x
|
|
10219
|
-
* lacks a per-request HTTP timeout and can block indefinitely.
|
|
10220
|
-
*/
|
|
10221
|
-
function runChannelsProbe(timeoutMs = 6e4) {
|
|
10222
|
-
let stdout = "";
|
|
10223
|
-
let execError;
|
|
10224
|
-
try {
|
|
10225
|
-
stdout = (0, node_child_process.execSync)("openclaw channels status --probe", {
|
|
10226
|
-
encoding: "utf-8",
|
|
10227
|
-
timeout: timeoutMs,
|
|
10228
|
-
stdio: [
|
|
10229
|
-
"ignore",
|
|
10230
|
-
"pipe",
|
|
10231
|
-
"pipe"
|
|
10232
|
-
]
|
|
10233
|
-
});
|
|
10234
|
-
} catch (e) {
|
|
10235
|
-
const err = e;
|
|
10236
|
-
stdout = err.stdout ?? "";
|
|
10237
|
-
execError = err.message;
|
|
10238
|
-
const stderrRaw = err.stderr;
|
|
10239
|
-
const stderr = (typeof stderrRaw === "string" ? stderrRaw : stderrRaw?.toString("utf-8") ?? "").trim();
|
|
10240
|
-
if (stderr) console.error(`channels-probe: stderr from CLI: ${stderr}`);
|
|
10241
|
-
}
|
|
10242
|
-
if (stdout.trim()) return {
|
|
10243
|
-
available: true,
|
|
10244
|
-
...parseChannelsProbeOutput(stdout)
|
|
10245
|
-
};
|
|
10246
|
-
return {
|
|
10247
|
-
available: false,
|
|
10248
|
-
gatewayReachable: false,
|
|
10249
|
-
accounts: [],
|
|
10250
|
-
anyAccountWorking: false,
|
|
10251
|
-
error: execError ?? "no output from openclaw channels status --probe"
|
|
10252
|
-
};
|
|
10253
|
-
}
|
|
10254
|
-
//#endregion
|
|
10255
10297
|
//#region src/innerapi/reportCliRun.ts
|
|
10256
10298
|
/**
|
|
10257
10299
|
* CLI-side client for studio_server's `openclaw.report_cli_run` inner
|
|
@@ -10331,7 +10373,7 @@ async function reportCliRun(opts) {
|
|
|
10331
10373
|
//#region src/help.ts
|
|
10332
10374
|
const BIN = "mclaw-diagnose";
|
|
10333
10375
|
function versionBanner() {
|
|
10334
|
-
return `v0.1.14-alpha.
|
|
10376
|
+
return `v0.1.14-alpha.3`;
|
|
10335
10377
|
}
|
|
10336
10378
|
const COMMANDS = [
|
|
10337
10379
|
{
|
|
@@ -10435,16 +10477,12 @@ EXIT CODES
|
|
|
10435
10477
|
hidden: true,
|
|
10436
10478
|
summary: "Run rule-engine check only",
|
|
10437
10479
|
help: `USAGE
|
|
10438
|
-
${BIN} check
|
|
10480
|
+
${BIN} check
|
|
10439
10481
|
|
|
10440
10482
|
DESCRIPTION
|
|
10441
10483
|
Runs the rule engine against the sandbox's current openclaw config and
|
|
10442
|
-
returns { failedRules }.
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
OPTIONS
|
|
10446
|
-
--ctx=<base64> Opaque ctx JSON (base64). When absent, fetched from
|
|
10447
|
-
innerapi (same path as doctor).
|
|
10484
|
+
returns { failedRules }. Ctx is fetched from innerapi automatically.
|
|
10485
|
+
End-users should prefer \`doctor\`.
|
|
10448
10486
|
`
|
|
10449
10487
|
},
|
|
10450
10488
|
{
|
|
@@ -10452,16 +10490,11 @@ OPTIONS
|
|
|
10452
10490
|
hidden: true,
|
|
10453
10491
|
summary: "Apply standard-mode repairs",
|
|
10454
10492
|
help: `USAGE
|
|
10455
|
-
${BIN} repair
|
|
10493
|
+
${BIN} repair
|
|
10456
10494
|
|
|
10457
10495
|
DESCRIPTION
|
|
10458
|
-
Runs repair for the failing rules
|
|
10459
|
-
|
|
10460
|
-
\`doctor --fix\` instead.
|
|
10461
|
-
|
|
10462
|
-
OPTIONS
|
|
10463
|
-
--ctx=<base64> Opaque ctx JSON (base64). When absent, fetched from
|
|
10464
|
-
innerapi.
|
|
10496
|
+
Runs repair for the failing rules. Ctx is fetched from innerapi
|
|
10497
|
+
automatically. End-users should use \`doctor --fix\` instead.
|
|
10465
10498
|
`
|
|
10466
10499
|
},
|
|
10467
10500
|
{
|
|
@@ -10469,14 +10502,15 @@ OPTIONS
|
|
|
10469
10502
|
hidden: true,
|
|
10470
10503
|
summary: "Re-initialize sandbox via the 9-step reset pipeline",
|
|
10471
10504
|
help: `USAGE
|
|
10472
|
-
${BIN} reset --async
|
|
10473
|
-
${BIN} reset --worker --task-id=<id>
|
|
10505
|
+
${BIN} reset --async
|
|
10506
|
+
${BIN} reset --worker --task-id=<id>
|
|
10474
10507
|
|
|
10475
10508
|
DESCRIPTION
|
|
10476
10509
|
Two-phase pipeline driven asynchronously: the --async invocation spawns
|
|
10477
10510
|
a detached worker and returns { taskId } immediately; the --worker
|
|
10478
10511
|
invocation (spawned by --async) runs the actual 9 steps and writes
|
|
10479
10512
|
progress to /tmp/openclaw-diagnose/reset-<taskId>.json.
|
|
10513
|
+
Ctx is fetched from innerapi automatically.
|
|
10480
10514
|
|
|
10481
10515
|
Poll progress with \`${BIN} get_reset_task --task-id=<id>\`.
|
|
10482
10516
|
|
|
@@ -10484,7 +10518,6 @@ OPTIONS
|
|
|
10484
10518
|
--async Start a detached worker and return taskId on stdout.
|
|
10485
10519
|
--worker Internal — run the 9-step pipeline (launched by --async).
|
|
10486
10520
|
--task-id=<id> Required with --worker; identifies the progress file.
|
|
10487
|
-
--ctx=<base64> Opaque ctx JSON; fetched from innerapi when absent.
|
|
10488
10521
|
`
|
|
10489
10522
|
},
|
|
10490
10523
|
{
|
|
@@ -10507,7 +10540,7 @@ OPTIONS
|
|
|
10507
10540
|
hidden: true,
|
|
10508
10541
|
summary: "Download + install the openclaw tarball",
|
|
10509
10542
|
help: `USAGE
|
|
10510
|
-
${BIN} install-openclaw <tag> [--
|
|
10543
|
+
${BIN} install-openclaw <tag> [--oss_file_map=<base64>]
|
|
10511
10544
|
|
|
10512
10545
|
DESCRIPTION
|
|
10513
10546
|
Downloads the openclaw@<tag> tgz via the signed OSS URL found in the
|
|
@@ -10519,9 +10552,9 @@ ARGUMENTS
|
|
|
10519
10552
|
<tag> Openclaw version tag, e.g. 2026.4.11.
|
|
10520
10553
|
|
|
10521
10554
|
OPTIONS
|
|
10522
|
-
--ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
|
|
10523
10555
|
--oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi
|
|
10524
|
-
entirely.
|
|
10556
|
+
entirely. When absent, ossFileMap is fetched from
|
|
10557
|
+
innerapi automatically.
|
|
10525
10558
|
`
|
|
10526
10559
|
},
|
|
10527
10560
|
{
|
|
@@ -10547,8 +10580,7 @@ OPTIONS
|
|
|
10547
10580
|
--home_base=<dir> Override the /home/gem base (tests).
|
|
10548
10581
|
--config_path=<p> Override the openclaw.json path (tests).
|
|
10549
10582
|
--skip-config-update Leave plugins.installs in openclaw.json untouched.
|
|
10550
|
-
--
|
|
10551
|
-
--oss_file_map=... Pre-built OSS URL map (base64 JSON).
|
|
10583
|
+
--oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
|
|
10552
10584
|
`
|
|
10553
10585
|
},
|
|
10554
10586
|
{
|
|
@@ -10575,7 +10607,6 @@ OPTIONS
|
|
|
10575
10607
|
--cli=<name> CLI package to install by short name or scoped
|
|
10576
10608
|
packageName (repeatable, at least one required).
|
|
10577
10609
|
--home_base=<dir> Override the /home/gem base (tests).
|
|
10578
|
-
--ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
|
|
10579
10610
|
--oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
|
|
10580
10611
|
|
|
10581
10612
|
EXAMPLES
|
|
@@ -10629,46 +10660,6 @@ OPTIONS
|
|
|
10629
10660
|
EXIT CODES
|
|
10630
10661
|
0 Success or skipped (prerequisites not met).
|
|
10631
10662
|
1 Secret/path unresolvable, lark-cli failed, or config unreadable.
|
|
10632
|
-
`
|
|
10633
|
-
},
|
|
10634
|
-
{
|
|
10635
|
-
name: "upgrade-lark",
|
|
10636
|
-
hidden: false,
|
|
10637
|
-
summary: "Upgrade the Feishu/Lark plugin via @larksuite/openclaw-lark-tools",
|
|
10638
|
-
help: `USAGE
|
|
10639
|
-
${BIN} upgrade-lark [--scene=<scene>] [--caller=<n>] [--trace-id=<id>]
|
|
10640
|
-
|
|
10641
|
-
DESCRIPTION
|
|
10642
|
-
Upgrades the Feishu/Lark plugin by running:
|
|
10643
|
-
npx -y @larksuite/openclaw-lark-tools update --use-existing
|
|
10644
|
-
|
|
10645
|
-
Before the upgrade, the following files are backed up:
|
|
10646
|
-
- openclaw.json
|
|
10647
|
-
- extensions/openclaw-lark/ (if present)
|
|
10648
|
-
- extensions/feishu-openclaw-plugin/ (if present)
|
|
10649
|
-
After the upgrade, the result is validated:
|
|
10650
|
-
- feishu.accounts bot count must not decrease
|
|
10651
|
-
- gateway config structure must remain valid (port/mode/bind/auth/trustedProxies)
|
|
10652
|
-
If the upgrade command fails, or validation fails, the backed-up files are
|
|
10653
|
-
restored to roll back the changes.
|
|
10654
|
-
|
|
10655
|
-
Execution is logged to /tmp/openclaw-diagnose/upgrade-lark-<runId>.log.
|
|
10656
|
-
|
|
10657
|
-
Output is a single JSON object on stdout:
|
|
10658
|
-
{ "ok": true, "stdout": "...", "stderr": "...", "logFile": "..." }
|
|
10659
|
-
{ "ok": false, "error": "...", "stderr": "...", "exitCode": 1,
|
|
10660
|
-
"rollbackOk": true, "validationError": "...", "logFile": "..." }
|
|
10661
|
-
|
|
10662
|
-
OPTIONS
|
|
10663
|
-
--scene=<scene> Telemetry label forwarded to Slardar only.
|
|
10664
|
-
Known values: PageUpgradeLark, etc. Custom strings accepted.
|
|
10665
|
-
--caller=<name> Optional metadata passed to innerapi.
|
|
10666
|
-
--trace-id=<id> Optional log-correlation id.
|
|
10667
|
-
|
|
10668
|
-
EXIT CODES
|
|
10669
|
-
0 Success: upgrade ran and all validations passed.
|
|
10670
|
-
1 Failure: npx error, validation failed, or git commit failed.
|
|
10671
|
-
File rollback was attempted (see rollbackOk in the JSON output).
|
|
10672
10663
|
`
|
|
10673
10664
|
},
|
|
10674
10665
|
{
|
|
@@ -10702,41 +10693,6 @@ EXAMPLES
|
|
|
10702
10693
|
${BIN} rules # all rules
|
|
10703
10694
|
${BIN} rules --rule=gateway # single rule
|
|
10704
10695
|
${BIN} rules --rule=gateway --rule=feishu_channel # multiple rules
|
|
10705
|
-
`
|
|
10706
|
-
},
|
|
10707
|
-
{
|
|
10708
|
-
name: "channels-probe",
|
|
10709
|
-
hidden: true,
|
|
10710
|
-
summary: "Check feishu channel health via openclaw channels status --probe",
|
|
10711
|
-
help: `USAGE
|
|
10712
|
-
${BIN} channels-probe [--timeout=<ms>]
|
|
10713
|
-
|
|
10714
|
-
DESCRIPTION
|
|
10715
|
-
Runs \`openclaw channels status --probe\` and returns a structured JSON
|
|
10716
|
-
summary of whether the current environment's feishu channels are
|
|
10717
|
-
configured and working correctly.
|
|
10718
|
-
|
|
10719
|
-
Output:
|
|
10720
|
-
{
|
|
10721
|
-
"available": true,
|
|
10722
|
-
"gatewayReachable": true,
|
|
10723
|
-
"accounts": [
|
|
10724
|
-
{ "id": "default", "bits": ["enabled","configured","running","works"],
|
|
10725
|
-
"isWorking": true, "raw": "- Feishu default: ..." }
|
|
10726
|
-
],
|
|
10727
|
-
"anyAccountWorking": true
|
|
10728
|
-
}
|
|
10729
|
-
|
|
10730
|
-
An account is considered working when:
|
|
10731
|
-
enabled ∧ configured ∧ ( works ∨ ( running ∧ no error: ∧ no probe failed ) )
|
|
10732
|
-
|
|
10733
|
-
"available": false means the CLI invocation itself failed (openclaw not
|
|
10734
|
-
found, gateway unreachable, or no parseable output returned).
|
|
10735
|
-
|
|
10736
|
-
OPTIONS
|
|
10737
|
-
--timeout=<ms> Max wait in milliseconds (default: 60000). The probe
|
|
10738
|
-
can hang indefinitely on openclaw v2026.4.x due to a
|
|
10739
|
-
missing per-request HTTP timeout — set this accordingly.
|
|
10740
10696
|
`
|
|
10741
10697
|
},
|
|
10742
10698
|
{
|
|
@@ -10757,8 +10713,7 @@ OPTIONS
|
|
|
10757
10713
|
--role=<role> Package role (e.g. template, config).
|
|
10758
10714
|
--name=<name> Package name within the role.
|
|
10759
10715
|
--dir=<dir> Target dir (defaults to dirname(pkg.installPath)).
|
|
10760
|
-
--
|
|
10761
|
-
--oss_file_map=... Pre-built OSS URL map (base64 JSON).
|
|
10716
|
+
--oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
|
|
10762
10717
|
`
|
|
10763
10718
|
}
|
|
10764
10719
|
];
|
|
@@ -10834,31 +10789,31 @@ function planVarsFields(opts = {}) {
|
|
|
10834
10789
|
*
|
|
10835
10790
|
* Per-command group needs:
|
|
10836
10791
|
*
|
|
10837
|
-
* doctor / check app
|
|
10838
|
-
* repair app + secrets
|
|
10839
|
-
* reset app + secrets + install + reset
|
|
10792
|
+
* doctor / check app + larkApps
|
|
10793
|
+
* repair app + secrets + larkApps
|
|
10794
|
+
* reset app + secrets + install + reset + larkApps
|
|
10840
10795
|
* install-* install only
|
|
10841
10796
|
*
|
|
10842
10797
|
* Empty result (`{}`) means "no group needed" — the CLI can skip the
|
|
10843
10798
|
* `fetchCtxViaInnerApi` call entirely and run with a synthetic empty ctx.
|
|
10844
|
-
* Happens e.g. when the user pinned `--rule=<key>` to a vars-free rule on
|
|
10845
|
-
* `doctor`.
|
|
10846
10799
|
*/
|
|
10847
10800
|
function planCtxPopulate(opts) {
|
|
10848
10801
|
if (opts.command === "install") return { install: true };
|
|
10849
10802
|
const populate = {};
|
|
10850
|
-
|
|
10803
|
+
if (planVarsFields({
|
|
10851
10804
|
disabled: opts.disabled,
|
|
10852
10805
|
onlyRules: opts.onlyRules,
|
|
10853
10806
|
profile: opts.profile
|
|
10854
|
-
});
|
|
10855
|
-
if (
|
|
10856
|
-
|
|
10857
|
-
|
|
10807
|
+
}).length > 0) populate.app = true;
|
|
10808
|
+
if (opts.command === "repair") {
|
|
10809
|
+
populate.secrets = true;
|
|
10810
|
+
populate.larkApps = true;
|
|
10811
|
+
} else if (opts.command === "reset") {
|
|
10858
10812
|
populate.secrets = true;
|
|
10859
10813
|
populate.install = true;
|
|
10860
10814
|
populate.reset = true;
|
|
10861
|
-
|
|
10815
|
+
populate.larkApps = true;
|
|
10816
|
+
} else if (opts.command === "doctor" || opts.command === "check") populate.larkApps = true;
|
|
10862
10817
|
return populate;
|
|
10863
10818
|
}
|
|
10864
10819
|
//#endregion
|
|
@@ -10912,378 +10867,11 @@ function reportDoctorRunToSlardar(opts) {
|
|
|
10912
10867
|
}
|
|
10913
10868
|
});
|
|
10914
10869
|
}
|
|
10915
|
-
/** Read the tail of a file, up to maxBytes. Returns empty string on any error. */
|
|
10916
|
-
function readFileTail(filePath, maxBytes = 4e3) {
|
|
10917
|
-
try {
|
|
10918
|
-
const content = node_fs.default.readFileSync(filePath, "utf-8");
|
|
10919
|
-
if (content.length <= maxBytes) return content;
|
|
10920
|
-
return `...(truncated — showing last ${maxBytes} chars)...\n` + content.slice(-maxBytes);
|
|
10921
|
-
} catch {
|
|
10922
|
-
return "";
|
|
10923
|
-
}
|
|
10924
|
-
}
|
|
10925
|
-
function reportUpgradeLarkToSlardar(opts) {
|
|
10926
|
-
console.error(`[slardar] upgrade-lark reportTask scene=${opts.scene ?? ""} success=${opts.success} exitCode=${opts.exitCode ?? ""} rollbackOk=${opts.rollbackOk ?? ""}`);
|
|
10927
|
-
reportTask({
|
|
10928
|
-
eventName: "upgrade_lark_run",
|
|
10929
|
-
durationMs: opts.durationMs,
|
|
10930
|
-
status: opts.success ? "success" : "failed",
|
|
10931
|
-
extraCategories: {
|
|
10932
|
-
scene: opts.scene ?? "",
|
|
10933
|
-
exit_code: String(opts.exitCode ?? ""),
|
|
10934
|
-
rollback_ok: opts.rollbackOk != null ? String(opts.rollbackOk) : "",
|
|
10935
|
-
validation_error: (opts.validationError ?? "").slice(0, 200),
|
|
10936
|
-
error_msg: (opts.error ?? "").slice(0, 200)
|
|
10937
|
-
}
|
|
10938
|
-
});
|
|
10939
|
-
const logContent = readFileTail(opts.logFile);
|
|
10940
|
-
console.error(`[slardar] upgrade_lark_detail logFile=${opts.logFile} contentLength=${logContent.length} sendLogAvailable=${typeof Slardar.sendLog}`);
|
|
10941
|
-
reportLog({
|
|
10942
|
-
event: "upgrade_lark_detail",
|
|
10943
|
-
message: logContent || "(no log content)",
|
|
10944
|
-
level: opts.success ? "info" : "error",
|
|
10945
|
-
extra: {
|
|
10946
|
-
log_file: opts.logFile,
|
|
10947
|
-
scene: opts.scene ?? "",
|
|
10948
|
-
success: String(opts.success),
|
|
10949
|
-
exit_code: String(opts.exitCode ?? ""),
|
|
10950
|
-
rollback_ok: opts.rollbackOk != null ? String(opts.rollbackOk) : ""
|
|
10951
|
-
}
|
|
10952
|
-
});
|
|
10953
|
-
}
|
|
10954
|
-
//#endregion
|
|
10955
|
-
//#region src/upgrade-lark.ts
|
|
10956
|
-
/** Plugin directories under extensions/ that are backed up before upgrade */
|
|
10957
|
-
const FEISHU_PLUGIN_DIRS = ["openclaw-lark", "feishu-openclaw-plugin"];
|
|
10958
|
-
/** Version compat rule keys checked in the doctor output after install */
|
|
10959
|
-
const VERSION_COMPAT_RULE_KEYS = ["feishu_plugin_version_compat_lark", "feishu_plugin_version_compat_openclaw"];
|
|
10960
|
-
function backupFiles(opts) {
|
|
10961
|
-
const { workspaceDir, configPath, backupDir, log } = opts;
|
|
10962
|
-
try {
|
|
10963
|
-
node_fs.default.mkdirSync(backupDir, { recursive: true });
|
|
10964
|
-
log(`backup dir: ${backupDir}`);
|
|
10965
|
-
if (node_fs.default.existsSync(configPath)) {
|
|
10966
|
-
const stat = node_fs.default.statSync(configPath);
|
|
10967
|
-
node_fs.default.copyFileSync(configPath, node_path.default.join(backupDir, "openclaw.json"));
|
|
10968
|
-
log(` backed up: openclaw.json (${stat.size} bytes)`);
|
|
10969
|
-
} else log(` skipped: openclaw.json (not found)`);
|
|
10970
|
-
const extSrc = node_path.default.join(workspaceDir, "extensions");
|
|
10971
|
-
for (const pluginDir of FEISHU_PLUGIN_DIRS) {
|
|
10972
|
-
const src = node_path.default.join(extSrc, pluginDir);
|
|
10973
|
-
if (node_fs.default.existsSync(src)) {
|
|
10974
|
-
const dst = node_path.default.join(backupDir, "extensions", pluginDir);
|
|
10975
|
-
node_fs.default.cpSync(src, dst, { recursive: true });
|
|
10976
|
-
const version = readPkgVersion(node_path.default.join(src, "package.json"));
|
|
10977
|
-
log(` backed up: extensions/${pluginDir}${version ? ` (version: ${version})` : ""}`);
|
|
10978
|
-
} else log(` skipped: extensions/${pluginDir} (not found)`);
|
|
10979
|
-
}
|
|
10980
|
-
return { ok: true };
|
|
10981
|
-
} catch (e) {
|
|
10982
|
-
return {
|
|
10983
|
-
ok: false,
|
|
10984
|
-
error: `backup failed: ${e.message}`
|
|
10985
|
-
};
|
|
10986
|
-
}
|
|
10987
|
-
}
|
|
10988
|
-
function restoreFiles(opts) {
|
|
10989
|
-
const { workspaceDir, configPath, backupDir, log } = opts;
|
|
10990
|
-
try {
|
|
10991
|
-
const configBackup = node_path.default.join(backupDir, "openclaw.json");
|
|
10992
|
-
if (node_fs.default.existsSync(configBackup)) {
|
|
10993
|
-
node_fs.default.copyFileSync(configBackup, configPath);
|
|
10994
|
-
log(` restored: openclaw.json`);
|
|
10995
|
-
}
|
|
10996
|
-
const extDst = node_path.default.join(workspaceDir, "extensions");
|
|
10997
|
-
for (const pluginDir of FEISHU_PLUGIN_DIRS) {
|
|
10998
|
-
const backupSrc = node_path.default.join(backupDir, "extensions", pluginDir);
|
|
10999
|
-
if (node_fs.default.existsSync(backupSrc)) {
|
|
11000
|
-
const dst = node_path.default.join(extDst, pluginDir);
|
|
11001
|
-
if (node_fs.default.existsSync(dst)) node_fs.default.rmSync(dst, {
|
|
11002
|
-
recursive: true,
|
|
11003
|
-
force: true
|
|
11004
|
-
});
|
|
11005
|
-
node_fs.default.cpSync(backupSrc, dst, { recursive: true });
|
|
11006
|
-
log(` restored: extensions/${pluginDir}`);
|
|
11007
|
-
}
|
|
11008
|
-
}
|
|
11009
|
-
return true;
|
|
11010
|
-
} catch (e) {
|
|
11011
|
-
log(` restore error: ${e.message}`);
|
|
11012
|
-
return false;
|
|
11013
|
-
}
|
|
11014
|
-
}
|
|
11015
|
-
function readPkgVersion(pkgPath) {
|
|
11016
|
-
try {
|
|
11017
|
-
const pkg = JSON.parse(node_fs.default.readFileSync(pkgPath, "utf-8"));
|
|
11018
|
-
return typeof pkg.version === "string" ? pkg.version : null;
|
|
11019
|
-
} catch {
|
|
11020
|
-
return null;
|
|
11021
|
-
}
|
|
11022
|
-
}
|
|
11023
|
-
function snapshotVersions(cwd, log) {
|
|
11024
|
-
const ocResult = (0, node_child_process.spawnSync)("openclaw", ["--version"], {
|
|
11025
|
-
cwd,
|
|
11026
|
-
encoding: "utf-8",
|
|
11027
|
-
stdio: [
|
|
11028
|
-
"ignore",
|
|
11029
|
-
"pipe",
|
|
11030
|
-
"pipe"
|
|
11031
|
-
],
|
|
11032
|
-
timeout: 5e3
|
|
11033
|
-
});
|
|
11034
|
-
const ocRaw = (ocResult.stdout ?? "").trim() || (ocResult.stderr ?? "").trim();
|
|
11035
|
-
const extDir = node_path.default.join(cwd, "extensions");
|
|
11036
|
-
const larkPkg = node_path.default.join(extDir, "openclaw-lark", "package.json");
|
|
11037
|
-
const feishuPkg = node_path.default.join(extDir, "feishu-openclaw-plugin", "package.json");
|
|
11038
|
-
log(` version-check paths: ${larkPkg} [${node_fs.default.existsSync(larkPkg) ? "exists" : "missing"}]`);
|
|
11039
|
-
log(` version-check paths: ${feishuPkg} [${node_fs.default.existsSync(feishuPkg) ? "exists" : "missing"}]`);
|
|
11040
|
-
return {
|
|
11041
|
-
openclaw: ocRaw || null,
|
|
11042
|
-
openclawLark: readPkgVersion(larkPkg),
|
|
11043
|
-
feishuOpenclawPlugin: readPkgVersion(feishuPkg)
|
|
11044
|
-
};
|
|
11045
|
-
}
|
|
11046
|
-
function logVersionSnapshot(label, v, log) {
|
|
11047
|
-
log(`${label}: openclaw=${v.openclaw ?? "n/a"} openclaw-lark=${v.openclawLark ?? "n/a"} feishu-openclaw-plugin=${v.feishuOpenclawPlugin ?? "n/a"}`);
|
|
11048
|
-
}
|
|
11049
|
-
function countFeishuBots(configPath) {
|
|
11050
|
-
try {
|
|
11051
|
-
const raw = node_fs.default.readFileSync(configPath, "utf-8");
|
|
11052
|
-
const config = loadJSON5().parse(raw);
|
|
11053
|
-
const accounts = getNestedMap(config, "channels", "feishu", "accounts");
|
|
11054
|
-
if (accounts) return Object.keys(accounts).length;
|
|
11055
|
-
const feishu = getNestedMap(config, "channels", "feishu");
|
|
11056
|
-
return typeof feishu?.appId === "string" && feishu.appId ? 1 : 0;
|
|
11057
|
-
} catch {
|
|
11058
|
-
return 0;
|
|
11059
|
-
}
|
|
11060
|
-
}
|
|
11061
|
-
/**
|
|
11062
|
-
* Parse doctor stdout (first JSON line) and return an error string if any
|
|
11063
|
-
* version compat rule failed. Returns null on parse failure so a broken doctor
|
|
11064
|
-
* output does not block the install.
|
|
11065
|
-
*/
|
|
11066
|
-
function checkVersionCompatFromDoctorOutput(stdout, log) {
|
|
11067
|
-
const firstLine = stdout.split("\n")[0]?.trim();
|
|
11068
|
-
if (!firstLine) {
|
|
11069
|
-
log(" doctor(compat): empty output, skipping version compat check");
|
|
11070
|
-
return null;
|
|
11071
|
-
}
|
|
11072
|
-
try {
|
|
11073
|
-
const report = JSON.parse(firstLine);
|
|
11074
|
-
for (const outcome of report.results) if (VERSION_COMPAT_RULE_KEYS.includes(outcome.rule)) {
|
|
11075
|
-
if (outcome.status === "failed" || outcome.status === "still-broken" || outcome.status === "error") return `version compat rule ${outcome.rule} ${outcome.status}: ${outcome.message ?? "(no message)"}`;
|
|
11076
|
-
}
|
|
11077
|
-
return null;
|
|
11078
|
-
} catch (e) {
|
|
11079
|
-
log(` doctor(compat): failed to parse output — ${e.message}`);
|
|
11080
|
-
return null;
|
|
11081
|
-
}
|
|
11082
|
-
}
|
|
11083
|
-
/** Run channels probe, log results, and return the result. Never throws. */
|
|
11084
|
-
function probeChannels(label, log, timeoutMs) {
|
|
11085
|
-
try {
|
|
11086
|
-
const r = runChannelsProbe(timeoutMs);
|
|
11087
|
-
log(` ${label} available=${r.available} anyAccountWorking=${r.anyAccountWorking}`);
|
|
11088
|
-
if (r.error) log(` ${label} error: ${r.error}`);
|
|
11089
|
-
if (r.gatewayReachable != null) log(` ${label} gatewayReachable: ${r.gatewayReachable}`);
|
|
11090
|
-
for (const acct of r.accounts ?? []) log(` ${label} account ${acct.id}: isWorking=${acct.isWorking} bits=[${acct.bits.join(",")}]`);
|
|
11091
|
-
return r;
|
|
11092
|
-
} catch (e) {
|
|
11093
|
-
log(` ${label} channels probe threw: ${e.message}`);
|
|
11094
|
-
return {
|
|
11095
|
-
available: false,
|
|
11096
|
-
accounts: [],
|
|
11097
|
-
anyAccountWorking: false
|
|
11098
|
-
};
|
|
11099
|
-
}
|
|
11100
|
-
}
|
|
11101
|
-
function runUpgradeLark(opts) {
|
|
11102
|
-
const cwd = opts.cwd ?? "/home/gem/workspace/agent";
|
|
11103
|
-
const configPath = opts.configPath ?? CONFIG_PATH;
|
|
11104
|
-
const logFile = upgradeLarkLogFile(opts.runId);
|
|
11105
|
-
const log = makeLogger(logFile);
|
|
11106
|
-
const fsOpts = {
|
|
11107
|
-
workspaceDir: cwd,
|
|
11108
|
-
configPath,
|
|
11109
|
-
backupDir: node_path.default.join(opts.backupBaseDir ?? "/tmp/openclaw-diagnose", `upgrade-lark-backup-${opts.runId}`),
|
|
11110
|
-
log
|
|
11111
|
-
};
|
|
11112
|
-
const cliScript = opts.cliScript ?? process.argv[1];
|
|
11113
|
-
const statusCheckDelayMs = opts.statusCheckDelayMs ?? 5e3;
|
|
11114
|
-
log(`${"=".repeat(60)}`);
|
|
11115
|
-
log(`upgrade-lark started runId=${opts.runId}`);
|
|
11116
|
-
log(` cwd : ${cwd}`);
|
|
11117
|
-
log(` configPath : ${configPath}`);
|
|
11118
|
-
log(`${"=".repeat(60)}`);
|
|
11119
|
-
log("");
|
|
11120
|
-
log("── [1/8] 升级前状态快照 ──────────────────────────────────");
|
|
11121
|
-
log(`before-state: botCount=${countFeishuBots(configPath)}`);
|
|
11122
|
-
log("");
|
|
11123
|
-
log("── [2/8] 文件备份 ────────────────────────────────────────");
|
|
11124
|
-
const backup = backupFiles(fsOpts);
|
|
11125
|
-
if (!backup.ok) {
|
|
11126
|
-
log(`ERROR: ${backup.error}`);
|
|
11127
|
-
return {
|
|
11128
|
-
ok: false,
|
|
11129
|
-
error: backup.error,
|
|
11130
|
-
logFile
|
|
11131
|
-
};
|
|
11132
|
-
}
|
|
11133
|
-
log("backup: ok");
|
|
11134
|
-
logVersionSnapshot("before-versions", snapshotVersions(cwd, log), log);
|
|
11135
|
-
log("");
|
|
11136
|
-
log("── [3/8] channels probe(升级前)────────────────────────");
|
|
11137
|
-
const beforeChannels = probeChannels("before", log, 3e4);
|
|
11138
|
-
log("");
|
|
11139
|
-
log("── [4/8] 清理本地 openclaw shim ─────────────────────────");
|
|
11140
|
-
const localOpenclawBin = node_path.default.join(cwd, "node_modules", ".bin", "openclaw");
|
|
11141
|
-
if (node_fs.default.existsSync(localOpenclawBin)) try {
|
|
11142
|
-
node_fs.default.rmSync(localOpenclawBin);
|
|
11143
|
-
log(` removed: ${localOpenclawBin}`);
|
|
11144
|
-
} catch (e) {
|
|
11145
|
-
log(` WARN: failed to remove ${localOpenclawBin}: ${e.message}`);
|
|
11146
|
-
}
|
|
11147
|
-
else log(` skipped: ${localOpenclawBin} (not found)`);
|
|
11148
|
-
log("");
|
|
11149
|
-
log("── [5/8] npx install (@larksuite/openclaw-lark-tools update) ──");
|
|
11150
|
-
const npxResult = (0, node_child_process.spawnSync)("npx", [
|
|
11151
|
-
"-y",
|
|
11152
|
-
"@larksuite/openclaw-lark-tools",
|
|
11153
|
-
"update"
|
|
11154
|
-
], {
|
|
11155
|
-
cwd,
|
|
11156
|
-
encoding: "utf-8",
|
|
11157
|
-
stdio: [
|
|
11158
|
-
"ignore",
|
|
11159
|
-
"pipe",
|
|
11160
|
-
"pipe"
|
|
11161
|
-
],
|
|
11162
|
-
timeout: 12e4
|
|
11163
|
-
});
|
|
11164
|
-
const npxStdout = npxResult.stdout?.trim() ?? "";
|
|
11165
|
-
const npxStderr = npxResult.stderr?.trim() ?? "";
|
|
11166
|
-
const npxExitCode = npxResult.status ?? 1;
|
|
11167
|
-
if (npxStdout) log(`npx stdout:\n${npxStdout}`);
|
|
11168
|
-
if (npxStderr) log(`npx stderr:\n${npxStderr}`);
|
|
11169
|
-
log(`npx exit: ${npxExitCode}${npxResult.error ? ` error: ${npxResult.error.message}` : ""}`);
|
|
11170
|
-
if (statusCheckDelayMs > 0) {
|
|
11171
|
-
log("");
|
|
11172
|
-
log(`── 等待 ${statusCheckDelayMs / 1e3}s(让 openclaw 服务完成重启) ─────────────`);
|
|
11173
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, statusCheckDelayMs);
|
|
11174
|
-
log("wait done");
|
|
11175
|
-
}
|
|
11176
|
-
const doRollback = (reason) => {
|
|
11177
|
-
log(`ERROR: ${reason}`);
|
|
11178
|
-
const rollbackOk = restoreFiles(fsOpts);
|
|
11179
|
-
log(`rollback: ${rollbackOk ? "ok" : "FAILED"}`);
|
|
11180
|
-
return {
|
|
11181
|
-
ok: false,
|
|
11182
|
-
error: reason,
|
|
11183
|
-
validationError: reason,
|
|
11184
|
-
stdout: npxStdout,
|
|
11185
|
-
stderr: npxStderr,
|
|
11186
|
-
exitCode: npxExitCode,
|
|
11187
|
-
rollbackOk,
|
|
11188
|
-
logFile
|
|
11189
|
-
};
|
|
11190
|
-
};
|
|
11191
|
-
log("");
|
|
11192
|
-
log("── [6/8] 插件安装检查 + 版本兼容校验 ───────────────────");
|
|
11193
|
-
const larkExtDir = node_path.default.join(cwd, "extensions", "openclaw-lark");
|
|
11194
|
-
const larkVersion = readPkgVersion(node_path.default.join(larkExtDir, "package.json"));
|
|
11195
|
-
log(` extensions/openclaw-lark: ${node_fs.default.existsSync(larkExtDir) ? "exists" : "missing"}, version=${larkVersion ?? "n/a"}`);
|
|
11196
|
-
if (!node_fs.default.existsSync(larkExtDir)) return doRollback("extensions/openclaw-lark not found after install");
|
|
11197
|
-
if (!larkVersion) return doRollback("extensions/openclaw-lark/package.json has no valid version after install");
|
|
11198
|
-
log(" running doctor version compat check...");
|
|
11199
|
-
const compatArgs = ["doctor"];
|
|
11200
|
-
if (opts.scene) compatArgs.push(`--scene=${opts.scene}`);
|
|
11201
|
-
const compatResult = (0, node_child_process.spawnSync)(process.execPath, [cliScript, ...compatArgs], {
|
|
11202
|
-
cwd,
|
|
11203
|
-
encoding: "utf-8",
|
|
11204
|
-
stdio: [
|
|
11205
|
-
"ignore",
|
|
11206
|
-
"pipe",
|
|
11207
|
-
"pipe"
|
|
11208
|
-
],
|
|
11209
|
-
timeout: 6e4,
|
|
11210
|
-
env: process.env
|
|
11211
|
-
});
|
|
11212
|
-
if (compatResult.stdout?.trim()) log(`doctor(compat) stdout:\n${compatResult.stdout.trim()}`);
|
|
11213
|
-
if (compatResult.stderr?.trim()) log(`doctor(compat) stderr:\n${compatResult.stderr.trim()}`);
|
|
11214
|
-
log(`doctor(compat) exit: ${compatResult.status ?? "null"}${compatResult.error ? ` error: ${compatResult.error.message}` : ""}`);
|
|
11215
|
-
const compatError = checkVersionCompatFromDoctorOutput(compatResult.stdout?.trim() ?? "", log);
|
|
11216
|
-
if (compatError) return doRollback(compatError);
|
|
11217
|
-
log(" version compat: ok");
|
|
11218
|
-
logVersionSnapshot("after-versions", snapshotVersions(cwd, log), log);
|
|
11219
|
-
log("");
|
|
11220
|
-
log("── [7/8] channels probe(升级后)────────────────────────");
|
|
11221
|
-
if (!probeChannels("after", log, 3e4).anyAccountWorking) {
|
|
11222
|
-
if (!beforeChannels.anyAccountWorking) {
|
|
11223
|
-
log(" channels: not working before or after install — pre-existing issue, skipping rollback");
|
|
11224
|
-
return {
|
|
11225
|
-
ok: false,
|
|
11226
|
-
error: "channels probe: no working account (pre-existing issue, not caused by install)",
|
|
11227
|
-
validationError: "channels probe: no working account (pre-existing)",
|
|
11228
|
-
stdout: npxStdout,
|
|
11229
|
-
stderr: npxStderr,
|
|
11230
|
-
exitCode: npxExitCode,
|
|
11231
|
-
logFile
|
|
11232
|
-
};
|
|
11233
|
-
}
|
|
11234
|
-
return doRollback("channels probe: no working account after install (was working before)");
|
|
11235
|
-
}
|
|
11236
|
-
log(" channels: ok");
|
|
11237
|
-
log("");
|
|
11238
|
-
log("── [8/8] doctor --fix ────────────────────────────────────");
|
|
11239
|
-
const fixArgs = ["doctor", "--fix"];
|
|
11240
|
-
if (opts.scene) fixArgs.push(`--scene=${opts.scene}`);
|
|
11241
|
-
const fixResult = (0, node_child_process.spawnSync)(process.execPath, [cliScript, ...fixArgs], {
|
|
11242
|
-
cwd,
|
|
11243
|
-
encoding: "utf-8",
|
|
11244
|
-
stdio: [
|
|
11245
|
-
"ignore",
|
|
11246
|
-
"pipe",
|
|
11247
|
-
"pipe"
|
|
11248
|
-
],
|
|
11249
|
-
timeout: 6e4,
|
|
11250
|
-
env: process.env
|
|
11251
|
-
});
|
|
11252
|
-
if (fixResult.stdout?.trim()) log(`doctor(fix) stdout:\n${fixResult.stdout.trim()}`);
|
|
11253
|
-
if (fixResult.stderr?.trim()) log(`doctor(fix) stderr:\n${fixResult.stderr.trim()}`);
|
|
11254
|
-
log(`doctor(fix) exit: ${fixResult.status ?? "null"}${fixResult.error ? ` error: ${fixResult.error.message}` : ""}`);
|
|
11255
|
-
log("");
|
|
11256
|
-
log(`${"=".repeat(60)}`);
|
|
11257
|
-
log("upgrade-lark completed successfully");
|
|
11258
|
-
log(`${"=".repeat(60)}`);
|
|
11259
|
-
return {
|
|
11260
|
-
ok: true,
|
|
11261
|
-
stdout: npxStdout,
|
|
11262
|
-
stderr: npxStderr,
|
|
11263
|
-
exitCode: npxExitCode,
|
|
11264
|
-
logFile
|
|
11265
|
-
};
|
|
11266
|
-
}
|
|
11267
10870
|
//#endregion
|
|
11268
10871
|
//#region src/index.ts
|
|
11269
10872
|
const args = node_process.default.argv.slice(2);
|
|
11270
10873
|
const mode = args.find((a) => !a.startsWith("-"));
|
|
11271
10874
|
/**
|
|
11272
|
-
* Decode `--ctx=<base64>` into an opaque JSON object. Returns undefined when
|
|
11273
|
-
* the flag isn't present — the caller decides whether to fall back to the
|
|
11274
|
-
* innerapi or to error out.
|
|
11275
|
-
*
|
|
11276
|
-
* The object's shape is not enforced here; downstream code consumes it via
|
|
11277
|
-
* either `normalizeCtx()` (new path) or direct field access for the legacy
|
|
11278
|
-
* check/repair/reset contract still used by sandbox_console push.
|
|
11279
|
-
*/
|
|
11280
|
-
function parseCtxFlag(args) {
|
|
11281
|
-
const ctxArg = args.find((a) => a.startsWith("--ctx="));
|
|
11282
|
-
if (!ctxArg) return void 0;
|
|
11283
|
-
const b64 = ctxArg.slice(6);
|
|
11284
|
-
return JSON.parse(Buffer.from(b64, "base64").toString("utf-8"));
|
|
11285
|
-
}
|
|
11286
|
-
/**
|
|
11287
10875
|
* Pull the first non-flag positional after the mode name.
|
|
11288
10876
|
* (The mode itself is args[0] in the filtered set, so we skip index 0.)
|
|
11289
10877
|
*/
|
|
@@ -11311,8 +10899,8 @@ function getMultiFlag(args, name) {
|
|
|
11311
10899
|
* case but is no longer consulted.
|
|
11312
10900
|
*/
|
|
11313
10901
|
async function reportRun(command, rc, _raw, invocation, durationMs, outcome, slardar = {
|
|
11314
|
-
scene,
|
|
11315
|
-
profile,
|
|
10902
|
+
scene: void 0,
|
|
10903
|
+
profile: "standard",
|
|
11316
10904
|
fix: false
|
|
11317
10905
|
}) {
|
|
11318
10906
|
console.error(`${command}: telemetry calling report_cli_run`);
|
|
@@ -11376,7 +10964,7 @@ async function main() {
|
|
|
11376
10964
|
console.error(`${mode}: begin argv=[${args.join(" ")}] version=${getVersion()} traceId=${traceId ?? "-"} caller=${caller ?? "-"} runIdGenerated=${rc.generated}`);
|
|
11377
10965
|
switch (mode) {
|
|
11378
10966
|
case "check": {
|
|
11379
|
-
const raw =
|
|
10967
|
+
const raw = await fetchCtxViaInnerApi({
|
|
11380
10968
|
populate: planCtxPopulate({
|
|
11381
10969
|
command: "check",
|
|
11382
10970
|
profile
|
|
@@ -11401,7 +10989,7 @@ async function main() {
|
|
|
11401
10989
|
break;
|
|
11402
10990
|
}
|
|
11403
10991
|
case "repair": {
|
|
11404
|
-
const raw =
|
|
10992
|
+
const raw = await fetchCtxViaInnerApi({
|
|
11405
10993
|
populate: planCtxPopulate({
|
|
11406
10994
|
command: "repair",
|
|
11407
10995
|
profile
|
|
@@ -11472,27 +11060,15 @@ async function main() {
|
|
|
11472
11060
|
break;
|
|
11473
11061
|
}
|
|
11474
11062
|
case "reset":
|
|
11475
|
-
if (args.includes("--async"))
|
|
11476
|
-
|
|
11477
|
-
let ctxBase64;
|
|
11478
|
-
if (ctxArg) ctxBase64 = ctxArg.slice(6);
|
|
11479
|
-
else {
|
|
11480
|
-
const fetched = await fetchCtxViaInnerApi({
|
|
11481
|
-
populate: planCtxPopulate({ command: "reset" }),
|
|
11482
|
-
caller,
|
|
11483
|
-
traceId
|
|
11484
|
-
});
|
|
11485
|
-
ctxBase64 = Buffer.from(JSON.stringify(fetched), "utf-8").toString("base64");
|
|
11486
|
-
}
|
|
11487
|
-
console.log(JSON.stringify(startAsyncReset(ctxBase64)));
|
|
11488
|
-
} else if (args.includes("--worker")) {
|
|
11063
|
+
if (args.includes("--async")) console.log(JSON.stringify(startAsyncReset()));
|
|
11064
|
+
else if (args.includes("--worker")) {
|
|
11489
11065
|
const taskId = args.find((a) => a.startsWith("--task-id="))?.slice(10);
|
|
11490
11066
|
if (!taskId) {
|
|
11491
11067
|
console.error("Error: --task-id=<id> is required for worker");
|
|
11492
11068
|
node_process.default.exit(1);
|
|
11493
11069
|
}
|
|
11494
11070
|
const resultFile = resetResultFile(taskId);
|
|
11495
|
-
const raw =
|
|
11071
|
+
const raw = await fetchCtxViaInnerApi({
|
|
11496
11072
|
populate: planCtxPopulate({ command: "reset" }),
|
|
11497
11073
|
caller,
|
|
11498
11074
|
traceId
|
|
@@ -11516,7 +11092,7 @@ async function main() {
|
|
|
11516
11092
|
return;
|
|
11517
11093
|
}
|
|
11518
11094
|
} else {
|
|
11519
|
-
console.error("Usage: reset --async
|
|
11095
|
+
console.error("Usage: reset --async | reset --worker --task-id=<id>");
|
|
11520
11096
|
node_process.default.exit(1);
|
|
11521
11097
|
}
|
|
11522
11098
|
break;
|
|
@@ -11532,14 +11108,14 @@ async function main() {
|
|
|
11532
11108
|
case "install-openclaw": {
|
|
11533
11109
|
const tag = getPositionalTag(args, "install-openclaw");
|
|
11534
11110
|
if (!tag) {
|
|
11535
|
-
console.error("Usage: install-openclaw <tag> [--
|
|
11111
|
+
console.error("Usage: install-openclaw <tag> [--oss_file_map=<base64>]");
|
|
11536
11112
|
node_process.default.exit(1);
|
|
11537
11113
|
}
|
|
11538
11114
|
const ossFileMapFlag = getFlag(args, "oss_file_map");
|
|
11539
11115
|
let installOssFileMap;
|
|
11540
11116
|
let rawForTelemetry;
|
|
11541
11117
|
if (!ossFileMapFlag) {
|
|
11542
|
-
rawForTelemetry =
|
|
11118
|
+
rawForTelemetry = await fetchCtxViaInnerApi({
|
|
11543
11119
|
populate: planCtxPopulate({ command: "install" }),
|
|
11544
11120
|
caller,
|
|
11545
11121
|
traceId
|
|
@@ -11574,7 +11150,7 @@ async function main() {
|
|
|
11574
11150
|
case "install-extension": {
|
|
11575
11151
|
const tag = getPositionalTag(args, "install-extension");
|
|
11576
11152
|
if (!tag) {
|
|
11577
|
-
console.error("Usage: install-extension <tag> (--all | --extension=<name>...) [--home_base=<dir>] [--config_path=<path>] [--skip-config-update] [--
|
|
11153
|
+
console.error("Usage: install-extension <tag> (--all | --extension=<name>...) [--home_base=<dir>] [--config_path=<path>] [--skip-config-update] [--oss_file_map=<base64>]");
|
|
11578
11154
|
node_process.default.exit(1);
|
|
11579
11155
|
}
|
|
11580
11156
|
const all = args.includes("--all");
|
|
@@ -11586,7 +11162,7 @@ async function main() {
|
|
|
11586
11162
|
let installOssFileMap;
|
|
11587
11163
|
let rawForTelemetry;
|
|
11588
11164
|
if (!ossFileMapFlag) {
|
|
11589
|
-
rawForTelemetry =
|
|
11165
|
+
rawForTelemetry = await fetchCtxViaInnerApi({
|
|
11590
11166
|
populate: planCtxPopulate({ command: "install" }),
|
|
11591
11167
|
caller,
|
|
11592
11168
|
traceId
|
|
@@ -11632,12 +11208,12 @@ async function main() {
|
|
|
11632
11208
|
case "install-cli": {
|
|
11633
11209
|
const tag = getPositionalTag(args, "install-cli");
|
|
11634
11210
|
if (!tag) {
|
|
11635
|
-
console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--
|
|
11211
|
+
console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
|
|
11636
11212
|
node_process.default.exit(1);
|
|
11637
11213
|
}
|
|
11638
11214
|
const names = getMultiFlag(args, "cli");
|
|
11639
11215
|
if (names.length === 0) {
|
|
11640
|
-
console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--
|
|
11216
|
+
console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
|
|
11641
11217
|
node_process.default.exit(1);
|
|
11642
11218
|
}
|
|
11643
11219
|
const homeBase = getFlag(args, "home_base");
|
|
@@ -11645,7 +11221,7 @@ async function main() {
|
|
|
11645
11221
|
let installOssFileMap;
|
|
11646
11222
|
let rawForTelemetry;
|
|
11647
11223
|
if (!ossFileMapFlag) {
|
|
11648
|
-
rawForTelemetry =
|
|
11224
|
+
rawForTelemetry = await fetchCtxViaInnerApi({
|
|
11649
11225
|
populate: planCtxPopulate({ command: "install" }),
|
|
11650
11226
|
caller,
|
|
11651
11227
|
traceId
|
|
@@ -11693,7 +11269,7 @@ async function main() {
|
|
|
11693
11269
|
case "download-resource": {
|
|
11694
11270
|
const tag = getPositionalTag(args, "download-resource");
|
|
11695
11271
|
if (!tag) {
|
|
11696
|
-
console.error("Usage: download-resource <tag> --role=<role> --name=<name> [--dir=<dir>] [--
|
|
11272
|
+
console.error("Usage: download-resource <tag> --role=<role> --name=<name> [--dir=<dir>] [--oss_file_map=<base64>]");
|
|
11697
11273
|
node_process.default.exit(1);
|
|
11698
11274
|
}
|
|
11699
11275
|
const role = getFlag(args, "role");
|
|
@@ -11707,7 +11283,7 @@ async function main() {
|
|
|
11707
11283
|
let installOssFileMap;
|
|
11708
11284
|
let rawForTelemetry;
|
|
11709
11285
|
if (!ossFileMapFlag) {
|
|
11710
|
-
rawForTelemetry =
|
|
11286
|
+
rawForTelemetry = await fetchCtxViaInnerApi({
|
|
11711
11287
|
populate: planCtxPopulate({ command: "install" }),
|
|
11712
11288
|
caller,
|
|
11713
11289
|
traceId
|
|
@@ -11781,50 +11357,6 @@ async function main() {
|
|
|
11781
11357
|
if (!result.ok) node_process.default.exit(1);
|
|
11782
11358
|
break;
|
|
11783
11359
|
}
|
|
11784
|
-
case "upgrade-lark": {
|
|
11785
|
-
const result = runUpgradeLark({
|
|
11786
|
-
runId: rc.runId,
|
|
11787
|
-
scene
|
|
11788
|
-
});
|
|
11789
|
-
const upgradeDurationMs = Date.now() - t0;
|
|
11790
|
-
console.log(JSON.stringify(result));
|
|
11791
|
-
reportUpgradeLarkToSlardar({
|
|
11792
|
-
scene,
|
|
11793
|
-
durationMs: upgradeDurationMs,
|
|
11794
|
-
success: result.ok,
|
|
11795
|
-
logFile: result.logFile,
|
|
11796
|
-
exitCode: result.exitCode,
|
|
11797
|
-
rollbackOk: result.rollbackOk,
|
|
11798
|
-
validationError: result.validationError,
|
|
11799
|
-
error: result.error
|
|
11800
|
-
});
|
|
11801
|
-
try {
|
|
11802
|
-
await reportCliRun({
|
|
11803
|
-
command: "upgrade-lark",
|
|
11804
|
-
runId: rc.runId,
|
|
11805
|
-
version: getVersion(),
|
|
11806
|
-
invocation: args.join(" "),
|
|
11807
|
-
durationMs: upgradeDurationMs,
|
|
11808
|
-
caller: rc.caller,
|
|
11809
|
-
traceId: rc.traceId,
|
|
11810
|
-
success: result.ok,
|
|
11811
|
-
result,
|
|
11812
|
-
error: result.ok ? void 0 : { message: result.error ?? "upgrade-lark failed" }
|
|
11813
|
-
});
|
|
11814
|
-
} catch (e) {
|
|
11815
|
-
console.error(`[telemetry] reportCliRun failed: ${e.message}`);
|
|
11816
|
-
}
|
|
11817
|
-
if (!result.ok) {
|
|
11818
|
-
node_process.default.exitCode = 1;
|
|
11819
|
-
return;
|
|
11820
|
-
}
|
|
11821
|
-
break;
|
|
11822
|
-
}
|
|
11823
|
-
case "channels-probe": {
|
|
11824
|
-
const result = runChannelsProbe(getFlag(args, "timeout") ? Number(getFlag(args, "timeout")) : void 0);
|
|
11825
|
-
console.log(JSON.stringify(result));
|
|
11826
|
-
break;
|
|
11827
|
-
}
|
|
11828
11360
|
default:
|
|
11829
11361
|
node_process.default.stderr.write(`Unknown command: ${mode}\n\n`);
|
|
11830
11362
|
node_process.default.stderr.write(formatTopLevelHelp(helpFlags.expert));
|
package/package.json
CHANGED