@lark-apaas/openclaw-scripts-diagnose-cli 0.1.13 → 0.1.14-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.cjs +218 -187
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -52,7 +52,7 @@ node_assert = __toESM(node_assert);
52
52
  * it terse and parseable.
53
53
  */
54
54
  function getVersion() {
55
- return "0.1.13";
55
+ return "0.1.14-alpha.0";
56
56
  }
57
57
  //#endregion
58
58
  //#region src/rule-engine/base.ts
@@ -3038,7 +3038,7 @@ function extractTarballTolerant(tarball, destDir, opts = {}) {
3038
3038
  }
3039
3039
  //#endregion
3040
3040
  //#region src/rules/feishu-plugin-state-normalize.ts
3041
- const PLUGIN_NAME$2 = "openclaw-lark";
3041
+ const PLUGIN_NAME$1 = "openclaw-lark";
3042
3042
  const BUILTIN_FEISHU = "feishu";
3043
3043
  const LEGACY_PLUGIN_NAME = "feishu-openclaw-plugin";
3044
3044
  const LEGACY_DIRS_TO_REMOVE = [LEGACY_PLUGIN_NAME, BUILTIN_FEISHU];
@@ -3087,7 +3087,7 @@ let FeishuPluginStateNormalizeRule = class FeishuPluginStateNormalizeRule extend
3087
3087
  validate(ctx) {
3088
3088
  if (!isPluginInstalled(ctx)) return { pass: true };
3089
3089
  const fails = [];
3090
- if (!isNewPluginEnabled(ctx.config)) fails.push(`plugins.entries["${PLUGIN_NAME$2}"].enabled !== true(应启用)`);
3090
+ if (!isNewPluginEnabled(ctx.config)) fails.push(`plugins.entries["${PLUGIN_NAME$1}"].enabled !== true(应启用)`);
3091
3091
  if (isBuiltinFeishuEnabled(ctx.config)) fails.push("plugins.entries.feishu.enabled === true(应禁用)");
3092
3092
  if (isTopLevelMissingFeishuTools(ctx.config)) fails.push("tools.alsoAllow 缺 feishu_* tools");
3093
3093
  const legacyResiduals = findLegacyResiduals(ctx);
@@ -3099,7 +3099,7 @@ let FeishuPluginStateNormalizeRule = class FeishuPluginStateNormalizeRule extend
3099
3099
  };
3100
3100
  }
3101
3101
  repair(ctx) {
3102
- setEntryEnabled(ctx.config, PLUGIN_NAME$2, true);
3102
+ setEntryEnabled(ctx.config, PLUGIN_NAME$1, true);
3103
3103
  setEntryEnabled(ctx.config, BUILTIN_FEISHU, false);
3104
3104
  ensureFeishuTools(ctx.config);
3105
3105
  cleanupLegacyResiduals(ctx);
@@ -3113,10 +3113,10 @@ FeishuPluginStateNormalizeRule = __decorate([Rule({
3113
3113
  level: "critical"
3114
3114
  })], FeishuPluginStateNormalizeRule);
3115
3115
  function isPluginInstalled(ctx) {
3116
- return node_fs.default.existsSync(node_path.default.join(getExtensionsDir(ctx.configPath), PLUGIN_NAME$2));
3116
+ return node_fs.default.existsSync(node_path.default.join(getExtensionsDir(ctx.configPath), PLUGIN_NAME$1));
3117
3117
  }
3118
3118
  function isNewPluginEnabled(config) {
3119
- return asRecord(getNestedMap(config, "plugins", "entries")?.[PLUGIN_NAME$2])?.enabled === true;
3119
+ return asRecord(getNestedMap(config, "plugins", "entries")?.[PLUGIN_NAME$1])?.enabled === true;
3120
3120
  }
3121
3121
  function isBuiltinFeishuEnabled(config) {
3122
3122
  return asRecord(getNestedMap(config, "plugins", "entries")?.[BUILTIN_FEISHU])?.enabled === true;
@@ -3169,7 +3169,7 @@ function cleanupLegacyResiduals(ctx) {
3169
3169
  const allow = plugins.allow;
3170
3170
  if (Array.isArray(allow)) {
3171
3171
  for (let i = allow.length - 1; i >= 0; i--) if (allow[i] === LEGACY_PLUGIN_NAME) allow.splice(i, 1);
3172
- if (!allow.includes(PLUGIN_NAME$2)) allow.push(PLUGIN_NAME$2);
3172
+ if (!allow.includes(PLUGIN_NAME$1)) allow.push(PLUGIN_NAME$1);
3173
3173
  }
3174
3174
  }
3175
3175
  const extDir = getExtensionsDir(ctx.configPath);
@@ -3314,7 +3314,7 @@ function findClosestEntry(pluginVersion) {
3314
3314
  }
3315
3315
  //#endregion
3316
3316
  //#region src/rules/feishu-plugin-version-compat.ts
3317
- const PLUGIN_NAME$1 = "openclaw-lark";
3317
+ const PLUGIN_NAME = "openclaw-lark";
3318
3318
  const LEGACY_SHORT_NAMES = ["feishu-openclaw-plugin"];
3319
3319
  const FORK_SCOPES = ["@lark-apaas"];
3320
3320
  /** 特化 fork 版全名:虽免于 VERSION_COMPAT_MAP 检查,仍需 openclaw ≥ 此版本 */
@@ -3483,13 +3483,13 @@ function detectInstalledPlugin(ctx) {
3483
3483
  const allow = Array.isArray(allowRaw) ? allowRaw.filter((e) => typeof e === "string") : [];
3484
3484
  const extDir = getExtensionsDir(ctx.configPath);
3485
3485
  const installs = getNestedMap(ctx.config, "plugins", "installs");
3486
- for (const name of [PLUGIN_NAME$1, ...LEGACY_SHORT_NAMES]) {
3486
+ for (const name of [PLUGIN_NAME, ...LEGACY_SHORT_NAMES]) {
3487
3487
  if (!allow.includes(name)) continue;
3488
3488
  const pkgPath = node_path.default.join(extDir, name, "package.json");
3489
3489
  if (!node_fs.default.existsSync(pkgPath)) continue;
3490
3490
  const pkg = readPluginPackageJson(pkgPath) ?? {};
3491
3491
  const installEntry = installs && asRecord(installs[name]);
3492
- const fullName = pkg.name ?? extractScopedNameFromSpec$1(installEntry?.spec);
3492
+ const fullName = pkg.name ?? extractScopedNameFromSpec(installEntry?.spec);
3493
3493
  return {
3494
3494
  allowName: name,
3495
3495
  fullName,
@@ -3500,7 +3500,7 @@ function detectInstalledPlugin(ctx) {
3500
3500
  return null;
3501
3501
  }
3502
3502
  /** "@scope/name@1.2.3" / "name@1.2.3" / "@scope/name" / "name" → 去掉 @version 后缀 */
3503
- function extractScopedNameFromSpec$1(spec) {
3503
+ function extractScopedNameFromSpec(spec) {
3504
3504
  if (typeof spec !== "string") return void 0;
3505
3505
  const at = spec.indexOf("@", 1);
3506
3506
  return at === -1 ? spec : spec.slice(0, at);
@@ -3555,100 +3555,110 @@ CleanupInstallBackupDirsRule = __decorate([Rule({
3555
3555
  level: "critical"
3556
3556
  })], CleanupInstallBackupDirsRule);
3557
3557
  //#endregion
3558
- //#region src/rules/lark-cli-missing-for-installed-lark-plugin.ts
3559
- const PLUGIN_NAME = "openclaw-lark";
3560
- const FORK_PACKAGE_NAME = "@lark-apaas/openclaw-lark";
3561
- const TARGET_VERSION = "2026.4.4";
3562
- const LARK_CLI_NAME$1 = "lark-cli";
3563
- function readInstalledLarkPlugin(ctx) {
3564
- const pkgPath = node_path.default.join(getExtensionsDir(ctx.configPath), PLUGIN_NAME, "package.json");
3565
- if (!node_fs.default.existsSync(pkgPath)) return null;
3566
- let pkg = {};
3567
- try {
3568
- const parsed = JSON.parse(node_fs.default.readFileSync(pkgPath, "utf-8"));
3569
- pkg = {
3570
- name: typeof parsed.name === "string" ? parsed.name : void 0,
3571
- version: typeof parsed.version === "string" ? parsed.version : void 0
3572
- };
3573
- } catch {
3574
- pkg = {};
3575
- }
3576
- const installs = getNestedMap(ctx.config, "plugins", "installs");
3577
- const installEntry = installs ? asRecord(installs[PLUGIN_NAME]) : void 0;
3578
- return {
3579
- name: pkg.name ?? extractScopedNameFromSpec(installEntry?.spec),
3580
- version: pkg.version ?? (typeof installEntry?.version === "string" ? installEntry.version : void 0)
3581
- };
3582
- }
3583
- function isTargetForkPlugin(plugin) {
3584
- return plugin?.name === FORK_PACKAGE_NAME && plugin.version === TARGET_VERSION;
3585
- }
3586
- function extractScopedNameFromSpec(spec) {
3587
- if (typeof spec !== "string") return void 0;
3588
- const at = spec.indexOf("@", 1);
3589
- return at === -1 ? spec : spec.slice(0, at);
3590
- }
3591
- function isLarkCliAvailable$1() {
3592
- try {
3593
- return (0, node_child_process.spawnSync)(LARK_CLI_NAME$1, ["--version"], {
3594
- encoding: "utf-8",
3595
- timeout: 5e3,
3596
- stdio: [
3597
- "ignore",
3598
- "pipe",
3599
- "ignore"
3600
- ]
3601
- }).status === 0;
3602
- } catch {
3603
- return false;
3604
- }
3605
- }
3606
- function installLarkCliOnce(tag) {
3607
- const entry = process.argv[1];
3608
- if (!entry) throw new Error("cannot resolve diagnose-cli entrypoint for lark-cli install");
3609
- const res = (0, node_child_process.spawnSync)(process.execPath, [
3610
- entry,
3611
- "install-cli",
3612
- tag,
3613
- "--cli=lark-cli"
3614
- ], {
3615
- encoding: "utf-8",
3616
- stdio: [
3617
- "ignore",
3618
- "pipe",
3619
- "pipe"
3620
- ]
3621
- });
3622
- const stdout = res.stdout?.trim();
3623
- const stderr = res.stderr?.trim();
3624
- if (stdout) console.error(`[lark-cli-missing] install-cli stdout: ${stdout}`);
3625
- if (stderr) console.error(`[lark-cli-missing] install-cli stderr: ${stderr}`);
3626
- if (res.error) throw new Error(`install-cli spawn error: ${res.error.message}`);
3627
- if (res.status !== 0) throw new Error(`install-cli exited with code ${res.status ?? "unknown"}`);
3628
- }
3629
- let LarkCliMissingForInstalledLarkPluginRule = class LarkCliMissingForInstalledLarkPluginRule extends DiagnoseRule {
3558
+ //#region src/rules/feishu-bot-channel-config.ts
3559
+ /**
3560
+ * Ensures each bot account's channel config is correct:
3561
+ * 1. `allowFrom` contains its own `creatorOpenID` from larkApps
3562
+ * 2. `appSecret` is either the canonical provider-ref or matches larkApps plaintext
3563
+ *
3564
+ * Covers both multi-account (channels.feishu.accounts) and single-account
3565
+ * (channels.feishu.appId + allowFrom at top level) layouts.
3566
+ */
3567
+ let FeishuBotChannelConfigRule = class FeishuBotChannelConfigRule extends DiagnoseRule {
3630
3568
  validate(ctx) {
3631
- if (!isTargetForkPlugin(readInstalledLarkPlugin(ctx))) return { pass: true };
3632
- if (isLarkCliAvailable$1()) return { pass: true };
3569
+ const larkApps = ctx.vars.larkApps;
3570
+ if (!larkApps || larkApps.length === 0) return { pass: true };
3571
+ const feishu = asRecord(getNestedMap(ctx.config, "channels", "feishu"));
3572
+ if (!feishu) return { pass: true };
3573
+ const issues = [];
3574
+ const accounts = asRecord(feishu.accounts);
3575
+ if (accounts) for (const [accountId, account] of Object.entries(accounts)) {
3576
+ const bot = asRecord(account);
3577
+ if (!bot) continue;
3578
+ const appId = bot.appId;
3579
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
3580
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
3581
+ if (!larkApp) continue;
3582
+ this.checkBot(accountId, bot, larkApp, issues);
3583
+ }
3584
+ const singleAppId = feishu.appId;
3585
+ if (typeof singleAppId === "string" && singleAppId.startsWith("cli_") && !accounts) {
3586
+ const larkApp = larkApps.find((e) => e.larkAppID === singleAppId);
3587
+ if (larkApp) this.checkBot("feishu", feishu, larkApp, issues);
3588
+ }
3589
+ if (issues.length === 0) return { pass: true };
3633
3590
  return {
3634
3591
  pass: false,
3635
- message: `${FORK_PACKAGE_NAME}@${TARGET_VERSION} 已安装,但 lark-cli 不可用;将执行一次 lark-cli 安装`
3592
+ message: issues.join("; ")
3636
3593
  };
3637
3594
  }
3595
+ /** Check a single bot entry (either an account object or the feishu channel itself). */
3596
+ checkBot(label, bot, larkApp, issues) {
3597
+ const creatorOpenID = larkApp.creatorOpenID;
3598
+ const allowFrom = Array.isArray(bot.allowFrom) ? bot.allowFrom : [];
3599
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
3600
+ if (!allowFrom.includes(creatorOpenID)) issues.push(`${label} allowFrom missing creatorOpenID ${creatorOpenID.length > 8 ? creatorOpenID.slice(0, 4) + "***" + creatorOpenID.slice(-4) : "***"}`);
3601
+ } else if (allowFrom.length === 0) issues.push(`${label} allowFrom is empty (creatorOpenID unavailable, cannot auto-fix)`);
3602
+ const secret = bot.appSecret;
3603
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
3604
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) issues.push(`${label} appSecret is a provider-ref but not the canonical one`);
3605
+ } else if (typeof secret === "string") {
3606
+ if (secret !== larkApp.appSecret) issues.push(`${label} appSecret plaintext mismatch`);
3607
+ } else issues.push(`${label} appSecret has unexpected type`);
3608
+ }
3638
3609
  repair(ctx) {
3639
- if (!isTargetForkPlugin(readInstalledLarkPlugin(ctx))) return;
3640
- if (isLarkCliAvailable$1()) return;
3641
- installLarkCliOnce(ctx.vars.recommendedOpenclawTag ?? TARGET_VERSION);
3610
+ const larkApps = ctx.vars.larkApps;
3611
+ if (!larkApps || larkApps.length === 0) return;
3612
+ const feishu = asRecord(getNestedMap(ctx.config, "channels", "feishu"));
3613
+ if (!feishu) return;
3614
+ const accounts = asRecord(feishu.accounts);
3615
+ if (accounts) for (const [, account] of Object.entries(accounts)) {
3616
+ const bot = asRecord(account);
3617
+ if (!bot) continue;
3618
+ const appId = bot.appId;
3619
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
3620
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
3621
+ if (!larkApp) continue;
3622
+ this.fixBot(bot, larkApp);
3623
+ }
3624
+ const singleAppId = feishu.appId;
3625
+ if (typeof singleAppId === "string" && singleAppId.startsWith("cli_") && !accounts) {
3626
+ const larkApp = larkApps.find((e) => e.larkAppID === singleAppId);
3627
+ if (larkApp) this.fixBot(feishu, larkApp);
3628
+ }
3629
+ }
3630
+ /** Fix a single bot entry in-place. */
3631
+ fixBot(bot, larkApp) {
3632
+ const creatorOpenID = larkApp.creatorOpenID;
3633
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
3634
+ const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
3635
+ if (!allowFrom.includes(creatorOpenID)) {
3636
+ allowFrom.push(creatorOpenID);
3637
+ bot.allowFrom = allowFrom;
3638
+ }
3639
+ }
3640
+ const secret = bot.appSecret;
3641
+ let needsFix = false;
3642
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
3643
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
3644
+ } else if (typeof secret === "string") {
3645
+ if (secret !== larkApp.appSecret) needsFix = true;
3646
+ } else needsFix = true;
3647
+ if (needsFix) bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
3642
3648
  }
3643
3649
  };
3644
- LarkCliMissingForInstalledLarkPluginRule = __decorate([Rule({
3645
- key: "lark_cli_missing_for_installed_lark_plugin",
3646
- description: "检测特定飞书插件版本已安装但 lark-cli 缺失的环境,并自动安装 lark-cli 一次",
3647
- dependsOn: ["config_syntax_check"],
3650
+ FeishuBotChannelConfigRule = __decorate([Rule({
3651
+ key: "feishu_bot_channel_config",
3652
+ description: "确保飞书配置中 bot 账号的 allowFrom 包含其创建者 openID 且 appSecret 值正确",
3653
+ dependsOn: [
3654
+ "config_syntax_check",
3655
+ "feishu_default_account",
3656
+ "feishu_bot_id"
3657
+ ],
3648
3658
  repairMode: "standard",
3649
- level: "critical",
3650
- usesVars: ["recommendedOpenclawTag"]
3651
- })], LarkCliMissingForInstalledLarkPluginRule);
3659
+ usesVars: ["larkApps"],
3660
+ level: "critical"
3661
+ })], FeishuBotChannelConfigRule);
3652
3662
  //#endregion
3653
3663
  //#region src/check.ts
3654
3664
  /** Telemetry-aware entry: returns both the legacy CheckResult (for stdout)
@@ -4249,9 +4259,10 @@ function makeLogger(logFile) {
4249
4259
  /**
4250
4260
  * Start an async reset task: spawn a detached child process and return the taskId.
4251
4261
  *
4252
- * The child process runs: node cli.js reset --worker --task-id=xxx --ctx=base64
4262
+ * The child process runs: node cli.js reset --worker --task-id=xxx
4263
+ * The worker fetches ctx from innerApi itself — no --ctx passthrough.
4253
4264
  */
4254
- function startAsyncReset(ctxBase64) {
4265
+ function startAsyncReset() {
4255
4266
  const taskId = (0, node_crypto.randomUUID)();
4256
4267
  const resultFile = resetResultFile(taskId);
4257
4268
  const log = makeLogger(resetLogFile(taskId));
@@ -4275,8 +4286,7 @@ function startAsyncReset(ctxBase64) {
4275
4286
  process.argv[1],
4276
4287
  "reset",
4277
4288
  "--worker",
4278
- `--task-id=${taskId}`,
4279
- `--ctx=${ctxBase64}`
4289
+ `--task-id=${taskId}`
4280
4290
  ], {
4281
4291
  detached: true,
4282
4292
  stdio: "ignore",
@@ -6790,6 +6800,60 @@ function mergeCoreBackupAndOrigins(configPath, vars, resetData, log) {
6790
6800
  log(`allowedOrigins: added ${added.length} (${JSON.stringify(added)}), total now ${mergedOrigins.length}`);
6791
6801
  }
6792
6802
  /**
6803
+ * Fix bot account allowFrom and appSecret using larkApps from innerApi.
6804
+ *
6805
+ * For each bot account (key starts with `bot-cli_`):
6806
+ * - allowFrom must contain the bot's own creatorOpenID from larkApps
6807
+ * - appSecret must be either the canonical provider-ref or match larkApps plaintext
6808
+ *
6809
+ * Runs after mergeCoreBackupAndOrigins so it operates on the final config state.
6810
+ */
6811
+ function fixBotChannelConfig(configPath, larkApps, log) {
6812
+ if (!larkApps || larkApps.length === 0) {
6813
+ log("no larkApps data, skip bot channel config fix");
6814
+ return;
6815
+ }
6816
+ const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
6817
+ const accounts = asRecord(getNestedMap(config, "channels", "feishu")?.accounts);
6818
+ if (!accounts) {
6819
+ log("no feishu accounts in config, skip bot channel config fix");
6820
+ return;
6821
+ }
6822
+ let fixCount = 0;
6823
+ for (const [, account] of Object.entries(accounts)) {
6824
+ const bot = asRecord(account);
6825
+ if (!bot) continue;
6826
+ const appId = bot.appId;
6827
+ if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
6828
+ const larkApp = larkApps.find((e) => e.larkAppID === appId);
6829
+ if (!larkApp) continue;
6830
+ const creatorOpenID = larkApp.creatorOpenID;
6831
+ if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
6832
+ const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
6833
+ if (!allowFrom.includes(creatorOpenID)) {
6834
+ allowFrom.push(creatorOpenID);
6835
+ bot.allowFrom = allowFrom;
6836
+ fixCount++;
6837
+ }
6838
+ }
6839
+ const secret = bot.appSecret;
6840
+ let needsFix = false;
6841
+ if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
6842
+ if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
6843
+ } else if (typeof secret === "string") {
6844
+ if (secret !== larkApp.appSecret) needsFix = true;
6845
+ } else needsFix = true;
6846
+ if (needsFix) {
6847
+ bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
6848
+ fixCount++;
6849
+ }
6850
+ }
6851
+ if (fixCount > 0) {
6852
+ node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
6853
+ log(`fixed ${fixCount} bot channel config issue(s) (allowFrom/appSecret)`);
6854
+ } else log("bot channel config ok, no fixes needed");
6855
+ }
6856
+ /**
6793
6857
  * Step 7: Verify startup scripts landed in configDir/scripts/.
6794
6858
  *
6795
6859
  * Scripts are extracted directly to configDir/scripts/ during stageTemplate —
@@ -6934,6 +6998,7 @@ async function runReset(input, taskId, resultFile) {
6934
6998
  await step5InstallOpenclaw(openclawTag, ossFileMap, log);
6935
6999
  step(6);
6936
7000
  mergeCoreBackupAndOrigins(configPath, vars, resetData, log);
7001
+ fixBotChannelConfig(configPath, vars.larkApps, log);
6937
7002
  step(7);
6938
7003
  verifyStartupScripts(configDir, log);
6939
7004
  step(8);
@@ -7732,7 +7797,8 @@ function normalizeCtx(raw) {
7732
7797
  reset: {
7733
7798
  templateVars: r.reset.templateVars ?? {},
7734
7799
  coreBackup: r.reset.coreBackup
7735
- }
7800
+ },
7801
+ larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7736
7802
  };
7737
7803
  }
7738
7804
  const vars = r.vars ?? {};
@@ -7757,7 +7823,8 @@ function normalizeCtx(raw) {
7757
7823
  reset: {
7758
7824
  templateVars: resetData.templateVars ?? {},
7759
7825
  coreBackup: resetData.coreBackup
7760
- }
7826
+ },
7827
+ larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7761
7828
  };
7762
7829
  }
7763
7830
  function fillApp(src) {
@@ -7822,7 +7889,8 @@ function buildCheckInput(raw, configPathOverride) {
7822
7889
  providerFilePath: PROVIDER_FILE_PATH,
7823
7890
  secretsFilePath: SECRETS_FILE_PATH,
7824
7891
  templateVars: ctx.app.templateVars,
7825
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7892
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7893
+ larkApps: ctx.larkApps
7826
7894
  },
7827
7895
  templateVars: ctx.app.templateVars
7828
7896
  };
@@ -7854,7 +7922,8 @@ function buildRepairInput(raw, configPathOverride) {
7854
7922
  providerFilePath: PROVIDER_FILE_PATH,
7855
7923
  secretsFilePath: SECRETS_FILE_PATH,
7856
7924
  templateVars: ctx.app.templateVars,
7857
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7925
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7926
+ larkApps: ctx.larkApps
7858
7927
  },
7859
7928
  repairData: {
7860
7929
  secretsContent: ctx.secrets.secretsContent,
@@ -7890,7 +7959,8 @@ function buildResetInput(raw, configPathOverride) {
7890
7959
  providerFilePath: PROVIDER_FILE_PATH,
7891
7960
  secretsFilePath: SECRETS_FILE_PATH,
7892
7961
  templateVars: ctx.app.templateVars,
7893
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7962
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7963
+ larkApps: ctx.larkApps
7894
7964
  },
7895
7965
  resetData: {
7896
7966
  templateVars: ctx.reset.templateVars,
@@ -10200,7 +10270,7 @@ async function reportCliRun(opts) {
10200
10270
  //#region src/help.ts
10201
10271
  const BIN = "mclaw-diagnose";
10202
10272
  function versionBanner() {
10203
- return `v0.1.13`;
10273
+ return `v0.1.14-alpha.0`;
10204
10274
  }
10205
10275
  const COMMANDS = [
10206
10276
  {
@@ -10304,16 +10374,12 @@ EXIT CODES
10304
10374
  hidden: true,
10305
10375
  summary: "Run rule-engine check only",
10306
10376
  help: `USAGE
10307
- ${BIN} check [--ctx=<base64>]
10377
+ ${BIN} check
10308
10378
 
10309
10379
  DESCRIPTION
10310
10380
  Runs the rule engine against the sandbox's current openclaw config and
10311
- returns { failedRules }. Used by sandbox_console's push-style callers
10312
- that already own the ctx — end-users should prefer \`doctor\`.
10313
-
10314
- OPTIONS
10315
- --ctx=<base64> Opaque ctx JSON (base64). When absent, fetched from
10316
- innerapi (same path as doctor).
10381
+ returns { failedRules }. Ctx is fetched from innerapi automatically.
10382
+ End-users should prefer \`doctor\`.
10317
10383
  `
10318
10384
  },
10319
10385
  {
@@ -10321,16 +10387,11 @@ OPTIONS
10321
10387
  hidden: true,
10322
10388
  summary: "Apply standard-mode repairs",
10323
10389
  help: `USAGE
10324
- ${BIN} repair [--ctx=<base64>]
10390
+ ${BIN} repair
10325
10391
 
10326
10392
  DESCRIPTION
10327
- Runs repair for the failing rules listed inside the ctx's repairData.
10328
- Intended for sandbox_console's push path — end-users should use
10329
- \`doctor --fix\` instead.
10330
-
10331
- OPTIONS
10332
- --ctx=<base64> Opaque ctx JSON (base64). When absent, fetched from
10333
- innerapi.
10393
+ Runs repair for the failing rules. Ctx is fetched from innerapi
10394
+ automatically. End-users should use \`doctor --fix\` instead.
10334
10395
  `
10335
10396
  },
10336
10397
  {
@@ -10338,14 +10399,15 @@ OPTIONS
10338
10399
  hidden: true,
10339
10400
  summary: "Re-initialize sandbox via the 9-step reset pipeline",
10340
10401
  help: `USAGE
10341
- ${BIN} reset --async [--ctx=<base64>]
10342
- ${BIN} reset --worker --task-id=<id> [--ctx=<base64>]
10402
+ ${BIN} reset --async
10403
+ ${BIN} reset --worker --task-id=<id>
10343
10404
 
10344
10405
  DESCRIPTION
10345
10406
  Two-phase pipeline driven asynchronously: the --async invocation spawns
10346
10407
  a detached worker and returns { taskId } immediately; the --worker
10347
10408
  invocation (spawned by --async) runs the actual 9 steps and writes
10348
10409
  progress to /tmp/openclaw-diagnose/reset-<taskId>.json.
10410
+ Ctx is fetched from innerapi automatically.
10349
10411
 
10350
10412
  Poll progress with \`${BIN} get_reset_task --task-id=<id>\`.
10351
10413
 
@@ -10353,7 +10415,6 @@ OPTIONS
10353
10415
  --async Start a detached worker and return taskId on stdout.
10354
10416
  --worker Internal — run the 9-step pipeline (launched by --async).
10355
10417
  --task-id=<id> Required with --worker; identifies the progress file.
10356
- --ctx=<base64> Opaque ctx JSON; fetched from innerapi when absent.
10357
10418
  `
10358
10419
  },
10359
10420
  {
@@ -10376,7 +10437,7 @@ OPTIONS
10376
10437
  hidden: true,
10377
10438
  summary: "Download + install the openclaw tarball",
10378
10439
  help: `USAGE
10379
- ${BIN} install-openclaw <tag> [--ctx=<base64> | --oss_file_map=<base64>]
10440
+ ${BIN} install-openclaw <tag> [--oss_file_map=<base64>]
10380
10441
 
10381
10442
  DESCRIPTION
10382
10443
  Downloads the openclaw@<tag> tgz via the signed OSS URL found in the
@@ -10388,9 +10449,9 @@ ARGUMENTS
10388
10449
  <tag> Openclaw version tag, e.g. 2026.4.11.
10389
10450
 
10390
10451
  OPTIONS
10391
- --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10392
10452
  --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi
10393
- entirely. Wins over --ctx when both provided.
10453
+ entirely. When absent, ossFileMap is fetched from
10454
+ innerapi automatically.
10394
10455
  `
10395
10456
  },
10396
10457
  {
@@ -10416,8 +10477,7 @@ OPTIONS
10416
10477
  --home_base=<dir> Override the /home/gem base (tests).
10417
10478
  --config_path=<p> Override the openclaw.json path (tests).
10418
10479
  --skip-config-update Leave plugins.installs in openclaw.json untouched.
10419
- --ctx=<base64> Opaque ctx; see install-openclaw for semantics.
10420
- --oss_file_map=... Pre-built OSS URL map (base64 JSON).
10480
+ --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10421
10481
  `
10422
10482
  },
10423
10483
  {
@@ -10444,7 +10504,6 @@ OPTIONS
10444
10504
  --cli=<name> CLI package to install by short name or scoped
10445
10505
  packageName (repeatable, at least one required).
10446
10506
  --home_base=<dir> Override the /home/gem base (tests).
10447
- --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10448
10507
  --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10449
10508
 
10450
10509
  EXAMPLES
@@ -10551,8 +10610,7 @@ OPTIONS
10551
10610
  --role=<role> Package role (e.g. template, config).
10552
10611
  --name=<name> Package name within the role.
10553
10612
  --dir=<dir> Target dir (defaults to dirname(pkg.installPath)).
10554
- --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10555
- --oss_file_map=... Pre-built OSS URL map (base64 JSON).
10613
+ --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10556
10614
  `
10557
10615
  }
10558
10616
  ];
@@ -10628,31 +10686,31 @@ function planVarsFields(opts = {}) {
10628
10686
  *
10629
10687
  * Per-command group needs:
10630
10688
  *
10631
- * doctor / check app (rule-driven)
10632
- * repair app + secrets (writes secretsContent / providerKeyContent)
10633
- * reset app + secrets + install + reset (the works)
10689
+ * doctor / check app + larkApps
10690
+ * repair app + secrets + larkApps
10691
+ * reset app + secrets + install + reset + larkApps
10634
10692
  * install-* install only
10635
10693
  *
10636
10694
  * Empty result (`{}`) means "no group needed" — the CLI can skip the
10637
10695
  * `fetchCtxViaInnerApi` call entirely and run with a synthetic empty ctx.
10638
- * Happens e.g. when the user pinned `--rule=<key>` to a vars-free rule on
10639
- * `doctor`.
10640
10696
  */
10641
10697
  function planCtxPopulate(opts) {
10642
10698
  if (opts.command === "install") return { install: true };
10643
10699
  const populate = {};
10644
- const appFields = planVarsFields({
10700
+ if (planVarsFields({
10645
10701
  disabled: opts.disabled,
10646
10702
  onlyRules: opts.onlyRules,
10647
10703
  profile: opts.profile
10648
- });
10649
- if (appFields.length > 0) populate.app = appFields;
10650
- if (opts.command === "repair") populate.secrets = true;
10651
- else if (opts.command === "reset") {
10704
+ }).length > 0) populate.app = true;
10705
+ if (opts.command === "repair") {
10706
+ populate.secrets = true;
10707
+ populate.larkApps = true;
10708
+ } else if (opts.command === "reset") {
10652
10709
  populate.secrets = true;
10653
10710
  populate.install = true;
10654
10711
  populate.reset = true;
10655
- }
10712
+ populate.larkApps = true;
10713
+ } else if (opts.command === "doctor" || opts.command === "check") populate.larkApps = true;
10656
10714
  return populate;
10657
10715
  }
10658
10716
  //#endregion
@@ -10711,21 +10769,6 @@ function reportDoctorRunToSlardar(opts) {
10711
10769
  const args = node_process.default.argv.slice(2);
10712
10770
  const mode = args.find((a) => !a.startsWith("-"));
10713
10771
  /**
10714
- * Decode `--ctx=<base64>` into an opaque JSON object. Returns undefined when
10715
- * the flag isn't present — the caller decides whether to fall back to the
10716
- * innerapi or to error out.
10717
- *
10718
- * The object's shape is not enforced here; downstream code consumes it via
10719
- * either `normalizeCtx()` (new path) or direct field access for the legacy
10720
- * check/repair/reset contract still used by sandbox_console push.
10721
- */
10722
- function parseCtxFlag(args) {
10723
- const ctxArg = args.find((a) => a.startsWith("--ctx="));
10724
- if (!ctxArg) return void 0;
10725
- const b64 = ctxArg.slice(6);
10726
- return JSON.parse(Buffer.from(b64, "base64").toString("utf-8"));
10727
- }
10728
- /**
10729
10772
  * Pull the first non-flag positional after the mode name.
10730
10773
  * (The mode itself is args[0] in the filtered set, so we skip index 0.)
10731
10774
  */
@@ -10753,8 +10796,8 @@ function getMultiFlag(args, name) {
10753
10796
  * case but is no longer consulted.
10754
10797
  */
10755
10798
  async function reportRun(command, rc, _raw, invocation, durationMs, outcome, slardar = {
10756
- scene,
10757
- profile,
10799
+ scene: void 0,
10800
+ profile: "standard",
10758
10801
  fix: false
10759
10802
  }) {
10760
10803
  console.error(`${command}: telemetry calling report_cli_run`);
@@ -10818,7 +10861,7 @@ async function main() {
10818
10861
  console.error(`${mode}: begin argv=[${args.join(" ")}] version=${getVersion()} traceId=${traceId ?? "-"} caller=${caller ?? "-"} runIdGenerated=${rc.generated}`);
10819
10862
  switch (mode) {
10820
10863
  case "check": {
10821
- const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10864
+ const raw = await fetchCtxViaInnerApi({
10822
10865
  populate: planCtxPopulate({
10823
10866
  command: "check",
10824
10867
  profile
@@ -10843,7 +10886,7 @@ async function main() {
10843
10886
  break;
10844
10887
  }
10845
10888
  case "repair": {
10846
- const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10889
+ const raw = await fetchCtxViaInnerApi({
10847
10890
  populate: planCtxPopulate({
10848
10891
  command: "repair",
10849
10892
  profile
@@ -10914,27 +10957,15 @@ async function main() {
10914
10957
  break;
10915
10958
  }
10916
10959
  case "reset":
10917
- if (args.includes("--async")) {
10918
- const ctxArg = args.find((a) => a.startsWith("--ctx="));
10919
- let ctxBase64;
10920
- if (ctxArg) ctxBase64 = ctxArg.slice(6);
10921
- else {
10922
- const fetched = await fetchCtxViaInnerApi({
10923
- populate: planCtxPopulate({ command: "reset" }),
10924
- caller,
10925
- traceId
10926
- });
10927
- ctxBase64 = Buffer.from(JSON.stringify(fetched), "utf-8").toString("base64");
10928
- }
10929
- console.log(JSON.stringify(startAsyncReset(ctxBase64)));
10930
- } else if (args.includes("--worker")) {
10960
+ if (args.includes("--async")) console.log(JSON.stringify(startAsyncReset()));
10961
+ else if (args.includes("--worker")) {
10931
10962
  const taskId = args.find((a) => a.startsWith("--task-id="))?.slice(10);
10932
10963
  if (!taskId) {
10933
10964
  console.error("Error: --task-id=<id> is required for worker");
10934
10965
  node_process.default.exit(1);
10935
10966
  }
10936
10967
  const resultFile = resetResultFile(taskId);
10937
- const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10968
+ const raw = await fetchCtxViaInnerApi({
10938
10969
  populate: planCtxPopulate({ command: "reset" }),
10939
10970
  caller,
10940
10971
  traceId
@@ -10958,7 +10989,7 @@ async function main() {
10958
10989
  return;
10959
10990
  }
10960
10991
  } else {
10961
- console.error("Usage: reset --async [--ctx=<base64>] | reset --worker --task-id=<id> [--ctx=<base64>]");
10992
+ console.error("Usage: reset --async | reset --worker --task-id=<id>");
10962
10993
  node_process.default.exit(1);
10963
10994
  }
10964
10995
  break;
@@ -10974,14 +11005,14 @@ async function main() {
10974
11005
  case "install-openclaw": {
10975
11006
  const tag = getPositionalTag(args, "install-openclaw");
10976
11007
  if (!tag) {
10977
- console.error("Usage: install-openclaw <tag> [--ctx=<base64> | --oss_file_map=<base64>]");
11008
+ console.error("Usage: install-openclaw <tag> [--oss_file_map=<base64>]");
10978
11009
  node_process.default.exit(1);
10979
11010
  }
10980
11011
  const ossFileMapFlag = getFlag(args, "oss_file_map");
10981
11012
  let installOssFileMap;
10982
11013
  let rawForTelemetry;
10983
11014
  if (!ossFileMapFlag) {
10984
- rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11015
+ rawForTelemetry = await fetchCtxViaInnerApi({
10985
11016
  populate: planCtxPopulate({ command: "install" }),
10986
11017
  caller,
10987
11018
  traceId
@@ -11016,7 +11047,7 @@ async function main() {
11016
11047
  case "install-extension": {
11017
11048
  const tag = getPositionalTag(args, "install-extension");
11018
11049
  if (!tag) {
11019
- console.error("Usage: install-extension <tag> (--all | --extension=<name>...) [--home_base=<dir>] [--config_path=<path>] [--skip-config-update] [--ctx=<base64> | --oss_file_map=<base64>]");
11050
+ console.error("Usage: install-extension <tag> (--all | --extension=<name>...) [--home_base=<dir>] [--config_path=<path>] [--skip-config-update] [--oss_file_map=<base64>]");
11020
11051
  node_process.default.exit(1);
11021
11052
  }
11022
11053
  const all = args.includes("--all");
@@ -11028,7 +11059,7 @@ async function main() {
11028
11059
  let installOssFileMap;
11029
11060
  let rawForTelemetry;
11030
11061
  if (!ossFileMapFlag) {
11031
- rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11062
+ rawForTelemetry = await fetchCtxViaInnerApi({
11032
11063
  populate: planCtxPopulate({ command: "install" }),
11033
11064
  caller,
11034
11065
  traceId
@@ -11074,12 +11105,12 @@ async function main() {
11074
11105
  case "install-cli": {
11075
11106
  const tag = getPositionalTag(args, "install-cli");
11076
11107
  if (!tag) {
11077
- console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--ctx=<base64> | --oss_file_map=<base64>]");
11108
+ console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
11078
11109
  node_process.default.exit(1);
11079
11110
  }
11080
11111
  const names = getMultiFlag(args, "cli");
11081
11112
  if (names.length === 0) {
11082
- console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--ctx=<base64> | --oss_file_map=<base64>]");
11113
+ console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
11083
11114
  node_process.default.exit(1);
11084
11115
  }
11085
11116
  const homeBase = getFlag(args, "home_base");
@@ -11087,7 +11118,7 @@ async function main() {
11087
11118
  let installOssFileMap;
11088
11119
  let rawForTelemetry;
11089
11120
  if (!ossFileMapFlag) {
11090
- rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11121
+ rawForTelemetry = await fetchCtxViaInnerApi({
11091
11122
  populate: planCtxPopulate({ command: "install" }),
11092
11123
  caller,
11093
11124
  traceId
@@ -11135,7 +11166,7 @@ async function main() {
11135
11166
  case "download-resource": {
11136
11167
  const tag = getPositionalTag(args, "download-resource");
11137
11168
  if (!tag) {
11138
- console.error("Usage: download-resource <tag> --role=<role> --name=<name> [--dir=<dir>] [--ctx=<base64> | --oss_file_map=<base64>]");
11169
+ console.error("Usage: download-resource <tag> --role=<role> --name=<name> [--dir=<dir>] [--oss_file_map=<base64>]");
11139
11170
  node_process.default.exit(1);
11140
11171
  }
11141
11172
  const role = getFlag(args, "role");
@@ -11149,7 +11180,7 @@ async function main() {
11149
11180
  let installOssFileMap;
11150
11181
  let rawForTelemetry;
11151
11182
  if (!ossFileMapFlag) {
11152
- rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11183
+ rawForTelemetry = await fetchCtxViaInnerApi({
11153
11184
  populate: planCtxPopulate({ command: "install" }),
11154
11185
  caller,
11155
11186
  traceId
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/openclaw-scripts-diagnose-cli",
3
- "version": "0.1.13",
3
+ "version": "0.1.14-alpha.0",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {