@lark-apaas/openclaw-scripts-diagnose-cli 0.1.13-alpha.7 → 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 -203
  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.7";
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,86 +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
- 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 {
3565
3630
  validate(ctx) {
3566
- const larkApps = ctx.vars.larkApps;
3567
- if (!larkApps || larkApps.length === 0) return { pass: true };
3568
- const accounts = asRecord(getNestedMap(ctx.config, "channels", "feishu")?.accounts);
3569
- if (!accounts) return { pass: true };
3570
- const issues = [];
3571
- for (const [accountId, account] of Object.entries(accounts)) {
3572
- const bot = asRecord(account);
3573
- if (!bot) continue;
3574
- const appId = bot.appId;
3575
- if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
3576
- const larkApp = larkApps.find((e) => e.larkAppID === appId);
3577
- if (!larkApp) continue;
3578
- const creatorOpenID = larkApp.creatorOpenID;
3579
- if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
3580
- if (!(Array.isArray(bot.allowFrom) ? bot.allowFrom : []).includes(creatorOpenID)) issues.push(`${accountId} allowFrom missing creatorOpenID ${creatorOpenID.length > 8 ? creatorOpenID.slice(0, 4) + "***" + creatorOpenID.slice(-4) : "***"}`);
3581
- }
3582
- const secret = bot.appSecret;
3583
- if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
3584
- if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) issues.push(`${accountId} appSecret is a provider-ref but not the canonical one`);
3585
- } else if (typeof secret === "string") {
3586
- if (secret !== larkApp.appSecret) issues.push(`${accountId} appSecret plaintext mismatch`);
3587
- } else issues.push(`${accountId} appSecret has unexpected type`);
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
3638
  repair(ctx) {
3596
- const larkApps = ctx.vars.larkApps;
3597
- if (!larkApps || larkApps.length === 0) return;
3598
- const accounts = asRecord(getNestedMap(ctx.config, "channels", "feishu")?.accounts);
3599
- if (!accounts) return;
3600
- for (const [, account] of Object.entries(accounts)) {
3601
- const bot = asRecord(account);
3602
- if (!bot) continue;
3603
- const appId = bot.appId;
3604
- if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
3605
- const larkApp = larkApps.find((e) => e.larkAppID === appId);
3606
- if (!larkApp) continue;
3607
- const creatorOpenID = larkApp.creatorOpenID;
3608
- if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
3609
- const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
3610
- if (!allowFrom.includes(creatorOpenID)) {
3611
- allowFrom.push(creatorOpenID);
3612
- bot.allowFrom = allowFrom;
3613
- }
3614
- }
3615
- const secret = bot.appSecret;
3616
- let needsFix = false;
3617
- if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
3618
- if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
3619
- } else if (typeof secret === "string") {
3620
- if (secret !== larkApp.appSecret) needsFix = true;
3621
- } else needsFix = true;
3622
- if (needsFix) bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
3623
- }
3639
+ if (!isTargetForkPlugin(readInstalledLarkPlugin(ctx))) return;
3640
+ if (isLarkCliAvailable$1()) return;
3641
+ installLarkCliOnce(ctx.vars.recommendedOpenclawTag ?? TARGET_VERSION);
3624
3642
  }
3625
3643
  };
3626
- FeishuBotChannelConfigRule = __decorate([Rule({
3627
- key: "feishu_bot_channel_config",
3628
- description: "确保多账号飞书配置中每个 bot 账号的 allowFrom 包含其创建者 openID 且 appSecret 值正确",
3629
- dependsOn: [
3630
- "config_syntax_check",
3631
- "feishu_default_account",
3632
- "feishu_bot_id"
3633
- ],
3644
+ LarkCliMissingForInstalledLarkPluginRule = __decorate([Rule({
3645
+ key: "lark_cli_missing_for_installed_lark_plugin",
3646
+ description: "检测特定飞书插件版本已安装但 lark-cli 缺失的环境,并自动安装 lark-cli 一次",
3647
+ dependsOn: ["config_syntax_check"],
3634
3648
  repairMode: "standard",
3635
- usesVars: ["larkApps"],
3636
- level: "critical"
3637
- })], FeishuBotChannelConfigRule);
3649
+ level: "critical",
3650
+ usesVars: ["recommendedOpenclawTag"]
3651
+ })], LarkCliMissingForInstalledLarkPluginRule);
3638
3652
  //#endregion
3639
3653
  //#region src/check.ts
3640
3654
  /** Telemetry-aware entry: returns both the legacy CheckResult (for stdout)
@@ -4235,10 +4249,9 @@ function makeLogger(logFile) {
4235
4249
  /**
4236
4250
  * Start an async reset task: spawn a detached child process and return the taskId.
4237
4251
  *
4238
- * The child process runs: node cli.js reset --worker --task-id=xxx
4239
- * 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
4240
4253
  */
4241
- function startAsyncReset() {
4254
+ function startAsyncReset(ctxBase64) {
4242
4255
  const taskId = (0, node_crypto.randomUUID)();
4243
4256
  const resultFile = resetResultFile(taskId);
4244
4257
  const log = makeLogger(resetLogFile(taskId));
@@ -4262,7 +4275,8 @@ function startAsyncReset() {
4262
4275
  process.argv[1],
4263
4276
  "reset",
4264
4277
  "--worker",
4265
- `--task-id=${taskId}`
4278
+ `--task-id=${taskId}`,
4279
+ `--ctx=${ctxBase64}`
4266
4280
  ], {
4267
4281
  detached: true,
4268
4282
  stdio: "ignore",
@@ -6776,60 +6790,6 @@ function mergeCoreBackupAndOrigins(configPath, vars, resetData, log) {
6776
6790
  log(`allowedOrigins: added ${added.length} (${JSON.stringify(added)}), total now ${mergedOrigins.length}`);
6777
6791
  }
6778
6792
  /**
6779
- * Fix bot account allowFrom and appSecret using larkApps from innerApi.
6780
- *
6781
- * For each bot account (key starts with `bot-cli_`):
6782
- * - allowFrom must contain the bot's own creatorOpenID from larkApps
6783
- * - appSecret must be either the canonical provider-ref or match larkApps plaintext
6784
- *
6785
- * Runs after mergeCoreBackupAndOrigins so it operates on the final config state.
6786
- */
6787
- function fixBotChannelConfig(configPath, larkApps, log) {
6788
- if (!larkApps || larkApps.length === 0) {
6789
- log("no larkApps data, skip bot channel config fix");
6790
- return;
6791
- }
6792
- const config = loadJSON5().parse(node_fs.default.readFileSync(configPath, "utf-8"));
6793
- const accounts = asRecord(getNestedMap(config, "channels", "feishu")?.accounts);
6794
- if (!accounts) {
6795
- log("no feishu accounts in config, skip bot channel config fix");
6796
- return;
6797
- }
6798
- let fixCount = 0;
6799
- for (const [, account] of Object.entries(accounts)) {
6800
- const bot = asRecord(account);
6801
- if (!bot) continue;
6802
- const appId = bot.appId;
6803
- if (typeof appId !== "string" || !appId.startsWith("cli_")) continue;
6804
- const larkApp = larkApps.find((e) => e.larkAppID === appId);
6805
- if (!larkApp) continue;
6806
- const creatorOpenID = larkApp.creatorOpenID;
6807
- if (typeof creatorOpenID === "string" && creatorOpenID !== "") {
6808
- const allowFrom = Array.isArray(bot.allowFrom) ? [...bot.allowFrom] : [];
6809
- if (!allowFrom.includes(creatorOpenID)) {
6810
- allowFrom.push(creatorOpenID);
6811
- bot.allowFrom = allowFrom;
6812
- fixCount++;
6813
- }
6814
- }
6815
- const secret = bot.appSecret;
6816
- let needsFix = false;
6817
- if (typeof secret === "object" && secret !== null && !Array.isArray(secret)) {
6818
- if (!matchMap(secret, DEFAULT_FEISHU_APP_SECRET)) needsFix = true;
6819
- } else if (typeof secret === "string") {
6820
- if (secret !== larkApp.appSecret) needsFix = true;
6821
- } else needsFix = true;
6822
- if (needsFix) {
6823
- bot.appSecret = { ...DEFAULT_FEISHU_APP_SECRET };
6824
- fixCount++;
6825
- }
6826
- }
6827
- if (fixCount > 0) {
6828
- node_fs.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
6829
- log(`fixed ${fixCount} bot channel config issue(s) (allowFrom/appSecret)`);
6830
- } else log("bot channel config ok, no fixes needed");
6831
- }
6832
- /**
6833
6793
  * Step 7: Verify startup scripts landed in configDir/scripts/.
6834
6794
  *
6835
6795
  * Scripts are extracted directly to configDir/scripts/ during stageTemplate —
@@ -6974,7 +6934,6 @@ async function runReset(input, taskId, resultFile) {
6974
6934
  await step5InstallOpenclaw(openclawTag, ossFileMap, log);
6975
6935
  step(6);
6976
6936
  mergeCoreBackupAndOrigins(configPath, vars, resetData, log);
6977
- fixBotChannelConfig(configPath, vars.larkApps, log);
6978
6937
  step(7);
6979
6938
  verifyStartupScripts(configDir, log);
6980
6939
  step(8);
@@ -7773,8 +7732,7 @@ function normalizeCtx(raw) {
7773
7732
  reset: {
7774
7733
  templateVars: r.reset.templateVars ?? {},
7775
7734
  coreBackup: r.reset.coreBackup
7776
- },
7777
- larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7735
+ }
7778
7736
  };
7779
7737
  }
7780
7738
  const vars = r.vars ?? {};
@@ -7799,8 +7757,7 @@ function normalizeCtx(raw) {
7799
7757
  reset: {
7800
7758
  templateVars: resetData.templateVars ?? {},
7801
7759
  coreBackup: resetData.coreBackup
7802
- },
7803
- larkApps: Array.isArray(r.larkApps) ? r.larkApps : []
7760
+ }
7804
7761
  };
7805
7762
  }
7806
7763
  function fillApp(src) {
@@ -7865,8 +7822,7 @@ function buildCheckInput(raw, configPathOverride) {
7865
7822
  providerFilePath: PROVIDER_FILE_PATH,
7866
7823
  secretsFilePath: SECRETS_FILE_PATH,
7867
7824
  templateVars: ctx.app.templateVars,
7868
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7869
- larkApps: ctx.larkApps
7825
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7870
7826
  },
7871
7827
  templateVars: ctx.app.templateVars
7872
7828
  };
@@ -7898,8 +7854,7 @@ function buildRepairInput(raw, configPathOverride) {
7898
7854
  providerFilePath: PROVIDER_FILE_PATH,
7899
7855
  secretsFilePath: SECRETS_FILE_PATH,
7900
7856
  templateVars: ctx.app.templateVars,
7901
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7902
- larkApps: ctx.larkApps
7857
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7903
7858
  },
7904
7859
  repairData: {
7905
7860
  secretsContent: ctx.secrets.secretsContent,
@@ -7935,8 +7890,7 @@ function buildResetInput(raw, configPathOverride) {
7935
7890
  providerFilePath: PROVIDER_FILE_PATH,
7936
7891
  secretsFilePath: SECRETS_FILE_PATH,
7937
7892
  templateVars: ctx.app.templateVars,
7938
- recommendedOpenclawTag: ctx.app.recommendedOpenclawTag,
7939
- larkApps: ctx.larkApps
7893
+ recommendedOpenclawTag: ctx.app.recommendedOpenclawTag
7940
7894
  },
7941
7895
  resetData: {
7942
7896
  templateVars: ctx.reset.templateVars,
@@ -10246,7 +10200,7 @@ async function reportCliRun(opts) {
10246
10200
  //#region src/help.ts
10247
10201
  const BIN = "mclaw-diagnose";
10248
10202
  function versionBanner() {
10249
- return `v0.1.13-alpha.7`;
10203
+ return `v0.1.13-alpha.9`;
10250
10204
  }
10251
10205
  const COMMANDS = [
10252
10206
  {
@@ -10350,12 +10304,16 @@ EXIT CODES
10350
10304
  hidden: true,
10351
10305
  summary: "Run rule-engine check only",
10352
10306
  help: `USAGE
10353
- ${BIN} check
10307
+ ${BIN} check [--ctx=<base64>]
10354
10308
 
10355
10309
  DESCRIPTION
10356
10310
  Runs the rule engine against the sandbox's current openclaw config and
10357
- returns { failedRules }. Ctx is fetched from innerapi automatically.
10358
- 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).
10359
10317
  `
10360
10318
  },
10361
10319
  {
@@ -10363,11 +10321,16 @@ DESCRIPTION
10363
10321
  hidden: true,
10364
10322
  summary: "Apply standard-mode repairs",
10365
10323
  help: `USAGE
10366
- ${BIN} repair
10324
+ ${BIN} repair [--ctx=<base64>]
10367
10325
 
10368
10326
  DESCRIPTION
10369
- Runs repair for the failing rules. Ctx is fetched from innerapi
10370
- 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.
10371
10334
  `
10372
10335
  },
10373
10336
  {
@@ -10375,15 +10338,14 @@ DESCRIPTION
10375
10338
  hidden: true,
10376
10339
  summary: "Re-initialize sandbox via the 9-step reset pipeline",
10377
10340
  help: `USAGE
10378
- ${BIN} reset --async
10379
- ${BIN} reset --worker --task-id=<id>
10341
+ ${BIN} reset --async [--ctx=<base64>]
10342
+ ${BIN} reset --worker --task-id=<id> [--ctx=<base64>]
10380
10343
 
10381
10344
  DESCRIPTION
10382
10345
  Two-phase pipeline driven asynchronously: the --async invocation spawns
10383
10346
  a detached worker and returns { taskId } immediately; the --worker
10384
10347
  invocation (spawned by --async) runs the actual 9 steps and writes
10385
10348
  progress to /tmp/openclaw-diagnose/reset-<taskId>.json.
10386
- Ctx is fetched from innerapi automatically.
10387
10349
 
10388
10350
  Poll progress with \`${BIN} get_reset_task --task-id=<id>\`.
10389
10351
 
@@ -10391,6 +10353,7 @@ OPTIONS
10391
10353
  --async Start a detached worker and return taskId on stdout.
10392
10354
  --worker Internal — run the 9-step pipeline (launched by --async).
10393
10355
  --task-id=<id> Required with --worker; identifies the progress file.
10356
+ --ctx=<base64> Opaque ctx JSON; fetched from innerapi when absent.
10394
10357
  `
10395
10358
  },
10396
10359
  {
@@ -10413,7 +10376,7 @@ OPTIONS
10413
10376
  hidden: true,
10414
10377
  summary: "Download + install the openclaw tarball",
10415
10378
  help: `USAGE
10416
- ${BIN} install-openclaw <tag> [--oss_file_map=<base64>]
10379
+ ${BIN} install-openclaw <tag> [--ctx=<base64> | --oss_file_map=<base64>]
10417
10380
 
10418
10381
  DESCRIPTION
10419
10382
  Downloads the openclaw@<tag> tgz via the signed OSS URL found in the
@@ -10425,9 +10388,9 @@ ARGUMENTS
10425
10388
  <tag> Openclaw version tag, e.g. 2026.4.11.
10426
10389
 
10427
10390
  OPTIONS
10391
+ --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10428
10392
  --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi
10429
- entirely. When absent, ossFileMap is fetched from
10430
- innerapi automatically.
10393
+ entirely. Wins over --ctx when both provided.
10431
10394
  `
10432
10395
  },
10433
10396
  {
@@ -10453,7 +10416,8 @@ OPTIONS
10453
10416
  --home_base=<dir> Override the /home/gem base (tests).
10454
10417
  --config_path=<p> Override the openclaw.json path (tests).
10455
10418
  --skip-config-update Leave plugins.installs in openclaw.json untouched.
10456
- --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).
10457
10421
  `
10458
10422
  },
10459
10423
  {
@@ -10480,6 +10444,7 @@ OPTIONS
10480
10444
  --cli=<name> CLI package to install by short name or scoped
10481
10445
  packageName (repeatable, at least one required).
10482
10446
  --home_base=<dir> Override the /home/gem base (tests).
10447
+ --ctx=<base64> Opaque ctx; ossFileMap is extracted from it.
10483
10448
  --oss_file_map=... Pre-built OSS URL map (base64 JSON); skips innerapi.
10484
10449
 
10485
10450
  EXAMPLES
@@ -10586,7 +10551,8 @@ OPTIONS
10586
10551
  --role=<role> Package role (e.g. template, config).
10587
10552
  --name=<name> Package name within the role.
10588
10553
  --dir=<dir> Target dir (defaults to dirname(pkg.installPath)).
10589
- --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).
10590
10556
  `
10591
10557
  }
10592
10558
  ];
@@ -10657,20 +10623,6 @@ function planVarsFields(opts = {}) {
10657
10623
  }
10658
10624
  return [...fields].sort();
10659
10625
  }
10660
- /** Check if any enabled rule declares a specific Vars field in usesVars. */
10661
- function doesAnyRuleUseVar(field, opts = {}) {
10662
- const disabled = new Set(opts.disabled ?? []);
10663
- const only = opts.onlyRules && opts.onlyRules.length > 0 ? new Set(opts.onlyRules) : null;
10664
- const profile = opts.profile ?? "standard";
10665
- for (const rule of getAllRules()) {
10666
- const key = rule.meta.key;
10667
- if (disabled.has(key)) continue;
10668
- if (only && !only.has(key)) continue;
10669
- if (!isProfileEnabled(rule, profile)) continue;
10670
- if ((rule.meta.usesVars ?? []).includes(field)) return true;
10671
- }
10672
- return false;
10673
- }
10674
10626
  /**
10675
10627
  * Plan a `populate` selector for a given subcommand.
10676
10628
  *
@@ -10695,17 +10647,11 @@ function planCtxPopulate(opts) {
10695
10647
  profile: opts.profile
10696
10648
  });
10697
10649
  if (appFields.length > 0) populate.app = appFields;
10698
- if (doesAnyRuleUseVar("larkApps", {
10699
- disabled: opts.disabled,
10700
- onlyRules: opts.onlyRules,
10701
- profile: opts.profile
10702
- })) populate.larkApps = true;
10703
10650
  if (opts.command === "repair") populate.secrets = true;
10704
10651
  else if (opts.command === "reset") {
10705
10652
  populate.secrets = true;
10706
10653
  populate.install = true;
10707
10654
  populate.reset = true;
10708
- populate.larkApps = true;
10709
10655
  }
10710
10656
  return populate;
10711
10657
  }
@@ -10765,6 +10711,21 @@ function reportDoctorRunToSlardar(opts) {
10765
10711
  const args = node_process.default.argv.slice(2);
10766
10712
  const mode = args.find((a) => !a.startsWith("-"));
10767
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
+ /**
10768
10729
  * Pull the first non-flag positional after the mode name.
10769
10730
  * (The mode itself is args[0] in the filtered set, so we skip index 0.)
10770
10731
  */
@@ -10792,8 +10753,8 @@ function getMultiFlag(args, name) {
10792
10753
  * case but is no longer consulted.
10793
10754
  */
10794
10755
  async function reportRun(command, rc, _raw, invocation, durationMs, outcome, slardar = {
10795
- scene: void 0,
10796
- profile: "standard",
10756
+ scene,
10757
+ profile,
10797
10758
  fix: false
10798
10759
  }) {
10799
10760
  console.error(`${command}: telemetry calling report_cli_run`);
@@ -10857,7 +10818,7 @@ async function main() {
10857
10818
  console.error(`${mode}: begin argv=[${args.join(" ")}] version=${getVersion()} traceId=${traceId ?? "-"} caller=${caller ?? "-"} runIdGenerated=${rc.generated}`);
10858
10819
  switch (mode) {
10859
10820
  case "check": {
10860
- const raw = await fetchCtxViaInnerApi({
10821
+ const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10861
10822
  populate: planCtxPopulate({
10862
10823
  command: "check",
10863
10824
  profile
@@ -10882,7 +10843,7 @@ async function main() {
10882
10843
  break;
10883
10844
  }
10884
10845
  case "repair": {
10885
- const raw = await fetchCtxViaInnerApi({
10846
+ const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10886
10847
  populate: planCtxPopulate({
10887
10848
  command: "repair",
10888
10849
  profile
@@ -10953,15 +10914,27 @@ async function main() {
10953
10914
  break;
10954
10915
  }
10955
10916
  case "reset":
10956
- if (args.includes("--async")) console.log(JSON.stringify(startAsyncReset()));
10957
- 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")) {
10958
10931
  const taskId = args.find((a) => a.startsWith("--task-id="))?.slice(10);
10959
10932
  if (!taskId) {
10960
10933
  console.error("Error: --task-id=<id> is required for worker");
10961
10934
  node_process.default.exit(1);
10962
10935
  }
10963
10936
  const resultFile = resetResultFile(taskId);
10964
- const raw = await fetchCtxViaInnerApi({
10937
+ const raw = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
10965
10938
  populate: planCtxPopulate({ command: "reset" }),
10966
10939
  caller,
10967
10940
  traceId
@@ -10985,7 +10958,7 @@ async function main() {
10985
10958
  return;
10986
10959
  }
10987
10960
  } else {
10988
- 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>]");
10989
10962
  node_process.default.exit(1);
10990
10963
  }
10991
10964
  break;
@@ -11001,14 +10974,14 @@ async function main() {
11001
10974
  case "install-openclaw": {
11002
10975
  const tag = getPositionalTag(args, "install-openclaw");
11003
10976
  if (!tag) {
11004
- console.error("Usage: install-openclaw <tag> [--oss_file_map=<base64>]");
10977
+ console.error("Usage: install-openclaw <tag> [--ctx=<base64> | --oss_file_map=<base64>]");
11005
10978
  node_process.default.exit(1);
11006
10979
  }
11007
10980
  const ossFileMapFlag = getFlag(args, "oss_file_map");
11008
10981
  let installOssFileMap;
11009
10982
  let rawForTelemetry;
11010
10983
  if (!ossFileMapFlag) {
11011
- rawForTelemetry = await fetchCtxViaInnerApi({
10984
+ rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11012
10985
  populate: planCtxPopulate({ command: "install" }),
11013
10986
  caller,
11014
10987
  traceId
@@ -11043,7 +11016,7 @@ async function main() {
11043
11016
  case "install-extension": {
11044
11017
  const tag = getPositionalTag(args, "install-extension");
11045
11018
  if (!tag) {
11046
- 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>]");
11047
11020
  node_process.default.exit(1);
11048
11021
  }
11049
11022
  const all = args.includes("--all");
@@ -11055,7 +11028,7 @@ async function main() {
11055
11028
  let installOssFileMap;
11056
11029
  let rawForTelemetry;
11057
11030
  if (!ossFileMapFlag) {
11058
- rawForTelemetry = await fetchCtxViaInnerApi({
11031
+ rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11059
11032
  populate: planCtxPopulate({ command: "install" }),
11060
11033
  caller,
11061
11034
  traceId
@@ -11101,12 +11074,12 @@ async function main() {
11101
11074
  case "install-cli": {
11102
11075
  const tag = getPositionalTag(args, "install-cli");
11103
11076
  if (!tag) {
11104
- 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>]");
11105
11078
  node_process.default.exit(1);
11106
11079
  }
11107
11080
  const names = getMultiFlag(args, "cli");
11108
11081
  if (names.length === 0) {
11109
- 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>]");
11110
11083
  node_process.default.exit(1);
11111
11084
  }
11112
11085
  const homeBase = getFlag(args, "home_base");
@@ -11114,7 +11087,7 @@ async function main() {
11114
11087
  let installOssFileMap;
11115
11088
  let rawForTelemetry;
11116
11089
  if (!ossFileMapFlag) {
11117
- rawForTelemetry = await fetchCtxViaInnerApi({
11090
+ rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11118
11091
  populate: planCtxPopulate({ command: "install" }),
11119
11092
  caller,
11120
11093
  traceId
@@ -11162,7 +11135,7 @@ async function main() {
11162
11135
  case "download-resource": {
11163
11136
  const tag = getPositionalTag(args, "download-resource");
11164
11137
  if (!tag) {
11165
- 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>]");
11166
11139
  node_process.default.exit(1);
11167
11140
  }
11168
11141
  const role = getFlag(args, "role");
@@ -11176,7 +11149,7 @@ async function main() {
11176
11149
  let installOssFileMap;
11177
11150
  let rawForTelemetry;
11178
11151
  if (!ossFileMapFlag) {
11179
- rawForTelemetry = await fetchCtxViaInnerApi({
11152
+ rawForTelemetry = parseCtxFlag(args) ?? await fetchCtxViaInnerApi({
11180
11153
  populate: planCtxPopulate({ command: "install" }),
11181
11154
  caller,
11182
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.7",
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": {