@lark-apaas/openclaw-scripts-diagnose-cli 0.1.13-alpha.8 → 0.1.13-alpha.9

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 +176 -227
  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-alpha.8";
55
+ return "0.1.13-alpha.9";
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$1 = "openclaw-lark";
3041
+ const PLUGIN_NAME$2 = "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$1}"].enabled !== true(应启用)`);
3090
+ if (!isNewPluginEnabled(ctx.config)) fails.push(`plugins.entries["${PLUGIN_NAME$2}"].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$1, true);
3102
+ setEntryEnabled(ctx.config, PLUGIN_NAME$2, 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$1));
3116
+ return node_fs.default.existsSync(node_path.default.join(getExtensionsDir(ctx.configPath), PLUGIN_NAME$2));
3117
3117
  }
3118
3118
  function isNewPluginEnabled(config) {
3119
- return asRecord(getNestedMap(config, "plugins", "entries")?.[PLUGIN_NAME$1])?.enabled === true;
3119
+ return asRecord(getNestedMap(config, "plugins", "entries")?.[PLUGIN_NAME$2])?.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$1)) allow.push(PLUGIN_NAME$1);
3172
+ if (!allow.includes(PLUGIN_NAME$2)) allow.push(PLUGIN_NAME$2);
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 = "openclaw-lark";
3317
+ const PLUGIN_NAME$1 = "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, ...LEGACY_SHORT_NAMES]) {
3486
+ for (const name of [PLUGIN_NAME$1, ...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(installEntry?.spec);
3492
+ const fullName = pkg.name ?? extractScopedNameFromSpec$1(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(spec) {
3503
+ function extractScopedNameFromSpec$1(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,110 +3555,100 @@ CleanupInstallBackupDirsRule = __decorate([Rule({
3555
3555
  level: "critical"
3556
3556
  })], CleanupInstallBackupDirsRule);
3557
3557
  //#endregion
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 {
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 {
3568
3630
  validate(ctx) {
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 };
3631
+ if (!isTargetForkPlugin(readInstalledLarkPlugin(ctx))) return { pass: true };
3632
+ if (isLarkCliAvailable$1()) return { pass: true };
3590
3633
  return {
3591
3634
  pass: false,
3592
- message: issues.join("; ")
3635
+ message: `${FORK_PACKAGE_NAME}@${TARGET_VERSION} 已安装,但 lark-cli 不可用;将执行一次 lark-cli 安装`
3593
3636
  };
3594
3637
  }
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
- }
3609
3638
  repair(ctx) {
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 };
3639
+ if (!isTargetForkPlugin(readInstalledLarkPlugin(ctx))) return;
3640
+ if (isLarkCliAvailable$1()) return;
3641
+ installLarkCliOnce(ctx.vars.recommendedOpenclawTag ?? TARGET_VERSION);
3648
3642
  }
3649
3643
  };
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
- ],
3644
+ LarkCliMissingForInstalledLarkPluginRule = __decorate([Rule({
3645
+ key: "lark_cli_missing_for_installed_lark_plugin",
3646
+ description: "检测特定飞书插件版本已安装但 lark-cli 缺失的环境,并自动安装 lark-cli 一次",
3647
+ dependsOn: ["config_syntax_check"],
3658
3648
  repairMode: "standard",
3659
- usesVars: ["larkApps"],
3660
- level: "critical"
3661
- })], FeishuBotChannelConfigRule);
3649
+ level: "critical",
3650
+ usesVars: ["recommendedOpenclawTag"]
3651
+ })], LarkCliMissingForInstalledLarkPluginRule);
3662
3652
  //#endregion
3663
3653
  //#region src/check.ts
3664
3654
  /** Telemetry-aware entry: returns both the legacy CheckResult (for stdout)
@@ -4259,10 +4249,9 @@ function makeLogger(logFile) {
4259
4249
  /**
4260
4250
  * Start an async reset task: spawn a detached child process and return the taskId.
4261
4251
  *
4262
- * The child process runs: node cli.js reset --worker --task-id=xxx
4263
- * The worker fetches ctx from innerApi itself — no --ctx passthrough.
4252
+ * The child process runs: node cli.js reset --worker --task-id=xxx --ctx=base64
4264
4253
  */
4265
- function startAsyncReset() {
4254
+ function startAsyncReset(ctxBase64) {
4266
4255
  const taskId = (0, node_crypto.randomUUID)();
4267
4256
  const resultFile = resetResultFile(taskId);
4268
4257
  const log = makeLogger(resetLogFile(taskId));
@@ -4286,7 +4275,8 @@ function startAsyncReset() {
4286
4275
  process.argv[1],
4287
4276
  "reset",
4288
4277
  "--worker",
4289
- `--task-id=${taskId}`
4278
+ `--task-id=${taskId}`,
4279
+ `--ctx=${ctxBase64}`
4290
4280
  ], {
4291
4281
  detached: true,
4292
4282
  stdio: "ignore",
@@ -6800,60 +6790,6 @@ function mergeCoreBackupAndOrigins(configPath, vars, resetData, log) {
6800
6790
  log(`allowedOrigins: added ${added.length} (${JSON.stringify(added)}), total now ${mergedOrigins.length}`);
6801
6791
  }
6802
6792
  /**
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
- /**
6857
6793
  * Step 7: Verify startup scripts landed in configDir/scripts/.
6858
6794
  *
6859
6795
  * Scripts are extracted directly to configDir/scripts/ during stageTemplate —
@@ -6998,7 +6934,6 @@ async function runReset(input, taskId, resultFile) {
6998
6934
  await step5InstallOpenclaw(openclawTag, ossFileMap, log);
6999
6935
  step(6);
7000
6936
  mergeCoreBackupAndOrigins(configPath, vars, resetData, log);
7001
- fixBotChannelConfig(configPath, vars.larkApps, log);
7002
6937
  step(7);
7003
6938
  verifyStartupScripts(configDir, log);
7004
6939
  step(8);
@@ -7797,8 +7732,7 @@ function normalizeCtx(raw) {
7797
7732
  reset: {
7798
7733
  templateVars: r.reset.templateVars ?? {},
7799
7734
  coreBackup: r.reset.coreBackup
7800
- },
7801
- larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7735
+ }
7802
7736
  };
7803
7737
  }
7804
7738
  const vars = r.vars ?? {};
@@ -7823,8 +7757,7 @@ function normalizeCtx(raw) {
7823
7757
  reset: {
7824
7758
  templateVars: resetData.templateVars ?? {},
7825
7759
  coreBackup: resetData.coreBackup
7826
- },
7827
- larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7760
+ }
7828
7761
  };
7829
7762
  }
7830
7763
  function fillApp(src) {
@@ -7889,8 +7822,7 @@ function buildCheckInput(raw, configPathOverride) {
7889
7822
  providerFilePath: PROVIDER_FILE_PATH,
7890
7823
  secretsFilePath: SECRETS_FILE_PATH,
7891
7824
  templateVars: ctx.app.templateVars,
7892
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7893
- larkApps: ctx.larkApps
7825
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7894
7826
  },
7895
7827
  templateVars: ctx.app.templateVars
7896
7828
  };
@@ -7922,8 +7854,7 @@ function buildRepairInput(raw, configPathOverride) {
7922
7854
  providerFilePath: PROVIDER_FILE_PATH,
7923
7855
  secretsFilePath: SECRETS_FILE_PATH,
7924
7856
  templateVars: ctx.app.templateVars,
7925
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7926
- larkApps: ctx.larkApps
7857
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7927
7858
  },
7928
7859
  repairData: {
7929
7860
  secretsContent: ctx.secrets.secretsContent,
@@ -7959,8 +7890,7 @@ function buildResetInput(raw, configPathOverride) {
7959
7890
  providerFilePath: PROVIDER_FILE_PATH,
7960
7891
  secretsFilePath: SECRETS_FILE_PATH,
7961
7892
  templateVars: ctx.app.templateVars,
7962
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7963
- larkApps: ctx.larkApps
7893
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7964
7894
  },
7965
7895
  resetData: {
7966
7896
  templateVars: ctx.reset.templateVars,
@@ -10270,7 +10200,7 @@ async function reportCliRun(opts) {
10270
10200
  //#region src/help.ts
10271
10201
  const BIN = "mclaw-diagnose";
10272
10202
  function versionBanner() {
10273
- return `v0.1.13-alpha.8`;
10203
+ return `v0.1.13-alpha.9`;
10274
10204
  }
10275
10205
  const COMMANDS = [
10276
10206
  {
@@ -10374,12 +10304,16 @@ EXIT CODES
10374
10304
  hidden: true,
10375
10305
  summary: "Run rule-engine check only",
10376
10306
  help: `USAGE
10377
- ${BIN} check
10307
+ ${BIN} check [--ctx=<base64>]
10378
10308
 
10379
10309
  DESCRIPTION
10380
10310
  Runs the rule engine against the sandbox's current openclaw config and
10381
- returns { failedRules }. Ctx is fetched from innerapi automatically.
10382
- End-users should prefer \`doctor\`.
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).
10383
10317
  `
10384
10318
  },
10385
10319
  {
@@ -10387,11 +10321,16 @@ DESCRIPTION
10387
10321
  hidden: true,
10388
10322
  summary: "Apply standard-mode repairs",
10389
10323
  help: `USAGE
10390
- ${BIN} repair
10324
+ ${BIN} repair [--ctx=<base64>]
10391
10325
 
10392
10326
  DESCRIPTION
10393
- Runs repair for the failing rules. Ctx is fetched from innerapi
10394
- automatically. End-users should use \`doctor --fix\` instead.
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.
10395
10334
  `
10396
10335
  },
10397
10336
  {
@@ -10399,15 +10338,14 @@ DESCRIPTION
10399
10338
  hidden: true,
10400
10339
  summary: "Re-initialize sandbox via the 9-step reset pipeline",
10401
10340
  help: `USAGE
10402
- ${BIN} reset --async
10403
- ${BIN} reset --worker --task-id=<id>
10341
+ ${BIN} reset --async [--ctx=<base64>]
10342
+ ${BIN} reset --worker --task-id=<id> [--ctx=<base64>]
10404
10343
 
10405
10344
  DESCRIPTION
10406
10345
  Two-phase pipeline driven asynchronously: the --async invocation spawns
10407
10346
  a detached worker and returns { taskId } immediately; the --worker
10408
10347
  invocation (spawned by --async) runs the actual 9 steps and writes
10409
10348
  progress to /tmp/openclaw-diagnose/reset-<taskId>.json.
10410
- Ctx is fetched from innerapi automatically.
10411
10349
 
10412
10350
  Poll progress with \`${BIN} get_reset_task --task-id=<id>\`.
10413
10351
 
@@ -10415,6 +10353,7 @@ OPTIONS
10415
10353
  --async Start a detached worker and return taskId on stdout.
10416
10354
  --worker Internal — run the 9-step pipeline (launched by --async).
10417
10355
  --task-id=<id> Required with --worker; identifies the progress file.
10356
+ --ctx=<base64> Opaque ctx JSON; fetched from innerapi when absent.
10418
10357
  `
10419
10358
  },
10420
10359
  {
@@ -10437,7 +10376,7 @@ OPTIONS
10437
10376
  hidden: true,
10438
10377
  summary: "Download + install the openclaw tarball",
10439
10378
  help: `USAGE
10440
- ${BIN} install-openclaw <tag> [--oss_file_map=<base64>]
10379
+ ${BIN} install-openclaw <tag> [--ctx=<base64> | --oss_file_map=<base64>]
10441
10380
 
10442
10381
  DESCRIPTION
10443
10382
  Downloads the openclaw@<tag> tgz via the signed OSS URL found in the
@@ -10449,9 +10388,9 @@ ARGUMENTS
10449
10388
  <tag> Openclaw version tag, e.g. 2026.4.11.
10450
10389
 
10451
10390
  OPTIONS
10391
+ --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10452
10392
  --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi
10453
- entirely. When absent, ossFileMap is fetched from
10454
- innerapi automatically.
10393
+ entirely. Wins over --ctx when both provided.
10455
10394
  `
10456
10395
  },
10457
10396
  {
@@ -10477,7 +10416,8 @@ OPTIONS
10477
10416
  --home_base=<dir> Override the /home/gem base (tests).
10478
10417
  --config_path=<p> Override the openclaw.json path (tests).
10479
10418
  --skip-config-update Leave plugins.installs in openclaw.json untouched.
10480
- --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10419
+ --ctx=<base64> Opaque ctx; see install-openclaw for semantics.
10420
+ --oss_file_map=... Pre-built OSS URL map (base64 JSON).
10481
10421
  `
10482
10422
  },
10483
10423
  {
@@ -10504,6 +10444,7 @@ OPTIONS
10504
10444
  --cli=<name> CLI package to install by short name or scoped
10505
10445
  packageName (repeatable, at least one required).
10506
10446
  --home_base=<dir> Override the /home/gem base (tests).
10447
+ --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10507
10448
  --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10508
10449
 
10509
10450
  EXAMPLES
@@ -10610,7 +10551,8 @@ OPTIONS
10610
10551
  --role=<role> Package role (e.g. template, config).
10611
10552
  --name=<name> Package name within the role.
10612
10553
  --dir=<dir> Target dir (defaults to dirname(pkg.installPath)).
10613
- --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10554
+ --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10555
+ --oss_file_map=... Pre-built OSS URL map (base64 JSON).
10614
10556
  `
10615
10557
  }
10616
10558
  ];
@@ -10681,20 +10623,6 @@ function planVarsFields(opts = {}) {
10681
10623
  }
10682
10624
  return [...fields].sort();
10683
10625
  }
10684
- /** Check if any enabled rule declares a specific Vars field in usesVars. */
10685
- function doesAnyRuleUseVar(field, opts = {}) {
10686
- const disabled = new Set(opts.disabled ?? []);
10687
- const only = opts.onlyRules && opts.onlyRules.length > 0 ? new Set(opts.onlyRules) : null;
10688
- const profile = opts.profile ?? "standard";
10689
- for (const rule of getAllRules()) {
10690
- const key = rule.meta.key;
10691
- if (disabled.has(key)) continue;
10692
- if (only && !only.has(key)) continue;
10693
- if (!isProfileEnabled(rule, profile)) continue;
10694
- if ((rule.meta.usesVars ?? []).includes(field)) return true;
10695
- }
10696
- return false;
10697
- }
10698
10626
  /**
10699
10627
  * Plan a `populate` selector for a given subcommand.
10700
10628
  *
@@ -10719,17 +10647,11 @@ function planCtxPopulate(opts) {
10719
10647
  profile: opts.profile
10720
10648
  });
10721
10649
  if (appFields.length > 0) populate.app = appFields;
10722
- if (doesAnyRuleUseVar("larkApps", {
10723
- disabled: opts.disabled,
10724
- onlyRules: opts.onlyRules,
10725
- profile: opts.profile
10726
- })) populate.larkApps = true;
10727
10650
  if (opts.command === "repair") populate.secrets = true;
10728
10651
  else if (opts.command === "reset") {
10729
10652
  populate.secrets = true;
10730
10653
  populate.install = true;
10731
10654
  populate.reset = true;
10732
- populate.larkApps = true;
10733
10655
  }
10734
10656
  return populate;
10735
10657
  }
@@ -10789,6 +10711,21 @@ function reportDoctorRunToSlardar(opts) {
10789
10711
  const args = node_process.default.argv.slice(2);
10790
10712
  const mode = args.find((a) => !a.startsWith("-"));
10791
10713
  /**
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
+ /**
10792
10729
  * Pull the first non-flag positional after the mode name.
10793
10730
  * (The mode itself is args[0] in the filtered set, so we skip index 0.)
10794
10731
  */
@@ -10816,8 +10753,8 @@ function getMultiFlag(args, name) {
10816
10753
  * case but is no longer consulted.
10817
10754
  */
10818
10755
  async function reportRun(command, rc, _raw, invocation, durationMs, outcome, slardar = {
10819
- scene: void 0,
10820
- profile: "standard",
10756
+ scene,
10757
+ profile,
10821
10758
  fix: false
10822
10759
  }) {
10823
10760
  console.error(`${command}: telemetry calling report_cli_run`);
@@ -10881,7 +10818,7 @@ async function main() {
10881
10818
  console.error(`${mode}: begin argv=[${args.join(" ")}] version=${getVersion()} traceId=${traceId ?? "-"} caller=${caller ?? "-"} runIdGenerated=${rc.generated}`);
10882
10819
  switch (mode) {
10883
10820
  case "check": {
10884
- const raw = await fetchCtxViaInnerApi({
10821
+ const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10885
10822
  populate: planCtxPopulate({
10886
10823
  command: "check",
10887
10824
  profile
@@ -10906,7 +10843,7 @@ async function main() {
10906
10843
  break;
10907
10844
  }
10908
10845
  case "repair": {
10909
- const raw = await fetchCtxViaInnerApi({
10846
+ const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10910
10847
  populate: planCtxPopulate({
10911
10848
  command: "repair",
10912
10849
  profile
@@ -10977,15 +10914,27 @@ async function main() {
10977
10914
  break;
10978
10915
  }
10979
10916
  case "reset":
10980
- if (args.includes("--async")) console.log(JSON.stringify(startAsyncReset()));
10981
- else if (args.includes("--worker")) {
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")) {
10982
10931
  const taskId = args.find((a) => a.startsWith("--task-id="))?.slice(10);
10983
10932
  if (!taskId) {
10984
10933
  console.error("Error: --task-id=<id> is required for worker");
10985
10934
  node_process.default.exit(1);
10986
10935
  }
10987
10936
  const resultFile = resetResultFile(taskId);
10988
- const raw = await fetchCtxViaInnerApi({
10937
+ const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10989
10938
  populate: planCtxPopulate({ command: "reset" }),
10990
10939
  caller,
10991
10940
  traceId
@@ -11009,7 +10958,7 @@ async function main() {
11009
10958
  return;
11010
10959
  }
11011
10960
  } else {
11012
- console.error("Usage: reset --async | reset --worker --task-id=<id>");
10961
+ console.error("Usage: reset --async [--ctx=<base64>] | reset --worker --task-id=<id> [--ctx=<base64>]");
11013
10962
  node_process.default.exit(1);
11014
10963
  }
11015
10964
  break;
@@ -11025,14 +10974,14 @@ async function main() {
11025
10974
  case "install-openclaw": {
11026
10975
  const tag = getPositionalTag(args, "install-openclaw");
11027
10976
  if (!tag) {
11028
- console.error("Usage: install-openclaw <tag> [--oss_file_map=<base64>]");
10977
+ console.error("Usage: install-openclaw <tag> [--ctx=<base64> | --oss_file_map=<base64>]");
11029
10978
  node_process.default.exit(1);
11030
10979
  }
11031
10980
  const ossFileMapFlag = getFlag(args, "oss_file_map");
11032
10981
  let installOssFileMap;
11033
10982
  let rawForTelemetry;
11034
10983
  if (!ossFileMapFlag) {
11035
- rawForTelemetry = await fetchCtxViaInnerApi({
10984
+ rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11036
10985
  populate: planCtxPopulate({ command: "install" }),
11037
10986
  caller,
11038
10987
  traceId
@@ -11067,7 +11016,7 @@ async function main() {
11067
11016
  case "install-extension": {
11068
11017
  const tag = getPositionalTag(args, "install-extension");
11069
11018
  if (!tag) {
11070
- console.error("Usage: install-extension <tag> (--all | --extension=<name>...) [--home_base=<dir>] [--config_path=<path>] [--skip-config-update] [--oss_file_map=<base64>]");
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>]");
11071
11020
  node_process.default.exit(1);
11072
11021
  }
11073
11022
  const all = args.includes("--all");
@@ -11079,7 +11028,7 @@ async function main() {
11079
11028
  let installOssFileMap;
11080
11029
  let rawForTelemetry;
11081
11030
  if (!ossFileMapFlag) {
11082
- rawForTelemetry = await fetchCtxViaInnerApi({
11031
+ rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11083
11032
  populate: planCtxPopulate({ command: "install" }),
11084
11033
  caller,
11085
11034
  traceId
@@ -11125,12 +11074,12 @@ async function main() {
11125
11074
  case "install-cli": {
11126
11075
  const tag = getPositionalTag(args, "install-cli");
11127
11076
  if (!tag) {
11128
- console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
11077
+ console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--ctx=<base64> | --oss_file_map=<base64>]");
11129
11078
  node_process.default.exit(1);
11130
11079
  }
11131
11080
  const names = getMultiFlag(args, "cli");
11132
11081
  if (names.length === 0) {
11133
- console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--oss_file_map=<base64>]");
11082
+ console.error("Usage: install-cli <tag> --cli=<name>... [--home_base=<dir>] [--ctx=<base64> | --oss_file_map=<base64>]");
11134
11083
  node_process.default.exit(1);
11135
11084
  }
11136
11085
  const homeBase = getFlag(args, "home_base");
@@ -11138,7 +11087,7 @@ async function main() {
11138
11087
  let installOssFileMap;
11139
11088
  let rawForTelemetry;
11140
11089
  if (!ossFileMapFlag) {
11141
- rawForTelemetry = await fetchCtxViaInnerApi({
11090
+ rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11142
11091
  populate: planCtxPopulate({ command: "install" }),
11143
11092
  caller,
11144
11093
  traceId
@@ -11186,7 +11135,7 @@ async function main() {
11186
11135
  case "download-resource": {
11187
11136
  const tag = getPositionalTag(args, "download-resource");
11188
11137
  if (!tag) {
11189
- console.error("Usage: download-resource <tag> --role=<role> --name=<name> [--dir=<dir>] [--oss_file_map=<base64>]");
11138
+ console.error("Usage: download-resource <tag> --role=<role> --name=<name> [--dir=<dir>] [--ctx=<base64> | --oss_file_map=<base64>]");
11190
11139
  node_process.default.exit(1);
11191
11140
  }
11192
11141
  const role = getFlag(args, "role");
@@ -11200,7 +11149,7 @@ async function main() {
11200
11149
  let installOssFileMap;
11201
11150
  let rawForTelemetry;
11202
11151
  if (!ossFileMapFlag) {
11203
- rawForTelemetry = await fetchCtxViaInnerApi({
11152
+ rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11204
11153
  populate: planCtxPopulate({ command: "install" }),
11205
11154
  caller,
11206
11155
  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-alpha.8",
3
+ "version": "0.1.13-alpha.9",
4
4
  "description": "CLI for OpenClaw config diagnose and repair with JSON5 support",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {